• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / di / 6739658573

03 Nov 2023 12:12AM UTC coverage: 93.859% (+0.005%) from 93.854%
6739658573

push

github

dg
ServiceDefinition: prepends @self to setup immediately

13 of 13 new or added lines in 2 files covered. (100.0%)

3 existing lines in 2 files now uncovered.

2262 of 2410 relevant lines covered (93.86%)

0.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

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

8
declare(strict_types=1);
9

10
namespace Nette\DI;
11

12
use Nette;
13
use Nette\DI\Definitions\Reference;
14
use Nette\DI\Definitions\Statement;
15
use Nette\Utils\Reflection;
16
use Nette\Utils\Type;
17

18

19
/**
20
 * The DI helpers.
21
 * @internal
22
 */
23
final class Helpers
24
{
25
        use Nette\StaticClass;
26

27
        /**
28
         * Expands %placeholders%.
29
         * @param  mixed  $var
30
         * @param  bool|array  $recursive
31
         * @return mixed
32
         * @throws Nette\InvalidArgumentException
33
         */
34
        public static function expand($var, array $params, $recursive = false)
1✔
35
        {
36
                if (is_array($var)) {
1✔
37
                        $res = [];
1✔
38
                        foreach ($var as $key => $val) {
1✔
39
                                $res[self::expand($key, $params, $recursive)] = self::expand($val, $params, $recursive);
1✔
40
                        }
41
                        return $res;
1✔
42

43
                } elseif ($var instanceof Statement) {
1✔
44
                        return new Statement(
1✔
45
                                self::expand($var->getEntity(), $params, $recursive),
1✔
46
                                self::expand($var->arguments, $params, $recursive)
1✔
47
                        );
48

49
                } elseif ($var === '%parameters%' && !array_key_exists('parameters', $params)) {
1✔
50
                        return $recursive
1✔
51
                                ? self::expand($params, $params, $recursive)
1✔
52
                                : $params;
1✔
53

54
                } elseif (is_string($var)) {
1✔
55
                        $recursive = is_array($recursive) ? $recursive : ($recursive ? [] : null);
1✔
56
                        return self::expandString($var, $params, $recursive);
1✔
57

58
                } else {
59
                        return $var;
1✔
60
                }
61
        }
62

63

64
        /**
65
         * Expands %placeholders% in string
66
         * @throws Nette\InvalidArgumentException
67
         */
68
        private static function expandString(string $string, array $params, ?array $recursive, bool $onlyString = false)
1✔
69
        {
70
                $parts = preg_split('#%([\w.-]*)%#i', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
1✔
71
                $res = [];
1✔
72
                $dynamic = false;
1✔
73
                foreach ($parts as $n => $part) {
1✔
74
                        if ($n % 2 === 0) {
1✔
75
                                $res[] = $part;
1✔
76
                        } elseif ($part === '') {
1✔
77
                                $res[] = '%';
1✔
78
                        } else {
79
                                $res[] = $val = self::expandParameter($part, $params, $recursive, $onlyString);
1✔
80
                                if (strlen($part) + 2 === strlen($string)) {
1✔
81
                                        return $val;
1✔
82
                                } elseif ($val instanceof DynamicParameter || $val instanceof Statement) {
1✔
83
                                        $dynamic = true;
1✔
84
                                } elseif (!is_scalar($val)) {
1✔
85
                                        throw new Nette\InvalidArgumentException(sprintf("Unable to concatenate non-scalar parameter '%s' into '%s'.", $part, $string));
1✔
86
                                }
87
                        }
88
                }
89

90
                return $dynamic
1✔
91
                        ? new Statement('::implode', ['', $res])
1✔
92
                        : implode('', $res);
1✔
93
        }
94

95

96
        private static function expandParameter(string $parameter, array $params, ?array $recursive, bool $onlyString)
1✔
97
        {
98
                $val = $params;
1✔
99
                $path = [];
1✔
100
                $keys = explode('.', $parameter);
1✔
101
                while (($key = $path[] = array_shift($keys)) !== null) {
1✔
102
                        if (is_array($val) && array_key_exists($key, $val)) {
1✔
103
                                $val = $val[$key];
1✔
104
                                $fullExpand = !$onlyString && !$keys; // last
1✔
105
                                if (is_array($recursive) && ($fullExpand || is_string($val))) {
1✔
106
                                        $pathStr = implode('.', $path);
1✔
107
                                        if (isset($recursive[$pathStr])) {
1✔
108
                                                throw new Nette\InvalidArgumentException('Circular reference detected for parameters: %' . implode('%, %', array_keys($recursive)) . '%');
1✔
109
                                        }
110
                                        $val = $fullExpand
1✔
111
                                                ? self::expand($val, $params, $recursive + [$pathStr => 1])
1✔
112
                                                : self::expandString($val, $params, $recursive + [$pathStr => 1], true);
1✔
113
                                }
114
                        } elseif ($val instanceof DynamicParameter) {
1✔
115
                                $val = new DynamicParameter($val . '[' . var_export($key, true) . ']');
1✔
116
                        } else {
117
                                throw new Nette\InvalidArgumentException(sprintf("Missing parameter '%s'.", $parameter));
1✔
118
                        }
119
                }
120
                return $val;
1✔
121
        }
122

123

124
        /**
125
         * Escapes '%' and '@'
126
         * @param  mixed  $value
127
         * @return mixed
128
         */
129
        public static function escape($value)
130
        {
131
                if (is_array($value)) {
1✔
132
                        $res = [];
1✔
133
                        foreach ($value as $key => $val) {
1✔
134
                                $key = is_string($key) ? str_replace('%', '%%', $key) : $key;
1✔
135
                                $res[$key] = self::escape($val);
1✔
136
                        }
137

138
                        return $res;
1✔
139
                } elseif (is_string($value)) {
1✔
140
                        return preg_replace('#^@|%#', '$0$0', $value);
1✔
141
                }
142

143
                return $value;
1✔
144
        }
145

146

147
        /**
148
         * Process constants recursively.
149
         */
150
        public static function filterArguments(array $args): array
1✔
151
        {
152
                foreach ($args as $k => $v) {
1✔
153
                        if (
154
                                PHP_VERSION_ID >= 80100
1✔
155
                                && is_string($v)
1✔
156
                                && preg_match('#^([\w\\\\]+)::\w+$#D', $v, $m)
1✔
157
                                && enum_exists($m[1])
1✔
158
                        ) {
UNCOV
159
                                $args[$k] = new Nette\PhpGenerator\PhpLiteral($v);
×
160
                        } elseif (is_string($v) && preg_match('#^[\w\\\\]*::[A-Z][a-zA-Z0-9_]*$#D', $v)) {
1✔
161
                                $args[$k] = new Nette\PhpGenerator\PhpLiteral(ltrim($v, ':'));
1✔
162
                        } elseif (is_string($v) && preg_match('#^@[\w\\\\]+$#D', $v)) {
1✔
163
                                $args[$k] = new Reference(substr($v, 1));
1✔
164
                        } elseif (is_array($v)) {
1✔
165
                                $args[$k] = self::filterArguments($v);
1✔
166
                        } elseif ($v instanceof Statement) {
1✔
167
                                [$tmp] = self::filterArguments([$v->getEntity()]);
1✔
168
                                $args[$k] = new Statement($tmp, self::filterArguments($v->arguments));
1✔
169
                        }
170
                }
171

172
                return $args;
1✔
173
        }
174

175

176
        /**
177
         * Replaces @extension with real extension name in service definition.
178
         * @param  mixed  $config
179
         * @return mixed
180
         */
181
        public static function prefixServiceName($config, string $namespace)
1✔
182
        {
183
                if (is_string($config)) {
1✔
184
                        if (strncmp($config, '@extension.', 10) === 0) {
1✔
185
                                $config = '@' . $namespace . '.' . substr($config, 11);
1✔
186
                        }
187
                } elseif ($config instanceof Reference) {
1✔
188
                        if (strncmp($config->getValue(), 'extension.', 9) === 0) {
1✔
189
                                $config = new Reference($namespace . '.' . substr($config->getValue(), 10));
1✔
190
                        }
191
                } elseif ($config instanceof Statement) {
1✔
192
                        return new Statement(
1✔
193
                                self::prefixServiceName($config->getEntity(), $namespace),
1✔
194
                                self::prefixServiceName($config->arguments, $namespace)
1✔
195
                        );
196
                } elseif (is_array($config)) {
1✔
197
                        foreach ($config as &$val) {
1✔
198
                                $val = self::prefixServiceName($val, $namespace);
1✔
199
                        }
200
                }
201

202
                return $config;
1✔
203
        }
204

205

206
        /**
207
         * Returns an annotation value.
208
         * @param  \ReflectionFunctionAbstract|\ReflectionProperty|\ReflectionClass  $ref
209
         */
210
        public static function parseAnnotation(\Reflector $ref, string $name): ?string
1✔
211
        {
212
                if (!Reflection::areCommentsAvailable()) {
1✔
UNCOV
213
                        throw new Nette\InvalidStateException('You have to enable phpDoc comments in opcode cache.');
×
214
                }
215

216
                $re = '#[\s*]@' . preg_quote($name, '#') . '(?=\s|$)(?:[ \t]+([^@\s]\S*))?#';
1✔
217
                if ($ref->getDocComment() && preg_match($re, trim($ref->getDocComment(), '/*'), $m)) {
1✔
218
                        return $m[1] ?? '';
1✔
219
                }
220

221
                return null;
1✔
222
        }
223

224

225
        public static function getReturnTypeAnnotation(\ReflectionFunctionAbstract $func): ?Type
1✔
226
        {
227
                $type = preg_replace('#[|\s].*#', '', (string) self::parseAnnotation($func, 'return'));
1✔
228
                if (!$type || $type === 'object' || $type === 'mixed') {
1✔
229
                        return null;
1✔
230
                } elseif ($func instanceof \ReflectionMethod) {
1✔
231
                        $type = $type === '$this' ? 'static' : $type;
1✔
232
                        $type = Reflection::expandClassName($type, $func->getDeclaringClass());
1✔
233
                }
234

235
                return Type::fromString($type);
1✔
236
        }
237

238

239
        public static function ensureClassType(?Type $type, string $hint, bool $allowNullable = false): string
1✔
240
        {
241
                if (!$type) {
1✔
242
                        throw new ServiceCreationException(sprintf('%s is not declared.', ucfirst($hint)));
1✔
243
                } elseif (!$type->isClass() || (!$allowNullable && $type->allows('null'))) {
1✔
244
                        throw new ServiceCreationException(sprintf("%s is expected to not be %sbuilt-in/complex, '%s' given.", ucfirst($hint), $allowNullable ? '' : 'nullable/', $type));
1✔
245
                }
246

247
                $class = $type->getSingleName();
1✔
248
                if (!class_exists($class) && !interface_exists($class)) {
1✔
249
                        throw new ServiceCreationException(sprintf("Class '%s' not found.\nCheck the %s.", $class, $hint));
1✔
250
                }
251

252
                return $class;
1✔
253
        }
254

255

256
        public static function normalizeClass(string $type): string
1✔
257
        {
258
                return class_exists($type) || interface_exists($type)
1✔
259
                        ? (new \ReflectionClass($type))->name
1✔
260
                        : $type;
1✔
261
        }
262

263

264
        /**
265
         * Non data-loss type conversion.
266
         * @param  mixed  $value
267
         * @return mixed
268
         * @throws Nette\InvalidStateException
269
         */
270
        public static function convertType($value, string $type)
1✔
271
        {
272
                if (is_scalar($value)) {
1✔
273
                        $norm = ($value === false ? '0' : (string) $value);
1✔
274
                        if ($type === 'float') {
1✔
275
                                $norm = preg_replace('#\.0*$#D', '', $norm);
1✔
276
                        }
277

278
                        $orig = $norm;
1✔
279
                        settype($norm, $type);
1✔
280
                        if ($orig === ($norm === false ? '0' : (string) $norm)) {
1✔
281
                                return $norm;
1✔
282
                        }
283
                }
284

285
                throw new Nette\InvalidStateException(sprintf(
1✔
286
                        'Cannot convert %s to %s.',
1✔
287
                        is_scalar($value) ? "'$value'" : gettype($value),
1✔
288
                        $type
289
                ));
290
        }
291
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc