PluginProbe
Packeta / trunk
Packeta vtrunk
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
packeta / deps / nette / utils / src / Utils / ObjectHelpers.php

ObjectHelpers.php in Packeta trunk, at deps/nette/utils/src/Utils/ObjectHelpers.php

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