Passed
Push — master ( 327a11...1a5769 )
by Valentin
58s
created

ProxyPrinter::propertyName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Cycle\ORM\Promise;
5
6
use Cycle\ORM\ORMInterface;
7
use Cycle\ORM\Promise\Declaration\DeclarationInterface;
8
use Cycle\ORM\Promise\Declaration\Extractor;
9
use Cycle\ORM\Promise\Declaration\Structure;
10
use Cycle\ORM\Promise\Visitor\AddMagicClone;
11
use Cycle\ORM\Promise\Visitor\AddMagicIsset;
12
use Cycle\ORM\Promise\Visitor\AddMagicGet;
13
use Cycle\ORM\Promise\Visitor\AddMagicSet;
14
use Cycle\ORM\Promise\Visitor\AddMagicUnset;
15
use PhpParser\Lexer;
16
use PhpParser\Node;
17
use PhpParser\Parser;
18
use PhpParser\PrettyPrinter\Standard;
19
use PhpParser\PrettyPrinterAbstract;
20
21
class ProxyPrinter
22
{
23
    private const RESOLVER_PROPERTY = '__resolver';
24
    private const UNSET_PROPERTIES  = 'UNSET_PROPERTIES';
25
    private const RESOLVE_METHOD    = '__resolve';
26
27
    private const DEPENDENCIES = [
28
        'orm'   => ORMInterface::class,
29
        'role'  => 'string',
30
        'scope' => 'array'
31
    ];
32
33
    /** @var ConflictResolver */
34
    private $resolver;
35
36
    /** @var Traverser */
37
    private $traverser;
38
39
    /** @var Extractor */
40
    private $extractor;
41
42
    /** @var Lexer */
43
    private $lexer;
44
45
    /** @var Parser */
46
    private $parser;
47
48
    /** @var PrettyPrinterAbstract */
49
    private $printer;
50
51
    /** @var Stubs */
52
    private $stubs;
53
54
    public function __construct(ConflictResolver $resolver, Traverser $traverser, Extractor $extractor, Stubs $stubs)
55
    {
56
        $this->resolver = $resolver;
57
        $this->traverser = $traverser;
58
        $this->extractor = $extractor;
59
60
        $lexer = new Lexer\Emulative([
61
            'usedAttributes' => [
62
                'comments',
63
                'startLine',
64
                'endLine',
65
                'startTokenPos',
66
                'endTokenPos',
67
            ],
68
        ]);
69
70
        $this->lexer = $lexer;
71
        $this->parser = new Parser\Php7($this->lexer);
72
73
        $this->printer = new Standard();
74
        $this->stubs = $stubs;
75
    }
76
77
    public function make(\ReflectionClass $reflection, DeclarationInterface $class, DeclarationInterface $parent): string
78
    {
79
        $structure = $this->extractor->extract($reflection);
80
81
        $property = $this->resolverPropertyName($structure);
82
        $unsetPropertiesConst = $this->unsetPropertiesConstName($structure);
83
84
        $visitors = [
85
            new Visitor\AddUseStmts($this->useStmts($class, $parent)),
86
            new Visitor\UpdateNamespace($class->getNamespaceName()),
87
            new Visitor\DeclareClass($class->getShortName(), $parent->getShortName()),
88
            new Visitor\AddUnsetPropertiesConst($unsetPropertiesConst, $structure->properties),
89
            new Visitor\AddResolverProperty($property, $this->propertyType(), $parent->getShortName()),
90
            new Visitor\UpdateConstructor(
91
                $structure->hasConstructor,
92
                $property,
93
                $this->propertyType(),
94
                self::DEPENDENCIES,
95
                $this->unsetPropertiesConstName($structure)
96
            ),
97
            new AddMagicClone($property, $structure->hasClone),
98
            new AddMagicGet($property, self::RESOLVE_METHOD),
99
            new AddMagicSet($property, self::RESOLVE_METHOD),
100
            new AddMagicIsset($property, self::RESOLVE_METHOD, $unsetPropertiesConst),
101
            new AddMagicUnset($property, self::RESOLVE_METHOD, $unsetPropertiesConst),
102
            new Visitor\UpdatePromiseMethods($property),
103
            new Visitor\AddProxiedMethods($property, $structure->methods, self::RESOLVE_METHOD),
104
        ];
105
106
        $nodes = $this->getNodesFromStub();
107
        $output = $this->traverser->traverseClonedNodes($nodes, ...$visitors);
108
109
        return $this->printer->printFormatPreserving(
110
            $output,
111
            $nodes,
112
            $this->lexer->getTokens()
113
        );
114
    }
115
116
    private function resolverPropertyName(Structure $structure): string
117
    {
118
        return $this->resolver->resolve($structure->properties, self::RESOLVER_PROPERTY)->fullName();
119
    }
120
121
    private function unsetPropertiesConstName(Structure $structure): string
122
    {
123
        return $this->resolver->resolve($structure->constants, self::UNSET_PROPERTIES)->fullName('_');
124
    }
125
126
    private function useStmts(DeclarationInterface $class, DeclarationInterface $parent): array
127
    {
128
        $useStmts = [];
129
        if ($class->getNamespaceName() !== $parent->getNamespaceName()) {
130
            $useStmts[] = $parent->getFullName();
131
        }
132
133
        $useStmts[] = PromiseResolver::class;
134
135
        return $useStmts;
136
    }
137
138
    private function propertyType(): string
139
    {
140
        return Utils::shortName(PromiseResolver::class);
141
    }
142
143
    /**
144
     * @return Node\Stmt[]
145
     */
146
    private function getNodesFromStub(): array
147
    {
148
        return $this->parser->parse($this->stubs->getContent()) ?? [];
149
    }
150
}