CallInitializer::__construct()   B
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 58

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 58
c 0
b 0
f 0
ccs 21
cts 21
cp 1
rs 8.9163
cc 1
nc 1
nop 3
crap 1

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
3
declare(strict_types=1);
4
5
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
6
7
use Laminas\Code\Generator\ParameterGenerator;
8
use Laminas\Code\Generator\PropertyGenerator;
9
use ProxyManager\Generator\MethodGenerator;
10
use ProxyManager\Generator\Util\IdentifierSuffixer;
11
use ProxyManager\ProxyGenerator\Util\Properties;
12
use ReflectionProperty;
13
use function array_map;
14
use function implode;
15
use function sprintf;
16
use function str_replace;
17
use function var_export;
18
19
/**
20
 * Implementation for {@see \ProxyManager\Proxy\LazyLoadingInterface::isProxyInitialized}
21
 * for lazy loading value holder objects
22
 */
23
class CallInitializer extends MethodGenerator
24
{
25
    public function __construct(
26
        PropertyGenerator $initializerProperty,
27
        PropertyGenerator $initTracker,
28
        Properties $properties
29
    ) {
30 1
        $docBlock = <<<'DOCBLOCK'
31
Triggers initialization logic for this ghost object
32
33
@param string  $methodName
34
@param mixed[] $parameters
35
36 1
@return mixed
37
DOCBLOCK;
38
39
        parent::__construct(
40
            IdentifierSuffixer::getIdentifier('callInitializer'),
41
            [
42
                new ParameterGenerator('methodName'),
43
                new ParameterGenerator('parameters', 'array'),
44 1
            ],
45 1
            self::FLAG_PRIVATE,
46
            null,
47 1
            $docBlock
48 1
        );
49
50 1
        $initializer    = $initializerProperty->getName();
51 1
        $initialization = $initTracker->getName();
52 1
53
        $bodyTemplate = <<<'PHP'
54
if ($this->%s || ! $this->%s) {
55 1
    return;
56 1
}
57
58
$this->%s = true;
59 1
60
%s
61
%s
62
63
$result = $this->%s->__invoke($this, $methodName, $parameters, $this->%s, $properties);
64
$this->%s = false;
65
66
return $result;
67
PHP;
68
69
        $referenceableProperties = $properties->onlyPropertiesThatCanBeUnset();
70
71
        $this->setBody(sprintf(
72
            $bodyTemplate,
73
            $initialization,
74 1
            $initializer,
75 1
            $initialization,
76 1
            $this->propertiesInitializationCode($referenceableProperties),
77 1
            $this->propertiesReferenceArrayCode($referenceableProperties),
78 1
            $initializer,
79 1
            $initializer,
80 1
            $initialization
81 1
        ));
82 1
    }
83 1
84
    private function propertiesInitializationCode(Properties $properties) : string
85 1
    {
86
        $assignments = [];
87 1
88
        foreach ($properties->getAccessibleProperties() as $property) {
89 1
            $assignments[] = '$this->'
90
                . $property->getName()
91 1
                . ' = ' . $this->getExportedPropertyDefaultValue($property)
92 1
                . ';';
93 1
        }
94 1
95 1
        foreach ($properties->getGroupedPrivateProperties() as $className => $privateProperties) {
96
            $cacheKey      = 'cache' . str_replace('\\', '_', $className);
97
            $assignments[] = 'static $' . $cacheKey . ";\n\n"
98 1
                . '$' . $cacheKey . ' ?: $' . $cacheKey . " = \\Closure::bind(static function (\$instance) {\n"
99 1
                . $this->getPropertyDefaultsAssignments($privateProperties) . "\n"
100 1
                . '}, null, ' . var_export($className, true) . ");\n\n"
101 1
                . '$' . $cacheKey . "(\$this);\n\n";
102 1
        }
103 1
104 1
        return implode("\n", $assignments) . "\n\n";
105
    }
106
107 1
    /**
108
     * @param ReflectionProperty[] $properties
109
     */
110
    private function getPropertyDefaultsAssignments(array $properties) : string
111
    {
112
        return implode(
113 1
            "\n",
114
            array_map(
115 1
                function (ReflectionProperty $property) : string {
116 1
                    return '    $instance->' . $property->getName()
117 1
                        . ' = ' . $this->getExportedPropertyDefaultValue($property) . ';';
118
                },
119 1
                $properties
120 1
            )
121 1
        );
122 1
    }
123
124
    private function propertiesReferenceArrayCode(Properties $properties) : string
125
    {
126
        $assignments = [];
127 1
128
        foreach ($properties->getAccessibleProperties() as $propertyInternalName => $property) {
129 1
            $assignments[] = '    '
130
                . var_export($propertyInternalName, true) . ' => & $this->' . $property->getName()
131 1
                . ',';
132 1
        }
133 1
134 1
        $code = "\$properties = [\n" . implode("\n", $assignments) . "\n];\n\n";
135
136
        // must use assignments, as direct reference during array definition causes a fatal error (not sure why)
137 1
        foreach ($properties->getGroupedPrivateProperties() as $className => $classPrivateProperties) {
138
            $cacheKey = 'cacheFetch' . str_replace('\\', '_', $className);
139
140 1
            $code .= 'static $' . $cacheKey . ";\n\n"
141 1
                . '$' . $cacheKey . ' ?: $' . $cacheKey
142
                . " = \\Closure::bind(function (\$instance, array & \$properties) {\n"
143 1
                . $this->generatePrivatePropertiesAssignmentsCode($classPrivateProperties)
144 1
                . '}, $this, ' . var_export($className, true) . ");\n\n"
145 1
                . '$' . $cacheKey . '($this, $properties);';
146 1
        }
147 1
148 1
        return $code;
149
    }
150
151 1
    /**
152
     * @param array<string, ReflectionProperty> $properties indexed by internal name
153
     */
154
    private function generatePrivatePropertiesAssignmentsCode(array $properties) : string
155
    {
156
        $code = '';
157
158 1
        foreach ($properties as $property) {
159
            $key   = "\0" . $property->getDeclaringClass()->getName() . "\0" . $property->getName();
160 1
            $code .= '    $properties[' . var_export($key, true) . '] = '
161
                . '& $instance->' . $property->getName() . ";\n";
162 1
        }
163 1
164 1
        return $code;
165 1
    }
166
167
    private function getExportedPropertyDefaultValue(ReflectionProperty $property) : string
168 1
    {
169
        $name     = $property->getName();
170
        $defaults = $property->getDeclaringClass()->getDefaultProperties();
171 1
172
        return var_export($defaults[$name] ?? null, true);
173 1
    }
174
}
175