Completed
Push — master ( 3be072...ba2d3a )
by Marco
231:32 queued 209:55
created

MagicIsset   A

Complexity

Total Complexity 2

Size/Duplication

Total Lines 98
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 98
c 0
b 0
f 0
wmc 2
lcom 0
cbo 2
ccs 18
cts 18
cp 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ProxyManager\ProxyGenerator\LazyLoadingGhost\MethodGenerator;
6
7
use InvalidArgumentException;
8
use ProxyManager\Generator\MagicMethodGenerator;
9
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\PrivatePropertiesMap;
10
use ProxyManager\ProxyGenerator\LazyLoadingGhost\PropertyGenerator\ProtectedPropertiesMap;
11
use ProxyManager\ProxyGenerator\PropertyGenerator\PublicPropertiesMap;
12
use ProxyManager\ProxyGenerator\Util\PublicScopeSimulator;
13
use ReflectionClass;
14
use Zend\Code\Generator\MethodGenerator;
15
use Zend\Code\Generator\ParameterGenerator;
16
use Zend\Code\Generator\PropertyGenerator;
17
use function sprintf;
18
19
/**
20
 * Magic `__isset` method for lazy loading ghost objects
21
 */
22
class MagicIsset extends MagicMethodGenerator
23
{
24
    private string $callParentTemplate = <<<'PHP'
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_STRING, expecting T_FUNCTION or T_CONST
Loading history...
25
%s
26
27
if (isset(self::$%s[$name])) {
28
    return isset($this->$name);
29
}
30
31
if (isset(self::$%s[$name])) {
32
    // check protected property access via compatible class
33
    $callers      = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
34
    $caller       = isset($callers[1]) ? $callers[1] : [];
35
    $object       = isset($caller['object']) ? $caller['object'] : '';
36
    $expectedType = self::$%s[$name];
37
38
    if ($object instanceof $expectedType) {
39
        return isset($this->$name);
40
    }
41
42
    $class = isset($caller['class']) ? $caller['class'] : '';
43
44
    if ($class === $expectedType || is_subclass_of($class, $expectedType)) {
45
        return isset($this->$name);
46
    }
47
} else {
48
    // check private property access via same class
49
    $callers = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
50
    $caller  = isset($callers[1]) ? $callers[1] : [];
51
    $class   = isset($caller['class']) ? $caller['class'] : '';
52
53
    static $accessorCache = [];
54
55
    if (isset(self::$%s[$name][$class])) {
56
        $cacheKey = $class . '#' . $name;
57
        $accessor = isset($accessorCache[$cacheKey])
58
            ? $accessorCache[$cacheKey]
59
            : $accessorCache[$cacheKey] = \Closure::bind(function ($instance) use ($name) {
60
                return isset($instance->$name);
61
            }, null, $class);
62
63
        return $accessor($this);
64
    }
65
66
    if ('ReflectionProperty' === $class) {
67
        $tmpClass = key(self::$%s[$name]);
68
        $cacheKey = $tmpClass . '#' . $name;
69
        $accessor = isset($accessorCache[$cacheKey])
70
            ? $accessorCache[$cacheKey]
71
            : $accessorCache[$cacheKey] = \Closure::bind(function ($instance) use ($name) {
72
                return isset($instance->$name);
73
            }, null, $tmpClass);
74
75
        return $accessor($this);
76
    }
77
}
78
79
%s
80
PHP;
81
82
    /**
83
     * @throws InvalidArgumentException
84
     */
85
    public function __construct(
86
        ReflectionClass $originalClass,
87
        PropertyGenerator $initializerProperty,
88
        MethodGenerator $callInitializer,
89 2
        PublicPropertiesMap $publicProperties,
90
        ProtectedPropertiesMap $protectedProperties,
91
        PrivatePropertiesMap $privateProperties
92
    ) {
93
        parent::__construct($originalClass, '__isset', [new ParameterGenerator('name')]);
94
95
        $override = $originalClass->hasMethod('__isset');
96
97 2
        $parentAccess = 'return parent::__isset($name);';
98
99 2
        if (! $override) {
100
            $parentAccess = PublicScopeSimulator::getPublicAccessSimulationCode(
101 2
                PublicScopeSimulator::OPERATION_ISSET,
102
                'name'
103 2
            );
104 1
        }
105 1
106 1
        $this->setBody(sprintf(
107
            $this->callParentTemplate,
108
            '$this->' . $initializerProperty->getName() . ' && $this->' . $callInitializer->getName()
109
            . '(\'__isset\', array(\'name\' => $name));',
110 2
            $publicProperties->getName(),
111 2
            $protectedProperties->getName(),
112 2
            $protectedProperties->getName(),
113 2
            $privateProperties->getName(),
114 2
            $privateProperties->getName(),
115 2
            $parentAccess
116 2
        ));
117 2
    }
118
}
119