Upgrade 1-11.38

This commit is contained in:
xesmyd
2026-03-30 14:10:30 +02:00
parent f2a7e6d1fc
commit ac648ef29d
24665 changed files with 69682 additions and 2205004 deletions
+20 -39
View File
@@ -11,16 +11,15 @@
namespace Symfony\Component\HttpKernel\Bundle;
use Symfony\Component\Console\Application;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\Console\Application;
use Symfony\Component\Finder\Finder;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\Finder\Finder;
/**
* An implementation of BundleInterface that adds a few conventions
* for DependencyInjection extensions and Console commands.
* An implementation of BundleInterface that adds a few conventions for DependencyInjection extensions.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
@@ -34,28 +33,24 @@ abstract class Bundle implements BundleInterface
private $namespace;
/**
* Boots the Bundle.
* {@inheritdoc}
*/
public function boot()
{
}
/**
* Shutdowns the Bundle.
* {@inheritdoc}
*/
public function shutdown()
{
}
/**
* Builds the bundle.
*
* It is only ever called once when the cache is empty.
* {@inheritdoc}
*
* This method can be overridden to register compilation passes,
* other extensions, ...
*
* @param ContainerBuilder $container A ContainerBuilder instance
*/
public function build(ContainerBuilder $container)
{
@@ -75,7 +70,7 @@ abstract class Bundle implements BundleInterface
if (null !== $extension) {
if (!$extension instanceof ExtensionInterface) {
throw new \LogicException(sprintf('Extension %s must implement Symfony\Component\DependencyInjection\Extension\ExtensionInterface.', get_class($extension)));
throw new \LogicException(sprintf('Extension "%s" must implement Symfony\Component\DependencyInjection\Extension\ExtensionInterface.', \get_class($extension)));
}
// check naming convention
@@ -83,10 +78,7 @@ abstract class Bundle implements BundleInterface
$expectedAlias = Container::underscore($basename);
if ($expectedAlias != $extension->getAlias()) {
throw new \LogicException(sprintf(
'Users will expect the alias of the default extension of a bundle to be the underscored version of the bundle name ("%s"). You can override "Bundle::getContainerExtension()" if you want to use "%s" or another alias.',
$expectedAlias, $extension->getAlias()
));
throw new \LogicException(sprintf('Users will expect the alias of the default extension of a bundle to be the underscored version of the bundle name ("%s"). You can override "Bundle::getContainerExtension()" if you want to use "%s" or another alias.', $expectedAlias, $extension->getAlias()));
}
$this->extension = $extension;
@@ -95,15 +87,11 @@ abstract class Bundle implements BundleInterface
}
}
if ($this->extension) {
return $this->extension;
}
return $this->extension ?: null;
}
/**
* Gets the Bundle namespace.
*
* @return string The Bundle namespace
* {@inheritdoc}
*/
public function getNamespace()
{
@@ -115,33 +103,27 @@ abstract class Bundle implements BundleInterface
}
/**
* Gets the Bundle directory path.
*
* @return string The Bundle absolute path
* {@inheritdoc}
*/
public function getPath()
{
if (null === $this->path) {
$reflected = new \ReflectionObject($this);
$this->path = dirname($reflected->getFileName());
$this->path = \dirname($reflected->getFileName());
}
return $this->path;
}
/**
* Returns the bundle parent name.
*
* @return string|null The Bundle parent name it overrides or null if no parent
* {@inheritdoc}
*/
public function getParent()
{
}
/**
* Returns the bundle name (the class short name).
*
* @return string The Bundle name
* {@inheritdoc}
*/
final public function getName()
{
@@ -159,8 +141,6 @@ abstract class Bundle implements BundleInterface
*
* * Commands are in the 'Command' sub-directory
* * Commands extend Symfony\Component\Console\Command\Command
*
* @param Application $application An Application instance
*/
public function registerCommands(Application $application)
{
@@ -183,13 +163,16 @@ abstract class Bundle implements BundleInterface
}
$class = $ns.'\\'.$file->getBasename('.php');
if ($this->container) {
$commandIds = $this->container->hasParameter('console.command.ids') ? $this->container->getParameter('console.command.ids') : [];
$alias = 'console.command.'.strtolower(str_replace('\\', '_', $class));
if ($this->container->has($alias)) {
if (isset($commandIds[$alias]) || $this->container->has($alias)) {
continue;
}
}
$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());
}
}
@@ -214,9 +197,7 @@ abstract class Bundle implements BundleInterface
*/
protected function createContainerExtension()
{
if (class_exists($class = $this->getContainerExtensionClass())) {
return new $class();
}
return class_exists($class = $this->getContainerExtensionClass()) ? new $class() : null;
}
private function parseClassName()
+2 -2
View File
@@ -36,8 +36,6 @@ interface BundleInterface extends ContainerAwareInterface
* Builds the bundle.
*
* It is only ever called once when the cache is empty.
*
* @param ContainerBuilder $container A ContainerBuilder instance
*/
public function build(ContainerBuilder $container);
@@ -56,6 +54,8 @@ interface BundleInterface extends ContainerAwareInterface
* bundle.
*
* @return string The Bundle name it overrides or null if no parent
*
* @deprecated This method is deprecated as of 3.4 and will be removed in 4.0.
*/
public function getParent();
+30
View File
@@ -1,11 +1,41 @@
CHANGELOG
=========
3.4.0
-----
* added a minimalist PSR-3 `Logger` class that writes in `stderr`
* made kernels implementing `CompilerPassInterface` able to process the container
* deprecated bundle inheritance
* added `RebootableInterface` and implemented it in `Kernel`
* deprecated commands auto registration
* deprecated `EnvParametersResource`
* added `Symfony\Component\HttpKernel\Client::catchExceptions()`
* deprecated the `ChainCacheClearer::add()` method
* deprecated the `CacheaWarmerAggregate::add()` and `setWarmers()` methods
* made `CacheWarmerAggregate` and `ChainCacheClearer` classes final
* added the possibility to reset the profiler to its initial state
* deprecated data collectors without a `reset()` method
* deprecated implementing `DebugLoggerInterface` without a `clear()` method
3.3.0
-----
* added `kernel.project_dir` and `Kernel::getProjectDir()`
* deprecated `kernel.root_dir` and `Kernel::getRootDir()`
* deprecated `Kernel::getEnvParameters()`
* deprecated the special `SYMFONY__` environment variables
* added the possibility to change the query string parameter used by `UriSigner`
* deprecated `LazyLoadingFragmentHandler::addRendererService()`
* deprecated `Extension::addClassesToCompile()` and `Extension::getClassesToCompile()`
* deprecated `Psr6CacheClearer::addPool()`
3.2.0
-----
* deprecated `DataCollector::varToString()`, use `cloneVar()` instead
* changed surrogate capability name in `AbstractSurrogate::addSurrogateCapability` to 'symfony'
* Added `ControllerArgumentValueResolverPass`
3.1.0
-----
@@ -15,12 +15,11 @@ namespace Symfony\Component\HttpKernel\CacheClearer;
* ChainCacheClearer.
*
* @author Dustin Dobervich <ddobervich@gmail.com>
*
* @final since version 3.4
*/
class ChainCacheClearer implements CacheClearerInterface
{
/**
* @var array
*/
protected $clearers;
/**
@@ -28,7 +27,7 @@ class ChainCacheClearer implements CacheClearerInterface
*
* @param array $clearers The initial clearers
*/
public function __construct(array $clearers = array())
public function __construct($clearers = [])
{
$this->clearers = $clearers;
}
@@ -46,10 +45,12 @@ class ChainCacheClearer implements CacheClearerInterface
/**
* Adds a cache clearer to the aggregate.
*
* @param CacheClearerInterface $clearer
* @deprecated since version 3.4, to be removed in 4.0, inject the list of clearers as a constructor argument instead.
*/
public function add(CacheClearerInterface $clearer)
{
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0, inject the list of clearers as a constructor argument instead.', __METHOD__), \E_USER_DEPRECATED);
$this->clearers[] = $clearer;
}
}
@@ -18,13 +18,34 @@ use Psr\Cache\CacheItemPoolInterface;
*/
class Psr6CacheClearer implements CacheClearerInterface
{
private $pools = array();
private $pools = [];
public function __construct(array $pools = [])
{
$this->pools = $pools;
}
public function addPool(CacheItemPoolInterface $pool)
{
@trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Pass an array of pools indexed by name to the constructor instead.', __METHOD__), \E_USER_DEPRECATED);
$this->pools[] = $pool;
}
public function hasPool($name)
{
return isset($this->pools[$name]);
}
public function clearPool($name)
{
if (!isset($this->pools[$name])) {
throw new \InvalidArgumentException(sprintf('Cache pool not found: "%s".', $name));
}
return $this->pools[$name]->clear();
}
/**
* {@inheritdoc}
*/
+1 -1
View File
@@ -20,7 +20,7 @@ abstract class CacheWarmer implements CacheWarmerInterface
{
protected function writeCacheFile($file, $content)
{
$tmpFile = @tempnam(dirname($file), basename($file));
$tmpFile = @tempnam(\dirname($file), basename($file));
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
@chmod($file, 0666 & ~umask());
@@ -15,17 +15,21 @@ namespace Symfony\Component\HttpKernel\CacheWarmer;
* Aggregates several cache warmers into a single one.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @final since version 3.4
*/
class CacheWarmerAggregate implements CacheWarmerInterface
{
protected $warmers = array();
protected $warmers = [];
protected $optionalsEnabled = false;
private $triggerDeprecation = false;
public function __construct(array $warmers = array())
public function __construct($warmers = [])
{
foreach ($warmers as $warmer) {
$this->add($warmer);
}
$this->triggerDeprecation = true;
}
public function enableOptionalWarmers()
@@ -59,16 +63,28 @@ class CacheWarmerAggregate implements CacheWarmerInterface
return false;
}
/**
* @deprecated since version 3.4, to be removed in 4.0, inject the list of clearers as a constructor argument instead.
*/
public function setWarmers(array $warmers)
{
$this->warmers = array();
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0, inject the list of clearers as a constructor argument instead.', __METHOD__), \E_USER_DEPRECATED);
$this->warmers = [];
foreach ($warmers as $warmer) {
$this->add($warmer);
}
}
/**
* @deprecated since version 3.4, to be removed in 4.0, inject the list of clearers as a constructor argument instead.
*/
public function add(CacheWarmerInterface $warmer)
{
if ($this->triggerDeprecation) {
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0, inject the list of clearers as a constructor argument instead.', __METHOD__), \E_USER_DEPRECATED);
}
$this->warmers[] = $warmer;
}
}
+40 -43
View File
@@ -12,11 +12,10 @@
namespace Symfony\Component\HttpKernel;
use Symfony\Component\BrowserKit\Client as BaseClient;
use Symfony\Component\BrowserKit\CookieJar;
use Symfony\Component\BrowserKit\History;
use Symfony\Component\BrowserKit\Request as DomRequest;
use Symfony\Component\BrowserKit\Response as DomResponse;
use Symfony\Component\BrowserKit\Cookie as DomCookie;
use Symfony\Component\BrowserKit\History;
use Symfony\Component\BrowserKit\CookieJar;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -26,22 +25,21 @@ use Symfony\Component\HttpFoundation\Response;
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @method Request|null getRequest() A Request instance
* @method Request|null getRequest() A Request instance
* @method Response|null getResponse() A Response instance
*/
class Client extends BaseClient
{
protected $kernel;
private $catchExceptions = true;
/**
* Constructor.
*
* @param HttpKernelInterface $kernel An HttpKernel instance
* @param array $server The server parameters (equivalent of $_SERVER)
* @param History $history A History instance to store the browser history
* @param CookieJar $cookieJar A CookieJar instance to store the cookies
*/
public function __construct(HttpKernelInterface $kernel, array $server = array(), History $history = null, CookieJar $cookieJar = null)
public function __construct(HttpKernelInterface $kernel, array $server = [], History $history = null, CookieJar $cookieJar = null)
{
// These class properties must be set before calling the parent constructor, as it may depend on it.
$this->kernel = $kernel;
@@ -51,15 +49,23 @@ class Client extends BaseClient
}
/**
* Makes a request.
* Sets whether to catch exceptions when the kernel is handling a request.
*
* @param Request $request A Request instance
* @param bool $catchExceptions Whether to catch exceptions
*/
public function catchExceptions($catchExceptions)
{
$this->catchExceptions = $catchExceptions;
}
/**
* Makes a request.
*
* @return Response A Response instance
*/
protected function doRequest($request)
{
$response = $this->kernel->handle($request);
$response = $this->kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, $this->catchExceptions);
if ($this->kernel instanceof TerminableInterface) {
$this->kernel->terminate($request, $response);
@@ -71,33 +77,39 @@ class Client extends BaseClient
/**
* Returns the script to execute when the request must be insulated.
*
* @param Request $request A Request instance
*
* @return string
*/
protected function getScript($request)
{
$kernel = str_replace("'", "\\'", serialize($this->kernel));
$request = str_replace("'", "\\'", serialize($request));
$kernel = var_export(serialize($this->kernel), true);
$request = var_export(serialize($request), true);
$r = new \ReflectionClass('\\Symfony\\Component\\ClassLoader\\ClassLoader');
$requirePath = str_replace("'", "\\'", $r->getFileName());
$symfonyPath = str_replace("'", "\\'", dirname(dirname(dirname(__DIR__))));
$errorReporting = error_reporting();
$requires = '';
foreach (get_declared_classes() as $class) {
if (0 === strpos($class, 'ComposerAutoloaderInit')) {
$r = new \ReflectionClass($class);
$file = \dirname(\dirname($r->getFileName())).'/autoload.php';
if (file_exists($file)) {
$requires .= 'require_once '.var_export($file, true).";\n";
}
}
}
if (!$requires) {
throw new \RuntimeException('Composer autoloader not found.');
}
$code = <<<EOF
<?php
error_reporting($errorReporting);
require_once '$requirePath';
$requires
\$loader = new Symfony\Component\ClassLoader\ClassLoader();
\$loader->addPrefix('Symfony', '$symfonyPath');
\$loader->register();
\$kernel = unserialize('$kernel');
\$request = unserialize('$request');
\$kernel = unserialize($kernel);
\$request = unserialize($request);
EOF;
return $code.$this->getHandleScript();
@@ -119,8 +131,6 @@ EOF;
/**
* Converts the BrowserKit request to a HttpKernel request.
*
* @param DomRequest $request A DomRequest instance
*
* @return Request A Request instance
*/
protected function filterRequest(DomRequest $request)
@@ -145,15 +155,13 @@ EOF;
*
* @see UploadedFile
*
* @param array $files An array of files
*
* @return array An array with all uploaded files marked as already moved
*/
protected function filterFiles(array $files)
{
$filtered = array();
$filtered = [];
foreach ($files as $key => $value) {
if (is_array($value)) {
if (\is_array($value)) {
$filtered[$key] = $this->filterFiles($value);
} elseif ($value instanceof UploadedFile) {
if ($value->isValid() && $value->getSize() > UploadedFile::getMaxFilesize()) {
@@ -162,7 +170,7 @@ EOF;
$value->getClientOriginalName(),
$value->getClientMimeType(),
0,
UPLOAD_ERR_INI_SIZE,
\UPLOAD_ERR_INI_SIZE,
true
);
} else {
@@ -184,26 +192,15 @@ EOF;
/**
* Converts the HttpKernel response to a BrowserKit response.
*
* @param Response $response A Response instance
*
* @return DomResponse A DomResponse instance
*/
protected function filterResponse($response)
{
$headers = $response->headers->all();
if ($response->headers->getCookies()) {
$cookies = array();
foreach ($response->headers->getCookies() as $cookie) {
$cookies[] = new DomCookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
}
$headers['Set-Cookie'] = $cookies;
}
// this is needed to support StreamedResponse
ob_start();
$response->sendContent();
$content = ob_get_clean();
return new DomResponse($content, $response->getStatusCode(), $headers);
return new DomResponse($content, $response->getStatusCode(), $response->headers->all());
}
}
+16 -6
View File
@@ -17,6 +17,8 @@ use Symfony\Component\Config\Resource\SelfCheckingResourceInterface;
* EnvParametersResource represents resources stored in prefixed environment variables.
*
* @author Chris Wilkinson <chriswilkinson84@gmail.com>
*
* @deprecated since version 3.4, to be removed in 4.0
*/
class EnvParametersResource implements SelfCheckingResourceInterface, \Serializable
{
@@ -31,8 +33,6 @@ class EnvParametersResource implements SelfCheckingResourceInterface, \Serializa
private $variables;
/**
* Constructor.
*
* @param string $prefix
*/
public function __construct($prefix)
@@ -54,7 +54,7 @@ class EnvParametersResource implements SelfCheckingResourceInterface, \Serializa
*/
public function getResource()
{
return array('prefix' => $this->prefix, 'variables' => $this->variables);
return ['prefix' => $this->prefix, 'variables' => $this->variables];
}
/**
@@ -65,14 +65,24 @@ class EnvParametersResource implements SelfCheckingResourceInterface, \Serializa
return $this->findVariables() === $this->variables;
}
/**
* @internal
*/
public function serialize()
{
return serialize(array('prefix' => $this->prefix, 'variables' => $this->variables));
return serialize(['prefix' => $this->prefix, 'variables' => $this->variables]);
}
/**
* @internal
*/
public function unserialize($serialized)
{
$unserialized = unserialize($serialized);
if (\PHP_VERSION_ID >= 70000) {
$unserialized = unserialize($serialized, ['allowed_classes' => false]);
} else {
$unserialized = unserialize($serialized);
}
$this->prefix = $unserialized['prefix'];
$this->variables = $unserialized['variables'];
@@ -80,7 +90,7 @@ class EnvParametersResource implements SelfCheckingResourceInterface, \Serializa
private function findVariables()
{
$variables = array();
$variables = [];
foreach ($_SERVER as $key => $value) {
if (0 === strpos($key, $this->prefix)) {
+2 -4
View File
@@ -25,13 +25,11 @@ class FileLocator extends BaseFileLocator
private $path;
/**
* Constructor.
*
* @param KernelInterface $kernel A KernelInterface instance
* @param null|string $path The path the global resource directory
* @param string|null $path The path the global resource directory
* @param array $paths An array of paths where to look for resources
*/
public function __construct(KernelInterface $kernel, $path = null, array $paths = array())
public function __construct(KernelInterface $kernel, $path = null, array $paths = [])
{
$this->kernel = $kernel;
if (null !== $path) {
+12 -10
View File
@@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestAttributeValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\VariadicValueResolver;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactoryInterface;
@@ -29,11 +30,11 @@ final class ArgumentResolver implements ArgumentResolverInterface
private $argumentMetadataFactory;
/**
* @var ArgumentValueResolverInterface[]
* @var iterable|ArgumentValueResolverInterface[]
*/
private $argumentValueResolvers;
public function __construct(ArgumentMetadataFactoryInterface $argumentMetadataFactory = null, array $argumentValueResolvers = array())
public function __construct(ArgumentMetadataFactoryInterface $argumentMetadataFactory = null, $argumentValueResolvers = [])
{
$this->argumentMetadataFactory = $argumentMetadataFactory ?: new ArgumentMetadataFactory();
$this->argumentValueResolvers = $argumentValueResolvers ?: self::getDefaultArgumentValueResolvers();
@@ -44,7 +45,7 @@ final class ArgumentResolver implements ArgumentResolverInterface
*/
public function getArguments(Request $request, $controller)
{
$arguments = array();
$arguments = [];
foreach ($this->argumentMetadataFactory->createArgumentMetadata($controller) as $metadata) {
foreach ($this->argumentValueResolvers as $resolver) {
@@ -55,7 +56,7 @@ final class ArgumentResolver implements ArgumentResolverInterface
$resolved = $resolver->resolve($request, $metadata);
if (!$resolved instanceof \Generator) {
throw new \InvalidArgumentException(sprintf('%s::resolve() must yield at least one value.', get_class($resolver)));
throw new \InvalidArgumentException(sprintf('"%s::resolve()" must yield at least one value.', \get_class($resolver)));
}
foreach ($resolved as $append) {
@@ -68,10 +69,10 @@ final class ArgumentResolver implements ArgumentResolverInterface
$representative = $controller;
if (is_array($representative)) {
$representative = sprintf('%s::%s()', get_class($representative[0]), $representative[1]);
} elseif (is_object($representative)) {
$representative = get_class($representative);
if (\is_array($representative)) {
$representative = sprintf('%s::%s()', \get_class($representative[0]), $representative[1]);
} elseif (\is_object($representative)) {
$representative = \get_class($representative);
}
throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.', $representative, $metadata->getName()));
@@ -82,11 +83,12 @@ final class ArgumentResolver implements ArgumentResolverInterface
public static function getDefaultArgumentValueResolvers()
{
return array(
return [
new RequestAttributeValueResolver(),
new RequestValueResolver(),
new SessionValueResolver(),
new DefaultValueResolver(),
new VariadicValueResolver(),
);
];
}
}
@@ -37,8 +37,8 @@ final class VariadicValueResolver implements ArgumentValueResolverInterface
{
$values = $request->attributes->get($argument->getName());
if (!is_array($values)) {
throw new \InvalidArgumentException(sprintf('The action argument "...$%1$s" is required to be an array, the request attribute "%1$s" contains a type of "%2$s" instead.', $argument->getName(), gettype($values)));
if (!\is_array($values)) {
throw new \InvalidArgumentException(sprintf('The action argument "...$%1$s" is required to be an array, the request attribute "%1$s" contains a type of "%2$s" instead.', $argument->getName(), \gettype($values)));
}
foreach ($values as $value) {
@@ -24,7 +24,6 @@ interface ArgumentResolverInterface
/**
* Returns the arguments to pass to the controller.
*
* @param Request $request
* @param callable $controller
*
* @return array An array of arguments to pass to the controller
@@ -24,9 +24,6 @@ interface ArgumentValueResolverInterface
/**
* Whether this resolver can resolve the value for the given ArgumentMetadata.
*
* @param Request $request
* @param ArgumentMetadata $argument
*
* @return bool
*/
public function supports(Request $request, ArgumentMetadata $argument);
@@ -34,9 +31,6 @@ interface ArgumentValueResolverInterface
/**
* Returns the possible value(s).
*
* @param Request $request
* @param ArgumentMetadata $argument
*
* @return \Generator
*/
public function resolve(Request $request, ArgumentMetadata $argument);
@@ -27,17 +27,15 @@ use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface;
class ControllerReference
{
public $controller;
public $attributes = array();
public $query = array();
public $attributes = [];
public $query = [];
/**
* Constructor.
*
* @param string $controller The controller name
* @param array $attributes An array of parameters to add to the Request attributes
* @param array $query An array of parameters to add to the Request query string
*/
public function __construct($controller, array $attributes = array(), array $query = array())
public function __construct($controller, array $attributes = [], array $query = [])
{
$this->controller = $controller;
$this->attributes = $attributes;
+58 -42
View File
@@ -15,8 +15,6 @@ use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* ControllerResolver.
*
* This implementation uses the '_controller' request attribute to determine
* the controller to execute and uses the request attributes to determine
* the controller method arguments.
@@ -43,11 +41,6 @@ class ControllerResolver implements ArgumentResolverInterface, ControllerResolve
*/
private $supportsScalarTypes;
/**
* Constructor.
*
* @param LoggerInterface $logger A LoggerInterface instance
*/
public function __construct(LoggerInterface $logger = null)
{
$this->logger = $logger;
@@ -72,30 +65,30 @@ class ControllerResolver implements ArgumentResolverInterface, ControllerResolve
return false;
}
if (is_array($controller)) {
if (\is_array($controller)) {
return $controller;
}
if (is_object($controller)) {
if (\is_object($controller)) {
if (method_exists($controller, '__invoke')) {
return $controller;
}
throw new \InvalidArgumentException(sprintf('Controller "%s" for URI "%s" is not callable.', get_class($controller), $request->getPathInfo()));
throw new \InvalidArgumentException(sprintf('Controller "%s" for URI "%s" is not callable.', \get_class($controller), $request->getPathInfo()));
}
if (false === strpos($controller, ':')) {
if (\is_string($controller) && false === strpos($controller, ':')) {
if (method_exists($controller, '__invoke')) {
return $this->instantiateController($controller);
} elseif (function_exists($controller)) {
} elseif (\function_exists($controller)) {
return $controller;
}
}
$callable = $this->createController($controller);
if (!is_callable($callable)) {
throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable. %s', $request->getPathInfo(), $this->getControllerError($callable)));
try {
$callable = $this->createController($controller);
} catch (\InvalidArgumentException $e) {
throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: ', $request->getPathInfo()).$e->getMessage(), 0, $e);
}
return $callable;
@@ -108,11 +101,11 @@ class ControllerResolver implements ArgumentResolverInterface, ControllerResolve
*/
public function getArguments(Request $request, $controller)
{
@trigger_error(sprintf('%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class), E_USER_DEPRECATED);
@trigger_error(sprintf('The "%s()" method is deprecated as of 3.1 and will be removed in 4.0. Implement the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class), \E_USER_DEPRECATED);
if (is_array($controller)) {
if (\is_array($controller)) {
$r = new \ReflectionMethod($controller[0], $controller[1]);
} elseif (is_object($controller) && !$controller instanceof \Closure) {
} elseif (\is_object($controller) && !$controller instanceof \Closure) {
$r = new \ReflectionObject($controller);
$r = $r->getMethod('__invoke');
} else {
@@ -123,7 +116,6 @@ class ControllerResolver implements ArgumentResolverInterface, ControllerResolve
}
/**
* @param Request $request
* @param callable $controller
* @param \ReflectionParameter[] $parameters
*
@@ -133,28 +125,28 @@ class ControllerResolver implements ArgumentResolverInterface, ControllerResolve
*/
protected function doGetArguments(Request $request, $controller, array $parameters)
{
@trigger_error(sprintf('%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class), E_USER_DEPRECATED);
@trigger_error(sprintf('The "%s()" method is deprecated as of 3.1 and will be removed in 4.0. Implement the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class), \E_USER_DEPRECATED);
$attributes = $request->attributes->all();
$arguments = array();
$arguments = [];
foreach ($parameters as $param) {
if (array_key_exists($param->name, $attributes)) {
if ($this->supportsVariadic && $param->isVariadic() && is_array($attributes[$param->name])) {
if (\array_key_exists($param->name, $attributes)) {
if ($this->supportsVariadic && $param->isVariadic() && \is_array($attributes[$param->name])) {
$arguments = array_merge($arguments, array_values($attributes[$param->name]));
} else {
$arguments[] = $attributes[$param->name];
}
} elseif ($param->getClass() && $param->getClass()->isInstance($request)) {
} elseif ($this->typeMatchesRequestClass($param, $request)) {
$arguments[] = $request;
} elseif ($param->isDefaultValueAvailable()) {
$arguments[] = $param->getDefaultValue();
} elseif ($this->supportsScalarTypes && $param->hasType() && $param->allowsNull()) {
$arguments[] = null;
} else {
if (is_array($controller)) {
$repr = sprintf('%s::%s()', get_class($controller[0]), $controller[1]);
} elseif (is_object($controller)) {
$repr = get_class($controller);
if (\is_array($controller)) {
$repr = sprintf('%s::%s()', \get_class($controller[0]), $controller[1]);
} elseif (\is_object($controller)) {
$repr = \get_class($controller);
} else {
$repr = $controller;
}
@@ -173,7 +165,7 @@ class ControllerResolver implements ArgumentResolverInterface, ControllerResolve
*
* @return callable A PHP callable
*
* @throws \InvalidArgumentException
* @throws \InvalidArgumentException When the controller cannot be created
*/
protected function createController($controller)
{
@@ -187,7 +179,13 @@ class ControllerResolver implements ArgumentResolverInterface, ControllerResolve
throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
}
return array($this->instantiateController($class), $method);
$controller = [$this->instantiateController($class), $method];
if (!\is_callable($controller)) {
throw new \InvalidArgumentException($this->getControllerError($controller));
}
return $controller;
}
/**
@@ -204,7 +202,7 @@ class ControllerResolver implements ArgumentResolverInterface, ControllerResolve
private function getControllerError($callable)
{
if (is_string($callable)) {
if (\is_string($callable)) {
if (false !== strpos($callable, '::')) {
$callable = explode('::', $callable);
}
@@ -213,26 +211,26 @@ class ControllerResolver implements ArgumentResolverInterface, ControllerResolve
return sprintf('Class "%s" does not have a method "__invoke".', $callable);
}
if (!function_exists($callable)) {
if (!\function_exists($callable)) {
return sprintf('Function "%s" does not exist.', $callable);
}
}
if (!is_array($callable)) {
return sprintf('Invalid type for controller given, expected string or array, got "%s".', gettype($callable));
if (!\is_array($callable)) {
return sprintf('Invalid type for controller given, expected string or array, got "%s".', \gettype($callable));
}
if (2 !== count($callable)) {
return sprintf('Invalid format for controller, expected array(controller, method) or controller::method.');
if (2 !== \count($callable)) {
return 'Invalid format for controller, expected [controller, method] or controller::method.';
}
list($controller, $method) = $callable;
if (is_string($controller) && !class_exists($controller)) {
if (\is_string($controller) && !class_exists($controller)) {
return sprintf('Class "%s" does not exist.', $controller);
}
$className = is_object($controller) ? get_class($controller) : $controller;
$className = \is_object($controller) ? \get_class($controller) : $controller;
if (method_exists($controller, $method)) {
return sprintf('Method "%s" on class "%s" should be public and non-abstract.', $method, $className);
@@ -240,12 +238,12 @@ class ControllerResolver implements ArgumentResolverInterface, ControllerResolve
$collection = get_class_methods($controller);
$alternatives = array();
$alternatives = [];
foreach ($collection as $item) {
$lev = levenshtein($method, $item);
if ($lev <= strlen($method) / 3 || false !== strpos($item, $method)) {
if ($lev <= \strlen($method) / 3 || false !== strpos($item, $method)) {
$alternatives[] = $item;
}
}
@@ -254,7 +252,7 @@ class ControllerResolver implements ArgumentResolverInterface, ControllerResolve
$message = sprintf('Expected method "%s" on class "%s"', $method, $className);
if (count($alternatives) > 0) {
if (\count($alternatives) > 0) {
$message .= sprintf(', did you mean "%s"?', implode('", "', $alternatives));
} else {
$message .= sprintf('. Available methods: "%s".', implode('", "', $collection));
@@ -262,4 +260,22 @@ class ControllerResolver implements ArgumentResolverInterface, ControllerResolve
return $message;
}
/**
* @return bool
*/
private function typeMatchesRequestClass(\ReflectionParameter $param, Request $request)
{
if (!method_exists($param, 'getType')) {
return $param->getClass() && $param->getClass()->isInstance($request);
}
if (!($type = $param->getType()) || $type->isBuiltin()) {
return false;
}
$class = new \ReflectionClass($type instanceof \ReflectionNamedType ? $type->getName() : (string) $type);
return $class && $class->isInstance($request);
}
}
@@ -31,11 +31,9 @@ interface ControllerResolverInterface
* As several resolvers can exist for a single application, a resolver must
* return false when it is not able to determine the controller.
*
* The resolver must only throw an exception when it should be able to load
* The resolver must only throw an exception when it should be able to load a
* controller but cannot because of some errors made by the developer.
*
* @param Request $request A Request instance
*
* @return callable|false A PHP callable representing the Controller,
* or false if this resolver is not able to determine the controller
*
@@ -11,8 +11,8 @@
namespace Symfony\Component\HttpKernel\Controller;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Stopwatch\Stopwatch;
/**
* @author Fabien Potencier <fabien@symfony.com>
@@ -11,12 +11,10 @@
namespace Symfony\Component\HttpKernel\Controller;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Stopwatch\Stopwatch;
/**
* TraceableControllerResolver.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TraceableControllerResolver implements ControllerResolverInterface, ArgumentResolverInterface
@@ -25,13 +23,6 @@ class TraceableControllerResolver implements ControllerResolverInterface, Argume
private $stopwatch;
private $argumentResolver;
/**
* Constructor.
*
* @param ControllerResolverInterface $resolver A ControllerResolverInterface instance
* @param Stopwatch $stopwatch A Stopwatch instance
* @param ArgumentResolverInterface $argumentResolver Only required for BC
*/
public function __construct(ControllerResolverInterface $resolver, Stopwatch $stopwatch, ArgumentResolverInterface $argumentResolver = null)
{
$this->resolver = $resolver;
@@ -69,7 +60,7 @@ class TraceableControllerResolver implements ControllerResolverInterface, Argume
*/
public function getArguments(Request $request, $controller)
{
@trigger_error(sprintf('The %s method is deprecated as of 3.1 and will be removed in 4.0. Please use the %s instead.', __METHOD__, TraceableArgumentResolver::class), E_USER_DEPRECATED);
@trigger_error(sprintf('The "%s()" method is deprecated as of 3.1 and will be removed in 4.0. Please use the %s instead.', __METHOD__, TraceableArgumentResolver::class), \E_USER_DEPRECATED);
$ret = $this->argumentResolver->getArguments($request, $controller);
@@ -58,7 +58,7 @@ class ArgumentMetadata
*
* The type is the PHP class in 5.5+ and additionally the basic type in PHP 7.0+.
*
* @return string
* @return string|null
*/
public function getType()
{
@@ -107,7 +107,7 @@ class ArgumentMetadata
public function getDefaultValue()
{
if (!$this->hasDefaultValue) {
throw new \LogicException(sprintf('Argument $%s does not have a default value. Use %s::hasDefaultValue() to avoid this exception.', $this->name, __CLASS__));
throw new \LogicException(sprintf('Argument $%s does not have a default value. Use "%s::hasDefaultValue()" to avoid this exception.', $this->name, __CLASS__));
}
return $this->defaultValue;
@@ -47,18 +47,18 @@ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
*/
public function createArgumentMetadata($controller)
{
$arguments = array();
$arguments = [];
if (is_array($controller)) {
if (\is_array($controller)) {
$reflection = new \ReflectionMethod($controller[0], $controller[1]);
} elseif (is_object($controller) && !$controller instanceof \Closure) {
} elseif (\is_object($controller) && !$controller instanceof \Closure) {
$reflection = (new \ReflectionObject($controller))->getMethod('__invoke');
} else {
$reflection = new \ReflectionFunction($controller);
}
foreach ($reflection->getParameters() as $param) {
$arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $this->isVariadic($param), $this->hasDefaultValue($param), $this->getDefaultValue($param), $param->allowsNull());
$arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param, $reflection), $this->isVariadic($param), $this->hasDefaultValue($param), $this->getDefaultValue($param), $param->allowsNull());
}
return $arguments;
@@ -67,8 +67,6 @@ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
/**
* Returns whether an argument is variadic.
*
* @param \ReflectionParameter $parameter
*
* @return bool
*/
private function isVariadic(\ReflectionParameter $parameter)
@@ -79,8 +77,6 @@ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
/**
* Determines whether an argument has a default value.
*
* @param \ReflectionParameter $parameter
*
* @return bool
*/
private function hasDefaultValue(\ReflectionParameter $parameter)
@@ -91,8 +87,6 @@ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
/**
* Returns a default value if available.
*
* @param \ReflectionParameter $parameter
*
* @return mixed|null
*/
private function getDefaultValue(\ReflectionParameter $parameter)
@@ -103,27 +97,39 @@ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
/**
* Returns an associated type to the given parameter if available.
*
* @param \ReflectionParameter $parameter
*
* @return null|string
* @return string|null
*/
private function getType(\ReflectionParameter $parameter)
private function getType(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function)
{
if ($this->supportsParameterType) {
if (!$type = $parameter->getType()) {
return;
return null;
}
$typeName = $type instanceof \ReflectionNamedType ? $type->getName() : $type->__toString();
if ('array' === $typeName && !$type->isBuiltin()) {
$name = $type instanceof \ReflectionNamedType ? $type->getName() : (string) $type;
if ('array' === $name && !$type->isBuiltin()) {
// Special case for HHVM with variadics
return;
return null;
}
} elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $parameter, $name)) {
$name = $name[1];
} else {
return null;
}
$lcName = strtolower($name);
return $typeName;
if ('self' !== $lcName && 'parent' !== $lcName) {
return $name;
}
if (!$function instanceof \ReflectionMethod) {
return null;
}
if ('self' === $lcName) {
return $function->getDeclaringClass()->name;
}
if ($parent = $function->getDeclaringClass()->getParentClass()) {
return $parent->name;
}
if (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $parameter, $info)) {
return $info[1];
}
return null;
}
}
@@ -26,6 +26,11 @@ class AjaxDataCollector extends DataCollector
// all collecting is done client side
}
public function reset()
{
// all collecting is done client side
}
public function getName()
{
return 'ajax';
@@ -11,17 +11,16 @@
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\VarDumper\Caster\LinkStub;
/**
* ConfigDataCollector.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ConfigDataCollector extends DataCollector
class ConfigDataCollector extends DataCollector implements LateDataCollectorInterface
{
/**
* @var KernelInterface
@@ -29,10 +28,9 @@ class ConfigDataCollector extends DataCollector
private $kernel;
private $name;
private $version;
private $hasVarDumper;
/**
* Constructor.
*
* @param string $name The name of the application using the web profiler
* @param string $version The version of the application using the web profiler
*/
@@ -40,12 +38,11 @@ class ConfigDataCollector extends DataCollector
{
$this->name = $name;
$this->version = $version;
$this->hasVarDumper = class_exists(LinkStub::class);
}
/**
* Sets the Kernel associated with this Request.
*
* @param KernelInterface $kernel A KernelInterface instance
*/
public function setKernel(KernelInterface $kernel = null)
{
@@ -57,7 +54,7 @@ class ConfigDataCollector extends DataCollector
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$this->data = array(
$this->data = [
'app_name' => $this->name,
'app_version' => $this->version,
'token' => $response->headers->get('X-Debug-Token'),
@@ -66,24 +63,47 @@ class ConfigDataCollector extends DataCollector
'name' => isset($this->kernel) ? $this->kernel->getName() : 'n/a',
'env' => isset($this->kernel) ? $this->kernel->getEnvironment() : 'n/a',
'debug' => isset($this->kernel) ? $this->kernel->isDebug() : 'n/a',
'php_version' => PHP_VERSION,
'xdebug_enabled' => extension_loaded('xdebug'),
'eaccel_enabled' => extension_loaded('eaccelerator') && ini_get('eaccelerator.enable'),
'apc_enabled' => extension_loaded('apc') && ini_get('apc.enabled'),
'xcache_enabled' => extension_loaded('xcache') && ini_get('xcache.cacher'),
'wincache_enabled' => extension_loaded('wincache') && ini_get('wincache.ocenabled'),
'zend_opcache_enabled' => extension_loaded('Zend OPcache') && ini_get('opcache.enable'),
'bundles' => array(),
'sapi_name' => PHP_SAPI,
);
'php_version' => \PHP_VERSION,
'php_architecture' => \PHP_INT_SIZE * 8,
'php_intl_locale' => class_exists('Locale', false) && \Locale::getDefault() ? \Locale::getDefault() : 'n/a',
'php_timezone' => date_default_timezone_get(),
'xdebug_enabled' => \extension_loaded('xdebug'),
'apcu_enabled' => \extension_loaded('apcu') && filter_var(ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN),
'zend_opcache_enabled' => \extension_loaded('Zend OPcache') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN),
'bundles' => [],
'sapi_name' => \PHP_SAPI,
];
if (isset($this->kernel)) {
foreach ($this->kernel->getBundles() as $name => $bundle) {
$this->data['bundles'][$name] = $bundle->getPath();
$this->data['bundles'][$name] = $this->hasVarDumper ? new LinkStub($bundle->getPath()) : $bundle->getPath();
}
$this->data['symfony_state'] = $this->determineSymfonyState();
$this->data['symfony_minor_version'] = sprintf('%s.%s', Kernel::MAJOR_VERSION, Kernel::MINOR_VERSION);
$eom = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_MAINTENANCE);
$eol = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_LIFE);
$this->data['symfony_eom'] = $eom->format('F Y');
$this->data['symfony_eol'] = $eol->format('F Y');
}
if (preg_match('~^(\d+(?:\.\d+)*)(.+)?$~', $this->data['php_version'], $matches) && isset($matches[2])) {
$this->data['php_version'] = $matches[1];
$this->data['php_version_extra'] = $matches[2];
}
}
/**
* {@inheritdoc}
*/
public function reset()
{
$this->data = [];
}
public function lateCollect()
{
$this->data = $this->cloneVar($this->data);
}
public function getApplicationName()
@@ -99,7 +119,7 @@ class ConfigDataCollector extends DataCollector
/**
* Gets the token.
*
* @return string The token
* @return string|null The token
*/
public function getToken()
{
@@ -126,6 +146,39 @@ class ConfigDataCollector extends DataCollector
return $this->data['symfony_state'];
}
/**
* Returns the minor Symfony version used (without patch numbers of extra
* suffix like "RC", "beta", etc.).
*
* @return string
*/
public function getSymfonyMinorVersion()
{
return $this->data['symfony_minor_version'];
}
/**
* Returns the human redable date when this Symfony version ends its
* maintenance period.
*
* @return string
*/
public function getSymfonyEom()
{
return $this->data['symfony_eom'];
}
/**
* Returns the human redable date when this Symfony version reaches its
* "end of life" and won't receive bugs or security fixes.
*
* @return string
*/
public function getSymfonyEol()
{
return $this->data['symfony_eol'];
}
/**
* Gets the PHP version.
*
@@ -136,6 +189,40 @@ class ConfigDataCollector extends DataCollector
return $this->data['php_version'];
}
/**
* Gets the PHP version extra part.
*
* @return string|null The extra part
*/
public function getPhpVersionExtra()
{
return isset($this->data['php_version_extra']) ? $this->data['php_version_extra'] : null;
}
/**
* @return int The PHP architecture as number of bits (e.g. 32 or 64)
*/
public function getPhpArchitecture()
{
return $this->data['php_architecture'];
}
/**
* @return string
*/
public function getPhpIntlLocale()
{
return $this->data['php_intl_locale'];
}
/**
* @return string
*/
public function getPhpTimezone()
{
return $this->data['php_timezone'];
}
/**
* Gets the application name.
*
@@ -177,23 +264,13 @@ class ConfigDataCollector extends DataCollector
}
/**
* Returns true if EAccelerator is enabled.
* Returns true if APCu is enabled.
*
* @return bool true if EAccelerator is enabled, false otherwise
* @return bool true if APCu is enabled, false otherwise
*/
public function hasEAccelerator()
public function hasApcu()
{
return $this->data['eaccel_enabled'];
}
/**
* Returns true if APC is enabled.
*
* @return bool true if APC is enabled, false otherwise
*/
public function hasApc()
{
return $this->data['apc_enabled'];
return $this->data['apcu_enabled'];
}
/**
@@ -206,36 +283,6 @@ class ConfigDataCollector extends DataCollector
return $this->data['zend_opcache_enabled'];
}
/**
* Returns true if XCache is enabled.
*
* @return bool true if XCache is enabled, false otherwise
*/
public function hasXCache()
{
return $this->data['xcache_enabled'];
}
/**
* Returns true if WinCache is enabled.
*
* @return bool true if WinCache is enabled, false otherwise
*/
public function hasWinCache()
{
return $this->data['wincache_enabled'];
}
/**
* Returns true if any accelerator is enabled.
*
* @return bool true if any accelerator is enabled, false otherwise
*/
public function hasAccelerator()
{
return $this->hasApc() || $this->hasZendOpcache() || $this->hasEAccelerator() || $this->hasXCache() || $this->hasWinCache();
}
public function getBundles()
{
return $this->data['bundles'];
@@ -267,8 +314,8 @@ class ConfigDataCollector extends DataCollector
private function determineSymfonyState()
{
$now = new \DateTime();
$eom = \DateTime::createFromFormat('m/Y', Kernel::END_OF_MAINTENANCE)->modify('last day of this month');
$eol = \DateTime::createFromFormat('m/Y', Kernel::END_OF_LIFE)->modify('last day of this month');
$eom = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_MAINTENANCE)->modify('last day of this month');
$eol = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_LIFE)->modify('last day of this month');
if ($now > $eol) {
$versionState = 'eol';
+35 -46
View File
@@ -12,9 +12,7 @@
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\HttpKernel\DataCollector\Util\ValueExporter;
use Symfony\Component\VarDumper\Caster\ClassStub;
use Symfony\Component\VarDumper\Caster\LinkStub;
use Symfony\Component\VarDumper\Caster\StubCaster;
use Symfony\Component\VarDumper\Caster\CutStub;
use Symfony\Component\VarDumper\Cloner\ClonerInterface;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\Stub;
@@ -30,7 +28,10 @@ use Symfony\Component\VarDumper\Cloner\VarCloner;
*/
abstract class DataCollector implements DataCollectorInterface, \Serializable
{
protected $data = array();
/**
* @var array|Data
*/
protected $data = [];
/**
* @var ValueExporter
@@ -42,16 +43,17 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
*/
private $cloner;
private static $stubsCache = array();
public function serialize()
{
return serialize($this->data);
$trace = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
$isCalledFromOverridingMethod = isset($trace[1]['function'], $trace[1]['object']) && 'serialize' === $trace[1]['function'] && $this === $trace[1]['object'];
return $isCalledFromOverridingMethod ? $this->data : serialize($this->data);
}
public function unserialize($data)
{
$this->data = unserialize($data);
$this->data = \is_array($data) ? $data : unserialize($data);
}
/**
@@ -66,17 +68,16 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
*/
protected function cloneVar($var)
{
if ($var instanceof Data) {
return $var;
}
if (null === $this->cloner) {
if (class_exists(ClassStub::class)) {
if (class_exists(CutStub::class)) {
$this->cloner = new VarCloner();
$this->cloner->setMaxItems(250);
$this->cloner->addCasters(array(
Stub::class => function (Stub $v, array $a, Stub $s, $isNested) {
return $isNested ? $a : StubCaster::castStub($v, $a, $s, true);
},
));
$this->cloner->setMaxItems(-1);
$this->cloner->addCasters($this->getCasters());
} else {
@trigger_error(sprintf('Using the %s() method without the VarDumper component is deprecated since version 3.2 and won\'t be supported in 4.0. Install symfony/var-dumper version 3.2 or above.', __METHOD__), E_USER_DEPRECATED);
@trigger_error(sprintf('Using the %s() method without the VarDumper component is deprecated since Symfony 3.2 and won\'t be supported in 4.0. Install symfony/var-dumper version 3.2 or above.', __METHOD__), \E_USER_DEPRECATED);
$this->cloner = false;
}
}
@@ -88,7 +89,7 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
return $this->valueExporter->exportValue($var);
}
return $this->cloner->cloneVar($this->decorateVar($var));
return $this->cloner->cloneVar($var);
}
/**
@@ -98,11 +99,11 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
*
* @return string The string representation of the variable
*
* @deprecated Deprecated since version 3.2, to be removed in 4.0. Use cloneVar() instead.
* @deprecated since version 3.2, to be removed in 4.0. Use cloneVar() instead.
*/
protected function varToString($var)
{
@trigger_error(sprintf('The %s() method is deprecated since version 3.2 and will be removed in 4.0. Use cloneVar() instead.', __METHOD__), E_USER_DEPRECATED);
@trigger_error(sprintf('The %s() method is deprecated since Symfony 3.2 and will be removed in 4.0. Use cloneVar() instead.', __METHOD__), \E_USER_DEPRECATED);
if (null === $this->valueExporter) {
$this->valueExporter = new ValueExporter();
@@ -111,35 +112,23 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
return $this->valueExporter->exportValue($var);
}
private function decorateVar($var)
/**
* @return callable[] The casters to add to the cloner
*/
protected function getCasters()
{
if (is_array($var)) {
if (isset($var[0], $var[1]) && is_callable($var)) {
return ClassStub::wrapCallable($var);
}
foreach ($var as $k => $v) {
if ($v !== $d = $this->decorateVar($v)) {
$var[$k] = $d;
return [
'*' => function ($v, array $a, Stub $s, $isNested) {
if (!$v instanceof Stub) {
foreach ($a as $k => $v) {
if (\is_object($v) && !$v instanceof \DateTimeInterface && !$v instanceof Stub) {
$a[$k] = new CutStub($v);
}
}
}
}
return $var;
}
if (is_string($var)) {
if (isset(self::$stubsCache[$var])) {
return self::$stubsCache[$var];
}
if (false !== strpos($var, '\\')) {
$c = (false !== $i = strpos($var, '::')) ? substr($var, 0, $i) : $var;
if (class_exists($c, false) || interface_exists($c, false) || trait_exists($c, false)) {
return self::$stubsCache[$var] = new ClassStub($var);
}
}
if (false !== strpos($var, DIRECTORY_SEPARATOR) && false === strpos($var, '://') && false === strpos($var, "\0") && @is_file($var)) {
return self::$stubsCache[$var] = new LinkStub($var);
}
}
return $var;
return $a;
},
];
}
}
@@ -18,15 +18,13 @@ use Symfony\Component\HttpFoundation\Response;
* DataCollectorInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @method reset() Resets this data collector to its initial state.
*/
interface DataCollectorInterface
{
/**
* Collects data for the given Request and Response.
*
* @param Request $request A Request instance
* @param Response $response A Response instance
* @param \Exception $exception An Exception instance
*/
public function collect(Request $request, Response $response, \Exception $exception = null);
@@ -18,8 +18,8 @@ use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\VarDumper\Cloner\Data;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Symfony\Component\VarDumper\Dumper\DataDumperInterface;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Twig\Template;
/**
@@ -49,12 +49,12 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
$this->dumperIsInjected = null !== $dumper;
// All clones share these properties by reference:
$this->rootRefs = array(
$this->rootRefs = [
&$this->data,
&$this->dataCount,
&$this->isCollected,
&$this->clonesCount,
);
];
}
public function __clone()
@@ -67,11 +67,11 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
if ($this->stopwatch) {
$this->stopwatch->start('dump');
}
if ($this->isCollected) {
if ($this->isCollected && !$this->dumper) {
$this->isCollected = false;
}
$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 7);
$trace = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, 7);
$file = $trace[0]['file'];
$line = $trace[0]['line'];
@@ -103,9 +103,9 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
if ($src) {
$src = explode("\n", $src);
$fileExcerpt = array();
$fileExcerpt = [];
for ($i = max($line - 3, 1), $max = min($line + 3, count($src)); $i <= $max; ++$i) {
for ($i = max($line - 3, 1), $max = min($line + 3, \count($src)); $i <= $max; ++$i) {
$fileExcerpt[] = '<li'.($i === $line ? ' class="selected"' : '').'><code>'.$this->htmlEncode($src[$i - 1]).'</code></li>';
}
@@ -153,7 +153,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
) {
if ($response->headers->has('Content-Type') && false !== strpos($response->headers->get('Content-Type'), 'html')) {
$this->dumper = new HtmlDumper('php://output', $this->charset);
$this->dumper->setDisplayOptions(array('fileLinkFormat' => $this->fileLinkFormat));
$this->dumper->setDisplayOptions(['fileLinkFormat' => $this->fileLinkFormat]);
} else {
$this->dumper = new CliDumper('php://output', $this->charset);
}
@@ -164,6 +164,18 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
}
}
public function reset()
{
if ($this->stopwatch) {
$this->stopwatch->reset();
}
$this->data = [];
$this->dataCount = 0;
$this->isCollected = true;
$this->clonesCount = 0;
$this->clonesIndex = 0;
}
public function serialize()
{
if ($this->clonesCount !== $this->clonesIndex) {
@@ -173,7 +185,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
$this->data[] = $this->fileLinkFormat;
$this->data[] = $this->charset;
$ser = serialize($this->data);
$this->data = array();
$this->data = [];
$this->dataCount = 0;
$this->isCollected = true;
if (!$this->dumperIsInjected) {
@@ -185,10 +197,10 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
public function unserialize($data)
{
parent::unserialize($data);
$this->data = unserialize($data);
$charset = array_pop($this->data);
$fileLinkFormat = array_pop($this->data);
$this->dataCount = count($this->data);
$this->dataCount = \count($this->data);
self::__construct($this->stopwatch, $fileLinkFormat, $charset);
}
@@ -203,11 +215,11 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
if ('html' === $format) {
$dumper = new HtmlDumper($data, $this->charset);
$dumper->setDisplayOptions(array('fileLinkFormat' => $this->fileLinkFormat));
$dumper->setDisplayOptions(['fileLinkFormat' => $this->fileLinkFormat]);
} else {
throw new \InvalidArgumentException(sprintf('Invalid dump format: %s', $format));
throw new \InvalidArgumentException(sprintf('Invalid dump format: "%s".', $format));
}
$dumps = array();
$dumps = [];
foreach ($this->data as $dump) {
$dumper->dump($dump['data']->withMaxDepth($maxDepthLimit)->withMaxItemsPerDepth($maxItemsPerDepth));
@@ -232,15 +244,15 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
$this->isCollected = true;
$h = headers_list();
$i = count($h);
$i = \count($h);
array_unshift($h, 'Content-Type: '.ini_get('default_mimetype'));
while (0 !== stripos($h[$i], 'Content-Type:')) {
--$i;
}
if ('cli' !== PHP_SAPI && stripos($h[$i], 'html')) {
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && stripos($h[$i], 'html')) {
$this->dumper = new HtmlDumper('php://output', $this->charset);
$this->dumper->setDisplayOptions(array('fileLinkFormat' => $this->fileLinkFormat));
$this->dumper->setDisplayOptions(['fileLinkFormat' => $this->fileLinkFormat]);
} else {
$this->dumper = new CliDumper('php://output', $this->charset);
}
@@ -250,7 +262,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
$this->doDump($dump['data'], $dump['name'], $dump['file'], $dump['line']);
}
$this->data = array();
$this->data = [];
$this->dataCount = 0;
}
}
@@ -264,7 +276,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
$s = $this->style('meta', '%s');
$f = strip_tags($this->style('', $file));
$name = strip_tags($this->style('', $name));
if ($fmt && $link = is_string($fmt) ? strtr($fmt, array('%f' => $file, '%l' => $line)) : $fmt->format($file, $line)) {
if ($fmt && $link = \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line)) {
$name = sprintf('<a href="%s" title="%s">'.$s.'</a>', strip_tags($this->style('', $link)), $f, $name);
} else {
$name = sprintf('<abbr title="%s">'.$s.'</abbr>', $f, $name);
@@ -11,10 +11,10 @@
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface;
/**
* EventDataCollector.
@@ -27,6 +27,9 @@ class EventDataCollector extends DataCollector implements LateDataCollectorInter
public function __construct(EventDispatcherInterface $dispatcher = null)
{
if ($dispatcher instanceof TraceableEventDispatcherInterface && !method_exists($dispatcher, 'reset')) {
@trigger_error(sprintf('Implementing "%s" without the "reset()" method is deprecated since Symfony 3.4 and will be unsupported in 4.0 for class "%s".', TraceableEventDispatcherInterface::class, \get_class($dispatcher)), \E_USER_DEPRECATED);
}
$this->dispatcher = $dispatcher;
}
@@ -35,10 +38,23 @@ class EventDataCollector extends DataCollector implements LateDataCollectorInter
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$this->data = array(
'called_listeners' => array(),
'not_called_listeners' => array(),
);
$this->data = [
'called_listeners' => [],
'not_called_listeners' => [],
];
}
public function reset()
{
$this->data = [];
if ($this->dispatcher instanceof TraceableEventDispatcherInterface) {
if (!method_exists($this->dispatcher, 'reset')) {
return; // @deprecated
}
$this->dispatcher->reset();
}
}
public function lateCollect()
@@ -47,6 +63,7 @@ class EventDataCollector extends DataCollector implements LateDataCollectorInter
$this->setCalledListeners($this->dispatcher->getCalledListeners());
$this->setNotCalledListeners($this->dispatcher->getNotCalledListeners());
}
$this->data = $this->cloneVar($this->data);
}
/**
@@ -28,12 +28,20 @@ class ExceptionDataCollector extends DataCollector
public function collect(Request $request, Response $response, \Exception $exception = null)
{
if (null !== $exception) {
$this->data = array(
$this->data = [
'exception' => FlattenException::create($exception),
);
];
}
}
/**
* {@inheritdoc}
*/
public function reset()
{
$this->data = [];
}
/**
* Checks if the exception is not null.
*
@@ -47,7 +55,7 @@ class ExceptionDataCollector extends DataCollector
/**
* Gets the exception.
*
* @return \Exception The exception
* @return \Exception|FlattenException
*/
public function getException()
{
@@ -24,12 +24,19 @@ use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
class LoggerDataCollector extends DataCollector implements LateDataCollectorInterface
{
private $logger;
private $containerPathPrefix;
public function __construct($logger = null)
public function __construct($logger = null, $containerPathPrefix = null)
{
if (null !== $logger && $logger instanceof DebugLoggerInterface) {
if (!method_exists($logger, 'clear')) {
@trigger_error(sprintf('Implementing "%s" without the "clear()" method is deprecated since Symfony 3.4 and will be unsupported in 4.0 for class "%s".', DebugLoggerInterface::class, \get_class($logger)), \E_USER_DEPRECATED);
}
$this->logger = $logger;
}
$this->containerPathPrefix = $containerPathPrefix;
}
/**
@@ -43,27 +50,36 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
/**
* {@inheritdoc}
*/
public function lateCollect()
public function reset()
{
if (null !== $this->logger) {
$this->data = $this->computeErrorsCount();
$this->data['logs'] = $this->sanitizeLogs($this->logger->getLogs());
if ($this->logger && method_exists($this->logger, 'clear')) {
$this->logger->clear();
}
$this->data = [];
}
/**
* Gets the logs.
*
* @return array An array of logs
* {@inheritdoc}
*/
public function lateCollect()
{
if (null !== $this->logger) {
$containerDeprecationLogs = $this->getContainerDeprecationLogs();
$this->data = $this->computeErrorsCount($containerDeprecationLogs);
$this->data['compiler_logs'] = $this->getContainerCompilerLogs();
$this->data['logs'] = $this->sanitizeLogs(array_merge($this->logger->getLogs(), $containerDeprecationLogs));
$this->data = $this->cloneVar($this->data);
}
}
public function getLogs()
{
return isset($this->data['logs']) ? $this->data['logs'] : array();
return isset($this->data['logs']) ? $this->data['logs'] : [];
}
public function getPriorities()
{
return isset($this->data['priorities']) ? $this->data['priorities'] : array();
return isset($this->data['priorities']) ? $this->data['priorities'] : [];
}
public function countErrors()
@@ -86,6 +102,11 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
return isset($this->data['scream_count']) ? $this->data['scream_count'] : 0;
}
public function getCompilerLogs()
{
return isset($this->data['compiler_logs']) ? $this->data['compiler_logs'] : [];
}
/**
* {@inheritdoc}
*/
@@ -94,30 +115,92 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
return 'logger';
}
private function getContainerDeprecationLogs()
{
if (null === $this->containerPathPrefix || !file_exists($file = $this->containerPathPrefix.'Deprecations.log')) {
return [];
}
if ('' === $logContent = trim(file_get_contents($file))) {
return [];
}
$bootTime = filemtime($file);
$logs = [];
foreach (unserialize($logContent) as $log) {
$log['context'] = ['exception' => new SilencedErrorContext($log['type'], $log['file'], $log['line'], $log['trace'], $log['count'])];
$log['timestamp'] = $bootTime;
$log['priority'] = 100;
$log['priorityName'] = 'DEBUG';
$log['channel'] = '-';
$log['scream'] = false;
unset($log['type'], $log['file'], $log['line'], $log['trace'], $log['trace'], $log['count']);
$logs[] = $log;
}
return $logs;
}
private function getContainerCompilerLogs()
{
if (null === $this->containerPathPrefix || !file_exists($file = $this->containerPathPrefix.'Compiler.log')) {
return [];
}
$logs = [];
foreach (file($file, \FILE_IGNORE_NEW_LINES) as $log) {
$log = explode(': ', $log, 2);
if (!isset($log[1]) || !preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)++$/', $log[0])) {
$log = ['Unknown Compiler Pass', implode(': ', $log)];
}
$logs[$log[0]][] = ['message' => $log[1]];
}
return $logs;
}
private function sanitizeLogs($logs)
{
$sanitizedLogs = array();
$sanitizedLogs = [];
$silencedLogs = [];
foreach ($logs as $log) {
if (!$this->isSilencedOrDeprecationErrorLog($log)) {
$log['context'] = $log['context'] ? $this->cloneVar($log['context']) : $log['context'];
$sanitizedLogs[] = $log;
continue;
}
$message = '_'.$log['message'];
$exception = $log['context']['exception'];
$errorId = md5("{$exception->getSeverity()}/{$exception->getLine()}/{$exception->getFile()}".($exception instanceof \Exception ? "\0".$exception->getMessage() : ''), true);
if ($exception instanceof SilencedErrorContext) {
if (isset($silencedLogs[$h = spl_object_hash($exception)])) {
continue;
}
$silencedLogs[$h] = true;
if (!isset($sanitizedLogs[$message])) {
$sanitizedLogs[$message] = $log + [
'errorCount' => 0,
'scream' => true,
];
}
$sanitizedLogs[$message]['errorCount'] += $exception->count;
continue;
}
$errorId = md5("{$exception->getSeverity()}/{$exception->getLine()}/{$exception->getFile()}\0{$message}", true);
if (isset($sanitizedLogs[$errorId])) {
++$sanitizedLogs[$errorId]['errorCount'];
} else {
$log['context'] = $log['context'] ? $this->cloneVar($log['context']) : $log['context'];
$log += array(
$log += [
'errorCount' => 1,
'scream' => $exception instanceof SilencedErrorContext,
);
'scream' => false,
];
$sanitizedLogs[$errorId] = $log;
}
@@ -138,45 +221,55 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
return true;
}
if ($exception instanceof \ErrorException && in_array($exception->getSeverity(), array(E_DEPRECATED, E_USER_DEPRECATED), true)) {
if ($exception instanceof \ErrorException && \in_array($exception->getSeverity(), [\E_DEPRECATED, \E_USER_DEPRECATED], true)) {
return true;
}
return false;
}
private function computeErrorsCount()
private function computeErrorsCount(array $containerDeprecationLogs)
{
$count = array(
$silencedLogs = [];
$count = [
'error_count' => $this->logger->countErrors(),
'deprecation_count' => 0,
'warning_count' => 0,
'scream_count' => 0,
'priorities' => array(),
);
'priorities' => [],
];
foreach ($this->logger->getLogs() as $log) {
if (isset($count['priorities'][$log['priority']])) {
++$count['priorities'][$log['priority']]['count'];
} else {
$count['priorities'][$log['priority']] = array(
$count['priorities'][$log['priority']] = [
'count' => 1,
'name' => $log['priorityName'],
);
];
}
if ('WARNING' === $log['priorityName']) {
++$count['warning_count'];
}
if ($this->isSilencedOrDeprecationErrorLog($log)) {
if ($log['context']['exception'] instanceof SilencedErrorContext) {
++$count['scream_count'];
$exception = $log['context']['exception'];
if ($exception instanceof SilencedErrorContext) {
if (isset($silencedLogs[$h = spl_object_hash($exception)])) {
continue;
}
$silencedLogs[$h] = true;
$count['scream_count'] += $exception->count;
} else {
++$count['deprecation_count'];
}
}
}
foreach ($containerDeprecationLogs as $deprecationLog) {
$count['deprecation_count'] += $deprecationLog['context']['exception']->count;
}
ksort($count['priorities']);
return $count;
@@ -23,10 +23,7 @@ class MemoryDataCollector extends DataCollector implements LateDataCollectorInte
{
public function __construct()
{
$this->data = array(
'memory' => 0,
'memory_limit' => $this->convertToBytes(ini_get('memory_limit')),
);
$this->reset();
}
/**
@@ -37,6 +34,17 @@ class MemoryDataCollector extends DataCollector implements LateDataCollectorInte
$this->updateMemoryUsage();
}
/**
* {@inheritdoc}
*/
public function reset()
{
$this->data = [
'memory' => 0,
'memory_limit' => $this->convertToBytes(ini_get('memory_limit')),
];
}
/**
* {@inheritdoc}
*/
@@ -90,17 +98,20 @@ class MemoryDataCollector extends DataCollector implements LateDataCollectorInte
$memoryLimit = strtolower($memoryLimit);
$max = strtolower(ltrim($memoryLimit, '+'));
if (0 === strpos($max, '0x')) {
$max = intval($max, 16);
$max = \intval($max, 16);
} elseif (0 === strpos($max, '0')) {
$max = intval($max, 8);
$max = \intval($max, 8);
} else {
$max = (int) $max;
}
switch (substr($memoryLimit, -1)) {
case 't': $max *= 1024;
// no break
case 'g': $max *= 1024;
// no break
case 'm': $max *= 1024;
// no break
case 'k': $max *= 1024;
}
@@ -11,22 +11,20 @@
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* RequestDataCollector.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class RequestDataCollector extends DataCollector implements EventSubscriberInterface
class RequestDataCollector extends DataCollector implements EventSubscriberInterface, LateDataCollectorInterface
{
/** @var \SplObjectStorage */
protected $controllers;
public function __construct()
@@ -39,24 +37,18 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
*/
public function collect(Request $request, Response $response, \Exception $exception = null)
{
$responseHeaders = $response->headers->all();
foreach ($response->headers->getCookies() as $cookie) {
$responseHeaders['set-cookie'][] = (string) $cookie;
}
// attributes are serialized and as they can be anything, they need to be converted to strings.
$attributes = array();
$attributes = [];
$route = '';
foreach ($request->attributes->all() as $key => $value) {
if ('_route' === $key) {
$route = is_object($value) ? $value->getPath() : $value;
$route = \is_object($value) ? $value->getPath() : $value;
$attributes[$key] = $route;
} else {
$attributes[$key] = $value;
}
}
$content = null;
try {
$content = $request->getContent();
} catch (\LogicException $e) {
@@ -64,15 +56,14 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
$content = false;
}
$sessionMetadata = array();
$sessionAttributes = array();
$session = null;
$flashes = array();
$sessionMetadata = [];
$sessionAttributes = [];
$flashes = [];
if ($request->hasSession()) {
$session = $request->getSession();
if ($session->isStarted()) {
$sessionMetadata['Created'] = date(DATE_RFC822, $session->getMetadataBag()->getCreated());
$sessionMetadata['Last used'] = date(DATE_RFC822, $session->getMetadataBag()->getLastUsed());
$sessionMetadata['Created'] = date(\DATE_RFC822, $session->getMetadataBag()->getCreated());
$sessionMetadata['Last used'] = date(\DATE_RFC822, $session->getMetadataBag()->getLastUsed());
$sessionMetadata['Lifetime'] = $session->getMetadataBag()->getLifetime();
$sessionAttributes = $session->all();
$flashes = $session->getFlashBag()->peekAll();
@@ -81,7 +72,12 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
$statusCode = $response->getStatusCode();
$this->data = array(
$responseCookies = [];
foreach ($response->headers->getCookies() as $cookie) {
$responseCookies[$cookie->getName()] = $cookie;
}
$this->data = [
'method' => $request->getMethod(),
'format' => $request->getRequestFormat(),
'content' => $content,
@@ -95,14 +91,15 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
'request_cookies' => $request->cookies->all(),
'request_attributes' => $attributes,
'route' => $route,
'response_headers' => $responseHeaders,
'response_headers' => $response->headers->all(),
'response_cookies' => $responseCookies,
'session_metadata' => $sessionMetadata,
'session_attributes' => $sessionAttributes,
'flashes' => $flashes,
'path_info' => $request->getPathInfo(),
'controller' => 'n/a',
'locale' => $request->getLocale(),
);
];
if (isset($this->data['request_headers']['php-auth-pw'])) {
$this->data['request_headers']['php-auth-pw'] = '******';
@@ -117,14 +114,11 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
}
foreach ($this->data as $key => $value) {
if (!is_array($value)) {
if (!\is_array($value)) {
continue;
}
if ('request_headers' === $key || 'response_headers' === $key) {
$value = array_map(function ($v) { return isset($v[0]) && !isset($v[1]) ? $v[0] : $v; }, $value);
}
if ('request_server' !== $key && 'request_cookies' !== $key) {
$this->data[$key] = array_map(array($this, 'cloneVar'), $value);
$this->data[$key] = array_map(function ($v) { return isset($v[0]) && !isset($v[1]) ? $v[0] : $v; }, $value);
}
}
@@ -133,22 +127,38 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
unset($this->controllers[$request]);
}
if (null !== $session && $session->isStarted()) {
if ($request->attributes->has('_redirected')) {
$this->data['redirect'] = $session->remove('sf_redirect');
}
if ($request->attributes->has('_redirected') && $redirectCookie = $request->cookies->get('sf_redirect')) {
$this->data['redirect'] = json_decode($redirectCookie, true);
if ($response->isRedirect()) {
$session->set('sf_redirect', array(
$response->headers->clearCookie('sf_redirect');
}
if ($response->isRedirect()) {
$response->headers->setCookie(new Cookie(
'sf_redirect',
json_encode([
'token' => $response->headers->get('x-debug-token'),
'route' => $request->attributes->get('_route', 'n/a'),
'method' => $request->getMethod(),
'controller' => $this->parseController($request->attributes->get('_controller')),
'status_code' => $statusCode,
'status_text' => Response::$statusTexts[(int) $statusCode],
));
}
])
));
}
$this->data['identifier'] = $this->data['route'] ?: (\is_array($this->data['controller']) ? $this->data['controller']['class'].'::'.$this->data['controller']['method'].'()' : $this->data['controller']);
}
public function lateCollect()
{
$this->data = $this->cloneVar($this->data);
}
public function reset()
{
$this->data = [];
$this->controllers = new \SplObjectStorage();
}
public function getMethod()
@@ -163,52 +173,57 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
public function getRequestRequest()
{
return new ParameterBag($this->data['request_request']);
return new ParameterBag($this->data['request_request']->getValue());
}
public function getRequestQuery()
{
return new ParameterBag($this->data['request_query']);
return new ParameterBag($this->data['request_query']->getValue());
}
public function getRequestHeaders()
{
return new ParameterBag($this->data['request_headers']);
return new ParameterBag($this->data['request_headers']->getValue());
}
public function getRequestServer($raw = false)
{
return new ParameterBag($raw ? $this->data['request_server'] : array_map(array($this, 'cloneVar'), $this->data['request_server']));
return new ParameterBag($this->data['request_server']->getValue($raw));
}
public function getRequestCookies($raw = false)
{
return new ParameterBag($raw ? $this->data['request_cookies'] : array_map(array($this, 'cloneVar'), $this->data['request_cookies']));
return new ParameterBag($this->data['request_cookies']->getValue($raw));
}
public function getRequestAttributes()
{
return new ParameterBag($this->data['request_attributes']);
return new ParameterBag($this->data['request_attributes']->getValue());
}
public function getResponseHeaders()
{
return new ParameterBag($this->data['response_headers']);
return new ParameterBag($this->data['response_headers']->getValue());
}
public function getResponseCookies()
{
return new ParameterBag($this->data['response_cookies']->getValue());
}
public function getSessionMetadata()
{
return $this->data['session_metadata'];
return $this->data['session_metadata']->getValue();
}
public function getSessionAttributes()
{
return $this->data['session_attributes'];
return $this->data['session_attributes']->getValue();
}
public function getFlashes()
{
return $this->data['flashes'];
return $this->data['flashes']->getValue();
}
public function getContent()
@@ -255,7 +270,7 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
public function getIdentifier()
{
return $this->data['route'] ?: (is_array($this->data['controller']) ? $this->data['controller']['class'].'::'.$this->data['controller']['method'].'()' : $this->data['controller']);
return $this->data['identifier'];
}
/**
@@ -267,22 +282,7 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
*/
public function getRouteParams()
{
if (!isset($this->data['request_attributes']['_route_params'])) {
return array();
}
$data = $this->data['request_attributes']['_route_params'];
$rawData = $data->getRawData();
if (!isset($rawData[1])) {
return array();
}
$params = array();
foreach ($rawData[1] as $k => $v) {
$params[$k] = $data->seek($k);
}
return $params;
return isset($this->data['request_attributes']['_route_params']) ? $this->data['request_attributes']['_route_params']->getValue() : [];
}
/**
@@ -314,21 +314,21 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
public function onKernelResponse(FilterResponseEvent $event)
{
if (!$event->isMasterRequest() || !$event->getRequest()->hasSession() || !$event->getRequest()->getSession()->isStarted()) {
if (!$event->isMasterRequest()) {
return;
}
if ($event->getRequest()->getSession()->has('sf_redirect')) {
if ($event->getRequest()->cookies->has('sf_redirect')) {
$event->getRequest()->attributes->set('_redirected', true);
}
}
public static function getSubscribedEvents()
{
return array(
return [
KernelEvents::CONTROLLER => 'onKernelController',
KernelEvents::RESPONSE => 'onKernelResponse',
);
];
}
/**
@@ -348,29 +348,29 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
*/
protected function parseController($controller)
{
if (is_string($controller) && false !== strpos($controller, '::')) {
if (\is_string($controller) && false !== strpos($controller, '::')) {
$controller = explode('::', $controller);
}
if (is_array($controller)) {
if (\is_array($controller)) {
try {
$r = new \ReflectionMethod($controller[0], $controller[1]);
return array(
'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0],
return [
'class' => \is_object($controller[0]) ? \get_class($controller[0]) : $controller[0],
'method' => $controller[1],
'file' => $r->getFileName(),
'line' => $r->getStartLine(),
);
];
} catch (\ReflectionException $e) {
if (is_callable($controller)) {
if (\is_callable($controller)) {
// using __call or __callStatic
return array(
'class' => is_object($controller[0]) ? get_class($controller[0]) : $controller[0],
return [
'class' => \is_object($controller[0]) ? \get_class($controller[0]) : $controller[0],
'method' => $controller[1],
'file' => 'n/a',
'line' => 'n/a',
);
];
}
}
}
@@ -378,25 +378,38 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
if ($controller instanceof \Closure) {
$r = new \ReflectionFunction($controller);
return array(
$controller = [
'class' => $r->getName(),
'method' => null,
'file' => $r->getFileName(),
'line' => $r->getStartLine(),
);
];
if (false !== strpos($r->name, '{closure}')) {
return $controller;
}
$controller['method'] = $r->name;
if ($class = $r->getClosureScopeClass()) {
$controller['class'] = $class->name;
} else {
return $r->name;
}
return $controller;
}
if (is_object($controller)) {
if (\is_object($controller)) {
$r = new \ReflectionClass($controller);
return array(
return [
'class' => $r->getName(),
'method' => null,
'file' => $r->getFileName(),
'line' => $r->getStartLine(),
);
];
}
return is_string($controller) ? $controller : 'n/a';
return \is_string($controller) ? $controller : 'n/a';
}
}
@@ -11,9 +11,9 @@
namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
/**
@@ -23,17 +23,14 @@ use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
*/
class RouterDataCollector extends DataCollector
{
/**
* @var \SplObjectStorage
*/
protected $controllers;
public function __construct()
{
$this->controllers = new \SplObjectStorage();
$this->data = array(
'redirect' => false,
'url' => null,
'route' => null,
);
$this->reset();
}
/**
@@ -53,6 +50,17 @@ class RouterDataCollector extends DataCollector
unset($this->controllers[$request]);
}
public function reset()
{
$this->controllers = new \SplObjectStorage();
$this->data = [
'redirect' => false,
'url' => null,
'route' => null,
];
}
protected function guessRoute(Request $request, $controller)
{
return 'n/a';
@@ -60,8 +68,6 @@ class RouterDataCollector extends DataCollector
/**
* Remembers the controller associated to each request.
*
* @param FilterControllerEvent $event The filter controller event
*/
public function onKernelController(FilterControllerEvent $event)
{
@@ -14,10 +14,10 @@ namespace Symfony\Component\HttpKernel\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Stopwatch\StopwatchEvent;
/**
* TimeDataCollector.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TimeDataCollector extends DataCollector implements LateDataCollectorInterface
@@ -25,7 +25,7 @@ class TimeDataCollector extends DataCollector implements LateDataCollectorInterf
protected $kernel;
protected $stopwatch;
public function __construct(KernelInterface $kernel = null, $stopwatch = null)
public function __construct(KernelInterface $kernel = null, Stopwatch $stopwatch = null)
{
$this->kernel = $kernel;
$this->stopwatch = $stopwatch;
@@ -39,14 +39,27 @@ class TimeDataCollector extends DataCollector implements LateDataCollectorInterf
if (null !== $this->kernel) {
$startTime = $this->kernel->getStartTime();
} else {
$startTime = $request->server->get('REQUEST_TIME_FLOAT', $request->server->get('REQUEST_TIME'));
$startTime = $request->server->get('REQUEST_TIME_FLOAT');
}
$this->data = array(
$this->data = [
'token' => $response->headers->get('X-Debug-Token'),
'start_time' => $startTime * 1000,
'events' => array(),
);
'events' => [],
'stopwatch_installed' => class_exists(Stopwatch::class, false),
];
}
/**
* {@inheritdoc}
*/
public function reset()
{
$this->data = [];
if (null !== $this->stopwatch) {
$this->stopwatch->reset();
}
}
/**
@@ -63,7 +76,7 @@ class TimeDataCollector extends DataCollector implements LateDataCollectorInterf
/**
* Sets the request events.
*
* @param array $events The request events
* @param StopwatchEvent[] $events The request events
*/
public function setEvents(array $events)
{
@@ -77,7 +90,7 @@ class TimeDataCollector extends DataCollector implements LateDataCollectorInterf
/**
* Gets the request events.
*
* @return array The request events
* @return StopwatchEvent[] The request events
*/
public function getEvents()
{
@@ -119,13 +132,21 @@ class TimeDataCollector extends DataCollector implements LateDataCollectorInterf
/**
* Gets the request time.
*
* @return int The time
* @return float
*/
public function getStartTime()
{
return $this->data['start_time'];
}
/**
* @return bool whether or not the stopwatch component is installed
*/
public function isStopwatchInstalled()
{
return $this->data['stopwatch_installed'];
}
/**
* {@inheritdoc}
*/
@@ -11,7 +11,7 @@
namespace Symfony\Component\HttpKernel\DataCollector\Util;
@trigger_error('The '.__NAMESPACE__.'\ValueExporter class is deprecated since version 3.2 and will be removed in 4.0. Use the VarDumper component instead.', E_USER_DEPRECATED);
@trigger_error('The '.__NAMESPACE__.'\ValueExporter class is deprecated since Symfony 3.2 and will be removed in 4.0. Use the VarDumper component instead.', \E_USER_DEPRECATED);
/**
* @author Bernhard Schussek <bschussek@gmail.com>
@@ -24,8 +24,8 @@ class ValueExporter
* Converts a PHP value to a string.
*
* @param mixed $value The PHP value
* @param int $depth only for internal usage
* @param bool $deep only for internal usage
* @param int $depth Only for internal usage
* @param bool $deep Only for internal usage
*
* @return string The string representation of the given value
*/
@@ -35,24 +35,24 @@ class ValueExporter
return sprintf('__PHP_Incomplete_Class(%s)', $this->getClassNameFromIncomplete($value));
}
if (is_object($value)) {
if (\is_object($value)) {
if ($value instanceof \DateTimeInterface) {
return sprintf('Object(%s) - %s', get_class($value), $value->format(\DateTime::ATOM));
return sprintf('Object(%s) - %s', \get_class($value), $value->format(\DateTime::ATOM));
}
return sprintf('Object(%s)', get_class($value));
return sprintf('Object(%s)', \get_class($value));
}
if (is_array($value)) {
if (\is_array($value)) {
if (empty($value)) {
return '[]';
}
$indent = str_repeat(' ', $depth);
$a = array();
$a = [];
foreach ($value as $k => $v) {
if (is_array($v)) {
if (\is_array($v)) {
$deep = true;
}
$a[] = sprintf('%s => %s', $k, $this->exportValue($v, $depth + 1, $deep));
@@ -64,14 +64,14 @@ class ValueExporter
$s = sprintf('[%s]', implode(', ', $a));
if (80 > strlen($s)) {
if (80 > \strlen($s)) {
return $s;
}
return sprintf("[\n%s%s\n]", $indent, implode(sprintf(",\n%s", $indent), $a));
}
if (is_resource($value)) {
if (\is_resource($value)) {
return sprintf('Resource(%s#%d)', get_resource_type($value), $value);
}
+43 -10
View File
@@ -13,6 +13,8 @@ namespace Symfony\Component\HttpKernel\Debug;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Exception\ExceptionInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Formats debug file links.
@@ -26,12 +28,15 @@ class FileLinkFormatter implements \Serializable
private $baseDir;
private $urlFormat;
/**
* @param string|\Closure $urlFormat the URL format, or a closure that returns it on-demand
*/
public function __construct($fileLinkFormat = null, RequestStack $requestStack = null, $baseDir = null, $urlFormat = null)
{
$fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
if ($fileLinkFormat && !is_array($fileLinkFormat)) {
$i = strpos($f = $fileLinkFormat, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: strlen($f);
$fileLinkFormat = array(substr($f, 0, $i)) + preg_split('/&([^>]++)>/', substr($f, $i), -1, PREG_SPLIT_DELIM_CAPTURE);
if ($fileLinkFormat && !\is_array($fileLinkFormat)) {
$i = strpos($f = $fileLinkFormat, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: \strlen($f);
$fileLinkFormat = [substr($f, 0, $i)] + preg_split('/&([^>]++)>/', substr($f, $i), -1, \PREG_SPLIT_DELIM_CAPTURE);
}
$this->fileLinkFormat = $fileLinkFormat;
@@ -45,25 +50,47 @@ class FileLinkFormatter implements \Serializable
if ($fmt = $this->getFileLinkFormat()) {
for ($i = 1; isset($fmt[$i]); ++$i) {
if (0 === strpos($file, $k = $fmt[$i++])) {
$file = substr_replace($file, $fmt[$i], 0, strlen($k));
$file = substr_replace($file, $fmt[$i], 0, \strlen($k));
break;
}
}
return strtr($fmt[0], array('%f' => $file, '%l' => $line));
return strtr($fmt[0], ['%f' => $file, '%l' => $line]);
}
return false;
}
/**
* @internal
*/
public function serialize()
{
return serialize($this->getFileLinkFormat());
}
/**
* @internal
*/
public function unserialize($serialized)
{
$this->fileLinkFormat = unserialize($serialized);
if (\PHP_VERSION_ID >= 70000) {
$this->fileLinkFormat = unserialize($serialized, ['allowed_classes' => false]);
} else {
$this->fileLinkFormat = unserialize($serialized);
}
}
/**
* @internal
*/
public static function generateUrlFormat(UrlGeneratorInterface $router, $routeName, $queryString)
{
try {
return $router->generate($routeName).$queryString;
} catch (ExceptionInterface $e) {
return null;
}
}
private function getFileLinkFormat()
@@ -74,11 +101,17 @@ class FileLinkFormatter implements \Serializable
if ($this->requestStack && $this->baseDir && $this->urlFormat) {
$request = $this->requestStack->getMasterRequest();
if ($request instanceof Request) {
return array(
$request->getSchemeAndHttpHost().$request->getBaseUrl().$this->urlFormat,
$this->baseDir.DIRECTORY_SEPARATOR, '',
);
if ($this->urlFormat instanceof \Closure && !$this->urlFormat = \call_user_func($this->urlFormat)) {
return null;
}
return [
$request->getSchemeAndHttpHost().$this->urlFormat,
$this->baseDir.\DIRECTORY_SEPARATOR, '',
];
}
}
return null;
}
}
@@ -12,8 +12,8 @@
namespace Symfony\Component\HttpKernel\Debug;
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher as BaseTraceableEventDispatcher;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Collects some data about event listeners.
@@ -42,6 +42,9 @@ class TraceableEventDispatcher extends BaseTraceableEventDispatcher
break;
case KernelEvents::TERMINATE:
$token = $event->getResponse()->headers->get('X-Debug-Token');
if (null === $token) {
break;
}
// There is a very special case when using built-in AppCache class as kernel wrapper, in the case
// of an ESI request leading to a `stale` response [B] inside a `fresh` cached response [A].
// In this case, `$token` contains the [B] debug token, but the open `stopwatch` section ID
@@ -66,12 +69,18 @@ class TraceableEventDispatcher extends BaseTraceableEventDispatcher
break;
case KernelEvents::RESPONSE:
$token = $event->getResponse()->headers->get('X-Debug-Token');
if (null === $token) {
break;
}
$this->stopwatch->stopSection($token);
break;
case KernelEvents::TERMINATE:
// In the special case described in the `preDispatch` method above, the `$token` section
// does not exist, then closing it throws an exception which must be caught.
$token = $event->getResponse()->headers->get('X-Debug-Token');
if (null === $token) {
break;
}
try {
$this->stopwatch->stopSection($token);
} catch (\LogicException $e) {
@@ -11,139 +11,15 @@
namespace Symfony\Component\HttpKernel\DependencyInjection;
use Composer\Autoload\ClassLoader;
use Symfony\Component\Debug\DebugClassLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\HttpKernel\Kernel;
@trigger_error('The '.__NAMESPACE__.'\AddClassesToCachePass class is deprecated since Symfony 3.3 and will be removed in 4.0.', \E_USER_DEPRECATED);
/**
* Sets the classes to compile in the cache for the container.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since version 3.3, to be removed in 4.0.
*/
class AddClassesToCachePass implements CompilerPassInterface
class AddClassesToCachePass extends AddAnnotatedClassesToCachePass
{
private $kernel;
public function __construct(Kernel $kernel)
{
$this->kernel = $kernel;
}
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
$classes = array();
$annotatedClasses = array();
foreach ($container->getExtensions() as $extension) {
if ($extension instanceof Extension) {
$classes = array_merge($classes, $extension->getClassesToCompile());
$annotatedClasses = array_merge($annotatedClasses, $extension->getAnnotatedClassesToCompile());
}
}
$classes = $container->getParameterBag()->resolveValue($classes);
$annotatedClasses = $container->getParameterBag()->resolveValue($annotatedClasses);
$existingClasses = $this->getClassesInComposerClassMaps();
$this->kernel->setClassCache($this->expandClasses($classes, $existingClasses));
$this->kernel->setAnnotatedClassCache($this->expandClasses($annotatedClasses, $existingClasses));
}
/**
* Expands the given class patterns using a list of existing classes.
*
* @param array $patterns The class patterns to expand
* @param array $classes The existing classes to match against the patterns
*
* @return array A list of classes derivated from the patterns
*/
private function expandClasses(array $patterns, array $classes)
{
$expanded = array();
// Explicit classes declared in the patterns are returned directly
foreach ($patterns as $key => $pattern) {
if (substr($pattern, -1) !== '\\' && false === strpos($pattern, '*')) {
unset($patterns[$key]);
$expanded[] = ltrim($pattern, '\\');
}
}
// Match patterns with the classes list
$regexps = $this->patternsToRegexps($patterns);
foreach ($classes as $class) {
$class = ltrim($class, '\\');
if ($this->matchAnyRegexps($class, $regexps)) {
$expanded[] = $class;
}
}
return array_unique($expanded);
}
private function getClassesInComposerClassMaps()
{
$classes = array();
foreach (spl_autoload_functions() as $function) {
if (!is_array($function)) {
continue;
}
if ($function[0] instanceof DebugClassLoader) {
$function = $function[0]->getClassLoader();
}
if (is_array($function) && $function[0] instanceof ClassLoader) {
$classes += array_filter($function[0]->getClassMap());
}
}
return array_keys($classes);
}
private function patternsToRegexps($patterns)
{
$regexps = array();
foreach ($patterns as $pattern) {
// Escape user input
$regex = preg_quote(ltrim($pattern, '\\'));
// Wildcards * and **
$regex = strtr($regex, array('\\*\\*' => '.*?', '\\*' => '[^\\\\]*?'));
// If this class does not end by a slash, anchor the end
if (substr($regex, -1) !== '\\') {
$regex .= '$';
}
$regexps[] = '{^\\\\'.$regex.'}';
}
return $regexps;
}
private function matchAnyRegexps($class, $regexps)
{
$blacklisted = false !== strpos($class, 'Test');
foreach ($regexps as $regex) {
if ($blacklisted && false === strpos($regex, 'Test')) {
continue;
}
if (preg_match($regex, '\\'.$class)) {
return true;
}
}
return false;
}
}
@@ -37,9 +37,6 @@ abstract class ConfigurableExtension extends Extension
/**
* Configures the passed container according to the merged configuration.
*
* @param array $mergedConfig
* @param ContainerBuilder $container
*/
abstract protected function loadInternal(array $mergedConfig, ContainerBuilder $container);
}
@@ -20,16 +20,22 @@ use Symfony\Component\DependencyInjection\Extension\Extension as BaseExtension;
*/
abstract class Extension extends BaseExtension
{
private $classes = array();
private $annotatedClasses = array();
private $classes = [];
private $annotatedClasses = [];
/**
* Gets the classes to cache.
*
* @return array An array of classes
*
* @deprecated since version 3.3, to be removed in 4.0.
*/
public function getClassesToCompile()
{
if (\PHP_VERSION_ID >= 70000) {
@trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', \E_USER_DEPRECATED);
}
return $this->classes;
}
@@ -47,9 +53,15 @@ abstract class Extension extends BaseExtension
* Adds classes to the class cache.
*
* @param array $classes An array of class patterns
*
* @deprecated since version 3.3, to be removed in 4.0.
*/
public function addClassesToCompile(array $classes)
{
if (\PHP_VERSION_ID >= 70000) {
@trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', \E_USER_DEPRECATED);
}
$this->classes = array_merge($this->classes, $classes);
}
@@ -11,9 +11,12 @@
namespace Symfony\Component\HttpKernel\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface;
/**
* Adds services tagged kernel.fragment_renderer as HTTP content rendering strategies.
@@ -42,30 +45,23 @@ class FragmentRendererPass implements CompilerPassInterface
}
$definition = $container->getDefinition($this->handlerService);
foreach ($container->findTaggedServiceIds($this->rendererTag) as $id => $tags) {
$renderers = [];
foreach ($container->findTaggedServiceIds($this->rendererTag, true) as $id => $tags) {
$def = $container->getDefinition($id);
if (!$def->isPublic()) {
throw new InvalidArgumentException(sprintf('The service "%s" must be public as fragment renderer are lazy-loaded.', $id));
}
if ($def->isAbstract()) {
throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as fragment renderer are lazy-loaded.', $id));
}
$class = $container->getParameterBag()->resolveValue($def->getClass());
$interface = 'Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface';
if (!is_subclass_of($class, $interface)) {
if (!class_exists($class, false)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
if (!$r = $container->getReflectionClass($class)) {
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
}
if (!$r->isSubclassOf(FragmentRendererInterface::class)) {
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, FragmentRendererInterface::class));
}
foreach ($tags as $tag) {
$definition->addMethodCall('addRendererService', array($tag['alias'], $id));
$renderers[$tag['alias']] = new Reference($id);
}
}
$definition->replaceArgument(0, ServiceLocatorTagPass::register($container, $renderers));
}
}
@@ -11,7 +11,7 @@
namespace Symfony\Component\HttpKernel\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Fragment\FragmentHandler;
@@ -23,11 +23,13 @@ use Symfony\Component\HttpKernel\Fragment\FragmentHandler;
class LazyLoadingFragmentHandler extends FragmentHandler
{
private $container;
private $rendererIds = array();
/**
* @deprecated since version 3.3, to be removed in 4.0
*/
private $rendererIds = [];
private $initialized = [];
/**
* Constructor.
*
* @param ContainerInterface $container A container
* @param RequestStack $requestStack The Request stack that controls the lifecycle of requests
* @param bool $debug Whether the debug mode is enabled or not
@@ -36,7 +38,7 @@ class LazyLoadingFragmentHandler extends FragmentHandler
{
$this->container = $container;
parent::__construct($requestStack, array(), $debug);
parent::__construct($requestStack, [], $debug);
}
/**
@@ -44,21 +46,32 @@ class LazyLoadingFragmentHandler extends FragmentHandler
*
* @param string $name The service name
* @param string $renderer The render service id
*
* @deprecated since version 3.3, to be removed in 4.0
*/
public function addRendererService($name, $renderer)
{
@trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0.', __METHOD__), \E_USER_DEPRECATED);
$this->rendererIds[$name] = $renderer;
}
/**
* {@inheritdoc}
*/
public function render($uri, $renderer = 'inline', array $options = array())
public function render($uri, $renderer = 'inline', array $options = [])
{
// BC 3.x, to be removed in 4.0
if (isset($this->rendererIds[$renderer])) {
$this->addRenderer($this->container->get($this->rendererIds[$renderer]));
unset($this->rendererIds[$renderer]);
return parent::render($uri, $renderer, $options);
}
if (!isset($this->initialized[$renderer]) && $this->container->has($renderer)) {
$this->addRenderer($this->container->get($renderer));
$this->initialized[$renderer] = true;
}
return parent::render($uri, $renderer, $options);
@@ -31,8 +31,8 @@ class MergeExtensionConfigurationPass extends BaseMergeExtensionConfigurationPas
public function process(ContainerBuilder $container)
{
foreach ($this->extensions as $extension) {
if (!count($container->getExtensionConfig($extension))) {
$container->loadFromExtension($extension, array());
if (!\count($container->getExtensionConfig($extension))) {
$container->loadFromExtension($extension, []);
}
}
@@ -11,8 +11,8 @@
namespace Symfony\Component\HttpKernel\Event;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Allows filtering of controller arguments.
@@ -45,9 +45,6 @@ class FilterControllerArgumentsEvent extends FilterControllerEvent
return $this->arguments;
}
/**
* @param array $arguments
*/
public function setArguments(array $arguments)
{
$this->arguments = $arguments;
+1 -9
View File
@@ -11,8 +11,8 @@
namespace Symfony\Component\HttpKernel\Event;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Allows filtering of a controller callable.
@@ -27,9 +27,6 @@ use Symfony\Component\HttpFoundation\Request;
*/
class FilterControllerEvent extends KernelEvent
{
/**
* The current controller.
*/
private $controller;
public function __construct(HttpKernelInterface $kernel, callable $controller, Request $request, $requestType)
@@ -49,11 +46,6 @@ class FilterControllerEvent extends KernelEvent
return $this->controller;
}
/**
* Sets a new controller.
*
* @param callable $controller
*/
public function setController(callable $controller)
{
$this->controller = $controller;
+1 -8
View File
@@ -11,9 +11,9 @@
namespace Symfony\Component\HttpKernel\Event;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Allows to filter a Response object.
@@ -26,11 +26,6 @@ use Symfony\Component\HttpFoundation\Response;
*/
class FilterResponseEvent extends KernelEvent
{
/**
* The current response object.
*
* @var Response
*/
private $response;
public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, Response $response)
@@ -52,8 +47,6 @@ class FilterResponseEvent extends KernelEvent
/**
* Sets a new response object.
*
* @param Response $response
*/
public function setResponse(Response $response)
{
+1 -8
View File
@@ -24,17 +24,12 @@ use Symfony\Component\HttpFoundation\Response;
*/
class GetResponseEvent extends KernelEvent
{
/**
* The response object.
*
* @var Response
*/
private $response;
/**
* Returns the response object.
*
* @return Response
* @return Response|null
*/
public function getResponse()
{
@@ -43,8 +38,6 @@ class GetResponseEvent extends KernelEvent
/**
* Sets a response and stops event propagation.
*
* @param Response $response
*/
public function setResponse(Response $response)
{
@@ -11,8 +11,8 @@
namespace Symfony\Component\HttpKernel\Event;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Allows to create a response for the return value of a controller.
@@ -11,8 +11,8 @@
namespace Symfony\Component\HttpKernel\Event;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Allows to create a response for a thrown exception.
@@ -36,6 +36,11 @@ class GetResponseForExceptionEvent extends GetResponseEvent
*/
private $exception;
/**
* @var bool
*/
private $allowCustomResponseCode = false;
public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, \Exception $e)
{
parent::__construct($kernel, $request, $requestType);
@@ -64,4 +69,22 @@ class GetResponseForExceptionEvent extends GetResponseEvent
{
$this->exception = $exception;
}
/**
* Mark the event as allowing a custom response code.
*/
public function allowCustomResponseCode()
{
$this->allowCustomResponseCode = true;
}
/**
* Returns true if the event allows a custom response code.
*
* @return bool
*/
public function isAllowingCustomResponseCode()
{
return $this->allowCustomResponseCode;
}
}
+8 -20
View File
@@ -11,9 +11,9 @@
namespace Symfony\Component\HttpKernel\Event;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Base class for events thrown in the HttpKernel component.
@@ -22,28 +22,16 @@ use Symfony\Component\EventDispatcher\Event;
*/
class KernelEvent extends Event
{
/**
* The kernel in which this event was thrown.
*
* @var HttpKernelInterface
*/
private $kernel;
/**
* The request the kernel is currently processing.
*
* @var Request
*/
private $request;
/**
* The request type the kernel is currently processing. One of
* HttpKernelInterface::MASTER_REQUEST and HttpKernelInterface::SUB_REQUEST.
*
* @var int
*/
private $requestType;
/**
* @param HttpKernelInterface $kernel The kernel in which this event was thrown
* @param Request $request The request the kernel is currently processing
* @param int $requestType The request type the kernel is currently processing; one of
* HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST
*/
public function __construct(HttpKernelInterface $kernel, Request $request, $requestType)
{
$this->kernel = $kernel;
+1 -1
View File
@@ -11,9 +11,9 @@
namespace Symfony\Component\HttpKernel\Event;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* Allows to execute logic after a response was sent.
@@ -12,8 +12,8 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Adds configured formats to each request.
@@ -22,14 +22,8 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent;
*/
class AddRequestFormatsListener implements EventSubscriberInterface
{
/**
* @var array
*/
protected $formats;
/**
* @param array $formats
*/
public function __construct(array $formats)
{
$this->formats = $formats;
@@ -37,13 +31,12 @@ class AddRequestFormatsListener implements EventSubscriberInterface
/**
* Adds request formats.
*
* @param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
foreach ($this->formats as $format => $mimeTypes) {
$event->getRequest()->setFormat($format, $mimeTypes);
$request->setFormat($format, $mimeTypes);
}
}
@@ -52,6 +45,6 @@ class AddRequestFormatsListener implements EventSubscriberInterface
*/
public static function getSubscribedEvents()
{
return array(KernelEvents::REQUEST => array('onKernelRequest', 1));
return [KernelEvents::REQUEST => ['onKernelRequest', 1]];
}
}
@@ -12,15 +12,16 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\ExceptionHandler;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Debug\FileLinkFormatter;
use Symfony\Component\HttpKernel\Event\KernelEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleEvent;
use Symfony\Component\Console\Output\ConsoleOutputInterface;
/**
* Configures errors and exceptions handlers.
@@ -37,22 +38,23 @@ class DebugHandlersListener implements EventSubscriberInterface
private $fileLinkFormat;
private $scope;
private $firstCall = true;
private $hasTerminatedWithException;
/**
* @param callable|null $exceptionHandler A handler that will be called on Exception
* @param LoggerInterface|null $logger A PSR-3 logger
* @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants
* @param int|null $throwAt Thrown errors in a bit field of E_* constants, or null to keep the current value
* @param bool $scream Enables/disables screaming mode, where even silenced errors are logged
* @param string|array $fileLinkFormat The format for links to source files
* @param bool $scope Enables/disables scoping mode
* @param callable|null $exceptionHandler A handler that will be called on Exception
* @param LoggerInterface|null $logger A PSR-3 logger
* @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants
* @param int|null $throwAt Thrown errors in a bit field of E_* constants, or null to keep the current value
* @param bool $scream Enables/disables screaming mode, where even silenced errors are logged
* @param string|FileLinkFormatter|null $fileLinkFormat The format for links to source files
* @param bool $scope Enables/disables scoping mode
*/
public function __construct(callable $exceptionHandler = null, LoggerInterface $logger = null, $levels = E_ALL, $throwAt = E_ALL, $scream = true, $fileLinkFormat = null, $scope = true)
public function __construct(callable $exceptionHandler = null, LoggerInterface $logger = null, $levels = \E_ALL, $throwAt = \E_ALL, $scream = true, $fileLinkFormat = null, $scope = true)
{
$this->exceptionHandler = $exceptionHandler;
$this->logger = $logger;
$this->levels = null === $levels ? E_ALL : $levels;
$this->throwAt = is_numeric($throwAt) ? (int) $throwAt : (null === $throwAt ? null : ($throwAt ? E_ALL : null));
$this->levels = null === $levels ? \E_ALL : $levels;
$this->throwAt = is_numeric($throwAt) ? (int) $throwAt : (null === $throwAt ? null : ($throwAt ? \E_ALL : null));
$this->scream = (bool) $scream;
$this->fileLinkFormat = $fileLinkFormat;
$this->scope = (bool) $scope;
@@ -60,23 +62,26 @@ class DebugHandlersListener implements EventSubscriberInterface
/**
* Configures the error handler.
*
* @param Event|null $event The triggering event
*/
public function configure(Event $event = null)
{
if (!$this->firstCall) {
if ($event instanceof ConsoleEvent && !\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
return;
}
$this->firstCall = false;
if (!$event instanceof KernelEvent ? !$this->firstCall : !$event->isMasterRequest()) {
return;
}
$this->firstCall = $this->hasTerminatedWithException = false;
$handler = set_exception_handler('var_dump');
$handler = \is_array($handler) ? $handler[0] : null;
restore_exception_handler();
if ($this->logger || null !== $this->throwAt) {
$handler = set_error_handler('var_dump');
$handler = is_array($handler) ? $handler[0] : null;
restore_error_handler();
if ($handler instanceof ErrorHandler) {
if ($this->logger) {
$handler->setDefaultLogger($this->logger, $this->levels);
if (is_array($this->levels)) {
if (\is_array($this->levels)) {
$levels = 0;
foreach ($this->levels as $type => $log) {
$levels |= $type;
@@ -88,7 +93,7 @@ class DebugHandlersListener implements EventSubscriberInterface
$handler->screamAt($levels);
}
if ($this->scope) {
$handler->scopeAt($this->levels);
$handler->scopeAt($levels & ~\E_USER_DEPRECATED & ~\E_DEPRECATED);
} else {
$handler->scopeAt(0, true);
}
@@ -101,8 +106,16 @@ class DebugHandlersListener implements EventSubscriberInterface
}
if (!$this->exceptionHandler) {
if ($event instanceof KernelEvent) {
if (method_exists($event->getKernel(), 'terminateWithException')) {
$this->exceptionHandler = array($event->getKernel(), 'terminateWithException');
if (method_exists($kernel = $event->getKernel(), 'terminateWithException')) {
$request = $event->getRequest();
$hasRun = &$this->hasTerminatedWithException;
$this->exceptionHandler = static function (\Exception $e) use ($kernel, $request, &$hasRun) {
if ($hasRun) {
throw $e;
}
$hasRun = true;
$kernel->terminateWithException($e, $request);
};
}
} elseif ($event instanceof ConsoleEvent && $app = $event->getCommand()->getApplication()) {
$output = $event->getOutput();
@@ -115,13 +128,14 @@ class DebugHandlersListener implements EventSubscriberInterface
}
}
if ($this->exceptionHandler) {
$handler = set_exception_handler('var_dump');
$handler = is_array($handler) ? $handler[0] : null;
restore_exception_handler();
if ($handler instanceof ErrorHandler) {
$h = $handler->setExceptionHandler('var_dump') ?: $this->exceptionHandler;
$handler->setExceptionHandler($h);
$handler = is_array($h) ? $h[0] : null;
$h = $handler->setExceptionHandler('var_dump');
if (\is_array($h) && $h[0] instanceof ExceptionHandler) {
$handler->setExceptionHandler($h);
$handler = $h[0];
} else {
$handler->setExceptionHandler($this->exceptionHandler);
}
}
if ($handler instanceof ExceptionHandler) {
$handler->setHandler($this->exceptionHandler);
@@ -135,10 +149,10 @@ class DebugHandlersListener implements EventSubscriberInterface
public static function getSubscribedEvents()
{
$events = array(KernelEvents::REQUEST => array('configure', 2048));
$events = [KernelEvents::REQUEST => ['configure', 2048]];
if ('cli' === PHP_SAPI && defined('Symfony\Component\Console\ConsoleEvents::COMMAND')) {
$events[ConsoleEvents::COMMAND] = array('configure', 2048);
if (\defined('Symfony\Component\Console\ConsoleEvents::COMMAND')) {
$events[ConsoleEvents::COMMAND] = ['configure', 2048];
}
return $events;
+2 -6
View File
@@ -27,10 +27,6 @@ class DumpListener implements EventSubscriberInterface
private $cloner;
private $dumper;
/**
* @param ClonerInterface $cloner Cloner service
* @param DataDumperInterface $dumper Dumper service
*/
public function __construct(ClonerInterface $cloner, DataDumperInterface $dumper)
{
$this->cloner = $cloner;
@@ -50,10 +46,10 @@ class DumpListener implements EventSubscriberInterface
public static function getSubscribedEvents()
{
if (!class_exists(ConsoleEvents::class)) {
return array();
return [];
}
// Register early to have a working dump() as early as possible
return array(ConsoleEvents::COMMAND => array('configure', 1024));
return [ConsoleEvents::COMMAND => ['configure', 1024]];
}
}
@@ -13,13 +13,15 @@ namespace Symfony\Component\HttpKernel\EventListener;
use Psr\Log\LoggerInterface;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
/**
* ExceptionListener.
@@ -30,36 +32,38 @@ class ExceptionListener implements EventSubscriberInterface
{
protected $controller;
protected $logger;
protected $debug;
public function __construct($controller, LoggerInterface $logger = null)
public function __construct($controller, LoggerInterface $logger = null, $debug = false)
{
$this->controller = $controller;
$this->logger = $logger;
$this->debug = $debug;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
$request = $event->getRequest();
$eventDispatcher = \func_num_args() > 2 ? func_get_arg(2) : null;
$this->logException($exception, sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine()));
$this->logException($exception, sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', \get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine()));
$request = $this->duplicateRequest($exception, $request);
try {
$response = $event->getKernel()->handle($request, HttpKernelInterface::SUB_REQUEST, false);
} catch (\Exception $e) {
$this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()));
$this->logException($e, sprintf('Exception thrown when handling an exception (%s: %s at %s line %s)', \get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()));
$wrapper = $e;
while ($prev = $wrapper->getPrevious()) {
$prev = $e;
do {
if ($exception === $wrapper = $prev) {
throw $e;
}
}
} while ($prev = $wrapper->getPrevious());
$prev = new \ReflectionProperty('Exception', 'previous');
$prev = new \ReflectionProperty($wrapper instanceof \Exception ? \Exception::class : \Error::class, 'previous');
$prev->setAccessible(true);
$prev->setValue($wrapper, $exception);
@@ -67,13 +71,21 @@ class ExceptionListener implements EventSubscriberInterface
}
$event->setResponse($response);
if ($this->debug && $eventDispatcher instanceof EventDispatcherInterface) {
$cspRemovalListener = function (FilterResponseEvent $event) use (&$cspRemovalListener, $eventDispatcher) {
$event->getResponse()->headers->remove('Content-Security-Policy');
$eventDispatcher->removeListener(KernelEvents::RESPONSE, $cspRemovalListener);
};
$eventDispatcher->addListener(KernelEvents::RESPONSE, $cspRemovalListener, -128);
}
}
public static function getSubscribedEvents()
{
return array(
KernelEvents::EXCEPTION => array('onKernelException', -128),
);
return [
KernelEvents::EXCEPTION => ['onKernelException', -128],
];
}
/**
@@ -86,9 +98,9 @@ class ExceptionListener implements EventSubscriberInterface
{
if (null !== $this->logger) {
if (!$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500) {
$this->logger->critical($message, array('exception' => $exception));
$this->logger->critical($message, ['exception' => $exception]);
} else {
$this->logger->error($message, array('exception' => $exception));
$this->logger->error($message, ['exception' => $exception]);
}
}
}
@@ -99,15 +111,15 @@ class ExceptionListener implements EventSubscriberInterface
* @param \Exception $exception The thrown exception
* @param Request $request The original request
*
* @return Request $request The cloned request
* @return Request The cloned request
*/
protected function duplicateRequest(\Exception $exception, Request $request)
{
$attributes = array(
$attributes = [
'_controller' => $this->controller,
'exception' => FlattenException::create($exception),
'logger' => $this->logger instanceof DebugLoggerInterface ? $this->logger : null,
);
];
$request = $request->duplicate(null, null, $attributes);
$request->setMethod('GET');
@@ -11,12 +11,12 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\UriSigner;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Handles content fragments represented by special URIs.
@@ -24,7 +24,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
* All URL paths starting with /_fragment are handled as
* content fragments by this listener.
*
* If throws an AccessDeniedHttpException exception if the request
* Throws an AccessDeniedHttpException exception if the request
* is not signed or if it is not an internal sub-request.
*
* @author Fabien Potencier <fabien@symfony.com>
@@ -35,8 +35,6 @@ class FragmentListener implements EventSubscriberInterface
private $fragmentPath;
/**
* Constructor.
*
* @param UriSigner $signer A UriSigner instance
* @param string $fragmentPath The path that triggers this listener
*/
@@ -49,9 +47,7 @@ class FragmentListener implements EventSubscriberInterface
/**
* Fixes request attributes when the path is '/_fragment'.
*
* @param GetResponseEvent $event A GetResponseEvent instance
*
* @throws AccessDeniedHttpException if the request does not come from a trusted IP.
* @throws AccessDeniedHttpException if the request does not come from a trusted IP
*/
public function onKernelRequest(GetResponseEvent $event)
{
@@ -74,7 +70,7 @@ class FragmentListener implements EventSubscriberInterface
parse_str($request->query->get('_path', ''), $attributes);
$request->attributes->add($attributes);
$request->attributes->set('_route_params', array_replace($request->attributes->get('_route_params', array()), $attributes));
$request->attributes->set('_route_params', array_replace($request->attributes->get('_route_params', []), $attributes));
$request->query->remove('_path');
}
@@ -96,8 +92,8 @@ class FragmentListener implements EventSubscriberInterface
public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => array(array('onKernelRequest', 48)),
);
return [
KernelEvents::REQUEST => [['onKernelRequest', 48]],
];
}
}
+10 -12
View File
@@ -11,13 +11,13 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RequestContextAwareInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\RequestContextAwareInterface;
/**
* Initializes the locale based on the current request.
@@ -31,8 +31,6 @@ class LocaleListener implements EventSubscriberInterface
private $requestStack;
/**
* Constructor.
*
* @param RequestStack $requestStack A RequestStack instance
* @param string $defaultLocale The default locale
* @param RequestContextAwareInterface|null $router The router
@@ -76,10 +74,10 @@ class LocaleListener implements EventSubscriberInterface
public static function getSubscribedEvents()
{
return array(
return [
// must be registered after the Router to have access to the _locale
KernelEvents::REQUEST => array(array('onKernelRequest', 16)),
KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)),
);
KernelEvents::REQUEST => [['onKernelRequest', 16]],
KernelEvents::FINISH_REQUEST => [['onKernelFinishRequest', 0]],
];
}
}
+11 -18
View File
@@ -11,14 +11,14 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Profiler\Profiler;
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* ProfilerListener collects data for the current request by listening to the kernel events.
@@ -37,13 +37,11 @@ class ProfilerListener implements EventSubscriberInterface
protected $parents;
/**
* Constructor.
*
* @param Profiler $profiler A Profiler instance
* @param RequestStack $requestStack A RequestStack instance
* @param RequestMatcherInterface|null $matcher A RequestMatcher instance
* @param bool $onlyException true if the profiler only collects data when an exception occurs, false otherwise
* @param bool $onlyMasterRequests true if the profiler only collects data when the request is a master request, false otherwise
* @param bool $onlyException True if the profiler only collects data when an exception occurs, false otherwise
* @param bool $onlyMasterRequests True if the profiler only collects data when the request is a master request, false otherwise
*/
public function __construct(Profiler $profiler, RequestStack $requestStack, RequestMatcherInterface $matcher = null, $onlyException = false, $onlyMasterRequests = false)
{
@@ -58,8 +56,6 @@ class ProfilerListener implements EventSubscriberInterface
/**
* Handles the onKernelException event.
*
* @param GetResponseForExceptionEvent $event A GetResponseForExceptionEvent instance
*/
public function onKernelException(GetResponseForExceptionEvent $event)
{
@@ -72,8 +68,6 @@ class ProfilerListener implements EventSubscriberInterface
/**
* Handles the onKernelResponse event.
*
* @param FilterResponseEvent $event A FilterResponseEvent instance
*/
public function onKernelResponse(FilterResponseEvent $event)
{
@@ -107,8 +101,7 @@ class ProfilerListener implements EventSubscriberInterface
{
// attach children to parents
foreach ($this->profiles as $request) {
// isset call should be removed when requestStack is required
if (isset($this->parents[$request]) && null !== $parentRequest = $this->parents[$request]) {
if (null !== $parentRequest = $this->parents[$request]) {
if (isset($this->profiles[$parentRequest])) {
$this->profiles[$parentRequest]->addChild($this->profiles[$request]);
}
@@ -126,10 +119,10 @@ class ProfilerListener implements EventSubscriberInterface
public static function getSubscribedEvents()
{
return array(
KernelEvents::RESPONSE => array('onKernelResponse', -100),
return [
KernelEvents::RESPONSE => ['onKernelResponse', -100],
KernelEvents::EXCEPTION => 'onKernelException',
KernelEvents::TERMINATE => array('onKernelTerminate', -1024),
);
KernelEvents::TERMINATE => ['onKernelTerminate', -1024],
];
}
}
@@ -11,9 +11,9 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* ResponseListener fixes the Response headers based on the Request.
@@ -31,8 +31,6 @@ class ResponseListener implements EventSubscriberInterface
/**
* Filters the Response.
*
* @param FilterResponseEvent $event A FilterResponseEvent instance
*/
public function onKernelResponse(FilterResponseEvent $event)
{
@@ -51,8 +49,8 @@ class ResponseListener implements EventSubscriberInterface
public static function getSubscribedEvents()
{
return array(
return [
KernelEvents::RESPONSE => 'onKernelResponse',
);
];
}
}
+60 -24
View File
@@ -12,25 +12,31 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RequestContextAwareInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Exception\NoConfigurationException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RequestContextAwareInterface;
/**
* Initializes the context from the request and sets request attributes based on a matching route.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Yonel Ceruto <yonelceruto@gmail.com>
*/
class RouterListener implements EventSubscriberInterface
{
@@ -38,18 +44,20 @@ class RouterListener implements EventSubscriberInterface
private $context;
private $logger;
private $requestStack;
private $projectDir;
private $debug;
/**
* Constructor.
*
* @param UrlMatcherInterface|RequestMatcherInterface $matcher The Url or Request matcher
* @param RequestStack $requestStack A RequestStack instance
* @param RequestContext|null $context The RequestContext (can be null when $matcher implements RequestContextAwareInterface)
* @param LoggerInterface|null $logger The logger
* @param string $projectDir
* @param bool $debug
*
* @throws \InvalidArgumentException
*/
public function __construct($matcher, RequestStack $requestStack, RequestContext $context = null, LoggerInterface $logger = null)
public function __construct($matcher, RequestStack $requestStack, RequestContext $context = null, LoggerInterface $logger = null, $projectDir = null, $debug = true)
{
if (!$matcher instanceof UrlMatcherInterface && !$matcher instanceof RequestMatcherInterface) {
throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.');
@@ -63,20 +71,24 @@ class RouterListener implements EventSubscriberInterface
$this->context = $context ?: $matcher->getContext();
$this->requestStack = $requestStack;
$this->logger = $logger;
$this->projectDir = $projectDir;
$this->debug = $debug;
}
private function setCurrentRequest(Request $request = null)
{
if (null !== $request) {
$this->context->fromRequest($request);
try {
$this->context->fromRequest($request);
} catch (\UnexpectedValueException $e) {
throw new BadRequestHttpException($e->getMessage(), $e, $e->getCode());
}
}
}
/**
* After a sub-request is done, we need to reset the routing context to the parent request so that the URL generator
* operates on the correct context again.
*
* @param FinishRequestEvent $event
*/
public function onKernelFinishRequest(FinishRequestEvent $event)
{
@@ -104,12 +116,12 @@ class RouterListener implements EventSubscriberInterface
}
if (null !== $this->logger) {
$this->logger->info('Matched route "{route}".', array(
$this->logger->info('Matched route "{route}".', [
'route' => isset($parameters['_route']) ? $parameters['_route'] : 'n/a',
'route_parameters' => $parameters,
'request_uri' => $request->getUri(),
'method' => $request->getMethod(),
));
]);
}
$request->attributes->add($parameters);
@@ -130,11 +142,35 @@ class RouterListener implements EventSubscriberInterface
}
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
if (!$this->debug || !($e = $event->getException()) instanceof NotFoundHttpException) {
return;
}
if ($e->getPrevious() instanceof NoConfigurationException) {
$event->setResponse($this->createWelcomeResponse());
}
}
public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => array(array('onKernelRequest', 32)),
KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)),
);
return [
KernelEvents::REQUEST => [['onKernelRequest', 32]],
KernelEvents::FINISH_REQUEST => [['onKernelFinishRequest', 0]],
KernelEvents::EXCEPTION => ['onKernelException', -64],
];
}
private function createWelcomeResponse()
{
$version = Kernel::VERSION;
$baseDir = realpath($this->projectDir).\DIRECTORY_SEPARATOR;
$docVersion = substr(Kernel::VERSION, 0, 3);
ob_start();
include __DIR__.'/../Resources/welcome.html.php';
return new Response(ob_get_clean(), Response::HTTP_NOT_FOUND);
}
}
@@ -29,7 +29,7 @@ use Symfony\Component\HttpKernel\KernelEvents;
* the one above. But by saving the session before long-running things in the terminate event,
* we ensure the session is not blocked longer than needed.
* * When regenerating the session ID no locking is involved in PHPs session design. See
* https://bugs.php.net/bug.php?id=61470 for a discussion. So in this case, the session must
* https://bugs.php.net/61470 for a discussion. So in this case, the session must
* be saved anyway before sending the headers with the new session ID. Otherwise session
* data could get lost again for concurrent requests with the new ID. One result could be
* that you get logged out after just logging in.
@@ -58,9 +58,9 @@ class SaveSessionListener implements EventSubscriberInterface
public static function getSubscribedEvents()
{
return array(
return [
// low priority but higher than StreamedResponseListener
KernelEvents::RESPONSE => array(array('onKernelResponse', -1000)),
);
KernelEvents::RESPONSE => [['onKernelResponse', -1000]],
];
}
}
+15 -29
View File
@@ -11,44 +11,30 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Psr\Container\ContainerInterface;
/**
* Sets the session in the request.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Fabien Potencier <fabien@symfony.com>
*
* @final since version 3.3
*/
abstract class SessionListener implements EventSubscriberInterface
class SessionListener extends AbstractSessionListener
{
public function onKernelRequest(GetResponseEvent $event)
private $container;
public function __construct(ContainerInterface $container)
{
if (!$event->isMasterRequest()) {
return;
}
$request = $event->getRequest();
$session = $this->getSession();
if (null === $session || $request->hasSession()) {
return;
}
$request->setSession($session);
$this->container = $container;
}
public static function getSubscribedEvents()
protected function getSession()
{
return array(
KernelEvents::REQUEST => array('onKernelRequest', 128),
);
}
if (!$this->container->has('session')) {
return null;
}
/**
* Gets the session object.
*
* @return SessionInterface|null A SessionInterface instance or null if no session is available
*/
abstract protected function getSession();
return $this->container->get('session');
}
}
@@ -11,10 +11,10 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* StreamedResponseListener is responsible for sending the Response
@@ -26,8 +26,6 @@ class StreamedResponseListener implements EventSubscriberInterface
{
/**
* Filters the Response.
*
* @param FilterResponseEvent $event A FilterResponseEvent instance
*/
public function onKernelResponse(FilterResponseEvent $event)
{
@@ -44,8 +42,8 @@ class StreamedResponseListener implements EventSubscriberInterface
public static function getSubscribedEvents()
{
return array(
KernelEvents::RESPONSE => array('onKernelResponse', -1024),
);
return [
KernelEvents::RESPONSE => ['onKernelResponse', -1024],
];
}
}
@@ -11,10 +11,11 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\HttpCache\HttpCache;
use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* SurrogateListener adds a Surrogate-Control HTTP header when the Response needs to be parsed for Surrogates.
@@ -25,11 +26,6 @@ class SurrogateListener implements EventSubscriberInterface
{
private $surrogate;
/**
* Constructor.
*
* @param SurrogateInterface $surrogate An SurrogateInterface instance
*/
public function __construct(SurrogateInterface $surrogate = null)
{
$this->surrogate = $surrogate;
@@ -37,22 +33,33 @@ class SurrogateListener implements EventSubscriberInterface
/**
* Filters the Response.
*
* @param FilterResponseEvent $event A FilterResponseEvent instance
*/
public function onKernelResponse(FilterResponseEvent $event)
{
if (!$event->isMasterRequest() || null === $this->surrogate) {
if (!$event->isMasterRequest()) {
return;
}
$this->surrogate->addSurrogateControl($event->getResponse());
$kernel = $event->getKernel();
$surrogate = $this->surrogate;
if ($kernel instanceof HttpCache) {
$surrogate = $kernel->getSurrogate();
if (null !== $this->surrogate && $this->surrogate->getName() !== $surrogate->getName()) {
$surrogate = $this->surrogate;
}
}
if (null === $surrogate) {
return;
}
$surrogate->addSurrogateControl($event->getResponse());
}
public static function getSubscribedEvents()
{
return array(
return [
KernelEvents::RESPONSE => 'onKernelResponse',
);
];
}
}
@@ -11,74 +11,30 @@
namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Psr\Container\ContainerInterface;
/**
* TestSessionListener.
* Sets the session in the request.
*
* Saves session in test environment.
*
* @author Bulat Shakirzyanov <mallluhuct@gmail.com>
* @author Fabien Potencier <fabien@symfony.com>
*
* @final since version 3.3
*/
abstract class TestSessionListener implements EventSubscriberInterface
class TestSessionListener extends AbstractTestSessionListener
{
public function onKernelRequest(GetResponseEvent $event)
private $container;
public function __construct(ContainerInterface $container)
{
if (!$event->isMasterRequest()) {
return;
}
// bootstrap the session
$session = $this->getSession();
if (!$session) {
return;
}
$cookies = $event->getRequest()->cookies;
if ($cookies->has($session->getName())) {
$session->setId($cookies->get($session->getName()));
}
$this->container = $container;
}
/**
* Checks if session was initialized and saves if current request is master
* Runs on 'kernel.response' in test environment.
*
* @param FilterResponseEvent $event
*/
public function onKernelResponse(FilterResponseEvent $event)
protected function getSession()
{
if (!$event->isMasterRequest()) {
return;
if (!$this->container->has('session')) {
return null;
}
$session = $event->getRequest()->getSession();
if ($session && $session->isStarted()) {
$session->save();
$params = session_get_cookie_params();
$event->getResponse()->headers->setCookie(new Cookie($session->getName(), $session->getId(), 0 === $params['lifetime'] ? 0 : time() + $params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly']));
}
return $this->container->get('session');
}
public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => array('onKernelRequest', 192),
KernelEvents::RESPONSE => array('onKernelResponse', -128),
);
}
/**
* Gets the session object.
*
* @return SessionInterface|null A SessionInterface instance or null if no session is available
*/
abstract protected function getSession();
}
@@ -13,10 +13,10 @@ namespace Symfony\Component\HttpKernel\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Translation\TranslatorInterface;
/**
@@ -51,11 +51,11 @@ class TranslatorListener implements EventSubscriberInterface
public static function getSubscribedEvents()
{
return array(
return [
// must be registered after the Locale listener
KernelEvents::REQUEST => array(array('onKernelRequest', 10)),
KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)),
);
KernelEvents::REQUEST => [['onKernelRequest', 10]],
KernelEvents::FINISH_REQUEST => [['onKernelFinishRequest', 0]],
];
}
private function setLocale(Request $request)
@@ -16,8 +16,7 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Validates that the headers and other information indicating the
* client IP address of a request are consistent.
* Validates Requests.
*
* @author Magnus Nordlander <magnus@fervo.se>
*/
@@ -25,8 +24,6 @@ class ValidateRequestListener implements EventSubscriberInterface
{
/**
* Performs the validation.
*
* @param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
{
@@ -36,9 +33,10 @@ class ValidateRequestListener implements EventSubscriberInterface
$request = $event->getRequest();
if ($request::getTrustedProxies()) {
// This will throw an exception if the headers are inconsistent.
$request->getClientIps();
}
$request->getHost();
}
/**
@@ -46,10 +44,10 @@ class ValidateRequestListener implements EventSubscriberInterface
*/
public static function getSubscribedEvents()
{
return array(
KernelEvents::REQUEST => array(
array('onKernelRequest', 256),
),
);
return [
KernelEvents::REQUEST => [
['onKernelRequest', 256],
],
];
}
}
@@ -12,22 +12,18 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* AccessDeniedHttpException.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Christophe Coevoet <stof@notk.org>
*/
class AccessDeniedHttpException extends HttpException
{
/**
* Constructor.
*
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
* @param int $code The internal exception code
*/
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct(403, $message, $previous, array(), $code);
parent::__construct(403, $message, $previous, [], $code);
}
}
@@ -12,21 +12,17 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* BadRequestHttpException.
*
* @author Ben Ramsey <ben@benramsey.com>
*/
class BadRequestHttpException extends HttpException
{
/**
* Constructor.
*
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
* @param int $code The internal exception code
*/
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct(400, $message, $previous, array(), $code);
parent::__construct(400, $message, $previous, [], $code);
}
}
@@ -12,21 +12,17 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* ConflictHttpException.
*
* @author Ben Ramsey <ben@benramsey.com>
*/
class ConflictHttpException extends HttpException
{
/**
* Constructor.
*
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
* @param int $code The internal exception code
*/
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct(409, $message, $previous, array(), $code);
parent::__construct(409, $message, $previous, [], $code);
}
}
+1 -5
View File
@@ -12,21 +12,17 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* GoneHttpException.
*
* @author Ben Ramsey <ben@benramsey.com>
*/
class GoneHttpException extends HttpException
{
/**
* Constructor.
*
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
* @param int $code The internal exception code
*/
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct(410, $message, $previous, array(), $code);
parent::__construct(410, $message, $previous, [], $code);
}
}
+1 -1
View File
@@ -21,7 +21,7 @@ class HttpException extends \RuntimeException implements HttpExceptionInterface
private $statusCode;
private $headers;
public function __construct($statusCode, $message = null, \Exception $previous = null, array $headers = array(), $code = 0)
public function __construct($statusCode, $message = null, \Exception $previous = null, array $headers = [], $code = 0)
{
$this->statusCode = $statusCode;
$this->headers = $headers;
@@ -12,21 +12,17 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* LengthRequiredHttpException.
*
* @author Ben Ramsey <ben@benramsey.com>
*/
class LengthRequiredHttpException extends HttpException
{
/**
* Constructor.
*
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
* @param int $code The internal exception code
*/
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct(411, $message, $previous, array(), $code);
parent::__construct(411, $message, $previous, [], $code);
}
}
@@ -12,15 +12,11 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* MethodNotAllowedHttpException.
*
* @author Kris Wallsmith <kris@symfony.com>
*/
class MethodNotAllowedHttpException extends HttpException
{
/**
* Constructor.
*
* @param array $allow An array of allowed methods
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
@@ -28,7 +24,7 @@ class MethodNotAllowedHttpException extends HttpException
*/
public function __construct(array $allow, $message = null, \Exception $previous = null, $code = 0)
{
$headers = array('Allow' => strtoupper(implode(', ', $allow)));
$headers = ['Allow' => strtoupper(implode(', ', $allow))];
parent::__construct(405, $message, $previous, $headers, $code);
}
@@ -12,21 +12,17 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* NotAcceptableHttpException.
*
* @author Ben Ramsey <ben@benramsey.com>
*/
class NotAcceptableHttpException extends HttpException
{
/**
* Constructor.
*
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
* @param int $code The internal exception code
*/
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct(406, $message, $previous, array(), $code);
parent::__construct(406, $message, $previous, [], $code);
}
}
@@ -12,21 +12,17 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* NotFoundHttpException.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class NotFoundHttpException extends HttpException
{
/**
* Constructor.
*
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
* @param int $code The internal exception code
*/
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct(404, $message, $previous, array(), $code);
parent::__construct(404, $message, $previous, [], $code);
}
}
@@ -12,21 +12,17 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* PreconditionFailedHttpException.
*
* @author Ben Ramsey <ben@benramsey.com>
*/
class PreconditionFailedHttpException extends HttpException
{
/**
* Constructor.
*
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
* @param int $code The internal exception code
*/
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct(412, $message, $previous, array(), $code);
parent::__construct(412, $message, $previous, [], $code);
}
}
@@ -12,8 +12,6 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* PreconditionRequiredHttpException.
*
* @author Ben Ramsey <ben@benramsey.com>
*
* @see http://tools.ietf.org/html/rfc6585
@@ -21,14 +19,12 @@ namespace Symfony\Component\HttpKernel\Exception;
class PreconditionRequiredHttpException extends HttpException
{
/**
* Constructor.
*
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
* @param int $code The internal exception code
*/
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct(428, $message, $previous, array(), $code);
parent::__construct(428, $message, $previous, [], $code);
}
}
@@ -12,15 +12,11 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* ServiceUnavailableHttpException.
*
* @author Ben Ramsey <ben@benramsey.com>
*/
class ServiceUnavailableHttpException extends HttpException
{
/**
* Constructor.
*
* @param int|string $retryAfter The number of seconds or HTTP-date after which the request may be retried
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
@@ -28,9 +24,9 @@ class ServiceUnavailableHttpException extends HttpException
*/
public function __construct($retryAfter = null, $message = null, \Exception $previous = null, $code = 0)
{
$headers = array();
$headers = [];
if ($retryAfter) {
$headers = array('Retry-After' => $retryAfter);
$headers = ['Retry-After' => $retryAfter];
}
parent::__construct(503, $message, $previous, $headers, $code);
@@ -12,8 +12,6 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* TooManyRequestsHttpException.
*
* @author Ben Ramsey <ben@benramsey.com>
*
* @see http://tools.ietf.org/html/rfc6585
@@ -21,8 +19,6 @@ namespace Symfony\Component\HttpKernel\Exception;
class TooManyRequestsHttpException extends HttpException
{
/**
* Constructor.
*
* @param int|string $retryAfter The number of seconds or HTTP-date after which the request may be retried
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
@@ -30,9 +26,9 @@ class TooManyRequestsHttpException extends HttpException
*/
public function __construct($retryAfter = null, $message = null, \Exception $previous = null, $code = 0)
{
$headers = array();
$headers = [];
if ($retryAfter) {
$headers = array('Retry-After' => $retryAfter);
$headers = ['Retry-After' => $retryAfter];
}
parent::__construct(429, $message, $previous, $headers, $code);
@@ -12,15 +12,11 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* UnauthorizedHttpException.
*
* @author Ben Ramsey <ben@benramsey.com>
*/
class UnauthorizedHttpException extends HttpException
{
/**
* Constructor.
*
* @param string $challenge WWW-Authenticate challenge string
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
@@ -28,7 +24,7 @@ class UnauthorizedHttpException extends HttpException
*/
public function __construct($challenge, $message = null, \Exception $previous = null, $code = 0)
{
$headers = array('WWW-Authenticate' => $challenge);
$headers = ['WWW-Authenticate' => $challenge];
parent::__construct(401, $message, $previous, $headers, $code);
}
@@ -12,21 +12,17 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* UnprocessableEntityHttpException.
*
* @author Steve Hutchins <hutchinsteve@gmail.com>
*/
class UnprocessableEntityHttpException extends HttpException
{
/**
* Constructor.
*
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
* @param int $code The internal exception code
*/
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct(422, $message, $previous, array(), $code);
parent::__construct(422, $message, $previous, [], $code);
}
}
@@ -12,21 +12,17 @@
namespace Symfony\Component\HttpKernel\Exception;
/**
* UnsupportedMediaTypeHttpException.
*
* @author Ben Ramsey <ben@benramsey.com>
*/
class UnsupportedMediaTypeHttpException extends HttpException
{
/**
* Constructor.
*
* @param string $message The internal exception message
* @param \Exception $previous The previous exception
* @param int $code The internal exception code
*/
public function __construct($message = null, \Exception $previous = null, $code = 0)
{
parent::__construct(415, $message, $previous, array(), $code);
parent::__construct(415, $message, $previous, [], $code);
}
}
@@ -29,8 +29,6 @@ abstract class AbstractSurrogateFragmentRenderer extends RoutableFragmentRendere
private $signer;
/**
* Constructor.
*
* The "fallback" strategy when surrogate is not available should always be an
* instance of InlineFragmentRenderer.
*
@@ -61,11 +59,11 @@ abstract class AbstractSurrogateFragmentRenderer extends RoutableFragmentRendere
*
* @see Symfony\Component\HttpKernel\HttpCache\SurrogateInterface
*/
public function render($uri, Request $request, array $options = array())
public function render($uri, Request $request, array $options = [])
{
if (!$this->surrogate || !$this->surrogate->hasSurrogateCapability($request)) {
if ($uri instanceof ControllerReference && $this->containsNonScalars($uri->attributes)) {
@trigger_error('Passing non-scalar values as part of URI attributes to the ESI and SSI rendering strategies is deprecated since version 3.1, and will be removed in 4.0. Use a different rendering strategy or pass scalar values.', E_USER_DEPRECATED);
@trigger_error('Passing non-scalar values as part of URI attributes to the ESI and SSI rendering strategies is deprecated since Symfony 3.1, and will be removed in 4.0. Use a different rendering strategy or pass scalar values.', \E_USER_DEPRECATED);
}
return $this->inlineStrategy->render($uri, $request, $options);
@@ -94,14 +92,14 @@ abstract class AbstractSurrogateFragmentRenderer extends RoutableFragmentRendere
// we need to sign the absolute URI, but want to return the path only.
$fragmentUri = $this->signer->sign($this->generateFragmentUri($uri, $request, true));
return substr($fragmentUri, strlen($request->getSchemeAndHttpHost()));
return substr($fragmentUri, \strlen($request->getSchemeAndHttpHost()));
}
private function containsNonScalars(array $values)
{
foreach ($values as $value) {
if (is_array($value) && $this->containsNonScalars($value)) {
return true;
if (\is_array($value)) {
return $this->containsNonScalars($value);
} elseif (!is_scalar($value) && null !== $value) {
return true;
}
+7 -11
View File
@@ -11,9 +11,9 @@
namespace Symfony\Component\HttpKernel\Fragment;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
/**
@@ -29,17 +29,15 @@ use Symfony\Component\HttpKernel\Controller\ControllerReference;
class FragmentHandler
{
private $debug;
private $renderers = array();
private $renderers = [];
private $requestStack;
/**
* Constructor.
*
* @param RequestStack $requestStack The Request stack that controls the lifecycle of requests
* @param FragmentRendererInterface[] $renderers An array of FragmentRendererInterface instances
* @param bool $debug Whether the debug mode is enabled or not
*/
public function __construct(RequestStack $requestStack, array $renderers = array(), $debug = false)
public function __construct(RequestStack $requestStack, array $renderers = [], $debug = false)
{
$this->requestStack = $requestStack;
foreach ($renderers as $renderer) {
@@ -50,8 +48,6 @@ class FragmentHandler
/**
* Adds a renderer.
*
* @param FragmentRendererInterface $renderer A FragmentRendererInterface instance
*/
public function addRenderer(FragmentRendererInterface $renderer)
{
@@ -74,7 +70,7 @@ class FragmentHandler
* @throws \InvalidArgumentException when the renderer does not exist
* @throws \LogicException when no master request is being handled
*/
public function render($uri, $renderer = 'inline', array $options = array())
public function render($uri, $renderer = 'inline', array $options = [])
{
if (!isset($options['ignore_errors'])) {
$options['ignore_errors'] = !$this->debug;
@@ -97,8 +93,6 @@ class FragmentHandler
* When the Response is a StreamedResponse, the content is streamed immediately
* instead of being returned.
*
* @param Response $response A Response instance
*
* @return string|null The Response content or null when the Response is streamed
*
* @throws \RuntimeException when the Response is not successful
@@ -106,7 +100,7 @@ class FragmentHandler
protected function deliver(Response $response)
{
if (!$response->isSuccessful()) {
throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $this->requestStack->getCurrentRequest()->getUri(), $response->getStatusCode()));
throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %d).', $this->requestStack->getCurrentRequest()->getUri(), $response->getStatusCode()));
}
if (!$response instanceof StreamedResponse) {
@@ -114,5 +108,7 @@ class FragmentHandler
}
$response->sendContent();
return null;
}
}
@@ -12,8 +12,8 @@
namespace Symfony\Component\HttpKernel\Fragment;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
/**
* Interface implemented by all rendering strategies.
@@ -31,7 +31,7 @@ interface FragmentRendererInterface
*
* @return Response A Response instance
*/
public function render($uri, Request $request, array $options = array());
public function render($uri, Request $request, array $options = []);
/**
* Gets the name of the strategy.
@@ -13,12 +13,13 @@ namespace Symfony\Component\HttpKernel\Fragment;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpKernel\UriSigner;
use Symfony\Component\Templating\EngineInterface;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Loader\ExistsLoaderInterface;
use Twig\Loader\SourceContextLoaderInterface;
/**
* Implements the Hinclude rendering strategy.
@@ -33,8 +34,6 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
private $charset;
/**
* Constructor.
*
* @param EngineInterface|Environment $templating An EngineInterface or a Twig instance
* @param UriSigner $signer A UriSigner instance
* @param string $globalDefaultTemplate The global default content (it can be a template name or the content)
@@ -58,7 +57,7 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
public function setTemplating($templating)
{
if (null !== $templating && !$templating instanceof EngineInterface && !$templating instanceof Environment) {
throw new \InvalidArgumentException('The hinclude rendering strategy needs an instance of Twig\Environment or Symfony\Component\Templating\EngineInterface');
throw new \InvalidArgumentException('The hinclude rendering strategy needs an instance of Twig\Environment or Symfony\Component\Templating\EngineInterface.');
}
$this->templating = $templating;
@@ -83,7 +82,7 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
* * id: An optional hx:include tag id attribute
* * attributes: An optional array of hx:include tag attributes
*/
public function render($uri, Request $request, array $options = array())
public function render($uri, Request $request, array $options = [])
{
if ($uri instanceof ControllerReference) {
if (null === $this->signer) {
@@ -91,7 +90,7 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
}
// we need to sign the absolute URI, but want to return the path only.
$uri = substr($this->signer->sign($this->generateFragmentUri($uri, $request, true)), strlen($request->getSchemeAndHttpHost()));
$uri = substr($this->signer->sign($this->generateFragmentUri($uri, $request, true)), \strlen($request->getSchemeAndHttpHost()));
}
// We need to replace ampersands in the URI with the encoded form in order to return valid html/xml content.
@@ -104,13 +103,13 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
$content = $template;
}
$attributes = isset($options['attributes']) && is_array($options['attributes']) ? $options['attributes'] : array();
$attributes = isset($options['attributes']) && \is_array($options['attributes']) ? $options['attributes'] : [];
if (isset($options['id']) && $options['id']) {
$attributes['id'] = $options['id'];
}
$renderedAttributes = '';
if (count($attributes) > 0) {
$flags = ENT_QUOTES | ENT_SUBSTITUTE;
if (\count($attributes) > 0) {
$flags = \ENT_QUOTES | \ENT_SUBSTITUTE;
foreach ($attributes as $attribute => $value) {
$renderedAttributes .= sprintf(
' %s="%s"',
@@ -133,28 +132,29 @@ class HIncludeFragmentRenderer extends RoutableFragmentRenderer
if ($this->templating instanceof EngineInterface) {
try {
return $this->templating->exists($template);
} catch (\InvalidArgumentException $e) {
} catch (\Exception $e) {
return false;
}
}
$loader = $this->templating->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);
if (1 === Environment::MAJOR_VERSION && !$loader instanceof ExistsLoaderInterface) {
try {
if ($loader instanceof SourceContextLoaderInterface) {
$loader->getSourceContext($template);
} else {
$loader->getSource($template);
}
return true;
} catch (LoaderError $e) {
}
return true;
} catch (LoaderError $e) {
return false;
}
return false;
return $loader->exists($template);
}
/**
@@ -11,13 +11,14 @@
namespace Symfony\Component\HttpKernel\Fragment;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\HttpCache\SubRequestHandler;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Implements the inline rendering strategy where the Request is rendered by the current HTTP kernel.
@@ -29,12 +30,6 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
private $kernel;
private $dispatcher;
/**
* Constructor.
*
* @param HttpKernelInterface $kernel A HttpKernelInterface instance
* @param EventDispatcherInterface $dispatcher A EventDispatcherInterface instance
*/
public function __construct(HttpKernelInterface $kernel, EventDispatcherInterface $dispatcher = null)
{
$this->kernel = $kernel;
@@ -48,7 +43,7 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
*
* * alt: an alternative URI to render in case of an error
*/
public function render($uri, Request $request, array $options = array())
public function render($uri, Request $request, array $options = [])
{
$reference = null;
if ($uri instanceof ControllerReference) {
@@ -59,10 +54,10 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
// want that as we want to preserve objects (so we manually set Request attributes
// below instead)
$attributes = $reference->attributes;
$reference->attributes = array();
$reference->attributes = [];
// The request format and locale might have been overridden by the user
foreach (array('_format', '_locale') as $key) {
foreach (['_format', '_locale'] as $key) {
if (isset($attributes[$key])) {
$reference->attributes[$key] = $attributes[$key];
}
@@ -82,10 +77,10 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
$level = ob_get_level();
try {
return $this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false);
return SubRequestHandler::handle($this->kernel, $subRequest, HttpKernelInterface::SUB_REQUEST, false);
} catch (\Exception $e) {
// we dispatch the exception event to trigger the logging
// the response that comes back is simply ignored
// the response that comes back is ignored
if (isset($options['ignore_errors']) && $options['ignore_errors'] && $this->dispatcher) {
$event = new GetResponseForExceptionEvent($this->kernel, $request, HttpKernelInterface::SUB_REQUEST, $e);
@@ -115,24 +110,10 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
$cookies = $request->cookies->all();
$server = $request->server->all();
// Override the arguments to emulate a sub-request.
// Sub-request object will point to localhost as client ip and real client ip
// will be included into trusted header for client ip
try {
if ($trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP)) {
$currentXForwardedFor = $request->headers->get($trustedHeaderName, '');
$server['HTTP_'.$trustedHeaderName] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp();
}
} catch (\InvalidArgumentException $e) {
// Do nothing
}
$server['REMOTE_ADDR'] = '127.0.0.1';
unset($server['HTTP_IF_MODIFIED_SINCE']);
unset($server['HTTP_IF_NONE_MATCH']);
$subRequest = Request::create($uri, 'get', array(), $cookies, array(), $server);
$subRequest = Request::create($uri, 'get', [], $cookies, [], $server);
if ($request->headers->has('Surrogate-Capability')) {
$subRequest->headers->set('Surrogate-Capability', $request->headers->get('Surrogate-Capability'));
}
@@ -141,6 +122,13 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
$subRequest->setSession($session);
}
if ($request->get('_format')) {
$subRequest->attributes->set('_format', $request->get('_format'));
}
if ($request->getDefaultLocale() !== $request->getLocale()) {
$subRequest->setLocale($request->getLocale());
}
return $subRequest;
}
@@ -11,8 +11,8 @@
namespace Symfony\Component\HttpKernel\Fragment;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpKernel\EventListener\FragmentListener;
/**
@@ -80,7 +80,7 @@ abstract class RoutableFragmentRenderer implements FragmentRendererInterface
private function checkNonScalar($values)
{
foreach ($values as $key => $value) {
if (is_array($value)) {
if (\is_array($value)) {
$this->checkNonScalar($value);
} elseif (!is_scalar($value) && null !== $value) {
throw new \LogicException(sprintf('Controller attributes cannot contain non-scalar/non-null values (value for key "%s" is not a scalar or null).', $key));
+9 -11
View File
@@ -24,18 +24,16 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
abstract class AbstractSurrogate implements SurrogateInterface
{
protected $contentTypes;
protected $phpEscapeMap = array(
array('<?', '<%', '<s', '<S'),
array('<?php echo "<?"; ?>', '<?php echo "<%"; ?>', '<?php echo "<s"; ?>', '<?php echo "<S"; ?>'),
);
protected $phpEscapeMap = [
['<?', '<%', '<s', '<S'],
['<?php echo "<?"; ?>', '<?php echo "<%"; ?>', '<?php echo "<s"; ?>', '<?php echo "<S"; ?>'],
];
/**
* Constructor.
*
* @param array $contentTypes An array of content-type that should be parsed for Surrogate information
* (default: text/html, text/xml, application/xhtml+xml, and application/xml)
*/
public function __construct(array $contentTypes = array('text/html', 'text/xml', 'application/xhtml+xml', 'application/xml'))
public function __construct(array $contentTypes = ['text/html', 'text/xml', 'application/xhtml+xml', 'application/xml'])
{
$this->contentTypes = $contentTypes;
}
@@ -92,13 +90,13 @@ abstract class AbstractSurrogate implements SurrogateInterface
*/
public function handle(HttpCache $cache, $uri, $alt, $ignoreErrors)
{
$subRequest = Request::create($uri, Request::METHOD_GET, array(), $cache->getRequest()->cookies->all(), array(), $cache->getRequest()->server->all());
$subRequest = Request::create($uri, Request::METHOD_GET, [], $cache->getRequest()->cookies->all(), [], $cache->getRequest()->server->all());
try {
$response = $cache->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true);
if (!$response->isSuccessful()) {
throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $subRequest->getUri(), $response->getStatusCode()));
throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %d).', $subRequest->getUri(), $response->getStatusCode()));
}
return $response->getContent();
@@ -111,12 +109,12 @@ abstract class AbstractSurrogate implements SurrogateInterface
throw $e;
}
}
return '';
}
/**
* Remove the Surrogate from the Surrogate-Control header.
*
* @param Response $response
*/
protected function removeFromControl(Response $response)
{
+6 -4
View File
@@ -71,7 +71,7 @@ class Esi extends AbstractSurrogate
}
$parts = explode(';', $type);
if (!in_array($parts[0], $this->contentTypes)) {
if (!\in_array($parts[0], $this->contentTypes)) {
return $response;
}
@@ -80,13 +80,13 @@ class Esi extends AbstractSurrogate
$content = preg_replace('#<esi\:remove>.*?</esi\:remove>#s', '', $content);
$content = preg_replace('#<esi\:comment[^>]+>#s', '', $content);
$chunks = preg_split('#<esi\:include\s+(.*?)\s*(?:/|</esi\:include)>#', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
$chunks = preg_split('#<esi\:include\s+(.*?)\s*(?:/|</esi\:include)>#', $content, -1, \PREG_SPLIT_DELIM_CAPTURE);
$chunks[0] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[0]);
$i = 1;
while (isset($chunks[$i])) {
$options = array();
preg_match_all('/(src|onerror|alt)="([^"]*?)"/', $chunks[$i], $matches, PREG_SET_ORDER);
$options = [];
preg_match_all('/(src|onerror|alt)="([^"]*?)"/', $chunks[$i], $matches, \PREG_SET_ORDER);
foreach ($matches as $set) {
$options[$set[1]] = $set[2];
}
@@ -111,5 +111,7 @@ class Esi extends AbstractSurrogate
// remove ESI/1.0 from the Surrogate-Control header
$this->removeFromControl($response);
return $response;
}
}
+166 -135
View File
@@ -15,10 +15,10 @@
namespace Symfony\Component\HttpKernel\HttpCache;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
/**
* Cache provides HTTP caching.
@@ -32,8 +32,8 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
private $request;
private $surrogate;
private $surrogateCacheStrategy;
private $options = array();
private $traces = array();
private $options = [];
private $traces = [];
/**
* Constructor.
@@ -69,36 +69,31 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
* the cache can serve a stale response when an error is encountered (default: 60).
* This setting is overridden by the stale-if-error HTTP Cache-Control extension
* (see RFC 5861).
*
* @param HttpKernelInterface $kernel An HttpKernelInterface instance
* @param StoreInterface $store A Store instance
* @param SurrogateInterface $surrogate A SurrogateInterface instance
* @param array $options An array of options
*/
public function __construct(HttpKernelInterface $kernel, StoreInterface $store, SurrogateInterface $surrogate = null, array $options = array())
public function __construct(HttpKernelInterface $kernel, StoreInterface $store, SurrogateInterface $surrogate = null, array $options = [])
{
$this->store = $store;
$this->kernel = $kernel;
$this->surrogate = $surrogate;
// needed in case there is a fatal error because the backend is too slow to respond
register_shutdown_function(array($this->store, 'cleanup'));
register_shutdown_function([$this->store, 'cleanup']);
$this->options = array_merge(array(
$this->options = array_merge([
'debug' => false,
'default_ttl' => 0,
'private_headers' => array('Authorization', 'Cookie'),
'private_headers' => ['Authorization', 'Cookie'],
'allow_reload' => false,
'allow_revalidate' => false,
'stale_while_revalidate' => 2,
'stale_if_error' => 60,
), $options);
], $options);
}
/**
* Gets the current store.
*
* @return StoreInterface $store A StoreInterface instance
* @return StoreInterface A StoreInterface instance
*/
public function getStore()
{
@@ -122,7 +117,7 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
*/
public function getLog()
{
$log = array();
$log = [];
foreach ($this->traces as $request => $traces) {
$log[] = sprintf('%s: %s', $request, implode(', ', $traces));
}
@@ -169,31 +164,36 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
{
// FIXME: catch exceptions and implement a 500 error page here? -> in Varnish, there is a built-in error page mechanism
if (HttpKernelInterface::MASTER_REQUEST === $type) {
$this->traces = array();
$this->request = $request;
$this->traces = [];
// Keep a clone of the original request for surrogates so they can access it.
// We must clone here to get a separate instance because the application will modify the request during
// the application flow (we know it always does because we do ourselves by setting REMOTE_ADDR to 127.0.0.1
// and adding the X-Forwarded-For header, see HttpCache::forward()).
$this->request = clone $request;
if (null !== $this->surrogate) {
$this->surrogateCacheStrategy = $this->surrogate->createCacheStrategy();
}
}
$path = $request->getPathInfo();
if ($qs = $request->getQueryString()) {
$path .= '?'.$qs;
}
$this->traces[$request->getMethod().' '.$path] = array();
$this->traces[$this->getTraceKey($request)] = [];
if (!$request->isMethodSafe(false)) {
$response = $this->invalidate($request, $catch);
} elseif ($request->headers->has('expect') || !$request->isMethodCacheable()) {
$response = $this->pass($request, $catch);
} elseif ($this->options['allow_reload'] && $request->isNoCache()) {
/*
If allow_reload is configured and the client requests "Cache-Control: no-cache",
reload the cache by fetching a fresh response and caching it (if possible).
*/
$this->record($request, 'reload');
$response = $this->fetch($request, $catch);
} else {
$response = $this->lookup($request, $catch);
}
$this->restoreResponseBody($request, $response);
$response->setDate(\DateTime::createFromFormat('U', time(), new \DateTimeZone('UTC')));
if (HttpKernelInterface::MASTER_REQUEST === $type && $this->options['debug']) {
$response->headers->set('X-Symfony-Cache', $this->getLog());
}
@@ -260,9 +260,9 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
$this->store->invalidate($request);
// As per the RFC, invalidate Location and Content-Location URLs if present
foreach (array('Location', 'Content-Location') as $header) {
foreach (['Location', 'Content-Location'] as $header) {
if ($uri = $response->headers->get($header)) {
$subRequest = Request::create($uri, 'get', array(), array(), array(), $request->server->all());
$subRequest = Request::create($uri, 'get', [], [], [], $request->server->all());
$this->store->invalidate($subRequest);
}
@@ -291,7 +291,7 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
* it triggers "miss" processing.
*
* @param Request $request A Request instance
* @param bool $catch whether to process exceptions
* @param bool $catch Whether to process exceptions
*
* @return Response A Response instance
*
@@ -299,13 +299,6 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
*/
protected function lookup(Request $request, $catch = false)
{
// if allow_reload and no-cache Cache-Control, allow a cache reload
if ($this->options['allow_reload'] && $request->isNoCache()) {
$this->record($request, 'reload');
return $this->fetch($request, $catch);
}
try {
$entry = $this->store->lookup($request);
} catch (\Exception $e) {
@@ -330,6 +323,10 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
return $this->validate($request, $entry, $catch);
}
if ($entry->headers->hasCacheControlDirective('no-cache')) {
return $this->validate($request, $entry, $catch);
}
$this->record($request, 'fresh');
$entry->headers->set('Age', $entry->getAge());
@@ -359,12 +356,14 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
}
// add our cached last-modified validator
$subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified'));
if ($entry->headers->has('Last-Modified')) {
$subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified'));
}
// Add our cached etag validator to the environment.
// We keep the etags from the client to handle the case when the client
// has a different private valid entry which is not cached here.
$cachedEtags = $entry->getEtag() ? array($entry->getEtag()) : array();
$cachedEtags = $entry->getEtag() ? [$entry->getEtag()] : [];
$requestEtags = $request->getETags();
if ($etags = array_unique(array_merge($cachedEtags, $requestEtags))) {
$subRequest->headers->set('if_none_match', implode(', ', $etags));
@@ -377,14 +376,14 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
// return the response and not the cache entry if the response is valid but not cached
$etag = $response->getEtag();
if ($etag && in_array($etag, $requestEtags) && !in_array($etag, $cachedEtags)) {
if ($etag && \in_array($etag, $requestEtags) && !\in_array($etag, $cachedEtags)) {
return $response;
}
$entry = clone $entry;
$entry->headers->remove('Date');
foreach (array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified') as $name) {
foreach (['Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified'] as $name) {
if ($response->headers->has($name)) {
$entry->headers->set($name, $response->headers->get($name));
}
@@ -403,12 +402,11 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
}
/**
* Forwards the Request to the backend and determines whether the response should be stored.
*
* This methods is triggered when the cache missed or a reload is required.
* Unconditionally fetches a fresh response from the backend and
* stores it in the cache if is cacheable.
*
* @param Request $request A Request instance
* @param bool $catch whether to process exceptions
* @param bool $catch Whether to process exceptions
*
* @return Response A Response instance
*/
@@ -437,9 +435,11 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
/**
* Forwards the Request to the backend and returns the Response.
*
* @param Request $request A Request instance
* @param bool $catch Whether to catch exceptions or not
* @param Response $entry A Response instance (the stale entry if present, null otherwise)
* All backend requests (cache passes, fetches, cache validations)
* run through this method.
*
* @param bool $catch Whether to catch exceptions or not
* @param Response|null $entry A Response instance (the stale entry if present, null otherwise)
*
* @return Response A Response instance
*/
@@ -449,41 +449,57 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
$this->surrogate->addSurrogateCapability($request);
}
// modify the X-Forwarded-For header if needed
$forwardedFor = $request->headers->get('X-Forwarded-For');
if ($forwardedFor) {
$request->headers->set('X-Forwarded-For', $forwardedFor.', '.$request->server->get('REMOTE_ADDR'));
} else {
$request->headers->set('X-Forwarded-For', $request->server->get('REMOTE_ADDR'));
}
// fix the client IP address by setting it to 127.0.0.1 as HttpCache
// is always called from the same process as the backend.
$request->server->set('REMOTE_ADDR', '127.0.0.1');
// make sure HttpCache is a trusted proxy
if (!in_array('127.0.0.1', $trustedProxies = Request::getTrustedProxies())) {
$trustedProxies[] = '127.0.0.1';
Request::setTrustedProxies($trustedProxies, method_exists('Request', 'getTrustedHeaderSet') ? Request::getTrustedHeaderSet() : -1);
}
// always a "master" request (as the real master request can be in cache)
$response = $this->kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, $catch);
// FIXME: we probably need to also catch exceptions if raw === true
$response = SubRequestHandler::handle($this->kernel, $request, HttpKernelInterface::MASTER_REQUEST, $catch);
// we don't implement the stale-if-error on Requests, which is nonetheless part of the RFC
if (null !== $entry && in_array($response->getStatusCode(), array(500, 502, 503, 504))) {
/*
* Support stale-if-error given on Responses or as a config option.
* RFC 7234 summarizes in Section 4.2.4 (but also mentions with the individual
* Cache-Control directives) that
*
* A cache MUST NOT generate a stale response if it is prohibited by an
* explicit in-protocol directive (e.g., by a "no-store" or "no-cache"
* cache directive, a "must-revalidate" cache-response-directive, or an
* applicable "s-maxage" or "proxy-revalidate" cache-response-directive;
* see Section 5.2.2).
*
* https://tools.ietf.org/html/rfc7234#section-4.2.4
*
* We deviate from this in one detail, namely that we *do* serve entries in the
* stale-if-error case even if they have a `s-maxage` Cache-Control directive.
*/
if (null !== $entry
&& \in_array($response->getStatusCode(), [500, 502, 503, 504])
&& !$entry->headers->hasCacheControlDirective('no-cache')
&& !$entry->mustRevalidate()
) {
if (null === $age = $entry->headers->getCacheControlDirective('stale-if-error')) {
$age = $this->options['stale_if_error'];
}
if (abs($entry->getTtl()) < $age) {
/*
* stale-if-error gives the (extra) time that the Response may be used *after* it has become stale.
* So we compare the time the $entry has been sitting in the cache already with the
* time it was fresh plus the allowed grace period.
*/
if ($entry->getAge() <= $entry->getMaxAge() + $age) {
$this->record($request, 'stale-if-error');
return $entry;
}
}
/*
RFC 7231 Sect. 7.1.1.2 says that a server that does not have a reasonably accurate
clock MUST NOT send a "Date" header, although it MUST send one in most other cases
except for 1xx or 5xx responses where it MAY do so.
Anyway, a client that received a message without a "Date" header MUST add it.
*/
if (!$response->headers->has('Date')) {
$response->setDate(\DateTime::createFromFormat('U', time()));
}
$this->processResponseBody($request, $response);
if ($this->isPrivateRequest($request) && !$response->headers->hasCacheControlDirective('public')) {
@@ -498,9 +514,6 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
/**
* Checks whether the cache entry is "fresh enough" to satisfy the Request.
*
* @param Request $request A Request instance
* @param Response $entry A Response instance
*
* @return bool true if the cache entry if fresh enough, false otherwise
*/
protected function isFreshEnough(Request $request, Response $entry)
@@ -519,9 +532,6 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
/**
* Locks a Request during the call to the backend.
*
* @param Request $request A Request instance
* @param Response $entry A Response instance
*
* @return bool true if the cache entry can be returned even if it is staled, false otherwise
*/
protected function lock(Request $request, Response $entry)
@@ -529,64 +539,48 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
// try to acquire a lock to call the backend
$lock = $this->store->lock($request);
if (true === $lock) {
// we have the lock, call the backend
return false;
}
// there is already another process calling the backend
if (true !== $lock) {
// check if we can serve the stale entry
if (null === $age = $entry->headers->getCacheControlDirective('stale-while-revalidate')) {
$age = $this->options['stale_while_revalidate'];
}
if (abs($entry->getTtl()) < $age) {
$this->record($request, 'stale-while-revalidate');
// server the stale response while there is a revalidation
return true;
}
// wait for the lock to be released
$wait = 0;
while ($this->store->isLocked($request) && $wait < 5000000) {
usleep(50000);
$wait += 50000;
}
if ($wait < 5000000) {
// replace the current entry with the fresh one
$new = $this->lookup($request);
$entry->headers = $new->headers;
$entry->setContent($new->getContent());
$entry->setStatusCode($new->getStatusCode());
$entry->setProtocolVersion($new->getProtocolVersion());
foreach ($new->headers->getCookies() as $cookie) {
$entry->headers->setCookie($cookie);
}
} else {
// backend is slow as hell, send a 503 response (to avoid the dog pile effect)
$entry->setStatusCode(503);
$entry->setContent('503 Service Unavailable');
$entry->headers->set('Retry-After', 10);
}
// May we serve a stale response?
if ($this->mayServeStaleWhileRevalidate($entry)) {
$this->record($request, 'stale-while-revalidate');
return true;
}
// we have the lock, call the backend
return false;
// wait for the lock to be released
if ($this->waitForLock($request)) {
// replace the current entry with the fresh one
$new = $this->lookup($request);
$entry->headers = $new->headers;
$entry->setContent($new->getContent());
$entry->setStatusCode($new->getStatusCode());
$entry->setProtocolVersion($new->getProtocolVersion());
foreach ($new->headers->getCookies() as $cookie) {
$entry->headers->setCookie($cookie);
}
} else {
// backend is slow as hell, send a 503 response (to avoid the dog pile effect)
$entry->setStatusCode(503);
$entry->setContent('503 Service Unavailable');
$entry->headers->set('Retry-After', 10);
}
return true;
}
/**
* Writes the Response to the cache.
*
* @param Request $request A Request instance
* @param Response $response A Response instance
*
* @throws \Exception
*/
protected function store(Request $request, Response $response)
{
if (!$response->headers->has('Date')) {
$response->setDate(\DateTime::createFromFormat('U', time()));
}
try {
$this->store->write($request, $response);
@@ -607,20 +601,9 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
/**
* Restores the Response body.
*
* @param Request $request A Request instance
* @param Response $response A Response instance
*/
private function restoreResponseBody(Request $request, Response $response)
{
if ($request->isMethod('HEAD') || 304 === $response->getStatusCode()) {
$response->setContent(null);
$response->headers->remove('X-Body-Eval');
$response->headers->remove('X-Body-File');
return;
}
if ($response->headers->has('X-Body-Eval')) {
ob_start();
@@ -633,10 +616,14 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
$response->setContent(ob_get_clean());
$response->headers->remove('X-Body-Eval');
if (!$response->headers->has('Transfer-Encoding')) {
$response->headers->set('Content-Length', strlen($response->getContent()));
$response->headers->set('Content-Length', \strlen($response->getContent()));
}
} elseif ($response->headers->has('X-Body-File')) {
$response->setContent(file_get_contents($response->headers->get('X-Body-File')));
// Response does not include possibly dynamic content (ESI, SSI), so we need
// not handle the content for HEAD requests
if (!$request->isMethod('HEAD')) {
$response->setContent(file_get_contents($response->headers->get('X-Body-File')));
}
} else {
return;
}
@@ -655,8 +642,6 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
* Checks if the Request includes authorization or other sensitive information
* that should cause the Response to be considered private by default.
*
* @param Request $request A Request instance
*
* @return bool true if the Request is private, false otherwise
*/
private function isPrivateRequest(Request $request)
@@ -665,7 +650,7 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
$key = strtolower(str_replace('HTTP_', '', $key));
if ('cookie' === $key) {
if (count($request->cookies->all())) {
if (\count($request->cookies->all())) {
return true;
}
} elseif ($request->headers->has($key)) {
@@ -683,11 +668,57 @@ class HttpCache implements HttpKernelInterface, TerminableInterface
* @param string $event The event name
*/
private function record(Request $request, $event)
{
$this->traces[$this->getTraceKey($request)][] = $event;
}
/**
* Calculates the key we use in the "trace" array for a given request.
*
* @return string
*/
private function getTraceKey(Request $request)
{
$path = $request->getPathInfo();
if ($qs = $request->getQueryString()) {
$path .= '?'.$qs;
}
$this->traces[$request->getMethod().' '.$path][] = $event;
return $request->getMethod().' '.$path;
}
/**
* Checks whether the given (cached) response may be served as "stale" when a revalidation
* is currently in progress.
*
* @return bool true when the stale response may be served, false otherwise
*/
private function mayServeStaleWhileRevalidate(Response $entry)
{
$timeout = $entry->headers->getCacheControlDirective('stale-while-revalidate');
if (null === $timeout) {
$timeout = $this->options['stale_while_revalidate'];
}
return abs($entry->getTtl()) < $timeout;
}
/**
* Waits for the store to release a locked entry.
*
* @param Request $request The request to wait for
*
* @return bool true if the lock was released before the internal timeout was hit; false if the wait timeout was exceeded
*/
private function waitForLock(Request $request)
{
$wait = 0;
while ($this->store->isLocked($request) && $wait < 100) {
usleep(50000);
++$wait;
}
return $wait < 100;
}
}
+159 -36
View File
@@ -5,10 +5,6 @@
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* This code is partially based on the Rack-Cache library by Ryan Tomayko,
* which is released under the MIT license.
* (based on commit 02d2b48d75bcb63cf1c0c7149c077ad256542801)
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
@@ -28,30 +24,69 @@ use Symfony\Component\HttpFoundation\Response;
*/
class ResponseCacheStrategy implements ResponseCacheStrategyInterface
{
private $cacheable = true;
/**
* Cache-Control headers that are sent to the final response if they appear in ANY of the responses.
*/
private static $overrideDirectives = ['private', 'no-cache', 'no-store', 'no-transform', 'must-revalidate', 'proxy-revalidate'];
/**
* Cache-Control headers that are sent to the final response if they appear in ALL of the responses.
*/
private static $inheritDirectives = ['public', 'immutable'];
private $embeddedResponses = 0;
private $ttls = array();
private $maxAges = array();
private $isNotCacheableResponseEmbedded = false;
private $age = 0;
private $flagDirectives = [
'no-cache' => null,
'no-store' => null,
'no-transform' => null,
'must-revalidate' => null,
'proxy-revalidate' => null,
'public' => null,
'private' => null,
'immutable' => null,
];
private $ageDirectives = [
'max-age' => null,
's-maxage' => null,
'expires' => null,
];
/**
* {@inheritdoc}
*/
public function add(Response $response)
{
if (!$response->isFresh() || !$response->isCacheable()) {
$this->cacheable = false;
} else {
$maxAge = $response->getMaxAge();
$this->ttls[] = $response->getTtl();
$this->maxAges[] = $maxAge;
++$this->embeddedResponses;
if (null === $maxAge) {
$this->isNotCacheableResponseEmbedded = true;
foreach (self::$overrideDirectives as $directive) {
if ($response->headers->hasCacheControlDirective($directive)) {
$this->flagDirectives[$directive] = true;
}
}
++$this->embeddedResponses;
foreach (self::$inheritDirectives as $directive) {
if (false !== $this->flagDirectives[$directive]) {
$this->flagDirectives[$directive] = $response->headers->hasCacheControlDirective($directive);
}
}
$age = $response->getAge();
$this->age = max($this->age, $age);
if ($this->willMakeFinalResponseUncacheable($response)) {
$this->isNotCacheableResponseEmbedded = true;
return;
}
$this->storeRelativeAgeDirective('max-age', $response->headers->getCacheControlDirective('max-age'), $age);
$this->storeRelativeAgeDirective('s-maxage', $response->headers->getCacheControlDirective('s-maxage') ?: $response->headers->getCacheControlDirective('max-age'), $age);
$expires = $response->getExpires();
$expires = null !== $expires ? (int) $expires->format('U') - (int) $response->getDate()->format('U') : null;
$this->storeRelativeAgeDirective('expires', $expires >= 0 ? $expires : null, 0);
}
/**
@@ -64,33 +99,121 @@ class ResponseCacheStrategy implements ResponseCacheStrategyInterface
return;
}
// Remove validation related headers in order to avoid browsers using
// their own cache, because some of the response content comes from
// at least one embedded response (which likely has a different caching strategy).
if ($response->isValidateable()) {
$response->setEtag(null);
$response->setLastModified(null);
}
// Remove validation related headers of the master response,
// because some of the response content comes from at least
// one embedded response (which likely has a different caching strategy).
$response->setEtag(null);
$response->setLastModified(null);
if (!$response->isFresh()) {
$this->cacheable = false;
}
$this->add($response);
if (!$this->cacheable) {
$response->headers->set('Cache-Control', 'no-cache, must-revalidate');
$response->headers->set('Age', $this->age);
if ($this->isNotCacheableResponseEmbedded) {
if ($this->flagDirectives['no-store']) {
$response->headers->set('Cache-Control', 'no-cache, no-store, must-revalidate');
} else {
$response->headers->set('Cache-Control', 'no-cache, must-revalidate');
}
return;
}
$this->ttls[] = $response->getTtl();
$this->maxAges[] = $response->getMaxAge();
$flags = array_filter($this->flagDirectives);
if ($this->isNotCacheableResponseEmbedded) {
$response->headers->removeCacheControlDirective('s-maxage');
} elseif (null !== $maxAge = min($this->maxAges)) {
$response->setSharedMaxAge($maxAge);
$response->headers->set('Age', $maxAge - min($this->ttls));
if (isset($flags['must-revalidate'])) {
$flags['no-cache'] = true;
}
$response->headers->set('Cache-Control', implode(', ', array_keys($flags)));
$maxAge = null;
if (is_numeric($this->ageDirectives['max-age'])) {
$maxAge = $this->ageDirectives['max-age'] + $this->age;
$response->headers->addCacheControlDirective('max-age', $maxAge);
}
if (is_numeric($this->ageDirectives['s-maxage'])) {
$sMaxage = $this->ageDirectives['s-maxage'] + $this->age;
if ($maxAge !== $sMaxage) {
$response->headers->addCacheControlDirective('s-maxage', $sMaxage);
}
}
if (is_numeric($this->ageDirectives['expires'])) {
$date = clone $response->getDate();
$date->modify('+'.($this->ageDirectives['expires'] + $this->age).' seconds');
$response->setExpires($date);
}
}
/**
* RFC2616, Section 13.4.
*
* @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.4
*
* @return bool
*/
private function willMakeFinalResponseUncacheable(Response $response)
{
// RFC2616: A response received with a status code of 200, 203, 300, 301 or 410
// MAY be stored by a cache […] unless a cache-control directive prohibits caching.
if ($response->headers->hasCacheControlDirective('no-cache')
|| $response->headers->getCacheControlDirective('no-store')
) {
return true;
}
// Last-Modified and Etag headers cannot be merged, they render the response uncacheable
// by default (except if the response also has max-age etc.).
if (\in_array($response->getStatusCode(), [200, 203, 300, 301, 410])
&& null === $response->getLastModified()
&& null === $response->getEtag()
) {
return false;
}
// RFC2616: A response received with any other status code (e.g. status codes 302 and 307)
// MUST NOT be returned in a reply to a subsequent request unless there are
// cache-control directives or another header(s) that explicitly allow it.
$cacheControl = ['max-age', 's-maxage', 'must-revalidate', 'proxy-revalidate', 'public', 'private'];
foreach ($cacheControl as $key) {
if ($response->headers->hasCacheControlDirective($key)) {
return false;
}
}
if ($response->headers->has('Expires')) {
return false;
}
return true;
}
/**
* Store lowest max-age/s-maxage/expires for the final response.
*
* The response might have been stored in cache a while ago. To keep things comparable,
* we have to subtract the age so that the value is normalized for an age of 0.
*
* If the value is lower than the currently stored value, we update the value, to keep a rolling
* minimal value of each instruction. If the value is NULL, the directive will not be set on the final response.
*
* @param string $directive
* @param int|null $value
* @param int $age
*/
private function storeRelativeAgeDirective($directive, $value, $age)
{
if (null === $value) {
$this->ageDirectives[$directive] = false;
}
if (false !== $this->ageDirectives[$directive]) {
$value -= $age;
$this->ageDirectives[$directive] = null !== $this->ageDirectives[$directive] ? min($this->ageDirectives[$directive], $value) : $value;
}
$response->setMaxAge(0);
}
}
@@ -27,15 +27,11 @@ interface ResponseCacheStrategyInterface
{
/**
* Adds a Response.
*
* @param Response $response
*/
public function add(Response $response);
/**
* Updates the Response HTTP headers based on the embedded Responses.
*
* @param Response $response
*/
public function update(Response $response);
}
+6 -4
View File
@@ -58,20 +58,20 @@ class Ssi extends AbstractSurrogate
}
$parts = explode(';', $type);
if (!in_array($parts[0], $this->contentTypes)) {
if (!\in_array($parts[0], $this->contentTypes)) {
return $response;
}
// we don't use a proper XML parser here as we can have SSI tags in a plain text response
$content = $response->getContent();
$chunks = preg_split('#<!--\#include\s+(.*?)\s*-->#', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
$chunks = preg_split('#<!--\#include\s+(.*?)\s*-->#', $content, -1, \PREG_SPLIT_DELIM_CAPTURE);
$chunks[0] = str_replace($this->phpEscapeMap[0], $this->phpEscapeMap[1], $chunks[0]);
$i = 1;
while (isset($chunks[$i])) {
$options = array();
preg_match_all('/(virtual)="([^"]*?)"/', $chunks[$i], $matches, PREG_SET_ORDER);
$options = [];
preg_match_all('/(virtual)="([^"]*?)"/', $chunks[$i], $matches, \PREG_SET_ORDER);
foreach ($matches as $set) {
$options[$set[1]] = $set[2];
}
@@ -94,5 +94,7 @@ class Ssi extends AbstractSurrogate
// remove SSI/1.0 from the Surrogate-Control header
$this->removeFromControl($response);
return $response;
}
}
+68 -68
View File
@@ -29,8 +29,6 @@ class Store implements StoreInterface
private $locks;
/**
* Constructor.
*
* @param string $root The path to the cache directory
*
* @throws \RuntimeException
@@ -42,7 +40,7 @@ class Store implements StoreInterface
throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root));
}
$this->keyCache = new \SplObjectStorage();
$this->locks = array();
$this->locks = [];
}
/**
@@ -52,18 +50,16 @@ class Store implements StoreInterface
{
// unlock everything
foreach ($this->locks as $lock) {
flock($lock, LOCK_UN);
flock($lock, \LOCK_UN);
fclose($lock);
}
$this->locks = array();
$this->locks = [];
}
/**
* Tries to lock the cache for a given Request, without blocking.
*
* @param Request $request A Request instance
*
* @return bool|string true if the lock is acquired, the path to the current lock otherwise
*/
public function lock(Request $request)
@@ -72,11 +68,11 @@ class Store implements StoreInterface
if (!isset($this->locks[$key])) {
$path = $this->getPath($key);
if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
if (!file_exists(\dirname($path)) && false === @mkdir(\dirname($path), 0777, true) && !is_dir(\dirname($path))) {
return $path;
}
$h = fopen($path, 'cb');
if (!flock($h, LOCK_EX | LOCK_NB)) {
if (!flock($h, \LOCK_EX | \LOCK_NB)) {
fclose($h);
return $path;
@@ -91,8 +87,6 @@ class Store implements StoreInterface
/**
* Releases the lock for the given Request.
*
* @param Request $request A Request instance
*
* @return bool False if the lock file does not exist or cannot be unlocked, true otherwise
*/
public function unlock(Request $request)
@@ -100,7 +94,7 @@ class Store implements StoreInterface
$key = $this->getCacheKey($request);
if (isset($this->locks[$key])) {
flock($this->locks[$key], LOCK_UN);
flock($this->locks[$key], \LOCK_UN);
fclose($this->locks[$key]);
unset($this->locks[$key]);
@@ -123,8 +117,8 @@ class Store implements StoreInterface
}
$h = fopen($path, 'rb');
flock($h, LOCK_EX | LOCK_NB, $wouldBlock);
flock($h, LOCK_UN); // release the lock we just acquired
flock($h, \LOCK_EX | \LOCK_NB, $wouldBlock);
flock($h, \LOCK_UN); // release the lock we just acquired
fclose($h);
return (bool) $wouldBlock;
@@ -133,8 +127,6 @@ class Store implements StoreInterface
/**
* Locates a cached Response for the Request provided.
*
* @param Request $request A Request instance
*
* @return Response|null A Response instance, or null if no cache entry was found
*/
public function lookup(Request $request)
@@ -142,7 +134,7 @@ class Store implements StoreInterface
$key = $this->getCacheKey($request);
if (!$entries = $this->getMetadata($key)) {
return;
return null;
}
// find a cached entry that matches the request.
@@ -156,17 +148,18 @@ class Store implements StoreInterface
}
if (null === $match) {
return;
return null;
}
list($req, $headers) = $match;
if (file_exists($body = $this->getPath($headers['x-content-digest'][0]))) {
return $this->restoreResponse($headers, $body);
$headers = $match[1];
if (file_exists($path = $this->getPath($headers['x-content-digest'][0]))) {
return $this->restoreResponse($headers, $path);
}
// TODO the metaStore referenced an entity that doesn't exist in
// the entityStore. We definitely want to return nil but we should
// also purge the entry from the meta-store when this is detected.
return null;
}
/**
@@ -175,9 +168,6 @@ class Store implements StoreInterface
* Existing entries are read and any that match the response are removed. This
* method calls write with the new list of cache entries.
*
* @param Request $request A Request instance
* @param Response $response A Response instance
*
* @return string The key under which the response is stored
*
* @throws \RuntimeException
@@ -187,30 +177,39 @@ class Store implements StoreInterface
$key = $this->getCacheKey($request);
$storedEnv = $this->persistRequest($request);
// write the response body to the entity store if this is the original response
if (!$response->headers->has('X-Content-Digest')) {
$digest = $this->generateContentDigest($response);
if ($response->headers->has('X-Body-File')) {
// Assume the response came from disk, but at least perform some safeguard checks
if (!$response->headers->has('X-Content-Digest')) {
throw new \RuntimeException('A restored response must have the X-Content-Digest header.');
}
if (false === $this->save($digest, $response->getContent())) {
$digest = $response->headers->get('X-Content-Digest');
if ($this->getPath($digest) !== $response->headers->get('X-Body-File')) {
throw new \RuntimeException('X-Body-File and X-Content-Digest do not match.');
}
// Everything seems ok, omit writing content to disk
} else {
$digest = $this->generateContentDigest($response);
$response->headers->set('X-Content-Digest', $digest);
if (!$this->save($digest, $response->getContent(), false)) {
throw new \RuntimeException('Unable to store the entity.');
}
$response->headers->set('X-Content-Digest', $digest);
if (!$response->headers->has('Transfer-Encoding')) {
$response->headers->set('Content-Length', strlen($response->getContent()));
$response->headers->set('Content-Length', \strlen($response->getContent()));
}
}
// read existing cache entries, remove non-varying, and add this one to the list
$entries = array();
$entries = [];
$vary = $response->headers->get('vary');
foreach ($this->getMetadata($key) as $entry) {
if (!isset($entry[1]['vary'][0])) {
$entry[1]['vary'] = array('');
$entry[1]['vary'] = [''];
}
if ($vary != $entry[1]['vary'][0] || !$this->requestsMatch($vary, $entry[0], $storedEnv)) {
if ($entry[1]['vary'][0] != $vary || !$this->requestsMatch($vary, $entry[0], $storedEnv)) {
$entries[] = $entry;
}
}
@@ -218,9 +217,9 @@ class Store implements StoreInterface
$headers = $this->persistResponse($response);
unset($headers['age']);
array_unshift($entries, array($storedEnv, $headers));
array_unshift($entries, [$storedEnv, $headers]);
if (false === $this->save($key, serialize($entries))) {
if (!$this->save($key, serialize($entries))) {
throw new \RuntimeException('Unable to store the metadata.');
}
@@ -230,8 +229,6 @@ class Store implements StoreInterface
/**
* Returns content digest for $response.
*
* @param Response $response
*
* @return string
*/
protected function generateContentDigest(Response $response)
@@ -242,8 +239,6 @@ class Store implements StoreInterface
/**
* Invalidates all cache entries that match the request.
*
* @param Request $request A Request instance
*
* @throws \RuntimeException
*/
public function invalidate(Request $request)
@@ -251,19 +246,19 @@ class Store implements StoreInterface
$modified = false;
$key = $this->getCacheKey($request);
$entries = array();
$entries = [];
foreach ($this->getMetadata($key) as $entry) {
$response = $this->restoreResponse($entry[1]);
if ($response->isFresh()) {
$response->expire();
$modified = true;
$entries[] = array($entry[0], $this->persistResponse($response));
$entries[] = [$entry[0], $this->persistResponse($response)];
} else {
$entries[] = $entry;
}
}
if ($modified && false === $this->save($key, serialize($entries))) {
if ($modified && !$this->save($key, serialize($entries))) {
throw new \RuntimeException('Unable to store the metadata.');
}
}
@@ -308,7 +303,7 @@ class Store implements StoreInterface
private function getMetadata($key)
{
if (!$entries = $this->load($key)) {
return array();
return [];
}
return unserialize($entries);
@@ -345,7 +340,7 @@ class Store implements StoreInterface
{
$key = $this->getCacheKey(Request::create($url));
if (isset($this->locks[$key])) {
flock($this->locks[$key], LOCK_UN);
flock($this->locks[$key], \LOCK_UN);
fclose($this->locks[$key]);
unset($this->locks[$key]);
}
@@ -364,64 +359,77 @@ class Store implements StoreInterface
*
* @param string $key The store key
*
* @return string The data associated with the key
* @return string|null The data associated with the key
*/
private function load($key)
{
$path = $this->getPath($key);
return file_exists($path) ? file_get_contents($path) : false;
return file_exists($path) && false !== ($contents = file_get_contents($path)) ? $contents : null;
}
/**
* Save data for the given key.
*
* @param string $key The store key
* @param string $data The data to store
* @param string $key The store key
* @param string $data The data to store
* @param bool $overwrite Whether existing data should be overwritten
*
* @return bool
*/
private function save($key, $data)
private function save($key, $data, $overwrite = true)
{
$path = $this->getPath($key);
if (!$overwrite && file_exists($path)) {
return true;
}
if (isset($this->locks[$key])) {
$fp = $this->locks[$key];
@ftruncate($fp, 0);
@fseek($fp, 0);
$len = @fwrite($fp, $data);
if (strlen($data) !== $len) {
if (\strlen($data) !== $len) {
@ftruncate($fp, 0);
return false;
}
} else {
if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
if (!file_exists(\dirname($path)) && false === @mkdir(\dirname($path), 0777, true) && !is_dir(\dirname($path))) {
return false;
}
$tmpFile = tempnam(dirname($path), basename($path));
$tmpFile = tempnam(\dirname($path), basename($path));
if (false === $fp = @fopen($tmpFile, 'wb')) {
@unlink($tmpFile);
return false;
}
@fwrite($fp, $data);
@fclose($fp);
if ($data != file_get_contents($tmpFile)) {
@unlink($tmpFile);
return false;
}
if (false === @rename($tmpFile, $path)) {
@unlink($tmpFile);
return false;
}
}
@chmod($path, 0666 & ~umask());
return true;
}
public function getPath($key)
{
return $this->root.DIRECTORY_SEPARATOR.substr($key, 0, 2).DIRECTORY_SEPARATOR.substr($key, 2, 2).DIRECTORY_SEPARATOR.substr($key, 4, 2).DIRECTORY_SEPARATOR.substr($key, 6);
return $this->root.\DIRECTORY_SEPARATOR.substr($key, 0, 2).\DIRECTORY_SEPARATOR.substr($key, 2, 2).\DIRECTORY_SEPARATOR.substr($key, 4, 2).\DIRECTORY_SEPARATOR.substr($key, 6);
}
/**
@@ -434,8 +442,6 @@ class Store implements StoreInterface
* headers, use a Vary header to indicate them, and each representation will
* be stored independently under the same cache key.
*
* @param Request $request A Request instance
*
* @return string A key for the given Request
*/
protected function generateCacheKey(Request $request)
@@ -446,8 +452,6 @@ class Store implements StoreInterface
/**
* Returns a cache key for the given Request.
*
* @param Request $request A Request instance
*
* @return string A key for the given Request
*/
private function getCacheKey(Request $request)
@@ -462,8 +466,6 @@ class Store implements StoreInterface
/**
* Persists the Request HTTP headers.
*
* @param Request $request A Request instance
*
* @return array An array of HTTP headers
*/
private function persistRequest(Request $request)
@@ -474,14 +476,12 @@ class Store implements StoreInterface
/**
* Persists the Response HTTP headers.
*
* @param Response $response A Response instance
*
* @return array An array of HTTP headers
*/
private function persistResponse(Response $response)
{
$headers = $response->headers->all();
$headers['X-Status'] = array($response->getStatusCode());
$headers['X-Status'] = [$response->getStatusCode()];
return $headers;
}
@@ -490,19 +490,19 @@ class Store implements StoreInterface
* Restores a Response from the HTTP headers and body.
*
* @param array $headers An array of HTTP headers for the Response
* @param string $body The Response body
* @param string $path Path to the Response body
*
* @return Response
*/
private function restoreResponse($headers, $body = null)
private function restoreResponse($headers, $path = null)
{
$status = $headers['X-Status'][0];
unset($headers['X-Status']);
if (null !== $body) {
$headers['X-Body-File'] = array($body);
if (null !== $path) {
$headers['X-Body-File'] = [$path];
}
return new Response($body, $status, $headers);
return new Response($path, $status, $headers);
}
}
-13
View File
@@ -27,8 +27,6 @@ interface StoreInterface
/**
* Locates a cached Response for the Request provided.
*
* @param Request $request A Request instance
*
* @return Response|null A Response instance, or null if no cache entry was found
*/
public function lookup(Request $request);
@@ -39,25 +37,18 @@ interface StoreInterface
* Existing entries are read and any that match the response are removed. This
* method calls write with the new list of cache entries.
*
* @param Request $request A Request instance
* @param Response $response A Response instance
*
* @return string The key under which the response is stored
*/
public function write(Request $request, Response $response);
/**
* Invalidates all cache entries that match the request.
*
* @param Request $request A Request instance
*/
public function invalidate(Request $request);
/**
* Locks the cache for a given Request.
*
* @param Request $request A Request instance
*
* @return bool|string true if the lock is acquired, the path to the current lock otherwise
*/
public function lock(Request $request);
@@ -65,8 +56,6 @@ interface StoreInterface
/**
* Releases the lock for the given Request.
*
* @param Request $request A Request instance
*
* @return bool False if the lock file does not exist or cannot be unlocked, true otherwise
*/
public function unlock(Request $request);
@@ -74,8 +63,6 @@ interface StoreInterface
/**
* Returns whether or not a lock exists.
*
* @param Request $request A Request instance
*
* @return bool true if lock exists, false otherwise
*/
public function isLocked(Request $request);
@@ -33,16 +33,12 @@ interface SurrogateInterface
/**
* Checks that at least one surrogate has Surrogate capability.
*
* @param Request $request A Request instance
*
* @return bool true if one surrogate has Surrogate capability, false otherwise
*/
public function hasSurrogateCapability(Request $request);
/**
* Adds Surrogate-capability to the given Request.
*
* @param Request $request A Request instance
*/
public function addSurrogateCapability(Request $request);
@@ -50,16 +46,12 @@ interface SurrogateInterface
* Adds HTTP headers to specify that the Response needs to be parsed for Surrogate.
*
* This method only adds an Surrogate HTTP header if the Response has some Surrogate tags.
*
* @param Response $response A Response instance
*/
public function addSurrogateControl(Response $response);
/**
* Checks that the Response needs to be parsed for Surrogate tags.
*
* @param Response $response A Response instance
*
* @return bool true if the Response needs to be parsed, false otherwise
*/
public function needsParsing(Response $response);
@@ -79,9 +71,6 @@ interface SurrogateInterface
/**
* Replaces a Response Surrogate tags with the included resource content.
*
* @param Request $request A Request instance
* @param Response $response A Response instance
*
* @return Response
*/
public function process(Request $request, Response $response);
+28 -26
View File
@@ -11,13 +11,15 @@
namespace Symfony\Component\HttpKernel;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
@@ -25,11 +27,9 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* HttpKernel notifies events to convert a Request object to a Response one.
@@ -51,7 +51,7 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
$this->argumentResolver = $argumentResolver;
if (null === $this->argumentResolver) {
@trigger_error(sprintf('As of 3.1 an %s is used to resolve arguments. In 4.0 the $argumentResolver becomes the %s if no other is provided instead of using the $resolver argument.', ArgumentResolverInterface::class, ArgumentResolver::class), E_USER_DEPRECATED);
@trigger_error(sprintf('As of 3.1 an %s is used to resolve arguments. In 4.0 the $argumentResolver becomes the %s if no other is provided instead of using the $resolver argument.', ArgumentResolverInterface::class, ArgumentResolver::class), \E_USER_DEPRECATED);
// fallback in case of deprecations
$this->argumentResolver = $resolver;
}
@@ -62,13 +62,13 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
*/
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
$request->headers->set('X-Php-Ob-Level', ob_get_level());
$request->headers->set('X-Php-Ob-Level', (string) ob_get_level());
try {
return $this->handleRaw($request, $type);
} catch (\Exception $e) {
if ($e instanceof ConflictingHeadersException) {
$e = new BadRequestHttpException('The request headers contain conflicting information regarding the origin of this request.', $e);
if ($e instanceof RequestExceptionInterface) {
$e = new BadRequestHttpException($e->getMessage(), $e);
}
if (false === $catch) {
$this->finishRequest($request, $type);
@@ -89,14 +89,12 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
}
/**
* @throws \LogicException If the request stack is empty
*
* @internal
*/
public function terminateWithException(\Exception $exception)
public function terminateWithException(\Exception $exception, Request $request = null)
{
if (!$request = $this->requestStack->getMasterRequest()) {
throw new \LogicException('Request stack is empty', 0, $exception);
if (!$request = $request ?: $this->requestStack->getMasterRequest()) {
throw $exception;
}
$response = $this->handleException($exception, $request, self::MASTER_REQUEST);
@@ -150,7 +148,7 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
$arguments = $event->getArguments();
// call controller
$response = call_user_func_array($controller, $arguments);
$response = \call_user_func_array($controller, $arguments);
// view
if (!$response instanceof Response) {
@@ -204,8 +202,7 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
* operations such as {@link RequestStack::getParentRequest()} can lead to
* weird results.
*
* @param Request $request
* @param int $type
* @param int $type
*/
private function finishRequest(Request $request, $type)
{
@@ -242,10 +239,12 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
// the developer asked for a specific status code
if ($response->headers->has('X-Status-Code')) {
@trigger_error(sprintf('Using the X-Status-Code header is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s::allowCustomResponseCode() instead.', GetResponseForExceptionEvent::class), \E_USER_DEPRECATED);
$response->setStatusCode($response->headers->get('X-Status-Code'));
$response->headers->remove('X-Status-Code');
} elseif (!$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) {
} elseif (!$event->isAllowingCustomResponseCode() && !$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) {
// ensure that we actually have an error response
if ($e instanceof HttpExceptionInterface) {
// keep the HTTP status code and headers
@@ -263,14 +262,17 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
}
}
/**
* Returns a human-readable string for the specified variable.
*/
private function varToString($var)
{
if (is_object($var)) {
return sprintf('Object(%s)', get_class($var));
if (\is_object($var)) {
return sprintf('Object(%s)', \get_class($var));
}
if (is_array($var)) {
$a = array();
if (\is_array($var)) {
$a = [];
foreach ($var as $k => $v) {
$a[] = sprintf('%s => %s', $k, $this->varToString($v));
}
@@ -278,7 +280,7 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
return sprintf('Array(%s)', implode(', ', $a));
}
if (is_resource($var)) {
if (\is_resource($var)) {
return sprintf('Resource(%s)', get_resource_type($var));
}
+330 -104
View File
@@ -13,26 +13,30 @@ namespace Symfony\Component\HttpKernel;
use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator;
use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\ClassLoader\ClassCollectionLoader;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Loader\DelegatingLoader;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
use Symfony\Component\DependencyInjection\Loader\GlobFileLoader;
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
use Symfony\Component\HttpKernel\Config\EnvParametersResource;
use Symfony\Component\HttpKernel\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass;
use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;
use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\Config\Loader\DelegatingLoader;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\ClassLoader\ClassCollectionLoader;
/**
* The Kernel is the heart of the Symfony system.
@@ -41,12 +45,12 @@ use Symfony\Component\ClassLoader\ClassCollectionLoader;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Kernel implements KernelInterface, TerminableInterface
abstract class Kernel implements KernelInterface, RebootableInterface, TerminableInterface
{
/**
* @var BundleInterface[]
*/
protected $bundles = array();
protected $bundles = [];
protected $bundleMap;
protected $container;
@@ -58,19 +62,22 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected $startTime;
protected $loadClassCache;
const VERSION = '3.2.13';
const VERSION_ID = 30213;
private $projectDir;
private $warmupDir;
private $requestStackSize = 0;
private $resetServices = false;
const VERSION = '3.4.49';
const VERSION_ID = 30449;
const MAJOR_VERSION = 3;
const MINOR_VERSION = 2;
const RELEASE_VERSION = 13;
const MINOR_VERSION = 4;
const RELEASE_VERSION = 49;
const EXTRA_VERSION = '';
const END_OF_MAINTENANCE = '07/2017';
const END_OF_LIFE = '01/2018';
const END_OF_MAINTENANCE = '11/2020';
const END_OF_LIFE = '11/2021';
/**
* Constructor.
*
* @param string $environment The environment
* @param bool $debug Whether to enable debugging or not
*/
@@ -80,30 +87,42 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$this->debug = (bool) $debug;
$this->rootDir = $this->getRootDir();
$this->name = $this->getName();
if ($this->debug) {
$this->startTime = microtime(true);
}
}
public function __clone()
{
if ($this->debug) {
$this->startTime = microtime(true);
}
$this->booted = false;
$this->container = null;
$this->requestStackSize = 0;
$this->resetServices = false;
}
/**
* Boots the current kernel.
* {@inheritdoc}
*/
public function boot()
{
if (true === $this->booted) {
if (!$this->requestStackSize && $this->resetServices) {
if ($this->container->has('services_resetter')) {
$this->container->get('services_resetter')->reset();
}
$this->resetServices = false;
if ($this->debug) {
$this->startTime = microtime(true);
}
}
return;
}
if ($this->debug) {
$this->startTime = microtime(true);
}
if ($this->debug && !isset($_ENV['SHELL_VERBOSITY']) && !isset($_SERVER['SHELL_VERBOSITY'])) {
putenv('SHELL_VERBOSITY=3');
$_ENV['SHELL_VERBOSITY'] = 3;
$_SERVER['SHELL_VERBOSITY'] = 3;
}
if ($this->loadClassCache) {
$this->doLoadClassCache($this->loadClassCache[0], $this->loadClassCache[1]);
@@ -123,6 +142,16 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$this->booted = true;
}
/**
* {@inheritdoc}
*/
public function reboot($warmupDir)
{
$this->shutdown();
$this->warmupDir = $warmupDir;
$this->boot();
}
/**
* {@inheritdoc}
*/
@@ -154,6 +183,8 @@ abstract class Kernel implements KernelInterface, TerminableInterface
}
$this->container = null;
$this->requestStackSize = 0;
$this->resetServices = false;
}
/**
@@ -161,17 +192,21 @@ abstract class Kernel implements KernelInterface, TerminableInterface
*/
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
if (false === $this->booted) {
$this->boot();
}
$this->boot();
++$this->requestStackSize;
$this->resetServices = true;
return $this->getHttpKernel()->handle($request, $type, $catch);
try {
return $this->getHttpKernel()->handle($request, $type, $catch);
} finally {
--$this->requestStackSize;
}
}
/**
* Gets a HTTP kernel from the container.
*
* @return HttpKernel
* @return HttpKernelInterface
*/
protected function getHttpKernel()
{
@@ -189,10 +224,19 @@ abstract class Kernel implements KernelInterface, TerminableInterface
/**
* {@inheritdoc}
*/
public function getBundle($name, $first = true)
public function getBundle($name, $first = true/*, $noDeprecation = false */)
{
$noDeprecation = false;
if (\func_num_args() >= 3) {
$noDeprecation = func_get_arg(2);
}
if (!$first && !$noDeprecation) {
@trigger_error(sprintf('Passing "false" as the second argument to "%s()" is deprecated as of 3.4 and will be removed in 4.0.', __METHOD__), \E_USER_DEPRECATED);
}
if (!isset($this->bundleMap[$name])) {
throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() method of your %s.php file?', $name, get_class($this)));
throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() method of your "%s.php" file?', $name, static::class));
}
if (true === $first) {
@@ -226,17 +270,13 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$isResource = 0 === strpos($path, 'Resources') && null !== $dir;
$overridePath = substr($path, 9);
$resourceBundle = null;
$bundles = $this->getBundle($bundleName, false);
$files = array();
$bundles = $this->getBundle($bundleName, false, true);
$files = [];
foreach ($bundles as $bundle) {
if ($isResource && file_exists($file = $dir.'/'.$bundle->getName().$overridePath)) {
if (null !== $resourceBundle) {
throw new \RuntimeException(sprintf('"%s" resource is hidden by a resource from the "%s" derived bundle. Create a "%s" file to override the bundle resource.',
$file,
$resourceBundle,
$dir.'/'.$bundles[0]->getName().$overridePath
));
throw new \RuntimeException(sprintf('"%s" resource is hidden by a resource from the "%s" derived bundle. Create a "%s" file to override the bundle resource.', $file, $resourceBundle, $dir.'/'.$bundles[0]->getName().$overridePath));
}
if ($first) {
@@ -254,7 +294,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
}
}
if (count($files) > 0) {
if (\count($files) > 0) {
return $first && $isResource ? $files[0] : $files;
}
@@ -299,12 +339,39 @@ abstract class Kernel implements KernelInterface, TerminableInterface
{
if (null === $this->rootDir) {
$r = new \ReflectionObject($this);
$this->rootDir = dirname($r->getFileName());
$this->rootDir = \dirname($r->getFileName());
}
return $this->rootDir;
}
/**
* Gets the application root dir (path of the project's composer file).
*
* @return string The project root dir
*/
public function getProjectDir()
{
if (null === $this->projectDir) {
$r = new \ReflectionObject($this);
if (!file_exists($dir = $r->getFileName())) {
throw new \LogicException(sprintf('Cannot auto-detect project dir for kernel of class "%s".', $r->name));
}
$dir = $rootDir = \dirname($dir);
while (!file_exists($dir.'/composer.json')) {
if ($dir === \dirname($dir)) {
return $this->projectDir = $rootDir;
}
$dir = \dirname($dir);
}
$this->projectDir = $dir;
}
return $this->projectDir;
}
/**
* {@inheritdoc}
*/
@@ -324,18 +391,30 @@ abstract class Kernel implements KernelInterface, TerminableInterface
*
* @param string $name The cache name prefix
* @param string $extension File extension of the resulting file
*
* @deprecated since version 3.3, to be removed in 4.0. The class cache is not needed anymore when using PHP 7.0.
*/
public function loadClassCache($name = 'classes', $extension = '.php')
{
$this->loadClassCache = array($name, $extension);
if (\PHP_VERSION_ID >= 70000) {
@trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', \E_USER_DEPRECATED);
}
$this->loadClassCache = [$name, $extension];
}
/**
* @internal
*
* @deprecated since version 3.3, to be removed in 4.0.
*/
public function setClassCache(array $classes)
{
file_put_contents($this->getCacheDir().'/classes.map', sprintf('<?php return %s;', var_export($classes, true)));
if (\PHP_VERSION_ID >= 70000) {
@trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', \E_USER_DEPRECATED);
}
file_put_contents(($this->warmupDir ?: $this->getCacheDir()).'/classes.map', sprintf('<?php return %s;', var_export($classes, true)));
}
/**
@@ -343,7 +422,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
*/
public function setAnnotatedClassCache(array $annotatedClasses)
{
file_put_contents($this->getCacheDir().'/annotations.map', sprintf('<?php return %s;', var_export($annotatedClasses, true)));
file_put_contents(($this->warmupDir ?: $this->getCacheDir()).'/annotations.map', sprintf('<?php return %s;', var_export($annotatedClasses, true)));
}
/**
@@ -351,7 +430,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
*/
public function getStartTime()
{
return $this->debug ? $this->startTime : -INF;
return $this->debug && null !== $this->startTime ? $this->startTime : -\INF;
}
/**
@@ -378,10 +457,18 @@ abstract class Kernel implements KernelInterface, TerminableInterface
return 'UTF-8';
}
/**
* @deprecated since version 3.3, to be removed in 4.0.
*/
protected function doLoadClassCache($name, $extension)
{
if (!$this->booted && is_file($this->getCacheDir().'/classes.map')) {
ClassCollectionLoader::load(include($this->getCacheDir().'/classes.map'), $this->getCacheDir(), $name, $this->debug, false, $extension);
if (\PHP_VERSION_ID >= 70000) {
@trigger_error(__METHOD__.'() is deprecated since Symfony 3.3, to be removed in 4.0.', \E_USER_DEPRECATED);
}
$cacheDir = $this->warmupDir ?: $this->getCacheDir();
if (!$this->booted && is_file($cacheDir.'/classes.map')) {
ClassCollectionLoader::load(include($cacheDir.'/classes.map'), $cacheDir, $name, $this->debug, false, $extension);
}
}
@@ -399,18 +486,20 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected function initializeBundles()
{
// init bundles
$this->bundles = array();
$topMostBundles = array();
$directChildren = array();
$this->bundles = [];
$topMostBundles = [];
$directChildren = [];
foreach ($this->registerBundles() as $bundle) {
$name = $bundle->getName();
if (isset($this->bundles[$name])) {
throw new \LogicException(sprintf('Trying to register two bundles with the same name "%s"', $name));
throw new \LogicException(sprintf('Trying to register two bundles with the same name "%s".', $name));
}
$this->bundles[$name] = $bundle;
if ($parentName = $bundle->getParent()) {
@trigger_error('Bundle inheritance is deprecated as of 3.4 and will be removed in 4.0.', \E_USER_DEPRECATED);
if (isset($directChildren[$parentName])) {
throw new \LogicException(sprintf('Bundle "%s" is directly extended by two bundles "%s" and "%s".', $parentName, $name, $directChildren[$parentName]));
}
@@ -424,17 +513,17 @@ abstract class Kernel implements KernelInterface, TerminableInterface
}
// look for orphans
if (!empty($directChildren) && count($diff = array_diff_key($directChildren, $this->bundles))) {
if (!empty($directChildren) && \count($diff = array_diff_key($directChildren, $this->bundles))) {
$diff = array_keys($diff);
throw new \LogicException(sprintf('Bundle "%s" extends bundle "%s", which is not registered.', $directChildren[$diff[0]], $diff[0]));
}
// inheritance
$this->bundleMap = array();
$this->bundleMap = [];
foreach ($topMostBundles as $name => $bundle) {
$bundleMap = array($bundle);
$hierarchy = array($name);
$bundleMap = [$bundle];
$hierarchy = [$name];
while (isset($directChildren[$name])) {
$name = $directChildren[$name];
@@ -449,6 +538,15 @@ abstract class Kernel implements KernelInterface, TerminableInterface
}
}
/**
* The extension point similar to the Bundle::build() method.
*
* Use this method to register compiler passes and manipulate the container during the building process.
*/
protected function build(ContainerBuilder $container)
{
}
/**
* Gets the container class.
*
@@ -480,22 +578,111 @@ abstract class Kernel implements KernelInterface, TerminableInterface
protected function initializeContainer()
{
$class = $this->getContainerClass();
$cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug);
$fresh = true;
if (!$cache->isFresh()) {
$container = $this->buildContainer();
$container->compile();
$this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());
$fresh = false;
$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) {
} catch (\Exception $e) {
} finally {
error_reporting($errorLevel);
}
}
require_once $cache->getPath();
if ($fresh) {
return;
}
$this->container = new $class();
if ($collectDeprecations = $this->debug && !\defined('PHPUNIT_COMPOSER_INSTALL')) {
$collectedLogs = [];
$previousHandler = set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) {
if (\E_USER_DEPRECATED !== $type && \E_DEPRECATED !== $type) {
return $previousHandler ? $previousHandler($type, $message, $file, $line) : false;
}
if (isset($collectedLogs[$message])) {
++$collectedLogs[$message]['count'];
return null;
}
$backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 3);
// Clean the trace by removing first frames added by the error handler itself.
for ($i = 0; isset($backtrace[$i]); ++$i) {
if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
$backtrace = \array_slice($backtrace, 1 + $i);
break;
}
}
$collectedLogs[$message] = [
'type' => $type,
'message' => $message,
'file' => $file,
'line' => $line,
'trace' => $backtrace,
'count' => 1,
];
return null;
});
}
try {
$container = null;
$container = $this->buildContainer();
$container->compile();
} finally {
if ($collectDeprecations) {
restore_error_handler();
file_put_contents($cacheDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs)));
file_put_contents($cacheDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : '');
}
}
if (null === $oldContainer && file_exists($cache->getPath())) {
$errorLevel = error_reporting(\E_ALL ^ \E_WARNING);
try {
$oldContainer = include $cache->getPath();
} catch (\Throwable $e) {
} catch (\Exception $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();
$this->container->set('kernel', $this);
if (!$fresh && $this->container->has('cache_warmer')) {
if ($oldContainer && \get_class($this->container) !== $oldContainer->name) {
// Because concurrent requests might still be using them,
// old container files are not removed immediately,
// but on a next dump of the container.
static $legacyContainers = [];
$oldContainerDir = \dirname($oldContainer->getFileName());
$legacyContainers[$oldContainerDir.'.legacy'] = true;
foreach (glob(\dirname($oldContainerDir).\DIRECTORY_SEPARATOR.'*.legacy', \GLOB_NOSORT) as $legacyContainer) {
if (!isset($legacyContainers[$legacyContainer]) && @unlink($legacyContainer)) {
(new Filesystem())->remove(substr($legacyContainer, 0, -7));
}
}
touch($oldContainerDir.'.legacy');
}
if ($this->container->has('cache_warmer')) {
$this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir'));
}
}
@@ -507,32 +694,33 @@ abstract class Kernel implements KernelInterface, TerminableInterface
*/
protected function getKernelParameters()
{
$bundles = array();
$bundlesMetadata = array();
$bundles = [];
$bundlesMetadata = [];
foreach ($this->bundles as $name => $bundle) {
$bundles[$name] = get_class($bundle);
$bundlesMetadata[$name] = array(
$bundles[$name] = \get_class($bundle);
$bundlesMetadata[$name] = [
'parent' => $bundle->getParent(),
'path' => $bundle->getPath(),
'namespace' => $bundle->getNamespace(),
);
];
}
return array_merge(
array(
[
'kernel.root_dir' => realpath($this->rootDir) ?: $this->rootDir,
'kernel.project_dir' => realpath($this->getProjectDir()) ?: $this->getProjectDir(),
'kernel.environment' => $this->environment,
'kernel.debug' => $this->debug,
'kernel.name' => $this->name,
'kernel.cache_dir' => realpath($this->getCacheDir()) ?: $this->getCacheDir(),
'kernel.cache_dir' => realpath($cacheDir = $this->warmupDir ?: $this->getCacheDir()) ?: $cacheDir,
'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(),
'kernel.bundles' => $bundles,
'kernel.bundles_metadata' => $bundlesMetadata,
'kernel.charset' => $this->getCharset(),
'kernel.container_class' => $this->getContainerClass(),
),
$this->getEnvParameters()
],
$this->getEnvParameters(false)
);
}
@@ -542,12 +730,19 @@ abstract class Kernel implements KernelInterface, TerminableInterface
* Only the parameters starting with "SYMFONY__" are considered.
*
* @return array An array of parameters
*
* @deprecated since version 3.3, to be removed in 4.0
*/
protected function getEnvParameters()
{
$parameters = array();
if (0 === \func_num_args() || func_get_arg(0)) {
@trigger_error(sprintf('The "%s()" method is deprecated as of 3.3 and will be removed in 4.0. Use the %%env()%% syntax to get the value of any environment variable from configuration files instead.', __METHOD__), \E_USER_DEPRECATED);
}
$parameters = [];
foreach ($_SERVER as $key => $value) {
if (0 === strpos($key, 'SYMFONY__')) {
@trigger_error(sprintf('The support of special environment variables that start with SYMFONY__ (such as "%s") is deprecated as of 3.3 and will be removed in 4.0. Use the %%env()%% syntax instead to get the value of environment variables in configuration files.', $key), \E_USER_DEPRECATED);
$parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value;
}
}
@@ -564,13 +759,13 @@ abstract class Kernel implements KernelInterface, TerminableInterface
*/
protected function buildContainer()
{
foreach (array('cache' => $this->getCacheDir(), 'logs' => $this->getLogDir()) as $name => $dir) {
foreach (['cache' => $this->warmupDir ?: $this->getCacheDir(), 'logs' => $this->getLogDir()] as $name => $dir) {
if (!is_dir($dir)) {
if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
throw new \RuntimeException(sprintf("Unable to create the %s directory (%s)\n", $name, $dir));
throw new \RuntimeException(sprintf('Unable to create the "%s" directory (%s).', $name, $dir));
}
} elseif (!is_writable($dir)) {
throw new \RuntimeException(sprintf("Unable to write in the %s directory (%s)\n", $name, $dir));
throw new \RuntimeException(sprintf('Unable to write in the "%s" directory (%s).', $name, $dir));
}
}
@@ -582,7 +777,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$container->merge($cont);
}
$container->addCompilerPass(new AddClassesToCachePass($this));
$container->addCompilerPass(new AddAnnotatedClassesToCachePass($this));
$container->addResource(new EnvParametersResource('SYMFONY__'));
return $container;
@@ -590,26 +785,30 @@ abstract class Kernel implements KernelInterface, TerminableInterface
/**
* Prepares the ContainerBuilder before it is compiled.
*
* @param ContainerBuilder $container A ContainerBuilder instance
*/
protected function prepareContainer(ContainerBuilder $container)
{
$extensions = array();
$extensions = [];
foreach ($this->bundles as $bundle) {
if ($extension = $bundle->getContainerExtension()) {
$container->registerExtension($extension);
$extensions[] = $extension->getAlias();
}
if ($this->debug) {
$container->addObjectResource($bundle);
}
}
foreach ($this->bundles as $bundle) {
$bundle->build($container);
}
$this->build($container);
foreach ($container->getExtensions() as $extension) {
$extensions[] = $extension->getAlias();
}
// ensure these extensions are implicitly loaded
$container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions));
}
@@ -624,6 +823,9 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$container = new ContainerBuilder();
$container->getParameterBag()->add($this->getKernelParameters());
if ($this instanceof CompilerPassInterface) {
$container->addCompilerPass($this, PassConfig::TYPE_BEFORE_OPTIMIZATION, -10000);
}
if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator')) {
$container->setProxyInstantiator(new RuntimeInstantiator());
}
@@ -645,32 +847,52 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$dumper = new PhpDumper($container);
if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper')) {
$dumper->setProxyDumper(new ProxyDumper(md5($cache->getPath())));
$dumper->setProxyDumper(new ProxyDumper());
}
$content = $dumper->dump(array('class' => $class, 'base_class' => $baseClass, 'file' => $cache->getPath(), 'debug' => $this->debug));
$content = $dumper->dump([
'class' => $class,
'base_class' => $baseClass,
'file' => $cache->getPath(),
'as_files' => true,
'debug' => $this->debug,
'inline_class_loader_parameter' => \PHP_VERSION_ID >= 70000 && !$this->loadClassCache && !class_exists(ClassCollectionLoader::class, false) ? 'container.dumper.inline_class_loader' : null,
'build_time' => $container->hasParameter('kernel.container_build_time') ? $container->getParameter('kernel.container_build_time') : time(),
]);
$cache->write($content, $container->getResources());
$rootCode = array_pop($content);
$dir = \dirname($cache->getPath()).'/';
$fs = new Filesystem();
foreach ($content as $file => $code) {
$fs->dumpFile($dir.$file, $code);
@chmod($dir.$file, 0666 & ~umask());
}
$legacyFile = \dirname($dir.key($content)).'.legacy';
if (file_exists($legacyFile)) {
@unlink($legacyFile);
}
$cache->write($rootCode, $container->getResources());
}
/**
* Returns a loader for the container.
*
* @param ContainerInterface $container The service container
*
* @return DelegatingLoader The loader
*/
protected function getContainerLoader(ContainerInterface $container)
{
$locator = new FileLocator($this);
$resolver = new LoaderResolver(array(
$resolver = new LoaderResolver([
new XmlFileLoader($container, $locator),
new YamlFileLoader($container, $locator),
new IniFileLoader($container, $locator),
new PhpFileLoader($container, $locator),
new GlobFileLoader($container, $locator),
new DirectoryLoader($container, $locator),
new ClosureLoader($container),
));
]);
return new DelegatingLoader($resolver);
}
@@ -687,7 +909,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
*/
public static function stripComments($source)
{
if (!function_exists('token_get_all')) {
if (!\function_exists('token_get_all')) {
return $source;
}
@@ -699,14 +921,14 @@ abstract class Kernel implements KernelInterface, TerminableInterface
$token = $tokens[$i];
if (!isset($token[1]) || 'b"' === $token) {
$rawChunk .= $token;
} elseif (T_START_HEREDOC === $token[0]) {
} elseif (\T_START_HEREDOC === $token[0]) {
$output .= $rawChunk.$token[1];
do {
$token = $tokens[++$i];
$output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token;
} while ($token[0] !== T_END_HEREDOC);
} while (\T_END_HEREDOC !== $token[0]);
$rawChunk = '';
} elseif (T_WHITESPACE === $token[0]) {
} elseif (\T_WHITESPACE === $token[0]) {
if ($ignoreSpace) {
$ignoreSpace = false;
@@ -714,14 +936,14 @@ abstract class Kernel implements KernelInterface, TerminableInterface
}
// replace multiple new lines with a single newline
$rawChunk .= preg_replace(array('/\n{2,}/S'), "\n", $token[1]);
} elseif (in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
$rawChunk .= preg_replace(['/\n{2,}/S'], "\n", $token[1]);
} elseif (\in_array($token[0], [\T_COMMENT, \T_DOC_COMMENT])) {
$ignoreSpace = true;
} else {
$rawChunk .= $token[1];
// The PHP-open tag already has a new-line
if (T_OPEN_TAG === $token[0]) {
if (\T_OPEN_TAG === $token[0]) {
$ignoreSpace = true;
}
}
@@ -740,12 +962,16 @@ abstract class Kernel implements KernelInterface, TerminableInterface
public function serialize()
{
return serialize(array($this->environment, $this->debug));
return serialize([$this->environment, $this->debug]);
}
public function unserialize($data)
{
list($environment, $debug) = unserialize($data);
if (\PHP_VERSION_ID >= 70000) {
list($environment, $debug) = unserialize($data, ['allowed_classes' => false]);
} else {
list($environment, $debug) = unserialize($data);
}
$this->__construct($environment, $debug);
}
-16
View File
@@ -26,8 +26,6 @@ final class KernelEvents
* other code in the framework is executed.
*
* @Event("Symfony\Component\HttpKernel\Event\GetResponseEvent")
*
* @var string
*/
const REQUEST = 'kernel.request';
@@ -38,8 +36,6 @@ final class KernelEvents
* to modify the thrown exception.
*
* @Event("Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent")
*
* @var string
*/
const EXCEPTION = 'kernel.exception';
@@ -51,8 +47,6 @@ final class KernelEvents
* controller.
*
* @Event("Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent")
*
* @var string
*/
const VIEW = 'kernel.view';
@@ -64,8 +58,6 @@ final class KernelEvents
* request.
*
* @Event("Symfony\Component\HttpKernel\Event\FilterControllerEvent")
*
* @var string
*/
const CONTROLLER = 'kernel.controller';
@@ -76,8 +68,6 @@ final class KernelEvents
* the controller.
*
* @Event("Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent")
*
* @var string
*/
const CONTROLLER_ARGUMENTS = 'kernel.controller_arguments';
@@ -89,8 +79,6 @@ final class KernelEvents
* replied.
*
* @Event("Symfony\Component\HttpKernel\Event\FilterResponseEvent")
*
* @var string
*/
const RESPONSE = 'kernel.response';
@@ -100,8 +88,6 @@ final class KernelEvents
* This event allows you to run expensive post-response jobs.
*
* @Event("Symfony\Component\HttpKernel\Event\PostResponseEvent")
*
* @var string
*/
const TERMINATE = 'kernel.terminate';
@@ -112,8 +98,6 @@ final class KernelEvents
* the application, when it was changed during the request.
*
* @Event("Symfony\Component\HttpKernel\Event\FinishRequestEvent")
*
* @var string
*/
const FINISH_REQUEST = 'kernel.finish_request';
}

Some files were not shown because too many files have changed in this diff Show More