PluginProbe ʕ •ᴥ•ʔ
FAPI Member / trunk
FAPI Member vtrunk
2.2.33 2.2.32 trunk 1.9.47 2.1.18 2.2.24 2.2.25 2.2.26 2.2.28 2.2.29 2.2.30 2.2.31
fapi-member / libs / nette / utils / src / Utils / ObjectHelpers.php
fapi-member / libs / nette / utils / src / Utils Last commit date
ArrayHash.php 2 years ago ArrayList.php 2 years ago Arrays.php 2 years ago Callback.php 2 years ago DateTime.php 2 years ago FileSystem.php 2 years ago Floats.php 2 years ago Helpers.php 2 years ago Html.php 2 years ago Image.php 2 years ago Json.php 2 years ago ObjectHelpers.php 2 years ago ObjectMixin.php 2 years ago Paginator.php 2 years ago Random.php 2 years ago Reflection.php 2 years ago Strings.php 2 years ago Type.php 2 years ago Validators.php 2 years ago exceptions.php 2 years ago
ObjectHelpers.php
175 lines
1 <?php
2
3 /**
4 * This file is part of the Nette Framework (https://nette.org)
5 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6 */
7 declare (strict_types=1);
8 namespace FapiMember\Library\Nette\Utils;
9
10 use FapiMember\Library\Nette;
11 use FapiMember\Library\Nette\MemberAccessException;
12 /**
13 * Nette\SmartObject helpers.
14 * @internal
15 */
16 final class ObjectHelpers
17 {
18 use Nette\StaticClass;
19 /**
20 * @return never
21 * @throws MemberAccessException
22 */
23 public static function strictGet(string $class, string $name): void
24 {
25 $rc = new \ReflectionClass($class);
26 $hint = self::getSuggestion(array_merge(array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), function ($p) {
27 return !$p->isStatic();
28 }), self::parseFullDoc($rc, '~^[ \t*]*@property(?:-read)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m')), $name);
29 throw new MemberAccessException("Cannot read an undeclared property {$class}::\${$name}" . ($hint ? ", did you mean \${$hint}?" : '.'));
30 }
31 /**
32 * @return never
33 * @throws MemberAccessException
34 */
35 public static function strictSet(string $class, string $name): void
36 {
37 $rc = new \ReflectionClass($class);
38 $hint = self::getSuggestion(array_merge(array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), function ($p) {
39 return !$p->isStatic();
40 }), self::parseFullDoc($rc, '~^[ \t*]*@property(?:-write)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m')), $name);
41 throw new MemberAccessException("Cannot write to an undeclared property {$class}::\${$name}" . ($hint ? ", did you mean \${$hint}?" : '.'));
42 }
43 /**
44 * @return never
45 * @throws MemberAccessException
46 */
47 public static function strictCall(string $class, string $method, array $additionalMethods = []): void
48 {
49 $trace = debug_backtrace(0, 3);
50 // suppose this method is called from __call()
51 $context = (($trace[1]['function'] ?? null) === '__call') ? $trace[2]['class'] ?? null : null;
52 if ($context && is_a($class, $context, \true) && method_exists($context, $method)) {
53 // called parent::$method()
54 $class = get_parent_class($context);
55 }
56 if (method_exists($class, $method)) {
57 // insufficient visibility
58 $rm = new \ReflectionMethod($class, $method);
59 $visibility = $rm->isPrivate() ? 'private ' : ($rm->isProtected() ? 'protected ' : '');
60 throw new MemberAccessException("Call to {$visibility}method {$class}::{$method}() from " . ($context ? "scope {$context}." : 'global scope.'));
61 } else {
62 $hint = self::getSuggestion(array_merge(get_class_methods($class), self::parseFullDoc(new \ReflectionClass($class), '~^[ \t*]*@method[ \t]+(?:static[ \t]+)?(?:\S+[ \t]+)??(\w+)\(~m'), $additionalMethods), $method);
63 throw new MemberAccessException("Call to undefined method {$class}::{$method}()" . ($hint ? ", did you mean {$hint}()?" : '.'));
64 }
65 }
66 /**
67 * @return never
68 * @throws MemberAccessException
69 */
70 public static function strictStaticCall(string $class, string $method): void
71 {
72 $trace = debug_backtrace(0, 3);
73 // suppose this method is called from __callStatic()
74 $context = (($trace[1]['function'] ?? null) === '__callStatic') ? $trace[2]['class'] ?? null : null;
75 if ($context && is_a($class, $context, \true) && method_exists($context, $method)) {
76 // called parent::$method()
77 $class = get_parent_class($context);
78 }
79 if (method_exists($class, $method)) {
80 // insufficient visibility
81 $rm = new \ReflectionMethod($class, $method);
82 $visibility = $rm->isPrivate() ? 'private ' : ($rm->isProtected() ? 'protected ' : '');
83 throw new MemberAccessException("Call to {$visibility}method {$class}::{$method}() from " . ($context ? "scope {$context}." : 'global scope.'));
84 } else {
85 $hint = self::getSuggestion(array_filter((new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC), function ($m) {
86 return $m->isStatic();
87 }), $method);
88 throw new MemberAccessException("Call to undefined static method {$class}::{$method}()" . ($hint ? ", did you mean {$hint}()?" : '.'));
89 }
90 }
91 /**
92 * Returns array of magic properties defined by annotation @property.
93 * @return array of [name => bit mask]
94 * @internal
95 */
96 public static function getMagicProperties(string $class): array
97 {
98 static $cache;
99 $props =& $cache[$class];
100 if ($props !== null) {
101 return $props;
102 }
103 $rc = new \ReflectionClass($class);
104 preg_match_all('~^ [ \t*]* @property(|-read|-write|-deprecated) [ \t]+ [^\s$]+ [ \t]+ \$ (\w+) ()~mx', (string) $rc->getDocComment(), $matches, \PREG_SET_ORDER);
105 $props = [];
106 foreach ($matches as [, $type, $name]) {
107 $uname = ucfirst($name);
108 $write = $type !== '-read' && $rc->hasMethod($nm = 'set' . $uname) && ($rm = $rc->getMethod($nm))->name === $nm && !$rm->isPrivate() && !$rm->isStatic();
109 $read = $type !== '-write' && ($rc->hasMethod($nm = 'get' . $uname) || $rc->hasMethod($nm = 'is' . $uname)) && ($rm = $rc->getMethod($nm))->name === $nm && !$rm->isPrivate() && !$rm->isStatic();
110 if ($read || $write) {
111 $props[$name] = $read << 0 | ($nm[0] === 'g') << 1 | $rm->returnsReference() << 2 | $write << 3 | ($type === '-deprecated') << 4;
112 }
113 }
114 foreach ($rc->getTraits() as $trait) {
115 $props += self::getMagicProperties($trait->name);
116 }
117 if ($parent = get_parent_class($class)) {
118 $props += self::getMagicProperties($parent);
119 }
120 return $props;
121 }
122 /**
123 * Finds the best suggestion (for 8-bit encoding).
124 * @param (\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionClass|\ReflectionProperty|string)[] $possibilities
125 * @internal
126 */
127 public static function getSuggestion(array $possibilities, string $value): ?string
128 {
129 $norm = preg_replace($re = '#^(get|set|has|is|add)(?=[A-Z])#', '+', $value);
130 $best = null;
131 $min = (strlen($value) / 4 + 1) * 10 + 0.1;
132 foreach (array_unique($possibilities, \SORT_REGULAR) as $item) {
133 $item = ($item instanceof \Reflector) ? $item->name : $item;
134 if ($item !== $value && (($len = levenshtein($item, $value, 10, 11, 10)) < $min || ($len = levenshtein(preg_replace($re, '*', $item), $norm, 10, 11, 10)) < $min)) {
135 $min = $len;
136 $best = $item;
137 }
138 }
139 return $best;
140 }
141 private static function parseFullDoc(\ReflectionClass $rc, string $pattern): array
142 {
143 do {
144 $doc[] = $rc->getDocComment();
145 $traits = $rc->getTraits();
146 while ($trait = array_pop($traits)) {
147 $doc[] = $trait->getDocComment();
148 $traits += $trait->getTraits();
149 }
150 } while ($rc = $rc->getParentClass());
151 return preg_match_all($pattern, implode($doc), $m) ? $m[1] : [];
152 }
153 /**
154 * Checks if the public non-static property exists.
155 * @return bool|string returns 'event' if the property exists and has event like name
156 * @internal
157 */
158 public static function hasProperty(string $class, string $name)
159 {
160 static $cache;
161 $prop =& $cache[$class][$name];
162 if ($prop === null) {
163 $prop = \false;
164 try {
165 $rp = new \ReflectionProperty($class, $name);
166 if ($rp->isPublic() && !$rp->isStatic()) {
167 $prop = ($name >= 'onA' && $name < 'on_') ? 'event' : \true;
168 }
169 } catch (\ReflectionException $e) {
170 }
171 }
172 return $prop;
173 }
174 }
175