src/Reflect.php source

1 <?php
2
3 declare(strict_types=1);
4
5 namespace Serializor;
6
7 use Closure;
8 use ReflectionClass;
9 use ReflectionFunction;
10 use ReflectionObject;
11 use ReflectionProperty;
12 use ReflectionReference;
13 use Reflector;
14 use WeakMap;
15
16 /**
17 * Provides Reflection objects for various use cases and caches the reflection
18 * instances to avoid excessive garbage collection.
19 *
20 * @package Serializor
21 */
22 final class Reflect
23 {
24
25 private static bool $initialized = false;
26 private static array $reflectionClassCache = [];
27 private static WeakMap $reflectionObjectCache;
28 private static WeakMap $reflectionFunctionCache;
29
30 public static function getVariableId(mixed &$value): string
31 {
32 return ReflectionReference::fromArrayElement([&$value], 0)->getId();
33 }
34
35 /**
36 * Provide a hash unique for the Reflection instance.
37 */
38 public static function getHash(Reflector $reflector): string
39 {
40 return \md5((string) $reflector);
41 }
42
43 /**
44 * Get and cache a ReflectionFunction
45 *
46 * @param callable $function
47 * @return ReflectionFunction
48 */
49 public static function getReflectionFunction(callable $function): ReflectionFunction
50 {
51 if (!self::$initialized) self::init();
52 if (!isset(self::$reflectionFunctionCache[$function])) {
53 self::$reflectionFunctionCache[$function] = new ReflectionFunction(Closure::fromCallable($function));
54 }
55 return self::$reflectionFunctionCache[$function];
56 }
57
58 private static function getReflectionObject(object $value): ReflectionObject
59 {
60 if (!self::$initialized) self::init();
61 if (!isset(self::$reflectionObjectCache[$value])) {
62 self::$reflectionObjectCache[$value] = new ReflectionObject($value);
63 }
64 return self::$reflectionObjectCache[$value];
65 }
66
67 public static function getReflectionClass(object|string $value): ReflectionClass
68 {
69 if (\is_object($value)) {
70 return self::getReflectionObject($value);
71 }
72 if (!isset(self::$reflectionClassCache[$value])) {
73 self::$reflectionClassCache[$value] = new ReflectionClass($value);
74 }
75 return self::$reflectionClassCache[$value];
76 }
77
78 /**
79 * Finds all reflection properties for an object and it's ancestors,
80 * in an array where the key identifies uniquely the property with
81 * the class name followed by \0 as a prefix.
82 *
83 * @param class-string<mixed> $className
84 * @return array<string,ReflectionProperty>
85 */
86 public static function getReflectionProperties(string $className): array
87 {
88 if (!isset(self::$propertyReflectors[$className])) {
89 $topClass = $className;
90 self::$propertyReflectors[$className] = [];
91 $rc = self::getReflectionClass($className);
92 do {
93 if ($className === $topClass) {
94 $prefix = '';
95 } else {
96 $prefix = $className . "\0";
97 }
98 foreach ($rc->getProperties() as $rp) {
99 self::$propertyReflectors[$className][$prefix . $rp->getName()] = self::$propertyReflectors[$className][$prefix . $rp->getName()] ?? $rp;
100 }
101 } while ($rc = $rc->getParentClass());
102 }
103 return self::$propertyReflectors[$className];
104 }
105
106 /**
107 * Caches ReflectionProperty instances for the class and
108 * its ancestors.
109 *
110 * @var array<class-string<mixed>,ReflectionProperty[]>
111 */
112 private static array $propertyReflectors = [];
113
114 /**
115 * Instantiate static properties
116 */
117 private static function init(): void
118 {
119 if (!self::$initialized) {
120 self::$initialized = true;
121 self::$reflectionObjectCache = new WeakMap;
122 self::$reflectionFunctionCache = new WeakMap;
123 }
124 }
125 }
126