AddProxiedMethods::findReturnStmt()   A
last analyzed

Complexity

Conditions 6
Paths 5

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.0444
c 0
b 0
f 0
cc 6
nc 5
nop 1
1
<?php
2
3
/**
4
 * Spiral Framework. Cycle ProxyFactory
5
 *
6
 * @license MIT
7
 * @author  Valentin V (Vvval)
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\ORM\Promise\Visitor;
13
14
use Cycle\ORM\Promise\PHPDoc;
15
use PhpParser\Builder\Param;
16
use PhpParser\Node;
17
use PhpParser\NodeVisitorAbstract;
18
19
use function Cycle\ORM\Promise\exprClone;
20
use function Cycle\ORM\Promise\resolveMethodCall;
21
use function Cycle\ORM\Promise\throwExceptionOnNull;
22
23
/**
24
 * Add parent call via proxy resolver
25
 */
26
final class AddProxiedMethods extends NodeVisitorAbstract
27
{
28
    /** @var string */
29
    private $class;
30
31
    /** @var string */
32
    private $resolverProperty;
33
34
    /** @var Node\Stmt\ClassMethod[] */
35
    private $methods;
36
37
    /** @var string */
38
    private $resolveMethod;
39
40
    /**
41
     * @param string $class
42
     * @param string $property
43
     * @param array  $methods
44
     * @param string $resolveMethod
45
     */
46
    public function __construct(string $class, string $property, array $methods, string $resolveMethod)
47
    {
48
        $this->class = $class;
49
        $this->resolverProperty = $property;
50
        $this->methods = $methods;
51
        $this->resolveMethod = $resolveMethod;
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57
    public function leaveNode(Node $node)
58
    {
59
        if (!$node instanceof Node\Stmt\Class_) {
60
            return null;
61
        }
62
63
        foreach ($this->methods as $method) {
64
            if ($method->name->name === '__clone') {
65
                $method->stmts = [exprClone($this->resolverProperty)];
66
                $node->stmts[] = $method;
67
            } else {
68
                $node->stmts[] = $this->modifyMethodMethod(
69
                    $method,
70
                    $this->hasReturnStmt($method) ? Node\Stmt\Return_::class : Node\Stmt\Expression::class
71
                );
72
            }
73
        }
74
75
        return $node;
76
    }
77
78
    /**
79
     * @param Node\Stmt\ClassMethod $method
80
     * @return bool
81
     */
82
    private function hasReturnStmt(Node\Stmt\ClassMethod $method): bool
83
    {
84
        if ($method->returnType === 'void') {
85
            return false;
86
        }
87
88
        if ($method->returnType === null) {
89
            return $this->findReturnStmt($method);
90
        }
91
92
        if ($method->returnType instanceof Node\NullableType) {
93
            return true;
94
        }
95
96
        return $method->returnType instanceof Node\Identifier && $method->returnType->name !== 'void';
97
    }
98
99
    /**
100
     * @param Node\Stmt|Node\Stmt\ClassMethod $node
101
     * @return bool
102
     */
103
    private function findReturnStmt(Node\Stmt $node): bool
104
    {
105
        if (!property_exists($node, 'stmts') || !is_array($node->stmts)) {
106
            return false;
107
        }
108
109
        foreach ($node->stmts as $stmt) {
110
            if ($stmt instanceof Node\Stmt\Return_) {
111
                return $stmt->expr !== null;
112
            }
113
114
            if ($this->findReturnStmt($stmt) === true) {
115
                return true;
116
            }
117
        }
118
119
        return false;
120
    }
121
122
    /**
123
     * @param Node\Stmt\ClassMethod $method
124
     * @param string                $stmtWrapper
125
     * @return Node\Stmt\ClassMethod
126
     */
127
    private function modifyMethodMethod(Node\Stmt\ClassMethod $method, string $stmtWrapper): Node\Stmt\ClassMethod
128
    {
129
        $resolved = resolveMethodCall('this', $this->resolverProperty, $this->resolveMethod);
130
        $methodCall = new Node\Expr\MethodCall($resolved, $method->name->name, $this->packMethodArgs($method));
131
132
        $method->setDocComment(PHPDoc::writeInheritdoc());
133
        $method->stmts = [
134
            throwExceptionOnNull(
135
                $resolved,
136
                new $stmtWrapper($methodCall),
137
                'Method `%s()` not loaded for `%s`',
138
                [
139
                    $method->name->name,
140
                    $this->class
141
                ]
142
            )
143
        ];
144
145
        return $method;
146
    }
147
148
    /**
149
     * @param Node\Stmt\ClassMethod $method
150
     * @return array
151
     */
152
    private function packMethodArgs(Node\Stmt\ClassMethod $method): array
153
    {
154
        $args = [];
155
        /** @var Node\Param $param */
156
        foreach ($method->getParams() as $param) {
157
            $args[] = (new Param($param->var->name))->getNode();
158
        }
159
160
        return $args;
161
    }
162
}
163