Completed
Pull Request — master (#423)
by Marco
24:53
created

Properties::onlyInstanceProperties()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ProxyManager\ProxyGenerator\Util;
6
7
use ReflectionClass;
8
use ReflectionProperty;
9
use function array_filter;
10
use function array_key_exists;
11
use function array_map;
12
use function array_merge;
13
use function array_values;
14
15
/**
16
 * DTO containing the list of all non-static proxy properties and utility methods to access them
17
 * in various formats/collections
18
 *
19
 */
20
final class Properties
21
{
22
    /** @var array|\ReflectionProperty[] */
23
    private $properties;
24
25
    /**
26
     * @param ReflectionProperty[] $properties
27 15
     */
28
    private function __construct(array $properties)
29 15
    {
30 15
        $this->properties = $properties;
31
    }
32 15
33
    public static function fromReflectionClass(ReflectionClass $reflection) : self
34 15
    {
35 15
        $class         = $reflection;
36
        $parentClasses = [];
37
38 15
        do {
39
            $parentClasses[] = $class;
40 15
41 15
            $class = $class->getParentClass();
42
        } while ($class);
43 15
44
        return new self(array_merge(
45 15
            ...array_map(function (ReflectionClass $class) : array {
46 15
                return array_values(array_filter(
47
                    $class->getProperties(),
48 13
                    function (ReflectionProperty $property) use ($class) : bool {
49 13
                        return $class->getName() === $property->getDeclaringClass()->getName()
50 15
                            && ! $property->isStatic();
51
                    }
52 15
                ));
53
            }, $parentClasses)
54
        ));
55
    }
56
57
    /**
58
     * @param string[] $excludedProperties
59 5
     */
60
    public function filter(array $excludedProperties) : self
61 5
    {
62
        $properties = $this->getInstanceProperties();
63 5
64 5
        foreach ($excludedProperties as $propertyName) {
65
            unset($properties[$propertyName]);
66
        }
67 5
68
        return new self($properties);
69
    }
70
71
    public function onlyNonReferenceableProperties() : self
72
    {
73 9
        return new self(array_filter($this->properties, function (ReflectionProperty $property) : bool {
74
            if (! $property->hasType()) {
75 9
                return false;
76
            }
77 9
78 8
            return ! array_key_exists(
79 8
                $property->getName(),
80
                // https://bugs.php.net/bug.php?id=77673
81
                array_flip(array_keys($property->getDeclaringClass()->getDefaultProperties()))
82 7
            );
83
        }));
84
    }
85 9
86
    public function onlyPropertiesThatCanBeUnset() : self
87
    {
88
        return new self(array_filter($this->properties, function (ReflectionProperty $property) : bool {
89
            if (! $property->hasType()) {
90
                return true;
91 9
            }
92
93 9
            return array_key_exists(
94
                $property->getName(),
95 9
                // https://bugs.php.net/bug.php?id=77673
96 8
                array_flip(array_keys($property->getDeclaringClass()->getDefaultProperties()))
97 8
            );
98
        }));
99
    }
100 7
101
    /**
102
     * Properties that cannot be referenced are non-nullable typed properties that aren't initialised
103 9
     */
104
    public function withoutNonReferenceableProperties() : self
105
    {
106
        return new self(array_filter($this->properties, function (ReflectionProperty $property) : bool {
107
            if (! $property->hasType()) {
108
                return true;
109 10
            }
110
111 10
            /** @var $type \ReflectionType */
112
            $type = $property->getType();
113 10
114 10
            if ($type->allowsNull()) {
115 7
                return true;
116
            }
117
118 10
            return array_key_exists(
119
                $property->getName(),
120 10
                // https://bugs.php.net/bug.php?id=77673
121
                array_flip(array_keys($property->getDeclaringClass()->getDefaultProperties()))
122
            );
123 10
        }));
124
    }
125
126
    public function onlyNullableProperties() : self
127
    {
128
        return new self(array_filter(
129 7
            $this->properties,
130
            static function (ReflectionProperty $property) : bool {
131 7
                $type = $property->getType();
132
133
                return null === $type || $type->allowsNull();
134
            }
135
        ));
136
    }
137 2
138
    public function onlyInstanceProperties() : self
139 2
    {
140
        return new self(array_values(array_merge($this->getAccessibleProperties(), $this->getPrivateProperties())));
141 2
    }
142 2
143
    public function empty() : bool
144 2
    {
145
        return [] === $this->properties;
146
    }
147 2
148
    /**
149
     * @return ReflectionProperty[] indexed by the property internal visibility-aware name
150
     */
151
    public function getPublicProperties() : array
152
    {
153 6
        $publicProperties = [];
154
155 6
        foreach ($this->properties as $property) {
156
            if (! $property->isPublic()) {
157
                continue;
158
            }
159
160
            $publicProperties[$property->getName()] = $property;
161
        }
162
163
        return $publicProperties;
164
    }
165
166
    /**
167
     * @return ReflectionProperty[] indexed by the property internal visibility-aware name (\0*\0propertyName)
168
     */
169
    public function getProtectedProperties() : array
170
    {
171
        $protectedProperties = [];
172
173
        foreach ($this->properties as $property) {
174
            if (! $property->isProtected()) {
175
                continue;
176
            }
177
178
            $protectedProperties["\0*\0" . $property->getName()] = $property;
179
        }
180
181
        return $protectedProperties;
182
    }
183
184
    /**
185
     * @return ReflectionProperty[] indexed by the property internal visibility-aware name (\0ClassName\0propertyName)
186
     */
187
    public function getPrivateProperties() : array
188
    {
189
        $privateProperties = [];
190
191
        foreach ($this->properties as $property) {
192
            if (! $property->isPrivate()) {
193
                continue;
194
            }
195
196
            $declaringClass = $property->getDeclaringClass()->getName();
197
198
            $privateProperties["\0" . $declaringClass . "\0" . $property->getName()] = $property;
199
        }
200
201
        return $privateProperties;
202
    }
203
204
    /**
205
     * @return ReflectionProperty[] indexed by the property internal visibility-aware name (\0*\0propertyName)
206
     */
207
    public function getAccessibleProperties() : array
208
    {
209
        return array_merge($this->getPublicProperties(), $this->getProtectedProperties());
210
    }
211
212
    /**
213
     * @return ReflectionProperty[][] indexed by class name and property name
214
     */
215
    public function getGroupedPrivateProperties() : array
216
    {
217
        $propertiesMap = [];
218
219
        foreach ($this->getPrivateProperties() as $property) {
220
            $class = &$propertiesMap[$property->getDeclaringClass()->getName()];
221
222
            $class[$property->getName()] = $property;
223
        }
224
225
        return $propertiesMap;
226
    }
227
228
    /**
229
     * @return ReflectionProperty[] indexed by the property internal visibility-aware name (\0*\0propertyName)
230
     */
231
    public function getInstanceProperties() : array
232
    {
233
        return array_merge($this->getAccessibleProperties(), $this->getPrivateProperties());
234
    }
235
}
236