mirror of
https://github.com/Combodo/iTop.git
synced 2026-05-19 07:12:26 +02:00
N°9319 increase php min. version to 8.2 (#887)
* Update minimum PHP version to 8.2 * Fix previous wrong resolution of merge conflict
This commit is contained in:
@@ -192,8 +192,7 @@ abstract class AbstractTagAwareAdapter implements TagAwareAdapterInterface, TagA
|
||||
if (\is_array($e) || 1 === \count($values)) {
|
||||
foreach (\is_array($e) ? $e : array_keys($values) as $id) {
|
||||
$ok = false;
|
||||
$v = $values[$id];
|
||||
$type = get_debug_type($v);
|
||||
$type = \array_key_exists($id, $values) ? get_debug_type($values[$id]) : 'unknown';
|
||||
$message = \sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': '.$e->getMessage() : '.');
|
||||
CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => get_debug_type($this)]);
|
||||
}
|
||||
|
||||
24
lib/symfony/cache/Adapter/ArrayAdapter.php
vendored
24
lib/symfony/cache/Adapter/ArrayAdapter.php
vendored
@@ -34,6 +34,7 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
|
||||
private array $values = [];
|
||||
private array $tags = [];
|
||||
private array $expiries = [];
|
||||
private array $explicitExpiries = [];
|
||||
private int $defaultLifetime;
|
||||
private float $maxLifetime;
|
||||
private int $maxItems;
|
||||
@@ -58,7 +59,7 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
|
||||
$this->maxLifetime = $maxLifetime;
|
||||
$this->maxItems = $maxItems;
|
||||
self::$createCacheItem ??= \Closure::bind(
|
||||
static function ($key, $value, $isHit, $tags) {
|
||||
static function ($key, $value, $isHit, $tags, $expiry = null) {
|
||||
$item = new CacheItem();
|
||||
$item->key = $key;
|
||||
$item->value = $value;
|
||||
@@ -66,6 +67,9 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
|
||||
if (null !== $tags) {
|
||||
$item->metadata[CacheItem::METADATA_TAGS] = $tags;
|
||||
}
|
||||
if (null !== $expiry) {
|
||||
$item->metadata[CacheItem::METADATA_EXPIRY] = $expiry;
|
||||
}
|
||||
|
||||
return $item;
|
||||
},
|
||||
@@ -126,7 +130,7 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
|
||||
$value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
|
||||
}
|
||||
|
||||
return (self::$createCacheItem)($key, $value, $isHit, $this->tags[$key] ?? null);
|
||||
return (self::$createCacheItem)($key, $value, $isHit, $this->tags[$key] ?? null, $this->explicitExpiries[$key] ?? null);
|
||||
}
|
||||
|
||||
public function getItems(array $keys = []): iterable
|
||||
@@ -139,7 +143,7 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
|
||||
public function deleteItem(mixed $key): bool
|
||||
{
|
||||
\assert('' !== CacheItem::validateKey($key));
|
||||
unset($this->values[$key], $this->tags[$key], $this->expiries[$key]);
|
||||
unset($this->values[$key], $this->tags[$key], $this->expiries[$key], $this->explicitExpiries[$key]);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -193,13 +197,19 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
|
||||
break;
|
||||
}
|
||||
|
||||
unset($this->values[$k], $this->tags[$k], $this->expiries[$k]);
|
||||
unset($this->values[$k], $this->tags[$k], $this->expiries[$k], $this->explicitExpiries[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->values[$key] = $value;
|
||||
$this->expiries[$key] = $expiry ?? \PHP_INT_MAX;
|
||||
|
||||
if (null !== $item["\0*\0expiry"] && \PHP_INT_MAX !== $this->expiries[$key]) {
|
||||
$this->explicitExpiries[$key] = $this->expiries[$key];
|
||||
} else {
|
||||
unset($this->explicitExpiries[$key]);
|
||||
}
|
||||
|
||||
if (null === $this->tags[$key] = $item["\0*\0newMetadata"][CacheItem::METADATA_TAGS] ?? null) {
|
||||
unset($this->tags[$key]);
|
||||
}
|
||||
@@ -224,7 +234,7 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
|
||||
|
||||
foreach ($this->values as $key => $value) {
|
||||
if (!isset($this->expiries[$key]) || $this->expiries[$key] <= $now || str_starts_with($key, $prefix)) {
|
||||
unset($this->values[$key], $this->tags[$key], $this->expiries[$key]);
|
||||
unset($this->values[$key], $this->tags[$key], $this->expiries[$key], $this->explicitExpiries[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +243,7 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
|
||||
}
|
||||
}
|
||||
|
||||
$this->values = $this->tags = $this->expiries = [];
|
||||
$this->values = $this->tags = $this->expiries = $this->explicitExpiries = [];
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -290,7 +300,7 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
|
||||
}
|
||||
unset($keys[$i]);
|
||||
|
||||
yield $key => $f($key, $value, $isHit, $this->tags[$key] ?? null);
|
||||
yield $key => $f($key, $value, $isHit, $this->tags[$key] ?? null, $this->explicitExpiries[$key] ?? null);
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
|
||||
1
lib/symfony/cache/Adapter/ChainAdapter.php
vendored
1
lib/symfony/cache/Adapter/ChainAdapter.php
vendored
@@ -79,6 +79,7 @@ class ChainAdapter implements AdapterInterface, CacheInterface, PruneableInterfa
|
||||
$item->expiresAt(\DateTimeImmutable::createFromFormat('U.u', \sprintf('%.6F', $item->metadata[CacheItem::METADATA_EXPIRY])));
|
||||
} elseif (0 < $defaultLifetime) {
|
||||
$item->expiresAfter($defaultLifetime);
|
||||
$item->newMetadata[CacheItem::METADATA_EXPIRY] = $item->expiry;
|
||||
}
|
||||
|
||||
return $item;
|
||||
|
||||
4
lib/symfony/cache/CacheItem.php
vendored
4
lib/symfony/cache/CacheItem.php
vendored
@@ -170,6 +170,10 @@ final class CacheItem implements ItemInterface
|
||||
}
|
||||
$valueWrapper = self::VALUE_WRAPPER;
|
||||
|
||||
if ($this->value instanceof $valueWrapper) {
|
||||
return new $valueWrapper($this->value->value, $m + ['expiry' => $this->expiry] + $this->value->metadata);
|
||||
}
|
||||
|
||||
return new $valueWrapper($this->value, $m + ['expiry' => $this->expiry]);
|
||||
}
|
||||
|
||||
|
||||
31
lib/symfony/cache/LockRegistry.php
vendored
31
lib/symfony/cache/LockRegistry.php
vendored
@@ -65,12 +65,14 @@ final class LockRegistry
|
||||
/**
|
||||
* Defines a set of existing files that will be used as keys to acquire locks.
|
||||
*
|
||||
* @return array The previously defined set of files
|
||||
* @param list<string> $files A list of existing files
|
||||
*
|
||||
* @return list<string> The previously defined set of files
|
||||
*/
|
||||
public static function setFiles(array $files): array
|
||||
{
|
||||
$previousFiles = self::$files;
|
||||
self::$files = $files;
|
||||
self::$files = array_values($files);
|
||||
|
||||
foreach (self::$openedFiles as $file) {
|
||||
if ($file) {
|
||||
@@ -97,7 +99,7 @@ final class LockRegistry
|
||||
}
|
||||
|
||||
self::$signalingException ??= unserialize("O:9:\"Exception\":1:{s:16:\"\0Exception\0trace\";a:0:{}}");
|
||||
self::$signalingCallback ??= fn () => throw self::$signalingException;
|
||||
self::$signalingCallback ??= static fn () => throw self::$signalingException;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
@@ -123,14 +125,33 @@ final class LockRegistry
|
||||
}
|
||||
// if we failed the race, retry locking in blocking mode to wait for the winner
|
||||
$logger?->info('Item "{key}" is locked, waiting for it to be released', ['key' => $item->getKey()]);
|
||||
flock($lock, \LOCK_SH);
|
||||
|
||||
$deadline = microtime(true) + 30.0;
|
||||
$acquired = false;
|
||||
do {
|
||||
if ($acquired = flock($lock, \LOCK_SH | \LOCK_NB)) {
|
||||
break;
|
||||
}
|
||||
usleep(100_000);
|
||||
} while (microtime(true) < $deadline);
|
||||
|
||||
if (!$acquired) {
|
||||
$logger?->warning('Lock on item "{key}" timed out, evicting slot', ['key' => $item->getKey()]);
|
||||
unset(self::$files[$key]);
|
||||
self::setFiles(self::$files);
|
||||
$lock = null;
|
||||
|
||||
return self::compute($callback, $item, $save, $pool, $setMetadata, $logger, $beta);
|
||||
}
|
||||
|
||||
if (\INF === $beta) {
|
||||
$logger?->info('Force-recomputing item "{key}"', ['key' => $item->getKey()]);
|
||||
continue;
|
||||
}
|
||||
} finally {
|
||||
flock($lock, \LOCK_UN);
|
||||
if ($lock) {
|
||||
flock($lock, \LOCK_UN);
|
||||
}
|
||||
unset(self::$lockedFiles[$key]);
|
||||
}
|
||||
|
||||
|
||||
7
lib/symfony/cache/Psr16Cache.php
vendored
7
lib/symfony/cache/Psr16Cache.php
vendored
@@ -18,6 +18,7 @@ use Psr\SimpleCache\CacheInterface;
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
use Symfony\Contracts\Cache\ItemInterface;
|
||||
|
||||
/**
|
||||
* Turns a PSR-6 cache into a PSR-16 one.
|
||||
@@ -68,6 +69,12 @@ class Psr16Cache implements CacheInterface, PruneableInterface, ResettableInterf
|
||||
};
|
||||
self::$packCacheItem ??= \Closure::bind(
|
||||
static function (CacheItem $item) {
|
||||
// Only re-pack if there's timing metadata (for Psr16Adapter compatibility)
|
||||
// Don't re-pack if only tags metadata exists (TagAwareAdapter direct use case)
|
||||
if (!isset($item->metadata[ItemInterface::METADATA_CTIME]) && !isset($item->metadata[ItemInterface::METADATA_EXPIRY])) {
|
||||
return $item->value;
|
||||
}
|
||||
|
||||
$item->newMetadata = $item->metadata;
|
||||
|
||||
return $item->pack();
|
||||
|
||||
37
lib/symfony/cache/Traits/Relay/Relay21Trait.php
vendored
Normal file
37
lib/symfony/cache/Traits/Relay/Relay21Trait.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Traits\Relay;
|
||||
|
||||
if (version_compare(phpversion('relay'), '0.21.0', '>=')) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
trait Relay21Trait
|
||||
{
|
||||
public function gcra($key, $maxBurst, $requestsPerPeriod, $period, $numRequests = 0): \Relay\Relay|array|false
|
||||
{
|
||||
return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->gcra(...\func_get_args());
|
||||
}
|
||||
|
||||
public function hotkeys($subcmd, $args = null): \Relay\Relay|array|bool
|
||||
{
|
||||
return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hotkeys(...\func_get_args());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
trait Relay21Trait
|
||||
{
|
||||
}
|
||||
}
|
||||
2
lib/symfony/cache/Traits/RelayProxy.php
vendored
2
lib/symfony/cache/Traits/RelayProxy.php
vendored
@@ -26,6 +26,7 @@ use Symfony\Component\Cache\Traits\Relay\Relay11Trait;
|
||||
use Symfony\Component\Cache\Traits\Relay\Relay121Trait;
|
||||
use Symfony\Component\Cache\Traits\Relay\Relay12Trait;
|
||||
use Symfony\Component\Cache\Traits\Relay\Relay20Trait;
|
||||
use Symfony\Component\Cache\Traits\Relay\Relay21Trait;
|
||||
use Symfony\Component\Cache\Traits\Relay\SwapdbTrait;
|
||||
use Symfony\Component\VarExporter\LazyObjectInterface;
|
||||
use Symfony\Component\VarExporter\LazyProxyTrait;
|
||||
@@ -60,6 +61,7 @@ class RelayProxy extends \Relay\Relay implements ResetInterface, LazyObjectInter
|
||||
use Relay12Trait;
|
||||
use Relay121Trait;
|
||||
use Relay20Trait;
|
||||
use Relay21Trait;
|
||||
use SwapdbTrait;
|
||||
|
||||
private const LAZY_OBJECT_PROPERTY_SCOPES = [];
|
||||
|
||||
@@ -133,6 +133,10 @@ class OutputFormatter implements WrappableOutputFormatterInterface
|
||||
return '';
|
||||
}
|
||||
|
||||
// For ASCII-only strings, byte positions equal character positions,
|
||||
// so we can use native strlen/substr which is much faster than Helper::length/substr.
|
||||
$isAscii = !preg_match('/[\x80-\xFF]/', $message);
|
||||
|
||||
$offset = 0;
|
||||
$output = '';
|
||||
$openTagRegex = '[a-z](?:[^\\\\<>]*+ | \\\\.)*';
|
||||
@@ -147,11 +151,17 @@ class OutputFormatter implements WrappableOutputFormatterInterface
|
||||
continue;
|
||||
}
|
||||
|
||||
// convert byte position to character position.
|
||||
$pos = Helper::length(substr($message, 0, $pos));
|
||||
// add the text up to the next tag
|
||||
$output .= $this->applyCurrentStyle(Helper::substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
|
||||
$offset = $pos + Helper::length($text);
|
||||
if ($isAscii) {
|
||||
// For ASCII, byte position = character position, no conversion needed
|
||||
$output .= $this->applyCurrentStyle(substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
|
||||
$offset = $pos + \strlen($text);
|
||||
} else {
|
||||
// convert byte position to character position.
|
||||
$pos = Helper::length(substr($message, 0, $pos));
|
||||
// add the text up to the next tag
|
||||
$output .= $this->applyCurrentStyle(Helper::substr($message, $offset, $pos - $offset), $output, $width, $currentLineLength);
|
||||
$offset = $pos + Helper::length($text);
|
||||
}
|
||||
|
||||
// opening tag?
|
||||
if ($open = '/' !== $text[1]) {
|
||||
@@ -172,7 +182,7 @@ class OutputFormatter implements WrappableOutputFormatterInterface
|
||||
}
|
||||
}
|
||||
|
||||
$output .= $this->applyCurrentStyle(Helper::substr($message, $offset), $output, $width, $currentLineLength);
|
||||
$output .= $this->applyCurrentStyle($isAscii ? substr($message, $offset) : Helper::substr($message, $offset), $output, $width, $currentLineLength);
|
||||
|
||||
return strtr($output, ["\0" => '\\', '\\<' => '<', '\\>' => '>']);
|
||||
}
|
||||
|
||||
@@ -142,12 +142,27 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
|
||||
*/
|
||||
private function openOutputStream()
|
||||
{
|
||||
static $stdout;
|
||||
|
||||
if ($stdout) {
|
||||
return $stdout;
|
||||
}
|
||||
|
||||
if (!$this->hasStdoutSupport()) {
|
||||
return fopen('php://output', 'w');
|
||||
return $stdout = fopen('php://output', 'w');
|
||||
}
|
||||
|
||||
// Use STDOUT when possible to prevent from opening too many file descriptors
|
||||
return \defined('STDOUT') ? \STDOUT : (@fopen('php://stdout', 'w') ?: fopen('php://output', 'w'));
|
||||
if (!\defined('STDOUT')) {
|
||||
return $stdout = @fopen('php://stdout', 'w') ?: fopen('php://output', 'w');
|
||||
}
|
||||
|
||||
// On Windows, STDOUT is opened in text mode; reopen in binary mode to prevent \n to \r\n conversion
|
||||
if ('\\' === \DIRECTORY_SEPARATOR) {
|
||||
return $stdout = @fopen('php://stdout', 'w') ?: \STDOUT;
|
||||
}
|
||||
|
||||
return $stdout = \STDOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,11 +170,26 @@ class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface
|
||||
*/
|
||||
private function openErrorStream()
|
||||
{
|
||||
static $stderr;
|
||||
|
||||
if ($stderr) {
|
||||
return $stderr;
|
||||
}
|
||||
|
||||
if (!$this->hasStderrSupport()) {
|
||||
return fopen('php://output', 'w');
|
||||
return $stderr = fopen('php://output', 'w');
|
||||
}
|
||||
|
||||
// Use STDERR when possible to prevent from opening too many file descriptors
|
||||
return \defined('STDERR') ? \STDERR : (@fopen('php://stderr', 'w') ?: fopen('php://output', 'w'));
|
||||
if (!\defined('STDERR')) {
|
||||
return $stderr = @fopen('php://stderr', 'w') ?: fopen('php://output', 'w');
|
||||
}
|
||||
|
||||
// On Windows, STDERR is opened in text mode; reopen in binary mode to prevent \n → \r\n conversion
|
||||
if ('\\' === \DIRECTORY_SEPARATOR) {
|
||||
return $stderr = @fopen('php://stderr', 'w') ?: \STDERR;
|
||||
}
|
||||
|
||||
return $stderr ??= \STDERR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class ApplicationTester
|
||||
*/
|
||||
public function run(array $input, array $options = []): int
|
||||
{
|
||||
$prevShellVerbosity = getenv('SHELL_VERBOSITY');
|
||||
$prevShellVerbosity = [getenv('SHELL_VERBOSITY'), $_ENV['SHELL_VERBOSITY'] ?? false, $_SERVER['SHELL_VERBOSITY'] ?? false];
|
||||
|
||||
try {
|
||||
$this->input = new ArrayInput($input);
|
||||
@@ -63,22 +63,35 @@ class ApplicationTester
|
||||
|
||||
$this->initOutput($options);
|
||||
|
||||
// Temporarily clear SHELL_VERBOSITY to prevent Application::configureIO
|
||||
// from overriding the interactive and verbosity settings set above
|
||||
if (\function_exists('putenv')) {
|
||||
@putenv('SHELL_VERBOSITY');
|
||||
}
|
||||
unset($_ENV['SHELL_VERBOSITY'], $_SERVER['SHELL_VERBOSITY']);
|
||||
|
||||
return $this->statusCode = $this->application->run($this->input, $this->output);
|
||||
} finally {
|
||||
// SHELL_VERBOSITY is set by Application::configureIO so we need to unset/reset it
|
||||
// to its previous value to avoid one test's verbosity to spread to the following tests
|
||||
if (false === $prevShellVerbosity) {
|
||||
if (false === $prevShellVerbosity[0]) {
|
||||
if (\function_exists('putenv')) {
|
||||
@putenv('SHELL_VERBOSITY');
|
||||
}
|
||||
unset($_ENV['SHELL_VERBOSITY']);
|
||||
unset($_SERVER['SHELL_VERBOSITY']);
|
||||
} else {
|
||||
if (\function_exists('putenv')) {
|
||||
@putenv('SHELL_VERBOSITY='.$prevShellVerbosity);
|
||||
@putenv('SHELL_VERBOSITY='.$prevShellVerbosity[0]);
|
||||
}
|
||||
$_ENV['SHELL_VERBOSITY'] = $prevShellVerbosity;
|
||||
$_SERVER['SHELL_VERBOSITY'] = $prevShellVerbosity;
|
||||
}
|
||||
if (false === $prevShellVerbosity[1]) {
|
||||
unset($_ENV['SHELL_VERBOSITY']);
|
||||
} else {
|
||||
$_ENV['SHELL_VERBOSITY'] = $prevShellVerbosity[1];
|
||||
}
|
||||
if (false === $prevShellVerbosity[2]) {
|
||||
unset($_SERVER['SHELL_VERBOSITY']);
|
||||
} else {
|
||||
$_SERVER['SHELL_VERBOSITY'] = $prevShellVerbosity[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
lib/symfony/debug-bundle/CHANGELOG.md
Normal file
8
lib/symfony/debug-bundle/CHANGELOG.md
Normal file
@@ -0,0 +1,8 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
4.1.0
|
||||
-----
|
||||
|
||||
* Added the `server:dump` command to run a server collecting and displaying
|
||||
dumps on a single place with multiple formats support
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DebugBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\VarDumper\Command\ServerDumpCommand;
|
||||
use Symfony\Component\VarDumper\Server\DumpServer;
|
||||
|
||||
/**
|
||||
* A placeholder command easing VarDumper server discovery.
|
||||
*
|
||||
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
#[AsCommand(name: 'server:dump', description: 'Start a dump server that collects and displays dumps in a single place')]
|
||||
class ServerDumpPlaceholderCommand extends Command
|
||||
{
|
||||
private ServerDumpCommand $replacedCommand;
|
||||
|
||||
public function __construct(?DumpServer $server = null, array $descriptors = [])
|
||||
{
|
||||
$this->replacedCommand = new ServerDumpCommand((new \ReflectionClass(DumpServer::class))->newInstanceWithoutConstructor(), $descriptors);
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setDefinition($this->replacedCommand->getDefinition());
|
||||
$this->setHelp($this->replacedCommand->getHelp());
|
||||
$this->setDescription($this->replacedCommand->getDescription());
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
(new SymfonyStyle($input, $output))->getErrorStyle()->warning('In order to use the VarDumper server, set the "debug.dump_destination" config option to "tcp://%env(VAR_DUMPER_SERVER)%"');
|
||||
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
72
lib/symfony/debug-bundle/DebugBundle.php
Normal file
72
lib/symfony/debug-bundle/DebugBundle.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DebugBundle;
|
||||
|
||||
use Symfony\Bundle\DebugBundle\DependencyInjection\Compiler\DumpDataCollectorPass;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
use Symfony\Component\VarDumper\VarDumper;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class DebugBundle extends Bundle
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
if ($this->container->getParameter('kernel.debug')) {
|
||||
$container = $this->container;
|
||||
|
||||
// This code is here to lazy load the dump stack. This default
|
||||
// configuration is overridden in CLI mode on 'console.command' event.
|
||||
// The dump data collector is used by default, so dump output is sent to
|
||||
// the WDT. In a CLI context, if dump is used too soon, the data collector
|
||||
// will buffer it, and release it at the end of the script.
|
||||
VarDumper::setHandler(function ($var, ?string $label = null) use ($container) {
|
||||
$dumper = $container->get('data_collector.dump');
|
||||
$cloner = $container->get('var_dumper.cloner');
|
||||
$handler = function ($var, ?string $label = null) use ($dumper, $cloner) {
|
||||
$var = $cloner->cloneVar($var);
|
||||
if (null !== $label) {
|
||||
$var = $var->withContext(['label' => $label]);
|
||||
}
|
||||
|
||||
$dumper->dump($var);
|
||||
};
|
||||
VarDumper::setHandler($handler);
|
||||
$handler($var, $label);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function build(ContainerBuilder $container)
|
||||
{
|
||||
parent::build($container);
|
||||
|
||||
$container->addCompilerPass(new DumpDataCollectorPass());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function registerCommands(Application $application)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DebugBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Registers the file link format for the {@link \Symfony\Component\HttpKernel\DataCollector\DumpDataCollector}.
|
||||
*
|
||||
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*/
|
||||
class DumpDataCollectorPass implements CompilerPassInterface
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition('data_collector.dump')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$definition = $container->getDefinition('data_collector.dump');
|
||||
|
||||
if (!$container->has('.virtual_request_stack')) {
|
||||
$definition->replaceArgument(3, new Reference('request_stack'));
|
||||
}
|
||||
|
||||
if (!$container->hasParameter('web_profiler.debug_toolbar.mode') || WebDebugToolbarListener::DISABLED === $container->getParameter('web_profiler.debug_toolbar.mode')) {
|
||||
$definition->replaceArgument(3, null);
|
||||
}
|
||||
|
||||
if (!$container->hasParameter('kernel.runtime_mode.web')) {
|
||||
$definition->replaceArgument(5, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DebugBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
|
||||
/**
|
||||
* DebugExtension configuration structure.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class Configuration implements ConfigurationInterface
|
||||
{
|
||||
public function getConfigTreeBuilder(): TreeBuilder
|
||||
{
|
||||
$treeBuilder = new TreeBuilder('debug');
|
||||
|
||||
$rootNode = $treeBuilder->getRootNode();
|
||||
$rootNode->children()
|
||||
->integerNode('max_items')
|
||||
->info('Max number of displayed items past the first level, -1 means no limit')
|
||||
->min(-1)
|
||||
->defaultValue(2500)
|
||||
->end()
|
||||
->integerNode('min_depth')
|
||||
->info('Minimum tree depth to clone all the items, 1 is default')
|
||||
->min(0)
|
||||
->defaultValue(1)
|
||||
->end()
|
||||
->integerNode('max_string_length')
|
||||
->info('Max length of displayed strings, -1 means no limit')
|
||||
->min(-1)
|
||||
->defaultValue(-1)
|
||||
->end()
|
||||
->scalarNode('dump_destination')
|
||||
->info('A stream URL where dumps should be written to')
|
||||
->example('php://stderr, or tcp://%env(VAR_DUMPER_SERVER)% when using the "server:dump" command')
|
||||
->defaultNull()
|
||||
->end()
|
||||
->enumNode('theme')
|
||||
->info('Changes the color of the dump() output when rendered directly on the templating. "dark" (default) or "light"')
|
||||
->example('dark')
|
||||
->values(['dark', 'light'])
|
||||
->defaultValue('dark')
|
||||
->end()
|
||||
;
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
}
|
||||
102
lib/symfony/debug-bundle/DependencyInjection/DebugExtension.php
Normal file
102
lib/symfony/debug-bundle/DependencyInjection/DebugExtension.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DebugBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Bridge\Monolog\Command\ServerLogCommand;
|
||||
use Symfony\Bundle\DebugBundle\Command\ServerDumpPlaceholderCommand;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\VarDumper\Caster\ReflectionCaster;
|
||||
|
||||
/**
|
||||
* DebugExtension.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class DebugExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
$configuration = new Configuration();
|
||||
$config = $this->processConfiguration($configuration, $configs);
|
||||
|
||||
$loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||
$loader->load('services.php');
|
||||
|
||||
$container->getDefinition('var_dumper.cloner')
|
||||
->addMethodCall('setMaxItems', [$config['max_items']])
|
||||
->addMethodCall('setMinDepth', [$config['min_depth']])
|
||||
->addMethodCall('setMaxString', [$config['max_string_length']])
|
||||
->addMethodCall('addCasters', [ReflectionCaster::UNSET_CLOSURE_FILE_INFO]);
|
||||
|
||||
if ('dark' !== $config['theme']) {
|
||||
$container->getDefinition('var_dumper.html_dumper')
|
||||
->addMethodCall('setTheme', [$config['theme']]);
|
||||
}
|
||||
|
||||
if (null === $config['dump_destination']) {
|
||||
$container->getDefinition('var_dumper.command.server_dump')
|
||||
->setClass(ServerDumpPlaceholderCommand::class)
|
||||
;
|
||||
} elseif (str_starts_with($config['dump_destination'], 'tcp://')) {
|
||||
$container->getDefinition('debug.dump_listener')
|
||||
->replaceArgument(2, new Reference('var_dumper.server_connection'))
|
||||
;
|
||||
$container->getDefinition('data_collector.dump')
|
||||
->replaceArgument(4, new Reference('var_dumper.server_connection'))
|
||||
;
|
||||
$container->getDefinition('var_dumper.dump_server')
|
||||
->replaceArgument(0, $config['dump_destination'])
|
||||
;
|
||||
$container->getDefinition('var_dumper.server_connection')
|
||||
->replaceArgument(0, $config['dump_destination'])
|
||||
;
|
||||
} else {
|
||||
$container->getDefinition('var_dumper.cli_dumper')
|
||||
->replaceArgument(0, $config['dump_destination'])
|
||||
;
|
||||
$container->getDefinition('data_collector.dump')
|
||||
->replaceArgument(4, new Reference('var_dumper.cli_dumper'))
|
||||
;
|
||||
$container->getDefinition('var_dumper.command.server_dump')
|
||||
->setClass(ServerDumpPlaceholderCommand::class)
|
||||
;
|
||||
}
|
||||
|
||||
$container->getDefinition('var_dumper.cli_dumper')
|
||||
->addMethodCall('setDisplayOptions', [[
|
||||
'fileLinkFormat' => new Reference('debug.file_link_formatter', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE),
|
||||
]])
|
||||
;
|
||||
|
||||
if (!class_exists(Command::class) || !class_exists(ServerLogCommand::class)) {
|
||||
$container->removeDefinition('monolog.command.server_log');
|
||||
}
|
||||
}
|
||||
|
||||
public function getXsdValidationBasePath(): string|false
|
||||
{
|
||||
return __DIR__.'/../Resources/config/schema';
|
||||
}
|
||||
|
||||
public function getNamespace(): string
|
||||
{
|
||||
return 'http://symfony.com/schema/dic/debug';
|
||||
}
|
||||
}
|
||||
19
lib/symfony/debug-bundle/LICENSE
Normal file
19
lib/symfony/debug-bundle/LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2014-present Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
13
lib/symfony/debug-bundle/README.md
Normal file
13
lib/symfony/debug-bundle/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
DebugBundle
|
||||
===========
|
||||
|
||||
DebugBundle provides a tight integration of the Symfony VarDumper component and
|
||||
the ServerLogCommand from MonologBridge into the Symfony full-stack framework.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<xsd:schema xmlns="http://symfony.com/schema/dic/debug"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://symfony.com/schema/dic/debug"
|
||||
elementFormDefault="qualified">
|
||||
|
||||
<xsd:element name="config" type="config" />
|
||||
|
||||
<xsd:complexType name="config">
|
||||
<xsd:attribute name="max-items" type="xsd:integer" />
|
||||
<xsd:attribute name="min-depth" type="xsd:integer" />
|
||||
<xsd:attribute name="max-string-length" type="xsd:integer" />
|
||||
<xsd:attribute name="dump-destination" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:schema>
|
||||
141
lib/symfony/debug-bundle/Resources/config/services.php
Normal file
141
lib/symfony/debug-bundle/Resources/config/services.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
|
||||
|
||||
use Monolog\Formatter\FormatterInterface;
|
||||
use Symfony\Bridge\Monolog\Command\ServerLogCommand;
|
||||
use Symfony\Bridge\Monolog\Formatter\ConsoleFormatter;
|
||||
use Symfony\Bridge\Twig\Extension\DumpExtension;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
|
||||
use Symfony\Component\HttpKernel\EventListener\DumpListener;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor;
|
||||
use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor;
|
||||
use Symfony\Component\VarDumper\Command\ServerDumpCommand;
|
||||
use Symfony\Component\VarDumper\Dumper\CliDumper;
|
||||
use Symfony\Component\VarDumper\Dumper\ContextProvider\CliContextProvider;
|
||||
use Symfony\Component\VarDumper\Dumper\ContextProvider\RequestContextProvider;
|
||||
use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
|
||||
use Symfony\Component\VarDumper\Dumper\ContextualizedDumper;
|
||||
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
|
||||
use Symfony\Component\VarDumper\Server\Connection;
|
||||
use Symfony\Component\VarDumper\Server\DumpServer;
|
||||
|
||||
return static function (ContainerConfigurator $container) {
|
||||
$container->parameters()
|
||||
->set('env(VAR_DUMPER_SERVER)', '127.0.0.1:9912')
|
||||
;
|
||||
|
||||
$container->services()
|
||||
|
||||
->set('twig.extension.dump', DumpExtension::class)
|
||||
->args([
|
||||
service('var_dumper.cloner'),
|
||||
service('var_dumper.html_dumper'),
|
||||
])
|
||||
->tag('twig.extension')
|
||||
|
||||
->set('data_collector.dump', DumpDataCollector::class)
|
||||
->public()
|
||||
->args([
|
||||
service('debug.stopwatch')->ignoreOnInvalid(),
|
||||
service('debug.file_link_formatter')->ignoreOnInvalid(),
|
||||
param('kernel.charset'),
|
||||
service('.virtual_request_stack'),
|
||||
null, // var_dumper.cli_dumper or var_dumper.server_connection when debug.dump_destination is set
|
||||
param('kernel.runtime_mode.web'),
|
||||
])
|
||||
->tag('data_collector', [
|
||||
'id' => 'dump',
|
||||
'template' => '@Debug/Profiler/dump.html.twig',
|
||||
'priority' => 240,
|
||||
])
|
||||
|
||||
->set('debug.dump_listener', DumpListener::class)
|
||||
->args([
|
||||
service('var_dumper.cloner'),
|
||||
service('var_dumper.cli_dumper'),
|
||||
null,
|
||||
])
|
||||
->tag('kernel.event_subscriber')
|
||||
|
||||
->set('var_dumper.cloner', VarCloner::class)
|
||||
->public()
|
||||
|
||||
->set('var_dumper.cli_dumper', CliDumper::class)
|
||||
->args([
|
||||
null, // debug.dump_destination,
|
||||
param('kernel.charset'),
|
||||
0, // flags
|
||||
])
|
||||
|
||||
->set('var_dumper.contextualized_cli_dumper', ContextualizedDumper::class)
|
||||
->decorate('var_dumper.cli_dumper')
|
||||
->args([
|
||||
service('var_dumper.contextualized_cli_dumper.inner'),
|
||||
[
|
||||
'source' => inline_service(SourceContextProvider::class)->args([
|
||||
param('kernel.charset'),
|
||||
param('kernel.project_dir'),
|
||||
service('debug.file_link_formatter')->nullOnInvalid(),
|
||||
]),
|
||||
],
|
||||
])
|
||||
|
||||
->set('var_dumper.html_dumper', HtmlDumper::class)
|
||||
->args([
|
||||
null,
|
||||
param('kernel.charset'),
|
||||
0, // flags
|
||||
])
|
||||
->call('setDisplayOptions', [
|
||||
['fileLinkFormat' => service('debug.file_link_formatter')->ignoreOnInvalid()],
|
||||
])
|
||||
|
||||
->set('var_dumper.server_connection', Connection::class)
|
||||
->args([
|
||||
'', // server host
|
||||
[
|
||||
'source' => inline_service(SourceContextProvider::class)->args([
|
||||
param('kernel.charset'),
|
||||
param('kernel.project_dir'),
|
||||
service('debug.file_link_formatter')->nullOnInvalid(),
|
||||
]),
|
||||
'request' => inline_service(RequestContextProvider::class)->args([service('request_stack')]),
|
||||
'cli' => inline_service(CliContextProvider::class),
|
||||
],
|
||||
])
|
||||
|
||||
->set('var_dumper.dump_server', DumpServer::class)
|
||||
->args([
|
||||
'', // server host
|
||||
service('logger')->nullOnInvalid(),
|
||||
])
|
||||
->tag('monolog.logger', ['channel' => 'debug'])
|
||||
|
||||
->set('var_dumper.command.server_dump', ServerDumpCommand::class)
|
||||
->args([
|
||||
service('var_dumper.dump_server'),
|
||||
[
|
||||
'cli' => inline_service(CliDescriptor::class)->args([service('var_dumper.contextualized_cli_dumper.inner')]),
|
||||
'html' => inline_service(HtmlDescriptor::class)->args([service('var_dumper.html_dumper')]),
|
||||
],
|
||||
])
|
||||
->tag('console.command')
|
||||
|
||||
->set('monolog.command.server_log', ServerLogCommand::class)
|
||||
;
|
||||
|
||||
if (class_exists(ConsoleFormatter::class) && interface_exists(FormatterInterface::class)) {
|
||||
$container->services()->get('monolog.command.server_log')->tag('console.command');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.dumpsCount %}
|
||||
{% set icon %}
|
||||
{{ source('@Debug/Profiler/icon.svg') }}
|
||||
<span class="sf-toolbar-value">{{ collector.dumpsCount }}</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
{% for dump in collector.getDumps('html') %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<span>
|
||||
{% if dump.label is defined and '' != dump.label %}
|
||||
<span class="sf-toolbar-file-line"><strong>{{ dump.label }}</strong> in </span>
|
||||
{% endif %}
|
||||
{% if dump.file %}
|
||||
{% set link = dump.file|file_link(dump.line) %}
|
||||
{% if link %}
|
||||
<a href="{{ link }}" title="{{ dump.file }}">{{ dump.name }}</a>
|
||||
{% else %}
|
||||
<abbr title="{{ dump.file }}">{{ dump.name }}</abbr>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ dump.name }}
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="sf-toolbar-file-line">line {{ dump.line }}</span>
|
||||
|
||||
{{ dump.data|raw }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': true }) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label {{ collector.dumpsCount == 0 ? 'disabled' }}">
|
||||
<span class="icon">{{ source('@Debug/Profiler/icon.svg') }}</span>
|
||||
<strong>Debug</strong>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
<h2>Dumped Contents</h2>
|
||||
|
||||
{% for dump in collector.getDumps('html') %}
|
||||
<div class="sf-dump sf-reset">
|
||||
<span class="metadata">
|
||||
{% if dump.label is defined and '' != dump.label %}
|
||||
<strong>{{ dump.label }}</strong> in
|
||||
{% else %}
|
||||
In
|
||||
{% endif %}
|
||||
{% if dump.line %}
|
||||
{% set link = dump.file|file_link(dump.line) %}
|
||||
{% if link %}
|
||||
<a href="{{ link }}" title="{{ dump.file }}">{{ dump.name }}</a>
|
||||
{% else %}
|
||||
<abbr title="{{ dump.file }}">{{ dump.name }}</abbr>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ dump.name }}
|
||||
{% endif %}
|
||||
line <a class="text-small sf-toggle" data-toggle-selector="#sf-trace-{{ loop.index0 }}">{{ dump.line }}</a>:
|
||||
</span>
|
||||
|
||||
<div class="sf-dump-compact hidden" id="sf-trace-{{ loop.index0 }}">
|
||||
<div class="trace">
|
||||
{{ dump.fileExcerpt ? dump.fileExcerpt|raw : dump.file|file_excerpt(dump.line) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ dump.data|raw }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty empty-panel">
|
||||
<p>No content was dumped.</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" data-icon-name="icon-tabler-viewfinder" width="24" height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<circle cx="12" cy="12" r="9"></circle>
|
||||
<line x1="12" y1="3" x2="12" y2="7"></line>
|
||||
<line x1="12" y1="21" x2="12" y2="18"></line>
|
||||
<line x1="3" y1="12" x2="7" y2="12"></line>
|
||||
<line x1="21" y1="12" x2="18" y2="12"></line>
|
||||
<line x1="12" y1="12" x2="12" y2="12.01"></line>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 586 B |
41
lib/symfony/debug-bundle/composer.json
Normal file
41
lib/symfony/debug-bundle/composer.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "symfony/debug-bundle",
|
||||
"type": "symfony-bundle",
|
||||
"description": "Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework",
|
||||
"keywords": [],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"ext-xml": "*",
|
||||
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
|
||||
"symfony/http-kernel": "^5.4|^6.0|^7.0",
|
||||
"symfony/twig-bridge": "^5.4|^6.0|^7.0",
|
||||
"symfony/var-dumper": "^5.4|^6.0|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/config": "^5.4|^6.0|^7.0",
|
||||
"symfony/web-profiler-bundle": "^5.4|^6.0|^7.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/config": "<5.4",
|
||||
"symfony/dependency-injection": "<5.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Bundle\\DebugBundle\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
@@ -1295,16 +1295,9 @@ EOF;
|
||||
}
|
||||
}
|
||||
|
||||
if (Container::class !== $this->baseClass) {
|
||||
$r = $this->container->getReflectionClass($this->baseClass, false);
|
||||
if (null !== $r
|
||||
&& (null !== $constructor = $r->getConstructor())
|
||||
&& 0 === $constructor->getNumberOfRequiredParameters()
|
||||
&& Container::class !== $constructor->getDeclaringClass()->name
|
||||
) {
|
||||
$code .= " parent::__construct();\n";
|
||||
$code .= " \$this->parameterBag = null;\n\n";
|
||||
}
|
||||
if ($this->needsUnsetParameterBag()) {
|
||||
$code .= " parent::__construct();\n";
|
||||
$code .= " unset(\$this->parameterBag);\n\n";
|
||||
}
|
||||
|
||||
if ($this->container->getParameterBag()->all()) {
|
||||
@@ -1582,9 +1575,22 @@ EOF;
|
||||
return $code ? \sprintf("\n \$this->privates['service_container'] = static function (\$container) {%s\n };\n", $code) : '';
|
||||
}
|
||||
|
||||
private function needsUnsetParameterBag(): bool
|
||||
{
|
||||
if (Container::class === $this->baseClass) {
|
||||
return false;
|
||||
}
|
||||
$r = $this->container->getReflectionClass($this->baseClass, false);
|
||||
|
||||
return null !== $r
|
||||
&& (null !== $constructor = $r->getConstructor())
|
||||
&& 0 === $constructor->getNumberOfRequiredParameters()
|
||||
&& Container::class !== $constructor->getDeclaringClass()->name;
|
||||
}
|
||||
|
||||
private function addDefaultParametersMethod(): string
|
||||
{
|
||||
if (!$this->container->getParameterBag()->all()) {
|
||||
if (!$this->container->getParameterBag()->all() && !$this->needsUnsetParameterBag()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,18 @@ abstract class AbstractConfigurator
|
||||
$value = (self::$valuePreProcessor)($value, $allowServices);
|
||||
}
|
||||
|
||||
if ($value instanceof ParamConfigurator) {
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
if (\is_scalar($value ?? '') || $value instanceof \UnitEnum) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (!$allowServices) {
|
||||
throw new InvalidArgumentException(\sprintf('Cannot use values of type "%s" in service configuration files.', get_debug_type($value)));
|
||||
}
|
||||
|
||||
if ($value instanceof ReferenceConfigurator) {
|
||||
$reference = new Reference($value->id, $value->invalidBehavior);
|
||||
|
||||
@@ -90,29 +102,18 @@ abstract class AbstractConfigurator
|
||||
return $def;
|
||||
}
|
||||
|
||||
if ($value instanceof ParamConfigurator) {
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
if ($value instanceof self) {
|
||||
throw new InvalidArgumentException(\sprintf('"%s()" can be used only at the root of service configuration files.', $value::FACTORY));
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case null === $value:
|
||||
case \is_scalar($value):
|
||||
case $value instanceof \UnitEnum:
|
||||
return $value;
|
||||
|
||||
case $value instanceof ArgumentInterface:
|
||||
case $value instanceof Definition:
|
||||
case $value instanceof Expression:
|
||||
case $value instanceof Parameter:
|
||||
case $value instanceof AbstractArgument:
|
||||
case $value instanceof Reference:
|
||||
if ($allowServices) {
|
||||
return $value;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(\sprintf('Cannot use values of type "%s" in service configuration files.', get_debug_type($value)));
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Symfony\Component\Dotenv;
|
||||
use Symfony\Component\Dotenv\Exception\FormatException;
|
||||
use Symfony\Component\Dotenv\Exception\FormatExceptionContext;
|
||||
use Symfony\Component\Dotenv\Exception\PathException;
|
||||
use Symfony\Component\Dotenv\Exception\VariableCircularReferenceException;
|
||||
use Symfony\Component\Process\Exception\ExceptionInterface as ProcessException;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
@@ -81,6 +82,7 @@ final class Dotenv
|
||||
public function load(string $path, string ...$extraPaths): void
|
||||
{
|
||||
$this->doLoad(false, \func_get_args());
|
||||
$this->resolveLoadedVars();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,33 +102,42 @@ final class Dotenv
|
||||
*/
|
||||
public function loadEnv(string $path, ?string $envKey = null, string $defaultEnv = 'dev', array $testEnvs = ['test'], bool $overrideExistingVars = false): void
|
||||
{
|
||||
$k = $envKey ?? $this->envKey;
|
||||
try {
|
||||
$k = $envKey ?? $this->envKey;
|
||||
|
||||
if (is_file($path) || !is_file($p = "$path.dist")) {
|
||||
$this->doLoad($overrideExistingVars, [$path]);
|
||||
} else {
|
||||
$this->doLoad($overrideExistingVars, [$p]);
|
||||
}
|
||||
if (is_file($path) || !is_file($p = "$path.dist")) {
|
||||
$this->doLoad($overrideExistingVars, [$path]);
|
||||
} else {
|
||||
$this->doLoad($overrideExistingVars, [$p]);
|
||||
}
|
||||
|
||||
if (null === $env = $_SERVER[$k] ?? $_ENV[$k] ?? null) {
|
||||
$this->populate([$k => $env = $defaultEnv], $overrideExistingVars);
|
||||
}
|
||||
if (null === $env = $_SERVER[$k] ?? $_ENV[$k] ?? null) {
|
||||
$this->populate([$k => $env = $defaultEnv], $overrideExistingVars);
|
||||
} elseif (str_contains($env, '$') || str_contains($env, "\x00") || str_contains($env, '\\')) {
|
||||
$env = $this->resolveEnvKey($env, $k);
|
||||
}
|
||||
|
||||
if (!\in_array($env, $testEnvs, true) && is_file($p = "$path.local")) {
|
||||
$this->doLoad($overrideExistingVars, [$p]);
|
||||
$env = $_SERVER[$k] ?? $_ENV[$k] ?? $env;
|
||||
}
|
||||
if (!\in_array($env, $testEnvs, true) && is_file($p = "$path.local")) {
|
||||
$this->doLoad($overrideExistingVars, [$p]);
|
||||
$env = $_SERVER[$k] ?? $_ENV[$k] ?? $env;
|
||||
if (str_contains($env, '$') || str_contains($env, "\x00") || str_contains($env, '\\')) {
|
||||
$env = $this->resolveEnvKey($env, $k);
|
||||
}
|
||||
}
|
||||
|
||||
if ('local' === $env) {
|
||||
return;
|
||||
}
|
||||
if ('local' === $env) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_file($p = "$path.$env")) {
|
||||
$this->doLoad($overrideExistingVars, [$p]);
|
||||
}
|
||||
if (is_file($p = "$path.$env")) {
|
||||
$this->doLoad($overrideExistingVars, [$p]);
|
||||
}
|
||||
|
||||
if (is_file($p = "$path.$env.local")) {
|
||||
$this->doLoad($overrideExistingVars, [$p]);
|
||||
if (is_file($p = "$path.$env.local")) {
|
||||
$this->doLoad($overrideExistingVars, [$p]);
|
||||
}
|
||||
} finally {
|
||||
$this->resolveLoadedVars();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +179,7 @@ final class Dotenv
|
||||
public function overload(string $path, string ...$extraPaths): void
|
||||
{
|
||||
$this->doLoad(true, \func_get_args());
|
||||
$this->resolveLoadedVars();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,6 +248,48 @@ final class Dotenv
|
||||
$this->values = [];
|
||||
$name = '';
|
||||
|
||||
$loadedVars = array_flip(explode(',', $_SERVER['SYMFONY_DOTENV_VARS'] ?? $_ENV['SYMFONY_DOTENV_VARS'] ?? ''));
|
||||
unset($loadedVars['']);
|
||||
|
||||
$this->skipEmptyLines();
|
||||
|
||||
while ($this->cursor < $this->end) {
|
||||
switch ($state) {
|
||||
case self::STATE_VARNAME:
|
||||
$name = $this->lexVarname();
|
||||
$state = self::STATE_VALUE;
|
||||
break;
|
||||
|
||||
case self::STATE_VALUE:
|
||||
$this->values[$name] = $this->resolveValue($this->lexValue(), $loadedVars);
|
||||
$state = self::STATE_VARNAME;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (self::STATE_VALUE === $state) {
|
||||
$this->values[$name] = '';
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->values;
|
||||
} finally {
|
||||
$this->values = [];
|
||||
unset($this->path, $this->cursor, $this->lineno, $this->data, $this->end);
|
||||
}
|
||||
}
|
||||
|
||||
private function parseRaw(string $data, string $path = '.env'): array
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->data = str_replace(["\r\n", "\r"], "\n", $data);
|
||||
$this->lineno = 1;
|
||||
$this->cursor = 0;
|
||||
$this->end = \strlen($this->data);
|
||||
$state = self::STATE_VARNAME;
|
||||
$this->values = [];
|
||||
$name = '';
|
||||
|
||||
$this->skipEmptyLines();
|
||||
|
||||
while ($this->cursor < $this->end) {
|
||||
@@ -260,10 +314,22 @@ final class Dotenv
|
||||
return $this->values;
|
||||
} finally {
|
||||
$this->values = [];
|
||||
unset($this->path, $this->cursor, $this->lineno, $this->data, $this->end);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a raw value by expanding commands, variables, backslash escapes,
|
||||
* and restoring literal $ markers.
|
||||
*/
|
||||
private function resolveValue(string $value, array $loadedVars): string
|
||||
{
|
||||
$resolved = $this->resolveCommands($value, $loadedVars);
|
||||
$resolved = $this->resolveVariables($resolved, $loadedVars);
|
||||
$resolved = str_replace('\\\\', '\\', $resolved);
|
||||
|
||||
return str_replace("\x00", '$', $resolved);
|
||||
}
|
||||
|
||||
private function lexVarname(): string
|
||||
{
|
||||
// var name + optional export
|
||||
@@ -305,8 +371,6 @@ final class Dotenv
|
||||
throw $this->createFormatException('Whitespace are not supported before the value');
|
||||
}
|
||||
|
||||
$loadedVars = array_flip(explode(',', $_SERVER['SYMFONY_DOTENV_VARS'] ?? $_ENV['SYMFONY_DOTENV_VARS'] ?? ''));
|
||||
unset($loadedVars['']);
|
||||
$v = '';
|
||||
|
||||
do {
|
||||
@@ -321,7 +385,10 @@ final class Dotenv
|
||||
}
|
||||
} while ("'" !== $this->data[$this->cursor + $len]);
|
||||
|
||||
$v .= substr($this->data, 1 + $this->cursor, $len - 1);
|
||||
// In single-quoted strings, $ is literal and \ has no special meaning.
|
||||
// Double backslashes so they survive the unescape in resolveValue(),
|
||||
// and mark $ as \x00 so it's not treated as a variable reference.
|
||||
$v .= str_replace(['\\', '$'], ['\\\\', "\x00"], substr($this->data, 1 + $this->cursor, $len - 1));
|
||||
$this->cursor += 1 + $len;
|
||||
} elseif ('"' === $this->data[$this->cursor]) {
|
||||
$value = '';
|
||||
@@ -340,11 +407,8 @@ final class Dotenv
|
||||
}
|
||||
++$this->cursor;
|
||||
$value = str_replace(['\\"', '\r', '\n'], ['"', "\r", "\n"], $value);
|
||||
$resolvedValue = $value;
|
||||
$resolvedValue = $this->resolveCommands($resolvedValue, $loadedVars);
|
||||
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);
|
||||
$resolvedValue = str_replace('\\\\', '\\', $resolvedValue);
|
||||
$v .= $resolvedValue;
|
||||
// Mark escaped $ (\$) as \x00 so it's treated as literal
|
||||
$v .= $this->protectEscapedDollars($value);
|
||||
} else {
|
||||
$value = '';
|
||||
$prevChr = $this->data[$this->cursor - 1];
|
||||
@@ -363,12 +427,9 @@ final class Dotenv
|
||||
++$this->cursor;
|
||||
}
|
||||
$value = rtrim($value);
|
||||
$resolvedValue = $value;
|
||||
$resolvedValue = $this->resolveCommands($resolvedValue, $loadedVars);
|
||||
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);
|
||||
$resolvedValue = str_replace('\\\\', '\\', $resolvedValue);
|
||||
$resolvedValue = $this->protectEscapedDollars($value);
|
||||
|
||||
if ($resolvedValue === $value && preg_match('/\s+/', $value)) {
|
||||
if ($resolvedValue === $value && preg_match('/\s+/', $value) && !str_contains($value, '$')) {
|
||||
throw $this->createFormatException('A value containing spaces must be surrounded by quotes');
|
||||
}
|
||||
|
||||
@@ -385,6 +446,26 @@ final class Dotenv
|
||||
return $v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts \$ (escaped dollar) to \x00 (literal marker), handling
|
||||
* even/odd backslash counts correctly: \$ → \x00, \\$ → \\$ (unchanged).
|
||||
*/
|
||||
private function protectEscapedDollars(string $value): string
|
||||
{
|
||||
if (!str_contains($value, '$')) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return preg_replace_callback('/\\\\+\$/', static function ($m) {
|
||||
$bs = substr($m[0], 0, -1);
|
||||
if (1 === \strlen($bs) % 2) {
|
||||
return substr($bs, 0, -1)."\x00";
|
||||
}
|
||||
|
||||
return $m[0];
|
||||
}, $value);
|
||||
}
|
||||
|
||||
private function lexNestedExpression(): string
|
||||
{
|
||||
++$this->cursor;
|
||||
@@ -559,7 +640,148 @@ final class Dotenv
|
||||
throw new FormatException('Loading files starting with a byte-order-mark (BOM) is not supported.', new FormatExceptionContext($data, $path, 1, 0));
|
||||
}
|
||||
|
||||
$this->populate($this->parse($data, $path), $overrideExistingVars);
|
||||
if (str_contains($data, "\0")) {
|
||||
throw new FormatException('Loading files containing NUL bytes is not supported.', new FormatExceptionContext($data, $path, 1, 0));
|
||||
}
|
||||
|
||||
$this->populate($this->parseRaw($data, $path), $overrideExistingVars);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Eagerly resolves a raw env key value so that loadEnv() can determine
|
||||
* which additional .env files to load before full deferred resolution.
|
||||
*/
|
||||
private function resolveEnvKey(string $value, string $name): string
|
||||
{
|
||||
$loadedVars = array_flip(explode(',', $_SERVER['SYMFONY_DOTENV_VARS'] ?? $_ENV['SYMFONY_DOTENV_VARS'] ?? ''));
|
||||
unset($loadedVars['']);
|
||||
|
||||
// Save and clear own value so self-referencing defaults work
|
||||
$envBackup = $_ENV[$name] ?? null;
|
||||
$serverBackup = $_SERVER[$name] ?? null;
|
||||
unset($_ENV[$name], $_SERVER[$name]);
|
||||
if ($this->usePutenv) {
|
||||
$getenvBackup = (string) getenv($name);
|
||||
putenv($name);
|
||||
}
|
||||
|
||||
$this->values = [];
|
||||
$this->path = '';
|
||||
$this->data = '';
|
||||
$this->lineno = 0;
|
||||
$this->cursor = 0;
|
||||
$this->end = 0;
|
||||
|
||||
$resolved = $this->resolveCommands($value, $loadedVars);
|
||||
$resolved = $this->resolveVariables($resolved, $loadedVars);
|
||||
$resolved = str_replace(["\x00", '\\\\'], ['$', '\\'], $resolved);
|
||||
|
||||
if (null !== $envBackup) {
|
||||
$_ENV[$name] = $envBackup;
|
||||
}
|
||||
if (null !== $serverBackup) {
|
||||
$_SERVER[$name] = $serverBackup;
|
||||
}
|
||||
if ($this->usePutenv) {
|
||||
putenv("$name=$getenvBackup");
|
||||
}
|
||||
|
||||
$this->values = [];
|
||||
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
private function resolveLoadedVars(): void
|
||||
{
|
||||
$loadedVars = array_flip(explode(',', $_SERVER['SYMFONY_DOTENV_VARS'] ?? $_ENV['SYMFONY_DOTENV_VARS'] ?? ''));
|
||||
unset($loadedVars['']);
|
||||
|
||||
$this->values = [];
|
||||
$this->path = '';
|
||||
$this->data = '';
|
||||
$this->lineno = 0;
|
||||
$this->cursor = 0;
|
||||
$this->end = 0;
|
||||
|
||||
// Detect variables that were originally defined as self-referencing
|
||||
// (e.g. MY_VAR="${MY_VAR:-default}") so their own raw value is hidden
|
||||
// during resolution, allowing the default to trigger correctly.
|
||||
$selfReferencingVars = [];
|
||||
foreach ($loadedVars as $name => $_) {
|
||||
if ('SYMFONY_DOTENV_VARS' === $name) {
|
||||
continue;
|
||||
}
|
||||
$value = $_ENV[$name] ?? '';
|
||||
if (str_contains($value, '$') && preg_match('/\$\{?'.preg_quote($name, '/').'(?![A-Za-z0-9_])/', $value)) {
|
||||
$selfReferencingVars[$name] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for ($pass = 0; $pass < 5; ++$pass) {
|
||||
$resolved = [];
|
||||
foreach ($loadedVars as $name => $_) {
|
||||
if ('SYMFONY_DOTENV_VARS' === $name) {
|
||||
continue;
|
||||
}
|
||||
if (!str_contains($value = $_ENV[$name] ?? '', '$')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($selfReferencingVars[$name])) {
|
||||
$envBackup = $_ENV[$name] ?? null;
|
||||
$serverBackup = $_SERVER[$name] ?? null;
|
||||
unset($_ENV[$name], $_SERVER[$name]);
|
||||
if ($this->usePutenv) {
|
||||
$getenvBackup = $this->usePutenv ? (string) getenv($name) : null;
|
||||
putenv($name);
|
||||
}
|
||||
}
|
||||
|
||||
$resolvedValue = $this->resolveCommands($value, $loadedVars);
|
||||
$resolvedValue = $this->resolveVariables($resolvedValue, $loadedVars);
|
||||
|
||||
if (isset($selfReferencingVars[$name])) {
|
||||
if (null !== $envBackup) {
|
||||
$_ENV[$name] = $envBackup;
|
||||
}
|
||||
if (null !== $serverBackup) {
|
||||
$_SERVER[$name] = $serverBackup;
|
||||
}
|
||||
if ($this->usePutenv) {
|
||||
putenv("$name=$getenvBackup");
|
||||
}
|
||||
}
|
||||
|
||||
if ($value !== $resolvedValue) {
|
||||
$resolved[$name] = $resolvedValue;
|
||||
}
|
||||
}
|
||||
if (!$resolved) {
|
||||
break;
|
||||
}
|
||||
$this->populate($resolved, true);
|
||||
}
|
||||
if (5 === $pass && $resolved) {
|
||||
throw new VariableCircularReferenceException('Too many levels of variable indirection in env vars: '.implode(', ', array_keys($resolved)).'.');
|
||||
}
|
||||
|
||||
// Restore literal $ signs and unescape backslashes
|
||||
$restored = [];
|
||||
foreach ($loadedVars as $name => $_) {
|
||||
if ('SYMFONY_DOTENV_VARS' === $name) {
|
||||
continue;
|
||||
}
|
||||
$value = $_ENV[$name] ?? '';
|
||||
if ($value !== $newValue = str_replace(["\x00", '\\\\'], ['$', '\\'], $value)) {
|
||||
$restored[$name] = $newValue;
|
||||
}
|
||||
}
|
||||
if ($restored) {
|
||||
$this->populate($restored, true);
|
||||
}
|
||||
|
||||
$this->values = [];
|
||||
unset($this->path, $this->data, $this->lineno, $this->cursor, $this->end);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Dotenv\Exception;
|
||||
|
||||
/**
|
||||
* Thrown when there are too many levels of variable indirection in env vars.
|
||||
*
|
||||
* @author Pascal CESCON <pascal.cescon@gmail.com>
|
||||
*/
|
||||
final class VariableCircularReferenceException extends \LogicException implements ExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -127,6 +127,21 @@ class DebugClassLoader
|
||||
private static array $internalMethods = [];
|
||||
private static array $annotatedParameters = [];
|
||||
private static array $darwinCache = ['/' => ['/', []]];
|
||||
/**
|
||||
* @var array<string, list<array{0: string, 1: bool, 2: string, 3: string, 4: string|null}>>
|
||||
*
|
||||
* Maps an interface FQCN (or an abstract class accumulating entries from its interfaces) to the list of
|
||||
* "@method" annotations declared on it. For interfaces, the entry is populated directly by parsing the
|
||||
* annotations from the interface's docblock. For abstract classes, the information from all implemented
|
||||
* interfaces is merged together, so that the check can later be applied to the first concrete subclass.
|
||||
*
|
||||
* Each entry is a tuple of:
|
||||
* [0] string $interface - FQCN of the interface that carries the "@method" annotation
|
||||
* [1] bool $static - whether the method is declared static
|
||||
* [2] string $returnType - return type from the annotation, or '' if absent
|
||||
* [3] string $name - method name plus its parameter signature, e.g. "foo($arg, int $n)"
|
||||
* [4] string|null $description - description text (period-normalised), or null if absent
|
||||
*/
|
||||
private static array $method = [];
|
||||
private static array $returnTypes = [];
|
||||
private static array $methodTraits = [];
|
||||
@@ -398,6 +413,14 @@ class DebugClassLoader
|
||||
|
||||
if ($refl->isInterface() && isset($doc['method'])) {
|
||||
foreach ($doc['method'] as $name => [$static, $returnType, $signature, $description]) {
|
||||
if ($refl->hasMethod($static ? '__callStatic' : '__call')) {
|
||||
// When the interface has "virtual" @method declarations but at the same time contains a __call/__callStatic magic method,
|
||||
// do not trigger a deprecation notice. This is to address special use cases like in Predis' ClientInterface where the
|
||||
// "@method" annotations never intend to actually add the method to the interface, but are used to document the "virtual"
|
||||
// API provided by the interface through the technical implementation of magic calls. This might cause false negatives
|
||||
// (missing notices) in the case that such interfaces are later amended with actual (real) methods.
|
||||
continue;
|
||||
}
|
||||
self::$method[$class][] = [$class, $static, $returnType, $name.$signature, $description];
|
||||
|
||||
if ('' !== $returnType) {
|
||||
@@ -420,6 +443,15 @@ class DebugClassLoader
|
||||
}
|
||||
}
|
||||
|
||||
// When the parent is a concrete class, we will trigger deprecation notices to make it aware that it needs
|
||||
// to add the new methods announced with @method. The parent will have to provide all those methods.
|
||||
// For child classes this means they will not need to deal with @method coming from any of the interfaces
|
||||
// the parent implements.
|
||||
// Put those interfaces that we can ignore into $parentInterfaces.
|
||||
// The ternary makes use of the fact that abstract parent classes will accumulate the methods in self::$method,
|
||||
// so !isset(self::$method[$parent]) indicates a concrete parent class.
|
||||
$parentInterfaces = ($parent && !isset(self::$method[$parent])) ? class_implements($parent, false) : [];
|
||||
|
||||
// Detect if the parent is annotated
|
||||
foreach ($parentAndOwnInterfaces + class_uses($class, false) as $use) {
|
||||
if (!isset(self::$checkedClasses[$use])) {
|
||||
@@ -435,13 +467,15 @@ class DebugClassLoader
|
||||
$deprecations[] = \sprintf('The "%s" %s is considered internal%s It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $className);
|
||||
}
|
||||
if (isset(self::$method[$use])) {
|
||||
if ($refl->isAbstract()) {
|
||||
if ($refl->isAbstract() || $refl->isInterface()) {
|
||||
// Abstract classes and interfaces inherit @method from interfaces they
|
||||
// implement directly or through inheritance.
|
||||
if (isset(self::$method[$class])) {
|
||||
self::$method[$class] = array_merge(self::$method[$class], self::$method[$use]);
|
||||
} else {
|
||||
self::$method[$class] = self::$method[$use];
|
||||
}
|
||||
} elseif (!$refl->isInterface()) {
|
||||
} else {
|
||||
if (!strncmp($vendor, str_replace('_', '\\', $use), $vendorLen)
|
||||
&& str_starts_with($className, 'Symfony\\')
|
||||
&& (!class_exists(InstalledVersions::class)
|
||||
@@ -450,14 +484,14 @@ class DebugClassLoader
|
||||
// skip "same vendor" @method deprecations for Symfony\* classes unless symfony/symfony is being tested
|
||||
continue;
|
||||
}
|
||||
$hasCall = $refl->hasMethod('__call');
|
||||
$hasStaticCall = $refl->hasMethod('__callStatic');
|
||||
foreach (self::$method[$use] as [$interface, $static, $returnType, $name, $description]) {
|
||||
if ($static ? $hasStaticCall : $hasCall) {
|
||||
if (isset($parentInterfaces[$interface])) {
|
||||
// The @method annotation comes from an interface that has already been implemented by a concrete parent class,
|
||||
// so we can ignore it here.
|
||||
continue;
|
||||
}
|
||||
$realName = substr($name, 0, strpos($name, '('));
|
||||
if (!$refl->hasMethod($realName) || !($methodRefl = $refl->getMethod($realName))->isPublic() || ($static && !$methodRefl->isStatic()) || (!$static && $methodRefl->isStatic())) {
|
||||
if (!$refl->hasMethod($realName) || !($methodRefl = $refl->getMethod($realName))->isPublic() || ($static xor $methodRefl->isStatic())) {
|
||||
$deprecations[] = \sprintf('Class "%s" should implement method "%s::%s%s"%s', $className, ($static ? 'static ' : '').$interface, $name, $returnType ? ': '.$returnType : '', null === $description ? '.' : ': '.$description);
|
||||
}
|
||||
}
|
||||
|
||||
0
lib/symfony/error-handler/Resources/bin/extract-tentative-return-types.php
Normal file → Executable file
0
lib/symfony/error-handler/Resources/bin/extract-tentative-return-types.php
Normal file → Executable file
@@ -40,6 +40,9 @@ class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterfa
|
||||
private EventDispatcherInterface $dispatcher;
|
||||
private array $wrappedListeners = [];
|
||||
private array $orphanedEvents = [];
|
||||
private array $dispatchDepth = [];
|
||||
private array $calledListenerInfos = [];
|
||||
private array $calledOriginalListeners = [];
|
||||
private ?RequestStack $requestStack;
|
||||
private string $currentRequestHash = '';
|
||||
|
||||
@@ -155,20 +158,20 @@ class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterfa
|
||||
|
||||
public function getCalledListeners(?Request $request = null): array
|
||||
{
|
||||
if (null === $this->callStack) {
|
||||
if (!$this->calledListenerInfos) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$hash = $request ? spl_object_hash($request) : null;
|
||||
$called = [];
|
||||
foreach ($this->callStack as $listener) {
|
||||
[$eventName, $requestHash] = $this->callStack->getInfo();
|
||||
|
||||
foreach ($this->calledListenerInfos as $requestHash => $infos) {
|
||||
if (null === $hash || $hash === $requestHash) {
|
||||
$called[] = $listener->getInfo($eventName);
|
||||
$called[] = $infos;
|
||||
}
|
||||
}
|
||||
|
||||
return $called;
|
||||
return $called ? array_merge(...$called) : [];
|
||||
}
|
||||
|
||||
public function getNotCalledListeners(?Request $request = null): array
|
||||
@@ -185,16 +188,14 @@ class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterfa
|
||||
$hash = $request ? spl_object_hash($request) : null;
|
||||
$calledListeners = [];
|
||||
|
||||
if (null !== $this->callStack) {
|
||||
foreach ($this->callStack as $calledListener) {
|
||||
[, $requestHash] = $this->callStack->getInfo();
|
||||
|
||||
if (null === $hash || $hash === $requestHash) {
|
||||
$calledListeners[] = $calledListener->getWrappedListener();
|
||||
}
|
||||
foreach ($this->calledOriginalListeners as $requestHash => $eventListeners) {
|
||||
if (null === $hash || $hash === $requestHash) {
|
||||
$calledListeners[] = array_merge(...array_values($eventListeners));
|
||||
}
|
||||
}
|
||||
|
||||
$calledListeners = $calledListeners ? array_merge(...$calledListeners) : [];
|
||||
|
||||
$notCalled = [];
|
||||
|
||||
foreach ($allListeners as $eventName => $listeners) {
|
||||
@@ -234,6 +235,9 @@ class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterfa
|
||||
$this->callStack = null;
|
||||
$this->orphanedEvents = [];
|
||||
$this->currentRequestHash = '';
|
||||
$this->dispatchDepth = [];
|
||||
$this->calledListenerInfos = [];
|
||||
$this->calledOriginalListeners = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,6 +271,8 @@ class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterfa
|
||||
|
||||
private function preProcess(string $eventName): void
|
||||
{
|
||||
$this->dispatchDepth[$eventName] = ($this->dispatchDepth[$eventName] ?? 0) + 1;
|
||||
|
||||
if (!$this->dispatcher->hasListeners($eventName)) {
|
||||
$this->orphanedEvents[$this->currentRequestHash][] = $eventName;
|
||||
|
||||
@@ -285,6 +291,8 @@ class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterfa
|
||||
|
||||
private function postProcess(string $eventName): void
|
||||
{
|
||||
--$this->dispatchDepth[$eventName];
|
||||
|
||||
unset($this->wrappedListeners[$eventName]);
|
||||
$skipped = false;
|
||||
foreach ($this->dispatcher->getListeners($eventName) as $listener) {
|
||||
@@ -302,10 +310,16 @@ class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterfa
|
||||
|
||||
if ($listener->wasCalled()) {
|
||||
$this->logger?->debug('Notified event "{event}" to listener "{listener}".', $context);
|
||||
} else {
|
||||
unset($this->callStack[$listener]);
|
||||
|
||||
$original = $listener->getWrappedListener();
|
||||
if (!\in_array($original, $this->calledOriginalListeners[$this->currentRequestHash][$eventName] ?? [], true)) {
|
||||
$this->calledOriginalListeners[$this->currentRequestHash][$eventName][] = $original;
|
||||
$this->calledListenerInfos[$this->currentRequestHash][] = $listener->getInfo($eventName);
|
||||
}
|
||||
}
|
||||
|
||||
unset($this->callStack[$listener]);
|
||||
|
||||
if (null !== $this->logger && $skipped) {
|
||||
$this->logger->debug('Listener "{listener}" was not called for event "{event}".', $context);
|
||||
}
|
||||
@@ -316,6 +330,28 @@ class TraceableEventDispatcher implements EventDispatcherInterface, ResetInterfa
|
||||
$skipped = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 < $this->dispatchDepth[$eventName]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up stale callStack entries left by nested same-event dispatches
|
||||
$stale = [];
|
||||
foreach ($this->callStack as $listener) {
|
||||
if ($this->callStack->getInfo()[0] === $eventName) {
|
||||
$stale[] = $listener;
|
||||
}
|
||||
}
|
||||
foreach ($stale as $listener) {
|
||||
if ($listener->wasCalled()) {
|
||||
$original = $listener->getWrappedListener();
|
||||
if (!\in_array($original, $this->calledOriginalListeners[$this->currentRequestHash][$eventName] ?? [], true)) {
|
||||
$this->calledOriginalListeners[$this->currentRequestHash][$eventName][] = $original;
|
||||
$this->calledListenerInfos[$this->currentRequestHash][] = $listener->getInfo($eventName);
|
||||
}
|
||||
}
|
||||
unset($this->callStack[$listener]);
|
||||
}
|
||||
}
|
||||
|
||||
private function sortNotCalledListeners(array $a, array $b): int
|
||||
|
||||
@@ -39,17 +39,23 @@ class ValidatorExtension extends AbstractExtension
|
||||
/** @var ClassMetadata $metadata */
|
||||
$metadata = $validator->getMetadataFor(\Symfony\Component\Form\Form::class);
|
||||
|
||||
$this->validator = $validator;
|
||||
$this->formRenderer = $formRenderer;
|
||||
$this->translator = $translator;
|
||||
|
||||
// Register the form constraints in the validator programmatically.
|
||||
// This functionality is required when using the Form component without
|
||||
// the DIC, where the XML file is loaded automatically. Thus the following
|
||||
// code must be kept synchronized with validation.xml
|
||||
|
||||
foreach ($metadata->getConstraints() as $constraint) {
|
||||
if ($constraint instanceof Form) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$metadata->addConstraint(new Form());
|
||||
$metadata->addConstraint(new Traverse(false));
|
||||
|
||||
$this->validator = $validator;
|
||||
$this->formRenderer = $formRenderer;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function loadTypeGuesser(): ?FormTypeGuesserInterface
|
||||
|
||||
@@ -1233,8 +1233,7 @@ class FrameworkExtension extends Extension
|
||||
$container->setParameter('request_listener.https_port', $config['https_port']);
|
||||
|
||||
if (null !== $config['default_uri']) {
|
||||
$container->getDefinition('router.request_context')
|
||||
->replaceArgument(0, $config['default_uri']);
|
||||
$container->setParameter('router.request_context.base_url', $config['default_uri']);
|
||||
}
|
||||
|
||||
if ($this->isInitializedConfigEnabled('annotations') && (new \ReflectionClass(AttributeClassLoader::class))->hasProperty('reader')) {
|
||||
@@ -1265,6 +1264,7 @@ class FrameworkExtension extends Extension
|
||||
}
|
||||
|
||||
$container->setParameter('session.storage.options', $options);
|
||||
$container->setParameter('session.metadata.cookie_lifetime', $options['cookie_lifetime'] ?? null);
|
||||
|
||||
// session handler (the internal callback registered with PHP session management)
|
||||
if (null === $config['handler_id']) {
|
||||
|
||||
@@ -43,6 +43,7 @@ return static function (ContainerConfigurator $container) {
|
||||
->args([
|
||||
param('session.metadata.storage_key'),
|
||||
param('session.metadata.update_threshold'),
|
||||
param('session.metadata.cookie_lifetime'),
|
||||
]),
|
||||
false,
|
||||
])
|
||||
@@ -53,6 +54,7 @@ return static function (ContainerConfigurator $container) {
|
||||
->args([
|
||||
param('session.metadata.storage_key'),
|
||||
param('session.metadata.update_threshold'),
|
||||
param('session.metadata.cookie_lifetime'),
|
||||
]),
|
||||
false,
|
||||
])
|
||||
@@ -64,6 +66,7 @@ return static function (ContainerConfigurator $container) {
|
||||
->args([
|
||||
param('session.metadata.storage_key'),
|
||||
param('session.metadata.update_threshold'),
|
||||
param('session.metadata.cookie_lifetime'),
|
||||
]),
|
||||
])
|
||||
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
namespace Symfony\Bundle\FrameworkBundle\Test;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Config\Resource\SelfCheckingResourceChecker;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Contracts\Service\ResetInterface;
|
||||
|
||||
@@ -38,6 +40,8 @@ abstract class KernelTestCase extends TestCase
|
||||
|
||||
protected static $booted = false;
|
||||
|
||||
private static bool $kernelHasBeenRebooted = false;
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
static::ensureKernelShutdown();
|
||||
@@ -88,6 +92,7 @@ abstract class KernelTestCase extends TestCase
|
||||
// reboot a fresh one.
|
||||
if ($kernel->getContainer()->initialized('cache_warmer')) {
|
||||
static::ensureKernelShutdown();
|
||||
self::$kernelHasBeenRebooted = true;
|
||||
|
||||
$kernel = static::createKernel($options);
|
||||
$kernel->boot();
|
||||
@@ -161,6 +166,20 @@ abstract class KernelTestCase extends TestCase
|
||||
static::$kernel->shutdown();
|
||||
static::$booted = false;
|
||||
|
||||
if (self::$kernelHasBeenRebooted) {
|
||||
self::$kernelHasBeenRebooted = false;
|
||||
try {
|
||||
(new \ReflectionProperty(Kernel::class, 'freshCache'))->setValue(null, []);
|
||||
} catch (\ReflectionException) {
|
||||
// ignore if the property doesn't exist
|
||||
}
|
||||
try {
|
||||
(new \ReflectionProperty(SelfCheckingResourceChecker::class, 'cache'))->setValue(null, []);
|
||||
} catch (\ReflectionException) {
|
||||
// ignore if the property doesn't exist
|
||||
}
|
||||
}
|
||||
|
||||
if ($container instanceof ResetInterface) {
|
||||
$container->reset();
|
||||
}
|
||||
|
||||
@@ -41,14 +41,18 @@ class MetadataBag implements SessionBagInterface
|
||||
|
||||
private int $updateThreshold;
|
||||
|
||||
private ?int $cookieLifetime;
|
||||
|
||||
/**
|
||||
* @param string $storageKey The key used to store bag in the session
|
||||
* @param int $updateThreshold The time to wait between two UPDATED updates
|
||||
* @param string $storageKey The key used to store bag in the session
|
||||
* @param int $updateThreshold The time to wait between two UPDATED updates
|
||||
* @param int|null $cookieLifetime The configured cookie lifetime; null to read from php.ini
|
||||
*/
|
||||
public function __construct(string $storageKey = '_sf2_meta', int $updateThreshold = 0)
|
||||
public function __construct(string $storageKey = '_sf2_meta', int $updateThreshold = 0, ?int $cookieLifetime = null)
|
||||
{
|
||||
$this->storageKey = $storageKey;
|
||||
$this->updateThreshold = $updateThreshold;
|
||||
$this->cookieLifetime = $cookieLifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,6 +147,6 @@ class MetadataBag implements SessionBagInterface
|
||||
{
|
||||
$timeStamp = time();
|
||||
$this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp;
|
||||
$this->meta[self::LIFETIME] = $lifetime ?? (int) \ini_get('session.cookie_lifetime');
|
||||
$this->meta[self::LIFETIME] = $lifetime ?? $this->cookieLifetime ?? (int) \ini_get('session.cookie_lifetime');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ class RequestPayloadValueResolver implements ValueResolverInterface, EventSubscr
|
||||
try {
|
||||
$payload = $this->$payloadMapper($request, $type, $argument);
|
||||
} catch (PartialDenormalizationException $e) {
|
||||
$trans = $this->translator ? $this->translator->trans(...) : fn ($m, $p) => strtr($m, $p);
|
||||
$trans = $this->translator ? $this->translator->trans(...) : static fn ($m, $p) => strtr($m, $p);
|
||||
foreach ($e->getErrors() as $error) {
|
||||
$parameters = [];
|
||||
$template = 'This value was of an unexpected type.';
|
||||
@@ -187,7 +187,7 @@ class RequestPayloadValueResolver implements ValueResolverInterface, EventSubscr
|
||||
}
|
||||
|
||||
if (\is_array($data)) {
|
||||
return $this->serializer->denormalize($data, $type, 'csv', $attribute->serializationContext + self::CONTEXT_DENORMALIZE);
|
||||
return $this->serializer->denormalize($data, $type, self::hasNonStringScalar($data) ? $format : 'csv', $attribute->serializationContext + self::CONTEXT_DENORMALIZE);
|
||||
}
|
||||
|
||||
if ('form' === $format) {
|
||||
@@ -202,4 +202,21 @@ class RequestPayloadValueResolver implements ValueResolverInterface, EventSubscr
|
||||
throw new HttpException(Response::HTTP_BAD_REQUEST, \sprintf('Request payload contains invalid "%s" data.', $format), $e);
|
||||
}
|
||||
}
|
||||
|
||||
private static function hasNonStringScalar(array $data): bool
|
||||
{
|
||||
$stack = [$data];
|
||||
|
||||
while ($stack) {
|
||||
foreach (array_pop($stack) as $v) {
|
||||
if (\is_array($v)) {
|
||||
$stack[] = $v;
|
||||
} elseif (!\is_string($v)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +171,7 @@ class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface
|
||||
}
|
||||
|
||||
if ($autowireAttributes) {
|
||||
$invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
|
||||
$attribute = $autowireAttributes[0]->newInstance();
|
||||
$value = $parameterBag->resolveValue($attribute->value);
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ class LocaleListener implements EventSubscriberInterface
|
||||
public function setDefaultLocale(KernelEvent $event): void
|
||||
{
|
||||
$event->getRequest()->setDefaultLocale($this->defaultLocale);
|
||||
$this->setRouterLocale($this->defaultLocale);
|
||||
}
|
||||
|
||||
public function onKernelRequest(RequestEvent $event): void
|
||||
@@ -54,14 +55,12 @@ class LocaleListener implements EventSubscriberInterface
|
||||
$request = $event->getRequest();
|
||||
|
||||
$this->setLocale($request);
|
||||
$this->setRouterContext($request);
|
||||
$this->setRouterLocale($request->getLocale());
|
||||
}
|
||||
|
||||
public function onKernelFinishRequest(FinishRequestEvent $event): void
|
||||
{
|
||||
if (null !== $parentRequest = $this->requestStack->getParentRequest()) {
|
||||
$this->setRouterContext($parentRequest);
|
||||
}
|
||||
$this->setRouterLocale($this->requestStack->getParentRequest()?->getLocale() ?? $this->defaultLocale);
|
||||
}
|
||||
|
||||
private function setLocale(Request $request): void
|
||||
@@ -76,9 +75,9 @@ class LocaleListener implements EventSubscriberInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function setRouterContext(Request $request): void
|
||||
private function setRouterLocale(string $locale): void
|
||||
{
|
||||
$this->router?->getContext()->setParameter('_locale', $request->getLocale());
|
||||
$this->router?->getContext()->setParameter('_locale', $locale);
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
|
||||
@@ -77,11 +77,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
|
||||
*/
|
||||
private static array $freshCache = [];
|
||||
|
||||
public const VERSION = '6.4.34';
|
||||
public const VERSION_ID = 60434;
|
||||
public const VERSION = '6.4.36';
|
||||
public const VERSION_ID = 60436;
|
||||
public const MAJOR_VERSION = 6;
|
||||
public const MINOR_VERSION = 4;
|
||||
public const RELEASE_VERSION = 34;
|
||||
public const RELEASE_VERSION = 36;
|
||||
public const EXTRA_VERSION = '';
|
||||
|
||||
public const END_OF_MAINTENANCE = '11/2026';
|
||||
|
||||
@@ -44,15 +44,11 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
return $supported;
|
||||
}
|
||||
|
||||
if ('\\' === \DIRECTORY_SEPARATOR || !\function_exists('passthru') || !\function_exists('escapeshellarg')) {
|
||||
if ('\\' === \DIRECTORY_SEPARATOR || !\function_exists('shell_exec') || !\function_exists('escapeshellarg')) {
|
||||
return $supported = false;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
passthru('command -v file', $exitStatus);
|
||||
$binPath = trim(ob_get_clean());
|
||||
|
||||
return $supported = 0 === $exitStatus && '' !== $binPath;
|
||||
return $supported = '' !== trim(shell_exec('command -v file') ?: '');
|
||||
}
|
||||
|
||||
public function guessMimeType(string $path): ?string
|
||||
@@ -65,17 +61,8 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
|
||||
throw new LogicException(\sprintf('The "%s" guesser is not supported.', __CLASS__));
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
// need to use --mime instead of -i. see #6641
|
||||
passthru(\sprintf($this->cmd, escapeshellarg((str_starts_with($path, '-') ? './' : '').$path)), $return);
|
||||
if ($return > 0) {
|
||||
ob_end_clean();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = trim(ob_get_clean());
|
||||
$type = trim(shell_exec(\sprintf($this->cmd, escapeshellarg((str_starts_with($path, '-') ? './' : '').$path))) ?: '');
|
||||
|
||||
if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\+\.]+)#i', $type, $match)) {
|
||||
// it's not a type, but an error message
|
||||
|
||||
@@ -141,7 +141,7 @@ class DataPart extends TextPart
|
||||
}
|
||||
$this->_headers = $this->getHeaders();
|
||||
|
||||
return ['_headers', '_parent', 'filename', 'mediaType'];
|
||||
return ['_headers', '_parent', 'filename', 'mediaType', 'cid'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -211,6 +211,10 @@ final class Ctype
|
||||
*/
|
||||
private static function convert_int_to_char_for_ctype($int, $function)
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 80100 && !\is_string($int)) {
|
||||
@trigger_error($function.'(): Argument of type '.get_debug_type($int).' will be interpreted as string in the future', \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if (!\is_int($int)) {
|
||||
return $int;
|
||||
}
|
||||
@@ -219,10 +223,6 @@ final class Ctype
|
||||
return (string) $int;
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID >= 80100) {
|
||||
@trigger_error($function.'(): Argument of type int will be interpreted as string in the future', \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if ($int < 0) {
|
||||
$int += 256;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Symfony\Polyfill\Intl\Grapheme;
|
||||
* - grapheme_strstr - Returns part of haystack string from the first occurrence of needle to the end of haystack
|
||||
* - grapheme_substr - Return part of a string
|
||||
* - grapheme_str_split - Splits a string into an array of individual or chunks of graphemes
|
||||
* - grapheme_levenshtein - Calculate the grapheme-unit Levenshtein distance between two strings
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
@@ -51,7 +52,7 @@ final class Grapheme
|
||||
|
||||
if (!\is_scalar($s)) {
|
||||
$hasError = false;
|
||||
set_error_handler(function () use (&$hasError) { $hasError = true; });
|
||||
set_error_handler(static function () use (&$hasError) { $hasError = true; });
|
||||
$next = substr($s, $start);
|
||||
restore_error_handler();
|
||||
if ($hasError) {
|
||||
@@ -223,6 +224,54 @@ final class Grapheme
|
||||
return $chunks;
|
||||
}
|
||||
|
||||
public static function grapheme_levenshtein($s1, $s2, $insertion_cost = 1, $replacement_cost = 1, $deletion_cost = 1)
|
||||
{
|
||||
if (!preg_match('//u', $s1) || !preg_match('//u', $s2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 > $insertion_cost || 0 > $replacement_cost || 0 > $deletion_cost) {
|
||||
if (80000 > \PHP_VERSION_ID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new \ValueError('grapheme_levenshtein(): Argument #3 ($insertion_cost), #4 ($replacement_cost), and #5 ($deletion_cost) must be greater than or equal to 0');
|
||||
}
|
||||
|
||||
preg_match_all('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', $s1, $s1);
|
||||
preg_match_all('/'.SYMFONY_GRAPHEME_CLUSTER_RX.'/u', $s2, $s2);
|
||||
|
||||
$s1 = $s1[0];
|
||||
$s2 = $s2[0];
|
||||
$l1 = \count($s1);
|
||||
$l2 = \count($s2);
|
||||
|
||||
if (0 === $l1) {
|
||||
return $l2 * $insertion_cost;
|
||||
}
|
||||
if (0 === $l2) {
|
||||
return $l1 * $deletion_cost;
|
||||
}
|
||||
|
||||
$dp = array_fill(0, $l1 + 1, array_fill(0, $l2 + 1, 0));
|
||||
|
||||
for ($i = 1; $i <= $l1; ++$i) {
|
||||
$dp[$i][0] = $dp[$i - 1][0] + $deletion_cost;
|
||||
}
|
||||
for ($j = 1; $j <= $l2; ++$j) {
|
||||
$dp[0][$j] = $dp[0][$j - 1] + $insertion_cost;
|
||||
}
|
||||
|
||||
for ($i = 1; $i <= $l1; ++$i) {
|
||||
for ($j = 1; $j <= $l2; ++$j) {
|
||||
$cost = ($s1[$i - 1] === $s2[$j - 1]) ? 0 : $replacement_cost;
|
||||
$dp[$i][$j] = min($dp[$i - 1][$j] + $deletion_cost, $dp[$i][$j - 1] + $insertion_cost, $dp[$i - 1][$j - 1] + $cost);
|
||||
}
|
||||
}
|
||||
|
||||
return $dp[$l1][$l2];
|
||||
}
|
||||
|
||||
private static function grapheme_position($s, $needle, $offset, $mode)
|
||||
{
|
||||
$needle = (string) $needle;
|
||||
|
||||
@@ -53,5 +53,8 @@ if (!function_exists('grapheme_substr')) {
|
||||
function grapheme_substr($string, $offset, $length = null) { return p\Grapheme::grapheme_substr($string, $offset, $length); }
|
||||
}
|
||||
if (!function_exists('grapheme_str_split')) {
|
||||
function grapheme_str_split($string, $length = 1) { return p\Grapheme::grapheme_str_split($string, $length); }
|
||||
function grapheme_str_split(string $string, int $length = 1) { return p\Grapheme::grapheme_str_split($string, $length); }
|
||||
}
|
||||
if (!function_exists('grapheme_levenshtein')) {
|
||||
function grapheme_levenshtein(string $string1, string $string2, int $insertion_cost = 1, int $replacement_cost = 1, int $deletion_cost = 1, string $locale = '') { return p\Php85::grapheme_levenshtein($string1, $string2, $insertion_cost, $replacement_cost, $deletion_cost); }
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ use Symfony\Polyfill\Intl\Grapheme as p;
|
||||
if (!function_exists('grapheme_str_split')) {
|
||||
function grapheme_str_split(string $string, int $length = 1): array|false { return p\Grapheme::grapheme_str_split($string, $length); }
|
||||
}
|
||||
if (!function_exists('grapheme_levenshtein')) {
|
||||
function grapheme_levenshtein(string $string1, string $string2, int $insertion_cost = 1, int $replacement_cost = 1, int $deletion_cost = 1, string $locale = ''): int|false { return p\Grapheme::grapheme_levenshtein($string1, $string2, $insertion_cost, $replacement_cost, $deletion_cost); }
|
||||
}
|
||||
|
||||
if (extension_loaded('intl')) {
|
||||
return;
|
||||
|
||||
@@ -118,17 +118,15 @@ abstract class Collator
|
||||
}
|
||||
|
||||
/**
|
||||
* Not supported. Compare two Unicode strings.
|
||||
* Compare two Unicode strings.
|
||||
*
|
||||
* @return int|false
|
||||
*
|
||||
* @see https://php.net/collator.compare
|
||||
*
|
||||
* @throws MethodNotImplementedException
|
||||
*/
|
||||
public function compare(string $string1, string $string2)
|
||||
{
|
||||
throw new MethodNotImplementedException(__METHOD__);
|
||||
return strcasecmp($string1, $string2) ?: $string2 <=> $string1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -104,7 +104,7 @@ class FullTransformer
|
||||
|
||||
// handle unimplemented characters
|
||||
if (false !== strpos($this->notImplementedChars, $dateChars[0])) {
|
||||
throw new NotImplementedException(sprintf('Unimplemented date character "%s" in format "%s".', $dateChars[0], $this->pattern));
|
||||
throw new NotImplementedException(\sprintf('Unimplemented date character "%s" in format "%s".', $dateChars[0], $this->pattern));
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -212,7 +212,7 @@ class FullTransformer
|
||||
{
|
||||
$specialCharsArray = str_split($specialChars);
|
||||
|
||||
$specialCharsMatch = implode('|', array_map(function ($char) {
|
||||
$specialCharsMatch = implode('|', array_map(static function ($char) {
|
||||
return $char.'+';
|
||||
}, $specialCharsArray));
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ class MonthTransformer extends Transformer
|
||||
public function __construct()
|
||||
{
|
||||
if (0 === \count(self::$shortMonths)) {
|
||||
self::$shortMonths = array_map(function ($month) {
|
||||
self::$shortMonths = array_map(static function ($month) {
|
||||
return substr($month, 0, 3);
|
||||
}, self::$months);
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ class QuarterTransformer extends Transformer
|
||||
$map = [1 => '1st quarter', 2 => '2nd quarter', 3 => '3rd quarter', 4 => '4th quarter'];
|
||||
|
||||
return $map[$quarter];
|
||||
} else {
|
||||
return $quarter;
|
||||
}
|
||||
|
||||
return $quarter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ class TimezoneTransformer extends Transformer
|
||||
return $dateTime->format('\G\M\TP');
|
||||
}
|
||||
|
||||
return sprintf('GMT%s%d', $offset >= 0 ? '+' : '', $offset / 100);
|
||||
return \sprintf('GMT%s%d', $offset >= 0 ? '+' : '', $offset / 100);
|
||||
}
|
||||
|
||||
public function getReverseMatchingRegExp(int $length): string
|
||||
@@ -97,12 +97,12 @@ class TimezoneTransformer extends Transformer
|
||||
$signal = '-' === $matches['signal'] ? '+' : '-';
|
||||
|
||||
if (0 < $minutes) {
|
||||
throw new NotImplementedException(sprintf('It is not possible to use a GMT time zone with minutes offset different than zero (0). GMT time zone tried: "%s".', $formattedTimeZone));
|
||||
throw new NotImplementedException(\sprintf('It is not possible to use a GMT time zone with minutes offset different than zero (0). GMT time zone tried: "%s".', $formattedTimeZone));
|
||||
}
|
||||
|
||||
return 'Etc/GMT'.(0 !== $hours ? $signal.$hours : '');
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('The GMT time zone "%s" does not match with the supported formats GMT[+-]HH:MM or GMT[+-]HHMM.', $formattedTimeZone));
|
||||
throw new \InvalidArgumentException(\sprintf('The GMT time zone "%s" does not match with the supported formats GMT[+-]HH:MM or GMT[+-]HHMM.', $formattedTimeZone));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class MethodArgumentNotImplementedException extends NotImplementedException
|
||||
*/
|
||||
public function __construct(string $methodName, string $argName)
|
||||
{
|
||||
$message = sprintf('The %s() method\'s argument $%s behavior is not implemented.', $methodName, $argName);
|
||||
$message = \sprintf('The %s() method\'s argument $%s behavior is not implemented.', $methodName, $argName);
|
||||
parent::__construct($message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class MethodArgumentValueNotImplementedException extends NotImplementedException
|
||||
*/
|
||||
public function __construct(string $methodName, string $argName, $argValue, string $additionalMessage = '')
|
||||
{
|
||||
$message = sprintf(
|
||||
$message = \sprintf(
|
||||
'The %s() method\'s argument $%s value %s behavior is not implemented.%s',
|
||||
$methodName,
|
||||
$argName,
|
||||
|
||||
@@ -21,6 +21,6 @@ class MethodNotImplementedException extends NotImplementedException
|
||||
*/
|
||||
public function __construct(string $methodName)
|
||||
{
|
||||
parent::__construct(sprintf('The %s() is not implemented.', $methodName));
|
||||
parent::__construct(\sprintf('The %s() is not implemented.', $methodName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,10 +108,10 @@ abstract class Icu
|
||||
public static function setError(int $code, string $message = '')
|
||||
{
|
||||
if (!isset(self::$errorCodes[$code])) {
|
||||
throw new \InvalidArgumentException(sprintf('No such error code: "%s".', $code));
|
||||
throw new \InvalidArgumentException(\sprintf('No such error code: "%s".', $code));
|
||||
}
|
||||
|
||||
self::$errorMessage = $message ? sprintf('%s: %s', $message, self::$errorCodes[$code]) : self::$errorCodes[$code];
|
||||
self::$errorMessage = $message ? \sprintf('%s: %s', $message, self::$errorCodes[$code]) : self::$errorCodes[$code];
|
||||
self::$errorCode = $code;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ abstract class IntlDateFormatter
|
||||
// behave like the intl extension
|
||||
$argumentError = null;
|
||||
if (!\is_int($datetime) && !$datetime instanceof \DateTimeInterface) {
|
||||
$argumentError = sprintf('datefmt_format: string \'%s\' is not numeric, which would be required for it to be a valid date', $datetime);
|
||||
$argumentError = \sprintf('datefmt_format: string \'%s\' is not numeric, which would be required for it to be a valid date', $datetime);
|
||||
}
|
||||
|
||||
if (null !== $argumentError) {
|
||||
|
||||
169
lib/symfony/polyfill-intl-icu/IntlListFormatter.php
Normal file
169
lib/symfony/polyfill-intl-icu/IntlListFormatter.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Polyfill\Intl\Icu;
|
||||
|
||||
/**
|
||||
* @author Ayesh Karunaratne <ayesh@aye.sh>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class IntlListFormatter
|
||||
{
|
||||
public const TYPE_AND = 0;
|
||||
public const TYPE_OR = 1;
|
||||
public const TYPE_UNITS = 2;
|
||||
|
||||
public const WIDTH_WIDE = 0;
|
||||
public const WIDTH_SHORT = 1;
|
||||
public const WIDTH_NARROW = 2;
|
||||
|
||||
private $type;
|
||||
private $width;
|
||||
|
||||
private const TYPE_MAP = [
|
||||
self::TYPE_AND => 'standard',
|
||||
self::TYPE_OR => 'or',
|
||||
self::TYPE_UNITS => 'unit',
|
||||
];
|
||||
|
||||
private const WIDTH_MAP = [
|
||||
self::WIDTH_WIDE => '',
|
||||
self::WIDTH_SHORT => '-short',
|
||||
self::WIDTH_NARROW => '-narrow',
|
||||
];
|
||||
|
||||
private const EN_LIST_PATTERNS = [
|
||||
'listPattern-type-standard' => [
|
||||
'start' => '{0}, {1}',
|
||||
'middle' => '{0}, {1}',
|
||||
'end' => '{0}, and {1}',
|
||||
2 => '{0} and {1}',
|
||||
],
|
||||
'listPattern-type-or' => [
|
||||
'start' => '{0}, {1}',
|
||||
'middle' => '{0}, {1}',
|
||||
'end' => '{0}, or {1}',
|
||||
2 => '{0} or {1}',
|
||||
],
|
||||
'listPattern-type-or-narrow' => [
|
||||
'start' => '{0}, {1}',
|
||||
'middle' => '{0}, {1}',
|
||||
'end' => '{0}, or {1}',
|
||||
2 => '{0} or {1}',
|
||||
],
|
||||
'listPattern-type-or-short' => [
|
||||
'start' => '{0}, {1}',
|
||||
'middle' => '{0}, {1}',
|
||||
'end' => '{0}, or {1}',
|
||||
2 => '{0} or {1}',
|
||||
],
|
||||
'listPattern-type-standard-narrow' => [
|
||||
'start' => '{0}, {1}',
|
||||
'middle' => '{0}, {1}',
|
||||
'end' => '{0}, {1}',
|
||||
2 => '{0}, {1}',
|
||||
],
|
||||
'listPattern-type-standard-short' => [
|
||||
'start' => '{0}, {1}',
|
||||
'middle' => '{0}, {1}',
|
||||
'end' => '{0}, & {1}',
|
||||
2 => '{0} & {1}',
|
||||
],
|
||||
'listPattern-type-unit' => [
|
||||
'start' => '{0}, {1}',
|
||||
'middle' => '{0}, {1}',
|
||||
'end' => '{0}, {1}',
|
||||
2 => '{0}, {1}',
|
||||
],
|
||||
'listPattern-type-unit-narrow' => [
|
||||
'start' => '{0} {1}',
|
||||
'middle' => '{0} {1}',
|
||||
'end' => '{0} {1}',
|
||||
2 => '{0} {1}',
|
||||
],
|
||||
'listPattern-type-unit-short' => [
|
||||
'start' => '{0}, {1}',
|
||||
'middle' => '{0}, {1}',
|
||||
'end' => '{0}, {1}',
|
||||
2 => '{0}, {1}',
|
||||
],
|
||||
];
|
||||
|
||||
public function __construct(string $locale, int $type = self::TYPE_AND, int $width = self::WIDTH_WIDE)
|
||||
{
|
||||
if ('en' !== $locale && 0 !== strpos($locale, 'en')) {
|
||||
if (80000 > \PHP_VERSION_ID) {
|
||||
throw new \InvalidArgumentException('Invalid locale, only "en" and "en-*" locales are supported.');
|
||||
}
|
||||
|
||||
throw new \ValueError('Invalid locale, only "en" and "en-*" locales are supported.');
|
||||
}
|
||||
|
||||
if (!isset(self::TYPE_MAP[$type])) {
|
||||
if (80000 > \PHP_VERSION_ID) {
|
||||
throw new \InvalidArgumentException('Argument #2 ($type) must be one of IntlListFormatter::TYPE_AND, IntlListFormatter::TYPE_OR, or IntlListFormatter::TYPE_UNITS.');
|
||||
}
|
||||
|
||||
throw new \ValueError('Argument #2 ($type) must be one of IntlListFormatter::TYPE_AND, IntlListFormatter::TYPE_OR, or IntlListFormatter::TYPE_UNITS.');
|
||||
}
|
||||
|
||||
if (!isset(self::WIDTH_MAP[$width])) {
|
||||
if (80000 > \PHP_VERSION_ID) {
|
||||
throw new \InvalidArgumentException('Argument #3 ($width) must be one of IntlListFormatter::WIDTH_WIDE, IntlListFormatter::WIDTH_SHORT, or IntlListFormatter::WIDTH_NARROW.');
|
||||
}
|
||||
|
||||
throw new \ValueError('Argument #3 ($width) must be one of IntlListFormatter::WIDTH_WIDE, IntlListFormatter::WIDTH_SHORT, or IntlListFormatter::WIDTH_NARROW.');
|
||||
}
|
||||
|
||||
$this->type = $type;
|
||||
$this->width = $width;
|
||||
}
|
||||
|
||||
public function format(array $strings): string
|
||||
{
|
||||
$count = \count($strings);
|
||||
|
||||
if (0 === $count) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$strings = array_values($strings);
|
||||
|
||||
if (1 === $count) {
|
||||
return (string) $strings[0];
|
||||
}
|
||||
|
||||
$pattern = self::EN_LIST_PATTERNS['listPattern-type-'.self::TYPE_MAP[$this->type].self::WIDTH_MAP[$this->width]];
|
||||
|
||||
if (2 === $count) {
|
||||
return strtr($pattern[2], ['{0}' => (string) $strings[0], '{1}' => (string) $strings[1]]);
|
||||
}
|
||||
|
||||
$result = strtr($pattern['start'], ['{0}' => (string) $strings[0], '{1}' => (string) $strings[1]]);
|
||||
|
||||
for ($i = 2; $i < $count - 1; ++$i) {
|
||||
$result = strtr($pattern['middle'], ['{0}' => $result, '{1}' => (string) $strings[$i]]);
|
||||
}
|
||||
|
||||
return strtr($pattern['end'], ['{0}' => $result, '{1}' => (string) $strings[$count - 1]]);
|
||||
}
|
||||
|
||||
public function getErrorCode(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getErrorMessage(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,28 @@ abstract class Locale
|
||||
public const GRANDFATHERED_LANG_TAG = 'grandfathered';
|
||||
public const PRIVATE_TAG = 'private';
|
||||
|
||||
private const RTL_SCRIPTS = [
|
||||
'Adlm' => true, 'Arab' => true, 'Armi' => true, 'Hebr' => true,
|
||||
'Mand' => true, 'Mani' => true, 'Mend' => true, 'Nkoo' => true,
|
||||
'Orkh' => true, 'Phnx' => true, 'Rohg' => true, 'Samr' => true,
|
||||
'Syrc' => true, 'Thaa' => true, 'Yezi' => true,
|
||||
];
|
||||
|
||||
private const LANG_TO_SCRIPT = [
|
||||
'ar' => 'Arab',
|
||||
'ckb' => 'Arab',
|
||||
'dv' => 'Thaa',
|
||||
'fa' => 'Arab',
|
||||
'he' => 'Hebr',
|
||||
'ku' => 'Arab',
|
||||
'nqo' => 'Nkoo',
|
||||
'ps' => 'Arab',
|
||||
'sd' => 'Arab',
|
||||
'ug' => 'Arab',
|
||||
'ur' => 'Arab',
|
||||
'yi' => 'Hebr',
|
||||
];
|
||||
|
||||
/**
|
||||
* Not supported. Returns the best available locale based on HTTP "Accept-Language" header according to RFC 2616.
|
||||
*
|
||||
@@ -307,4 +329,22 @@ abstract class Locale
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function isRightToLeft(string $locale): bool
|
||||
{
|
||||
if ('' === $locale) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$parts = preg_split('/[_-]/', $locale);
|
||||
$language = strtolower($parts[0]);
|
||||
|
||||
foreach ($parts as $part) {
|
||||
if (4 === \strlen($part) && ctype_alpha($part)) {
|
||||
return isset(self::RTL_SCRIPTS[ucfirst(strtolower($part))]);
|
||||
}
|
||||
}
|
||||
|
||||
return isset(self::LANG_TO_SCRIPT[$language]) && isset(self::RTL_SCRIPTS[self::LANG_TO_SCRIPT[$language]]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ abstract class NumberFormatter
|
||||
}
|
||||
|
||||
if (!\in_array($style, self::$supportedStyles)) {
|
||||
$message = sprintf('The available styles are: %s.', implode(', ', array_keys(self::$supportedStyles)));
|
||||
$message = \sprintf('The available styles are: %s.', implode(', ', array_keys(self::$supportedStyles)));
|
||||
throw new MethodArgumentValueNotImplementedException(__METHOD__, 'style', $style, $message);
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ abstract class NumberFormatter
|
||||
// The original NumberFormatter does not support this format type
|
||||
if (self::TYPE_CURRENCY === $type) {
|
||||
if (\PHP_VERSION_ID >= 80000) {
|
||||
throw new \ValueError(sprintf('The format type must be a NumberFormatter::TYPE_* constant (%s given).', $type));
|
||||
throw new \ValueError(\sprintf('The format type must be a NumberFormatter::TYPE_* constant (%s given).', $type));
|
||||
}
|
||||
|
||||
trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING);
|
||||
@@ -361,7 +361,7 @@ abstract class NumberFormatter
|
||||
}
|
||||
|
||||
if (self::CURRENCY === $this->style) {
|
||||
throw new NotImplementedException(sprintf('"%s()" method does not support the formatting of currencies (instance with CURRENCY style). "%s".', __METHOD__, NotImplementedException::INTL_INSTALL_MESSAGE));
|
||||
throw new NotImplementedException(\sprintf('"%s()" method does not support the formatting of currencies (instance with CURRENCY style). "%s".', __METHOD__, NotImplementedException::INTL_INSTALL_MESSAGE));
|
||||
}
|
||||
|
||||
// Only the default type is supported.
|
||||
@@ -496,7 +496,7 @@ abstract class NumberFormatter
|
||||
{
|
||||
if (self::TYPE_DEFAULT === $type || self::TYPE_CURRENCY === $type) {
|
||||
if (\PHP_VERSION_ID >= 80000) {
|
||||
throw new \ValueError(sprintf('The format type must be a NumberFormatter::TYPE_* constant (%d given).', $type));
|
||||
throw new \ValueError(\sprintf('The format type must be a NumberFormatter::TYPE_* constant (%d given).', $type));
|
||||
}
|
||||
|
||||
trigger_error(__METHOD__.'(): Unsupported format type '.$type, \E_USER_WARNING);
|
||||
@@ -553,7 +553,7 @@ abstract class NumberFormatter
|
||||
public function setAttribute(int $attribute, $value)
|
||||
{
|
||||
if (!\in_array($attribute, self::$supportedAttributes)) {
|
||||
$message = sprintf(
|
||||
$message = \sprintf(
|
||||
'The available attributes are: %s',
|
||||
implode(', ', array_keys(self::$supportedAttributes))
|
||||
);
|
||||
@@ -562,7 +562,7 @@ abstract class NumberFormatter
|
||||
}
|
||||
|
||||
if (self::$supportedAttributes['ROUNDING_MODE'] === $attribute && $this->isInvalidRoundingMode($value)) {
|
||||
$message = sprintf(
|
||||
$message = \sprintf(
|
||||
'The supported values for ROUNDING_MODE are: %s',
|
||||
implode(', ', array_keys(self::$roundingModes))
|
||||
);
|
||||
|
||||
@@ -13,6 +13,7 @@ It is limited to the "en" locale and to:
|
||||
- [`NumberFormatter`](https://php.net/NumberFormatter)
|
||||
- [`Locale`](https://php.net/Locale)
|
||||
- [`IntlDateFormatter`](https://php.net/IntlDateFormatter)
|
||||
- [`IntlListFormatter`](https://php.net/IntlListFormatter)
|
||||
|
||||
More information can be found in the
|
||||
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Symfony\Polyfill\Intl\Icu\IntlListFormatter as IntlListFormatterPolyfill;
|
||||
|
||||
/**
|
||||
* Stub implementation for the IntlListFormatter class of the intl extension.
|
||||
*
|
||||
* @author Ayesh Karunaratne <ayesh@aye.sh>
|
||||
*/
|
||||
final class IntlListFormatter extends IntlListFormatterPolyfill
|
||||
{
|
||||
}
|
||||
@@ -133,7 +133,7 @@ final class Mbstring
|
||||
public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars)
|
||||
{
|
||||
$ok = true;
|
||||
array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
|
||||
array_walk_recursive($vars, static function (&$v) use (&$ok, $toEncoding, $fromEncoding) {
|
||||
if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) {
|
||||
$ok = false;
|
||||
}
|
||||
@@ -194,7 +194,7 @@ final class Mbstring
|
||||
$convmap[$i + 1] += $convmap[$i + 2];
|
||||
}
|
||||
|
||||
$s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) {
|
||||
$s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))'.(\PHP_VERSION_ID >= 80200 ? '' : '(?!&)').';?/', static function (array $m) use ($cnt, $convmap) {
|
||||
$c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1];
|
||||
for ($i = 0; $i < $cnt; $i += 4) {
|
||||
if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) {
|
||||
@@ -268,7 +268,7 @@ final class Mbstring
|
||||
for ($j = 0; $j < $cnt; $j += 4) {
|
||||
if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) {
|
||||
$cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3];
|
||||
$result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';';
|
||||
$result .= $is_hex ? \sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';';
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
@@ -382,7 +382,7 @@ final class Mbstring
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new \ValueError(sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding));
|
||||
throw new \ValueError(\sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding));
|
||||
}
|
||||
|
||||
public static function mb_language($lang = null)
|
||||
@@ -403,7 +403,7 @@ final class Mbstring
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new \ValueError(sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang));
|
||||
throw new \ValueError(\sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang));
|
||||
}
|
||||
|
||||
public static function mb_list_encodings()
|
||||
@@ -834,19 +834,32 @@ final class Mbstring
|
||||
return $code;
|
||||
}
|
||||
|
||||
public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null): string
|
||||
/** @return string|false */
|
||||
public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null)
|
||||
{
|
||||
if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) {
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
trigger_error('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH', \E_USER_WARNING);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH');
|
||||
}
|
||||
|
||||
if (null === $encoding) {
|
||||
$encoding = self::mb_internal_encoding();
|
||||
} else {
|
||||
self::assertEncoding($encoding, 'mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given');
|
||||
} elseif (!self::assertEncoding($encoding, 'mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (self::mb_strlen($pad_string, $encoding) <= 0) {
|
||||
if (\PHP_VERSION_ID < 80000) {
|
||||
trigger_error('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string', \E_USER_WARNING);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string');
|
||||
}
|
||||
|
||||
@@ -869,12 +882,13 @@ final class Mbstring
|
||||
}
|
||||
}
|
||||
|
||||
public static function mb_ucfirst(string $string, ?string $encoding = null): string
|
||||
/** @return string|false */
|
||||
public static function mb_ucfirst(string $string, ?string $encoding = null)
|
||||
{
|
||||
if (null === $encoding) {
|
||||
$encoding = self::mb_internal_encoding();
|
||||
} else {
|
||||
self::assertEncoding($encoding, 'mb_ucfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given');
|
||||
} elseif (!self::assertEncoding($encoding, 'mb_ucfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$firstChar = mb_substr($string, 0, 1, $encoding);
|
||||
@@ -883,12 +897,13 @@ final class Mbstring
|
||||
return $firstChar.mb_substr($string, 1, null, $encoding);
|
||||
}
|
||||
|
||||
public static function mb_lcfirst(string $string, ?string $encoding = null): string
|
||||
/** @return string|false */
|
||||
public static function mb_lcfirst(string $string, ?string $encoding = null)
|
||||
{
|
||||
if (null === $encoding) {
|
||||
$encoding = self::mb_internal_encoding();
|
||||
} else {
|
||||
self::assertEncoding($encoding, 'mb_lcfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given');
|
||||
} elseif (!self::assertEncoding($encoding, 'mb_lcfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$firstChar = mb_substr($string, 0, 1, $encoding);
|
||||
@@ -968,30 +983,42 @@ final class Mbstring
|
||||
return 'UTF-8';
|
||||
}
|
||||
|
||||
if ('UTF-32' === $encoding) {
|
||||
return 'UTF-32BE';
|
||||
}
|
||||
|
||||
if ('UTF-16' === $encoding) {
|
||||
return 'UTF-16BE';
|
||||
}
|
||||
|
||||
return $encoding;
|
||||
}
|
||||
|
||||
public static function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string
|
||||
/** @return string|false */
|
||||
public static function mb_trim(string $string, ?string $characters = null, ?string $encoding = null)
|
||||
{
|
||||
return self::mb_internal_trim('{^[%s]+|[%1$s]+$}Du', $string, $characters, $encoding, __FUNCTION__);
|
||||
}
|
||||
|
||||
public static function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string
|
||||
/** @return string|false */
|
||||
public static function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null)
|
||||
{
|
||||
return self::mb_internal_trim('{^[%s]+}Du', $string, $characters, $encoding, __FUNCTION__);
|
||||
}
|
||||
|
||||
public static function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string
|
||||
/** @return string|false */
|
||||
public static function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null)
|
||||
{
|
||||
return self::mb_internal_trim('{[%s]+$}Du', $string, $characters, $encoding, __FUNCTION__);
|
||||
}
|
||||
|
||||
private static function mb_internal_trim(string $regex, string $string, ?string $characters, ?string $encoding, string $function): string
|
||||
/** @return string|false */
|
||||
private static function mb_internal_trim(string $regex, string $string, ?string $characters, ?string $encoding, string $function)
|
||||
{
|
||||
if (null === $encoding) {
|
||||
$encoding = self::mb_internal_encoding();
|
||||
} else {
|
||||
self::assertEncoding($encoding, $function.'(): Argument #3 ($encoding) must be a valid encoding, "%s" given');
|
||||
} elseif (!self::assertEncoding($encoding, $function.'(): Argument #3 ($encoding) must be a valid encoding, "%s" given')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ('' === $characters) {
|
||||
@@ -1020,7 +1047,7 @@ final class Mbstring
|
||||
$characters = preg_quote($characters);
|
||||
}
|
||||
|
||||
$string = preg_replace(sprintf($regex, $characters), '', $string);
|
||||
$string = preg_replace(\sprintf($regex, $characters), '', $string);
|
||||
|
||||
if (null === $encoding) {
|
||||
return $string;
|
||||
@@ -1029,17 +1056,22 @@ final class Mbstring
|
||||
return iconv('UTF-8', $encoding.'//IGNORE', $string);
|
||||
}
|
||||
|
||||
private static function assertEncoding(string $encoding, string $errorFormat): void
|
||||
private static function assertEncoding(string $encoding, string $errorFormat): bool
|
||||
{
|
||||
try {
|
||||
$validEncoding = @self::mb_check_encoding('', $encoding);
|
||||
} catch (\ValueError $e) {
|
||||
throw new \ValueError(sprintf($errorFormat, $encoding));
|
||||
throw new \ValueError(\sprintf($errorFormat, $encoding));
|
||||
}
|
||||
|
||||
// BC for PHP 7.3 and lower
|
||||
if (!$validEncoding) {
|
||||
throw new \ValueError(sprintf($errorFormat, $encoding));
|
||||
if (80000 > \PHP_VERSION_ID) {
|
||||
trigger_error(\sprintf($errorFormat, $encoding), \E_USER_WARNING);
|
||||
} else {
|
||||
throw new \ValueError(\sprintf($errorFormat, $encoding));
|
||||
}
|
||||
}
|
||||
|
||||
return $validEncoding;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,30 +133,29 @@ if (!function_exists('mb_str_split')) {
|
||||
}
|
||||
|
||||
if (!function_exists('mb_str_pad')) {
|
||||
function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); }
|
||||
function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null) { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); }
|
||||
}
|
||||
|
||||
if (!function_exists('mb_ucfirst')) {
|
||||
function mb_ucfirst(string $string, ?string $encoding = null): string { return p\Mbstring::mb_ucfirst($string, $encoding); }
|
||||
function mb_ucfirst(string $string, ?string $encoding = null) { return p\Mbstring::mb_ucfirst($string, $encoding); }
|
||||
}
|
||||
|
||||
if (!function_exists('mb_lcfirst')) {
|
||||
function mb_lcfirst(string $string, ?string $encoding = null): string { return p\Mbstring::mb_lcfirst($string, $encoding); }
|
||||
function mb_lcfirst(string $string, ?string $encoding = null) { return p\Mbstring::mb_lcfirst($string, $encoding); }
|
||||
}
|
||||
|
||||
if (!function_exists('mb_trim')) {
|
||||
function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_trim($string, $characters, $encoding); }
|
||||
function mb_trim(string $string, ?string $characters = null, ?string $encoding = null) { return p\Mbstring::mb_trim($string, $characters, $encoding); }
|
||||
}
|
||||
|
||||
if (!function_exists('mb_ltrim')) {
|
||||
function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_ltrim($string, $characters, $encoding); }
|
||||
function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null) { return p\Mbstring::mb_ltrim($string, $characters, $encoding); }
|
||||
}
|
||||
|
||||
if (!function_exists('mb_rtrim')) {
|
||||
function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_rtrim($string, $characters, $encoding); }
|
||||
function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null) { return p\Mbstring::mb_rtrim($string, $characters, $encoding); }
|
||||
}
|
||||
|
||||
|
||||
if (extension_loaded('mbstring')) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ final class Php83
|
||||
}
|
||||
|
||||
if ($depth > self::JSON_MAX_DEPTH) {
|
||||
throw new \ValueError(sprintf('json_validate(): Argument #2 ($depth) must be less than %d', self::JSON_MAX_DEPTH));
|
||||
throw new \ValueError(\sprintf('json_validate(): Argument #2 ($depth) must be less than %d', self::JSON_MAX_DEPTH));
|
||||
}
|
||||
|
||||
json_decode($json, true, $depth, $flags);
|
||||
@@ -40,7 +40,8 @@ final class Php83
|
||||
return \JSON_ERROR_NONE === json_last_error();
|
||||
}
|
||||
|
||||
public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null): string
|
||||
/** @return string|false */
|
||||
public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null)
|
||||
{
|
||||
if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) {
|
||||
throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH');
|
||||
@@ -50,19 +51,27 @@ final class Php83
|
||||
$encoding = mb_internal_encoding();
|
||||
}
|
||||
|
||||
$errorToTrigger = null;
|
||||
try {
|
||||
$validEncoding = @mb_check_encoding('', $encoding);
|
||||
if (!@mb_check_encoding('', $encoding)) {
|
||||
$errorToTrigger = \sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding);
|
||||
}
|
||||
} catch (\ValueError $e) {
|
||||
throw new \ValueError(sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding));
|
||||
}
|
||||
|
||||
// BC for PHP 7.3 and lower
|
||||
if (!$validEncoding) {
|
||||
throw new \ValueError(sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding));
|
||||
$errorToTrigger = \sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding);
|
||||
}
|
||||
|
||||
if (mb_strlen($pad_string, $encoding) <= 0) {
|
||||
throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string');
|
||||
$errorToTrigger = 'mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string';
|
||||
}
|
||||
|
||||
if (null !== $errorToTrigger) {
|
||||
if (80000 > \PHP_VERSION_ID) {
|
||||
trigger_error($errorToTrigger, \E_USER_WARNING);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new \ValueError($errorToTrigger);
|
||||
}
|
||||
|
||||
$paddingRequired = $length - mb_strlen($string, $encoding);
|
||||
@@ -135,7 +144,7 @@ final class Php83
|
||||
}
|
||||
|
||||
if (preg_match('/\A(?:0[aA0]?|[aA])\z/', $string)) {
|
||||
throw new \ValueError(sprintf('str_decrement(): Argument #1 ($string) "%s" is out of decrement range', $string));
|
||||
throw new \ValueError(\sprintf('str_decrement(): Argument #1 ($string) "%s" is out of decrement range', $string));
|
||||
}
|
||||
|
||||
if (!\in_array(substr($string, -1), ['A', 'a', '0'], true)) {
|
||||
|
||||
@@ -19,12 +19,6 @@ if (!function_exists('json_validate')) {
|
||||
function json_validate(string $json, int $depth = 512, int $flags = 0): bool { return p\Php83::json_validate($json, $depth, $flags); }
|
||||
}
|
||||
|
||||
if (extension_loaded('mbstring')) {
|
||||
if (!function_exists('mb_str_pad')) {
|
||||
function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Php83::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); }
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('stream_context_set_options')) {
|
||||
function stream_context_set_options($context, array $options): bool { return stream_context_set_option($context, $options); }
|
||||
}
|
||||
@@ -37,8 +31,14 @@ if (!function_exists('str_decrement')) {
|
||||
function str_decrement(string $string): string { return p\Php83::str_decrement($string); }
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID >= 80100) {
|
||||
return require __DIR__.'/bootstrap81.php';
|
||||
if (\PHP_VERSION_ID >= 80000) {
|
||||
return require __DIR__.'/bootstrap80.php';
|
||||
}
|
||||
|
||||
if (extension_loaded('mbstring')) {
|
||||
if (!function_exists('mb_str_pad')) {
|
||||
function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null) { return p\Php83::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); }
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('ldap_exop_sync') && function_exists('ldap_exop')) {
|
||||
|
||||
30
lib/symfony/polyfill-php83/bootstrap80.php
Normal file
30
lib/symfony/polyfill-php83/bootstrap80.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
use Symfony\Polyfill\Php83 as p;
|
||||
|
||||
if (extension_loaded('mbstring')) {
|
||||
if (!function_exists('mb_str_pad')) {
|
||||
function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Php83::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); }
|
||||
}
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID >= 80100) {
|
||||
return require __DIR__.'/bootstrap81.php';
|
||||
}
|
||||
|
||||
if (!function_exists('ldap_exop_sync') && function_exists('ldap_exop')) {
|
||||
function ldap_exop_sync($ldap, string $request_oid, ?string $request_data = null, ?array $controls = null, &$response_data = null, &$response_oid = null): bool { return ldap_exop($ldap, $request_oid, $request_data, $controls, $response_data, $response_oid); }
|
||||
}
|
||||
|
||||
if (!function_exists('ldap_connect_wallet') && function_exists('ldap_connect')) {
|
||||
function ldap_connect_wallet(?string $uri, string $wallet, string $password, int $auth_mode = \GSLC_SSL_NO_AUTH) { return ldap_connect($uri, $wallet, $password, $auth_mode); }
|
||||
}
|
||||
19
lib/symfony/runtime/CHANGELOG.md
Normal file
19
lib/symfony/runtime/CHANGELOG.md
Normal file
@@ -0,0 +1,19 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
6.4
|
||||
---
|
||||
|
||||
* Add argument `bool $debug = false` to `HttpKernelRunner::__construct()`
|
||||
|
||||
5.4
|
||||
---
|
||||
|
||||
* The component is not experimental anymore
|
||||
* Add options "env_var_name" and "debug_var_name" to `GenericRuntime` and `SymfonyRuntime`
|
||||
* Add option "dotenv_overload" to `SymfonyRuntime`
|
||||
|
||||
5.3.0
|
||||
-----
|
||||
|
||||
* Add the component
|
||||
223
lib/symfony/runtime/GenericRuntime.php
Normal file
223
lib/symfony/runtime/GenericRuntime.php
Normal file
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime;
|
||||
|
||||
use Symfony\Component\Runtime\Internal\BasicErrorHandler;
|
||||
use Symfony\Component\Runtime\Resolver\ClosureResolver;
|
||||
use Symfony\Component\Runtime\Resolver\DebugClosureResolver;
|
||||
use Symfony\Component\Runtime\Runner\ClosureRunner;
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(ClosureResolver::class);
|
||||
|
||||
/**
|
||||
* A runtime to do bare-metal PHP without using superglobals.
|
||||
*
|
||||
* It supports the following options:
|
||||
* - "debug" toggles displaying errors and defaults
|
||||
* to the "APP_DEBUG" environment variable;
|
||||
* - "runtimes" maps types to a GenericRuntime implementation
|
||||
* that knows how to deal with each of them;
|
||||
* - "error_handler" defines the class to use to handle PHP errors;
|
||||
* - "env_var_name" and "debug_var_name" define the name of the env
|
||||
* vars that hold the Symfony env and the debug flag respectively.
|
||||
*
|
||||
* The app-callable can declare arguments among either:
|
||||
* - "array $context" to get a local array similar to $_SERVER;
|
||||
* - "array $argv" to get the command line arguments when running on the CLI;
|
||||
* - "array $request" to get a local array with keys "query", "body", "files" and
|
||||
* "session", which map to $_GET, $_POST, $FILES and &$_SESSION respectively.
|
||||
*
|
||||
* It should return a Closure():int|string|null or an instance of RunnerInterface.
|
||||
*
|
||||
* In debug mode, the runtime registers a strict error handler
|
||||
* that throws exceptions when a PHP warning/notice is raised.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class GenericRuntime implements RuntimeInterface
|
||||
{
|
||||
protected $options;
|
||||
|
||||
/**
|
||||
* @param array {
|
||||
* debug?: ?bool,
|
||||
* runtimes?: ?array,
|
||||
* error_handler?: string|false,
|
||||
* env_var_name?: string,
|
||||
* debug_var_name?: string,
|
||||
* } $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$options['env_var_name'] ??= 'APP_ENV';
|
||||
$debugKey = $options['debug_var_name'] ??= 'APP_DEBUG';
|
||||
|
||||
$debug = $options['debug'] ?? $_SERVER[$debugKey] ?? $_ENV[$debugKey] ?? true;
|
||||
|
||||
if (!\is_bool($debug)) {
|
||||
$debug = filter_var($debug, \FILTER_VALIDATE_BOOL);
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
umask(0000);
|
||||
$_SERVER[$debugKey] = $_ENV[$debugKey] = '1';
|
||||
} else {
|
||||
$_SERVER[$debugKey] = $_ENV[$debugKey] = '0';
|
||||
}
|
||||
|
||||
if (false !== $errorHandler = ($options['error_handler'] ?? BasicErrorHandler::class)) {
|
||||
$errorHandler::register($debug);
|
||||
$options['error_handler'] = false;
|
||||
}
|
||||
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
public function getResolver(callable $callable, ?\ReflectionFunction $reflector = null): ResolverInterface
|
||||
{
|
||||
$callable = $callable(...);
|
||||
$parameters = ($reflector ?? new \ReflectionFunction($callable))->getParameters();
|
||||
$arguments = function () use ($parameters) {
|
||||
$arguments = [];
|
||||
|
||||
try {
|
||||
foreach ($parameters as $parameter) {
|
||||
$type = $parameter->getType();
|
||||
$arguments[] = $this->getArgument($parameter, $type instanceof \ReflectionNamedType ? $type->getName() : null);
|
||||
}
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
if (!$parameter->isOptional()) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
};
|
||||
|
||||
if ($_SERVER[$this->options['debug_var_name']]) {
|
||||
return new DebugClosureResolver($callable, $arguments);
|
||||
}
|
||||
|
||||
return new ClosureResolver($callable, $arguments);
|
||||
}
|
||||
|
||||
public function getRunner(?object $application): RunnerInterface
|
||||
{
|
||||
$application ??= static fn () => 0;
|
||||
|
||||
if ($application instanceof RunnerInterface) {
|
||||
return $application;
|
||||
}
|
||||
|
||||
if (!$application instanceof \Closure) {
|
||||
if ($runtime = $this->resolveRuntime($application::class)) {
|
||||
return $runtime->getRunner($application);
|
||||
}
|
||||
|
||||
if (!\is_callable($application)) {
|
||||
throw new \LogicException(\sprintf('"%s" doesn\'t know how to handle apps of type "%s".', get_debug_type($this), get_debug_type($application)));
|
||||
}
|
||||
|
||||
$application = $application(...);
|
||||
}
|
||||
|
||||
if ($_SERVER[$this->options['debug_var_name']] && ($r = new \ReflectionFunction($application)) && $r->getNumberOfRequiredParameters()) {
|
||||
throw new \ArgumentCountError(\sprintf('Zero argument should be required by the runner callable, but at least one is in "%s" on line "%d.', $r->getFileName(), $r->getStartLine()));
|
||||
}
|
||||
|
||||
return new ClosureRunner($application);
|
||||
}
|
||||
|
||||
protected function getArgument(\ReflectionParameter $parameter, ?string $type): mixed
|
||||
{
|
||||
if ('array' === $type) {
|
||||
switch ($parameter->name) {
|
||||
case 'context':
|
||||
$context = $_SERVER;
|
||||
|
||||
if ($_ENV && !isset($_SERVER['PATH']) && !isset($_SERVER['Path'])) {
|
||||
$context += $_ENV;
|
||||
}
|
||||
|
||||
return $context;
|
||||
|
||||
case 'argv':
|
||||
return $_SERVER['argv'] ?? [];
|
||||
|
||||
case 'request':
|
||||
return [
|
||||
'query' => $_GET,
|
||||
'body' => $_POST,
|
||||
'files' => $_FILES,
|
||||
'session' => &$_SESSION,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (RuntimeInterface::class === $type) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (!$runtime = $this->getRuntime($type)) {
|
||||
$r = $parameter->getDeclaringFunction();
|
||||
|
||||
throw new \InvalidArgumentException(\sprintf('Cannot resolve argument "%s $%s" in "%s" on line "%d": "%s" supports only arguments "array $context", "array $argv" and "array $request", or a runtime named "Symfony\Runtime\%1$sRuntime".', $type, $parameter->name, $r->getFileName(), $r->getStartLine(), get_debug_type($this)));
|
||||
}
|
||||
|
||||
return $runtime->getArgument($parameter, $type);
|
||||
}
|
||||
|
||||
protected static function register(self $runtime): self
|
||||
{
|
||||
return $runtime;
|
||||
}
|
||||
|
||||
private function getRuntime(string $type): ?self
|
||||
{
|
||||
if (null === $runtime = ($this->options['runtimes'][$type] ?? null)) {
|
||||
$runtime = 'Symfony\Runtime\\'.$type.'Runtime';
|
||||
$runtime = class_exists($runtime) ? $runtime : $this->options['runtimes'][$type] = false;
|
||||
}
|
||||
|
||||
if (\is_string($runtime)) {
|
||||
$runtime = $runtime::register($this);
|
||||
}
|
||||
|
||||
if ($this === $runtime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $runtime ?: null;
|
||||
}
|
||||
|
||||
private function resolveRuntime(string $class): ?self
|
||||
{
|
||||
if ($runtime = $this->getRuntime($class)) {
|
||||
return $runtime;
|
||||
}
|
||||
|
||||
foreach (class_parents($class) as $type) {
|
||||
if ($runtime = $this->getRuntime($type)) {
|
||||
return $runtime;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (class_implements($class) as $type) {
|
||||
if ($runtime = $this->getRuntime($type)) {
|
||||
return $runtime;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
53
lib/symfony/runtime/Internal/BasicErrorHandler.php
Normal file
53
lib/symfony/runtime/Internal/BasicErrorHandler.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime\Internal;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class BasicErrorHandler
|
||||
{
|
||||
public static function register(bool $debug): void
|
||||
{
|
||||
error_reporting(\E_ALL & ~\E_DEPRECATED & ~\E_USER_DEPRECATED);
|
||||
|
||||
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
|
||||
ini_set('display_errors', $debug);
|
||||
} elseif (!filter_var(\ini_get('log_errors'), \FILTER_VALIDATE_BOOL) || \ini_get('error_log')) {
|
||||
// CLI - display errors only if they're not already logged to STDERR
|
||||
ini_set('display_errors', 1);
|
||||
}
|
||||
|
||||
if (0 <= \ini_get('zend.assertions')) {
|
||||
ini_set('zend.assertions', (int) $debug);
|
||||
}
|
||||
ini_set('assert.active', 1);
|
||||
ini_set('assert.exception', 1);
|
||||
|
||||
set_error_handler(new self());
|
||||
}
|
||||
|
||||
public function __invoke(int $type, string $message, string $file, int $line): bool
|
||||
{
|
||||
if ((\E_DEPRECATED | \E_USER_DEPRECATED) & $type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((error_reporting() | \E_ERROR | \E_RECOVERABLE_ERROR | \E_PARSE | \E_CORE_ERROR | \E_COMPILE_ERROR | \E_USER_ERROR) & $type) {
|
||||
throw new \ErrorException($message, 0, $type, $file, $line);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
119
lib/symfony/runtime/Internal/ComposerPlugin.php
Normal file
119
lib/symfony/runtime/Internal/ComposerPlugin.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime\Internal;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\EventDispatcher\EventSubscriberInterface;
|
||||
use Composer\Factory;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Plugin\PluginInterface;
|
||||
use Composer\Script\ScriptEvents;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class ComposerPlugin implements PluginInterface, EventSubscriberInterface
|
||||
{
|
||||
private Composer $composer;
|
||||
private IOInterface $io;
|
||||
|
||||
private static bool $activated = false;
|
||||
|
||||
public function activate(Composer $composer, IOInterface $io): void
|
||||
{
|
||||
self::$activated = true;
|
||||
$this->composer = $composer;
|
||||
$this->io = $io;
|
||||
}
|
||||
|
||||
public function deactivate(Composer $composer, IOInterface $io): void
|
||||
{
|
||||
self::$activated = false;
|
||||
}
|
||||
|
||||
public function uninstall(Composer $composer, IOInterface $io): void
|
||||
{
|
||||
@unlink($composer->getConfig()->get('vendor-dir').'/autoload_runtime.php');
|
||||
}
|
||||
|
||||
public function updateAutoloadFile(): void
|
||||
{
|
||||
$vendorDir = realpath($this->composer->getConfig()->get('vendor-dir'));
|
||||
|
||||
if (!is_file($autoloadFile = $vendorDir.'/autoload.php')
|
||||
|| false === $extra = $this->composer->getPackage()->getExtra()['runtime'] ?? []
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fs = new Filesystem();
|
||||
$projectDir = \dirname(realpath(Factory::getComposerFile()));
|
||||
|
||||
if (null === $autoloadTemplate = $extra['autoload_template'] ?? null) {
|
||||
$autoloadTemplate = __DIR__.'/autoload_runtime.template';
|
||||
} else {
|
||||
if (!$fs->isAbsolutePath($autoloadTemplate)) {
|
||||
$autoloadTemplate = $projectDir.'/'.$autoloadTemplate;
|
||||
}
|
||||
|
||||
if (!is_file($autoloadTemplate)) {
|
||||
throw new \InvalidArgumentException(\sprintf('File "%s" defined under "extra.runtime.autoload_template" in your composer.json file not found.', $this->composer->getPackage()->getExtra()['runtime']['autoload_template']));
|
||||
}
|
||||
}
|
||||
|
||||
$projectDir = $fs->makePathRelative($projectDir, $vendorDir);
|
||||
$nestingLevel = 0;
|
||||
|
||||
while (str_starts_with($projectDir, '../')) {
|
||||
++$nestingLevel;
|
||||
$projectDir = substr($projectDir, 3);
|
||||
}
|
||||
|
||||
if (!$nestingLevel) {
|
||||
$projectDir = '__'.'DIR__.'.var_export('/'.$projectDir, true);
|
||||
} else {
|
||||
$projectDir = 'dirname(__'."DIR__, $nestingLevel)".('' !== $projectDir ? '.'.var_export('/'.$projectDir, true) : '');
|
||||
}
|
||||
|
||||
$runtimeClass = $extra['class'] ?? SymfonyRuntime::class;
|
||||
|
||||
unset($extra['class'], $extra['autoload_template']);
|
||||
|
||||
$code = strtr(file_get_contents($autoloadTemplate), [
|
||||
'%project_dir%' => $projectDir,
|
||||
'%runtime_class%' => var_export($runtimeClass, true),
|
||||
'%runtime_options%' => '['.substr(var_export($extra, true), 7, -1)." 'project_dir' => {$projectDir},\n]",
|
||||
]);
|
||||
|
||||
// could use Composer\Util\Filesystem::filePutContentsIfModified once Composer 1.x support is dropped for this plugin
|
||||
$path = substr_replace($autoloadFile, '_runtime', -4, 0);
|
||||
$currentContent = @file_exists($path) ? @file_get_contents($path) : false;
|
||||
if (false === $currentContent || $currentContent !== $code) {
|
||||
file_put_contents($path, $code);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
if (!self::$activated) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
ScriptEvents::POST_AUTOLOAD_DUMP => 'updateAutoloadFile',
|
||||
];
|
||||
}
|
||||
}
|
||||
21
lib/symfony/runtime/Internal/Console/ApplicationRuntime.php
Normal file
21
lib/symfony/runtime/Internal/Console/ApplicationRuntime.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\Console;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ApplicationRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\Console\Command;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class CommandRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\Console\Input;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class InputInterfaceRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\Console\Output;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class OutputInterfaceRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\HttpFoundation;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class RequestRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\HttpFoundation;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ResponseRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Runtime\Symfony\Component\HttpKernel;
|
||||
|
||||
use Symfony\Component\Runtime\SymfonyRuntime;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class HttpKernelInterfaceRuntime extends SymfonyRuntime
|
||||
{
|
||||
}
|
||||
19
lib/symfony/runtime/Internal/MissingDotenv.php
Normal file
19
lib/symfony/runtime/Internal/MissingDotenv.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime\Internal;
|
||||
|
||||
/**
|
||||
* @internal class that should be loaded only when symfony/dotenv is not installed
|
||||
*/
|
||||
class MissingDotenv
|
||||
{
|
||||
}
|
||||
54
lib/symfony/runtime/Internal/SymfonyErrorHandler.php
Normal file
54
lib/symfony/runtime/Internal/SymfonyErrorHandler.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime\Internal;
|
||||
|
||||
use Symfony\Component\ErrorHandler\BufferingLogger;
|
||||
use Symfony\Component\ErrorHandler\DebugClassLoader;
|
||||
use Symfony\Component\ErrorHandler\ErrorHandler;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class SymfonyErrorHandler
|
||||
{
|
||||
public static function register(bool $debug): void
|
||||
{
|
||||
if (!class_exists(ErrorHandler::class)) {
|
||||
BasicErrorHandler::register($debug);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
error_reporting(\E_ALL & ~\E_DEPRECATED & ~\E_USER_DEPRECATED);
|
||||
|
||||
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
|
||||
ini_set('display_errors', $debug);
|
||||
} elseif (!filter_var(\ini_get('log_errors'), \FILTER_VALIDATE_BOOL) || \ini_get('error_log')) {
|
||||
// CLI - display errors only if they're not already logged to STDERR
|
||||
ini_set('display_errors', 1);
|
||||
}
|
||||
|
||||
if (0 <= \ini_get('zend.assertions')) {
|
||||
ini_set('zend.assertions', (int) $debug);
|
||||
}
|
||||
ini_set('assert.active', 1);
|
||||
ini_set('assert.exception', 1);
|
||||
|
||||
if ($debug) {
|
||||
DebugClassLoader::enable();
|
||||
}
|
||||
|
||||
ErrorHandler::register(new ErrorHandler(new BufferingLogger(), $debug));
|
||||
}
|
||||
}
|
||||
28
lib/symfony/runtime/Internal/autoload_runtime.template
Normal file
28
lib/symfony/runtime/Internal/autoload_runtime.template
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
// autoload_runtime.php @generated by Symfony Runtime
|
||||
|
||||
if (true === (require_once __DIR__.'/autoload.php') || empty($_SERVER['SCRIPT_FILENAME'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$app = require $_SERVER['SCRIPT_FILENAME'];
|
||||
|
||||
if (!is_object($app)) {
|
||||
throw new TypeError(sprintf('Invalid return value: callable object expected, "%s" returned from "%s".', get_debug_type($app), $_SERVER['SCRIPT_FILENAME']));
|
||||
}
|
||||
|
||||
$runtime = $_SERVER['APP_RUNTIME'] ?? $_ENV['APP_RUNTIME'] ?? %runtime_class%;
|
||||
$runtime = new $runtime(($_SERVER['APP_RUNTIME_OPTIONS'] ?? $_ENV['APP_RUNTIME_OPTIONS'] ?? []) + %runtime_options%);
|
||||
|
||||
[$app, $args] = $runtime
|
||||
->getResolver($app)
|
||||
->resolve();
|
||||
|
||||
$app = $app(...$args);
|
||||
|
||||
exit(
|
||||
$runtime
|
||||
->getRunner($app)
|
||||
->run()
|
||||
);
|
||||
19
lib/symfony/runtime/LICENSE
Normal file
19
lib/symfony/runtime/LICENSE
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2021-present Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
13
lib/symfony/runtime/README.md
Normal file
13
lib/symfony/runtime/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
Runtime Component
|
||||
=================
|
||||
|
||||
Symfony Runtime enables decoupling applications from global state.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/runtime.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
31
lib/symfony/runtime/Resolver/ClosureResolver.php
Normal file
31
lib/symfony/runtime/Resolver/ClosureResolver.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime\Resolver;
|
||||
|
||||
use Symfony\Component\Runtime\ResolverInterface;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ClosureResolver implements ResolverInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly \Closure $closure,
|
||||
private readonly \Closure $arguments,
|
||||
) {
|
||||
}
|
||||
|
||||
public function resolve(): array
|
||||
{
|
||||
return [$this->closure, ($this->arguments)()];
|
||||
}
|
||||
}
|
||||
36
lib/symfony/runtime/Resolver/DebugClosureResolver.php
Normal file
36
lib/symfony/runtime/Resolver/DebugClosureResolver.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime\Resolver;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class DebugClosureResolver extends ClosureResolver
|
||||
{
|
||||
public function resolve(): array
|
||||
{
|
||||
[$closure, $arguments] = parent::resolve();
|
||||
|
||||
return [
|
||||
static function (...$arguments) use ($closure) {
|
||||
if (\is_object($app = $closure(...$arguments)) || null === $app) {
|
||||
return $app;
|
||||
}
|
||||
|
||||
$r = new \ReflectionFunction($closure);
|
||||
|
||||
throw new \TypeError(\sprintf('Unexpected value of type "%s" returned, "object" expected from "%s" on line "%d".', get_debug_type($app), $r->getFileName(), $r->getStartLine()));
|
||||
},
|
||||
$arguments,
|
||||
];
|
||||
}
|
||||
}
|
||||
23
lib/symfony/runtime/ResolverInterface.php
Normal file
23
lib/symfony/runtime/ResolverInterface.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface ResolverInterface
|
||||
{
|
||||
/**
|
||||
* @return array{0: callable, 1: mixed[]}
|
||||
*/
|
||||
public function resolve(): array;
|
||||
}
|
||||
44
lib/symfony/runtime/Runner/ClosureRunner.php
Normal file
44
lib/symfony/runtime/Runner/ClosureRunner.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime\Runner;
|
||||
|
||||
use Symfony\Component\Runtime\RunnerInterface;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ClosureRunner implements RunnerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly \Closure $closure,
|
||||
) {
|
||||
}
|
||||
|
||||
public function run(): int
|
||||
{
|
||||
$exitStatus = ($this->closure)();
|
||||
|
||||
if (\is_string($exitStatus)) {
|
||||
echo $exitStatus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (null !== $exitStatus && !\is_int($exitStatus)) {
|
||||
$r = new \ReflectionFunction($this->closure);
|
||||
|
||||
throw new \TypeError(\sprintf('Unexpected value of type "%s" returned, "string|int|null" expected from "%s" on line "%d".', get_debug_type($exitStatus), $r->getFileName(), $r->getStartLine()));
|
||||
}
|
||||
|
||||
return $exitStatus ?? 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime\Runner\Symfony;
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Runtime\RunnerInterface;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ConsoleApplicationRunner implements RunnerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Application $application,
|
||||
private readonly ?string $defaultEnv,
|
||||
private readonly InputInterface $input,
|
||||
private readonly ?OutputInterface $output = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public function run(): int
|
||||
{
|
||||
if (null === $this->defaultEnv) {
|
||||
return $this->application->run($this->input, $this->output);
|
||||
}
|
||||
|
||||
$definition = $this->application->getDefinition();
|
||||
|
||||
if (!$definition->hasOption('env') && !$definition->hasOption('e') && !$definition->hasShortcut('e')) {
|
||||
$definition->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'The Environment name.', $this->defaultEnv));
|
||||
}
|
||||
|
||||
if (!$definition->hasOption('no-debug')) {
|
||||
$definition->addOption(new InputOption('--no-debug', null, InputOption::VALUE_NONE, 'Switches off debug mode.'));
|
||||
}
|
||||
|
||||
return $this->application->run($this->input, $this->output);
|
||||
}
|
||||
}
|
||||
58
lib/symfony/runtime/Runner/Symfony/HttpKernelRunner.php
Normal file
58
lib/symfony/runtime/Runner/Symfony/HttpKernelRunner.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime\Runner\Symfony;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
use Symfony\Component\HttpKernel\TerminableInterface;
|
||||
use Symfony\Component\Runtime\RunnerInterface;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class HttpKernelRunner implements RunnerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly HttpKernelInterface $kernel,
|
||||
private readonly Request $request,
|
||||
private readonly bool $debug = false,
|
||||
) {
|
||||
}
|
||||
|
||||
public function run(): int
|
||||
{
|
||||
$response = $this->kernel->handle($this->request);
|
||||
|
||||
if (Kernel::VERSION_ID >= 60400) {
|
||||
$response->send(false);
|
||||
|
||||
if (\function_exists('fastcgi_finish_request') && !$this->debug) {
|
||||
fastcgi_finish_request();
|
||||
} elseif (\function_exists('litespeed_finish_request') && !$this->debug) {
|
||||
litespeed_finish_request();
|
||||
} else {
|
||||
Response::closeOutputBuffers(0, true);
|
||||
flush();
|
||||
}
|
||||
} else {
|
||||
$response->send();
|
||||
}
|
||||
|
||||
if ($this->kernel instanceof TerminableInterface) {
|
||||
$this->kernel->terminate($this->request, $response);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
33
lib/symfony/runtime/Runner/Symfony/ResponseRunner.php
Normal file
33
lib/symfony/runtime/Runner/Symfony/ResponseRunner.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime\Runner\Symfony;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Runtime\RunnerInterface;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ResponseRunner implements RunnerInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Response $response,
|
||||
) {
|
||||
}
|
||||
|
||||
public function run(): int
|
||||
{
|
||||
$this->response->send();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
20
lib/symfony/runtime/RunnerInterface.php
Normal file
20
lib/symfony/runtime/RunnerInterface.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface RunnerInterface
|
||||
{
|
||||
public function run(): int;
|
||||
}
|
||||
34
lib/symfony/runtime/RuntimeInterface.php
Normal file
34
lib/symfony/runtime/RuntimeInterface.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime;
|
||||
|
||||
/**
|
||||
* Enables decoupling applications from global state.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface RuntimeInterface
|
||||
{
|
||||
/**
|
||||
* Returns a resolver that should compute the arguments of a callable.
|
||||
*
|
||||
* The callable itself should return an object that represents the application to pass to the getRunner() method.
|
||||
*/
|
||||
public function getResolver(callable $callable, ?\ReflectionFunction $reflector = null): ResolverInterface;
|
||||
|
||||
/**
|
||||
* Returns a callable that knows how to run the passed object and that returns its exit status as int.
|
||||
*
|
||||
* The passed object is typically created by calling ResolverInterface::resolve().
|
||||
*/
|
||||
public function getRunner(?object $application): RunnerInterface;
|
||||
}
|
||||
235
lib/symfony/runtime/SymfonyRuntime.php
Normal file
235
lib/symfony/runtime/SymfonyRuntime.php
Normal file
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Runtime;
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\Runtime\Internal\MissingDotenv;
|
||||
use Symfony\Component\Runtime\Internal\SymfonyErrorHandler;
|
||||
use Symfony\Component\Runtime\Runner\Symfony\ConsoleApplicationRunner;
|
||||
use Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner;
|
||||
use Symfony\Component\Runtime\Runner\Symfony\ResponseRunner;
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(MissingDotenv::class, false) || class_exists(Dotenv::class) || class_exists(MissingDotenv::class);
|
||||
|
||||
/**
|
||||
* Knows the basic conventions to run Symfony apps.
|
||||
*
|
||||
* In addition to the options managed by GenericRuntime, it accepts the following options:
|
||||
* - "env" to define the name of the environment the app runs in;
|
||||
* - "disable_dotenv" to disable looking for .env files;
|
||||
* - "dotenv_path" to define the path of dot-env files - defaults to ".env";
|
||||
* - "prod_envs" to define the names of the production envs - defaults to ["prod"];
|
||||
* - "test_envs" to define the names of the test envs - defaults to ["test"];
|
||||
* - "use_putenv" to tell Dotenv to set env vars using putenv() (NOT RECOMMENDED.)
|
||||
* - "dotenv_overload" to tell Dotenv to override existing vars
|
||||
*
|
||||
* When the "debug" / "env" options are not defined, they will fallback to the
|
||||
* "APP_DEBUG" / "APP_ENV" environment variables, and to the "--env|-e" / "--no-debug"
|
||||
* command line arguments if "symfony/console" is installed.
|
||||
*
|
||||
* When the "symfony/dotenv" component is installed, .env files are loaded.
|
||||
* When "symfony/error-handler" is installed, it is registered in debug mode.
|
||||
*
|
||||
* On top of the base arguments provided by GenericRuntime,
|
||||
* this runtime can feed the app-callable with arguments of type:
|
||||
* - Request from "symfony/http-foundation" if the component is installed;
|
||||
* - Application, Command, InputInterface and/or OutputInterface
|
||||
* from "symfony/console" if the component is installed.
|
||||
*
|
||||
* This runtime can handle app-callables that return instances of either:
|
||||
* - HttpKernelInterface,
|
||||
* - Response,
|
||||
* - Application,
|
||||
* - Command,
|
||||
* - int|string|null as handled by GenericRuntime.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class SymfonyRuntime extends GenericRuntime
|
||||
{
|
||||
private readonly ArgvInput $input;
|
||||
private readonly ConsoleOutput $output;
|
||||
private readonly Application $console;
|
||||
private readonly Command $command;
|
||||
private readonly Request $request;
|
||||
|
||||
/**
|
||||
* @param array {
|
||||
* debug?: ?bool,
|
||||
* env?: ?string,
|
||||
* disable_dotenv?: ?bool,
|
||||
* project_dir?: ?string,
|
||||
* prod_envs?: ?string[],
|
||||
* dotenv_path?: ?string,
|
||||
* test_envs?: ?string[],
|
||||
* use_putenv?: ?bool,
|
||||
* runtimes?: ?array,
|
||||
* error_handler?: string|false,
|
||||
* env_var_name?: string,
|
||||
* debug_var_name?: string,
|
||||
* dotenv_overload?: ?bool,
|
||||
* } $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$envKey = $options['env_var_name'] ??= 'APP_ENV';
|
||||
$debugKey = $options['debug_var_name'] ??= 'APP_DEBUG';
|
||||
|
||||
if (isset($options['env'])) {
|
||||
$_SERVER[$envKey] = $options['env'];
|
||||
} elseif (empty($_GET) && isset($_SERVER['argv']) && class_exists(ArgvInput::class)) {
|
||||
$this->options = $options;
|
||||
$this->getInput();
|
||||
}
|
||||
|
||||
if (!($options['disable_dotenv'] ?? false) && isset($options['project_dir']) && !class_exists(MissingDotenv::class, false)) {
|
||||
(new Dotenv($envKey, $debugKey))
|
||||
->setProdEnvs((array) ($options['prod_envs'] ?? ['prod']))
|
||||
->usePutenv($options['use_putenv'] ?? false)
|
||||
->bootEnv($options['project_dir'].'/'.($options['dotenv_path'] ?? '.env'), 'dev', (array) ($options['test_envs'] ?? ['test']), $options['dotenv_overload'] ?? false);
|
||||
|
||||
if (isset($this->input) && ($options['dotenv_overload'] ?? false)) {
|
||||
if ($this->input->getParameterOption(['--env', '-e'], $_SERVER[$envKey], true) !== $_SERVER[$envKey]) {
|
||||
throw new \LogicException(\sprintf('Cannot use "--env" or "-e" when the "%s" file defines "%s" and the "dotenv_overload" runtime option is true.', $options['dotenv_path'] ?? '.env', $envKey));
|
||||
}
|
||||
|
||||
if ($_SERVER[$debugKey] && $this->input->hasParameterOption('--no-debug', true)) {
|
||||
putenv($debugKey.'='.$_SERVER[$debugKey] = $_ENV[$debugKey] = '0');
|
||||
}
|
||||
}
|
||||
|
||||
$options['debug'] ??= '1' === $_SERVER[$debugKey];
|
||||
$options['disable_dotenv'] = true;
|
||||
} else {
|
||||
$_SERVER[$envKey] ??= $_ENV[$envKey] ?? 'dev';
|
||||
$_SERVER[$debugKey] ??= $_ENV[$debugKey] ?? !\in_array($_SERVER[$envKey], (array) ($options['prod_envs'] ?? ['prod']), true);
|
||||
}
|
||||
|
||||
$options['error_handler'] ??= SymfonyErrorHandler::class;
|
||||
|
||||
parent::__construct($options);
|
||||
}
|
||||
|
||||
public function getRunner(?object $application): RunnerInterface
|
||||
{
|
||||
if ($application instanceof HttpKernelInterface) {
|
||||
return new HttpKernelRunner($application, $this->request ??= Request::createFromGlobals(), $this->options['debug'] ?? false);
|
||||
}
|
||||
|
||||
if ($application instanceof Response) {
|
||||
return new ResponseRunner($application);
|
||||
}
|
||||
|
||||
if ($application instanceof Command) {
|
||||
$console = $this->console ??= new Application();
|
||||
$console->setName($application->getName() ?: $console->getName());
|
||||
|
||||
if (!$application->getName() || !$console->has($application->getName())) {
|
||||
$application->setName($_SERVER['argv'][0]);
|
||||
if (method_exists($console, 'addCommand')) {
|
||||
$console->addCommand($application);
|
||||
} else {
|
||||
$console->add($application);
|
||||
}
|
||||
}
|
||||
|
||||
$console->setDefaultCommand($application->getName(), true);
|
||||
$console->getDefinition()->addOptions($application->getDefinition()->getOptions());
|
||||
|
||||
return $this->getRunner($console);
|
||||
}
|
||||
|
||||
if ($application instanceof Application) {
|
||||
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
|
||||
echo 'Warning: The console should be invoked via the CLI version of PHP, not the '.\PHP_SAPI.' SAPI'.\PHP_EOL;
|
||||
}
|
||||
|
||||
set_time_limit(0);
|
||||
$defaultEnv = !isset($this->options['env']) ? ($_SERVER[$this->options['env_var_name']] ?? 'dev') : null;
|
||||
$output = $this->output ??= new ConsoleOutput();
|
||||
|
||||
return new ConsoleApplicationRunner($application, $defaultEnv, $this->getInput(), $output);
|
||||
}
|
||||
|
||||
if (isset($this->command)) {
|
||||
$this->getInput()->bind($this->command->getDefinition());
|
||||
}
|
||||
|
||||
return parent::getRunner($application);
|
||||
}
|
||||
|
||||
protected function getArgument(\ReflectionParameter $parameter, ?string $type): mixed
|
||||
{
|
||||
return match ($type) {
|
||||
Request::class => $this->request ??= Request::createFromGlobals(),
|
||||
InputInterface::class => $this->getInput(),
|
||||
OutputInterface::class => $this->output ??= new ConsoleOutput(),
|
||||
Application::class => $this->console ??= new Application(),
|
||||
Command::class => $this->command ??= new Command(),
|
||||
default => parent::getArgument($parameter, $type),
|
||||
};
|
||||
}
|
||||
|
||||
protected static function register(GenericRuntime $runtime): GenericRuntime
|
||||
{
|
||||
$self = new self($runtime->options + ['runtimes' => []]);
|
||||
$self->options['runtimes'] += [
|
||||
HttpKernelInterface::class => $self,
|
||||
Request::class => $self,
|
||||
Response::class => $self,
|
||||
Application::class => $self,
|
||||
Command::class => $self,
|
||||
InputInterface::class => $self,
|
||||
OutputInterface::class => $self,
|
||||
];
|
||||
$runtime->options = $self->options;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
private function getInput(): ArgvInput
|
||||
{
|
||||
if (!empty($_GET) && filter_var(\ini_get('register_argc_argv'), \FILTER_VALIDATE_BOOL)) {
|
||||
throw new \Exception('CLI applications cannot be run safely on non-CLI SAPIs with register_argc_argv=On.');
|
||||
}
|
||||
|
||||
if (isset($this->input)) {
|
||||
return $this->input;
|
||||
}
|
||||
|
||||
$input = new ArgvInput();
|
||||
|
||||
if (isset($this->options['env'])) {
|
||||
return $this->input = $input;
|
||||
}
|
||||
|
||||
if (null !== $env = $input->getParameterOption(['--env', '-e'], null, true)) {
|
||||
putenv($this->options['env_var_name'].'='.$_SERVER[$this->options['env_var_name']] = $_ENV[$this->options['env_var_name']] = $env);
|
||||
}
|
||||
|
||||
if ($input->hasParameterOption('--no-debug', true)) {
|
||||
putenv($this->options['debug_var_name'].'='.$_SERVER[$this->options['debug_var_name']] = $_ENV[$this->options['debug_var_name']] = '0');
|
||||
}
|
||||
|
||||
return $this->input = $input;
|
||||
}
|
||||
}
|
||||
45
lib/symfony/runtime/composer.json
Normal file
45
lib/symfony/runtime/composer.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "symfony/runtime",
|
||||
"type": "composer-plugin",
|
||||
"description": "Enables decoupling PHP applications from global state",
|
||||
"keywords": ["runtime"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license" : "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"composer-plugin-api": "^1.0|^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "^1.0.2|^2.0",
|
||||
"symfony/console": "^5.4.9|^6.0.9|^7.0",
|
||||
"symfony/dotenv": "^5.4|^6.0|^7.0",
|
||||
"symfony/http-foundation": "^5.4|^6.0|^7.0",
|
||||
"symfony/http-kernel": "^5.4|^6.0|^7.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/dotenv": "<5.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Runtime\\": "",
|
||||
"Symfony\\Runtime\\Symfony\\Component\\": "Internal/"
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"class": "Symfony\\Component\\Runtime\\Internal\\ComposerPlugin"
|
||||
}
|
||||
}
|
||||
@@ -72,11 +72,11 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="19">
|
||||
<source>Too many failed login attempts, please try again in %minutes% minute.</source>
|
||||
<target state="needs-review-translation">Te veel mislukte aanmeldpogings, probeer asseblief weer oor %minutes% minuut.</target>
|
||||
<target>Te veel mislukte aanmeldpogings, probeer asseblief weer oor %minutes% minute.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="20">
|
||||
<source>Too many failed login attempts, please try again in %minutes% minutes.</source>
|
||||
<target state="needs-review-translation">Te veel mislukte aanmeldpogings, probeer asseblief weer oor %minutes% minute.</target>
|
||||
<target>Te veel mislukte aanmeldpogings, probeer asseblief weer oor %minutes% minute.</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="20">
|
||||
<source>Too many failed login attempts, please try again in %minutes% minutes.</source>
|
||||
<target state="needs-review-translation">Çox sayda uğursuz giriş cəhdi, zəhmət olmasa %minutes% dəqiqə sonra yenidən cəhd edin.|Çox sayda uğursuz giriş cəhdi, zəhmət olmasa %minutes% dəqiqə sonra yenidən cəhd edin.</target>
|
||||
<target>Çox sayda uğursuz giriş cəhdi, zəhmət olmasa %minutes% dəqiqə sonra yenidən cəhd edin.|Çox sayda uğursuz giriş cəhdi, zəhmət olmasa %minutes% dəqiqə sonra yenidən cəhd edin.</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="20">
|
||||
<source>Too many failed login attempts, please try again in %minutes% minutes.</source>
|
||||
<target state="needs-review-translation">Liiga palju nurjunud sisselogimiskatseid, proovige uuesti %minutes% minuti pärast.</target>
|
||||
<target>Liiga palju nurjunud sisselogimiskatseid, proovige uuesti %minutes% minuti pärast.</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="20">
|
||||
<source>Too many failed login attempts, please try again in %minutes% minutes.</source>
|
||||
<target state="needs-review-translation">Չափազանց շատ անհաջող մուտքի փորձեր, խնդրում ենք փորձել կրկին %minutes% րոպեից.</target>
|
||||
<target>Չափազանց շատ անհաջող մուտքի փորձեր, խնդրում ենք փորձել կրկին %minutes% րոպեից.</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="20">
|
||||
<source>Too many failed login attempts, please try again in %minutes% minutes.</source>
|
||||
<target state="needs-review-translation">Per daug nesėkmingų prisijungimo bandymų, bandykite vėl po %minutes% minutės.|Per daug nesėkmingų prisijungimo bandymų, bandykite vėl po %minutes% minučių.</target>
|
||||
<target>Per daug nesėkmingų prisijungimo bandymų, bandykite vėl po %minutes% minutės.|Per daug nesėkmingų prisijungimo bandymų, bandykite vėl po %minutes% minučių.</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="20">
|
||||
<source>Too many failed login attempts, please try again in %minutes% minutes.</source>
|
||||
<target state="needs-review-translation">Koʻplab muvaffaqiyatsiz kirish urinishlari, iltimos, %minutes% daqiqadan so'ng qayta urinib koʻring.|Koʻplab muvaffaqiyatsiz kirish urinishlari, iltimos, %minutes% daqiqadan so'ng qayta urinib koʻring.</target>
|
||||
<target>Koʻplab muvaffaqiyatsiz kirish urinishlari, iltimos, %minutes% daqiqadan so'ng qayta urinib koʻring.|Koʻplab muvaffaqiyatsiz kirish urinishlari, iltimos, %minutes% daqiqadan so'ng qayta urinib koʻring.</target>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
|
||||
@@ -47,9 +47,10 @@ final class WrappedTemplatedEmail
|
||||
{
|
||||
$file = $this->twig->getLoader()->getSourceContext($image);
|
||||
$body = $file->getPath() ? new File($file->getPath()) : $file->getCode();
|
||||
$this->message->addPart((new DataPart($body, $image, $contentType))->asInline());
|
||||
$dataPart = (new DataPart($body, $image, $contentType))->asInline();
|
||||
$this->message->addPart($dataPart);
|
||||
|
||||
return 'cid:'.$image;
|
||||
return 'cid:'.$dataPart->getContentId();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user