PluginProbe
Packeta / 1.6.1
Packeta v1.6.1
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 1.6.1, at deps/nette/utils/src/Utils/ObjectHelpers.php

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