Merge branch '4.4'
* 4.4: (24 commits) [Console] Command::execute() should always return int - deprecate returning null [FrameworkBundle] Fix wrong returned status code in ConfigDebugCommand [AnnotationCacheWarmer] add RedirectController to annotation cache [WebProfilerBundle] Try to display the most useful panel by default Add note about deprecating the XmlEncoder::TYPE_CASE_ATTRIBUTES constant in the upgrade guide fix merge [DI] add tests loading calls with returns-clone [DI] dont mandate a class on inline services with a factory Fixed Redis Sentinel usage when only one Sentinel specified [EventDispatcher] Added tests for aliased events. Sync Twig templateExists behaviors Fix the :only-of-type pseudo class selector Deprecate the XmlEncoder::TYPE_CASE_ATTRIBUTES constant [Mailer] Tweak some code [Serializer] Add CsvEncoder tests for PHP 7.4 Copy phpunit.xsd to a predictable path [WebserverBundle] Remove duplicated deprecation message remove duplicated test [Security/Http] fix parsing X509 emailAddress [FrameworkBundle] conflict with VarDumper < 4.4 ...
This commit is contained in:
commit
3ee9dbd17b
@ -10,6 +10,7 @@ Console
|
||||
-------
|
||||
|
||||
* Deprecated finding hidden commands using an abbreviation, use the full name instead
|
||||
* Deprecated returning `null` from `Command::execute()`, return `0` instead
|
||||
|
||||
Debug
|
||||
-----
|
||||
@ -219,6 +220,11 @@ Security
|
||||
) {}
|
||||
```
|
||||
|
||||
Serializer
|
||||
----------
|
||||
|
||||
* Deprecated the `XmlEncoder::TYPE_CASE_ATTRIBUTES` constant. Use `XmlEncoder::TYPE_CAST_ATTRIBUTES` instead.
|
||||
|
||||
Stopwatch
|
||||
---------
|
||||
|
||||
|
@ -37,6 +37,7 @@ Console
|
||||
* Removed the `getHorizontalBorderChar()` method in favor of the `getBorderChars()` method in `TableStyle`.
|
||||
* Removed the `setVerticalBorderChar()` method in favor of the `setVerticalBorderChars()` method in `TableStyle`.
|
||||
* Removed the `getVerticalBorderChar()` method in favor of the `getBorderChars()` method in `TableStyle`.
|
||||
* Removed support for returning `null` from `Command::execute()`, return `0` instead
|
||||
* The `ProcessHelper::run()` method takes the command as an array of arguments.
|
||||
|
||||
Before:
|
||||
@ -536,6 +537,11 @@ Serializer
|
||||
* The `AbstractNormalizer::handleCircularReference()` method has two new `$format` and `$context` arguments.
|
||||
* Removed support for instantiating a `DataUriNormalizer` with a default MIME type guesser when the `symfony/mime` component isn't installed.
|
||||
|
||||
Serializer
|
||||
----------
|
||||
|
||||
* Removed the `XmlEncoder::TYPE_CASE_ATTRIBUTES` constant. Use `XmlEncoder::TYPE_CAST_ATTRIBUTES` instead.
|
||||
|
||||
Stopwatch
|
||||
---------
|
||||
|
||||
|
@ -116,6 +116,7 @@ if (!file_exists("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit") || $configurationH
|
||||
passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? 'rmdir /S /Q %s': 'rm -rf %s', "$PHPUNIT_VERSION_DIR.old"));
|
||||
}
|
||||
passthru("$COMPOSER create-project --no-install --prefer-dist --no-scripts --no-plugins --no-progress --ansi phpunit/phpunit $PHPUNIT_VERSION_DIR \"$PHPUNIT_VERSION.*\"");
|
||||
@copy("$PHPUNIT_VERSION_DIR/phpunit.xsd", 'phpunit.xsd');
|
||||
chdir("$PHPUNIT_VERSION_DIR");
|
||||
if ($SYMFONY_PHPUNIT_REMOVE) {
|
||||
passthru("$COMPOSER remove --no-update ".$SYMFONY_PHPUNIT_REMOVE);
|
||||
|
@ -97,12 +97,16 @@ EOF
|
||||
|
||||
switch ($input->getOption('format')) {
|
||||
case 'text':
|
||||
return $name ? $this->displayPathsText($io, $name) : $this->displayGeneralText($io, $filter);
|
||||
$name ? $this->displayPathsText($io, $name) : $this->displayGeneralText($io, $filter);
|
||||
break;
|
||||
case 'json':
|
||||
return $name ? $this->displayPathsJson($io, $name) : $this->displayGeneralJson($io, $filter);
|
||||
$name ? $this->displayPathsJson($io, $name) : $this->displayGeneralJson($io, $filter);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $input->getOption('format')));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function displayPathsText(SymfonyStyle $io, string $name)
|
||||
|
@ -54,7 +54,7 @@ EOT
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
@ -100,6 +100,8 @@ EOT
|
||||
}
|
||||
|
||||
$io->table([], $rows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static function formatPath(string $path, string $baseDir): string
|
||||
|
@ -72,7 +72,7 @@ EOF
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$fs = $this->filesystem;
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
@ -175,6 +175,8 @@ EOF
|
||||
}
|
||||
|
||||
$io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully cleared.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function warmup(string $warmupDir, string $realCacheDir, bool $enableOptionalWarmers = true)
|
||||
|
@ -60,7 +60,7 @@ EOF
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$kernel = $this->getApplication()->getKernel();
|
||||
@ -99,5 +99,7 @@ EOF
|
||||
}
|
||||
|
||||
$io->success('Cache was successfully cleared.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ EOF
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$pool = $input->getArgument('pool');
|
||||
@ -69,7 +69,7 @@ EOF
|
||||
if (!$cachePool->hasItem($key)) {
|
||||
$io->note(sprintf('Cache item "%s" does not exist in cache pool "%s".', $key, $pool));
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!$cachePool->deleteItem($key)) {
|
||||
@ -77,5 +77,7 @@ EOF
|
||||
}
|
||||
|
||||
$io->success(sprintf('Cache item "%s" was successfully deleted.', $key));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -51,12 +51,14 @@ EOF
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$io->table(['Pool name'], array_map(function ($pool) {
|
||||
return [$pool];
|
||||
}, $this->poolNames));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ EOF
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
@ -67,5 +67,7 @@ EOF
|
||||
}
|
||||
|
||||
$io->success('Successfully pruned cache pool(s).');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ EOF
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
@ -80,5 +80,7 @@ EOF
|
||||
$this->cacheWarmer->warmUp($kernel->getContainer()->getParameter('kernel.cache_dir'));
|
||||
|
||||
$io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully warmed.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ EOF
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$errorIo = $io->getErrorStyle();
|
||||
@ -73,7 +73,7 @@ EOF
|
||||
$errorIo->comment('Provide the name of a bundle as the first argument of this command to dump its configuration. (e.g. <comment>debug:config FrameworkBundle</comment>)');
|
||||
$errorIo->comment('For dumping a specific option, add its path as the second argument of this command. (e.g. <comment>debug:config FrameworkBundle serializer</comment> to dump the <comment>framework.serializer</comment> configuration)');
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
$extension = $this->findExtension($name);
|
||||
@ -101,7 +101,7 @@ EOF
|
||||
|
||||
$io->writeln(Yaml::dump([$extensionAlias => $config], 10));
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
@ -109,12 +109,14 @@ EOF
|
||||
} catch (LogicException $e) {
|
||||
$errorIo->error($e->getMessage());
|
||||
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
$io->title(sprintf('Current configuration for "%s.%s"', $extensionAlias, $path));
|
||||
|
||||
$io->writeln(Yaml::dump($config, 10));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function compileContainer(): ContainerBuilder
|
||||
|
@ -113,7 +113,7 @@ EOF
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$errorIo = $io->getErrorStyle();
|
||||
@ -179,6 +179,8 @@ EOF
|
||||
$errorIo->comment('To search for a specific service, re-run this command with a search term. (e.g. <comment>debug:container log</comment>)');
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,7 +69,7 @@ EOF
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
@ -78,7 +78,7 @@ EOF
|
||||
if (!$this->dispatcher->hasListeners($event)) {
|
||||
$io->getErrorStyle()->warning(sprintf('The event "%s" does not have any registered listeners.', $event));
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
$options = ['event' => $event];
|
||||
@ -89,5 +89,7 @@ EOF
|
||||
$options['raw_text'] = $input->getOption('raw');
|
||||
$options['output'] = $io;
|
||||
$helper->describe($io, $this->dispatcher, $options);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ EOF
|
||||
*
|
||||
* @throws InvalidArgumentException When route does not exist
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$name = $input->getArgument('name');
|
||||
@ -105,6 +105,8 @@ EOF
|
||||
'output' => $io,
|
||||
]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function findRouteNameContaining(string $name, RouteCollection $routes): array
|
||||
|
@ -117,7 +117,7 @@ EOF
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
@ -191,7 +191,7 @@ EOF
|
||||
|
||||
$io->getErrorStyle()->warning($outputMessage);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Load the fallback catalogues
|
||||
@ -240,6 +240,8 @@ EOF
|
||||
}
|
||||
|
||||
$io->table($headers, $rows);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function formatState(int $state): string
|
||||
|
@ -59,7 +59,7 @@ EOF
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$container = $this->getApplication()->getKernel()->getContainer();
|
||||
$serviceId = $input->getArgument('name');
|
||||
@ -97,5 +97,7 @@ EOF
|
||||
],
|
||||
];
|
||||
$output->writeln($dumper->dump($workflow->getDefinition(), $marking, $options));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@
|
||||
<service id="annotations.cache_warmer" class="Symfony\Bundle\FrameworkBundle\CacheWarmer\AnnotationsCacheWarmer">
|
||||
<argument type="service" id="annotations.reader" />
|
||||
<argument>%kernel.cache_dir%/annotations.php</argument>
|
||||
<argument>#^Symfony\\(?:Component\\HttpKernel\\|Bundle\\FrameworkBundle\\Controller\\(?!AbstractController$|Controller$))#</argument>
|
||||
<argument>#^Symfony\\(?:Component\\HttpKernel\\|Bundle\\FrameworkBundle\\Controller\\(?!.*Controller$))#</argument>
|
||||
<argument>%kernel.debug%</argument>
|
||||
</service>
|
||||
|
||||
|
@ -83,6 +83,7 @@
|
||||
"symfony/twig-bridge": "<4.4",
|
||||
"symfony/twig-bundle": "<4.4",
|
||||
"symfony/validator": "<4.4",
|
||||
"symfony/var-dumper": "<4.4",
|
||||
"symfony/workflow": "<4.4"
|
||||
},
|
||||
"suggest": {
|
||||
|
@ -17,6 +17,8 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
|
||||
use Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profiler;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
@ -76,7 +78,7 @@ class ProfilerController
|
||||
$this->cspHandler->disableCsp();
|
||||
}
|
||||
|
||||
$panel = $request->query->get('panel', 'request');
|
||||
$panel = $request->query->get('panel');
|
||||
$page = $request->query->get('page', 'home');
|
||||
|
||||
if ('latest' === $token && $latest = current($this->profiler->find(null, null, 1, null, null, null))) {
|
||||
@ -87,6 +89,22 @@ class ProfilerController
|
||||
return new Response($this->twig->render('@WebProfiler/Profiler/info.html.twig', ['about' => 'no_token', 'token' => $token, 'request' => $request]), 200, ['Content-Type' => 'text/html']);
|
||||
}
|
||||
|
||||
if (null === $panel) {
|
||||
$panel = 'request';
|
||||
|
||||
foreach ($profile->getCollectors() as $collector) {
|
||||
if ($collector instanceof ExceptionDataCollector && $collector->hasException()) {
|
||||
$panel = $collector->getName();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ($collector instanceof DumpDataCollector && $collector->getDumpsCount() > 0) {
|
||||
$panel = $collector->getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$profile->hasCollector($panel)) {
|
||||
throw new NotFoundHttpException(sprintf('Panel "%s" is not available for token "%s".', $panel, $token));
|
||||
}
|
||||
|
@ -267,7 +267,7 @@
|
||||
if (request.profilerUrl) {
|
||||
profilerCell.textContent = '';
|
||||
var profilerLink = document.createElement('a');
|
||||
profilerLink.setAttribute('href', request.statusCode < 400 ? request.profilerUrl : request.profilerUrl + '?panel=exception');
|
||||
profilerLink.setAttribute('href', request.profilerUrl);
|
||||
profilerLink.textContent = request.profile;
|
||||
profilerCell.appendChild(profilerLink);
|
||||
}
|
||||
|
@ -15,8 +15,16 @@ use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController;
|
||||
use Symfony\Bundle\WebProfilerBundle\Csp\ContentSecurityPolicyHandler;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
|
||||
use Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector;
|
||||
use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profiler;
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\LoaderInterface;
|
||||
use Twig\Loader\SourceContextLoaderInterface;
|
||||
|
||||
class ProfilerControllerTest extends TestCase
|
||||
{
|
||||
@ -185,7 +193,97 @@ class ProfilerControllerTest extends TestCase
|
||||
];
|
||||
}
|
||||
|
||||
private function createController($profiler, $twig, $withCSP): ProfilerController
|
||||
/**
|
||||
* @dataProvider defaultPanelProvider
|
||||
*/
|
||||
public function testDefaultPanel(string $expectedPanel, Profile $profile)
|
||||
{
|
||||
$profiler = $this->createMock(Profiler::class);
|
||||
$profiler
|
||||
->expects($this->atLeastOnce())
|
||||
->method('loadProfile')
|
||||
->with($profile->getToken())
|
||||
->willReturn($profile);
|
||||
|
||||
$profiler
|
||||
->expects($this->atLeastOnce())
|
||||
->method('has')
|
||||
->with($this->logicalXor($collectorsNames = array_keys($profile->getCollectors())))
|
||||
->willReturn(true);
|
||||
|
||||
$expectedTemplate = 'expected_template.html.twig';
|
||||
|
||||
if (Environment::MAJOR_VERSION > 1) {
|
||||
$loader = $this->createMock(LoaderInterface::class);
|
||||
$loader
|
||||
->expects($this->atLeastOnce())
|
||||
->method('exists')
|
||||
->with($this->logicalXor($expectedTemplate, 'other_template.html.twig'))
|
||||
->willReturn(true);
|
||||
} else {
|
||||
$loader = $this->createMock(SourceContextLoaderInterface::class);
|
||||
}
|
||||
|
||||
$twig = $this->createMock(Environment::class);
|
||||
$twig
|
||||
->expects($this->atLeastOnce())
|
||||
->method('getLoader')
|
||||
->willReturn($loader);
|
||||
$twig
|
||||
->expects($this->once())
|
||||
->method('render')
|
||||
->with($expectedTemplate);
|
||||
|
||||
$this
|
||||
->createController($profiler, $twig, false, array_map(function (string $collectorName) use ($expectedPanel, $expectedTemplate): array {
|
||||
if ($collectorName === $expectedPanel) {
|
||||
return [$expectedPanel, $expectedTemplate];
|
||||
}
|
||||
|
||||
return [$collectorName, 'other_template.html.twig'];
|
||||
}, $collectorsNames))
|
||||
->panelAction(new Request(), $profile->getToken());
|
||||
}
|
||||
|
||||
public function defaultPanelProvider(): \Generator
|
||||
{
|
||||
// Test default behavior
|
||||
$profile = new Profile('xxxxxx');
|
||||
$profile->addCollector($requestDataCollector = new RequestDataCollector());
|
||||
yield [$requestDataCollector->getName(), $profile];
|
||||
|
||||
// Test exception
|
||||
$profile = new Profile('xxxxxx');
|
||||
$profile->addCollector($exceptionDataCollector = new ExceptionDataCollector());
|
||||
$exceptionDataCollector->collect(new Request(), new Response(), new \DomainException());
|
||||
yield [$exceptionDataCollector->getName(), $profile];
|
||||
|
||||
// Test exception priority
|
||||
$dumpDataCollector = $this->createMock(DumpDataCollector::class);
|
||||
$dumpDataCollector
|
||||
->expects($this->atLeastOnce())
|
||||
->method('getName')
|
||||
->willReturn('dump');
|
||||
$dumpDataCollector
|
||||
->expects($this->atLeastOnce())
|
||||
->method('getDumpsCount')
|
||||
->willReturn(1);
|
||||
$profile = new Profile('xxxxxx');
|
||||
$profile->setCollectors([$exceptionDataCollector, $dumpDataCollector]);
|
||||
yield [$exceptionDataCollector->getName(), $profile];
|
||||
|
||||
// Test exception priority when defined afterwards
|
||||
$profile = new Profile('xxxxxx');
|
||||
$profile->setCollectors([$dumpDataCollector, $exceptionDataCollector]);
|
||||
yield [$exceptionDataCollector->getName(), $profile];
|
||||
|
||||
// Test dump
|
||||
$profile = new Profile('xxxxxx');
|
||||
$profile->addCollector($dumpDataCollector);
|
||||
yield [$dumpDataCollector->getName(), $profile];
|
||||
}
|
||||
|
||||
private function createController($profiler, $twig, $withCSP, array $templates = []): ProfilerController
|
||||
{
|
||||
$urlGenerator = $this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->getMock();
|
||||
|
||||
@ -193,9 +291,9 @@ class ProfilerControllerTest extends TestCase
|
||||
$nonceGenerator = $this->getMockBuilder('Symfony\Bundle\WebProfilerBundle\Csp\NonceGenerator')->getMock();
|
||||
$nonceGenerator->method('generate')->willReturn('dummy_nonce');
|
||||
|
||||
return new ProfilerController($urlGenerator, $profiler, $twig, [], new ContentSecurityPolicyHandler($nonceGenerator));
|
||||
return new ProfilerController($urlGenerator, $profiler, $twig, $templates, new ContentSecurityPolicyHandler($nonceGenerator));
|
||||
}
|
||||
|
||||
return new ProfilerController($urlGenerator, $profiler, $twig, []);
|
||||
return new ProfilerController($urlGenerator, $profiler, $twig, $templates);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,8 @@ use Symfony\Bundle\WebProfilerBundle\Profiler\TemplateManager;
|
||||
use Symfony\Bundle\WebProfilerBundle\Tests\TestCase;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
use Twig\Environment;
|
||||
use Twig\Source;
|
||||
use Twig\Loader\LoaderInterface;
|
||||
use Twig\Loader\SourceContextLoaderInterface;
|
||||
|
||||
/**
|
||||
* Test for TemplateManager class.
|
||||
|
@ -380,7 +380,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterfac
|
||||
continue;
|
||||
}
|
||||
$version -= $this->knownTagVersions[$tag][1];
|
||||
if ((0 !== $version && 1 !== $version) || $this->knownTagVersionsTtl > $now - $this->knownTagVersions[$tag][0]) {
|
||||
if ((0 !== $version && 1 !== $version) || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) {
|
||||
// reuse previously fetched tag versions up to the ttl, unless we are storing items or a potential miss arises
|
||||
$fetchTagVersions = true;
|
||||
} else {
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\Cache\Tests\Adapter;
|
||||
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
@ -67,6 +68,42 @@ class TagAwareAdapterTest extends AdapterTestCase
|
||||
$this->assertFalse($cache->prune());
|
||||
}
|
||||
|
||||
public function testKnownTagVersionsTtl()
|
||||
{
|
||||
$itemsPool = new FilesystemAdapter('', 10);
|
||||
$tagsPool = $this
|
||||
->getMockBuilder(AdapterInterface::class)
|
||||
->getMock();
|
||||
|
||||
$pool = new TagAwareAdapter($itemsPool, $tagsPool, 10);
|
||||
|
||||
$item = $pool->getItem('foo');
|
||||
$item->tag(['baz']);
|
||||
$item->expiresAfter(100);
|
||||
|
||||
$tag = $this->getMockBuilder(CacheItemInterface::class)->getMock();
|
||||
$tag->expects(self::exactly(2))->method('get')->willReturn(10);
|
||||
|
||||
$tagsPool->expects(self::exactly(2))->method('getItems')->willReturn([
|
||||
'baz'.TagAwareAdapter::TAGS_PREFIX => $tag,
|
||||
]);
|
||||
|
||||
$pool->save($item);
|
||||
$this->assertTrue($pool->getItem('foo')->isHit());
|
||||
$this->assertTrue($pool->getItem('foo')->isHit());
|
||||
|
||||
sleep(20);
|
||||
|
||||
$this->assertTrue($pool->getItem('foo')->isHit());
|
||||
|
||||
sleep(5);
|
||||
|
||||
$this->assertTrue($pool->getItem('foo')->isHit());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MockObject|PruneableCacheInterface
|
||||
*/
|
||||
private function getPruneableMock(): AdapterInterface
|
||||
{
|
||||
$pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]);
|
||||
|
@ -271,7 +271,7 @@ trait RedisTrait
|
||||
if (null !== $auth) {
|
||||
$params['parameters']['password'] = $auth;
|
||||
}
|
||||
if (1 === \count($hosts) && !$params['redis_cluster']) {
|
||||
if (1 === \count($hosts) && !($params['redis_cluster'] || $params['redis_sentinel'])) {
|
||||
$hosts = $hosts[0];
|
||||
} elseif (\in_array($params['failover'], ['slaves', 'distribute'], true) && !isset($params['replication'])) {
|
||||
$params['replication'] = true;
|
||||
|
@ -23,6 +23,7 @@ CHANGELOG
|
||||
* `Application` implements `ResetInterface`
|
||||
* marked all dispatched event classes as `@final`
|
||||
* added support for displaying table horizontally
|
||||
* deprecated returning `null` from `Command::execute()`, return `0` instead
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
@ -150,7 +150,7 @@ class Command
|
||||
* execute() method, you set the code to execute by passing
|
||||
* a Closure to the setCode() method.
|
||||
*
|
||||
* @return int|void void or 0 if everything went fine, or an exit code
|
||||
* @return int 0 if everything went fine, or an exit code
|
||||
*
|
||||
* @throws LogicException When this abstract method is not implemented
|
||||
*
|
||||
@ -253,6 +253,10 @@ class Command
|
||||
$statusCode = ($this->code)($input, $output);
|
||||
} else {
|
||||
$statusCode = $this->execute($input, $output);
|
||||
|
||||
if (!\is_int($statusCode)) {
|
||||
@trigger_error(sprintf('A non numeric or nullable $statusCode returned by Command::execute() is deprecated since Symfony 4.4, return an integer value instead.'), E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
|
||||
return is_numeric($statusCode) ? (int) $statusCode : 0;
|
||||
|
@ -77,5 +77,7 @@ EOF
|
||||
]);
|
||||
|
||||
$this->command = null;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,8 @@ EOF
|
||||
'raw_text' => $input->getOption('raw'),
|
||||
'namespace' => $input->getArgument('namespace'),
|
||||
]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function createDefinition(): InputDefinition
|
||||
|
@ -728,6 +728,30 @@ class ApplicationTest extends TestCase
|
||||
$this->assertInstanceOf('FooHiddenCommand', $application->find('afoohidden'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
* @expectedDeprecation Command "%s:hidden" is hidden, finding it using an abbreviation is deprecated since Symfony 4.4, use its full name instead.
|
||||
* @dataProvider provideAbbreviationsForHiddenCommands
|
||||
*/
|
||||
public function testFindHiddenWithAbbreviatedName($name)
|
||||
{
|
||||
$application = new Application();
|
||||
|
||||
$application->add(new \FooHiddenCommand());
|
||||
$application->add(new \BarHiddenCommand());
|
||||
|
||||
$application->find($name);
|
||||
}
|
||||
|
||||
public function provideAbbreviationsForHiddenCommands()
|
||||
{
|
||||
return [
|
||||
['foo:hidde'],
|
||||
['afoohidd'],
|
||||
['bar:hidde'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testFindAmbiguousCommandsIfAllAlternativesAreHidden()
|
||||
{
|
||||
$application = new Application();
|
||||
@ -1823,9 +1847,11 @@ class CustomDefaultCommandApplication extends Application
|
||||
|
||||
class LazyCommand extends Command
|
||||
{
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
public function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('lazy-command called');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,20 +293,6 @@ class CommandTest extends TestCase
|
||||
$tester->execute(['--bar' => true]);
|
||||
}
|
||||
|
||||
public function testRunReturnsIntegerExitCode()
|
||||
{
|
||||
$command = new \TestCommand();
|
||||
$exitCode = $command->run(new StringInput(''), new NullOutput());
|
||||
$this->assertSame(0, $exitCode, '->run() returns integer exit code (treats null as 0)');
|
||||
|
||||
$command = $this->getMockBuilder('TestCommand')->setMethods(['execute'])->getMock();
|
||||
$command->expects($this->once())
|
||||
->method('execute')
|
||||
->willReturn('2.3');
|
||||
$exitCode = $command->run(new StringInput(''), new NullOutput());
|
||||
$this->assertSame(2, $exitCode, '->run() returns integer exit code (casts numeric to int)');
|
||||
}
|
||||
|
||||
public function testRunWithApplication()
|
||||
{
|
||||
$command = new \TestCommand();
|
||||
|
@ -15,7 +15,8 @@ class BarHiddenCommand extends Command
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,11 @@ class Foo1Command extends Command
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,8 @@ class Foo2Command extends Command
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class Foo3Command extends Command
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
try {
|
||||
try {
|
||||
@ -25,5 +25,7 @@ class Foo3Command extends Command
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception('Third exception <fg=blue;bg=red>comment</>', 404, $e);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,13 @@ class FooCommand extends Command
|
||||
$output->writeln('interact called');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
|
||||
$output->writeln('called');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,8 @@ class FooHiddenCommand extends Command
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -25,12 +25,14 @@ class FooOptCommand extends Command
|
||||
$output->writeln('interact called');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
|
||||
$output->writeln('called');
|
||||
$output->writeln($this->input->getOption('fooopt'));
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,11 @@ class FooSubnamespaced1Command extends Command
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,11 @@ class FooSubnamespaced2Command extends Command
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,10 @@ class FooWithoutAliasCommand extends Command
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('called');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,11 @@ class FoobarCommand extends Command
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,10 @@ class TestAmbiguousCommandRegistering extends Command
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->write('test-ambiguous');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,10 @@ class TestAmbiguousCommandRegistering2 extends Command
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->write('test-ambiguous2');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ class TestCommand extends Command
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$output->writeln('execute called');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function interact(InputInterface $input, OutputInterface $output)
|
||||
|
@ -98,7 +98,7 @@ class TranslatorTest extends TestCase
|
||||
$elements = $document->xpath($translator->cssToXPath($css));
|
||||
$this->assertCount(\count($elementsId), $elements);
|
||||
foreach ($elements as $element) {
|
||||
$this->assertTrue(\in_array($element->attributes()->id, $elementsId));
|
||||
$this->assertContains((string) $element->attributes()->id, $elementsId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ class TranslatorTest extends TestCase
|
||||
$this->assertCount(\count($elementsId), $elementsId);
|
||||
foreach ($elements as $element) {
|
||||
if (null !== $element->attributes()->id) {
|
||||
$this->assertTrue(\in_array($element->attributes()->id, $elementsId));
|
||||
$this->assertContains((string) $element->attributes()->id, $elementsId);
|
||||
}
|
||||
}
|
||||
libxml_clear_errors();
|
||||
@ -137,6 +137,33 @@ class TranslatorTest extends TestCase
|
||||
$this->assertCount($count, $elements);
|
||||
}
|
||||
|
||||
public function testOnlyOfTypeFindsSingleChildrenOfGivenType()
|
||||
{
|
||||
$translator = new Translator();
|
||||
$translator->registerExtension(new HtmlExtension($translator));
|
||||
$document = new \DOMDocument();
|
||||
$document->loadHTML(<<<'HTML'
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
<span>A</span>
|
||||
</p>
|
||||
<p>
|
||||
<span>B</span>
|
||||
<span>C</span>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
HTML
|
||||
);
|
||||
|
||||
$xpath = new \DOMXPath($document);
|
||||
$nodeList = $xpath->query($translator->cssToXPath('span:only-of-type'));
|
||||
|
||||
$this->assertSame(1, $nodeList->length);
|
||||
$this->assertSame('A', $nodeList->item(0)->textContent);
|
||||
}
|
||||
|
||||
public function getXpathLiteralTestData()
|
||||
{
|
||||
return [
|
||||
@ -175,7 +202,7 @@ class TranslatorTest extends TestCase
|
||||
['e:first-of-type', '*/e[position() = 1]'],
|
||||
['e:last-of-type', '*/e[position() = last()]'],
|
||||
['e:only-child', "*/*[(name() = 'e') and (last() = 1)]"],
|
||||
['e:only-of-type', 'e[last() = 1]'],
|
||||
['e:only-of-type', 'e[count(preceding-sibling::e)=0 and count(following-sibling::e)=0]'],
|
||||
['e:empty', 'e[not(*) and not(string-length())]'],
|
||||
['e:EmPTY', 'e[not(*) and not(string-length())]'],
|
||||
['e:root', 'e[not(parent::*)]'],
|
||||
|
@ -105,11 +105,13 @@ class PseudoClassExtension extends AbstractExtension
|
||||
*/
|
||||
public function translateOnlyOfType(XPathExpr $xpath): XPathExpr
|
||||
{
|
||||
if ('*' === $xpath->getElement()) {
|
||||
$element = $xpath->getElement();
|
||||
|
||||
if ('*' === $element) {
|
||||
throw new ExpressionErrorException('"*:only-of-type" is not implemented.');
|
||||
}
|
||||
|
||||
return $xpath->addCondition('last() = 1');
|
||||
return $xpath->addCondition(sprintf('count(preceding-sibling::%s)=0 and count(following-sibling::%s)=0', $element, $element));
|
||||
}
|
||||
|
||||
public function translateEmpty(XPathExpr $xpath): XPathExpr
|
||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\EnvParameterException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\Loader\FileLoader;
|
||||
|
||||
/**
|
||||
* This pass validates each definition individually only taking the information
|
||||
@ -43,7 +44,7 @@ class CheckDefinitionValidityPass implements CompilerPassInterface
|
||||
}
|
||||
|
||||
// non-synthetic, non-abstract service has class
|
||||
if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass()) {
|
||||
if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass() && (!$definition->getFactory() || !preg_match(FileLoader::ANONYMOUS_ID_REGEXP, $id))) {
|
||||
if ($definition->getFactory()) {
|
||||
throw new RuntimeException(sprintf('Please add the class to service "%s" even if it is constructed by a factory since we might need to add method calls based on compile-time checks.', $id));
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceExce
|
||||
use Symfony\Component\DependencyInjection\ExpressionLanguage;
|
||||
use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper;
|
||||
use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper;
|
||||
use Symfony\Component\DependencyInjection\Loader\FileLoader;
|
||||
use Symfony\Component\DependencyInjection\Parameter;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator as BaseServiceLocator;
|
||||
@ -1210,7 +1211,7 @@ EOF;
|
||||
$ids = array_keys($ids);
|
||||
sort($ids);
|
||||
foreach ($ids as $id) {
|
||||
if (preg_match('/^\.\d+_[^~]++~[._a-zA-Z\d]{7}$/', $id)) {
|
||||
if (preg_match(FileLoader::ANONYMOUS_ID_REGEXP, $id)) {
|
||||
continue;
|
||||
}
|
||||
$code .= ' '.$this->doExport($id)." => true,\n";
|
||||
|
@ -236,7 +236,7 @@ class EnvVarProcessor implements EnvVarProcessorInterface
|
||||
}
|
||||
|
||||
if ('csv' === $prefix) {
|
||||
return str_getcsv($env);
|
||||
return str_getcsv($env, ',', '"', \PHP_VERSION_ID >= 70400 ? '' : '\\');
|
||||
}
|
||||
|
||||
if ('trim' === $prefix) {
|
||||
|
@ -26,6 +26,8 @@ use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
*/
|
||||
abstract class FileLoader extends BaseFileLoader
|
||||
{
|
||||
public const ANONYMOUS_ID_REGEXP = '/^\.\d+_[^~]++~[._a-zA-Z\d]{7}$/';
|
||||
|
||||
protected $container;
|
||||
protected $isLoadingInstanceof = false;
|
||||
protected $instanceof = [];
|
||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CheckDefinitionValidityPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
|
||||
class CheckDefinitionValidityPassTest extends TestCase
|
||||
{
|
||||
@ -35,6 +36,19 @@ class CheckDefinitionValidityPassTest extends TestCase
|
||||
$this->process($container);
|
||||
}
|
||||
|
||||
public function testProcessDetectsFactoryWithoutClass()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container->register('.123_anonymous_service_id_should_not_throw_~1234567')->setFactory('factory');
|
||||
$this->process($container);
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
$container->register('.any_non_anonymous_id_throws')->setFactory('factory');
|
||||
|
||||
$this->process($container);
|
||||
}
|
||||
|
||||
public function testProcess()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
@ -478,4 +478,37 @@ class EnvVarProcessorTest extends TestCase
|
||||
|
||||
$this->assertEquals('foo', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider validCsv
|
||||
*/
|
||||
public function testGetEnvCsv($value, $processed)
|
||||
{
|
||||
$processor = new EnvVarProcessor(new Container());
|
||||
|
||||
$result = $processor->getEnv('csv', 'foo', function ($name) use ($value) {
|
||||
$this->assertSame('foo', $name);
|
||||
|
||||
return $value;
|
||||
});
|
||||
|
||||
$this->assertSame($processed, $result);
|
||||
}
|
||||
|
||||
public function validCsv()
|
||||
{
|
||||
$complex = <<<'CSV'
|
||||
,"""","foo""","\""",\,foo\
|
||||
CSV;
|
||||
|
||||
return [
|
||||
['', [null]],
|
||||
[',', ['', '']],
|
||||
['1', ['1']],
|
||||
['1,2," 3 "', ['1', '2', ' 3 ']],
|
||||
['\\,\\\\', ['\\', '\\\\']],
|
||||
[$complex, \PHP_VERSION_ID >= 70400 ? ['', '"', 'foo"', '\\"', '\\', 'foo\\'] : ['', '"', 'foo"', '\\"",\\,foo\\']],
|
||||
[null, null],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" ?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<services>
|
||||
<service id="foo">
|
||||
<call method="bar" returns-clone="true" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
@ -0,0 +1,5 @@
|
||||
services:
|
||||
foo:
|
||||
calls:
|
||||
- {method: bar, arguments: [1], returns_clone: true}
|
||||
- [bar, [2], true]
|
@ -904,6 +904,15 @@ class XmlFileLoaderTest extends TestCase
|
||||
$this->assertSame('overridden', $container->get('bar')->quz);
|
||||
}
|
||||
|
||||
public function testReturnsClone()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
|
||||
$loader->load('returns_clone.xml');
|
||||
|
||||
$this->assertSame([['bar', [], true]], $container->getDefinition('foo')->getMethodCalls());
|
||||
}
|
||||
|
||||
public function testSinglyImplementedInterfacesInMultipleResources()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
@ -810,6 +810,19 @@ class YamlFileLoaderTest extends TestCase
|
||||
$this->assertNull($iteratorArgument->getIndexAttribute());
|
||||
}
|
||||
|
||||
public function testReturnsClone()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
|
||||
$loader->load('returns_clone.yaml');
|
||||
|
||||
$expected = [
|
||||
['bar', [1], true],
|
||||
['bar', [2], true],
|
||||
];
|
||||
$this->assertSame($expected, $container->getDefinition('foo')->getMethodCalls());
|
||||
}
|
||||
|
||||
public function testSinglyImplementedInterfacesInMultipleResources()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
@ -95,6 +95,8 @@ EOF
|
||||
$io->newLine();
|
||||
$io->table(['Format', 'Class'], $tableRows);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function formatClassLink(string $class): string
|
||||
|
@ -16,6 +16,7 @@ use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class RegisterListenersPassTest extends TestCase
|
||||
{
|
||||
@ -37,10 +38,6 @@ class RegisterListenersPassTest extends TestCase
|
||||
|
||||
public function testValidEventSubscriber()
|
||||
{
|
||||
$services = [
|
||||
'my_event_subscriber' => [0 => []],
|
||||
];
|
||||
|
||||
$builder = new ContainerBuilder();
|
||||
$eventDispatcherDefinition = $builder->register('event_dispatcher');
|
||||
$builder->register('my_event_subscriber', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService')
|
||||
@ -62,6 +59,30 @@ class RegisterListenersPassTest extends TestCase
|
||||
$this->assertEquals($expectedCalls, $eventDispatcherDefinition->getMethodCalls());
|
||||
}
|
||||
|
||||
public function testAliasedEventSubscriber(): void
|
||||
{
|
||||
$builder = new ContainerBuilder();
|
||||
$builder->setParameter('event_dispatcher.event_aliases', [AliasedEvent::class => 'aliased_event']);
|
||||
$builder->register('event_dispatcher');
|
||||
$builder->register('my_event_subscriber', AliasedSubscriber::class)
|
||||
->addTag('kernel.event_subscriber');
|
||||
|
||||
$registerListenersPass = new RegisterListenersPass();
|
||||
$registerListenersPass->process($builder);
|
||||
|
||||
$expectedCalls = [
|
||||
[
|
||||
'addListener',
|
||||
[
|
||||
'aliased_event',
|
||||
[new ServiceClosureArgument(new Reference('my_event_subscriber')), 'onAliasedEvent'],
|
||||
0,
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expectedCalls, $builder->getDefinition('event_dispatcher')->getMethodCalls());
|
||||
}
|
||||
|
||||
public function testAbstractEventListener()
|
||||
{
|
||||
$this->expectException('InvalidArgumentException');
|
||||
@ -175,9 +196,33 @@ class RegisterListenersPassTest extends TestCase
|
||||
];
|
||||
$this->assertEquals($expectedCalls, $definition->getMethodCalls());
|
||||
}
|
||||
|
||||
public function testAliasedEventListener(): void
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->setParameter('event_dispatcher.event_aliases', [AliasedEvent::class => 'aliased_event']);
|
||||
$container->register('foo', InvokableListenerService::class)->addTag('kernel.event_listener', ['event' => AliasedEvent::class, 'method' => 'onEvent']);
|
||||
$container->register('event_dispatcher');
|
||||
|
||||
$registerListenersPass = new RegisterListenersPass();
|
||||
$registerListenersPass->process($container);
|
||||
|
||||
$definition = $container->getDefinition('event_dispatcher');
|
||||
$expectedCalls = [
|
||||
[
|
||||
'addListener',
|
||||
[
|
||||
'aliased_event',
|
||||
[new ServiceClosureArgument(new Reference('foo')), 'onEvent'],
|
||||
0,
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expectedCalls, $definition->getMethodCalls());
|
||||
}
|
||||
}
|
||||
|
||||
class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
|
||||
class SubscriberService implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
@ -197,3 +242,17 @@ class InvokableListenerService
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
final class AliasedSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
AliasedEvent::class => 'onAliasedEvent',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
final class AliasedEvent
|
||||
{
|
||||
}
|
||||
|
@ -152,6 +152,8 @@ EOF
|
||||
$options['format'] = $input->getOption('format');
|
||||
$options['show_deprecated'] = $input->getOption('show-deprecated');
|
||||
$helper->describe($io, $object, $options);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function getFqcnTypeClass(InputInterface $input, SymfonyStyle $io, string $shortClassName)
|
||||
|
@ -18,6 +18,7 @@ use Symfony\Component\HttpKernel\UriSigner;
|
||||
use Twig\Environment;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Loader\ExistsLoaderInterface;
|
||||
use Twig\Loader\SourceContextLoaderInterface;
|
||||
|
||||
/**
|
||||
* Implements the Hinclude rendering strategy.
|
||||
@ -76,7 +77,7 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
|
||||
$uri = str_replace('&', '&', $uri);
|
||||
|
||||
$template = isset($options['default']) ? $options['default'] : $this->globalDefaultTemplate;
|
||||
if (null !== $this->twig && $template && $this->templateExists($template)) {
|
||||
if (null !== $this->twig && $template && $this->twig->getLoader()->exists($template)) {
|
||||
$content = $this->twig->render($template);
|
||||
} else {
|
||||
$content = $template;
|
||||
@ -101,27 +102,6 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
|
||||
return new Response(sprintf('<hx:include src="%s"%s>%s</hx:include>', $uri, $renderedAttributes, $content));
|
||||
}
|
||||
|
||||
private function templateExists(string $template): bool
|
||||
{
|
||||
$loader = $this->twig->getLoader();
|
||||
if ($loader instanceof ExistsLoaderInterface || method_exists($loader, 'exists')) {
|
||||
return $loader->exists($template);
|
||||
}
|
||||
|
||||
try {
|
||||
if (method_exists($loader, 'getSourceContext')) {
|
||||
$loader->getSourceContext($template);
|
||||
} else {
|
||||
$loader->getSource($template);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (LoaderError $e) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -424,25 +424,72 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
|
||||
$class = $this->getContainerClass();
|
||||
$cacheDir = $this->warmupDir ?: $this->getCacheDir();
|
||||
$cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug);
|
||||
$oldContainer = null;
|
||||
if ($fresh = $cache->isFresh()) {
|
||||
// Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors
|
||||
$errorLevel = error_reporting(\E_ALL ^ \E_WARNING);
|
||||
$fresh = $oldContainer = false;
|
||||
try {
|
||||
if (file_exists($cache->getPath()) && \is_object($this->container = include $cache->getPath())) {
|
||||
$this->container->set('kernel', $this);
|
||||
$oldContainer = $this->container;
|
||||
$fresh = true;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} finally {
|
||||
$cachePath = $cache->getPath();
|
||||
|
||||
// Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors
|
||||
$errorLevel = error_reporting(\E_ALL ^ \E_WARNING);
|
||||
|
||||
try {
|
||||
if (file_exists($cachePath) && \is_object($this->container = include $cachePath) && (!$this->debug || $cache->isFresh())) {
|
||||
$this->container->set('kernel', $this);
|
||||
error_reporting($errorLevel);
|
||||
|
||||
return;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
}
|
||||
|
||||
if ($fresh) {
|
||||
return;
|
||||
$oldContainer = \is_object($this->container) ? new \ReflectionClass($this->container) : $this->container = null;
|
||||
|
||||
try {
|
||||
is_dir($cacheDir) ?: mkdir($cacheDir, 0777, true);
|
||||
|
||||
if ($lock = fopen($cachePath, 'w')) {
|
||||
chmod($cachePath, 0666 & ~umask());
|
||||
flock($lock, LOCK_EX | LOCK_NB, $wouldBlock);
|
||||
|
||||
if (!flock($lock, $wouldBlock ? LOCK_SH : LOCK_EX)) {
|
||||
fclose($lock);
|
||||
} else {
|
||||
$cache = new class($cachePath, $this->debug) extends ConfigCache {
|
||||
public $lock;
|
||||
|
||||
public function write($content, array $metadata = null)
|
||||
{
|
||||
rewind($this->lock);
|
||||
ftruncate($this->lock, 0);
|
||||
fwrite($this->lock, $content);
|
||||
|
||||
if (null !== $metadata) {
|
||||
file_put_contents($this->getPath().'.meta', serialize($metadata));
|
||||
@chmod($this->getPath().'.meta', 0666 & ~umask());
|
||||
}
|
||||
|
||||
if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) {
|
||||
opcache_invalidate($this->getPath(), true);
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
flock($this->lock, LOCK_UN);
|
||||
fclose($this->lock);
|
||||
}
|
||||
};
|
||||
$cache->lock = $lock;
|
||||
|
||||
if (!\is_object($this->container = include $cachePath)) {
|
||||
$this->container = null;
|
||||
} elseif (!$oldContainer || \get_class($this->container) !== $oldContainer->name) {
|
||||
$this->container->set('kernel', $this);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
} finally {
|
||||
error_reporting($errorLevel);
|
||||
}
|
||||
|
||||
if ($collectDeprecations = $this->debug && !\defined('PHPUNIT_COMPOSER_INSTALL')) {
|
||||
@ -500,19 +547,9 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $oldContainer && file_exists($cache->getPath())) {
|
||||
$errorLevel = error_reporting(\E_ALL ^ \E_WARNING);
|
||||
try {
|
||||
$oldContainer = include $cache->getPath();
|
||||
} catch (\Throwable $e) {
|
||||
} finally {
|
||||
error_reporting($errorLevel);
|
||||
}
|
||||
}
|
||||
$oldContainer = \is_object($oldContainer) ? new \ReflectionClass($oldContainer) : false;
|
||||
|
||||
$this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());
|
||||
$this->container = require $cache->getPath();
|
||||
unset($cache);
|
||||
$this->container = require $cachePath;
|
||||
$this->container->set('kernel', $this);
|
||||
|
||||
if ($oldContainer && \get_class($this->container) !== $oldContainer->name) {
|
||||
|
@ -35,7 +35,7 @@ class RoundRobinTransport implements TransportInterface
|
||||
public function __construct(array $transports, int $retryPeriod = 60)
|
||||
{
|
||||
if (!$transports) {
|
||||
throw new TransportException(__CLASS__.' must have at least one transport configured.');
|
||||
throw new TransportException(sprintf('"%s" must have at least one transport configured.', static::class));
|
||||
}
|
||||
|
||||
$this->transports = $transports;
|
||||
@ -58,9 +58,7 @@ class RoundRobinTransport implements TransportInterface
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->getNameSymbol().'('.implode(' ', array_map(function (TransportInterface $transport) {
|
||||
return (string) $transport;
|
||||
}, $this->transports)).')';
|
||||
return $this->getNameSymbol().'('.implode(' ', array_map('strval', $this->transports)).')';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,7 +21,7 @@ use Symfony\Component\Mime\RawMessage;
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Transports implements TransportInterface
|
||||
final class Transports implements TransportInterface
|
||||
{
|
||||
private $transports;
|
||||
private $default;
|
||||
@ -64,6 +64,6 @@ class Transports implements TransportInterface
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return 'all';
|
||||
return '['.implode(',', array_keys($this->transports)).']';
|
||||
}
|
||||
}
|
||||
|
@ -205,6 +205,8 @@ EOF
|
||||
$worker->run([
|
||||
'sleep' => $input->getOption('sleep') * 1000000,
|
||||
]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function convertToBytes(string $memoryLimit): int
|
||||
|
@ -96,6 +96,8 @@ EOF
|
||||
$io->warning(sprintf('No handled message found in bus "%s".', $bus));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function formatConditions(array $options): string
|
||||
|
@ -61,6 +61,8 @@ EOF
|
||||
|
||||
$shouldForce = $input->getOption('force');
|
||||
$this->removeSingleMessage($input->getArgument('id'), $receiver, $io, $shouldForce);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function removeSingleMessage(string $id, ReceiverInterface $receiver, SymfonyStyle $io, bool $shouldForce)
|
||||
|
@ -110,11 +110,13 @@ EOF
|
||||
|
||||
$this->runInteractive($io, $shouldForce);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
$this->retrySpecificIds($ids, $io, $shouldForce);
|
||||
$io->success('All done!');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function runInteractive(SymfonyStyle $io, bool $shouldForce)
|
||||
|
@ -71,6 +71,8 @@ EOF
|
||||
} else {
|
||||
$this->showMessage($id, $io);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function listMessages(SymfonyStyle $io, int $max)
|
||||
|
@ -76,5 +76,7 @@ EOF
|
||||
$io->note(sprintf('The "%s" transport does not support setup.', $transportName));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -68,5 +68,7 @@ EOF
|
||||
$this->restartSignalCachePool->save($cacheItem);
|
||||
|
||||
$io->success('Signal successfully sent to stop any running workers.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ class X509AuthenticationListener extends AbstractPreAuthenticatedListener
|
||||
$user = $request->server->get($this->userKey);
|
||||
} elseif (
|
||||
$request->server->has($this->credentialKey)
|
||||
&& preg_match('#emailAddress=(.+\@.+\.[^,/]+)($|,|/)#', $request->server->get($this->credentialKey), $matches)
|
||||
&& preg_match('#emailAddress=([^,/@]++@[^,/]++)#', $request->server->get($this->credentialKey), $matches)
|
||||
) {
|
||||
$user = $matches[1];
|
||||
}
|
||||
|
@ -81,6 +81,7 @@ class X509AuthenticationListenerTest extends TestCase
|
||||
yield ['cert+something@example.com', 'CN=Sample certificate DN,emailAddress=cert+something@example.com'];
|
||||
yield ['cert+something@example.com', 'emailAddress=cert+something@example.com,CN=Sample certificate DN'];
|
||||
yield ['cert+something@example.com', 'emailAddress=cert+something@example.com'];
|
||||
yield ['firstname.lastname@mycompany.co.uk', 'emailAddress=firstname.lastname@mycompany.co.uk,CN=Firstname.Lastname,OU=london,OU=company design and engineering,OU=Issuer London,OU=Roaming,OU=Interactive,OU=Users,OU=Standard,OU=Business,DC=england,DC=core,DC=company,DC=co,DC=uk'];
|
||||
}
|
||||
|
||||
public function testGetPreAuthenticatedDataNoData()
|
||||
|
@ -19,6 +19,11 @@ CHANGELOG
|
||||
* removed support for instantiating a `DataUriNormalizer` with a default MIME type guesser when the `symfony/mime` component isn't installed.
|
||||
* removed the `XmlEncoder::TYPE_CASE_ATTRIBUTES` constant. Use `XmlEncoder::TYPE_CAST_ATTRIBUTES` instead.
|
||||
|
||||
4.4.0
|
||||
-----
|
||||
|
||||
* deprecated the `XmlEncoder::TYPE_CASE_ATTRIBUTES` constant, use `XmlEncoder::TYPE_CAST_ATTRIBUTES` instead
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
|
@ -36,7 +36,7 @@ class CsvEncoder implements EncoderInterface, DecoderInterface
|
||||
self::AS_COLLECTION_KEY => true,
|
||||
self::DELIMITER_KEY => ',',
|
||||
self::ENCLOSURE_KEY => '"',
|
||||
self::ESCAPE_CHAR_KEY => '\\',
|
||||
self::ESCAPE_CHAR_KEY => '',
|
||||
self::ESCAPE_FORMULAS_KEY => false,
|
||||
self::HEADERS_KEY => [],
|
||||
self::KEY_SEPARATOR_KEY => '.',
|
||||
@ -46,6 +46,10 @@ class CsvEncoder implements EncoderInterface, DecoderInterface
|
||||
public function __construct(array $defaultContext = [])
|
||||
{
|
||||
$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
|
||||
|
||||
if (\PHP_VERSION_ID < 70400 && '' === $this->defaultContext[self::ESCAPE_CHAR_KEY]) {
|
||||
$this->defaultContext[self::ESCAPE_CHAR_KEY] = '\\';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -370,16 +370,18 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($type->isCollection() && null !== ($collectionValueType = $type->getCollectionValueType()) && Type::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()) {
|
||||
$collectionValueType = $type->isCollection() ? $type->getCollectionValueType() : null;
|
||||
|
||||
// Fix a collection that contains the only one element
|
||||
// This is special to xml format only
|
||||
if ('xml' === $format && null !== $collectionValueType && (!\is_array($data) || !\is_int(key($data)))) {
|
||||
$data = [$data];
|
||||
}
|
||||
|
||||
if (null !== $collectionValueType && Type::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()) {
|
||||
$builtinType = Type::BUILTIN_TYPE_OBJECT;
|
||||
$class = $collectionValueType->getClassName().'[]';
|
||||
|
||||
// Fix a collection that contains the only one element
|
||||
// This is special to xml format only
|
||||
if ('xml' === $format && !\is_int(key($data))) {
|
||||
$data = [$data];
|
||||
}
|
||||
|
||||
if (null !== $collectionKeyType = $type->getCollectionKeyType()) {
|
||||
$context['key_type'] = $collectionKeyType;
|
||||
}
|
||||
|
@ -36,15 +36,51 @@ class CsvEncoderTest extends TestCase
|
||||
'int' => 2,
|
||||
'false' => false,
|
||||
'true' => true,
|
||||
'int_one' => 1,
|
||||
'string_one' => '1',
|
||||
];
|
||||
|
||||
// Check that true and false are appropriately handled
|
||||
$this->assertEquals(<<<'CSV'
|
||||
string,int,false,true
|
||||
foo,2,0,1
|
||||
$this->assertSame($csv = <<<'CSV'
|
||||
string,int,false,true,int_one,string_one
|
||||
foo,2,0,1,1,1
|
||||
|
||||
CSV
|
||||
, $this->encoder->encode($data, 'csv'));
|
||||
, $this->encoder->encode($data, 'csv'));
|
||||
|
||||
$this->assertSame([
|
||||
'string' => 'foo',
|
||||
'int' => '2',
|
||||
'false' => '0',
|
||||
'true' => '1',
|
||||
'int_one' => '1',
|
||||
'string_one' => '1',
|
||||
], $this->encoder->decode($csv, 'csv', [CsvEncoder::AS_COLLECTION_KEY => false]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 7.4
|
||||
*/
|
||||
public function testDoubleQuotesAndSlashes()
|
||||
{
|
||||
$this->assertSame($csv = <<<'CSV'
|
||||
0,1,2,3,4,5
|
||||
,"""","foo""","\""",\,foo\
|
||||
|
||||
CSV
|
||||
, $this->encoder->encode($data = ['', '"', 'foo"', '\\"', '\\', 'foo\\'], 'csv'));
|
||||
|
||||
$this->assertSame($data, $this->encoder->decode($csv, 'csv'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 7.4
|
||||
*/
|
||||
public function testSingleSlash()
|
||||
{
|
||||
$this->assertSame($csv = "0\n\\\n", $this->encoder->encode($data = ['\\'], 'csv'));
|
||||
$this->assertSame($data, $this->encoder->decode($csv, 'csv'));
|
||||
$this->assertSame($data, $this->encoder->decode(trim($csv), 'csv'));
|
||||
}
|
||||
|
||||
public function testSupportEncoding()
|
||||
|
@ -132,16 +132,54 @@ class AbstractObjectNormalizerTest extends TestCase
|
||||
$extractor = $this->getMockBuilder(PhpDocExtractor::class)->getMock();
|
||||
$extractor->method('getTypes')
|
||||
->will($this->onConsecutiveCalls(
|
||||
[
|
||||
new Type(
|
||||
'array',
|
||||
false,
|
||||
null,
|
||||
true,
|
||||
new Type('int'),
|
||||
new Type('object', false, DummyChild::class)
|
||||
),
|
||||
],
|
||||
[new Type('array', false, null, true, new Type('int'), new Type('object', false, DummyChild::class))],
|
||||
null
|
||||
));
|
||||
|
||||
$denormalizer = new AbstractObjectNormalizerCollectionDummy(null, null, $extractor);
|
||||
$arrayDenormalizer = new ArrayDenormalizerDummy();
|
||||
$serializer = new SerializerCollectionDummy([$arrayDenormalizer, $denormalizer]);
|
||||
$arrayDenormalizer->setSerializer($serializer);
|
||||
$denormalizer->setSerializer($serializer);
|
||||
|
||||
return $denormalizer;
|
||||
}
|
||||
|
||||
public function testDenormalizeStringCollectionDecodedFromXmlWithOneChild()
|
||||
{
|
||||
$denormalizer = $this->getDenormalizerForStringCollection();
|
||||
|
||||
// if an xml-node can have children which should be deserialized as string[]
|
||||
// and only one child exists
|
||||
$stringCollection = $denormalizer->denormalize(['children' => 'foo'], StringCollection::class, 'xml');
|
||||
|
||||
$this->assertInstanceOf(StringCollection::class, $stringCollection);
|
||||
$this->assertIsArray($stringCollection->children);
|
||||
$this->assertCount(1, $stringCollection->children);
|
||||
$this->assertEquals('foo', $stringCollection->children[0]);
|
||||
}
|
||||
|
||||
public function testDenormalizeStringCollectionDecodedFromXmlWithTwoChildren()
|
||||
{
|
||||
$denormalizer = $this->getDenormalizerForStringCollection();
|
||||
|
||||
// if an xml-node can have children which should be deserialized as string[]
|
||||
// and only one child exists
|
||||
$stringCollection = $denormalizer->denormalize(['children' => ['foo', 'bar']], StringCollection::class, 'xml');
|
||||
|
||||
$this->assertInstanceOf(StringCollection::class, $stringCollection);
|
||||
$this->assertIsArray($stringCollection->children);
|
||||
$this->assertCount(2, $stringCollection->children);
|
||||
$this->assertEquals('foo', $stringCollection->children[0]);
|
||||
$this->assertEquals('bar', $stringCollection->children[1]);
|
||||
}
|
||||
|
||||
private function getDenormalizerForStringCollection()
|
||||
{
|
||||
$extractor = $this->getMockBuilder(PhpDocExtractor::class)->getMock();
|
||||
$extractor->method('getTypes')
|
||||
->will($this->onConsecutiveCalls(
|
||||
[new Type('array', false, null, true, new Type('int'), new Type('string'))],
|
||||
null
|
||||
));
|
||||
|
||||
@ -275,6 +313,12 @@ class AbstractObjectNormalizerWithMetadata extends AbstractObjectNormalizer
|
||||
}
|
||||
}
|
||||
|
||||
class StringCollection
|
||||
{
|
||||
/** @var string[] */
|
||||
public $children;
|
||||
}
|
||||
|
||||
class DummyCollection
|
||||
{
|
||||
/** @var DummyChild[] */
|
||||
|
@ -75,7 +75,7 @@ EOF
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$format = $input->getOption('format');
|
||||
|
Reference in New Issue
Block a user