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 / di / src / DI / Helpers.php

Helpers.php in Packeta trunk, at deps/nette/di/src/DI/Helpers.php

231 lines 9.7 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\DI;
9
10 use Packetery\Nette;
11 use Packetery\Nette\DI\Definitions\Reference;
12 use Packetery\Nette\DI\Definitions\Statement;
13 use Packetery\Nette\Utils\Reflection;
14 use Packetery\Nette\Utils\Type;
15 /**
16 * The DI helpers.
17 * @internal
18 */
19 final class Helpers
20 {
21 use \Packetery\Nette\StaticClass;
22 /**
23 * Expands %placeholders%.
24 * @param mixed $var
25 * @param bool|array $recursive
26 * @return mixed
27 * @throws \Packetery\Nette\InvalidArgumentException
28 */
29 public static function expand($var, array $params, $recursive = \false)
30 {
31 if (\is_array($var)) {
32 $res = [];
33 foreach ($var as $key => $val) {
34 $res[self::expand($key, $params, $recursive)] = self::expand($val, $params, $recursive);
35 }
36 return $res;
37 } elseif ($var instanceof Statement) {
38 return new Statement(self::expand($var->getEntity(), $params, $recursive), self::expand($var->arguments, $params, $recursive));
39 } elseif ($var === '%parameters%' && !\array_key_exists('parameters', $params)) {
40 return $recursive ? self::expand($params, $params, $recursive) : $params;
41 } elseif (\is_string($var)) {
42 $recursive = \is_array($recursive) ? $recursive : ($recursive ? [] : null);
43 return self::expandString($var, $params, $recursive);
44 } else {
45 return $var;
46 }
47 }
48 /**
49 * Expands %placeholders% in string
50 * @throws \Packetery\Nette\InvalidArgumentException
51 */
52 private static function expandString(string $string, array $params, ?array $recursive, bool $onlyString = \false)
53 {
54 $parts = \preg_split('#%([\\w.-]*)%#i', $string, -1, \PREG_SPLIT_DELIM_CAPTURE);
55 $res = [];
56 $dynamic = \false;
57 foreach ($parts as $n => $part) {
58 if ($n % 2 === 0) {
59 $res[] = $part;
60 } elseif ($part === '') {
61 $res[] = '%';
62 } else {
63 $res[] = $val = self::expandParameter($part, $params, $recursive, $onlyString);
64 if (\strlen($part) + 2 === \strlen($string)) {
65 return $val;
66 } elseif ($val instanceof DynamicParameter || $val instanceof Statement) {
67 $dynamic = \true;
68 } elseif (!\is_scalar($val)) {
69 throw new \Packetery\Nette\InvalidArgumentException(\sprintf("Unable to concatenate non-scalar parameter '%s' into '%s'.", $part, $string));
70 }
71 }
72 }
73 return $dynamic ? new Statement('::implode', ['', $res]) : \implode('', $res);
74 }
75 private static function expandParameter(string $parameter, array $params, ?array $recursive, bool $onlyString)
76 {
77 $val = $params;
78 $path = [];
79 $keys = \explode('.', $parameter);
80 while (($key = $path[] = \array_shift($keys)) !== null) {
81 if (\is_array($val) && \array_key_exists($key, $val)) {
82 $val = $val[$key];
83 $fullExpand = !$onlyString && !$keys;
84 // last
85 if (\is_array($recursive) && ($fullExpand || \is_string($val))) {
86 $pathStr = \implode('.', $path);
87 if (isset($recursive[$pathStr])) {
88 throw new \Packetery\Nette\InvalidArgumentException('Circular reference detected for parameters: %' . \implode('%, %', \array_keys($recursive)) . '%');
89 }
90 $val = $fullExpand ? self::expand($val, $params, $recursive + [$pathStr => 1]) : self::expandString($val, $params, $recursive + [$pathStr => 1], \true);
91 }
92 } elseif ($val instanceof DynamicParameter) {
93 $val = new DynamicParameter($val . '[' . \var_export($key, \true) . ']');
94 } elseif ($val instanceof Statement) {
95 $val = new Statement('(?)[?]', [$val, $key]);
96 } else {
97 throw new \Packetery\Nette\InvalidArgumentException(\sprintf("Missing parameter '%s'.", $parameter));
98 }
99 }
100 return $val;
101 }
102 /**
103 * Escapes '%' and '@'
104 * @param mixed $value
105 * @return mixed
106 */
107 public static function escape($value)
108 {
109 if (\is_array($value)) {
110 $res = [];
111 foreach ($value as $key => $val) {
112 $key = \is_string($key) ? \str_replace('%', '%%', $key) : $key;
113 $res[$key] = self::escape($val);
114 }
115 return $res;
116 } elseif (\is_string($value)) {
117 return \preg_replace('#^@|%#', '$0$0', $value);
118 }
119 return $value;
120 }
121 /**
122 * Process constants recursively.
123 */
124 public static function filterArguments(array $args) : array
125 {
126 foreach ($args as $k => $v) {
127 if (\PHP_VERSION_ID >= 80100 && \is_string($v) && \preg_match('#^([\\w\\\\]+)::\\w+$#D', $v, $m) && \enum_exists($m[1])) {
128 $args[$k] = new \Packetery\Nette\PhpGenerator\PhpLiteral($v);
129 } elseif (\is_string($v) && \preg_match('#^[\\w\\\\]*::[A-Z][a-zA-Z0-9_]*$#D', $v)) {
130 $args[$k] = new \Packetery\Nette\PhpGenerator\PhpLiteral(\ltrim($v, ':'));
131 } elseif (\is_string($v) && \preg_match('#^@[\\w\\\\]+$#D', $v)) {
132 $args[$k] = new Reference(\substr($v, 1));
133 } elseif (\is_array($v)) {
134 $args[$k] = self::filterArguments($v);
135 } elseif ($v instanceof Statement) {
136 [$tmp] = self::filterArguments([$v->getEntity()]);
137 $args[$k] = new Statement($tmp, self::filterArguments($v->arguments));
138 }
139 }
140 return $args;
141 }
142 /**
143 * Replaces @extension with real extension name in service definition.
144 * @param mixed $config
145 * @return mixed
146 */
147 public static function prefixServiceName($config, string $namespace)
148 {
149 if (\is_string($config)) {
150 if (\strncmp($config, '@extension.', 10) === 0) {
151 $config = '@' . $namespace . '.' . \substr($config, 11);
152 }
153 } elseif ($config instanceof Reference) {
154 if (\strncmp($config->getValue(), 'extension.', 9) === 0) {
155 $config = new Reference($namespace . '.' . \substr($config->getValue(), 10));
156 }
157 } elseif ($config instanceof Statement) {
158 return new Statement(self::prefixServiceName($config->getEntity(), $namespace), self::prefixServiceName($config->arguments, $namespace));
159 } elseif (\is_array($config)) {
160 foreach ($config as &$val) {
161 $val = self::prefixServiceName($val, $namespace);
162 }
163 }
164 return $config;
165 }
166 /**
167 * Returns an annotation value.
168 * @param \ReflectionFunctionAbstract|\ReflectionProperty|\ReflectionClass $ref
169 */
170 public static function parseAnnotation(\Reflector $ref, string $name) : ?string
171 {
172 if (!Reflection::areCommentsAvailable()) {
173 throw new \Packetery\Nette\InvalidStateException('You have to enable phpDoc comments in opcode cache.');
174 }
175 $re = '#[\\s*]@' . \preg_quote($name, '#') . '(?=\\s|$)(?:[ \\t]+([^@\\s]\\S*))?#';
176 if ($ref->getDocComment() && \preg_match($re, \trim($ref->getDocComment(), '/*'), $m)) {
177 return $m[1] ?? '';
178 }
179 return null;
180 }
181 public static function getReturnTypeAnnotation(\ReflectionFunctionAbstract $func) : ?Type
182 {
183 $type = \preg_replace('#[|\\s].*#', '', (string) self::parseAnnotation($func, 'return'));
184 if (!$type || $type === 'object' || $type === 'mixed') {
185 return null;
186 } elseif ($func instanceof \ReflectionMethod) {
187 $type = $type === '$this' ? 'static' : $type;
188 $type = Reflection::expandClassName($type, $func->getDeclaringClass());
189 }
190 return Type::fromString($type);
191 }
192 public static function ensureClassType(?Type $type, string $hint, bool $allowNullable = \false) : string
193 {
194 if (!$type) {
195 throw new ServiceCreationException(\sprintf('%s is not declared.', \ucfirst($hint)));
196 } elseif (!$type->isClass() || !$allowNullable && $type->allows('null')) {
197 throw new ServiceCreationException(\sprintf("%s is expected to not be %sbuilt-in/complex, '%s' given.", \ucfirst($hint), $allowNullable ? '' : 'nullable/', $type));
198 }
199 $class = $type->getSingleName();
200 if (!\class_exists($class) && !\interface_exists($class)) {
201 throw new ServiceCreationException(\sprintf("Class '%s' not found.\nCheck the %s.", $class, $hint));
202 }
203 return $class;
204 }
205 public static function normalizeClass(string $type) : string
206 {
207 return \class_exists($type) || \interface_exists($type) ? (new \ReflectionClass($type))->name : $type;
208 }
209 /**
210 * Non data-loss type conversion.
211 * @param mixed $value
212 * @return mixed
213 * @throws \Packetery\Nette\InvalidStateException
214 */
215 public static function convertType($value, string $type)
216 {
217 if (\is_scalar($value)) {
218 $norm = $value === \false ? '0' : (string) $value;
219 if ($type === 'float') {
220 $norm = \preg_replace('#\\.0*$#D', '', $norm);
221 }
222 $orig = $norm;
223 \settype($norm, $type);
224 if ($orig === ($norm === \false ? '0' : (string) $norm)) {
225 return $norm;
226 }
227 }
228 throw new \Packetery\Nette\InvalidStateException(\sprintf('Cannot convert %s to %s.', \is_scalar($value) ? "'{$value}'" : \gettype($value), $type));
229 }
230 }
231