diff options
| author | polo <ordipolo@gmx.fr> | 2024-08-13 23:45:21 +0200 |
|---|---|---|
| committer | polo <ordipolo@gmx.fr> | 2024-08-13 23:45:21 +0200 |
| commit | bf6655a534a6775d30cafa67bd801276bda1d98d (patch) | |
| tree | c6381e3f6c81c33eab72508f410b165ba05f7e9c /vendor/symfony/var-exporter/ProxyHelper.php | |
| parent | 94d67a4b51f8e62e7d518cce26a526ae1ec48278 (diff) | |
| download | AppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.tar.gz AppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.tar.bz2 AppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.zip | |
VERSION 0.2 doctrine ORM et entités
Diffstat (limited to 'vendor/symfony/var-exporter/ProxyHelper.php')
| -rw-r--r-- | vendor/symfony/var-exporter/ProxyHelper.php | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/vendor/symfony/var-exporter/ProxyHelper.php b/vendor/symfony/var-exporter/ProxyHelper.php new file mode 100644 index 0000000..4cf0f65 --- /dev/null +++ b/vendor/symfony/var-exporter/ProxyHelper.php | |||
| @@ -0,0 +1,413 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | /* | ||
| 4 | * This file is part of the Symfony package. | ||
| 5 | * | ||
| 6 | * (c) Fabien Potencier <fabien@symfony.com> | ||
| 7 | * | ||
| 8 | * For the full copyright and license information, please view the LICENSE | ||
| 9 | * file that was distributed with this source code. | ||
| 10 | */ | ||
| 11 | |||
| 12 | namespace Symfony\Component\VarExporter; | ||
| 13 | |||
| 14 | use Symfony\Component\VarExporter\Exception\LogicException; | ||
| 15 | use Symfony\Component\VarExporter\Internal\Hydrator; | ||
| 16 | use Symfony\Component\VarExporter\Internal\LazyObjectRegistry; | ||
| 17 | |||
| 18 | /** | ||
| 19 | * @author Nicolas Grekas <p@tchwork.com> | ||
| 20 | */ | ||
| 21 | final class ProxyHelper | ||
| 22 | { | ||
| 23 | /** | ||
| 24 | * Helps generate lazy-loading ghost objects. | ||
| 25 | * | ||
| 26 | * @throws LogicException When the class is incompatible with ghost objects | ||
| 27 | */ | ||
| 28 | public static function generateLazyGhost(\ReflectionClass $class): string | ||
| 29 | { | ||
| 30 | if (\PHP_VERSION_ID < 80300 && $class->isReadOnly()) { | ||
| 31 | throw new LogicException(sprintf('Cannot generate lazy ghost with PHP < 8.3: class "%s" is readonly.', $class->name)); | ||
| 32 | } | ||
| 33 | if ($class->isFinal()) { | ||
| 34 | throw new LogicException(sprintf('Cannot generate lazy ghost: class "%s" is final.', $class->name)); | ||
| 35 | } | ||
| 36 | if ($class->isInterface() || $class->isAbstract()) { | ||
| 37 | throw new LogicException(sprintf('Cannot generate lazy ghost: "%s" is not a concrete class.', $class->name)); | ||
| 38 | } | ||
| 39 | if (\stdClass::class !== $class->name && $class->isInternal()) { | ||
| 40 | throw new LogicException(sprintf('Cannot generate lazy ghost: class "%s" is internal.', $class->name)); | ||
| 41 | } | ||
| 42 | if ($class->hasMethod('__get') && 'mixed' !== (self::exportType($class->getMethod('__get')) ?? 'mixed')) { | ||
| 43 | throw new LogicException(sprintf('Cannot generate lazy ghost: return type of method "%s::__get()" should be "mixed".', $class->name)); | ||
| 44 | } | ||
| 45 | |||
| 46 | static $traitMethods; | ||
| 47 | $traitMethods ??= (new \ReflectionClass(LazyGhostTrait::class))->getMethods(); | ||
| 48 | |||
| 49 | foreach ($traitMethods as $method) { | ||
| 50 | if ($class->hasMethod($method->name) && $class->getMethod($method->name)->isFinal()) { | ||
| 51 | throw new LogicException(sprintf('Cannot generate lazy ghost: method "%s::%s()" is final.', $class->name, $method->name)); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | $parent = $class; | ||
| 56 | while ($parent = $parent->getParentClass()) { | ||
| 57 | if (\stdClass::class !== $parent->name && $parent->isInternal()) { | ||
| 58 | throw new LogicException(sprintf('Cannot generate lazy ghost: class "%s" extends "%s" which is internal.', $class->name, $parent->name)); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | $propertyScopes = self::exportPropertyScopes($class->name); | ||
| 62 | |||
| 63 | return <<<EOPHP | ||
| 64 | extends \\{$class->name} implements \Symfony\Component\VarExporter\LazyObjectInterface | ||
| 65 | { | ||
| 66 | use \Symfony\Component\VarExporter\LazyGhostTrait; | ||
| 67 | |||
| 68 | private const LAZY_OBJECT_PROPERTY_SCOPES = {$propertyScopes}; | ||
| 69 | } | ||
| 70 | |||
| 71 | // Help opcache.preload discover always-needed symbols | ||
| 72 | class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); | ||
| 73 | class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); | ||
| 74 | class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); | ||
| 75 | |||
| 76 | EOPHP; | ||
| 77 | } | ||
| 78 | |||
| 79 | /** | ||
| 80 | * Helps generate lazy-loading virtual proxies. | ||
| 81 | * | ||
| 82 | * @param \ReflectionClass[] $interfaces | ||
| 83 | * | ||
| 84 | * @throws LogicException When the class is incompatible with virtual proxies | ||
| 85 | */ | ||
| 86 | public static function generateLazyProxy(?\ReflectionClass $class, array $interfaces = []): string | ||
| 87 | { | ||
| 88 | if (!class_exists($class?->name ?? \stdClass::class, false)) { | ||
| 89 | throw new LogicException(sprintf('Cannot generate lazy proxy: "%s" is not a class.', $class->name)); | ||
| 90 | } | ||
| 91 | if ($class?->isFinal()) { | ||
| 92 | throw new LogicException(sprintf('Cannot generate lazy proxy: class "%s" is final.', $class->name)); | ||
| 93 | } | ||
| 94 | if (\PHP_VERSION_ID < 80300 && $class?->isReadOnly()) { | ||
| 95 | throw new LogicException(sprintf('Cannot generate lazy proxy with PHP < 8.3: class "%s" is readonly.', $class->name)); | ||
| 96 | } | ||
| 97 | |||
| 98 | $methodReflectors = [$class?->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) ?? []]; | ||
| 99 | foreach ($interfaces as $interface) { | ||
| 100 | if (!$interface->isInterface()) { | ||
| 101 | throw new LogicException(sprintf('Cannot generate lazy proxy: "%s" is not an interface.', $interface->name)); | ||
| 102 | } | ||
| 103 | $methodReflectors[] = $interface->getMethods(); | ||
| 104 | } | ||
| 105 | $methodReflectors = array_merge(...$methodReflectors); | ||
| 106 | |||
| 107 | $extendsInternalClass = false; | ||
| 108 | if ($parent = $class) { | ||
| 109 | do { | ||
| 110 | $extendsInternalClass = \stdClass::class !== $parent->name && $parent->isInternal(); | ||
| 111 | } while (!$extendsInternalClass && $parent = $parent->getParentClass()); | ||
| 112 | } | ||
| 113 | $methodsHaveToBeProxied = $extendsInternalClass; | ||
| 114 | $methods = []; | ||
| 115 | |||
| 116 | foreach ($methodReflectors as $method) { | ||
| 117 | if ('__get' !== strtolower($method->name) || 'mixed' === ($type = self::exportType($method) ?? 'mixed')) { | ||
| 118 | continue; | ||
| 119 | } | ||
| 120 | $methodsHaveToBeProxied = true; | ||
| 121 | $trait = new \ReflectionMethod(LazyProxyTrait::class, '__get'); | ||
| 122 | $body = \array_slice(file($trait->getFileName()), $trait->getStartLine() - 1, $trait->getEndLine() - $trait->getStartLine()); | ||
| 123 | $body[0] = str_replace('): mixed', '): '.$type, $body[0]); | ||
| 124 | $methods['__get'] = strtr(implode('', $body).' }', [ | ||
| 125 | 'Hydrator' => '\\'.Hydrator::class, | ||
| 126 | 'Registry' => '\\'.LazyObjectRegistry::class, | ||
| 127 | ]); | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | |||
| 131 | foreach ($methodReflectors as $method) { | ||
| 132 | if (($method->isStatic() && !$method->isAbstract()) || isset($methods[$lcName = strtolower($method->name)])) { | ||
| 133 | continue; | ||
| 134 | } | ||
| 135 | if ($method->isFinal()) { | ||
| 136 | if ($extendsInternalClass || $methodsHaveToBeProxied || method_exists(LazyProxyTrait::class, $method->name)) { | ||
| 137 | throw new LogicException(sprintf('Cannot generate lazy proxy: method "%s::%s()" is final.', $class->name, $method->name)); | ||
| 138 | } | ||
| 139 | continue; | ||
| 140 | } | ||
| 141 | if (method_exists(LazyProxyTrait::class, $method->name) || ($method->isProtected() && !$method->isAbstract())) { | ||
| 142 | continue; | ||
| 143 | } | ||
| 144 | |||
| 145 | $signature = self::exportSignature($method, true, $args); | ||
| 146 | $parentCall = $method->isAbstract() ? "throw new \BadMethodCallException('Cannot forward abstract method \"{$method->class}::{$method->name}()\".')" : "parent::{$method->name}({$args})"; | ||
| 147 | |||
| 148 | if ($method->isStatic()) { | ||
| 149 | $body = " $parentCall;"; | ||
| 150 | } elseif (str_ends_with($signature, '): never') || str_ends_with($signature, '): void')) { | ||
| 151 | $body = <<<EOPHP | ||
| 152 | if (isset(\$this->lazyObjectState)) { | ||
| 153 | (\$this->lazyObjectState->realInstance ??= (\$this->lazyObjectState->initializer)())->{$method->name}({$args}); | ||
| 154 | } else { | ||
| 155 | {$parentCall}; | ||
| 156 | } | ||
| 157 | EOPHP; | ||
| 158 | } else { | ||
| 159 | if (!$methodsHaveToBeProxied && !$method->isAbstract()) { | ||
| 160 | // Skip proxying methods that might return $this | ||
| 161 | foreach (preg_split('/[()|&]++/', self::exportType($method) ?? 'static') as $type) { | ||
| 162 | if (\in_array($type = ltrim($type, '?'), ['static', 'object'], true)) { | ||
| 163 | continue 2; | ||
| 164 | } | ||
| 165 | foreach ([$class, ...$interfaces] as $r) { | ||
| 166 | if ($r && is_a($r->name, $type, true)) { | ||
| 167 | continue 3; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | $body = <<<EOPHP | ||
| 174 | if (isset(\$this->lazyObjectState)) { | ||
| 175 | return (\$this->lazyObjectState->realInstance ??= (\$this->lazyObjectState->initializer)())->{$method->name}({$args}); | ||
| 176 | } | ||
| 177 | |||
| 178 | return {$parentCall}; | ||
| 179 | EOPHP; | ||
| 180 | } | ||
| 181 | $methods[$lcName] = " {$signature}\n {\n{$body}\n }"; | ||
| 182 | } | ||
| 183 | |||
| 184 | $types = $interfaces = array_unique(array_column($interfaces, 'name')); | ||
| 185 | $interfaces[] = LazyObjectInterface::class; | ||
| 186 | $interfaces = implode(', \\', $interfaces); | ||
| 187 | $parent = $class ? ' extends \\'.$class->name : ''; | ||
| 188 | array_unshift($types, $class ? 'parent' : ''); | ||
| 189 | $type = ltrim(implode('&\\', $types), '&'); | ||
| 190 | |||
| 191 | if (!$class) { | ||
| 192 | $trait = new \ReflectionMethod(LazyProxyTrait::class, 'initializeLazyObject'); | ||
| 193 | $body = \array_slice(file($trait->getFileName()), $trait->getStartLine() - 1, $trait->getEndLine() - $trait->getStartLine()); | ||
| 194 | $body[0] = str_replace('): parent', '): '.$type, $body[0]); | ||
| 195 | $methods = ['initializeLazyObject' => implode('', $body).' }'] + $methods; | ||
| 196 | } | ||
| 197 | $body = $methods ? "\n".implode("\n\n", $methods)."\n" : ''; | ||
| 198 | $propertyScopes = $class ? self::exportPropertyScopes($class->name) : '[]'; | ||
| 199 | |||
| 200 | if ( | ||
| 201 | $class?->hasMethod('__unserialize') | ||
| 202 | && !$class->getMethod('__unserialize')->getParameters()[0]->getType() | ||
| 203 | ) { | ||
| 204 | // fix contravariance type problem when $class declares a `__unserialize()` method without typehint. | ||
| 205 | $lazyProxyTraitStatement = <<<EOPHP | ||
| 206 | use \Symfony\Component\VarExporter\LazyProxyTrait { | ||
| 207 | __unserialize as private __doUnserialize; | ||
| 208 | } | ||
| 209 | EOPHP; | ||
| 210 | |||
| 211 | $body .= <<<EOPHP | ||
| 212 | |||
| 213 | public function __unserialize(\$data): void | ||
| 214 | { | ||
| 215 | \$this->__doUnserialize(\$data); | ||
| 216 | } | ||
| 217 | |||
| 218 | EOPHP; | ||
| 219 | } else { | ||
| 220 | $lazyProxyTraitStatement = <<<EOPHP | ||
| 221 | use \Symfony\Component\VarExporter\LazyProxyTrait; | ||
| 222 | EOPHP; | ||
| 223 | } | ||
| 224 | |||
| 225 | return <<<EOPHP | ||
| 226 | {$parent} implements \\{$interfaces} | ||
| 227 | { | ||
| 228 | {$lazyProxyTraitStatement} | ||
| 229 | |||
| 230 | private const LAZY_OBJECT_PROPERTY_SCOPES = {$propertyScopes}; | ||
| 231 | {$body}} | ||
| 232 | |||
| 233 | // Help opcache.preload discover always-needed symbols | ||
| 234 | class_exists(\Symfony\Component\VarExporter\Internal\Hydrator::class); | ||
| 235 | class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectRegistry::class); | ||
| 236 | class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); | ||
| 237 | |||
| 238 | EOPHP; | ||
| 239 | } | ||
| 240 | |||
| 241 | public static function exportSignature(\ReflectionFunctionAbstract $function, bool $withParameterTypes = true, ?string &$args = null): string | ||
| 242 | { | ||
| 243 | $byRefIndex = 0; | ||
| 244 | $args = ''; | ||
| 245 | $param = null; | ||
| 246 | $parameters = []; | ||
| 247 | $namespace = $function instanceof \ReflectionMethod ? $function->class : $function->getNamespaceName().'\\'; | ||
| 248 | $namespace = substr($namespace, 0, strrpos($namespace, '\\') ?: 0); | ||
| 249 | foreach ($function->getParameters() as $param) { | ||
| 250 | $parameters[] = ($param->getAttributes(\SensitiveParameter::class) ? '#[\SensitiveParameter] ' : '') | ||
| 251 | .($withParameterTypes && $param->hasType() ? self::exportType($param).' ' : '') | ||
| 252 | .($param->isPassedByReference() ? '&' : '') | ||
| 253 | .($param->isVariadic() ? '...' : '').'$'.$param->name | ||
| 254 | .($param->isOptional() && !$param->isVariadic() ? ' = '.self::exportDefault($param, $namespace) : ''); | ||
| 255 | if ($param->isPassedByReference()) { | ||
| 256 | $byRefIndex = 1 + $param->getPosition(); | ||
| 257 | } | ||
| 258 | $args .= ($param->isVariadic() ? '...$' : '$').$param->name.', '; | ||
| 259 | } | ||
| 260 | |||
| 261 | if (!$param || !$byRefIndex) { | ||
| 262 | $args = '...\func_get_args()'; | ||
| 263 | } elseif ($param->isVariadic()) { | ||
| 264 | $args = substr($args, 0, -2); | ||
| 265 | } else { | ||
| 266 | $args = explode(', ', $args, 1 + $byRefIndex); | ||
| 267 | $args[$byRefIndex] = sprintf('...\array_slice(\func_get_args(), %d)', $byRefIndex); | ||
| 268 | $args = implode(', ', $args); | ||
| 269 | } | ||
| 270 | |||
| 271 | $signature = 'function '.($function->returnsReference() ? '&' : '') | ||
| 272 | .($function->isClosure() ? '' : $function->name).'('.implode(', ', $parameters).')'; | ||
| 273 | |||
| 274 | if ($function instanceof \ReflectionMethod) { | ||
| 275 | $signature = ($function->isPublic() ? 'public ' : ($function->isProtected() ? 'protected ' : 'private ')) | ||
| 276 | .($function->isStatic() ? 'static ' : '').$signature; | ||
| 277 | } | ||
| 278 | if ($function->hasReturnType()) { | ||
| 279 | $signature .= ': '.self::exportType($function); | ||
| 280 | } | ||
| 281 | |||
| 282 | static $getPrototype; | ||
| 283 | $getPrototype ??= (new \ReflectionMethod(\ReflectionMethod::class, 'getPrototype'))->invoke(...); | ||
| 284 | |||
| 285 | while ($function) { | ||
| 286 | if ($function->hasTentativeReturnType()) { | ||
| 287 | return '#[\ReturnTypeWillChange] '.$signature; | ||
| 288 | } | ||
| 289 | |||
| 290 | try { | ||
| 291 | $function = $function instanceof \ReflectionMethod && $function->isAbstract() ? false : $getPrototype($function); | ||
| 292 | } catch (\ReflectionException) { | ||
| 293 | break; | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | return $signature; | ||
| 298 | } | ||
| 299 | |||
| 300 | public static function exportType(\ReflectionFunctionAbstract|\ReflectionProperty|\ReflectionParameter $owner, bool $noBuiltin = false, ?\ReflectionType $type = null): ?string | ||
| 301 | { | ||
| 302 | if (!$type ??= $owner instanceof \ReflectionFunctionAbstract ? $owner->getReturnType() : $owner->getType()) { | ||
| 303 | return null; | ||
| 304 | } | ||
| 305 | $class = null; | ||
| 306 | $types = []; | ||
| 307 | if ($type instanceof \ReflectionUnionType) { | ||
| 308 | $reflectionTypes = $type->getTypes(); | ||
| 309 | $glue = '|'; | ||
| 310 | } elseif ($type instanceof \ReflectionIntersectionType) { | ||
| 311 | $reflectionTypes = $type->getTypes(); | ||
| 312 | $glue = '&'; | ||
| 313 | } else { | ||
| 314 | $reflectionTypes = [$type]; | ||
| 315 | $glue = null; | ||
| 316 | } | ||
| 317 | |||
| 318 | foreach ($reflectionTypes as $type) { | ||
| 319 | if ($type instanceof \ReflectionIntersectionType) { | ||
| 320 | if ('' !== $name = '('.self::exportType($owner, $noBuiltin, $type).')') { | ||
| 321 | $types[] = $name; | ||
| 322 | } | ||
| 323 | continue; | ||
| 324 | } | ||
| 325 | $name = $type->getName(); | ||
| 326 | |||
| 327 | if ($noBuiltin && $type->isBuiltin()) { | ||
| 328 | continue; | ||
| 329 | } | ||
| 330 | if (\in_array($name, ['parent', 'self'], true) && $class ??= $owner->getDeclaringClass()) { | ||
| 331 | $name = 'parent' === $name ? ($class->getParentClass() ?: null)?->name ?? 'parent' : $class->name; | ||
| 332 | } | ||
| 333 | |||
| 334 | $types[] = ($noBuiltin || $type->isBuiltin() || 'static' === $name ? '' : '\\').$name; | ||
| 335 | } | ||
| 336 | |||
| 337 | if (!$types) { | ||
| 338 | return ''; | ||
| 339 | } | ||
| 340 | if (null === $glue) { | ||
| 341 | return (!$noBuiltin && $type->allowsNull() && !\in_array($name, ['mixed', 'null'], true) ? '?' : '').$types[0]; | ||
| 342 | } | ||
| 343 | sort($types); | ||
| 344 | |||
| 345 | return implode($glue, $types); | ||
| 346 | } | ||
| 347 | |||
| 348 | private static function exportPropertyScopes(string $parent): string | ||
| 349 | { | ||
| 350 | $propertyScopes = Hydrator::$propertyScopes[$parent] ??= Hydrator::getPropertyScopes($parent); | ||
| 351 | uksort($propertyScopes, 'strnatcmp'); | ||
| 352 | foreach ($propertyScopes as $k => $v) { | ||
| 353 | unset($propertyScopes[$k][3]); | ||
| 354 | } | ||
| 355 | $propertyScopes = VarExporter::export($propertyScopes); | ||
| 356 | $propertyScopes = str_replace(VarExporter::export($parent), 'parent::class', $propertyScopes); | ||
| 357 | $propertyScopes = preg_replace("/(?|(,)\n( ) |\n |,\n (\]))/", '$1$2', $propertyScopes); | ||
| 358 | $propertyScopes = str_replace("\n", "\n ", $propertyScopes); | ||
| 359 | |||
| 360 | return $propertyScopes; | ||
| 361 | } | ||
| 362 | |||
| 363 | private static function exportDefault(\ReflectionParameter $param, $namespace): string | ||
| 364 | { | ||
| 365 | $default = rtrim(substr(explode('$'.$param->name.' = ', (string) $param, 2)[1] ?? '', 0, -2)); | ||
| 366 | |||
| 367 | if (\in_array($default, ['<default>', 'NULL'], true)) { | ||
| 368 | return 'null'; | ||
| 369 | } | ||
| 370 | if (str_ends_with($default, "...'") && preg_match("/^'(?:[^'\\\\]*+(?:\\\\.)*+)*+'$/", $default)) { | ||
| 371 | return VarExporter::export($param->getDefaultValue()); | ||
| 372 | } | ||
| 373 | |||
| 374 | $regexp = "/(\"(?:[^\"\\\\]*+(?:\\\\.)*+)*+\"|'(?:[^'\\\\]*+(?:\\\\.)*+)*+')/"; | ||
| 375 | $parts = preg_split($regexp, $default, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); | ||
| 376 | |||
| 377 | $regexp = '/([\[\( ]|^)([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z0-9_\x7f-\xff]++)*+)(\(?)(?!: )/'; | ||
| 378 | $callback = (false !== strpbrk($default, "\\:('") && $class = $param->getDeclaringClass()) | ||
| 379 | ? fn ($m) => $m[1].match ($m[2]) { | ||
| 380 | 'new', 'false', 'true', 'null' => $m[2], | ||
| 381 | 'NULL' => 'null', | ||
| 382 | 'self' => '\\'.$class->name, | ||
| 383 | 'namespace\\parent', | ||
| 384 | 'parent' => ($parent = $class->getParentClass()) ? '\\'.$parent->name : 'parent', | ||
| 385 | default => self::exportSymbol($m[2], '(' !== $m[3], $namespace), | ||
| 386 | }.$m[3] | ||
| 387 | : fn ($m) => $m[1].match ($m[2]) { | ||
| 388 | 'new', 'false', 'true', 'null', 'self', 'parent' => $m[2], | ||
| 389 | 'NULL' => 'null', | ||
| 390 | default => self::exportSymbol($m[2], '(' !== $m[3], $namespace), | ||
| 391 | }.$m[3]; | ||
| 392 | |||
| 393 | return implode('', array_map(fn ($part) => match ($part[0]) { | ||
| 394 | '"' => $part, // for internal classes only | ||
| 395 | "'" => false !== strpbrk($part, "\\\0\r\n") ? '"'.substr(str_replace(['$', "\0", "\r", "\n"], ['\$', '\0', '\r', '\n'], $part), 1, -1).'"' : $part, | ||
| 396 | default => preg_replace_callback($regexp, $callback, $part), | ||
| 397 | }, $parts)); | ||
| 398 | } | ||
| 399 | |||
| 400 | private static function exportSymbol(string $symbol, bool $mightBeRootConst, string $namespace): string | ||
| 401 | { | ||
| 402 | if (!$mightBeRootConst | ||
| 403 | || false === ($ns = strrpos($symbol, '\\')) | ||
| 404 | || substr($symbol, 0, $ns) !== $namespace | ||
| 405 | || \defined($symbol) | ||
| 406 | || !\defined(substr($symbol, $ns + 1)) | ||
| 407 | ) { | ||
| 408 | return '\\'.$symbol; | ||
| 409 | } | ||
| 410 | |||
| 411 | return '\\'.substr($symbol, $ns + 1); | ||
| 412 | } | ||
| 413 | } | ||
