Passed
Push — master ( 57dad7...6a59ef )
by Valentin
02:00
created

Printer::make()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 55
rs 8.9818
c 0
b 0
f 0
cc 4
nc 5
nop 3

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
8
use PhpParser\Lexer;
9
use PhpParser\Node;
10
use PhpParser\Parser;
11
use PhpParser\PrettyPrinter\Standard;
12
use PhpParser\PrettyPrinterAbstract;
13
14
class Printer
15
{
16
    private const RESOLVER_PROPERTY      = '__resolver';
17
    private const UNSET_PROPERTIES_CONST = 'UNSET_PROPERTIES';
18
    private const INIT_METHOD            = '__init';
19
20
    private const LOADED_METHOD  = '__loaded';
21
    private const ROLE_METHOD    = '__role';
22
    private const SCOPE_METHOD   = '__scope';
23
    private const RESOLVE_METHOD = '__resolve';
24
25
    private const DEPENDENCIES = [
26
        'orm'   => ORMInterface::class,
27
        'role'  => 'string',
28
        'scope' => 'array'
29
    ];
30
31
    private const USE_STMTS = [
32
        PromiseInterface::class,
33
        PromiseResolver::class,
34
        PromiseException::class,
35
        ORMInterface::class
36
    ];
37
38
    private const PROMISE_METHODS = [
39
        self::LOADED_METHOD  => 'bool',
40
        self::ROLE_METHOD    => 'string',
41
        self::SCOPE_METHOD   => 'array',
42
        self::RESOLVE_METHOD => null,
43
    ];
44
45
    /** @var ConflictResolver */
46
    private $resolver;
47
48
    /** @var Traverser */
49
    private $traverser;
50
51
    /** @var Declaration\Extractor */
52
    private $extractor;
53
54
    /** @var Lexer */
55
    private $lexer;
56
57
    /** @var Parser */
58
    private $parser;
59
60
    /** @var PrettyPrinterAbstract */
61
    private $printer;
62
63
    /** @var Stubs */
64
    private $stubs;
65
66
    public function __construct(ConflictResolver $resolver, Traverser $traverser, Declaration\Extractor $extractor, Stubs $stubs)
67
    {
68
        $this->resolver = $resolver;
69
        $this->traverser = $traverser;
70
        $this->extractor = $extractor;
71
72
        $lexer = new Lexer\Emulative([
73
            'usedAttributes' => [
74
                'comments',
75
                'startLine',
76
                'endLine',
77
                'startTokenPos',
78
                'endTokenPos',
79
            ],
80
        ]);
81
82
        $this->lexer = $lexer;
83
        $this->parser = new Parser\Php7($this->lexer);
84
85
        $this->printer = new Standard();
86
        $this->stubs = $stubs;
87
    }
88
89
    /**
90
     * @param \ReflectionClass                 $reflection
91
     * @param Declaration\DeclarationInterface $class
92
     * @param Declaration\DeclarationInterface $parent
93
     *
94
     * @return string
95
     * @throws \Cycle\ORM\Promise\ProxyFactoryException
96
     */
97
    public function make(\ReflectionClass $reflection, Declaration\DeclarationInterface $class, Declaration\DeclarationInterface $parent): string
98
    {
99
        $structure = $this->extractor->extract($reflection);
100
        foreach ($structure->methodNames() as $name) {
101
            if (array_key_exists($name, self::PROMISE_METHODS)) {
102
                throw new ProxyFactoryException("Promise method `$name` already defined.");
103
            }
104
        }
105
106
        $property = $this->resolverPropertyName($structure);
107
        $unsetPropertiesConst = $this->unsetPropertiesConstName($structure);
108
109
        $visitors = [
110
            new Visitor\AddUseStmts($this->useStmts($class, $parent)),
111
            new Visitor\UpdateNamespace($class->getNamespaceName()),
112
            new Visitor\DeclareClass($class->getShortName(), $parent->getShortName(), Utils::shortName(PromiseInterface::class)),
113
            new Visitor\AddUnsetPropertiesConst($unsetPropertiesConst, $structure->properties),
114
            new Visitor\AddResolverProperty($property, $this->propertyType(), $parent->getShortName()),
115
            new Visitor\AddInitMethod(
116
                $property,
117
                $this->propertyType(),
118
                self::DEPENDENCIES,
119
                $this->unsetPropertiesConstName($structure),
120
                $this->initMethodName($structure)
121
            ),
122
            new Visitor\AddMagicCloneMethod($property, $structure->hasClone),
123
            new Visitor\AddMagicGetMethod($property, self::RESOLVE_METHOD),
124
            new Visitor\AddMagicSetMethod($property, self::RESOLVE_METHOD),
125
            new Visitor\AddMagicIssetMethod($property, self::RESOLVE_METHOD, $unsetPropertiesConst),
126
            new Visitor\AddMagicUnset($property, self::RESOLVE_METHOD, $unsetPropertiesConst),
127
            new Visitor\AddMagicDebugInfoMethod(
128
                $property,
129
                self::RESOLVE_METHOD,
130
                self::LOADED_METHOD,
131
                self::ROLE_METHOD,
132
                self::SCOPE_METHOD,
133
                $structure->properties
134
            ),
135
            new Visitor\UpdatePromiseMethods($property),
136
            new Visitor\AddProxiedMethods($property, $structure->methods, self::RESOLVE_METHOD),
137
        ];
138
139
        foreach (self::PROMISE_METHODS as $method => $returnType) {
140
            $visitors[] = new Visitor\AddPromiseMethod($property, $method, $returnType);
141
        }
142
143
        $nodes = $this->getNodesFromStub();
144
        $output = $this->traverser->traverseClonedNodes($nodes, ...$visitors);
145
146
        return $this->printer->printFormatPreserving(
147
            $output,
148
            $nodes,
149
            $this->lexer->getTokens()
150
        );
151
    }
152
153
    public function initMethodName(Declaration\Structure $structure): string
154
    {
155
        return $this->resolver->resolve($structure->methodNames(), self::INIT_METHOD)->fullName();
156
    }
157
158
    private function resolverPropertyName(Declaration\Structure $structure): string
159
    {
160
        return $this->resolver->resolve($structure->properties, self::RESOLVER_PROPERTY)->fullName();
161
    }
162
163
    private function unsetPropertiesConstName(Declaration\Structure $structure): string
164
    {
165
        return $this->resolver->resolve($structure->constants, self::UNSET_PROPERTIES_CONST)->fullName('_');
166
    }
167
168
    private function useStmts(Declaration\DeclarationInterface $class, Declaration\DeclarationInterface $parent): array
169
    {
170
        $useStmts = self::USE_STMTS;
171
        if ($class->getNamespaceName() !== $parent->getNamespaceName()) {
172
            $useStmts[] = $parent->getFullName();
173
        }
174
175
        return $useStmts;
176
    }
177
178
    private function propertyType(): string
179
    {
180
        return Utils::shortName(PromiseResolver::class);
181
    }
182
183
    /**
184
     * @return Node\Stmt[]
185
     */
186
    private function getNodesFromStub(): array
187
    {
188
        return $this->parser->parse($this->stubs->getContent()) ?? [];
189
    }
190
}