Test Setup Failed
Push — master ( 4d8696...980fb9 )
by Valentin
05:05
created

AddProxiedMethods::leaveNode()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 8.9457
c 0
b 0
f 0
cc 6
nc 8
nop 1
1
<?php
2
3
/**
4
 * Spiral Framework.
5
 *
6
 * @license MIT
7
 * @author  Valentin V (Vvval)
8
 */
9
declare(strict_types=1);
10
11
namespace Cycle\ORM\Promise\Visitor;
12
13
use Cycle\ORM\Promise\Expressions;
14
use Cycle\ORM\Promise\PHPDoc;
15
use PhpParser\Builder\Param;
16
use PhpParser\Node;
17
use PhpParser\NodeVisitorAbstract;
18
19
/**
20
 * Add parent call via proxy resolver
21
 */
22
final class AddProxiedMethods extends NodeVisitorAbstract
23
{
24
    /** @var string */
25
    private $resolverProperty;
26
27
    /** @var Node\Stmt\ClassMethod[] */
28
    private $methods;
29
30
    /** @var string */
31
    private $resolveMethod;
32
33
    /**
34
     * @param string $property
35
     * @param array  $methods
36
     * @param string $resolveMethod
37
     */
38
    public function __construct(string $property, array $methods, string $resolveMethod)
39
    {
40
        $this->resolverProperty = $property;
41
        $this->methods = $methods;
42
        $this->resolveMethod = $resolveMethod;
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function leaveNode(Node $node)
49
    {
50
        if (!$node instanceof Node\Stmt\Class_) {
51
            return null;
52
        }
53
54
        foreach ($this->methods as $method) {
55
            if ($method->name->name === 'undefinedReturn') {
56
//                print_r([__METHOD__ => $method->stmts]);
57
            }
58
            if ($method->name->name === '__clone') {
59
                $method->stmts = [$this->buildCloneExpression()];
60
                $node->stmts[] = $method;
61
            } elseif ($this->hasReturnStmt($method)) {
62
                $node->stmts[] = $this->modifyReturnMethod($method);
63
            } else {
64
                $node->stmts[] = $this->modifyExprMethod($method);
65
            }
66
        }
67
68
        return $node;
69
    }
70
71
    /**
72
     * @return Node\Stmt\Expression
73
     */
74 View Code Duplication
    private function buildCloneExpression(): Node\Stmt\Expression
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
75
    {
76
        return new Node\Stmt\Expression(
77
            new Node\Expr\Assign(
78
                Expressions::resolvePropertyFetch('this', $this->resolverProperty),
79
                new Node\Expr\Clone_(Expressions::resolvePropertyFetch('this', $this->resolverProperty))
80
            )
81
        );
82
    }
83
84
    /**
85
     * @param Node\Stmt\ClassMethod $method
86
     * @return bool
87
     */
88
    private function hasReturnStmt(Node\Stmt\ClassMethod $method): bool
89
    {
90
        if ($method->returnType === 'void') {
91
            return false;
92
        }
93
94
        if ($method->returnType === null) {
95
            return $this->findReturnStmt($method);
96
        }
97
98
        if ($method->returnType instanceof Node\NullableType) {
99
            return true;
100
        }
101
102
        return $method->returnType instanceof Node\Identifier && $method->returnType->name !== 'void';
103
    }
104
105
    /**
106
     * @param Node\Stmt|Node\Stmt\ClassMethod $node
107
     * @return bool
108
     */
109
    private function findReturnStmt(Node\Stmt $node): bool
110
    {
111
        if (!property_exists($node, 'stmts') || !is_array($node->stmts)) {
112
            return false;
113
        }
114
115
        foreach ($node->stmts as $stmt) {
116
            if ($stmt instanceof Node\Stmt\Return_) {
117
                return true;
118
            }
119
120
            if ($this->findReturnStmt($stmt) === true) {
121
                return true;
122
            }
123
        }
124
125
        return false;
126
    }
127
128
    /**
129
     * @param Node\Stmt\ClassMethod $method
130
     * @return Node\Stmt\ClassMethod
131
     */
132 View Code Duplication
    private function modifyReturnMethod(Node\Stmt\ClassMethod $method): Node\Stmt\ClassMethod
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
133
    {
134
        $method->setDocComment(PHPDoc::writeInheritdoc());
135
136
        $resolved = Expressions::resolveMethodCall('this', $this->resolverProperty, $this->resolveMethod);
137
        $stmt = new Node\Stmt\Return_(new Node\Expr\MethodCall(
138
            $resolved,
139
            $method->name->name,
140
            $this->packMethodArgs($method)
141
        ));
142
143
        $method->stmts = [Expressions::throwExceptionOnNull($resolved, $stmt)];
144
145
        return $method;
146
    }
147
148
    /**
149
     * @param Node\Stmt\ClassMethod $method
150
     * @return Node\Stmt\ClassMethod
151
     */
152 View Code Duplication
    private function modifyExprMethod(Node\Stmt\ClassMethod $method): Node\Stmt\ClassMethod
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
153
    {
154
        $method->setDocComment(PHPDoc::writeInheritdoc());
155
156
        $resolved = Expressions::resolveMethodCall('this', $this->resolverProperty, $this->resolveMethod);
157
        $stmt = new Node\Stmt\Expression(
158
            new Node\Expr\MethodCall($resolved, $method->name->name, $this->packMethodArgs($method))
159
        );
160
161
        $method->stmts = [Expressions::throwExceptionOnNull($resolved, $stmt)];
162
163
        return $method;
164
    }
165
166
    /**
167
     * @param Node\Stmt\ClassMethod $method
168
     * @return array
169
     */
170
    private function packMethodArgs(Node\Stmt\ClassMethod $method): array
171
    {
172
        $args = [];
173
        /** @var \PhpParser\Node\Param $param */
174
        foreach ($method->getParams() as $param) {
175
            $args[] = (new Param($param->var->name))->getNode();
176
        }
177
178
        return $args;
179
    }
180
}
181