Completed
Branch master (f2a445)
by Valentin
01:50
created

AddProxiedMethods::modifyMethodMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 1
nc 1
nop 2
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 === '__clone') {
56
                $method->stmts = [Expressions::buildCloneExpression($this->resolverProperty)];
57
                $node->stmts[] = $method;
58
            } else {
59
                $node->stmts[] = $this->modifyMethodMethod(
60
                    $method,
61
                    $this->hasReturnStmt($method) ? Node\Stmt\Return_::class : Node\Stmt\Expression::class
62
                );
63
            }
64
        }
65
66
        return $node;
67
    }
68
69
    /**
70
     * @param Node\Stmt\ClassMethod $method
71
     * @return bool
72
     */
73
    private function hasReturnStmt(Node\Stmt\ClassMethod $method): bool
74
    {
75
        if ($method->returnType === 'void') {
76
            return false;
77
        }
78
79
        if ($method->returnType === null) {
80
            return $this->findReturnStmt($method);
81
        }
82
83
        if ($method->returnType instanceof Node\NullableType) {
84
            return true;
85
        }
86
87
        return $method->returnType instanceof Node\Identifier && $method->returnType->name !== 'void';
88
    }
89
90
    /**
91
     * @param Node\Stmt|Node\Stmt\ClassMethod $node
92
     * @return bool
93
     */
94
    private function findReturnStmt(Node\Stmt $node): bool
95
    {
96
        if (!property_exists($node, 'stmts') || !is_array($node->stmts)) {
97
            return false;
98
        }
99
100
        foreach ($node->stmts as $stmt) {
101
            if ($stmt instanceof Node\Stmt\Return_) {
102
                return true;
103
            }
104
105
            if ($this->findReturnStmt($stmt) === true) {
106
                return true;
107
            }
108
        }
109
110
        return false;
111
    }
112
113
    /**
114
     * @param Node\Stmt\ClassMethod $method
115
     * @param string                $stmtWrapper
116
     * @return Node\Stmt\ClassMethod
117
     */
118
    private function modifyMethodMethod(Node\Stmt\ClassMethod $method, string $stmtWrapper): Node\Stmt\ClassMethod
119
    {
120
        $resolved = Expressions::resolveMethodCall('this', $this->resolverProperty, $this->resolveMethod);
121
        $methodCall = new Node\Expr\MethodCall($resolved, $method->name->name, $this->packMethodArgs($method));
122
123
        $method->setDocComment(PHPDoc::writeInheritdoc());
124
        $method->stmts = [Expressions::throwExceptionOnNull($resolved, new $stmtWrapper($methodCall))];
125
126
        return $method;
127
    }
128
129
    /**
130
     * @param Node\Stmt\ClassMethod $method
131
     * @return array
132
     */
133
    private function packMethodArgs(Node\Stmt\ClassMethod $method): array
134
    {
135
        $args = [];
136
        /** @var \PhpParser\Node\Param $param */
137
        foreach ($method->getParams() as $param) {
138
            $args[] = (new Param($param->var->name))->getNode();
139
        }
140
141
        return $args;
142
    }
143
}
144