55namespace staabm \PHPStanDba \PdoReflection ;
66
77use PDOStatement ;
8- use PhpParser \Node ;
98use PhpParser \Node \Expr ;
10- use PhpParser \Node \Expr \Assign ;
119use PhpParser \Node \Expr \MethodCall ;
12- use PhpParser \Node \FunctionLike ;
13- use PhpParser \NodeFinder ;
1410use PHPStan \Reflection \MethodReflection ;
1511use PHPStan \ShouldNotHappenException ;
12+ use staabm \PHPStanDba \QueryReflection \ExpressionFinder ;
1613
1714final class PdoStatementReflection
1815{
19- /**
20- * Do not change, part of internal PHPStan naming.
21- *
22- * @var string
23- */
24- private const PREVIOUS = 'previous ' ;
25-
26- /**
27- * Convention key name in php-parser and PHPStan for parent node.
28- *
29- * @var string
30- */
31- private const PARENT = 'parent ' ;
32-
33- private NodeFinder $ nodeFinder ;
34-
35- public function __construct ()
36- {
37- $ this ->nodeFinder = new NodeFinder ();
38- }
39-
4016 public function findPrepareQueryStringExpression (MethodReflection $ methodReflection , MethodCall $ methodCall ): ?Expr
4117 {
4218 if ('execute ' !== $ methodReflection ->getName () || PdoStatement::class !== $ methodReflection ->getDeclaringClass ()->getName ()) {
4319 throw new ShouldNotHappenException ();
4420 }
4521
46- $ queryExpr = $ this ->findQueryStringExpression ($ methodCall );
22+ $ exprFinder = new ExpressionFinder ();
23+ $ queryExpr = $ exprFinder ->findQueryStringExpression ($ methodCall );
4724
4825 // resolve query parameter from "prepare"
4926 if ($ queryExpr instanceof MethodCall) {
@@ -54,74 +31,4 @@ public function findPrepareQueryStringExpression(MethodReflection $methodReflect
5431
5532 return null ;
5633 }
57-
58- private function findQueryStringExpression (MethodCall $ methodCall ): ?Expr
59- {
60- // todo: use astral simpleNameResolver
61- $ nameResolver = function ($ node ) {
62- if (\is_string ($ node ->name )) {
63- return $ node ->name ;
64- }
65- if ($ node ->name instanceof Node \Identifier) {
66- return $ node ->name ->toString ();
67- }
68- };
69-
70- $ current = $ methodCall ;
71- while (null !== $ current ) {
72- /** @var Assign|null $assign */
73- $ assign = $ this ->findFirstPreviousOfNode ($ current , function ($ node ) {
74- return $ node instanceof Assign;
75- });
76-
77- if (null !== $ assign && $ nameResolver ($ assign ->var ) === $ nameResolver ($ methodCall ->var )) {
78- return $ assign ->expr ;
79- }
80-
81- $ current = $ assign ;
82- }
83-
84- return null ;
85- }
86-
87- /**
88- * @param callable(Node $node):bool $filter
89- */
90- private function findFirstPreviousOfNode (Node $ node , callable $ filter ): ?Node
91- {
92- // move to previous expression
93- $ previousStatement = $ node ->getAttribute (self ::PREVIOUS );
94- if (null !== $ previousStatement ) {
95- if (!$ previousStatement instanceof Node) {
96- throw new ShouldNotHappenException ();
97- }
98- $ foundNode = $ this ->findFirst ([$ previousStatement ], $ filter );
99- // we found what we need
100- if (null !== $ foundNode ) {
101- return $ foundNode ;
102- }
103-
104- return $ this ->findFirstPreviousOfNode ($ previousStatement , $ filter );
105- }
106-
107- $ parent = $ node ->getAttribute (self ::PARENT );
108- if ($ parent instanceof FunctionLike) {
109- return null ;
110- }
111-
112- if ($ parent instanceof Node) {
113- return $ this ->findFirstPreviousOfNode ($ parent , $ filter );
114- }
115-
116- return null ;
117- }
118-
119- /**
120- * @param Node|Node[] $nodes
121- * @param callable(Node $node):bool $filter
122- */
123- private function findFirst (Node |array $ nodes , callable $ filter ): ?Node
124- {
125- return $ this ->nodeFinder ->findFirst ($ nodes , $ filter );
126- }
12734}
0 commit comments