Merge branch '3.4'
* 3.4: (23 commits) [DI] Allow dumping inline services in Yaml fixed CS [2.8] Modify 2.8 upgrade doc - key option is deprecated. Fix lock failling test [Debug] Correctly detect methods not from the same vendor [HttpKernel] Deprecated commands auto-registration Fix minors in date caster [FrameworkBundle] Catch Fatal errors in commands registration [Debug] Detect internal and deprecated methods [Profiler] Make the validator toolbar item consistent with the form one [DebugBundle] Reword an outdated comment about var dumper wiring updated CHANGELOG [HttpFoundation] Remove length limit on ETag [DI] Fix some docblocks [DI] Fix some docblocks Fixed the exception page design in responsive mode [Console] Log exit codes as debug messages instead of errors Fixed UPGRADE-4.0 about Container::set Ignore memcached missing key error on dession destroy [FrameworkBundle] Allow micro kernel to subscribe events easily ...
This commit is contained in:
commit
9295a4f1eb
@ -7,6 +7,22 @@ in 3.2 minor versions.
|
||||
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
|
||||
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.2.0...v3.2.1
|
||||
|
||||
* 3.2.13 (2017-08-01)
|
||||
|
||||
* bug #22244 [Console] Fix passing options with defaultCommand (Jakub Sacha)
|
||||
* bug #23684 [Debug] Missing escape in debug output (c960657)
|
||||
* bug #23654 [DI] Fix using private services in expressions (nicolas-grekas)
|
||||
* bug #23662 [VarDumper] Adapt to php 7.2 changes (nicolas-grekas)
|
||||
* bug #23649 [Form][TwigBridge] Don't render _method in form_rest() for a child form (fmarchalemisys)
|
||||
* bug #23023 [DoctrineBridge][PropertyInfo] Added support for Doctrine Embeddables (vudaltsov)
|
||||
* bug #23619 [Validator] Fix IbanValidator for ukrainian IBANs (paroe)
|
||||
* bug #23586 Fix case sensitive sameSite cookie (mikefrancis)
|
||||
* bug #23238 [Security] ensure the 'route' index is set before attempting to use it (gsdevme)
|
||||
* bug #23330 [WebProfilerBundle] Fix full sized dump hovering in toolbar (ogizanagi)
|
||||
* bug #23580 Fix login redirect when referer contains a query string (fabpot)
|
||||
* bug #23558 [FrameworkBundle] fix ValidatorCacheWarmer: use serializing ArrayAdapter (dmaicher)
|
||||
* bug #23574 [VarDumper] Move locale sniffing to dump() time (nicolas-grekas)
|
||||
|
||||
* 3.2.12 (2017-07-17)
|
||||
|
||||
* bug #23549 [PropertyInfo] conflict for phpdocumentor/reflection-docblock 3.2 (xabbuh)
|
||||
|
@ -114,6 +114,35 @@ FrameworkBundle
|
||||
class has been deprecated and will be removed in 4.0. Use the
|
||||
`Symfony\Component\Translation\DependencyInjection\TranslatorPass` class instead.
|
||||
|
||||
HttpKernel
|
||||
----------
|
||||
|
||||
* Relying on convention-based commands discovery has been deprecated and
|
||||
won't be supported in 4.0. Use PSR-4 based service discovery instead.
|
||||
|
||||
Before:
|
||||
|
||||
```yml
|
||||
# app/config/services.yml
|
||||
services:
|
||||
# ...
|
||||
|
||||
# implicit registration of all commands in the `Command` folder
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```yml
|
||||
# app/config/services.yml
|
||||
services:
|
||||
# ...
|
||||
|
||||
# explicit commands registration
|
||||
AppBundle\Command:
|
||||
resource: '../../src/AppBundle/Command/*'
|
||||
tags: ['console.command']
|
||||
```
|
||||
|
||||
Process
|
||||
-------
|
||||
|
||||
|
@ -164,8 +164,8 @@ DependencyInjection
|
||||
|
||||
* Using unsupported options to configure service aliases raises an exception.
|
||||
|
||||
* Setting or unsetting a private service with the `Container::set()` method is
|
||||
no longer supported. Only public services can be set or unset.
|
||||
* Setting or unsetting a service with the `Container::set()` method is
|
||||
no longer supported. Only synthetic services can be set or unset.
|
||||
|
||||
* Checking the existence of a private service with the `Container::has()`
|
||||
method is no longer supported and will return `false`.
|
||||
@ -455,6 +455,32 @@ HttpFoundation
|
||||
HttpKernel
|
||||
----------
|
||||
|
||||
* Relying on convention-based commands discovery is not supported anymore.
|
||||
Use PSR-4 based service discovery instead.
|
||||
|
||||
Before:
|
||||
|
||||
```yml
|
||||
# app/config/services.yml
|
||||
services:
|
||||
# ...
|
||||
|
||||
# implicit registration of all commands in the `Command` folder
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```yml
|
||||
# app/config/services.yml
|
||||
services:
|
||||
# ...
|
||||
|
||||
# explicit commands registration
|
||||
AppBundle\Command:
|
||||
resource: '../../src/AppBundle/Command/*'
|
||||
tags: ['console.command']
|
||||
```
|
||||
|
||||
* Removed the `kernel.root_dir` parameter. Use the `kernel.project_dir` parameter
|
||||
instead.
|
||||
|
||||
|
@ -27,8 +27,10 @@ class DebugBundle extends Bundle
|
||||
$container = $this->container;
|
||||
|
||||
// This code is here to lazy load the dump stack. This default
|
||||
// configuration for CLI mode is overridden in HTTP mode on
|
||||
// 'kernel.request' event
|
||||
// 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) use ($container) {
|
||||
$dumper = $container->get('data_collector.dump');
|
||||
$cloner = $container->get('var_dumper.cloner');
|
||||
|
@ -24,6 +24,7 @@ CHANGELOG
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
* Added support for `EventSubscriberInterface` on `MicroKernelTrait`
|
||||
* Removed `doctrine/cache` from the list of required dependencies in `composer.json`
|
||||
* Deprecated `validator.mapping.cache.doctrine.apc` service
|
||||
* The `symfony/stopwatch` dependency has been removed, require it via `composer
|
||||
|
@ -11,6 +11,9 @@
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Console;
|
||||
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Debug\Exception\FatalThrowableError;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\Console\Application as BaseApplication;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
@ -30,6 +33,7 @@ class Application extends BaseApplication
|
||||
{
|
||||
private $kernel;
|
||||
private $commandsRegistered = false;
|
||||
private $registrationErrors = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -70,9 +74,25 @@ class Application extends BaseApplication
|
||||
|
||||
$this->setDispatcher($this->kernel->getContainer()->get('event_dispatcher'));
|
||||
|
||||
if ($this->registrationErrors) {
|
||||
$this->renderRegistrationErrors($input, $output);
|
||||
}
|
||||
|
||||
return parent::doRun($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($this->registrationErrors) {
|
||||
$this->renderRegistrationErrors($input, $output);
|
||||
}
|
||||
|
||||
return parent::doRunCommand($command, $input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -138,7 +158,13 @@ class Application extends BaseApplication
|
||||
|
||||
foreach ($this->kernel->getBundles() as $bundle) {
|
||||
if ($bundle instanceof Bundle) {
|
||||
$bundle->registerCommands($this);
|
||||
try {
|
||||
$bundle->registerCommands($this);
|
||||
} catch (\Exception $e) {
|
||||
$this->registrationErrors[] = $e;
|
||||
} catch (\Throwable $e) {
|
||||
$this->registrationErrors[] = new FatalThrowableError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,9 +175,30 @@ class Application extends BaseApplication
|
||||
if ($container->hasParameter('console.command.ids')) {
|
||||
foreach ($container->getParameter('console.command.ids') as $id) {
|
||||
if (false !== $id) {
|
||||
$this->add($container->get($id));
|
||||
try {
|
||||
$this->add($container->get($id));
|
||||
} catch (\Exception $e) {
|
||||
$this->registrationErrors[] = $e;
|
||||
} catch (\Throwable $e) {
|
||||
$this->registrationErrors[] = new FatalThrowableError($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function renderRegistrationErrors(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
if ($output instanceof ConsoleOutputInterface) {
|
||||
$output = $output->getErrorOutput();
|
||||
}
|
||||
|
||||
(new SymfonyStyle($input, $output))->warning('Some commands could not be registered.');
|
||||
|
||||
foreach ($this->registrationErrors as $error) {
|
||||
$this->doRenderException($error, $output);
|
||||
}
|
||||
|
||||
$this->registrationErrors = array();
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Bundle\FrameworkBundle\Kernel;
|
||||
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Routing\RouteCollectionBuilder;
|
||||
|
||||
/**
|
||||
@ -68,6 +69,13 @@ trait MicroKernelTrait
|
||||
),
|
||||
));
|
||||
|
||||
if ($this instanceof EventSubscriberInterface) {
|
||||
$container->register('kernel', static::class)
|
||||
->setSynthetic(true)
|
||||
->addTag('kernel.event_subscriber')
|
||||
;
|
||||
}
|
||||
|
||||
$this->configureContainer($container, $loader);
|
||||
|
||||
$container->addObjectResource($this);
|
||||
|
@ -15,8 +15,13 @@ use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Tester\ApplicationTester;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
class ApplicationTest extends TestCase
|
||||
{
|
||||
@ -130,6 +135,36 @@ class ApplicationTest extends TestCase
|
||||
$this->assertSame($newCommand, $application->get('example'));
|
||||
}
|
||||
|
||||
public function testRunOnlyWarnsOnUnregistrableCommand()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('event_dispatcher', EventDispatcher::class);
|
||||
$container->register(ThrowingCommand::class, ThrowingCommand::class);
|
||||
$container->setParameter('console.command.ids', array(ThrowingCommand::class => ThrowingCommand::class));
|
||||
|
||||
$kernel = $this->getMockBuilder(KernelInterface::class)->getMock();
|
||||
$kernel
|
||||
->method('getBundles')
|
||||
->willReturn(array($this->createBundleMock(
|
||||
array((new Command('fine'))->setCode(function (InputInterface $input, OutputInterface $output) { $output->write('fine'); }))
|
||||
)));
|
||||
$kernel
|
||||
->method('getContainer')
|
||||
->willReturn($container);
|
||||
|
||||
$application = new Application($kernel);
|
||||
$application->setAutoExit(false);
|
||||
|
||||
$tester = new ApplicationTester($application);
|
||||
$tester->run(array('command' => 'fine'));
|
||||
$output = $tester->getDisplay();
|
||||
|
||||
$this->assertSame(0, $tester->getStatusCode());
|
||||
$this->assertContains('Some commands could not be registered.', $output);
|
||||
$this->assertContains('throwing', $output);
|
||||
$this->assertContains('fine', $output);
|
||||
}
|
||||
|
||||
private function getKernel(array $bundles, $useDispatcher = false)
|
||||
{
|
||||
$container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock();
|
||||
@ -189,3 +224,11 @@ class ApplicationTest extends TestCase
|
||||
return $bundle;
|
||||
}
|
||||
}
|
||||
|
||||
class ThrowingCommand extends Command
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
throw new \Exception('throwing');
|
||||
}
|
||||
}
|
||||
|
@ -15,22 +15,37 @@ use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
|
||||
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\Routing\RouteCollectionBuilder;
|
||||
|
||||
class ConcreteMicroKernel extends Kernel
|
||||
class ConcreteMicroKernel extends Kernel implements EventSubscriberInterface
|
||||
{
|
||||
use MicroKernelTrait;
|
||||
|
||||
private $cacheDir;
|
||||
|
||||
public function onKernelException(GetResponseForExceptionEvent $event)
|
||||
{
|
||||
if ($event->getException() instanceof Danger) {
|
||||
$event->setResponse(Response::create('It\'s dangerous to go alone. Take this ⚔'));
|
||||
}
|
||||
}
|
||||
|
||||
public function halloweenAction()
|
||||
{
|
||||
return new Response('halloween');
|
||||
}
|
||||
|
||||
public function dangerousAction()
|
||||
{
|
||||
throw new Danger();
|
||||
}
|
||||
|
||||
public function registerBundles()
|
||||
{
|
||||
return array(
|
||||
@ -57,6 +72,7 @@ class ConcreteMicroKernel extends Kernel
|
||||
protected function configureRoutes(RouteCollectionBuilder $routes)
|
||||
{
|
||||
$routes->add('/', 'kernel:halloweenAction');
|
||||
$routes->add('/danger', 'kernel:dangerousAction');
|
||||
}
|
||||
|
||||
protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
|
||||
@ -68,4 +84,18 @@ class ConcreteMicroKernel extends Kernel
|
||||
$c->setParameter('halloween', 'Have a great day!');
|
||||
$c->register('halloween', 'stdClass');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::EXCEPTION => 'onKernelException',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Danger extends \RuntimeException
|
||||
{
|
||||
}
|
||||
|
@ -28,4 +28,15 @@ class MicroKernelTraitTest extends TestCase
|
||||
$this->assertEquals('Have a great day!', $kernel->getContainer()->getParameter('halloween'));
|
||||
$this->assertInstanceOf('stdClass', $kernel->getContainer()->get('halloween'));
|
||||
}
|
||||
|
||||
public function testAsEventSubscriber()
|
||||
{
|
||||
$kernel = new ConcreteMicroKernel('test', true);
|
||||
$kernel->boot();
|
||||
|
||||
$request = Request::create('/danger');
|
||||
$response = $kernel->handle($request);
|
||||
|
||||
$this->assertSame('It\'s dangerous to go alone. Take this ⚔', $response->getContent());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
# to be removed once https://github.com/doctrine/DoctrineBundle/pull/684 is merged
|
||||
services:
|
||||
Doctrine\Bundle\DoctrineBundle\Command\:
|
||||
resource: "@DoctrineBundle/Command/*"
|
||||
tags: [console.command]
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Bundle\SecurityBundle\Tests\Functional\app;
|
||||
|
||||
use Doctrine\ORM\Version;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
@ -82,6 +83,11 @@ class AppKernel extends Kernel
|
||||
public function registerContainerConfiguration(LoaderInterface $loader)
|
||||
{
|
||||
$loader->load($this->rootConfig);
|
||||
|
||||
// to be removed once https://github.com/doctrine/DoctrineBundle/pull/684 is merged
|
||||
if ('Acl' === $this->testCase && class_exists(Version::class)) {
|
||||
$loader->load(__DIR__.'/Acl/doctrine.yml');
|
||||
}
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
|
@ -80,7 +80,7 @@ header .container { display: flex; justify-content: space-between; }
|
||||
.exception-hierarchy .icon svg { height: 13px; width: 13px; vertical-align: -2px; }
|
||||
|
||||
.exception-without-message .exception-message-wrapper { display: none; }
|
||||
.exception-message-wrapper .container { display: flex; align-items: flex-start; min-height: 70px; padding: 10px 0 8px; }
|
||||
.exception-message-wrapper .container { display: flex; align-items: flex-start; min-height: 70px; padding: 10px 15px 8px; }
|
||||
.exception-message { flex-grow: 1; }
|
||||
.exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; }
|
||||
.exception-message.long { font-size: 18px; }
|
||||
@ -107,11 +107,11 @@ header .container { display: flex; justify-content: space-between; }
|
||||
.trace-line .icon svg { height: 16px; width: 16px; }
|
||||
.trace-line-header { padding-left: 36px; }
|
||||
|
||||
.trace-file-path, .trace-file-path a { color: #999; color: #795da3; color: #B0413E; color: #222; font-size: 13px; }
|
||||
.trace-file-path, .trace-file-path a { color: #222; font-size: 13px; }
|
||||
.trace-class { color: #B0413E; }
|
||||
.trace-type { padding: 0 2px; }
|
||||
.trace-method { color: #B0413E; color: #222; font-weight: bold; color: #B0413E; }
|
||||
.trace-arguments { color: #222; color: #999; font-weight: normal; color: #795da3; color: #777; padding-left: 2px; }
|
||||
.trace-method { color: #B0413E; font-weight: bold; }
|
||||
.trace-arguments { color: #777; font-weight: normal; padding-left: 2px; }
|
||||
|
||||
.trace-code { background: #FFF; font-size: 12px; margin: 10px 10px 2px 10px; padding: 10px; overflow-x: auto; white-space: nowrap; }
|
||||
.trace-code ol { margin: 0; float: left; }
|
||||
|
@ -6,7 +6,7 @@
|
||||
{% set icon %}
|
||||
{{ include('@WebProfiler/Icon/validator.svg') }}
|
||||
<span class="sf-toolbar-value">
|
||||
{{ collector.violationsCount }}
|
||||
{{ collector.violationsCount ?: collector.calls|length }}
|
||||
</span>
|
||||
{% endset %}
|
||||
|
||||
|
@ -698,6 +698,16 @@ class Application
|
||||
{
|
||||
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
|
||||
|
||||
$this->doRenderException($e, $output);
|
||||
|
||||
if (null !== $this->runningCommand) {
|
||||
$output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
|
||||
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
|
||||
}
|
||||
}
|
||||
|
||||
protected function doRenderException(\Exception $e, OutputInterface $output)
|
||||
{
|
||||
do {
|
||||
$title = sprintf(
|
||||
' [%s%s] ',
|
||||
@ -755,11 +765,6 @@ class Application
|
||||
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
|
||||
}
|
||||
} while ($e = $e->getPrevious());
|
||||
|
||||
if (null !== $this->runningCommand) {
|
||||
$output->writeln(sprintf('<info>%s</info>', sprintf($this->runningCommand->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
|
||||
$output->writeln('', OutputInterface::VERBOSITY_QUIET);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,10 +59,10 @@ class ErrorListener implements EventSubscriberInterface
|
||||
}
|
||||
|
||||
if (!$inputString = $this->getInputString($event)) {
|
||||
return $this->logger->error('The console exited with code "{code}"', array('code' => $exitCode));
|
||||
return $this->logger->debug('The console exited with code "{code}"', array('code' => $exitCode));
|
||||
}
|
||||
|
||||
$this->logger->error('Command "{command}" exited with code "{code}"', array('command' => $inputString, 'code' => $exitCode));
|
||||
$this->logger->debug('Command "{command}" exited with code "{code}"', array('command' => $inputString, 'code' => $exitCode));
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
|
@ -61,7 +61,7 @@ class ErrorListenerTest extends TestCase
|
||||
$logger = $this->getLogger();
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('error')
|
||||
->method('debug')
|
||||
->with('Command "{command}" exited with code "{code}"', array('command' => 'test:run', 'code' => 255))
|
||||
;
|
||||
|
||||
@ -74,7 +74,7 @@ class ErrorListenerTest extends TestCase
|
||||
$logger = $this->getLogger();
|
||||
$logger
|
||||
->expects($this->never())
|
||||
->method('error')
|
||||
->method('debug')
|
||||
;
|
||||
|
||||
$listener = new ErrorListener($logger);
|
||||
@ -97,7 +97,7 @@ class ErrorListenerTest extends TestCase
|
||||
$logger = $this->getLogger();
|
||||
$logger
|
||||
->expects($this->exactly(3))
|
||||
->method('error')
|
||||
->method('debug')
|
||||
->with('Command "{command}" exited with code "{code}"', array('command' => 'test:run --foo=bar', 'code' => 255))
|
||||
;
|
||||
|
||||
@ -112,7 +112,7 @@ class ErrorListenerTest extends TestCase
|
||||
$logger = $this->getLogger();
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('error')
|
||||
->method('debug')
|
||||
->with('Command "{command}" exited with code "{code}"', array('command' => 'test:run', 'code' => 255))
|
||||
;
|
||||
|
||||
|
@ -27,10 +27,12 @@ class DebugClassLoader
|
||||
private $classLoader;
|
||||
private $isFinder;
|
||||
private static $caseCheck;
|
||||
private static $internal = array();
|
||||
private static $final = array();
|
||||
private static $finalMethods = array();
|
||||
private static $deprecated = array();
|
||||
private static $deprecatedMethods = array();
|
||||
private static $internal = array();
|
||||
private static $internalMethods = array();
|
||||
private static $darwinCache = array('/' => array('/', array()));
|
||||
|
||||
/**
|
||||
@ -165,50 +167,6 @@ class DebugClassLoader
|
||||
throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name));
|
||||
}
|
||||
|
||||
$parent = get_parent_class($class);
|
||||
$doc = $refl->getDocComment();
|
||||
if (preg_match('#\n \* @internal(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s', $doc, $notice)) {
|
||||
self::$internal[$name] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
|
||||
}
|
||||
|
||||
// Not an interface nor a trait
|
||||
if (class_exists($name, false)) {
|
||||
if (preg_match('#\n \* @final(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s', $doc, $notice)) {
|
||||
self::$final[$name] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
|
||||
}
|
||||
|
||||
if ($parent && isset(self::$final[$parent])) {
|
||||
@trigger_error(sprintf('The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $name), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
// Inherit @final annotations
|
||||
self::$finalMethods[$name] = $parent && isset(self::$finalMethods[$parent]) ? self::$finalMethods[$parent] : array();
|
||||
|
||||
foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
|
||||
if ($method->class !== $name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($parent && isset(self::$finalMethods[$parent][$method->name])) {
|
||||
@trigger_error(sprintf('%s It may change without further notice as of its next major version. You should not extend it from "%s".', self::$finalMethods[$parent][$method->name], $name), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
$doc = $method->getDocComment();
|
||||
if (false === $doc || false === strpos($doc, '@final')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preg_match('#\n\s+\* @final(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) {
|
||||
$message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
|
||||
self::$finalMethods[$name][$method->name] = sprintf('The "%s::%s()" method is considered final%s.', $name, $method->name, $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('#\n \* @deprecated (.*?)\r?\n \*(?: @|/$)#s', $refl->getDocComment(), $notice)) {
|
||||
self::$deprecated[$name] = preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]);
|
||||
}
|
||||
|
||||
// Don't trigger deprecations for classes in the same vendor
|
||||
if (2 > $len = 1 + (strpos($name, '\\', 1 + strpos($name, '\\')) ?: strpos($name, '_'))) {
|
||||
$len = 0;
|
||||
@ -224,37 +182,89 @@ class DebugClassLoader
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array_merge(array($parent), class_implements($name, false), class_uses($name, false)) as $use) {
|
||||
// Detect annotations on the class
|
||||
if (false !== $doc = $refl->getDocComment()) {
|
||||
foreach (array('final', 'deprecated', 'internal') as $annotation) {
|
||||
if (false !== strpos($doc, '@'.$annotation) && preg_match('#\n \* @'.$annotation.'(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s', $doc, $notice)) {
|
||||
self::${$annotation}[$name] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$parentAndTraits = class_uses($name, false);
|
||||
if ($parent = get_parent_class($class)) {
|
||||
$parentAndTraits[] = $parent;
|
||||
|
||||
if (isset(self::$final[$parent])) {
|
||||
@trigger_error(sprintf('The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $name), E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
|
||||
// Detect if the parent is annotated
|
||||
foreach ($parentAndTraits + $this->getOwnInterfaces($name, $parent) as $use) {
|
||||
if (isset(self::$deprecated[$use]) && strncmp($ns, $use, $len)) {
|
||||
$type = class_exists($name, false) ? 'class' : (interface_exists($name, false) ? 'interface' : 'trait');
|
||||
$verb = class_exists($use, false) || interface_exists($name, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses');
|
||||
|
||||
@trigger_error(sprintf('The "%s" %s %s "%s" that is deprecated%s.', $name, $type, $verb, $use, self::$deprecated[$use]), E_USER_DEPRECATED);
|
||||
}
|
||||
if (isset(self::$internal[$use]) && strncmp($ns, $use, $len)) {
|
||||
@trigger_error(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], $name), E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$parent || strncmp($ns, $parent, $len)) {
|
||||
if ($parent && isset(self::$deprecated[$parent]) && strncmp($ns, $parent, $len)) {
|
||||
@trigger_error(sprintf('The "%s" class extends "%s" that is deprecated %s', $name, $parent, self::$deprecated[$parent]), E_USER_DEPRECATED);
|
||||
// Inherit @final and @deprecated annotations for methods
|
||||
self::$finalMethods[$name] = array();
|
||||
self::$deprecatedMethods[$name] = array();
|
||||
self::$internalMethods[$name] = array();
|
||||
foreach ($parentAndTraits as $use) {
|
||||
foreach (array('finalMethods', 'deprecatedMethods', 'internalMethods') as $property) {
|
||||
if (isset(self::${$property}[$use])) {
|
||||
self::${$property}[$name] = array_merge(self::${$property}[$name], self::${$property}[$use]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$isClass = class_exists($name, false);
|
||||
foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
|
||||
if ($method->class !== $name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentInterfaces = array();
|
||||
$deprecatedInterfaces = array();
|
||||
if ($parent) {
|
||||
foreach (class_implements($parent) as $interface) {
|
||||
$parentInterfaces[$interface] = 1;
|
||||
// Method from a trait
|
||||
if ($method->getFilename() !== $refl->getFileName()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($isClass && $parent && isset(self::$finalMethods[$parent][$method->name])) {
|
||||
list($declaringClass, $message) = self::$finalMethods[$parent][$method->name];
|
||||
@trigger_error(sprintf('The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
foreach ($parentAndTraits as $use) {
|
||||
if (isset(self::$deprecatedMethods[$use][$method->name])) {
|
||||
list($declaringClass, $message) = self::$deprecatedMethods[$use][$method->name];
|
||||
if (strncmp($ns, $declaringClass, $len)) {
|
||||
@trigger_error(sprintf('The "%s::%s()" method is deprecated%s. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
if (isset(self::$internalMethods[$use][$method->name])) {
|
||||
list($declaringClass, $message) = self::$internalMethods[$use][$method->name];
|
||||
if (strncmp($ns, $declaringClass, $len)) {
|
||||
@trigger_error(sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($refl->getInterfaceNames() as $interface) {
|
||||
if (isset(self::$deprecated[$interface]) && strncmp($ns, $interface, $len)) {
|
||||
$deprecatedInterfaces[] = $interface;
|
||||
}
|
||||
foreach (class_implements($interface) as $interface) {
|
||||
$parentInterfaces[$interface] = 1;
|
||||
}
|
||||
// Detect method annotations
|
||||
if (false === $doc = $method->getDocComment()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($deprecatedInterfaces as $interface) {
|
||||
if (!isset($parentInterfaces[$interface])) {
|
||||
@trigger_error(sprintf('The "%s" %s "%s" that is deprecated %s', $name, $refl->isInterface() ? 'interface extends' : 'class implements', $interface, self::$deprecated[$interface]), E_USER_DEPRECATED);
|
||||
foreach (array('final', 'deprecated', 'internal') as $annotation) {
|
||||
if (false !== strpos($doc, '@'.$annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) {
|
||||
$message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
|
||||
self::${$annotation.'Methods'}[$name][$method->name] = array($name, $message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -357,4 +367,31 @@ class DebugClassLoader
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `class_implements` includes interfaces from the parents so we have to manually exclude them.
|
||||
*
|
||||
* @param string $class
|
||||
* @param string|false $parent
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function getOwnInterfaces($class, $parent)
|
||||
{
|
||||
$ownInterfaces = class_implements($class, false);
|
||||
|
||||
if ($parent) {
|
||||
foreach (class_implements($parent, false) as $interface) {
|
||||
unset($ownInterfaces[$interface]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($ownInterfaces as $interface) {
|
||||
foreach (class_implements($interface) as $interface) {
|
||||
unset($ownInterfaces[$interface]);
|
||||
}
|
||||
}
|
||||
|
||||
return $ownInterfaces;
|
||||
}
|
||||
}
|
||||
|
@ -320,11 +320,11 @@ EOF;
|
||||
|
||||
.trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; }
|
||||
|
||||
.trace-file-path, .trace-file-path a { margin-top: 3px; color: #999; color: #795da3; color: #B0413E; color: #222; font-size: 13px; }
|
||||
.trace-file-path, .trace-file-path a { color: #222; margin-top: 3px; font-size: 13px; }
|
||||
.trace-class { color: #B0413E; }
|
||||
.trace-type { padding: 0 2px; }
|
||||
.trace-method { color: #B0413E; color: #222; font-weight: bold; color: #B0413E; }
|
||||
.trace-arguments { color: #222; color: #999; font-weight: normal; color: #795da3; color: #777; padding-left: 2px; }
|
||||
.trace-method { color: #B0413E; font-weight: bold; }
|
||||
.trace-arguments { color: #777; font-weight: normal; padding-left: 2px; }
|
||||
|
||||
@media (min-width: 575px) {
|
||||
.hidden-xs-down { display: initial; }
|
||||
|
@ -223,6 +223,28 @@ class DebugClassLoaderTest extends TestCase
|
||||
$this->assertSame($xError, $lastError);
|
||||
}
|
||||
|
||||
public function testExtendedDeprecatedMethod()
|
||||
{
|
||||
set_error_handler(function () { return false; });
|
||||
$e = error_reporting(0);
|
||||
trigger_error('', E_USER_NOTICE);
|
||||
|
||||
class_exists('Test\\'.__NAMESPACE__.'\\ExtendsAnnotatedClass', true);
|
||||
|
||||
error_reporting($e);
|
||||
restore_error_handler();
|
||||
|
||||
$lastError = error_get_last();
|
||||
unset($lastError['file'], $lastError['line']);
|
||||
|
||||
$xError = array(
|
||||
'type' => E_USER_DEPRECATED,
|
||||
'message' => 'The "Symfony\Component\Debug\Tests\Fixtures\AnnotatedClass::deprecatedMethod()" method is deprecated since version 3.4. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsAnnotatedClass".',
|
||||
);
|
||||
|
||||
$this->assertSame($xError, $lastError);
|
||||
}
|
||||
|
||||
public function testInternalsUse()
|
||||
{
|
||||
$deprecations = array();
|
||||
@ -235,9 +257,10 @@ class DebugClassLoaderTest extends TestCase
|
||||
restore_error_handler();
|
||||
|
||||
$this->assertSame($deprecations, array(
|
||||
'The "Symfony\Component\Debug\Tests\Fixtures\InternalClass" class is considered internal since version 3.4. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".',
|
||||
'The "Symfony\Component\Debug\Tests\Fixtures\InternalInterface" interface is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".',
|
||||
'The "Symfony\Component\Debug\Tests\Fixtures\InternalClass" class is considered internal since version 3.4. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternalsParent".',
|
||||
'The "Symfony\Component\Debug\Tests\Fixtures\InternalInterface" interface is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternalsParent".',
|
||||
'The "Symfony\Component\Debug\Tests\Fixtures\InternalTrait" trait is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".',
|
||||
'The "Symfony\Component\Debug\Tests\Fixtures\InternalTrait2::internalMethod()" method is considered internal since version 3.4. It may change without further notice. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".',
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -281,10 +304,18 @@ class ClassLoader
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class Float {}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsFinalClass' === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsFinalClass extends \\'.__NAMESPACE__.'\Fixtures\FinalClass {}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternals' === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternals extends \\'.__NAMESPACE__.'\Fixtures\InternalClass implements \\'.__NAMESPACE__.'\Fixtures\InternalInterface {
|
||||
use \\'.__NAMESPACE__.'\Fixtures\InternalTrait;
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsAnnotatedClass' === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsAnnotatedClass extends \\'.__NAMESPACE__.'\Fixtures\AnnotatedClass {
|
||||
public function deprecatedMethod() { }
|
||||
}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternals' === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternals extends ExtendsInternalsParent {
|
||||
use \\'.__NAMESPACE__.'\Fixtures\InternalTrait;
|
||||
|
||||
public function internalMethod() { }
|
||||
}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternalsParent' === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternalsParent extends \\'.__NAMESPACE__.'\Fixtures\InternalClass implements \\'.__NAMESPACE__.'\Fixtures\InternalInterface { }');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
class AnnotatedClass
|
||||
{
|
||||
/**
|
||||
* @deprecated since version 3.4.
|
||||
*/
|
||||
public function deprecatedMethod()
|
||||
{
|
||||
}
|
||||
}
|
@ -8,4 +8,8 @@ namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
class InternalClass
|
||||
{
|
||||
use InternalTrait2;
|
||||
|
||||
public function usedInInternalClass()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -7,4 +7,17 @@ namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
*/
|
||||
trait InternalTrait2
|
||||
{
|
||||
/**
|
||||
* @internal since version 3.4
|
||||
*/
|
||||
public function internalMethod()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal but should not trigger a deprecation.
|
||||
*/
|
||||
public function usedInInternalClass()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class ChildDefinition extends Definition
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Definition being decorated.
|
||||
* Returns the Definition to inherit from.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -43,7 +43,7 @@ class ChildDefinition extends Definition
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Definition being decorated.
|
||||
* Sets the Definition to inherit from.
|
||||
*
|
||||
* @param string $parent
|
||||
*
|
||||
|
@ -817,8 +817,8 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
* This methods allows for simple registration of service definition
|
||||
* with a fluid interface.
|
||||
*
|
||||
* @param string $id The service identifier
|
||||
* @param string $class The service class
|
||||
* @param string $id The service identifier
|
||||
* @param string $class|null The service class
|
||||
*
|
||||
* @return Definition A Definition instance
|
||||
*/
|
||||
|
@ -189,6 +189,13 @@ class Definition
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the properties to define when creating the service.
|
||||
*
|
||||
* @param array $properties
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setProperties(array $properties)
|
||||
{
|
||||
$this->properties = $properties;
|
||||
@ -196,11 +203,24 @@ class Definition
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the properties to define when creating the service.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getProperties()
|
||||
{
|
||||
return $this->properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a specific property.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setProperty($name, $value)
|
||||
{
|
||||
$this->properties[$name] = $value;
|
||||
@ -223,7 +243,7 @@ class Definition
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a specific argument.
|
||||
* Replaces a specific argument.
|
||||
*
|
||||
* @param int|string $index
|
||||
* @param mixed $argument
|
||||
@ -251,6 +271,14 @@ class Definition
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a specific argument.
|
||||
*
|
||||
* @param int|string $key
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setArgument($key, $value)
|
||||
{
|
||||
$this->arguments[$key] = $value;
|
||||
@ -757,7 +785,7 @@ class Definition
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets autowired.
|
||||
* Enables/disables autowiring.
|
||||
*
|
||||
* @param bool $autowired
|
||||
*
|
||||
|
@ -12,7 +12,9 @@
|
||||
namespace Symfony\Component\DependencyInjection\Dumper;
|
||||
|
||||
use Symfony\Component\Yaml\Dumper as YmlDumper;
|
||||
use Symfony\Component\Yaml\Parser;
|
||||
use Symfony\Component\Yaml\Tag\TaggedValue;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Symfony\Component\DependencyInjection\Alias;
|
||||
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
@ -267,6 +269,8 @@ class YamlDumper extends Dumper
|
||||
return $this->getParameterCall((string) $value);
|
||||
} elseif ($value instanceof Expression) {
|
||||
return $this->getExpressionCall((string) $value);
|
||||
} elseif ($value instanceof Definition) {
|
||||
return new TaggedValue('service', (new Parser())->parse("_:\n".$this->addService('_', $value), Yaml::PARSE_CUSTOM_TAGS)['_']['_']);
|
||||
} elseif (is_object($value) || is_resource($value)) {
|
||||
throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.');
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Dumper;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Dumper\YamlDumper;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Symfony\Component\Yaml\Parser;
|
||||
@ -64,6 +65,19 @@ class YamlDumperTest extends TestCase
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services24.yml', $dumper->dump());
|
||||
}
|
||||
|
||||
public function testInlineServices()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$container->register('foo', 'Class1')
|
||||
->addArgument((new Definition('Class2'))
|
||||
->addArgument(new Definition('Class2'))
|
||||
)
|
||||
;
|
||||
|
||||
$dumper = new YamlDumper($container);
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_inline.yml', $dumper->dump());
|
||||
}
|
||||
|
||||
private function assertEqualYamlStructure($expected, $yaml, $message = '')
|
||||
{
|
||||
$parser = new Parser();
|
||||
|
@ -0,0 +1,14 @@
|
||||
|
||||
services:
|
||||
service_container:
|
||||
class: Symfony\Component\DependencyInjection\ContainerInterface
|
||||
synthetic: true
|
||||
foo:
|
||||
class: Class1
|
||||
arguments: [!service { class: Class2, arguments: [!service { class: Class2 }] }]
|
||||
Psr\Container\ContainerInterface:
|
||||
alias: service_container
|
||||
public: false
|
||||
Symfony\Component\DependencyInjection\ContainerInterface:
|
||||
alias: service_container
|
||||
public: false
|
@ -141,7 +141,7 @@ class BinaryFileResponse extends Response
|
||||
*/
|
||||
public function setAutoEtag()
|
||||
{
|
||||
$this->setEtag(substr(base64_encode(hash_file('sha256', $this->file->getPathname(), true)), 0, 32));
|
||||
$this->setEtag(base64_encode(hash_file('sha256', $this->file->getPathname(), true)));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -95,7 +95,9 @@ class MemcacheSessionHandler implements \SessionHandlerInterface
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
return $this->memcache->delete($this->prefix.$sessionId);
|
||||
$this->memcache->delete($this->prefix.$sessionId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,7 +101,9 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
return $this->memcached->delete($this->prefix.$sessionId);
|
||||
$result = $this->memcached->delete($this->prefix.$sessionId);
|
||||
|
||||
return $result || $this->memcached->getResultCode() == \Memcached::RES_NOTFOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,6 +191,8 @@ abstract class Bundle implements BundleInterface
|
||||
}
|
||||
$r = new \ReflectionClass($class);
|
||||
if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract() && !$r->getConstructor()->getNumberOfRequiredParameters()) {
|
||||
@trigger_error(sprintf('Auto-registration of the command "%s" is deprecated since Symfony 3.4 and won\'t be supported in 4.0. Use PSR-4 based service discovery instead.', $class), E_USER_DEPRECATED);
|
||||
|
||||
$application->add($r->newInstance());
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ CHANGELOG
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
* deprecated commands auto registration
|
||||
* added `AddCacheClearerPass`
|
||||
* added `AddCacheWarmerPass`
|
||||
|
||||
|
@ -31,6 +31,10 @@ class BundleTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
* @expectedDeprecation Auto-registration of the command "Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\Command\FooCommand" is deprecated since Symfony 3.4 and won't be supported in 4.0. Use PSR-4 based service discovery instead.
|
||||
*/
|
||||
public function testRegisterCommands()
|
||||
{
|
||||
$cmd = new FooCommand();
|
||||
|
@ -35,36 +35,51 @@ trait BlockingStoreTestTrait
|
||||
public function testBlockingLocks()
|
||||
{
|
||||
// Amount a microsecond used to order async actions
|
||||
$clockDelay = 200000;
|
||||
$clockDelay = 50000;
|
||||
|
||||
/** @var StoreInterface $store */
|
||||
$store = $this->getStore();
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$parentPID = posix_getpid();
|
||||
|
||||
if ($childPID1 = pcntl_fork()) {
|
||||
// give time to fork to start
|
||||
usleep(1 * $clockDelay);
|
||||
// Block SIGHUP signal
|
||||
pcntl_sigprocmask(SIG_BLOCK, array(SIGHUP));
|
||||
|
||||
if ($childPID = pcntl_fork()) {
|
||||
// Wait the start of the child
|
||||
pcntl_sigwaitinfo(array(SIGHUP), $info);
|
||||
|
||||
try {
|
||||
// This call should failed given the lock should already by acquired by the child #1
|
||||
// This call should failed given the lock should already by acquired by the child
|
||||
$store->save($key);
|
||||
$this->fail('The store saves a locked key.');
|
||||
} catch (LockConflictedException $e) {
|
||||
}
|
||||
|
||||
// send the ready signal to the child
|
||||
posix_kill($childPID, SIGHUP);
|
||||
|
||||
// This call should be blocked by the child #1
|
||||
$store->waitAndSave($key);
|
||||
$this->assertTrue($store->exists($key));
|
||||
$store->delete($key);
|
||||
|
||||
// Now, assert the child process worked well
|
||||
pcntl_waitpid($childPID1, $status1);
|
||||
pcntl_waitpid($childPID, $status1);
|
||||
$this->assertSame(0, pcntl_wexitstatus($status1), 'The child process couldn\'t lock the resource');
|
||||
} else {
|
||||
// Block SIGHUP signal
|
||||
pcntl_sigprocmask(SIG_BLOCK, array(SIGHUP));
|
||||
try {
|
||||
$store->save($key);
|
||||
// Wait 2 ClockDelay to let parent process to finish
|
||||
usleep(2 * $clockDelay);
|
||||
// send the ready signal to the parent
|
||||
posix_kill($parentPID, SIGHUP);
|
||||
|
||||
// Wait for the parent to be ready
|
||||
pcntl_sigwaitinfo(array(SIGHUP), $info);
|
||||
|
||||
// Wait ClockDelay to let parent assert to finish
|
||||
usleep($clockDelay);
|
||||
$store->delete($key);
|
||||
exit(0);
|
||||
} catch (\Exception $e) {
|
||||
|
@ -27,7 +27,7 @@ class DateCaster
|
||||
$fromNow = (new \DateTime())->diff($d);
|
||||
|
||||
$title = $d->format('l, F j, Y')
|
||||
."\n".$fromNow->format('%R').self::formatInterval($fromNow).' from now'
|
||||
."\n".self::formatInterval($fromNow).' from now'
|
||||
.($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '')
|
||||
;
|
||||
|
||||
|
@ -27,42 +27,45 @@ class DateCasterTest extends TestCase
|
||||
/**
|
||||
* @dataProvider provideDateTimes
|
||||
*/
|
||||
public function testDumpDateTime($time, $timezone, $expected)
|
||||
public function testDumpDateTime($time, $timezone, $xDate, $xTimestamp)
|
||||
{
|
||||
$date = new \DateTime($time, new \DateTimeZone($timezone));
|
||||
|
||||
$xDump = <<<EODUMP
|
||||
DateTime @1493503200 {
|
||||
date: $expected
|
||||
DateTime @$xTimestamp {
|
||||
date: $xDate
|
||||
}
|
||||
EODUMP;
|
||||
|
||||
$this->assertDumpMatchesFormat($xDump, $date);
|
||||
$this->assertDumpEquals($xDump, $date);
|
||||
}
|
||||
|
||||
public function testCastDateTime()
|
||||
/**
|
||||
* @dataProvider provideDateTimes
|
||||
*/
|
||||
public function testCastDateTime($time, $timezone, $xDate, $xTimestamp, $xInfos)
|
||||
{
|
||||
if ((defined('HHVM_VERSION_ID') || PHP_VERSION_ID <= 50509) && preg_match('/[-+]\d{2}:\d{2}/', $timezone)) {
|
||||
$this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.');
|
||||
}
|
||||
|
||||
$stub = new Stub();
|
||||
$date = new \DateTime('2017-08-30 00:00:00.000000', new \DateTimeZone('Europe/Zurich'));
|
||||
$date = new \DateTime($time, new \DateTimeZone($timezone));
|
||||
$cast = DateCaster::castDateTime($date, array('foo' => 'bar'), $stub, false, 0);
|
||||
|
||||
$xDump = <<<'EODUMP'
|
||||
$xDump = <<<EODUMP
|
||||
array:1 [
|
||||
"\x00~\x00date" => 2017-08-30 00:00:00.0 Europe/Zurich (+02:00)
|
||||
"\\x00~\\x00date" => $xDate
|
||||
]
|
||||
EODUMP;
|
||||
|
||||
$this->assertDumpMatchesFormat($xDump, $cast);
|
||||
$this->assertDumpEquals($xDump, $cast);
|
||||
|
||||
$xDump = <<<'EODUMP'
|
||||
$xDump = <<<EODUMP
|
||||
Symfony\Component\VarDumper\Caster\ConstStub {
|
||||
+type: 1
|
||||
+class: "2017-08-30 00:00:00.0 Europe/Zurich (+02:00)"
|
||||
+value: """
|
||||
Wednesday, August 30, 2017\n
|
||||
+%a from now\n
|
||||
DST On
|
||||
"""
|
||||
+class: "$xDate"
|
||||
+value: "%A$xInfos%A"
|
||||
+cut: 0
|
||||
+handle: 0
|
||||
+refCount: 0
|
||||
@ -77,15 +80,16 @@ EODUMP;
|
||||
public function provideDateTimes()
|
||||
{
|
||||
return array(
|
||||
array('2017-04-30 00:00:00.000000', 'Europe/Zurich', '2017-04-30 00:00:00.0 Europe/Zurich (+02:00)'),
|
||||
array('2017-04-30 00:00:00.000000', '+02:00', '2017-04-30 00:00:00.0 +02:00'),
|
||||
array('2017-04-30 00:00:00.000000', 'Europe/Zurich', '2017-04-30 00:00:00.0 Europe/Zurich (+02:00)', 1493503200, 'Sunday, April 30, 2017%Afrom now%ADST On'),
|
||||
array('2017-12-31 00:00:00.000000', 'Europe/Zurich', '2017-12-31 00:00:00.0 Europe/Zurich (+01:00)', 1514674800, 'Sunday, December 31, 2017%Afrom now%ADST Off'),
|
||||
array('2017-04-30 00:00:00.000000', '+02:00', '2017-04-30 00:00:00.0 +02:00', 1493503200, 'Sunday, April 30, 2017%Afrom now'),
|
||||
|
||||
array('2017-04-30 00:00:00.100000', '+02:00', '2017-04-30 00:00:00.100 +02:00'),
|
||||
array('2017-04-30 00:00:00.120000', '+02:00', '2017-04-30 00:00:00.120 +02:00'),
|
||||
array('2017-04-30 00:00:00.123000', '+02:00', '2017-04-30 00:00:00.123 +02:00'),
|
||||
array('2017-04-30 00:00:00.123400', '+02:00', '2017-04-30 00:00:00.123400 +02:00'),
|
||||
array('2017-04-30 00:00:00.123450', '+02:00', '2017-04-30 00:00:00.123450 +02:00'),
|
||||
array('2017-04-30 00:00:00.123456', '+02:00', '2017-04-30 00:00:00.123456 +02:00'),
|
||||
array('2017-04-30 00:00:00.100000', '+00:00', '2017-04-30 00:00:00.100 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'),
|
||||
array('2017-04-30 00:00:00.120000', '+00:00', '2017-04-30 00:00:00.120 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'),
|
||||
array('2017-04-30 00:00:00.123000', '+00:00', '2017-04-30 00:00:00.123 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'),
|
||||
array('2017-04-30 00:00:00.123400', '+00:00', '2017-04-30 00:00:00.123400 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'),
|
||||
array('2017-04-30 00:00:00.123450', '+00:00', '2017-04-30 00:00:00.123450 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'),
|
||||
array('2017-04-30 00:00:00.123456', '+00:00', '2017-04-30 00:00:00.123456 +00:00', 1493510400, 'Sunday, April 30, 2017%Afrom now'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -120,7 +124,7 @@ DateInterval {
|
||||
}
|
||||
EODUMP;
|
||||
|
||||
$this->assertDumpMatchesFormat($xDump, $interval, Caster::EXCLUDE_VERBOSE);
|
||||
$this->assertDumpEquals($xDump, $interval, Caster::EXCLUDE_VERBOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -140,7 +144,7 @@ array:1 [
|
||||
]
|
||||
EODUMP;
|
||||
|
||||
$this->assertDumpMatchesFormat($xDump, $cast);
|
||||
$this->assertDumpEquals($xDump, $cast);
|
||||
|
||||
if (null === $xSeconds) {
|
||||
return;
|
||||
@ -159,7 +163,7 @@ Symfony\Component\VarDumper\Caster\ConstStub {
|
||||
}
|
||||
EODUMP;
|
||||
|
||||
$this->assertDumpMatchesFormat($xDump, $cast["\0~\0interval"]);
|
||||
$this->assertDumpEquals($xDump, $cast["\0~\0interval"]);
|
||||
}
|
||||
|
||||
public function provideIntervals()
|
||||
@ -214,7 +218,7 @@ DateTimeZone {
|
||||
}
|
||||
EODUMP;
|
||||
|
||||
$this->assertDumpMatchesFormat($xDump, $timezone, Caster::EXCLUDE_VERBOSE);
|
||||
$this->assertDumpEquals($xDump, $timezone, Caster::EXCLUDE_VERBOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -233,7 +237,7 @@ array:1 [
|
||||
]
|
||||
EODUMP;
|
||||
|
||||
$this->assertDumpMatchesFormat($xDump, $cast);
|
||||
$this->assertDumpEquals($xDump, $cast);
|
||||
|
||||
$xDump = <<<EODUMP
|
||||
Symfony\Component\VarDumper\Caster\ConstStub {
|
||||
|
Reference in New Issue
Block a user