Passed
Push — master ( f5649d...9c809e )
by Valentin
02:10
created

Printer::unsetPropertiesConstName()   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
/**
3
 * Spiral Framework.
4
 *
5
 * @license MIT
6
 * @author  Valentin V (Vvval)
7
 */
8
declare(strict_types=1);
9
10
namespace Cycle\ORM\Promise;
11
12
use Cycle\ORM\Promise\Declaration;
13
use PhpParser\Lexer;
14
use PhpParser\Node;
15
use PhpParser\Parser;
16
use PhpParser\PrettyPrinter\Standard;
17
use PhpParser\PrettyPrinterAbstract;
18
19
final class Printer
20
{
21
22
    private const LOADED_METHOD  = '__loaded';
23
    private const ROLE_METHOD    = '__role';
24
    private const SCOPE_METHOD   = '__scope';
25
    private const RESOLVE_METHOD = '__resolve';
26
27
    private const PROMISE_METHODS = [
28
        self::LOADED_METHOD  => 'bool',
29
        self::ROLE_METHOD    => 'string',
30
        self::SCOPE_METHOD   => 'array',
31
        self::RESOLVE_METHOD => null,
32
    ];
33
34
    /** @var ConflictResolver */
35
    private $resolver;
36
37
    /** @var Traverser */
38
    private $traverser;
39
40
    /** @var Declaration\Extractor */
41
    private $extractor;
42
43
    /** @var Lexer */
44
    private $lexer;
45
46
    /** @var Parser */
47
    private $parser;
48
49
    /** @var PrettyPrinterAbstract */
50
    private $printer;
51
52
    /** @var Stubs */
53
    private $stubs;
54
55
    /** @var Schema */
56
    private $schema;
57
58
    public function __construct(ConflictResolver $resolver, Traverser $traverser, Declaration\Extractor $extractor, Stubs $stubs, Schema $schema)
59
    {
60
        $this->resolver = $resolver;
61
        $this->traverser = $traverser;
62
        $this->extractor = $extractor;
63
64
        $lexer = new Lexer\Emulative([
65
            'usedAttributes' => [
66
                'comments',
67
                'startLine',
68
                'endLine',
69
                'startTokenPos',
70
                'endTokenPos',
71
            ],
72
        ]);
73
74
        $this->lexer = $lexer;
75
        $this->parser = new Parser\Php7($this->lexer);
76
77
        $this->printer = new Standard();
78
        $this->stubs = $stubs;
79
        $this->schema = $schema;
80
    }
81
82
    /**
83
     * @param \ReflectionClass                 $reflection
84
     * @param Declaration\DeclarationInterface $class
85
     * @param Declaration\DeclarationInterface $parent
86
     * @return string
87
     *
88
     * @throws \Cycle\ORM\Promise\ProxyFactoryException
89
     */
90
    public function make(\ReflectionClass $reflection, Declaration\DeclarationInterface $class, Declaration\DeclarationInterface $parent): string
91
    {
92
        $structure = $this->extractor->extract($reflection);
93
        foreach ($structure->methodNames() as $name) {
94
            if (array_key_exists($name, self::PROMISE_METHODS)) {
95
                throw new ProxyFactoryException("Promise method `$name` already defined.");
96
            }
97
        }
98
99
        $property = $this->schema->resolverPropertyName($structure);
100
        $unsetPropertiesConst = $this->schema->unsetPropertiesConstName($structure);
101
102
        $visitors = [
103
            new Visitor\AddUseStmts($this->schema->useStmts($class, $parent)),
104
            new Visitor\UpdateNamespace($class->getNamespaceName()),
105
            new Visitor\DeclareClass($class->getShortName(), $parent->getShortName(), Utils::shortName(PromiseInterface::class)),
106
            new Visitor\AddUnsetPropertiesConst($unsetPropertiesConst, $structure->properties),
107
            new Visitor\AddResolverProperty($property, $this->schema->propertyType(), $parent->getShortName()),
108
            new Visitor\AddInitMethod(
109
                $property,
110
                $this->schema->propertyType(),
111
                Schema::INIT_DEPENDENCIES,
112
                $this->schema->unsetPropertiesConstName($structure),
113
                $this->schema->initMethodName($structure)
114
            ),
115
            new Visitor\AddMagicCloneMethod($property, $structure->hasClone),
116
            new Visitor\AddMagicGetMethod($property, self::RESOLVE_METHOD),
117
            new Visitor\AddMagicSetMethod($property, self::RESOLVE_METHOD),
118
            new Visitor\AddMagicIssetMethod($property, self::RESOLVE_METHOD, $unsetPropertiesConst),
119
            new Visitor\AddMagicUnset($property, self::RESOLVE_METHOD, $unsetPropertiesConst),
120
            new Visitor\AddMagicDebugInfoMethod(
121
                $property,
122
                self::RESOLVE_METHOD,
123
                self::LOADED_METHOD,
124
                self::ROLE_METHOD,
125
                self::SCOPE_METHOD,
126
                $structure->properties
127
            ),
128
            new Visitor\UpdatePromiseMethods($property),
129
            new Visitor\AddProxiedMethods($property, $structure->methods, self::RESOLVE_METHOD),
130
        ];
131
132
        foreach (self::PROMISE_METHODS as $method => $returnType) {
133
            $visitors[] = new Visitor\AddPromiseMethod($property, $method, $returnType);
134
        }
135
136
        $nodes = $this->getNodesFromStub();
137
        $output = $this->traverser->traverseClonedNodes($nodes, ...$visitors);
138
139
        return $this->printer->printFormatPreserving(
140
            $output,
141
            $nodes,
142
            $this->lexer->getTokens()
143
        );
144
    }
145
146
    /**
147
     * @return Node\Stmt[]
148
     */
149
    private function getNodesFromStub(): array
150
    {
151
        return $this->parser->parse($this->stubs->getContent()) ?? [];
152
    }
153
}