remove dependencies
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
/Tests export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
@@ -1,2 +0,0 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
@@ -1,194 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Bundle;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerAware;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
/**
|
||||
* An implementation of BundleInterface that adds a few conventions
|
||||
* for DependencyInjection extensions and Console commands.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
abstract class Bundle extends ContainerAware implements BundleInterface
|
||||
{
|
||||
protected $name;
|
||||
protected $reflected;
|
||||
protected $extension;
|
||||
|
||||
/**
|
||||
* Boots the Bundle.
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdowns the Bundle.
|
||||
*/
|
||||
public function shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the bundle.
|
||||
*
|
||||
* It is only ever called once when the cache is empty.
|
||||
*
|
||||
* This method can be overridden to register compilation passes,
|
||||
* other extensions, ...
|
||||
*
|
||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||
*/
|
||||
public function build(ContainerBuilder $container)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bundle's container extension.
|
||||
*
|
||||
* @return ExtensionInterface|null The container extension
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getContainerExtension()
|
||||
{
|
||||
if (null === $this->extension) {
|
||||
$basename = preg_replace('/Bundle$/', '', $this->getName());
|
||||
|
||||
$class = $this->getNamespace().'\\DependencyInjection\\'.$basename.'Extension';
|
||||
if (class_exists($class)) {
|
||||
$extension = new $class();
|
||||
|
||||
// check naming convention
|
||||
$expectedAlias = Container::underscore($basename);
|
||||
if ($expectedAlias != $extension->getAlias()) {
|
||||
throw new \LogicException(sprintf(
|
||||
'The extension alias for the default extension of a '.
|
||||
'bundle must be the underscored version of the '.
|
||||
'bundle name ("%s" instead of "%s")',
|
||||
$expectedAlias, $extension->getAlias()
|
||||
));
|
||||
}
|
||||
|
||||
$this->extension = $extension;
|
||||
} else {
|
||||
$this->extension = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->extension) {
|
||||
return $this->extension;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Bundle namespace.
|
||||
*
|
||||
* @return string The Bundle namespace
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
if (null === $this->reflected) {
|
||||
$this->reflected = new \ReflectionObject($this);
|
||||
}
|
||||
|
||||
return $this->reflected->getNamespaceName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Bundle directory path.
|
||||
*
|
||||
* @return string The Bundle absolute path
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
if (null === $this->reflected) {
|
||||
$this->reflected = new \ReflectionObject($this);
|
||||
}
|
||||
|
||||
return dirname($this->reflected->getFileName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bundle parent name.
|
||||
*
|
||||
* @return string The Bundle parent name it overrides or null if no parent
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bundle name (the class short name).
|
||||
*
|
||||
* @return string The Bundle name
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
final public function getName()
|
||||
{
|
||||
if (null !== $this->name) {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
$name = get_class($this);
|
||||
$pos = strrpos($name, '\\');
|
||||
|
||||
return $this->name = false === $pos ? $name : substr($name, $pos + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and registers Commands.
|
||||
*
|
||||
* Override this method if your bundle commands do not follow the conventions:
|
||||
*
|
||||
* * 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)
|
||||
{
|
||||
if (!$dir = realpath($this->getPath().'/Command')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$finder = new Finder();
|
||||
$finder->files()->name('*Command.php')->in($dir);
|
||||
|
||||
$prefix = $this->getNamespace().'\\Command';
|
||||
foreach ($finder as $file) {
|
||||
$ns = $prefix;
|
||||
if ($relativePath = $file->getRelativePath()) {
|
||||
$ns .= '\\'.strtr($relativePath, '/', '\\');
|
||||
}
|
||||
$r = new \ReflectionClass($ns.'\\'.$file->getBasename('.php'));
|
||||
if ($r->isSubclassOf('Symfony\\Component\\Console\\Command\\Command') && !$r->isAbstract()) {
|
||||
$application->add($r->newInstance());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Bundle;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
|
||||
|
||||
/**
|
||||
* BundleInterface.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
interface BundleInterface extends ContainerAwareInterface
|
||||
{
|
||||
/**
|
||||
* Boots the Bundle.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function boot();
|
||||
|
||||
/**
|
||||
* Shutdowns the Bundle.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function shutdown();
|
||||
|
||||
/**
|
||||
* Builds the bundle.
|
||||
*
|
||||
* It is only ever called once when the cache is empty.
|
||||
*
|
||||
* @param ContainerBuilder $container A ContainerBuilder instance
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function build(ContainerBuilder $container);
|
||||
|
||||
/**
|
||||
* Returns the container extension that should be implicitly loaded.
|
||||
*
|
||||
* @return ExtensionInterface|null The default extension or null if there is none
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getContainerExtension();
|
||||
|
||||
/**
|
||||
* Returns the bundle name that this bundle overrides.
|
||||
*
|
||||
* Despite its name, this method does not imply any parent/child relationship
|
||||
* between the bundles, just a way to extend and override an existing
|
||||
* bundle.
|
||||
*
|
||||
* @return string The Bundle name it overrides or null if no parent
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getParent();
|
||||
|
||||
/**
|
||||
* Returns the bundle name (the class short name).
|
||||
*
|
||||
* @return string The Bundle name
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Gets the Bundle namespace.
|
||||
*
|
||||
* @return string The Bundle namespace
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getNamespace();
|
||||
|
||||
/**
|
||||
* Gets the Bundle directory path.
|
||||
*
|
||||
* The path should always be returned as a Unix path (with /).
|
||||
*
|
||||
* @return string The Bundle absolute path
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getPath();
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
2.1.0
|
||||
-----
|
||||
|
||||
* [BC BREAK] the charset is now configured via the Kernel::getCharset() method
|
||||
* [BC BREAK] the current locale for the user is not stored anymore in the session
|
||||
* added the HTTP method to the profiler storage
|
||||
* updated all listeners to implement EventSubscriberInterface
|
||||
* added TimeDataCollector
|
||||
* added ContainerAwareTraceableEventDispatcher
|
||||
* moved TraceableEventDispatcherInterface to the EventDispatcher component
|
||||
* added RouterListener, LocaleListener, and StreamedResponseListener
|
||||
* added CacheClearerInterface (and ChainCacheClearer)
|
||||
* added a kernel.terminate event (via TerminableInterface and PostResponseEvent)
|
||||
* added a Stopwatch class
|
||||
* added WarmableInterface
|
||||
* improved extensibility between bundles
|
||||
* added profiler storages for Memcache(d), File-based, MongoDB, Redis
|
||||
* moved Filesystem class to its own component
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\CacheClearer;
|
||||
|
||||
/**
|
||||
* CacheClearerInterface.
|
||||
*
|
||||
* @author Dustin Dobervich <ddobervich@gmail.com>
|
||||
*/
|
||||
interface CacheClearerInterface
|
||||
{
|
||||
/**
|
||||
* Clears any caches necessary.
|
||||
*
|
||||
* @param string $cacheDir The cache directory.
|
||||
*/
|
||||
public function clear($cacheDir);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\CacheClearer;
|
||||
|
||||
/**
|
||||
* ChainCacheClearer.
|
||||
*
|
||||
* @author Dustin Dobervich <ddobervich@gmail.com>
|
||||
*/
|
||||
class ChainCacheClearer implements CacheClearerInterface
|
||||
{
|
||||
/**
|
||||
* @var array $clearers
|
||||
*/
|
||||
protected $clearers;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of ChainCacheClearer.
|
||||
*
|
||||
* @param array $clearers The initial clearers.
|
||||
*/
|
||||
public function __construct(array $clearers = array())
|
||||
{
|
||||
$this->clearers = $clearers;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function clear($cacheDir)
|
||||
{
|
||||
foreach ($this->clearers as $clearer) {
|
||||
$clearer->clear($cacheDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a cache clearer to the aggregate.
|
||||
*
|
||||
* @param CacheClearerInterface $clearer
|
||||
*/
|
||||
public function add(CacheClearerInterface $clearer)
|
||||
{
|
||||
$this->clearers[] = $clearer;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\CacheWarmer;
|
||||
|
||||
/**
|
||||
* Abstract cache warmer that knows how to write a file to the cache.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class CacheWarmer implements CacheWarmerInterface
|
||||
{
|
||||
protected function writeCacheFile($file, $content)
|
||||
{
|
||||
$tmpFile = tempnam(dirname($file), basename($file));
|
||||
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $file)) {
|
||||
@chmod($file, 0666 & ~umask());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $file));
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\CacheWarmer;
|
||||
|
||||
/**
|
||||
* Aggregates several cache warmers into a single one.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class CacheWarmerAggregate implements CacheWarmerInterface
|
||||
{
|
||||
protected $warmers;
|
||||
protected $optionalsEnabled;
|
||||
|
||||
public function __construct(array $warmers = array())
|
||||
{
|
||||
$this->setWarmers($warmers);
|
||||
$this->optionalsEnabled = false;
|
||||
}
|
||||
|
||||
public function enableOptionalWarmers()
|
||||
{
|
||||
$this->optionalsEnabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Warms up the cache.
|
||||
*
|
||||
* @param string $cacheDir The cache directory
|
||||
*/
|
||||
public function warmUp($cacheDir)
|
||||
{
|
||||
foreach ($this->warmers as $warmer) {
|
||||
if (!$this->optionalsEnabled && $warmer->isOptional()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$warmer->warmUp($cacheDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this warmer is optional or not.
|
||||
*
|
||||
* @return Boolean always true
|
||||
*/
|
||||
public function isOptional()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setWarmers(array $warmers)
|
||||
{
|
||||
$this->warmers = array();
|
||||
foreach ($warmers as $warmer) {
|
||||
$this->add($warmer);
|
||||
}
|
||||
}
|
||||
|
||||
public function add(CacheWarmerInterface $warmer)
|
||||
{
|
||||
$this->warmers[] = $warmer;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\CacheWarmer;
|
||||
|
||||
/**
|
||||
* Interface for classes able to warm up the cache.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface CacheWarmerInterface extends WarmableInterface
|
||||
{
|
||||
/**
|
||||
* Checks whether this warmer is optional or not.
|
||||
*
|
||||
* Optional warmers can be ignored on certain conditions.
|
||||
*
|
||||
* A warmer should return true if the cache can be
|
||||
* generated incrementally and on-demand.
|
||||
*
|
||||
* @return Boolean true if the warmer is optional, false otherwise
|
||||
*/
|
||||
public function isOptional();
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\CacheWarmer;
|
||||
|
||||
/**
|
||||
* Interface for classes that support warming their cache.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface WarmableInterface
|
||||
{
|
||||
/**
|
||||
* Warms up the cache.
|
||||
*
|
||||
* @param string $cacheDir The cache directory
|
||||
*/
|
||||
public function warmUp($cacheDir);
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\BrowserKit\Client as BaseClient;
|
||||
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\HttpKernel\TerminableInterface;
|
||||
|
||||
/**
|
||||
* Client simulates a browser and makes requests to a Kernel object.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class Client extends BaseClient
|
||||
{
|
||||
protected $kernel;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
|
||||
parent::__construct($server, $history, $cookieJar);
|
||||
|
||||
$this->followRedirects = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a request.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*/
|
||||
protected function doRequest($request)
|
||||
{
|
||||
$response = $this->kernel->handle($request);
|
||||
|
||||
if ($this->kernel instanceof TerminableInterface) {
|
||||
$this->kernel->terminate($request, $response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the script to execute when the request must be insulated.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*/
|
||||
protected function getScript($request)
|
||||
{
|
||||
$kernel = str_replace("'", "\\'", serialize($this->kernel));
|
||||
$request = str_replace("'", "\\'", serialize($request));
|
||||
|
||||
$r = new \ReflectionClass('\\Symfony\\Component\\ClassLoader\\UniversalClassLoader');
|
||||
$requirePath = str_replace("'", "\\'", $r->getFileName());
|
||||
|
||||
$symfonyPath = str_replace("'", "\\'", realpath(__DIR__.'/../../..'));
|
||||
|
||||
return <<<EOF
|
||||
<?php
|
||||
|
||||
require_once '$requirePath';
|
||||
|
||||
\$loader = new Symfony\Component\ClassLoader\UniversalClassLoader();
|
||||
\$loader->registerNamespaces(array('Symfony' => '$symfonyPath'));
|
||||
\$loader->register();
|
||||
|
||||
\$kernel = unserialize('$kernel');
|
||||
echo serialize(\$kernel->handle(unserialize('$request')));
|
||||
EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the BrowserKit request to a HttpKernel request.
|
||||
*
|
||||
* @param DomRequest $request A Request instance
|
||||
*
|
||||
* @return Request A Request instance
|
||||
*/
|
||||
protected function filterRequest(DomRequest $request)
|
||||
{
|
||||
$httpRequest = Request::create($request->getUri(), $request->getMethod(), $request->getParameters(), $request->getCookies(), $request->getFiles(), $request->getServer(), $request->getContent());
|
||||
|
||||
$httpRequest->files->replace($this->filterFiles($httpRequest->files->all()));
|
||||
|
||||
return $httpRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters an array of files.
|
||||
*
|
||||
* This method created test instances of UploadedFile so that the move()
|
||||
* method can be called on those instances.
|
||||
*
|
||||
* If the size of a file is greater than the allowed size (from php.ini) then
|
||||
* an invalid UploadedFile is returned with an error set to UPLOAD_ERR_INI_SIZE.
|
||||
*
|
||||
* @see Symfony\Component\HttpFoundation\File\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();
|
||||
foreach ($files as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$filtered[$key] = $this->filterFiles($value);
|
||||
} elseif ($value instanceof UploadedFile) {
|
||||
if ($value->isValid() && $value->getSize() > UploadedFile::getMaxFilesize()) {
|
||||
$filtered[$key] = new UploadedFile(
|
||||
'',
|
||||
$value->getClientOriginalName(),
|
||||
$value->getClientMimeType(),
|
||||
0,
|
||||
UPLOAD_ERR_INI_SIZE,
|
||||
true
|
||||
);
|
||||
} else {
|
||||
$filtered[$key] = new UploadedFile(
|
||||
$value->getPathname(),
|
||||
$value->getClientOriginalName(),
|
||||
$value->getClientMimeType(),
|
||||
$value->getClientSize(),
|
||||
$value->getError(),
|
||||
true
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$filtered[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the HttpKernel response to a BrowserKit response.
|
||||
*
|
||||
* @param Response $response A Response instance
|
||||
*
|
||||
* @return Response A Response 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);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Config;
|
||||
|
||||
use Symfony\Component\Config\FileLocator as BaseFileLocator;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
|
||||
/**
|
||||
* FileLocator uses the KernelInterface to locate resources in bundles.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class FileLocator extends BaseFileLocator
|
||||
{
|
||||
private $kernel;
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param KernelInterface $kernel A KernelInterface instance
|
||||
* @param string $path The path the global resource directory
|
||||
* @param string|array $paths A path or an array of paths where to look for resources
|
||||
*/
|
||||
public function __construct(KernelInterface $kernel, $path = null, array $paths = array())
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
$this->path = $path;
|
||||
$paths[] = $path;
|
||||
|
||||
parent::__construct($paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function locate($file, $currentPath = null, $first = true)
|
||||
{
|
||||
if ('@' === $file[0]) {
|
||||
return $this->kernel->locateResource($file, $this->path, $first);
|
||||
}
|
||||
|
||||
return parent::locate($file, $currentPath, $first);
|
||||
}
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Controller;
|
||||
|
||||
use Symfony\Component\HttpKernel\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.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class ControllerResolver implements ControllerResolverInterface
|
||||
{
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param LoggerInterface $logger A LoggerInterface instance
|
||||
*/
|
||||
public function __construct(LoggerInterface $logger = null)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Controller instance associated with a Request.
|
||||
*
|
||||
* This method looks for a '_controller' request attribute that represents
|
||||
* the controller name (a string like ClassName::MethodName).
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @return mixed|Boolean A PHP callable representing the Controller,
|
||||
* or false if this resolver is not able to determine the controller
|
||||
*
|
||||
* @throws \InvalidArgumentException|\LogicException If the controller can't be found
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getController(Request $request)
|
||||
{
|
||||
if (!$controller = $request->attributes->get('_controller')) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->warn('Unable to look for the controller as the "_controller" parameter is missing');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_array($controller) || (is_object($controller) && method_exists($controller, '__invoke'))) {
|
||||
return $controller;
|
||||
}
|
||||
|
||||
if (false === strpos($controller, ':')) {
|
||||
if (method_exists($controller, '__invoke')) {
|
||||
return new $controller;
|
||||
} elseif (function_exists($controller)) {
|
||||
return $controller;
|
||||
}
|
||||
}
|
||||
|
||||
list($controller, $method) = $this->createController($controller);
|
||||
|
||||
if (!method_exists($controller, $method)) {
|
||||
throw new \InvalidArgumentException(sprintf('Method "%s::%s" does not exist.', get_class($controller), $method));
|
||||
}
|
||||
|
||||
return array($controller, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the arguments to pass to the controller.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param mixed $controller A PHP callable
|
||||
*
|
||||
* @throws \RuntimeException When value for argument given is not provided
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getArguments(Request $request, $controller)
|
||||
{
|
||||
if (is_array($controller)) {
|
||||
$r = new \ReflectionMethod($controller[0], $controller[1]);
|
||||
} elseif (is_object($controller) && !$controller instanceof \Closure) {
|
||||
$r = new \ReflectionObject($controller);
|
||||
$r = $r->getMethod('__invoke');
|
||||
} else {
|
||||
$r = new \ReflectionFunction($controller);
|
||||
}
|
||||
|
||||
return $this->doGetArguments($request, $controller, $r->getParameters());
|
||||
}
|
||||
|
||||
protected function doGetArguments(Request $request, $controller, array $parameters)
|
||||
{
|
||||
$attributes = $request->attributes->all();
|
||||
$arguments = array();
|
||||
foreach ($parameters as $param) {
|
||||
if (array_key_exists($param->name, $attributes)) {
|
||||
$arguments[] = $attributes[$param->name];
|
||||
} elseif ($param->getClass() && $param->getClass()->isInstance($request)) {
|
||||
$arguments[] = $request;
|
||||
} elseif ($param->isDefaultValueAvailable()) {
|
||||
$arguments[] = $param->getDefaultValue();
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument (because there is no default value or because there is a non optional argument after this one).', $repr, $param->name));
|
||||
}
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a callable for the given controller.
|
||||
*
|
||||
* @param string $controller A Controller string
|
||||
*
|
||||
* @return mixed A PHP callable
|
||||
*/
|
||||
protected function createController($controller)
|
||||
{
|
||||
if (false === strpos($controller, '::')) {
|
||||
throw new \InvalidArgumentException(sprintf('Unable to find controller "%s".', $controller));
|
||||
}
|
||||
|
||||
list($class, $method) = explode('::', $controller, 2);
|
||||
|
||||
if (!class_exists($class)) {
|
||||
throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
|
||||
}
|
||||
|
||||
return array(new $class(), $method);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Controller;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* A ControllerResolverInterface implementation knows how to determine the
|
||||
* controller to execute based on a Request object.
|
||||
*
|
||||
* It can also determine the arguments to pass to the Controller.
|
||||
*
|
||||
* A Controller can be any valid PHP callable.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
interface ControllerResolverInterface
|
||||
{
|
||||
/**
|
||||
* Returns the Controller instance associated with a Request.
|
||||
*
|
||||
* 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
|
||||
* controller but cannot because of some errors made by the developer.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @return mixed|Boolean A PHP callable representing the Controller,
|
||||
* or false if this resolver is not able to determine the controller
|
||||
*
|
||||
* @throws \InvalidArgumentException|\LogicException If the controller can't be found
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getController(Request $request);
|
||||
|
||||
/**
|
||||
* Returns the arguments to pass to the controller.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param mixed $controller A PHP callable
|
||||
*
|
||||
* @return array An array of arguments to pass to the controller
|
||||
*
|
||||
* @throws \RuntimeException When value for argument given is not provided
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getArguments(Request $request, $controller);
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* ConfigDataCollector.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ConfigDataCollector extends DataCollector
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param KernelInterface $kernel A KernelInterface instance
|
||||
*/
|
||||
public function setKernel(KernelInterface $kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||
{
|
||||
$this->data = array(
|
||||
'token' => $response->headers->get('X-Debug-Token'),
|
||||
'symfony_version' => Kernel::VERSION,
|
||||
'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'),
|
||||
'bundles' => array(),
|
||||
);
|
||||
|
||||
if (isset($this->kernel)) {
|
||||
foreach ($this->kernel->getBundles() as $name => $bundle) {
|
||||
$this->data['bundles'][$name] = $bundle->getPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token.
|
||||
*
|
||||
* @return string The token
|
||||
*/
|
||||
public function getToken()
|
||||
{
|
||||
return $this->data['token'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Symfony version.
|
||||
*
|
||||
* @return string The Symfony version
|
||||
*/
|
||||
public function getSymfonyVersion()
|
||||
{
|
||||
return $this->data['symfony_version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the PHP version.
|
||||
*
|
||||
* @return string The PHP version
|
||||
*/
|
||||
public function getPhpVersion()
|
||||
{
|
||||
return $this->data['php_version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the application name.
|
||||
*
|
||||
* @return string The application name
|
||||
*/
|
||||
public function getAppName()
|
||||
{
|
||||
return $this->data['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the environment.
|
||||
*
|
||||
* @return string The environment
|
||||
*/
|
||||
public function getEnv()
|
||||
{
|
||||
return $this->data['env'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the debug is enabled.
|
||||
*
|
||||
* @return Boolean true if debug is enabled, false otherwise
|
||||
*/
|
||||
public function isDebug()
|
||||
{
|
||||
return $this->data['debug'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the XDebug is enabled.
|
||||
*
|
||||
* @return Boolean true if XDebug is enabled, false otherwise
|
||||
*/
|
||||
public function hasXDebug()
|
||||
{
|
||||
return $this->data['xdebug_enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if EAccelerator is enabled.
|
||||
*
|
||||
* @return Boolean true if EAccelerator is enabled, false otherwise
|
||||
*/
|
||||
public function hasEAccelerator()
|
||||
{
|
||||
return $this->data['eaccel_enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if APC is enabled.
|
||||
*
|
||||
* @return Boolean true if APC is enabled, false otherwise
|
||||
*/
|
||||
public function hasApc()
|
||||
{
|
||||
return $this->data['apc_enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if XCache is enabled.
|
||||
*
|
||||
* @return Boolean true if XCache is enabled, false otherwise
|
||||
*/
|
||||
public function hasXCache()
|
||||
{
|
||||
return $this->data['xcache_enabled'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any accelerator is enabled.
|
||||
*
|
||||
* @return Boolean true if any accelerator is enabled, false otherwise
|
||||
*/
|
||||
public function hasAccelerator()
|
||||
{
|
||||
return $this->hasApc() || $this->hasEAccelerator() || $this->hasXCache();
|
||||
}
|
||||
|
||||
public function getBundles()
|
||||
{
|
||||
return $this->data['bundles'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'config';
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
/**
|
||||
* DataCollector.
|
||||
*
|
||||
* Children of this class must store the collected data in the data property.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class DataCollector implements DataCollectorInterface, \Serializable
|
||||
{
|
||||
protected $data;
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize($this->data);
|
||||
}
|
||||
|
||||
public function unserialize($data)
|
||||
{
|
||||
$this->data = unserialize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a PHP variable to a string.
|
||||
*
|
||||
* @param mixed $var A PHP variable
|
||||
*
|
||||
* @return string The string representation of the variable
|
||||
*/
|
||||
protected function varToString($var)
|
||||
{
|
||||
if (is_object($var)) {
|
||||
return sprintf('Object(%s)', get_class($var));
|
||||
}
|
||||
|
||||
if (is_array($var)) {
|
||||
$a = array();
|
||||
foreach ($var as $k => $v) {
|
||||
$a[] = sprintf('%s => %s', $k, $this->varToString($v));
|
||||
}
|
||||
|
||||
return sprintf("Array(%s)", implode(', ', $a));
|
||||
}
|
||||
|
||||
if (is_resource($var)) {
|
||||
return sprintf('Resource(%s)', get_resource_type($var));
|
||||
}
|
||||
|
||||
if (null === $var) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
if (false === $var) {
|
||||
return 'false';
|
||||
}
|
||||
|
||||
if (true === $var) {
|
||||
return 'true';
|
||||
}
|
||||
|
||||
return (string) $var;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* DataCollectorInterface.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = null);
|
||||
|
||||
/**
|
||||
* Returns the name of the collector.
|
||||
*
|
||||
* @return string The collector name
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getName();
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* EventDataCollector.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class EventDataCollector extends DataCollector
|
||||
{
|
||||
private $dispatcher;
|
||||
|
||||
public function setEventDispatcher(EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if ($dispatcher instanceof TraceableEventDispatcherInterface) {
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||
{
|
||||
$this->data = array(
|
||||
'called_listeners' => null !== $this->dispatcher ? $this->dispatcher->getCalledListeners() : array(),
|
||||
'not_called_listeners' => null !== $this->dispatcher ? $this->dispatcher->getNotCalledListeners() : array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the called listeners.
|
||||
*
|
||||
* @return array An array of called listeners
|
||||
*
|
||||
* @see TraceableEventDispatcherInterface
|
||||
*/
|
||||
public function getCalledListeners()
|
||||
{
|
||||
return $this->data['called_listeners'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the not called listeners.
|
||||
*
|
||||
* @return array An array of not called listeners
|
||||
*
|
||||
* @see TraceableEventDispatcherInterface
|
||||
*/
|
||||
public function getNotCalledListeners()
|
||||
{
|
||||
return $this->data['not_called_listeners'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'events';
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\FlattenException;
|
||||
|
||||
/**
|
||||
* ExceptionDataCollector.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ExceptionDataCollector extends DataCollector
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||
{
|
||||
if (null !== $exception) {
|
||||
$this->data = array(
|
||||
'exception' => FlattenException::create($exception),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the exception is not null.
|
||||
*
|
||||
* @return Boolean true if the exception is not null, false otherwise
|
||||
*/
|
||||
public function hasException()
|
||||
{
|
||||
return isset($this->data['exception']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception.
|
||||
*
|
||||
* @return \Exception The exception
|
||||
*/
|
||||
public function getException()
|
||||
{
|
||||
return $this->data['exception'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception message.
|
||||
*
|
||||
* @return string The exception message
|
||||
*/
|
||||
public function getMessage()
|
||||
{
|
||||
return $this->data['exception']->getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception code.
|
||||
*
|
||||
* @return integer The exception code
|
||||
*/
|
||||
public function getCode()
|
||||
{
|
||||
return $this->data['exception']->getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the status code.
|
||||
*
|
||||
* @return integer The status code
|
||||
*/
|
||||
public function getStatusCode()
|
||||
{
|
||||
return $this->data['exception']->getStatusCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception trace.
|
||||
*
|
||||
* @return array The exception trace
|
||||
*/
|
||||
public function getTrace()
|
||||
{
|
||||
return $this->data['exception']->getTrace();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'exception';
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
|
||||
|
||||
/**
|
||||
* LogDataCollector.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class LoggerDataCollector extends DataCollector
|
||||
{
|
||||
private $logger;
|
||||
|
||||
public function __construct($logger = null)
|
||||
{
|
||||
if (null !== $logger && $logger instanceof DebugLoggerInterface) {
|
||||
$this->logger = $logger;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||
{
|
||||
if (null !== $this->logger) {
|
||||
$this->data = array(
|
||||
'error_count' => $this->logger->countErrors(),
|
||||
'logs' => $this->sanitizeLogs($this->logger->getLogs()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the called events.
|
||||
*
|
||||
* @return array An array of called events
|
||||
*
|
||||
* @see TraceableEventDispatcherInterface
|
||||
*/
|
||||
public function countErrors()
|
||||
{
|
||||
return isset($this->data['error_count']) ? $this->data['error_count'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the logs.
|
||||
*
|
||||
* @return array An array of logs
|
||||
*/
|
||||
public function getLogs()
|
||||
{
|
||||
return isset($this->data['logs']) ? $this->data['logs'] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'logger';
|
||||
}
|
||||
|
||||
private function sanitizeLogs($logs)
|
||||
{
|
||||
foreach ($logs as $i => $log) {
|
||||
$logs[$i]['context'] = $this->sanitizeContext($log['context']);
|
||||
}
|
||||
|
||||
return $logs;
|
||||
}
|
||||
|
||||
private function sanitizeContext($context)
|
||||
{
|
||||
if (is_array($context)) {
|
||||
foreach ($context as $key => $value) {
|
||||
$context[$key] = $this->sanitizeContext($value);
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
if (is_resource($context)) {
|
||||
return sprintf('Resource(%s)', get_resource_type($context));
|
||||
}
|
||||
|
||||
if (is_object($context)) {
|
||||
return sprintf('Object(%s)', get_class($context));
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* MemoryDataCollector.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class MemoryDataCollector extends DataCollector
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||
{
|
||||
$this->data = array(
|
||||
'memory' => memory_get_peak_usage(true),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the memory.
|
||||
*
|
||||
* @return integer The memory
|
||||
*/
|
||||
public function getMemory()
|
||||
{
|
||||
return $this->data['memory'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'memory';
|
||||
}
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
use Symfony\Component\HttpFoundation\HeaderBag;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||
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
|
||||
{
|
||||
protected $controllers;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->controllers = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||
{
|
||||
$responseHeaders = $response->headers->all();
|
||||
$cookies = array();
|
||||
foreach ($response->headers->getCookies() as $cookie) {
|
||||
$cookies[] = $this->getCookieHeader($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
|
||||
}
|
||||
if (count($cookies) > 0) {
|
||||
$responseHeaders['Set-Cookie'] = $cookies;
|
||||
}
|
||||
|
||||
$attributes = array();
|
||||
foreach ($request->attributes->all() as $key => $value) {
|
||||
$attributes[$key] = $this->varToString($value);
|
||||
}
|
||||
|
||||
$content = null;
|
||||
try {
|
||||
$content = $request->getContent();
|
||||
} catch (\LogicException $e) {
|
||||
// the user already got the request content as a resource
|
||||
$content = false;
|
||||
}
|
||||
|
||||
$sessionMetadata = array();
|
||||
$sessionAttributes = array();
|
||||
$flashes = array();
|
||||
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['Lifetime'] = $session->getMetadataBag()->getLifetime();
|
||||
$sessionAttributes = $session->all();
|
||||
$flashes = $session->getFlashBag()->peekAll();
|
||||
}
|
||||
}
|
||||
|
||||
$this->data = array(
|
||||
'format' => $request->getRequestFormat(),
|
||||
'content' => $content,
|
||||
'content_type' => $response->headers->get('Content-Type') ? $response->headers->get('Content-Type') : 'text/html',
|
||||
'status_code' => $response->getStatusCode(),
|
||||
'request_query' => $request->query->all(),
|
||||
'request_request' => $request->request->all(),
|
||||
'request_headers' => $request->headers->all(),
|
||||
'request_server' => $request->server->all(),
|
||||
'request_cookies' => $request->cookies->all(),
|
||||
'request_attributes' => $attributes,
|
||||
'response_headers' => $responseHeaders,
|
||||
'session_metadata' => $sessionMetadata,
|
||||
'session_attributes' => $sessionAttributes,
|
||||
'flashes' => $flashes,
|
||||
'path_info' => $request->getPathInfo(),
|
||||
'controller' => 'n/a',
|
||||
);
|
||||
|
||||
if (isset($this->controllers[$request])) {
|
||||
$controller = $this->controllers[$request];
|
||||
if (is_array($controller)) {
|
||||
$r = new \ReflectionMethod($controller[0], $controller[1]);
|
||||
$this->data['controller'] = array(
|
||||
'class' => get_class($controller[0]),
|
||||
'method' => $controller[1],
|
||||
'file' => $r->getFilename(),
|
||||
'line' => $r->getStartLine(),
|
||||
);
|
||||
} elseif ($controller instanceof \Closure) {
|
||||
$this->data['controller'] = 'Closure';
|
||||
} else {
|
||||
$this->data['controller'] = (string) $controller ?: 'n/a';
|
||||
}
|
||||
unset($this->controllers[$request]);
|
||||
}
|
||||
}
|
||||
|
||||
public function getPathInfo()
|
||||
{
|
||||
return $this->data['path_info'];
|
||||
}
|
||||
|
||||
public function getRequestRequest()
|
||||
{
|
||||
return new ParameterBag($this->data['request_request']);
|
||||
}
|
||||
|
||||
public function getRequestQuery()
|
||||
{
|
||||
return new ParameterBag($this->data['request_query']);
|
||||
}
|
||||
|
||||
public function getRequestHeaders()
|
||||
{
|
||||
return new HeaderBag($this->data['request_headers']);
|
||||
}
|
||||
|
||||
public function getRequestServer()
|
||||
{
|
||||
return new ParameterBag($this->data['request_server']);
|
||||
}
|
||||
|
||||
public function getRequestCookies()
|
||||
{
|
||||
return new ParameterBag($this->data['request_cookies']);
|
||||
}
|
||||
|
||||
public function getRequestAttributes()
|
||||
{
|
||||
return new ParameterBag($this->data['request_attributes']);
|
||||
}
|
||||
|
||||
public function getResponseHeaders()
|
||||
{
|
||||
return new ResponseHeaderBag($this->data['response_headers']);
|
||||
}
|
||||
|
||||
public function getSessionMetadata()
|
||||
{
|
||||
return $this->data['session_metadata'];
|
||||
}
|
||||
|
||||
public function getSessionAttributes()
|
||||
{
|
||||
return $this->data['session_attributes'];
|
||||
}
|
||||
|
||||
public function getFlashes()
|
||||
{
|
||||
return $this->data['flashes'];
|
||||
}
|
||||
|
||||
public function getContent()
|
||||
{
|
||||
return $this->data['content'];
|
||||
}
|
||||
|
||||
public function getContentType()
|
||||
{
|
||||
return $this->data['content_type'];
|
||||
}
|
||||
|
||||
public function getStatusCode()
|
||||
{
|
||||
return $this->data['status_code'];
|
||||
}
|
||||
|
||||
public function getFormat()
|
||||
{
|
||||
return $this->data['format'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the route name.
|
||||
*
|
||||
* The _route request attributes is automatically set by the Router Matcher.
|
||||
*
|
||||
* @return string The route
|
||||
*/
|
||||
public function getRoute()
|
||||
{
|
||||
return isset($this->data['request_attributes']['_route']) ? $this->data['request_attributes']['_route'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the route parameters.
|
||||
*
|
||||
* The _route_params request attributes is automatically set by the RouterListener.
|
||||
*
|
||||
* @return array The parameters
|
||||
*/
|
||||
public function getRouteParams()
|
||||
{
|
||||
return isset($this->data['request_attributes']['_route_params']) ? $this->data['request_attributes']['_route_params'] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller.
|
||||
*
|
||||
* @return string The controller as a string
|
||||
*/
|
||||
public function getController()
|
||||
{
|
||||
return $this->data['controller'];
|
||||
}
|
||||
|
||||
public function onKernelController(FilterControllerEvent $event)
|
||||
{
|
||||
$this->controllers[$event->getRequest()] = $event->getController();
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(KernelEvents::CONTROLLER => 'onKernelController');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'request';
|
||||
}
|
||||
|
||||
private function getCookieHeader($name, $value, $expires, $path, $domain, $secure, $httponly)
|
||||
{
|
||||
$cookie = sprintf('%s=%s', $name, urlencode($value));
|
||||
|
||||
if (0 !== $expires) {
|
||||
if (is_numeric($expires)) {
|
||||
$expires = (int) $expires;
|
||||
} elseif ($expires instanceof \DateTime) {
|
||||
$expires = $expires->getTimestamp();
|
||||
} else {
|
||||
$expires = strtotime($expires);
|
||||
if (false === $expires || -1 == $expires) {
|
||||
throw new \InvalidArgumentException(sprintf('The "expires" cookie parameter is not valid.', $expires));
|
||||
}
|
||||
}
|
||||
|
||||
$cookie .= '; expires='.substr(\DateTime::createFromFormat('U', $expires, new \DateTimeZone('UTC'))->format('D, d-M-Y H:i:s T'), 0, -5);
|
||||
}
|
||||
|
||||
if ($domain) {
|
||||
$cookie .= '; domain='.$domain;
|
||||
}
|
||||
|
||||
$cookie .= '; path='.$path;
|
||||
|
||||
if ($secure) {
|
||||
$cookie .= '; secure';
|
||||
}
|
||||
|
||||
if ($httponly) {
|
||||
$cookie .= '; httponly';
|
||||
}
|
||||
|
||||
return $cookie;
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
|
||||
|
||||
/**
|
||||
* RouterDataCollector.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class RouterDataCollector extends DataCollector
|
||||
{
|
||||
protected $controllers;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->controllers = new \SplObjectStorage();
|
||||
|
||||
$this->data = array(
|
||||
'redirect' => false,
|
||||
'url' => null,
|
||||
'route' => null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||
{
|
||||
if ($response instanceof RedirectResponse) {
|
||||
$this->data['redirect'] = true;
|
||||
$this->data['url'] = $response->getTargetUrl();
|
||||
|
||||
if ($this->controllers->contains($request)) {
|
||||
$this->data['route'] = $this->guessRoute($request, $this->controllers[$request]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function guessRoute(Request $request, $controller)
|
||||
{
|
||||
return 'n/a';
|
||||
}
|
||||
|
||||
/**
|
||||
* Remembers the controller associated to each request.
|
||||
*
|
||||
* @param FilterControllerEvent $event The filter controller event
|
||||
*/
|
||||
public function onKernelController(FilterControllerEvent $event)
|
||||
{
|
||||
$this->controllers[$event->getRequest()] = $event->getController();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Boolean Whether this request will result in a redirect
|
||||
*/
|
||||
public function getRedirect()
|
||||
{
|
||||
return $this->data['redirect'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null The target URL
|
||||
*/
|
||||
public function getTargetUrl()
|
||||
{
|
||||
return $this->data['url'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null The target route
|
||||
*/
|
||||
public function getTargetRoute()
|
||||
{
|
||||
return $this->data['route'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'router';
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* TimeDataCollector.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class TimeDataCollector extends DataCollector
|
||||
{
|
||||
protected $kernel;
|
||||
|
||||
public function __construct(KernelInterface $kernel = null)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||
{
|
||||
$this->data = array(
|
||||
'start_time' => (null !== $this->kernel ? $this->kernel->getStartTime() : $_SERVER['REQUEST_TIME']) * 1000,
|
||||
'events' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the request events.
|
||||
*
|
||||
* @param array $events The request events
|
||||
*/
|
||||
public function setEvents(array $events)
|
||||
{
|
||||
foreach ($events as $event) {
|
||||
$event->ensureStopped();
|
||||
}
|
||||
|
||||
$this->data['events'] = $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the request events.
|
||||
*
|
||||
* @return array The request events
|
||||
*/
|
||||
public function getEvents()
|
||||
{
|
||||
return $this->data['events'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the request elapsed time.
|
||||
*
|
||||
* @return float The elapsed time
|
||||
*/
|
||||
public function getTotalTime()
|
||||
{
|
||||
$lastEvent = $this->data['events']['__section__'];
|
||||
|
||||
return $lastEvent->getOrigin() + $lastEvent->getTotalTime() - $this->getStartTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the initialization time.
|
||||
*
|
||||
* This is the time spent until the beginning of the request handling.
|
||||
*
|
||||
* @return float The elapsed time
|
||||
*/
|
||||
public function getInitTime()
|
||||
{
|
||||
return $this->data['events']['__section__']->getOrigin() - $this->getStartTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the request time.
|
||||
*
|
||||
* @return integer The time
|
||||
*/
|
||||
public function getStartTime()
|
||||
{
|
||||
return $this->data['start_time'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'time';
|
||||
}
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Debug;
|
||||
|
||||
use Symfony\Component\HttpKernel\Debug\Stopwatch;
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profiler;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* Extends the ContainerAwareEventDispatcher to add some debugging tools.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ContainerAwareTraceableEventDispatcher extends ContainerAwareEventDispatcher implements TraceableEventDispatcherInterface
|
||||
{
|
||||
private $logger;
|
||||
private $called;
|
||||
private $stopwatch;
|
||||
private $priorities;
|
||||
private $profiler;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ContainerInterface $container A ContainerInterface instance
|
||||
* @param Stopwatch $stopwatch A Stopwatch instance
|
||||
* @param LoggerInterface $logger A LoggerInterface instance
|
||||
*/
|
||||
public function __construct(ContainerInterface $container, Stopwatch $stopwatch, LoggerInterface $logger = null)
|
||||
{
|
||||
parent::__construct($container);
|
||||
|
||||
$this->stopwatch = $stopwatch;
|
||||
$this->logger = $logger;
|
||||
$this->called = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dispatch($eventName, Event $event = null)
|
||||
{
|
||||
switch ($eventName) {
|
||||
case 'kernel.request':
|
||||
$this->stopwatch->openSection();
|
||||
break;
|
||||
case 'kernel.view':
|
||||
case 'kernel.response':
|
||||
// stop only if a controller has been executed
|
||||
try {
|
||||
$this->stopwatch->stop('controller');
|
||||
} catch (\LogicException $e) {
|
||||
}
|
||||
break;
|
||||
case 'kernel.terminate':
|
||||
$token = $event->getResponse()->headers->get('X-Debug-Token');
|
||||
$this->stopwatch->openSection($token);
|
||||
break;
|
||||
}
|
||||
|
||||
$e1 = $this->stopwatch->start($eventName, 'section');
|
||||
|
||||
parent::dispatch($eventName, $event);
|
||||
|
||||
$e1->stop();
|
||||
|
||||
switch ($eventName) {
|
||||
case 'kernel.controller':
|
||||
$this->stopwatch->start('controller', 'section');
|
||||
break;
|
||||
case 'kernel.response':
|
||||
$token = $event->getResponse()->headers->get('X-Debug-Token');
|
||||
$this->stopwatch->stopSection($token);
|
||||
if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) {
|
||||
// The profiles can only be updated once they have been created
|
||||
// that is after the 'kernel.response' event of the main request
|
||||
$this->updateProfiles($token, true);
|
||||
}
|
||||
break;
|
||||
case 'kernel.terminate':
|
||||
$this->stopwatch->stopSection($token);
|
||||
// The children profiles have been updated by the previous 'kernel.response'
|
||||
// event. Only the root profile need to be updated with the 'kernel.terminate'
|
||||
// timing informations.
|
||||
$this->updateProfiles($token, false);
|
||||
break;
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @throws \RuntimeException if the listener method is not callable
|
||||
*/
|
||||
public function addListener($eventName, $listener, $priority = 0)
|
||||
{
|
||||
if (!is_callable($listener)) {
|
||||
throw new \RuntimeException(sprintf('The given callback (%s) for event "%s" is not callable.', $this->getListenerAsString($listener), $eventName));
|
||||
}
|
||||
|
||||
$this->priorities[$eventName.'_'.$this->getListenerAsString($listener)] = $priority;
|
||||
|
||||
parent::addListener($eventName, $listener, $priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function doDispatch($listeners, $eventName, Event $event)
|
||||
{
|
||||
foreach ($listeners as $listener) {
|
||||
$info = $this->getListenerInfo($listener, $eventName);
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty']));
|
||||
}
|
||||
|
||||
$this->called[$eventName.'.'.$info['pretty']] = $info;
|
||||
|
||||
$e2 = $this->stopwatch->start(isset($info['class']) ? substr($info['class'], strrpos($info['class'], '\\') + 1) : $info['type'], 'event_listener');
|
||||
|
||||
call_user_func($listener, $event);
|
||||
|
||||
$e2->stop();
|
||||
|
||||
if ($event->isPropagationStopped()) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName));
|
||||
|
||||
$skippedListeners = $this->getListeners($eventName);
|
||||
$skipped = false;
|
||||
|
||||
foreach ($skippedListeners as $skippedListener) {
|
||||
if ($skipped) {
|
||||
$info = $this->getListenerInfo($skippedListener, $eventName);
|
||||
$this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName));
|
||||
}
|
||||
|
||||
if ($skippedListener === $listener) {
|
||||
$skipped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function lazyLoad($eventName)
|
||||
{
|
||||
$e = $this->stopwatch->start($eventName.'.loading', 'event_listener_loading');
|
||||
|
||||
parent::lazyLoad($eventName);
|
||||
|
||||
$e->stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getCalledListeners()
|
||||
{
|
||||
return $this->called;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getNotCalledListeners()
|
||||
{
|
||||
$notCalled = array();
|
||||
|
||||
foreach ($this->getListeners() as $name => $listeners) {
|
||||
foreach ($listeners as $listener) {
|
||||
$info = $this->getListenerInfo($listener, $name);
|
||||
if (!isset($this->called[$name.'.'.$info['pretty']])) {
|
||||
$notCalled[$name.'.'.$info['pretty']] = $info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $notCalled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about the listener
|
||||
*
|
||||
* @param object $listener The listener
|
||||
* @param string $eventName The event name
|
||||
*
|
||||
* @return array Informations about the listener
|
||||
*/
|
||||
private function getListenerInfo($listener, $eventName)
|
||||
{
|
||||
$info = array(
|
||||
'event' => $eventName,
|
||||
'priority' => $this->priorities[$eventName.'_'.$this->getListenerAsString($listener)],
|
||||
);
|
||||
if ($listener instanceof \Closure) {
|
||||
$info += array(
|
||||
'type' => 'Closure',
|
||||
'pretty' => 'closure'
|
||||
);
|
||||
} elseif (is_string($listener)) {
|
||||
try {
|
||||
$r = new \ReflectionFunction($listener);
|
||||
$file = $r->getFileName();
|
||||
$line = $r->getStartLine();
|
||||
} catch (\ReflectionException $e) {
|
||||
$file = null;
|
||||
$line = null;
|
||||
}
|
||||
$info += array(
|
||||
'type' => 'Function',
|
||||
'function' => $listener,
|
||||
'file' => $file,
|
||||
'line' => $line,
|
||||
'pretty' => $listener,
|
||||
);
|
||||
} elseif (is_array($listener) || (is_object($listener) && is_callable($listener))) {
|
||||
if (!is_array($listener)) {
|
||||
$listener = array($listener, '__invoke');
|
||||
}
|
||||
$class = is_object($listener[0]) ? get_class($listener[0]) : $listener[0];
|
||||
try {
|
||||
$r = new \ReflectionMethod($class, $listener[1]);
|
||||
$file = $r->getFileName();
|
||||
$line = $r->getStartLine();
|
||||
} catch (\ReflectionException $e) {
|
||||
$file = null;
|
||||
$line = null;
|
||||
}
|
||||
$info += array(
|
||||
'type' => 'Method',
|
||||
'class' => $class,
|
||||
'method' => $listener[1],
|
||||
'file' => $file,
|
||||
'line' => $line,
|
||||
'pretty' => $class.'::'.$listener[1],
|
||||
);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the stopwatch data in the profile hierarchy.
|
||||
*
|
||||
* @param string $token Profile token
|
||||
* @param Boolean $updateChildren Whether to update the children altogether
|
||||
*/
|
||||
private function updateProfiles($token, $updateChildren)
|
||||
{
|
||||
if (!$this->getContainer()->has('profiler')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->profiler = $this->getContainer()->get('profiler');
|
||||
|
||||
if (!$profile = $this->profiler->loadProfile($token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->saveStopwatchInfoInProfile($profile, $updateChildren);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the profiles with the timing info and saves them.
|
||||
*
|
||||
* @param Profile $profile The root profile
|
||||
* @param Boolean $updateChildren Whether to update the children altogether
|
||||
*/
|
||||
private function saveStopwatchInfoInProfile(Profile $profile, $updateChildren)
|
||||
{
|
||||
$profile->getCollector('time')->setEvents($this->stopwatch->getSectionEvents($profile->getToken()));
|
||||
$this->profiler->saveProfile($profile);
|
||||
|
||||
if ($updateChildren) {
|
||||
foreach ($profile->getChildren() as $child) {
|
||||
$this->saveStopwatchInfoInProfile($child, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getListenerAsString($listener)
|
||||
{
|
||||
if (is_string($listener)) {
|
||||
return '[string] '.$listener;
|
||||
} elseif (is_array($listener)) {
|
||||
return '[array] '.(is_object($listener[0]) ? get_class($listener[0]) : $listener[0]).'::'.$listener[1];
|
||||
} elseif (is_object($listener)) {
|
||||
return '[object] '.get_class($listener);
|
||||
}
|
||||
|
||||
return '[?] '.var_export($listener, true);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Debug;
|
||||
|
||||
/**
|
||||
* ErrorHandler.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ErrorHandler
|
||||
{
|
||||
private $levels = array(
|
||||
E_WARNING => 'Warning',
|
||||
E_NOTICE => 'Notice',
|
||||
E_USER_ERROR => 'User Error',
|
||||
E_USER_WARNING => 'User Warning',
|
||||
E_USER_NOTICE => 'User Notice',
|
||||
E_STRICT => 'Runtime Notice',
|
||||
E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
|
||||
E_DEPRECATED => 'Deprecated',
|
||||
E_USER_DEPRECATED => 'User Deprecated',
|
||||
);
|
||||
|
||||
private $level;
|
||||
|
||||
/**
|
||||
* Register the error handler.
|
||||
*
|
||||
* @param integer $level The level at which the conversion to Exception is done (null to use the error_reporting() value and 0 to disable)
|
||||
*
|
||||
* @return The registered error handler
|
||||
*/
|
||||
public static function register($level = null)
|
||||
{
|
||||
$handler = new static();
|
||||
$handler->setLevel($level);
|
||||
|
||||
set_error_handler(array($handler, 'handle'));
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
||||
public function setLevel($level)
|
||||
{
|
||||
$this->level = null === $level ? error_reporting() : $level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \ErrorException When error_reporting returns error
|
||||
*/
|
||||
public function handle($level, $message, $file, $line, $context)
|
||||
{
|
||||
if (0 === $this->level) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (error_reporting() & $level && $this->level & $level) {
|
||||
throw new \ErrorException(sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line), 0, $level, $file, $line);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Debug;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\FlattenException;
|
||||
|
||||
if (!defined('ENT_SUBSTITUTE')) {
|
||||
define('ENT_SUBSTITUTE', 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* ExceptionHandler converts an exception to a Response object.
|
||||
*
|
||||
* It is mostly useful in debug mode to replace the default PHP/XDebug
|
||||
* output with something prettier and more useful.
|
||||
*
|
||||
* As this class is mainly used during Kernel boot, where nothing is yet
|
||||
* available, the Response content is always HTML.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ExceptionHandler
|
||||
{
|
||||
private $debug;
|
||||
private $charset;
|
||||
|
||||
public function __construct($debug = true, $charset = 'UTF-8')
|
||||
{
|
||||
$this->debug = $debug;
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the exception handler.
|
||||
*
|
||||
* @param Boolean $debug
|
||||
*
|
||||
* @return ExceptionHandler The registered exception handler
|
||||
*/
|
||||
public static function register($debug = true)
|
||||
{
|
||||
$handler = new static($debug);
|
||||
|
||||
set_exception_handler(array($handler, 'handle'));
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Response for the given Exception.
|
||||
*
|
||||
* @param \Exception $exception An \Exception instance
|
||||
*/
|
||||
public function handle(\Exception $exception)
|
||||
{
|
||||
$this->createResponse($exception)->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the error Response associated with the given Exception.
|
||||
*
|
||||
* @param \Exception|FlattenException $exception An \Exception instance
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*/
|
||||
public function createResponse($exception)
|
||||
{
|
||||
$content = '';
|
||||
$title = '';
|
||||
try {
|
||||
if (!$exception instanceof FlattenException) {
|
||||
$exception = FlattenException::create($exception);
|
||||
}
|
||||
|
||||
switch ($exception->getStatusCode()) {
|
||||
case 404:
|
||||
$title = 'Sorry, the page you are looking for could not be found.';
|
||||
break;
|
||||
default:
|
||||
$title = 'Whoops, looks like something went wrong.';
|
||||
}
|
||||
|
||||
if ($this->debug) {
|
||||
$content = $this->getContent($exception);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// something nasty happened and we cannot throw an exception here anymore
|
||||
if ($this->debug) {
|
||||
$title = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($exception), $exception->getMessage());
|
||||
} else {
|
||||
$title = 'Whoops, looks like something went wrong.';
|
||||
}
|
||||
}
|
||||
|
||||
return new Response($this->decorate($content, $title), $exception->getStatusCode(), $exception->getHeaders());
|
||||
}
|
||||
|
||||
private function getContent($exception)
|
||||
{
|
||||
$message = nl2br($exception->getMessage());
|
||||
$class = $this->abbrClass($exception->getClass());
|
||||
$count = count($exception->getAllPrevious());
|
||||
$content = '';
|
||||
foreach ($exception->toArray() as $position => $e) {
|
||||
$ind = $count - $position + 1;
|
||||
$total = $count + 1;
|
||||
$class = $this->abbrClass($e['class']);
|
||||
$message = nl2br($e['message']);
|
||||
$content .= sprintf(<<<EOF
|
||||
<div class="block_exception clear_fix">
|
||||
<h2><span>%d/%d</span> %s: %s</h2>
|
||||
</div>
|
||||
<div class="block">
|
||||
<ol class="traces list_exception">
|
||||
|
||||
EOF
|
||||
, $ind, $total, $class, $message);
|
||||
foreach ($e['trace'] as $i => $trace) {
|
||||
$content .= ' <li>';
|
||||
if ($trace['function']) {
|
||||
$content .= sprintf('at %s%s%s(%s)', $this->abbrClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args']));
|
||||
}
|
||||
if (isset($trace['file']) && isset($trace['line'])) {
|
||||
if ($linkFormat = ini_get('xdebug.file_link_format')) {
|
||||
$link = str_replace(array('%f', '%l'), array($trace['file'], $trace['line']), $linkFormat);
|
||||
$content .= sprintf(' in <a href="%s" title="Go to source">%s line %s</a>', $link, $trace['file'], $trace['line']);
|
||||
} else {
|
||||
$content .= sprintf(' in %s line %s', $trace['file'], $trace['line']);
|
||||
}
|
||||
}
|
||||
$content .= "</li>\n";
|
||||
}
|
||||
|
||||
$content .= " </ol>\n</div>\n";
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function decorate($content, $title)
|
||||
{
|
||||
return <<<EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<meta name="robots" content="noindex,nofollow" />
|
||||
<title>{$title}</title>
|
||||
<style>
|
||||
/* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html */
|
||||
html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:text-top;}sub{vertical-align:text-bottom;}input,textarea,select{font-family:inherit;font-size:inherit;font-weight:inherit;}input,textarea,select{*font-size:100%;}legend{color:#000;}
|
||||
|
||||
html { background: #eee; padding: 10px }
|
||||
body { font: 11px Verdana, Arial, sans-serif; color: #333 }
|
||||
img { border: 0; }
|
||||
.clear { clear:both; height:0; font-size:0; line-height:0; }
|
||||
.clear_fix:after { display:block; height:0; clear:both; visibility:hidden; }
|
||||
.clear_fix { display:inline-block; }
|
||||
* html .clear_fix { height:1%; }
|
||||
.clear_fix { display:block; }
|
||||
#content { width:970px; margin:0 auto; }
|
||||
.sf-exceptionreset, .sf-exceptionreset .block { margin: auto }
|
||||
.sf-exceptionreset abbr { border-bottom: 1px dotted #000; cursor: help; }
|
||||
.sf-exceptionreset p { font-size:14px; line-height:20px; color:#868686; padding-bottom:20px }
|
||||
.sf-exceptionreset strong { font-weight:bold; }
|
||||
.sf-exceptionreset a { color:#6c6159; }
|
||||
.sf-exceptionreset a img { border:none; }
|
||||
.sf-exceptionreset a:hover { text-decoration:underline; }
|
||||
.sf-exceptionreset em { font-style:italic; }
|
||||
.sf-exceptionreset h1, .sf-exceptionreset h2 { font: 20px Georgia, "Times New Roman", Times, serif }
|
||||
.sf-exceptionreset h2 span { background-color: #fff; color: #333; padding: 6px; float: left; margin-right: 10px; }
|
||||
.sf-exceptionreset .traces li { font-size:12px; padding: 2px 4px; list-style-type:decimal; margin-left:20px; }
|
||||
.sf-exceptionreset .block { background-color:#FFFFFF; padding:10px 28px; margin-bottom:20px;
|
||||
-webkit-border-bottom-right-radius: 16px;
|
||||
-webkit-border-bottom-left-radius: 16px;
|
||||
-moz-border-radius-bottomright: 16px;
|
||||
-moz-border-radius-bottomleft: 16px;
|
||||
border-bottom-right-radius: 16px;
|
||||
border-bottom-left-radius: 16px;
|
||||
border-bottom:1px solid #ccc;
|
||||
border-right:1px solid #ccc;
|
||||
border-left:1px solid #ccc;
|
||||
}
|
||||
.sf-exceptionreset .block_exception { background-color:#ddd; color: #333; padding:20px;
|
||||
-webkit-border-top-left-radius: 16px;
|
||||
-webkit-border-top-right-radius: 16px;
|
||||
-moz-border-radius-topleft: 16px;
|
||||
-moz-border-radius-topright: 16px;
|
||||
border-top-left-radius: 16px;
|
||||
border-top-right-radius: 16px;
|
||||
border-top:1px solid #ccc;
|
||||
border-right:1px solid #ccc;
|
||||
border-left:1px solid #ccc;
|
||||
}
|
||||
.sf-exceptionreset li a { background:none; color:#868686; text-decoration:none; }
|
||||
.sf-exceptionreset li a:hover { background:none; color:#313131; text-decoration:underline; }
|
||||
.sf-exceptionreset ol { padding: 10px 0; }
|
||||
.sf-exceptionreset h1 { background-color:#FFFFFF; padding: 15px 28px; margin-bottom: 20px;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="content" class="sf-exceptionreset">
|
||||
<h1>$title</h1>
|
||||
$content
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
EOF;
|
||||
}
|
||||
|
||||
private function abbrClass($class)
|
||||
{
|
||||
$parts = explode('\\', $class);
|
||||
|
||||
return sprintf("<abbr title=\"%s\">%s</abbr>", $class, array_pop($parts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an array as a string.
|
||||
*
|
||||
* @param array $args The argument array
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function formatArgs(array $args)
|
||||
{
|
||||
$result = array();
|
||||
foreach ($args as $key => $item) {
|
||||
if ('object' === $item[0]) {
|
||||
$formattedValue = sprintf("<em>object</em>(%s)", $this->abbrClass($item[1]));
|
||||
} elseif ('array' === $item[0]) {
|
||||
$formattedValue = sprintf("<em>array</em>(%s)", is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]);
|
||||
} elseif ('string' === $item[0]) {
|
||||
$formattedValue = sprintf("'%s'", htmlspecialchars($item[1], ENT_QUOTES | ENT_SUBSTITUTE, $this->charset));
|
||||
} elseif ('null' === $item[0]) {
|
||||
$formattedValue = '<em>null</em>';
|
||||
} elseif ('boolean' === $item[0]) {
|
||||
$formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>';
|
||||
} elseif ('resource' === $item[0]) {
|
||||
$formattedValue = '<em>resource</em>';
|
||||
} else {
|
||||
$formattedValue = str_replace("\n", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES | ENT_SUBSTITUTE, $this->charset), true));
|
||||
}
|
||||
|
||||
$result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue);
|
||||
}
|
||||
|
||||
return implode(', ', $result);
|
||||
}
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Debug;
|
||||
|
||||
/**
|
||||
* Stopwatch provides a way to profile code.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Stopwatch
|
||||
{
|
||||
private $sections;
|
||||
private $activeSections;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->sections = $this->activeSections = array('__root__' => new Section('__root__'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new section or re-opens an existing section.
|
||||
*
|
||||
* @param string|null $id The id of the session to re-open, null to create a new one
|
||||
*
|
||||
* @throws \LogicException When the section to re-open is not reachable
|
||||
*/
|
||||
public function openSection($id = null)
|
||||
{
|
||||
$current = end($this->activeSections);
|
||||
|
||||
if (null !== $id && null === $current->get($id)) {
|
||||
throw new \LogicException(sprintf('The section "%s" has been started at an other level and can not be opened.', $id));
|
||||
}
|
||||
|
||||
$this->start('__section__.child', 'section');
|
||||
$this->activeSections[] = $current->open($id);
|
||||
$this->start('__section__');
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the last started section.
|
||||
*
|
||||
* The id parameter is used to retrieve the events from this section.
|
||||
*
|
||||
* @see getSectionEvents
|
||||
*
|
||||
* @param string $id The identifier of the section
|
||||
*/
|
||||
public function stopSection($id)
|
||||
{
|
||||
$this->stop('__section__');
|
||||
|
||||
if (1 == count($this->activeSections)) {
|
||||
throw new \LogicException('There is no started section to stop.');
|
||||
}
|
||||
|
||||
$this->sections[$id] = array_pop($this->activeSections)->setId($id);
|
||||
$this->stop('__section__.child');
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
* @param string $category The event category
|
||||
*
|
||||
* @return StopwatchEvent A StopwatchEvent instance
|
||||
*/
|
||||
public function start($name, $category = null)
|
||||
{
|
||||
return end($this->activeSections)->startEvent($name, $category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return StopwatchEvent A StopwatchEvent instance
|
||||
*/
|
||||
public function stop($name)
|
||||
{
|
||||
return end($this->activeSections)->stopEvent($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops then restarts an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return StopwatchEvent A StopwatchEvent instance
|
||||
*/
|
||||
public function lap($name)
|
||||
{
|
||||
return end($this->activeSections)->stopEvent($name)->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all events for a given section.
|
||||
*
|
||||
* @param string $id A section identifier
|
||||
*
|
||||
* @return StopwatchEvent[] An array of StopwatchEvent instances
|
||||
*/
|
||||
public function getSectionEvents($id)
|
||||
{
|
||||
return isset($this->sections[$id]) ? $this->sections[$id]->getEvents() : array();
|
||||
}
|
||||
}
|
||||
|
||||
class Section
|
||||
{
|
||||
private $events = array();
|
||||
private $origin;
|
||||
private $id;
|
||||
private $children = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param float|null $origin Set the origin of the events in this section, use null to set their origin to their start time
|
||||
*/
|
||||
public function __construct($origin = null)
|
||||
{
|
||||
$this->origin = is_numeric($origin) ? $origin : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the child section.
|
||||
*
|
||||
* @param string $id The child section identifier
|
||||
*
|
||||
* @return Section|null The child section or null when none found
|
||||
*/
|
||||
public function get($id)
|
||||
{
|
||||
foreach ($this->children as $child) {
|
||||
if ($id === $child->getId()) {
|
||||
return $child;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or re-opens a child section.
|
||||
*
|
||||
* @param string|null $id null to create a new section, the identifier to re-open an existing one.
|
||||
*
|
||||
* @return Section A child section
|
||||
*/
|
||||
public function open($id)
|
||||
{
|
||||
if (null === $session = $this->get($id)) {
|
||||
$session = $this->children[] = new self(microtime(true) * 1000);
|
||||
}
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string The identifier of the section
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session identifier.
|
||||
*
|
||||
* @param string $id The session identifier
|
||||
*
|
||||
* @return Section The current section
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
* @param string $category The event category
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*/
|
||||
public function startEvent($name, $category)
|
||||
{
|
||||
if (!isset($this->events[$name])) {
|
||||
$this->events[$name] = new StopwatchEvent($this->origin ?: microtime(true) * 1000, $category);
|
||||
}
|
||||
|
||||
return $this->events[$name]->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*
|
||||
* @throws \LogicException When the event has not been started
|
||||
*/
|
||||
public function stopEvent($name)
|
||||
{
|
||||
if (!isset($this->events[$name])) {
|
||||
throw new \LogicException(sprintf('Event "%s" is not started.', $name));
|
||||
}
|
||||
|
||||
return $this->events[$name]->stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops then restarts an event.
|
||||
*
|
||||
* @param string $name The event name
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*
|
||||
* @throws \LogicException When the event has not been started
|
||||
*/
|
||||
public function lap($name)
|
||||
{
|
||||
return $this->stop($name)->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the events from this section.
|
||||
*
|
||||
* @return StopwatchEvent[] An array of StopwatchEvent instances
|
||||
*/
|
||||
public function getEvents()
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Debug;
|
||||
|
||||
/**
|
||||
* Represents an Event managed by Stopwatch.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class StopwatchEvent
|
||||
{
|
||||
private $periods;
|
||||
private $origin;
|
||||
private $category;
|
||||
private $started;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param float $origin The origin time in milliseconds
|
||||
* @param string $category The event category
|
||||
*
|
||||
* @throws \InvalidArgumentException When the raw time is not valid
|
||||
*/
|
||||
public function __construct($origin, $category = null)
|
||||
{
|
||||
$this->origin = $this->formatTime($origin);
|
||||
$this->category = is_string($category) ? $category : 'default';
|
||||
$this->started = array();
|
||||
$this->periods = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the category.
|
||||
*
|
||||
* @return string The category
|
||||
*/
|
||||
public function getCategory()
|
||||
{
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the origin.
|
||||
*
|
||||
* @return integer The origin in milliseconds
|
||||
*/
|
||||
public function getOrigin()
|
||||
{
|
||||
return $this->origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new event period.
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*/
|
||||
public function start()
|
||||
{
|
||||
$this->started[] = $this->getNow();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the last started event period.
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
if (!count($this->started)) {
|
||||
throw new \LogicException('stop() called but start() has not been called before.');
|
||||
}
|
||||
|
||||
$this->periods[] = array(array_pop($this->started), $this->getNow());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current period and then starts a new one.
|
||||
*
|
||||
* @return StopwatchEvent The event
|
||||
*/
|
||||
public function lap()
|
||||
{
|
||||
return $this->stop()->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops all non already stopped periods.
|
||||
*/
|
||||
public function ensureStopped()
|
||||
{
|
||||
while (count($this->started)) {
|
||||
$this->stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all event periods.
|
||||
*
|
||||
* @return array An array of periods
|
||||
*/
|
||||
public function getPeriods()
|
||||
{
|
||||
return $this->periods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative time of the start of the first period.
|
||||
*
|
||||
* @return integer The time (in milliseconds)
|
||||
*/
|
||||
public function getStartTime()
|
||||
{
|
||||
return isset($this->periods[0]) ? $this->periods[0][0] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the relative time of the end of the last period.
|
||||
*
|
||||
* @return integer The time (in milliseconds)
|
||||
*/
|
||||
public function getEndTime()
|
||||
{
|
||||
return ($count = count($this->periods)) ? $this->periods[$count - 1][1] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total time of all periods.
|
||||
*
|
||||
* @return integer The time (in milliseconds)
|
||||
*/
|
||||
public function getTotalTime()
|
||||
{
|
||||
$total = 0;
|
||||
foreach ($this->periods as $period) {
|
||||
$total += $period[1] - $period[0];
|
||||
}
|
||||
|
||||
return $this->formatTime($total);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current time relative to origin.
|
||||
*
|
||||
* @return float Time in ms
|
||||
*/
|
||||
protected function getNow()
|
||||
{
|
||||
return $this->formatTime(microtime(true) * 1000 - $this->origin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a time.
|
||||
*
|
||||
* @param numerical $time A raw time
|
||||
*
|
||||
* @return float The formatted time
|
||||
*
|
||||
* @throws \InvalidArgumentException When the raw time is not valid
|
||||
*/
|
||||
private function formatTime($time)
|
||||
{
|
||||
if (!is_numeric($time)) {
|
||||
throw new \InvalidArgumentException('The time must be a numerical value');
|
||||
}
|
||||
|
||||
return round($time, 1);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
|
||||
/**
|
||||
* Sets the classes to compile in the cache for the container.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class AddClassesToCachePass implements CompilerPassInterface
|
||||
{
|
||||
private $kernel;
|
||||
|
||||
public function __construct(Kernel $kernel)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$classes = array();
|
||||
foreach ($container->getExtensions() as $extension) {
|
||||
if ($extension instanceof Extension) {
|
||||
$classes = array_merge($classes, $extension->getClassesToCompile());
|
||||
}
|
||||
}
|
||||
|
||||
$this->kernel->setClassCache(array_unique($container->getParameterBag()->resolveValue($classes)));
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* This extension sub-class provides first-class integration with the
|
||||
* Config/Definition Component.
|
||||
*
|
||||
* You can use this as base class if you
|
||||
*
|
||||
* a) use the Config/Definition component for configuration
|
||||
* b) your configuration class is named "Configuration" and
|
||||
* c) the configuration class resides in the DependencyInjection sub-folder
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
abstract class ConfigurableExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
final public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
$this->loadInternal($this->processConfiguration($this->getConfiguration(array(), $container), $configs), $container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the passed container according to the merged configuration.
|
||||
*
|
||||
* @param array $mergedConfig
|
||||
* @param ContainerBuilder $container
|
||||
*/
|
||||
abstract protected function loadInternal(array $mergedConfig, ContainerBuilder $container);
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\Definition\Processor;
|
||||
use Symfony\Component\Config\Definition\ConfigurationInterface;
|
||||
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
|
||||
use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
|
||||
/**
|
||||
* Provides useful features shared by many extensions.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface
|
||||
{
|
||||
private $classes = array();
|
||||
|
||||
/**
|
||||
* Gets the classes to cache.
|
||||
*
|
||||
* @return array An array of classes
|
||||
*/
|
||||
public function getClassesToCompile()
|
||||
{
|
||||
return $this->classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds classes to the class cache.
|
||||
*
|
||||
* @param array $classes An array of classes
|
||||
*/
|
||||
public function addClassesToCompile(array $classes)
|
||||
{
|
||||
$this->classes = array_merge($this->classes, $classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base path for the XSD files.
|
||||
*
|
||||
* @return string The XSD base path
|
||||
*/
|
||||
public function getXsdValidationBasePath()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace to be used for this extension (XML namespace).
|
||||
*
|
||||
* @return string The XML namespace
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return 'http://example.org/schema/dic/'.$this->getAlias();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the recommended alias to use in XML.
|
||||
*
|
||||
* This alias is also the mandatory prefix to use when using YAML.
|
||||
*
|
||||
* This convention is to remove the "Extension" postfix from the class
|
||||
* name and then lowercase and underscore the result. So:
|
||||
*
|
||||
* AcmeHelloExtension
|
||||
*
|
||||
* becomes
|
||||
*
|
||||
* acme_hello
|
||||
*
|
||||
* This can be overridden in a sub-class to specify the alias manually.
|
||||
*
|
||||
* @return string The alias
|
||||
*/
|
||||
public function getAlias()
|
||||
{
|
||||
$className = get_class($this);
|
||||
if (substr($className, -9) != 'Extension') {
|
||||
throw new \BadMethodCallException('This extension does not follow the naming convention; you must overwrite the getAlias() method.');
|
||||
}
|
||||
$classBaseName = substr(strrchr($className, '\\'), 1, -9);
|
||||
|
||||
return Container::underscore($classBaseName);
|
||||
}
|
||||
|
||||
final protected function processConfiguration(ConfigurationInterface $configuration, array $configs)
|
||||
{
|
||||
$processor = new Processor();
|
||||
|
||||
return $processor->processConfiguration($configuration, $configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getConfiguration(array $config, ContainerBuilder $container)
|
||||
{
|
||||
$reflected = new \ReflectionClass($this);
|
||||
$namespace = $reflected->getNamespaceName();
|
||||
|
||||
$class = $namespace . '\\Configuration';
|
||||
if (class_exists($class)) {
|
||||
if (!method_exists($class, '__construct')) {
|
||||
$configuration = new $class();
|
||||
|
||||
return $configuration;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass as BaseMergeExtensionConfigurationPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* Ensures certain extensions are always loaded.
|
||||
*
|
||||
* @author Kris Wallsmith <kris@symfony.com>
|
||||
*/
|
||||
class MergeExtensionConfigurationPass extends BaseMergeExtensionConfigurationPass
|
||||
{
|
||||
private $extensions;
|
||||
|
||||
public function __construct(array $extensions)
|
||||
{
|
||||
$this->extensions = $extensions;
|
||||
}
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
foreach ($this->extensions as $extension) {
|
||||
if (!count($container->getExtensionConfig($extension))) {
|
||||
$container->loadFromExtension($extension, array());
|
||||
}
|
||||
}
|
||||
|
||||
parent::process($container);
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Event;
|
||||
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Allows filtering of a controller callable
|
||||
*
|
||||
* You can call getController() to retrieve the current controller. With
|
||||
* setController() you can set a new controller that is used in the processing
|
||||
* of the request.
|
||||
*
|
||||
* Controllers should be callables.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class FilterControllerEvent extends KernelEvent
|
||||
{
|
||||
/**
|
||||
* The current controller
|
||||
* @var callable
|
||||
*/
|
||||
private $controller;
|
||||
|
||||
public function __construct(HttpKernelInterface $kernel, $controller, Request $request, $requestType)
|
||||
{
|
||||
parent::__construct($kernel, $request, $requestType);
|
||||
|
||||
$this->setController($controller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current controller
|
||||
*
|
||||
* @return callable
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getController()
|
||||
{
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new controller
|
||||
*
|
||||
* @param callable $controller
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function setController($controller)
|
||||
{
|
||||
// controller must be a callable
|
||||
if (!is_callable($controller)) {
|
||||
throw new \LogicException(sprintf('The controller must be a callable (%s given).', $this->varToString($controller)));
|
||||
}
|
||||
|
||||
$this->controller = $controller;
|
||||
}
|
||||
|
||||
private function varToString($var)
|
||||
{
|
||||
if (is_object($var)) {
|
||||
return sprintf('Object(%s)', get_class($var));
|
||||
}
|
||||
|
||||
if (is_array($var)) {
|
||||
$a = array();
|
||||
foreach ($var as $k => $v) {
|
||||
$a[] = sprintf('%s => %s', $k, $this->varToString($v));
|
||||
}
|
||||
|
||||
return sprintf("Array(%s)", implode(', ', $a));
|
||||
}
|
||||
|
||||
if (is_resource($var)) {
|
||||
return sprintf('Resource(%s)', get_resource_type($var));
|
||||
}
|
||||
|
||||
if (null === $var) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
if (false === $var) {
|
||||
return 'false';
|
||||
}
|
||||
|
||||
if (true === $var) {
|
||||
return 'true';
|
||||
}
|
||||
|
||||
return (string) $var;
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Event;
|
||||
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Allows to filter a Response object
|
||||
*
|
||||
* You can call getResponse() to retrieve the current response. With
|
||||
* setResponse() you can set a new response that will be returned to the
|
||||
* browser.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class FilterResponseEvent extends KernelEvent
|
||||
{
|
||||
/**
|
||||
* The current response object
|
||||
* @var Response
|
||||
*/
|
||||
private $response;
|
||||
|
||||
public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, Response $response)
|
||||
{
|
||||
parent::__construct($kernel, $request, $requestType);
|
||||
|
||||
$this->setResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current response object
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new response object
|
||||
*
|
||||
* @param Response $response
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function setResponse(Response $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Event;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Allows to create a response for a request
|
||||
*
|
||||
* Call setResponse() to set the response that will be returned for the
|
||||
* current request. The propagation of this event is stopped as soon as a
|
||||
* response is set.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class GetResponseEvent extends KernelEvent
|
||||
{
|
||||
/**
|
||||
* The response object
|
||||
* @var Response
|
||||
*/
|
||||
private $response;
|
||||
|
||||
/**
|
||||
* Returns the response object
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a response and stops event propagation
|
||||
*
|
||||
* @param Response $response
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function setResponse(Response $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
|
||||
$this->stopPropagation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a response was set
|
||||
*
|
||||
* @return Boolean Whether a response was set
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function hasResponse()
|
||||
{
|
||||
return null !== $this->response;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Event;
|
||||
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Allows to create a response for the return value of a controller
|
||||
*
|
||||
* Call setResponse() to set the response that will be returned for the
|
||||
* current request. The propagation of this event is stopped as soon as a
|
||||
* response is set.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class GetResponseForControllerResultEvent extends GetResponseEvent
|
||||
{
|
||||
/**
|
||||
* The return value of the controller
|
||||
* @var mixed
|
||||
*/
|
||||
private $controllerResult;
|
||||
|
||||
public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, $controllerResult)
|
||||
{
|
||||
parent::__construct($kernel, $request, $requestType);
|
||||
|
||||
$this->controllerResult = $controllerResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the return value of the controller
|
||||
*
|
||||
* @return mixed The controller return value
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getControllerResult()
|
||||
{
|
||||
return $this->controllerResult;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Event;
|
||||
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Allows to create a response for a thrown exception
|
||||
*
|
||||
* Call setResponse() to set the response that will be returned for the
|
||||
* current request. The propagation of this event is stopped as soon as a
|
||||
* response is set.
|
||||
*
|
||||
* You can also call setException() to replace the thrown exception. This
|
||||
* exception will be thrown if no response is set during processing of this
|
||||
* event.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class GetResponseForExceptionEvent extends GetResponseEvent
|
||||
{
|
||||
/**
|
||||
* The exception object
|
||||
* @var \Exception
|
||||
*/
|
||||
private $exception;
|
||||
|
||||
public function __construct(HttpKernelInterface $kernel, Request $request, $requestType, \Exception $e)
|
||||
{
|
||||
parent::__construct($kernel, $request, $requestType);
|
||||
|
||||
$this->setException($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the thrown exception
|
||||
*
|
||||
* @return \Exception The thrown exception
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getException()
|
||||
{
|
||||
return $this->exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the thrown exception
|
||||
*
|
||||
* This exception will be thrown if no response is set in the event.
|
||||
*
|
||||
* @param \Exception $exception The thrown exception
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function setException(\Exception $exception)
|
||||
{
|
||||
$this->exception = $exception;
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Event;
|
||||
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Base class for events thrown in the HttpKernel component
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
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 integer
|
||||
*/
|
||||
private $requestType;
|
||||
|
||||
public function __construct(HttpKernelInterface $kernel, Request $request, $requestType)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
$this->request = $request;
|
||||
$this->requestType = $requestType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the kernel in which this event was thrown
|
||||
*
|
||||
* @return HttpKernelInterface
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getKernel()
|
||||
{
|
||||
return $this->kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request the kernel is currently processing
|
||||
*
|
||||
* @return Request
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request type the kernel is currently processing
|
||||
*
|
||||
* @return integer One of HttpKernelInterface::MASTER_REQUEST and
|
||||
* HttpKernelInterface::SUB_REQUEST
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getRequestType()
|
||||
{
|
||||
return $this->requestType;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Event;
|
||||
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Allows to execute logic after a response was sent
|
||||
*
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class PostResponseEvent extends Event
|
||||
{
|
||||
/**
|
||||
* The kernel in which this event was thrown
|
||||
* @var HttpKernelInterface
|
||||
*/
|
||||
private $kernel;
|
||||
|
||||
private $request;
|
||||
|
||||
private $response;
|
||||
|
||||
public function __construct(HttpKernelInterface $kernel, Request $request, Response $response)
|
||||
{
|
||||
$this->kernel = $kernel;
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the kernel in which this event was thrown.
|
||||
*
|
||||
* @return HttpKernelInterface
|
||||
*/
|
||||
public function getKernel()
|
||||
{
|
||||
return $this->kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request for which this event was thrown.
|
||||
*
|
||||
* @return Request
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the response for which this event was thrown.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\HttpCache\Esi;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* EsiListener adds a Surrogate-Control HTTP header when the Response needs to be parsed for ESI.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class EsiListener implements EventSubscriberInterface
|
||||
{
|
||||
private $esi;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Esi $esi An ESI instance
|
||||
*/
|
||||
public function __construct(Esi $esi = null)
|
||||
{
|
||||
$this->esi = $esi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the Response.
|
||||
*
|
||||
* @param FilterResponseEvent $event A FilterResponseEvent instance
|
||||
*/
|
||||
public function onKernelResponse(FilterResponseEvent $event)
|
||||
{
|
||||
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType() || null === $this->esi) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->esi->addSurrogateControl($event->getResponse());
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::RESPONSE => 'onKernelResponse',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
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\FlattenException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* ExceptionListener.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ExceptionListener implements EventSubscriberInterface
|
||||
{
|
||||
private $controller;
|
||||
private $logger;
|
||||
|
||||
public function __construct($controller, LoggerInterface $logger = null)
|
||||
{
|
||||
$this->controller = $controller;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function onKernelException(GetResponseForExceptionEvent $event)
|
||||
{
|
||||
static $handling;
|
||||
|
||||
if (true === $handling) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$handling = true;
|
||||
|
||||
$exception = $event->getException();
|
||||
$request = $event->getRequest();
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$message = sprintf('%s: %s (uncaught exception) at %s line %s', get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine());
|
||||
if (!$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500) {
|
||||
$this->logger->crit($message);
|
||||
} else {
|
||||
$this->logger->err($message);
|
||||
}
|
||||
} else {
|
||||
error_log(sprintf('Uncaught PHP Exception %s: "%s" at %s line %s', get_class($exception), $exception->getMessage(), $exception->getFile(), $exception->getLine()));
|
||||
}
|
||||
|
||||
$logger = $this->logger instanceof DebugLoggerInterface ? $this->logger : null;
|
||||
|
||||
$attributes = array(
|
||||
'_controller' => $this->controller,
|
||||
'exception' => FlattenException::create($exception),
|
||||
'logger' => $logger,
|
||||
'format' => $request->getRequestFormat(),
|
||||
);
|
||||
|
||||
$request = $request->duplicate(null, null, $attributes);
|
||||
$request->setMethod('GET');
|
||||
|
||||
try {
|
||||
$response = $event->getKernel()->handle($request, HttpKernelInterface::SUB_REQUEST, true);
|
||||
} catch (\Exception $e) {
|
||||
$message = sprintf('Exception thrown when handling an exception (%s: %s)', get_class($e), $e->getMessage());
|
||||
if (null !== $this->logger) {
|
||||
if (!$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500) {
|
||||
$this->logger->crit($message);
|
||||
} else {
|
||||
$this->logger->err($message);
|
||||
}
|
||||
} else {
|
||||
error_log($message);
|
||||
}
|
||||
|
||||
// set handling to false otherwise it wont be able to handle further more
|
||||
$handling = false;
|
||||
|
||||
// re-throw the exception as this is a catch-all
|
||||
return;
|
||||
}
|
||||
|
||||
$event->setResponse($response);
|
||||
|
||||
$handling = false;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::EXCEPTION => array('onKernelException', -128),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\Routing\RequestContextAwareInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* Initializes the locale based on the current request.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class LocaleListener implements EventSubscriberInterface
|
||||
{
|
||||
private $router;
|
||||
private $defaultLocale;
|
||||
|
||||
public function __construct($defaultLocale = 'en', RequestContextAwareInterface $router = null)
|
||||
{
|
||||
$this->defaultLocale = $defaultLocale;
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
public function onKernelRequest(GetResponseEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
|
||||
$request->setDefaultLocale($this->defaultLocale);
|
||||
|
||||
if ($locale = $request->attributes->get('_locale')) {
|
||||
$request->setLocale($locale);
|
||||
}
|
||||
|
||||
if (null !== $this->router) {
|
||||
$this->router->getContext()->setParameter('_locale', $request->getLocale());
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
// must be registered after the Router to have access to the _locale
|
||||
KernelEvents::REQUEST => array(array('onKernelRequest', 16)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profiler;
|
||||
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* ProfilerListener collects data for the current request by listening to the onKernelResponse event.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ProfilerListener implements EventSubscriberInterface
|
||||
{
|
||||
protected $profiler;
|
||||
protected $matcher;
|
||||
protected $onlyException;
|
||||
protected $onlyMasterRequests;
|
||||
protected $exception;
|
||||
protected $children;
|
||||
protected $requests;
|
||||
protected $profiles;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Profiler $profiler A Profiler instance
|
||||
* @param RequestMatcherInterface $matcher A RequestMatcher instance
|
||||
* @param Boolean $onlyException true if the profiler only collects data when an exception occurs, false otherwise
|
||||
* @param Boolean $onlyMasterRequests true if the profiler only collects data when the request is a master request, false otherwise
|
||||
*/
|
||||
public function __construct(Profiler $profiler, RequestMatcherInterface $matcher = null, $onlyException = false, $onlyMasterRequests = false)
|
||||
{
|
||||
$this->profiler = $profiler;
|
||||
$this->matcher = $matcher;
|
||||
$this->onlyException = (Boolean) $onlyException;
|
||||
$this->onlyMasterRequests = (Boolean) $onlyMasterRequests;
|
||||
$this->children = new \SplObjectStorage();
|
||||
$this->profiles = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the onKernelException event.
|
||||
*
|
||||
* @param GetResponseForExceptionEvent $event A GetResponseForExceptionEvent instance
|
||||
*/
|
||||
public function onKernelException(GetResponseForExceptionEvent $event)
|
||||
{
|
||||
if ($this->onlyMasterRequests && HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->exception = $event->getException();
|
||||
}
|
||||
|
||||
public function onKernelRequest(GetResponseEvent $event)
|
||||
{
|
||||
$this->requests[] = $event->getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the onKernelResponse event.
|
||||
*
|
||||
* @param FilterResponseEvent $event A FilterResponseEvent instance
|
||||
*/
|
||||
public function onKernelResponse(FilterResponseEvent $event)
|
||||
{
|
||||
$master = HttpKernelInterface::MASTER_REQUEST === $event->getRequestType();
|
||||
if ($this->onlyMasterRequests && !$master) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->onlyException && null === $this->exception) {
|
||||
return;
|
||||
}
|
||||
|
||||
$request = $event->getRequest();
|
||||
$exception = $this->exception;
|
||||
$this->exception = null;
|
||||
|
||||
if (null !== $this->matcher && !$this->matcher->matches($request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$profile = $this->profiler->collect($request, $event->getResponse(), $exception)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->profiles[] = $profile;
|
||||
|
||||
if (null !== $exception) {
|
||||
foreach ($this->profiles as $profile) {
|
||||
$this->profiler->saveProfile($profile);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// keep the profile as the child of its parent
|
||||
if (!$master) {
|
||||
array_pop($this->requests);
|
||||
|
||||
$parent = end($this->requests);
|
||||
|
||||
// when simulating requests, we might not have the parent
|
||||
if ($parent) {
|
||||
$profiles = isset($this->children[$parent]) ? $this->children[$parent] : array();
|
||||
$profiles[] = $profile;
|
||||
$this->children[$parent] = $profiles;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->children[$request])) {
|
||||
foreach ($this->children[$request] as $child) {
|
||||
$profile->addChild($child);
|
||||
}
|
||||
$this->children[$request] = array();
|
||||
}
|
||||
|
||||
if ($master) {
|
||||
$this->saveProfiles($profile);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
// kernel.request must be registered as early as possible to not break
|
||||
// when an exception is thrown in any other kernel.request listener
|
||||
KernelEvents::REQUEST => array('onKernelRequest', 1024),
|
||||
KernelEvents::RESPONSE => array('onKernelResponse', -100),
|
||||
KernelEvents::EXCEPTION => 'onKernelException',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the profile hierarchy.
|
||||
*
|
||||
* @param Profile $profile The root profile
|
||||
*/
|
||||
private function saveProfiles(Profile $profile)
|
||||
{
|
||||
$this->profiler->saveProfile($profile);
|
||||
foreach ($profile->getChildren() as $profile) {
|
||||
$this->saveProfiles($profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* ResponseListener fixes the Response headers based on the Request.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ResponseListener implements EventSubscriberInterface
|
||||
{
|
||||
private $charset;
|
||||
|
||||
public function __construct($charset)
|
||||
{
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the Response.
|
||||
*
|
||||
* @param FilterResponseEvent $event A FilterResponseEvent instance
|
||||
*/
|
||||
public function onKernelResponse(FilterResponseEvent $event)
|
||||
{
|
||||
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$response = $event->getResponse();
|
||||
|
||||
if (null === $response->getCharset()) {
|
||||
$response->setCharset($this->charset);
|
||||
}
|
||||
|
||||
$response->prepare($event->getRequest());
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::RESPONSE => 'onKernelResponse',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Initializes the context from the request and sets request attributes based on a matching route.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class RouterListener implements EventSubscriberInterface
|
||||
{
|
||||
private $matcher;
|
||||
private $context;
|
||||
private $logger;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param UrlMatcherInterface|RequestMatcherInterface $matcher The Url or Request matcher
|
||||
* @param RequestContext|null $context The RequestContext (can be null when $matcher implements RequestContextAwareInterface)
|
||||
* @param LoggerInterface|null $logger The logger
|
||||
*/
|
||||
public function __construct($matcher, RequestContext $context = null, LoggerInterface $logger = null)
|
||||
{
|
||||
if (!$matcher instanceof UrlMatcherInterface && !$matcher instanceof RequestMatcherInterface) {
|
||||
throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.');
|
||||
}
|
||||
|
||||
if (null === $context && !$matcher instanceof RequestContextAwareInterface) {
|
||||
throw new \InvalidArgumentException('You must either pass a RequestContext or the matcher must implement RequestContextAwareInterface.');
|
||||
}
|
||||
|
||||
$this->matcher = $matcher;
|
||||
$this->context = $context ?: $matcher->getContext();
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function onKernelRequest(GetResponseEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
|
||||
// initialize the context that is also used by the generator (assuming matcher and generator share the same context instance)
|
||||
$this->context->fromRequest($request);
|
||||
|
||||
if ($request->attributes->has('_controller')) {
|
||||
// routing is already done
|
||||
return;
|
||||
}
|
||||
|
||||
// add attributes based on the request (routing)
|
||||
try {
|
||||
// matching a request is more powerful than matching a URL path + context, so try that first
|
||||
if ($this->matcher instanceof RequestMatcherInterface) {
|
||||
$parameters = $this->matcher->matchRequest($request);
|
||||
} else {
|
||||
$parameters = $this->matcher->match($request->getPathInfo());
|
||||
}
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->info(sprintf('Matched route "%s" (parameters: %s)', $parameters['_route'], $this->parametersToString($parameters)));
|
||||
}
|
||||
|
||||
$request->attributes->add($parameters);
|
||||
unset($parameters['_route']);
|
||||
unset($parameters['_controller']);
|
||||
$request->attributes->set('_route_params', $parameters);
|
||||
} catch (ResourceNotFoundException $e) {
|
||||
$message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo());
|
||||
|
||||
throw new NotFoundHttpException($message, $e);
|
||||
} catch (MethodNotAllowedException $e) {
|
||||
$message = sprintf('No route found for "%s %s": Method Not Allowed (Allow: %s)', $request->getMethod(), $request->getPathInfo(), strtoupper(implode(', ', $e->getAllowedMethods())));
|
||||
|
||||
throw new MethodNotAllowedHttpException($e->getAllowedMethods(), $message, $e);
|
||||
}
|
||||
}
|
||||
|
||||
private function parametersToString(array $parameters)
|
||||
{
|
||||
$pieces = array();
|
||||
foreach ($parameters as $key => $val) {
|
||||
$pieces[] = sprintf('"%s": "%s"', $key, (is_string($val) ? $val : json_encode($val)));
|
||||
}
|
||||
|
||||
return implode(', ', $pieces);
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::REQUEST => array(array('onKernelRequest', 32)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
/**
|
||||
* StreamedResponseListener is responsible for sending the Response
|
||||
* to the client.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class StreamedResponseListener implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* Filters the Response.
|
||||
*
|
||||
* @param FilterResponseEvent $event A FilterResponseEvent instance
|
||||
*/
|
||||
public function onKernelResponse(FilterResponseEvent $event)
|
||||
{
|
||||
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$response = $event->getResponse();
|
||||
|
||||
if ($response instanceof StreamedResponse) {
|
||||
$response->send();
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::RESPONSE => array('onKernelResponse', -1024),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\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 integer $code The internal exception code
|
||||
*/
|
||||
public function __construct($message = null, \Exception $previous = null, $code = 0)
|
||||
{
|
||||
parent::__construct(403, $message, $previous, array(), $code);
|
||||
}
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Exception;
|
||||
|
||||
/**
|
||||
* FlattenException wraps a PHP Exception to be able to serialize it.
|
||||
*
|
||||
* Basically, this class removes all objects from the trace.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class FlattenException
|
||||
{
|
||||
private $message;
|
||||
private $code;
|
||||
private $previous;
|
||||
private $trace;
|
||||
private $class;
|
||||
private $statusCode;
|
||||
private $headers;
|
||||
private $file;
|
||||
private $line;
|
||||
|
||||
public static function create(\Exception $exception, $statusCode = null, array $headers = array())
|
||||
{
|
||||
$e = new static();
|
||||
$e->setMessage($exception->getMessage());
|
||||
$e->setCode($exception->getCode());
|
||||
|
||||
if ($exception instanceof HttpExceptionInterface) {
|
||||
$statusCode = $exception->getStatusCode();
|
||||
$headers = array_merge($headers, $exception->getHeaders());
|
||||
}
|
||||
|
||||
if (null === $statusCode) {
|
||||
$statusCode = 500;
|
||||
}
|
||||
|
||||
$e->setStatusCode($statusCode);
|
||||
$e->setHeaders($headers);
|
||||
$e->setTrace($exception->getTrace(), $exception->getFile(), $exception->getLine());
|
||||
$e->setClass(get_class($exception));
|
||||
$e->setFile($exception->getFile());
|
||||
$e->setLine($exception->getLine());
|
||||
if ($exception->getPrevious()) {
|
||||
$e->setPrevious(static::create($exception->getPrevious()));
|
||||
}
|
||||
|
||||
return $e;
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
$exceptions = array();
|
||||
foreach (array_merge(array($this), $this->getAllPrevious()) as $exception) {
|
||||
$exceptions[] = array(
|
||||
'message' => $exception->getMessage(),
|
||||
'class' => $exception->getClass(),
|
||||
'trace' => $exception->getTrace(),
|
||||
);
|
||||
}
|
||||
|
||||
return $exceptions;
|
||||
}
|
||||
|
||||
public function getStatusCode()
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
public function setStatusCode($code)
|
||||
{
|
||||
$this->statusCode = $code;
|
||||
}
|
||||
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
public function setHeaders(array $headers)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
public function getClass()
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function setClass($class)
|
||||
{
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
public function getFile()
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
public function setFile($file)
|
||||
{
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
public function getLine()
|
||||
{
|
||||
return $this->line;
|
||||
}
|
||||
|
||||
public function setLine($line)
|
||||
{
|
||||
$this->line = $line;
|
||||
}
|
||||
|
||||
public function getMessage()
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function setMessage($message)
|
||||
{
|
||||
$this->message = $message;
|
||||
}
|
||||
|
||||
public function getCode()
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
}
|
||||
|
||||
public function getPrevious()
|
||||
{
|
||||
return $this->previous;
|
||||
}
|
||||
|
||||
public function setPrevious(FlattenException $previous)
|
||||
{
|
||||
$this->previous = $previous;
|
||||
}
|
||||
|
||||
public function getAllPrevious()
|
||||
{
|
||||
$exceptions = array();
|
||||
$e = $this;
|
||||
while ($e = $e->getPrevious()) {
|
||||
$exceptions[] = $e;
|
||||
}
|
||||
|
||||
return $exceptions;
|
||||
}
|
||||
|
||||
public function getTrace()
|
||||
{
|
||||
return $this->trace;
|
||||
}
|
||||
|
||||
public function setTrace($trace, $file, $line)
|
||||
{
|
||||
$this->trace = array();
|
||||
$this->trace[] = array(
|
||||
'namespace' => '',
|
||||
'short_class' => '',
|
||||
'class' => '',
|
||||
'type' => '',
|
||||
'function' => '',
|
||||
'file' => $file,
|
||||
'line' => $line,
|
||||
'args' => array(),
|
||||
);
|
||||
foreach ($trace as $entry) {
|
||||
$class = '';
|
||||
$namespace = '';
|
||||
if (isset($entry['class'])) {
|
||||
$parts = explode('\\', $entry['class']);
|
||||
$class = array_pop($parts);
|
||||
$namespace = implode('\\', $parts);
|
||||
}
|
||||
|
||||
$this->trace[] = array(
|
||||
'namespace' => $namespace,
|
||||
'short_class' => $class,
|
||||
'class' => isset($entry['class']) ? $entry['class'] : '',
|
||||
'type' => isset($entry['type']) ? $entry['type'] : '',
|
||||
'function' => $entry['function'],
|
||||
'file' => isset($entry['file']) ? $entry['file'] : null,
|
||||
'line' => isset($entry['line']) ? $entry['line'] : null,
|
||||
'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function flattenArgs($args, $level = 0)
|
||||
{
|
||||
$result = array();
|
||||
foreach ($args as $key => $value) {
|
||||
if (is_object($value)) {
|
||||
$result[$key] = array('object', get_class($value));
|
||||
} elseif (is_array($value)) {
|
||||
if ($level > 10) {
|
||||
$result[$key] = array('array', '*DEEP NESTED ARRAY*');
|
||||
} else {
|
||||
$result[$key] = array('array', $this->flattenArgs($value, ++$level));
|
||||
}
|
||||
} elseif (null === $value) {
|
||||
$result[$key] = array('null', null);
|
||||
} elseif (is_bool($value)) {
|
||||
$result[$key] = array('boolean', $value);
|
||||
} elseif (is_resource($value)) {
|
||||
$result[$key] = array('resource', get_resource_type($value));
|
||||
} else {
|
||||
$result[$key] = array('string', (string) $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Exception;
|
||||
|
||||
/**
|
||||
* HttpException.
|
||||
*
|
||||
* @author Kris Wallsmith <kris@symfony.com>
|
||||
*/
|
||||
class HttpException extends \RuntimeException implements HttpExceptionInterface
|
||||
{
|
||||
private $statusCode;
|
||||
private $headers;
|
||||
|
||||
public function __construct($statusCode, $message = null, \Exception $previous = null, array $headers = array(), $code = 0)
|
||||
{
|
||||
$this->statusCode = $statusCode;
|
||||
$this->headers = $headers;
|
||||
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public function getStatusCode()
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Exception;
|
||||
|
||||
/**
|
||||
* Interface for HTTP error exceptions.
|
||||
*
|
||||
* @author Kris Wallsmith <kris@symfony.com>
|
||||
*/
|
||||
interface HttpExceptionInterface
|
||||
{
|
||||
/**
|
||||
* Returns the status code.
|
||||
*
|
||||
* @return integer An HTTP response status code
|
||||
*/
|
||||
public function getStatusCode();
|
||||
|
||||
/**
|
||||
* Returns response headers.
|
||||
*
|
||||
* @return array Response headers
|
||||
*/
|
||||
public function getHeaders();
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\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
|
||||
* @param integer $code The internal exception code
|
||||
*/
|
||||
public function __construct(array $allow, $message = null, \Exception $previous = null, $code = 0)
|
||||
{
|
||||
$headers = array('Allow' => strtoupper(implode(', ', $allow)));
|
||||
|
||||
parent::__construct(405, $message, $previous, $headers, $code);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\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 integer $code The internal exception code
|
||||
*/
|
||||
public function __construct($message = null, \Exception $previous = null, $code = 0)
|
||||
{
|
||||
parent::__construct(404, $message, $previous, array(), $code);
|
||||
}
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\HttpCache;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
/**
|
||||
* Esi implements the ESI capabilities to Request and Response instances.
|
||||
*
|
||||
* For more information, read the following W3C notes:
|
||||
*
|
||||
* * ESI Language Specification 1.0 (http://www.w3.org/TR/esi-lang)
|
||||
*
|
||||
* * Edge Architecture Specification (http://www.w3.org/TR/edge-arch)
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Esi
|
||||
{
|
||||
private $contentTypes;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $contentTypes An array of content-type that should be parsed for ESI information.
|
||||
* (default: text/html, text/xml, and application/xml)
|
||||
*/
|
||||
public function __construct(array $contentTypes = array('text/html', 'text/xml', 'application/xml'))
|
||||
{
|
||||
$this->contentTypes = $contentTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new cache strategy instance.
|
||||
*
|
||||
* @return EsiResponseCacheStrategyInterface A EsiResponseCacheStrategyInterface instance
|
||||
*/
|
||||
public function createCacheStrategy()
|
||||
{
|
||||
return new EsiResponseCacheStrategy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that at least one surrogate has ESI/1.0 capability.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @return Boolean true if one surrogate has ESI/1.0 capability, false otherwise
|
||||
*/
|
||||
public function hasSurrogateEsiCapability(Request $request)
|
||||
{
|
||||
if (null === $value = $request->headers->get('Surrogate-Capability')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false !== strpos($value, 'ESI/1.0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds ESI/1.0 capability to the given Request.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*/
|
||||
public function addSurrogateEsiCapability(Request $request)
|
||||
{
|
||||
$current = $request->headers->get('Surrogate-Capability');
|
||||
$new = 'symfony2="ESI/1.0"';
|
||||
|
||||
$request->headers->set('Surrogate-Capability', $current ? $current.', '.$new : $new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds HTTP headers to specify that the Response needs to be parsed for ESI.
|
||||
*
|
||||
* This method only adds an ESI HTTP header if the Response has some ESI tags.
|
||||
*
|
||||
* @param Response $response A Response instance
|
||||
*/
|
||||
public function addSurrogateControl(Response $response)
|
||||
{
|
||||
if (false !== strpos($response->getContent(), '<esi:include')) {
|
||||
$response->headers->set('Surrogate-Control', 'content="ESI/1.0"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the Response needs to be parsed for ESI tags.
|
||||
*
|
||||
* @param Response $response A Response instance
|
||||
*
|
||||
* @return Boolean true if the Response needs to be parsed, false otherwise
|
||||
*/
|
||||
public function needsEsiParsing(Response $response)
|
||||
{
|
||||
if (!$control = $response->headers->get('Surrogate-Control')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (Boolean) preg_match('#content="[^"]*ESI/1.0[^"]*"#', $control);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders an ESI tag.
|
||||
*
|
||||
* @param string $uri A URI
|
||||
* @param string $alt An alternate URI
|
||||
* @param Boolean $ignoreErrors Whether to ignore errors or not
|
||||
* @param string $comment A comment to add as an esi:include tag
|
||||
*/
|
||||
public function renderIncludeTag($uri, $alt = null, $ignoreErrors = true, $comment = '')
|
||||
{
|
||||
$html = sprintf('<esi:include src="%s"%s%s />',
|
||||
$uri,
|
||||
$ignoreErrors ? ' onerror="continue"' : '',
|
||||
$alt ? sprintf(' alt="%s"', $alt) : ''
|
||||
);
|
||||
|
||||
if (!empty($comment)) {
|
||||
return sprintf("<esi:comment text=\"%s\" />\n%s", $comment, $html);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a Response ESI tags with the included resource content.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Response $response A Response instance
|
||||
*/
|
||||
public function process(Request $request, Response $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$type = $response->headers->get('Content-Type');
|
||||
if (empty($type)) {
|
||||
$type = 'text/html';
|
||||
}
|
||||
|
||||
$parts = explode(';', $type);
|
||||
if (!in_array($parts[0], $this->contentTypes)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// we don't use a proper XML parser here as we can have ESI tags in a plain text response
|
||||
$content = $response->getContent();
|
||||
$content = str_replace(array('<?', '<%'), array('<?php echo "<?"; ?>', '<?php echo "<%"; ?>'), $content);
|
||||
$content = preg_replace_callback('#<esi\:include\s+(.*?)\s*(?:/|</esi\:include)>#', array($this, 'handleEsiIncludeTag'), $content);
|
||||
$content = preg_replace('#<esi\:comment[^>]*(?:/|</esi\:comment)>#', '', $content);
|
||||
$content = preg_replace('#<esi\:remove>.*?</esi\:remove>#', '', $content);
|
||||
|
||||
$response->setContent($content);
|
||||
$response->headers->set('X-Body-Eval', 'ESI');
|
||||
|
||||
// remove ESI/1.0 from the Surrogate-Control header
|
||||
if ($response->headers->has('Surrogate-Control')) {
|
||||
$value = $response->headers->get('Surrogate-Control');
|
||||
if ('content="ESI/1.0"' == $value) {
|
||||
$response->headers->remove('Surrogate-Control');
|
||||
} elseif (preg_match('#,\s*content="ESI/1.0"#', $value)) {
|
||||
$response->headers->set('Surrogate-Control', preg_replace('#,\s*content="ESI/1.0"#', '', $value));
|
||||
} elseif (preg_match('#content="ESI/1.0",\s*#', $value)) {
|
||||
$response->headers->set('Surrogate-Control', preg_replace('#content="ESI/1.0",\s*#', '', $value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an ESI from the cache.
|
||||
*
|
||||
* @param HttpCache $cache An HttpCache instance
|
||||
* @param string $uri The main URI
|
||||
* @param string $alt An alternative URI
|
||||
* @param Boolean $ignoreErrors Whether to ignore errors or not
|
||||
*/
|
||||
public function handle(HttpCache $cache, $uri, $alt, $ignoreErrors)
|
||||
{
|
||||
$subRequest = Request::create($uri, 'get', array(), $cache->getRequest()->cookies->all(), array(), $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()));
|
||||
}
|
||||
|
||||
return $response->getContent();
|
||||
} catch (\Exception $e) {
|
||||
if ($alt) {
|
||||
return $this->handle($cache, $alt, '', $ignoreErrors);
|
||||
}
|
||||
|
||||
if (!$ignoreErrors) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an ESI include tag (called internally).
|
||||
*
|
||||
* @param array $attributes An array containing the attributes.
|
||||
*
|
||||
* @return string The response content for the include.
|
||||
*/
|
||||
private function handleEsiIncludeTag($attributes)
|
||||
{
|
||||
$options = array();
|
||||
preg_match_all('/(src|onerror|alt)="([^"]*?)"/', $attributes[1], $matches, PREG_SET_ORDER);
|
||||
foreach ($matches as $set) {
|
||||
$options[$set[1]] = $set[2];
|
||||
}
|
||||
|
||||
if (!isset($options['src'])) {
|
||||
throw new \RuntimeException('Unable to process an ESI tag without a "src" attribute.');
|
||||
}
|
||||
|
||||
return sprintf('<?php echo $this->esi->handle($this, \'%s\', \'%s\', %s) ?>'."\n",
|
||||
$options['src'],
|
||||
isset($options['alt']) ? $options['alt'] : null,
|
||||
isset($options['onerror']) && 'continue' == $options['onerror'] ? 'true' : 'false'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (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.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\HttpCache;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* EsiResponseCacheStrategy knows how to compute the Response cache HTTP header
|
||||
* based on the different ESI response cache headers.
|
||||
*
|
||||
* This implementation changes the master response TTL to the smallest TTL received
|
||||
* or force validation if one of the ESI has validation cache strategy.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class EsiResponseCacheStrategy implements EsiResponseCacheStrategyInterface
|
||||
{
|
||||
private $cacheable = true;
|
||||
private $ttls = array();
|
||||
private $maxAges = array();
|
||||
|
||||
/**
|
||||
* Adds a Response.
|
||||
*
|
||||
* @param Response $response
|
||||
*/
|
||||
public function add(Response $response)
|
||||
{
|
||||
if ($response->isValidateable()) {
|
||||
$this->cacheable = false;
|
||||
} else {
|
||||
$this->ttls[] = $response->getTtl();
|
||||
$this->maxAges[] = $response->getMaxAge();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the Response HTTP headers based on the embedded Responses.
|
||||
*
|
||||
* @param Response $response
|
||||
*/
|
||||
public function update(Response $response)
|
||||
{
|
||||
// if we only have one Response, do nothing
|
||||
if (1 === count($this->ttls)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->cacheable) {
|
||||
$response->headers->set('Cache-Control', 'no-cache, must-revalidate');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (null !== $maxAge = min($this->maxAges)) {
|
||||
$response->setSharedMaxAge($maxAge);
|
||||
$response->headers->set('Age', $maxAge - min($this->ttls));
|
||||
}
|
||||
$response->setMaxAge(0);
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (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.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\HttpCache;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* EsiResponseCacheStrategyInterface implementations know how to compute the
|
||||
* Response cache HTTP header based on the different ESI response cache headers.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface EsiResponseCacheStrategyInterface
|
||||
{
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -1,660 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (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.
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Cache provides HTTP caching.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class HttpCache implements HttpKernelInterface, TerminableInterface
|
||||
{
|
||||
private $kernel;
|
||||
private $store;
|
||||
private $request;
|
||||
private $esi;
|
||||
private $esiCacheStrategy;
|
||||
private $traces;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* The available options are:
|
||||
*
|
||||
* * debug: If true, the traces are added as a HTTP header to ease debugging
|
||||
*
|
||||
* * default_ttl The number of seconds that a cache entry should be considered
|
||||
* fresh when no explicit freshness information is provided in
|
||||
* a response. Explicit Cache-Control or Expires headers
|
||||
* override this value. (default: 0)
|
||||
*
|
||||
* * private_headers Set of request headers that trigger "private" cache-control behavior
|
||||
* on responses that don't explicitly state whether the response is
|
||||
* public or private via a Cache-Control directive. (default: Authorization and Cookie)
|
||||
*
|
||||
* * allow_reload Specifies whether the client can force a cache reload by including a
|
||||
* Cache-Control "no-cache" directive in the request. Set it to ``true``
|
||||
* for compliance with RFC 2616. (default: false)
|
||||
*
|
||||
* * allow_revalidate Specifies whether the client can force a cache revalidate by including
|
||||
* a Cache-Control "max-age=0" directive in the request. Set it to ``true``
|
||||
* for compliance with RFC 2616. (default: false)
|
||||
*
|
||||
* * stale_while_revalidate Specifies the default number of seconds (the granularity is the second as the
|
||||
* Response TTL precision is a second) during which the cache can immediately return
|
||||
* a stale response while it revalidates it in the background (default: 2).
|
||||
* This setting is overridden by the stale-while-revalidate HTTP Cache-Control
|
||||
* extension (see RFC 5861).
|
||||
*
|
||||
* * stale_if_error Specifies the default number of seconds (the granularity is the second) during which
|
||||
* 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 Esi $esi An Esi instance
|
||||
* @param array $options An array of options
|
||||
*/
|
||||
public function __construct(HttpKernelInterface $kernel, StoreInterface $store, Esi $esi = null, array $options = array())
|
||||
{
|
||||
$this->store = $store;
|
||||
$this->kernel = $kernel;
|
||||
|
||||
// needed in case there is a fatal error because the backend is too slow to respond
|
||||
register_shutdown_function(array($this->store, 'cleanup'));
|
||||
|
||||
$this->options = array_merge(array(
|
||||
'debug' => false,
|
||||
'default_ttl' => 0,
|
||||
'private_headers' => array('Authorization', 'Cookie'),
|
||||
'allow_reload' => false,
|
||||
'allow_revalidate' => false,
|
||||
'stale_while_revalidate' => 2,
|
||||
'stale_if_error' => 60,
|
||||
), $options);
|
||||
$this->esi = $esi;
|
||||
$this->traces = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current store.
|
||||
*
|
||||
* @return StoreInterface $store A StoreInterface instance
|
||||
*/
|
||||
public function getStore()
|
||||
{
|
||||
return $this->store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of events that took place during processing of the last request.
|
||||
*
|
||||
* @return array An array of events
|
||||
*/
|
||||
public function getTraces()
|
||||
{
|
||||
return $this->traces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a log message for the events of the last request processing.
|
||||
*
|
||||
* @return string A log message
|
||||
*/
|
||||
public function getLog()
|
||||
{
|
||||
$log = array();
|
||||
foreach ($this->traces as $request => $traces) {
|
||||
$log[] = sprintf('%s: %s', $request, implode(', ', $traces));
|
||||
}
|
||||
|
||||
return implode('; ', $log);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Request instance associated with the master request.
|
||||
*
|
||||
* @return Symfony\Component\HttpFoundation\Request A Request instance
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Kernel instance
|
||||
*
|
||||
* @return Symfony\Component\HttpKernel\HttpKernelInterface An HttpKernelInterface instance
|
||||
*/
|
||||
public function getKernel()
|
||||
{
|
||||
return $this->kernel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the Esi instance
|
||||
*
|
||||
* @return Symfony\Component\HttpKernel\HttpCache\Esi An Esi instance
|
||||
*/
|
||||
public function getEsi()
|
||||
{
|
||||
return $this->esi;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
|
||||
{
|
||||
// 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;
|
||||
if (null !== $this->esi) {
|
||||
$this->esiCacheStrategy = $this->esi->createCacheStrategy();
|
||||
}
|
||||
}
|
||||
|
||||
$path = $request->getPathInfo();
|
||||
if ($qs = $request->getQueryString()) {
|
||||
$path .= '?'.$qs;
|
||||
}
|
||||
$this->traces[$request->getMethod().' '.$path] = array();
|
||||
|
||||
if (!$request->isMethodSafe()) {
|
||||
$response = $this->invalidate($request, $catch);
|
||||
} elseif ($request->headers->has('expect')) {
|
||||
$response = $this->pass($request, $catch);
|
||||
} else {
|
||||
$response = $this->lookup($request, $catch);
|
||||
}
|
||||
|
||||
$response->isNotModified($request);
|
||||
|
||||
$this->restoreResponseBody($request, $response);
|
||||
|
||||
$response->setDate(new \DateTime(null, new \DateTimeZone('UTC')));
|
||||
|
||||
if (HttpKernelInterface::MASTER_REQUEST === $type && $this->options['debug']) {
|
||||
$response->headers->set('X-Symfony-Cache', $this->getLog());
|
||||
}
|
||||
|
||||
if (null !== $this->esi) {
|
||||
$this->esiCacheStrategy->add($response);
|
||||
|
||||
if (HttpKernelInterface::MASTER_REQUEST === $type) {
|
||||
$this->esiCacheStrategy->update($response);
|
||||
}
|
||||
}
|
||||
|
||||
$response->prepare($request);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function terminate(Request $request, Response $response)
|
||||
{
|
||||
if ($this->getKernel() instanceof TerminableInterface) {
|
||||
$this->getKernel()->terminate($request, $response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards the Request to the backend without storing the Response in the cache.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Boolean $catch Whether to process exceptions
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*/
|
||||
protected function pass(Request $request, $catch = false)
|
||||
{
|
||||
$this->record($request, 'pass');
|
||||
|
||||
return $this->forward($request, $catch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates non-safe methods (like POST, PUT, and DELETE).
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Boolean $catch Whether to process exceptions
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*
|
||||
* @see RFC2616 13.10
|
||||
*/
|
||||
protected function invalidate(Request $request, $catch = false)
|
||||
{
|
||||
$response = $this->pass($request, $catch);
|
||||
|
||||
// invalidate only when the response is successful
|
||||
if ($response->isSuccessful() || $response->isRedirect()) {
|
||||
try {
|
||||
$this->store->invalidate($request, $catch);
|
||||
|
||||
$this->record($request, 'invalidate');
|
||||
} catch (\Exception $e) {
|
||||
$this->record($request, 'invalidate-failed');
|
||||
|
||||
if ($this->options['debug']) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookups a Response from the cache for the given Request.
|
||||
*
|
||||
* When a matching cache entry is found and is fresh, it uses it as the
|
||||
* response without forwarding any request to the backend. When a matching
|
||||
* cache entry is found but is stale, it attempts to "validate" the entry with
|
||||
* the backend using conditional GET. When no matching cache entry is found,
|
||||
* it triggers "miss" processing.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Boolean $catch whether to process exceptions
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
try {
|
||||
$entry = $this->store->lookup($request);
|
||||
} catch (\Exception $e) {
|
||||
$this->record($request, 'lookup-failed');
|
||||
|
||||
if ($this->options['debug']) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $this->pass($request, $catch);
|
||||
}
|
||||
|
||||
if (null === $entry) {
|
||||
$this->record($request, 'miss');
|
||||
|
||||
return $this->fetch($request, $catch);
|
||||
}
|
||||
|
||||
if (!$this->isFreshEnough($request, $entry)) {
|
||||
$this->record($request, 'stale');
|
||||
|
||||
return $this->validate($request, $entry, $catch);
|
||||
}
|
||||
|
||||
$this->record($request, 'fresh');
|
||||
|
||||
$entry->headers->set('Age', $entry->getAge());
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a cache entry is fresh.
|
||||
*
|
||||
* The original request is used as a template for a conditional
|
||||
* GET request with the backend.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Response $entry A Response instance to validate
|
||||
* @param Boolean $catch Whether to process exceptions
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*/
|
||||
protected function validate(Request $request, Response $entry, $catch = false)
|
||||
{
|
||||
$subRequest = clone $request;
|
||||
|
||||
// send no head requests because we want content
|
||||
$subRequest->setMethod('GET');
|
||||
|
||||
// add our cached last-modified validator
|
||||
$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();
|
||||
$requestEtags = $request->getEtags();
|
||||
if ($etags = array_unique(array_merge($cachedEtags, $requestEtags))) {
|
||||
$subRequest->headers->set('if_none_match', implode(', ', $etags));
|
||||
}
|
||||
|
||||
$response = $this->forward($subRequest, $catch, $entry);
|
||||
|
||||
if (304 == $response->getStatusCode()) {
|
||||
$this->record($request, 'valid');
|
||||
|
||||
// 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)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$entry = clone $entry;
|
||||
$entry->headers->remove('Date');
|
||||
|
||||
foreach (array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified') as $name) {
|
||||
if ($response->headers->has($name)) {
|
||||
$entry->headers->set($name, $response->headers->get($name));
|
||||
}
|
||||
}
|
||||
|
||||
$response = $entry;
|
||||
} else {
|
||||
$this->record($request, 'invalid');
|
||||
}
|
||||
|
||||
if ($response->isCacheable()) {
|
||||
$this->store($request, $response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Boolean $catch whether to process exceptions
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*/
|
||||
protected function fetch(Request $request, $catch = false)
|
||||
{
|
||||
$subRequest = clone $request;
|
||||
|
||||
// send no head requests because we want content
|
||||
$subRequest->setMethod('GET');
|
||||
|
||||
// avoid that the backend sends no content
|
||||
$subRequest->headers->remove('if_modified_since');
|
||||
$subRequest->headers->remove('if_none_match');
|
||||
|
||||
$response = $this->forward($subRequest, $catch);
|
||||
|
||||
if ($this->isPrivateRequest($request) && !$response->headers->hasCacheControlDirective('public')) {
|
||||
$response->setPrivate(true);
|
||||
} elseif ($this->options['default_ttl'] > 0 && null === $response->getTtl() && !$response->headers->getCacheControlDirective('must-revalidate')) {
|
||||
$response->setTtl($this->options['default_ttl']);
|
||||
}
|
||||
|
||||
if ($response->isCacheable()) {
|
||||
$this->store($request, $response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards the Request to the backend and returns the Response.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Boolean $catch Whether to catch exceptions or not
|
||||
* @param Response $entry A Response instance (the stale entry if present, null otherwise)
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*/
|
||||
protected function forward(Request $request, $catch = false, Response $entry = null)
|
||||
{
|
||||
if ($this->esi) {
|
||||
$this->esi->addSurrogateEsiCapability($request);
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// 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))) {
|
||||
if (null === $age = $entry->headers->getCacheControlDirective('stale-if-error')) {
|
||||
$age = $this->options['stale_if_error'];
|
||||
}
|
||||
|
||||
if (abs($entry->getTtl()) < $age) {
|
||||
$this->record($request, 'stale-if-error');
|
||||
|
||||
return $entry;
|
||||
}
|
||||
}
|
||||
|
||||
$this->processResponseBody($request, $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Boolean true if the cache entry if fresh enough, false otherwise
|
||||
*/
|
||||
protected function isFreshEnough(Request $request, Response $entry)
|
||||
{
|
||||
if (!$entry->isFresh()) {
|
||||
return $this->lock($request, $entry);
|
||||
}
|
||||
|
||||
if ($this->options['allow_revalidate'] && null !== $maxAge = $request->headers->getCacheControlDirective('max-age')) {
|
||||
return $maxAge > 0 && $maxAge >= $entry->getAge();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks a Request during the call to the backend.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Response $entry A Response instance
|
||||
*
|
||||
* @return Boolean true if the cache entry can be returned even if it is staled, false otherwise
|
||||
*/
|
||||
protected function lock(Request $request, Response $entry)
|
||||
{
|
||||
// try to acquire a lock to call the backend
|
||||
$lock = $this->store->lock($request, $entry);
|
||||
|
||||
// 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 (is_file($lock) && $wait < 5000000) {
|
||||
usleep(50000);
|
||||
$wait += 50000;
|
||||
}
|
||||
|
||||
if ($wait < 2000000) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// we have the lock, call the backend
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the Response to the cache.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Response $response A Response instance
|
||||
*/
|
||||
protected function store(Request $request, Response $response)
|
||||
{
|
||||
try {
|
||||
$this->store->write($request, $response);
|
||||
|
||||
$this->record($request, 'store');
|
||||
|
||||
$response->headers->set('Age', $response->getAge());
|
||||
} catch (\Exception $e) {
|
||||
$this->record($request, 'store-failed');
|
||||
|
||||
if ($this->options['debug']) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
// now that the response is cached, release the lock
|
||||
$this->store->unlock($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the Response body.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Response $response A Response instance
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*/
|
||||
private function restoreResponseBody(Request $request, Response $response)
|
||||
{
|
||||
if ('HEAD' === $request->getMethod() || 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();
|
||||
|
||||
if ($response->headers->has('X-Body-File')) {
|
||||
include $response->headers->get('X-Body-File');
|
||||
} else {
|
||||
eval('; ?>'.$response->getContent().'<?php ;');
|
||||
}
|
||||
|
||||
$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()));
|
||||
}
|
||||
} elseif ($response->headers->has('X-Body-File')) {
|
||||
$response->setContent(file_get_contents($response->headers->get('X-Body-File')));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
$response->headers->remove('X-Body-File');
|
||||
}
|
||||
|
||||
protected function processResponseBody(Request $request, Response $response)
|
||||
{
|
||||
if (null !== $this->esi && $this->esi->needsEsiParsing($response)) {
|
||||
$this->esi->process($request, $response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Boolean true if the Request is private, false otherwise
|
||||
*/
|
||||
private function isPrivateRequest(Request $request)
|
||||
{
|
||||
foreach ($this->options['private_headers'] as $key) {
|
||||
$key = strtolower(str_replace('HTTP_', '', $key));
|
||||
|
||||
if ('cookie' === $key) {
|
||||
if (count($request->cookies->all())) {
|
||||
return true;
|
||||
}
|
||||
} elseif ($request->headers->has($key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records that an event took place.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param string $event The event name
|
||||
*/
|
||||
private function record(Request $request, $event)
|
||||
{
|
||||
$path = $request->getPathInfo();
|
||||
if ($qs = $request->getQueryString()) {
|
||||
$path .= '?'.$qs;
|
||||
}
|
||||
$this->traces[$request->getMethod().' '.$path][] = $event;
|
||||
}
|
||||
}
|
||||
@@ -1,419 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (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.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\HttpCache;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Store implements all the logic for storing cache metadata (Request and Response headers).
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Store implements StoreInterface
|
||||
{
|
||||
protected $root;
|
||||
private $keyCache;
|
||||
private $locks;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $root The path to the cache directory
|
||||
*/
|
||||
public function __construct($root)
|
||||
{
|
||||
$this->root = $root;
|
||||
if (!is_dir($this->root)) {
|
||||
mkdir($this->root, 0777, true);
|
||||
}
|
||||
$this->keyCache = new \SplObjectStorage();
|
||||
$this->locks = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanups storage.
|
||||
*/
|
||||
public function cleanup()
|
||||
{
|
||||
// unlock everything
|
||||
foreach ($this->locks as $lock) {
|
||||
@unlink($lock);
|
||||
}
|
||||
|
||||
$error = error_get_last();
|
||||
if (1 === $error['type'] && false === headers_sent()) {
|
||||
// send a 503
|
||||
header('HTTP/1.0 503 Service Unavailable');
|
||||
header('Retry-After: 10');
|
||||
echo '503 Service Unavailable';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks the cache for a given Request.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @return Boolean|string true if the lock is acquired, the path to the current lock otherwise
|
||||
*/
|
||||
public function lock(Request $request)
|
||||
{
|
||||
if (false !== $lock = @fopen($path = $this->getPath($this->getCacheKey($request).'.lck'), 'x')) {
|
||||
fclose($lock);
|
||||
|
||||
$this->locks[] = $path;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the lock for the given Request.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @return Boolean False if the lock file does not exist or cannot be unlocked, true otherwise
|
||||
*/
|
||||
public function unlock(Request $request)
|
||||
{
|
||||
$file = $this->getPath($this->getCacheKey($request).'.lck');
|
||||
|
||||
return is_file($file) ? @unlink($file) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$key = $this->getCacheKey($request);
|
||||
|
||||
if (!$entries = $this->getMetadata($key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// find a cached entry that matches the request.
|
||||
$match = null;
|
||||
foreach ($entries as $entry) {
|
||||
if ($this->requestsMatch(isset($entry[1]['vary'][0]) ? $entry[1]['vary'][0] : '', $request->headers->all(), $entry[0])) {
|
||||
$match = $entry;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
list($req, $headers) = $match;
|
||||
if (is_file($body = $this->getPath($headers['x-content-digest'][0]))) {
|
||||
return $this->restoreResponse($headers, $body);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a cache entry to the store for the given Request and Response.
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
$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 (false === $this->save($digest, $response->getContent())) {
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
// read existing cache entries, remove non-varying, and add this one to the list
|
||||
$entries = array();
|
||||
$vary = $response->headers->get('vary');
|
||||
foreach ($this->getMetadata($key) as $entry) {
|
||||
if (!isset($entry[1]['vary'][0])) {
|
||||
$entry[1]['vary'] = array('');
|
||||
}
|
||||
|
||||
if ($vary != $entry[1]['vary'][0] || !$this->requestsMatch($vary, $entry[0], $storedEnv)) {
|
||||
$entries[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
$headers = $this->persistResponse($response);
|
||||
unset($headers['age']);
|
||||
|
||||
array_unshift($entries, array($storedEnv, $headers));
|
||||
|
||||
if (false === $this->save($key, serialize($entries))) {
|
||||
throw new \RuntimeException('Unable to store the metadata.');
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns content digest for $response.
|
||||
*
|
||||
* @param Response $response
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateContentDigest(Response $response)
|
||||
{
|
||||
return 'en'.sha1($response->getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates all cache entries that match the request.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*/
|
||||
public function invalidate(Request $request)
|
||||
{
|
||||
$modified = false;
|
||||
$key = $this->getCacheKey($request);
|
||||
|
||||
$entries = array();
|
||||
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));
|
||||
} else {
|
||||
$entries[] = $entry;
|
||||
}
|
||||
}
|
||||
|
||||
if ($modified) {
|
||||
if (false === $this->save($key, serialize($entries))) {
|
||||
throw new \RuntimeException('Unable to store the metadata.');
|
||||
}
|
||||
}
|
||||
|
||||
// As per the RFC, invalidate Location and Content-Location URLs if present
|
||||
foreach (array('Location', 'Content-Location') as $header) {
|
||||
if ($uri = $request->headers->get($header)) {
|
||||
$subRequest = Request::create($uri, 'get', array(), array(), array(), $request->server->all());
|
||||
|
||||
$this->invalidate($subRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether two Request HTTP header sets are non-varying based on
|
||||
* the vary response header value provided.
|
||||
*
|
||||
* @param string $vary A Response vary header
|
||||
* @param array $env1 A Request HTTP header array
|
||||
* @param array $env2 A Request HTTP header array
|
||||
*
|
||||
* @return Boolean true if the the two environments match, false otherwise
|
||||
*/
|
||||
private function requestsMatch($vary, $env1, $env2)
|
||||
{
|
||||
if (empty($vary)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (preg_split('/[\s,]+/', $vary) as $header) {
|
||||
$key = strtr(strtolower($header), '_', '-');
|
||||
$v1 = isset($env1[$key]) ? $env1[$key] : null;
|
||||
$v2 = isset($env2[$key]) ? $env2[$key] : null;
|
||||
if ($v1 !== $v2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all data associated with the given key.
|
||||
*
|
||||
* Use this method only if you know what you are doing.
|
||||
*
|
||||
* @param string $key The store key
|
||||
*
|
||||
* @return array An array of data associated with the key
|
||||
*/
|
||||
private function getMetadata($key)
|
||||
{
|
||||
if (false === $entries = $this->load($key)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return unserialize($entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges data for the given URL.
|
||||
*
|
||||
* @param string $url A URL
|
||||
*
|
||||
* @return Boolean true if the URL exists and has been purged, false otherwise
|
||||
*/
|
||||
public function purge($url)
|
||||
{
|
||||
if (is_file($path = $this->getPath($this->getCacheKey(Request::create($url))))) {
|
||||
unlink($path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads data for the given key.
|
||||
*
|
||||
* @param string $key The store key
|
||||
*
|
||||
* @return string The data associated with the key
|
||||
*/
|
||||
private function load($key)
|
||||
{
|
||||
$path = $this->getPath($key);
|
||||
|
||||
return is_file($path) ? file_get_contents($path) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save data for the given key.
|
||||
*
|
||||
* @param string $key The store key
|
||||
* @param string $data The data to store
|
||||
*/
|
||||
private function save($key, $data)
|
||||
{
|
||||
$path = $this->getPath($key);
|
||||
if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tmpFile = tempnam(dirname($path), basename($path));
|
||||
if (false === $fp = @fopen($tmpFile, 'wb')) {
|
||||
return false;
|
||||
}
|
||||
@fwrite($fp, $data);
|
||||
@fclose($fp);
|
||||
|
||||
if ($data != file_get_contents($tmpFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false === @rename($tmpFile, $path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@chmod($path, 0666 & ~umask());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
if (isset($this->keyCache[$request])) {
|
||||
return $this->keyCache[$request];
|
||||
}
|
||||
|
||||
return $this->keyCache[$request] = 'md'.sha1($request->getUri());
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the Request HTTP headers.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @return array An array of HTTP headers
|
||||
*/
|
||||
private function persistRequest(Request $request)
|
||||
{
|
||||
return $request->headers->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private function restoreResponse($headers, $body = null)
|
||||
{
|
||||
$status = $headers['X-Status'][0];
|
||||
unset($headers['X-Status']);
|
||||
|
||||
if (null !== $body) {
|
||||
$headers['X-Body-File'] = array($body);
|
||||
}
|
||||
|
||||
return new Response($body, $status, $headers);
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (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.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\HttpCache;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Interface implemented by HTTP cache stores.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Writes a cache entry to the store for the given Request and Response.
|
||||
*
|
||||
* 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 Boolean|string true if the lock is acquired, the path to the current lock otherwise
|
||||
*/
|
||||
public function lock(Request $request);
|
||||
|
||||
/**
|
||||
* Releases the lock for the given Request.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @return Boolean False if the lock file does not exist or cannot be unlocked, true otherwise
|
||||
*/
|
||||
public function unlock(Request $request);
|
||||
|
||||
/**
|
||||
* Purges data for the given URL.
|
||||
*
|
||||
* @param string $url A URL
|
||||
*
|
||||
* @return Boolean true if the URL exists and has been purged, false otherwise
|
||||
*/
|
||||
public function purge($url);
|
||||
|
||||
/**
|
||||
* Cleanups storage.
|
||||
*/
|
||||
public function cleanup();
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel;
|
||||
|
||||
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
|
||||
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\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* HttpKernel notifies events to convert a Request object to a Response one.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class HttpKernel implements HttpKernelInterface, TerminableInterface
|
||||
{
|
||||
protected $dispatcher;
|
||||
protected $resolver;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
|
||||
* @param ControllerResolverInterface $resolver A ControllerResolverInterface instance
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver)
|
||||
{
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->resolver = $resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a Request to convert it to a Response.
|
||||
*
|
||||
* When $catch is true, the implementation must catch all exceptions
|
||||
* and do its best to convert them to a Response instance.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param integer $type The type of the request
|
||||
* (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
|
||||
* @param Boolean $catch Whether to catch exceptions or not
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*
|
||||
* @throws \Exception When an Exception occurs during processing
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
|
||||
{
|
||||
try {
|
||||
return $this->handleRaw($request, $type);
|
||||
} catch (\Exception $e) {
|
||||
if (false === $catch) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $this->handleException($e, $request, $type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function terminate(Request $request, Response $response)
|
||||
{
|
||||
$this->dispatcher->dispatch(KernelEvents::TERMINATE, new PostResponseEvent($this, $request, $response));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request to convert it to a response.
|
||||
*
|
||||
* Exceptions are not caught.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*
|
||||
* @throws \LogicException If one of the listener does not behave as expected
|
||||
* @throws NotFoundHttpException When controller cannot be found
|
||||
*/
|
||||
private function handleRaw(Request $request, $type = self::MASTER_REQUEST)
|
||||
{
|
||||
// request
|
||||
$event = new GetResponseEvent($this, $request, $type);
|
||||
$this->dispatcher->dispatch(KernelEvents::REQUEST, $event);
|
||||
|
||||
if ($event->hasResponse()) {
|
||||
return $this->filterResponse($event->getResponse(), $request, $type);
|
||||
}
|
||||
|
||||
// load controller
|
||||
if (false === $controller = $this->resolver->getController($request)) {
|
||||
throw new NotFoundHttpException(sprintf('Unable to find the controller for path "%s". Maybe you forgot to add the matching route in your routing configuration?', $request->getPathInfo()));
|
||||
}
|
||||
|
||||
$event = new FilterControllerEvent($this, $controller, $request, $type);
|
||||
$this->dispatcher->dispatch(KernelEvents::CONTROLLER, $event);
|
||||
$controller = $event->getController();
|
||||
|
||||
// controller arguments
|
||||
$arguments = $this->resolver->getArguments($request, $controller);
|
||||
|
||||
// call controller
|
||||
$response = call_user_func_array($controller, $arguments);
|
||||
|
||||
// view
|
||||
if (!$response instanceof Response) {
|
||||
$event = new GetResponseForControllerResultEvent($this, $request, $type, $response);
|
||||
$this->dispatcher->dispatch(KernelEvents::VIEW, $event);
|
||||
|
||||
if ($event->hasResponse()) {
|
||||
$response = $event->getResponse();
|
||||
}
|
||||
|
||||
if (!$response instanceof Response) {
|
||||
$msg = sprintf('The controller must return a response (%s given).', $this->varToString($response));
|
||||
|
||||
// the user may have forgotten to return something
|
||||
if (null === $response) {
|
||||
$msg .= ' Did you forget to add a return statement somewhere in your controller?';
|
||||
}
|
||||
throw new \LogicException($msg);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->filterResponse($response, $request, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a response object.
|
||||
*
|
||||
* @param Response $response A Response instance
|
||||
* @param Request $request A error message in case the response is not a Response object
|
||||
* @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
|
||||
*
|
||||
* @return Response The filtered Response instance
|
||||
*
|
||||
* @throws \RuntimeException if the passed object is not a Response instance
|
||||
*/
|
||||
private function filterResponse(Response $response, Request $request, $type)
|
||||
{
|
||||
$event = new FilterResponseEvent($this, $request, $type, $response);
|
||||
|
||||
$this->dispatcher->dispatch(KernelEvents::RESPONSE, $event);
|
||||
|
||||
return $event->getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles and exception by trying to convert it to a Response.
|
||||
*
|
||||
* @param \Exception $e An \Exception instance
|
||||
* @param Request $request A Request instance
|
||||
* @param integer $type The type of the request
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*/
|
||||
private function handleException(\Exception $e, $request, $type)
|
||||
{
|
||||
$event = new GetResponseForExceptionEvent($this, $request, $type, $e);
|
||||
$this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event);
|
||||
|
||||
// a listener might have replaced the exception
|
||||
$e = $event->getException();
|
||||
|
||||
if (!$event->hasResponse()) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$response = $event->getResponse();
|
||||
|
||||
// the developer asked for a specific status code
|
||||
if ($response->headers->has('X-Status-Code')) {
|
||||
$response->setStatusCode($response->headers->get('X-Status-Code'));
|
||||
|
||||
$response->headers->remove('X-Status-Code');
|
||||
} elseif (!$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
|
||||
$response->setStatusCode($e->getStatusCode());
|
||||
$response->headers->add($e->getHeaders());
|
||||
} else {
|
||||
$response->setStatusCode(500);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->filterResponse($response, $request, $type);
|
||||
} catch (\Exception $e) {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
private function varToString($var)
|
||||
{
|
||||
if (is_object($var)) {
|
||||
return sprintf('Object(%s)', get_class($var));
|
||||
}
|
||||
|
||||
if (is_array($var)) {
|
||||
$a = array();
|
||||
foreach ($var as $k => $v) {
|
||||
$a[] = sprintf('%s => %s', $k, $this->varToString($v));
|
||||
}
|
||||
|
||||
return sprintf("Array(%s)", implode(', ', $a));
|
||||
}
|
||||
|
||||
if (is_resource($var)) {
|
||||
return sprintf('Resource(%s)', get_resource_type($var));
|
||||
}
|
||||
|
||||
if (null === $var) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
if (false === $var) {
|
||||
return 'false';
|
||||
}
|
||||
|
||||
if (true === $var) {
|
||||
return 'true';
|
||||
}
|
||||
|
||||
return (string) $var;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* HttpKernelInterface handles a Request to convert it to a Response.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
interface HttpKernelInterface
|
||||
{
|
||||
const MASTER_REQUEST = 1;
|
||||
const SUB_REQUEST = 2;
|
||||
|
||||
/**
|
||||
* Handles a Request to convert it to a Response.
|
||||
*
|
||||
* When $catch is true, the implementation must catch all exceptions
|
||||
* and do its best to convert them to a Response instance.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param integer $type The type of the request
|
||||
* (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST)
|
||||
* @param Boolean $catch Whether to catch exceptions or not
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*
|
||||
* @throws \Exception When an Exception occurs during processing
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true);
|
||||
}
|
||||
@@ -1,787 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Loader\IniFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
|
||||
use Symfony\Component\HttpKernel\Config\FileLocator;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\AddClassesToCachePass;
|
||||
use Symfony\Component\HttpKernel\Debug\ErrorHandler;
|
||||
use Symfony\Component\HttpKernel\Debug\ExceptionHandler;
|
||||
use Symfony\Component\Config\Loader\LoaderResolver;
|
||||
use Symfony\Component\Config\Loader\DelegatingLoader;
|
||||
use Symfony\Component\Config\ConfigCache;
|
||||
use Symfony\Component\ClassLoader\ClassCollectionLoader;
|
||||
use Symfony\Component\ClassLoader\DebugClassLoader;
|
||||
|
||||
/**
|
||||
* The Kernel is the heart of the Symfony system.
|
||||
*
|
||||
* It manages an environment made of bundles.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
abstract class Kernel implements KernelInterface, TerminableInterface
|
||||
{
|
||||
protected $bundles;
|
||||
protected $bundleMap;
|
||||
protected $container;
|
||||
protected $rootDir;
|
||||
protected $environment;
|
||||
protected $debug;
|
||||
protected $booted;
|
||||
protected $name;
|
||||
protected $startTime;
|
||||
protected $classes;
|
||||
protected $errorReportingLevel;
|
||||
|
||||
const VERSION = '2.1.4';
|
||||
const VERSION_ID = '20104';
|
||||
const MAJOR_VERSION = '2';
|
||||
const MINOR_VERSION = '1';
|
||||
const RELEASE_VERSION = '4';
|
||||
const EXTRA_VERSION = '';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $environment The environment
|
||||
* @param Boolean $debug Whether to enable debugging or not
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function __construct($environment, $debug)
|
||||
{
|
||||
$this->environment = $environment;
|
||||
$this->debug = (Boolean) $debug;
|
||||
$this->booted = false;
|
||||
$this->rootDir = $this->getRootDir();
|
||||
$this->name = $this->getName();
|
||||
$this->classes = array();
|
||||
|
||||
if ($this->debug) {
|
||||
$this->startTime = microtime(true);
|
||||
}
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
if ($this->debug) {
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(-1);
|
||||
|
||||
DebugClassLoader::enable();
|
||||
ErrorHandler::register($this->errorReportingLevel);
|
||||
if ('cli' !== php_sapi_name()) {
|
||||
ExceptionHandler::register();
|
||||
}
|
||||
} else {
|
||||
ini_set('display_errors', 0);
|
||||
}
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
if ($this->debug) {
|
||||
$this->startTime = microtime(true);
|
||||
}
|
||||
|
||||
$this->booted = false;
|
||||
$this->container = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Boots the current kernel.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
if (true === $this->booted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// init bundles
|
||||
$this->initializeBundles();
|
||||
|
||||
// init container
|
||||
$this->initializeContainer();
|
||||
|
||||
foreach ($this->getBundles() as $bundle) {
|
||||
$bundle->setContainer($this->container);
|
||||
$bundle->boot();
|
||||
}
|
||||
|
||||
$this->booted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function terminate(Request $request, Response $response)
|
||||
{
|
||||
if (false === $this->booted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->getHttpKernel() instanceof TerminableInterface) {
|
||||
$this->getHttpKernel()->terminate($request, $response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdowns the kernel.
|
||||
*
|
||||
* This method is mainly useful when doing functional testing.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function shutdown()
|
||||
{
|
||||
if (false === $this->booted) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->booted = false;
|
||||
|
||||
foreach ($this->getBundles() as $bundle) {
|
||||
$bundle->shutdown();
|
||||
$bundle->setContainer(null);
|
||||
}
|
||||
|
||||
$this->container = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
|
||||
{
|
||||
if (false === $this->booted) {
|
||||
$this->boot();
|
||||
}
|
||||
|
||||
return $this->getHttpKernel()->handle($request, $type, $catch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a http kernel from the container
|
||||
*
|
||||
* @return HttpKernel
|
||||
*/
|
||||
protected function getHttpKernel()
|
||||
{
|
||||
return $this->container->get('http_kernel');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the registered bundle instances.
|
||||
*
|
||||
* @return array An array of registered bundle instances
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getBundles()
|
||||
{
|
||||
return $this->bundles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given class name belongs to an active bundle.
|
||||
*
|
||||
* @param string $class A class name
|
||||
*
|
||||
* @return Boolean true if the class belongs to an active bundle, false otherwise
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function isClassInActiveBundle($class)
|
||||
{
|
||||
foreach ($this->getBundles() as $bundle) {
|
||||
if (0 === strpos($class, $bundle->getNamespace())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bundle and optionally its descendants by its name.
|
||||
*
|
||||
* @param string $name Bundle name
|
||||
* @param Boolean $first Whether to return the first bundle only or together with its descendants
|
||||
*
|
||||
* @return BundleInterface|Array A BundleInterface instance or an array of BundleInterface instances if $first is false
|
||||
*
|
||||
* @throws \InvalidArgumentException when the bundle is not enabled
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getBundle($name, $first = true)
|
||||
{
|
||||
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)));
|
||||
}
|
||||
|
||||
if (true === $first) {
|
||||
return $this->bundleMap[$name][0];
|
||||
}
|
||||
|
||||
return $this->bundleMap[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file path for a given resource.
|
||||
*
|
||||
* A Resource can be a file or a directory.
|
||||
*
|
||||
* The resource name must follow the following pattern:
|
||||
*
|
||||
* @<BundleName>/path/to/a/file.something
|
||||
*
|
||||
* where BundleName is the name of the bundle
|
||||
* and the remaining part is the relative path in the bundle.
|
||||
*
|
||||
* If $dir is passed, and the first segment of the path is "Resources",
|
||||
* this method will look for a file named:
|
||||
*
|
||||
* $dir/<BundleName>/path/without/Resources
|
||||
*
|
||||
* before looking in the bundle resource folder.
|
||||
*
|
||||
* @param string $name A resource name to locate
|
||||
* @param string $dir A directory where to look for the resource first
|
||||
* @param Boolean $first Whether to return the first path or paths for all matching bundles
|
||||
*
|
||||
* @return string|array The absolute path of the resource or an array if $first is false
|
||||
*
|
||||
* @throws \InvalidArgumentException if the file cannot be found or the name is not valid
|
||||
* @throws \RuntimeException if the name contains invalid/unsafe
|
||||
* @throws \RuntimeException if a custom resource is hidden by a resource in a derived bundle
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function locateResource($name, $dir = null, $first = true)
|
||||
{
|
||||
if ('@' !== $name[0]) {
|
||||
throw new \InvalidArgumentException(sprintf('A resource name must start with @ ("%s" given).', $name));
|
||||
}
|
||||
|
||||
if (false !== strpos($name, '..')) {
|
||||
throw new \RuntimeException(sprintf('File name "%s" contains invalid characters (..).', $name));
|
||||
}
|
||||
|
||||
$bundleName = substr($name, 1);
|
||||
$path = '';
|
||||
if (false !== strpos($bundleName, '/')) {
|
||||
list($bundleName, $path) = explode('/', $bundleName, 2);
|
||||
}
|
||||
|
||||
$isResource = 0 === strpos($path, 'Resources') && null !== $dir;
|
||||
$overridePath = substr($path, 9);
|
||||
$resourceBundle = null;
|
||||
$bundles = $this->getBundle($bundleName, false);
|
||||
$files = array();
|
||||
|
||||
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
|
||||
));
|
||||
}
|
||||
|
||||
if ($first) {
|
||||
return $file;
|
||||
}
|
||||
$files[] = $file;
|
||||
}
|
||||
|
||||
if (file_exists($file = $bundle->getPath().'/'.$path)) {
|
||||
if ($first && !$isResource) {
|
||||
return $file;
|
||||
}
|
||||
$files[] = $file;
|
||||
$resourceBundle = $bundle->getName();
|
||||
}
|
||||
}
|
||||
|
||||
if (count($files) > 0) {
|
||||
return $first && $isResource ? $files[0] : $files;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the kernel
|
||||
*
|
||||
* @return string The kernel name
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
if (null === $this->name) {
|
||||
$this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir));
|
||||
}
|
||||
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the environment.
|
||||
*
|
||||
* @return string The current environment
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getEnvironment()
|
||||
{
|
||||
return $this->environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if debug mode is enabled.
|
||||
*
|
||||
* @return Boolean true if debug mode is enabled, false otherwise
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function isDebug()
|
||||
{
|
||||
return $this->debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the application root dir.
|
||||
*
|
||||
* @return string The application root dir
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getRootDir()
|
||||
{
|
||||
if (null === $this->rootDir) {
|
||||
$r = new \ReflectionObject($this);
|
||||
$this->rootDir = str_replace('\\', '/', dirname($r->getFileName()));
|
||||
}
|
||||
|
||||
return $this->rootDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current container.
|
||||
*
|
||||
* @return ContainerInterface A ContainerInterface instance
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getContainer()
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the PHP class cache.
|
||||
*
|
||||
* @param string $name The cache name prefix
|
||||
* @param string $extension File extension of the resulting file
|
||||
*/
|
||||
public function loadClassCache($name = 'classes', $extension = '.php')
|
||||
{
|
||||
if (!$this->booted && is_file($this->getCacheDir().'/classes.map')) {
|
||||
ClassCollectionLoader::load(include($this->getCacheDir().'/classes.map'), $this->getCacheDir(), $name, $this->debug, false, $extension);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally.
|
||||
*/
|
||||
public function setClassCache(array $classes)
|
||||
{
|
||||
file_put_contents($this->getCacheDir().'/classes.map', sprintf('<?php return %s;', var_export($classes, true)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the request start time (not available if debug is disabled).
|
||||
*
|
||||
* @return integer The request start timestamp
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getStartTime()
|
||||
{
|
||||
return $this->debug ? $this->startTime : -INF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache directory.
|
||||
*
|
||||
* @return string The cache directory
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getCacheDir()
|
||||
{
|
||||
return $this->rootDir.'/cache/'.$this->environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the log directory.
|
||||
*
|
||||
* @return string The log directory
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getLogDir()
|
||||
{
|
||||
return $this->rootDir.'/logs';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the charset of the application.
|
||||
*
|
||||
* @return string The charset
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getCharset()
|
||||
{
|
||||
return 'UTF-8';
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the data structures related to the bundle management.
|
||||
*
|
||||
* - the bundles property maps a bundle name to the bundle instance,
|
||||
* - the bundleMap property maps a bundle name to the bundle inheritance hierarchy (most derived bundle first).
|
||||
*
|
||||
* @throws \LogicException if two bundles share a common name
|
||||
* @throws \LogicException if a bundle tries to extend a non-registered bundle
|
||||
* @throws \LogicException if a bundle tries to extend itself
|
||||
* @throws \LogicException if two bundles extend the same ancestor
|
||||
*/
|
||||
protected function initializeBundles()
|
||||
{
|
||||
// init bundles
|
||||
$this->bundles = array();
|
||||
$topMostBundles = array();
|
||||
$directChildren = array();
|
||||
|
||||
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));
|
||||
}
|
||||
$this->bundles[$name] = $bundle;
|
||||
|
||||
if ($parentName = $bundle->getParent()) {
|
||||
if (isset($directChildren[$parentName])) {
|
||||
throw new \LogicException(sprintf('Bundle "%s" is directly extended by two bundles "%s" and "%s".', $parentName, $name, $directChildren[$parentName]));
|
||||
}
|
||||
if ($parentName == $name) {
|
||||
throw new \LogicException(sprintf('Bundle "%s" can not extend itself.', $name));
|
||||
}
|
||||
$directChildren[$parentName] = $name;
|
||||
} else {
|
||||
$topMostBundles[$name] = $bundle;
|
||||
}
|
||||
}
|
||||
|
||||
// look for orphans
|
||||
if (count($diff = array_values(array_diff(array_keys($directChildren), array_keys($this->bundles))))) {
|
||||
throw new \LogicException(sprintf('Bundle "%s" extends bundle "%s", which is not registered.', $directChildren[$diff[0]], $diff[0]));
|
||||
}
|
||||
|
||||
// inheritance
|
||||
$this->bundleMap = array();
|
||||
foreach ($topMostBundles as $name => $bundle) {
|
||||
$bundleMap = array($bundle);
|
||||
$hierarchy = array($name);
|
||||
|
||||
while (isset($directChildren[$name])) {
|
||||
$name = $directChildren[$name];
|
||||
array_unshift($bundleMap, $this->bundles[$name]);
|
||||
$hierarchy[] = $name;
|
||||
}
|
||||
|
||||
foreach ($hierarchy as $bundle) {
|
||||
$this->bundleMap[$bundle] = $bundleMap;
|
||||
array_pop($bundleMap);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the container class.
|
||||
*
|
||||
* @return string The container class
|
||||
*/
|
||||
protected function getContainerClass()
|
||||
{
|
||||
return $this->name.ucfirst($this->environment).($this->debug ? 'Debug' : '').'ProjectContainer';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the container's base class.
|
||||
*
|
||||
* All names except Container must be fully qualified.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getContainerBaseClass()
|
||||
{
|
||||
return 'Container';
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the service container.
|
||||
*
|
||||
* The cached version of the service container is used when fresh, otherwise the
|
||||
* container is built.
|
||||
*/
|
||||
protected function initializeContainer()
|
||||
{
|
||||
$class = $this->getContainerClass();
|
||||
$cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug);
|
||||
$fresh = true;
|
||||
if (!$cache->isFresh()) {
|
||||
$container = $this->buildContainer();
|
||||
$this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());
|
||||
|
||||
$fresh = false;
|
||||
}
|
||||
|
||||
require_once $cache;
|
||||
|
||||
$this->container = new $class();
|
||||
$this->container->set('kernel', $this);
|
||||
|
||||
if (!$fresh && $this->container->has('cache_warmer')) {
|
||||
$this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the kernel parameters.
|
||||
*
|
||||
* @return array An array of kernel parameters
|
||||
*/
|
||||
protected function getKernelParameters()
|
||||
{
|
||||
$bundles = array();
|
||||
foreach ($this->bundles as $name => $bundle) {
|
||||
$bundles[$name] = get_class($bundle);
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
array(
|
||||
'kernel.root_dir' => $this->rootDir,
|
||||
'kernel.environment' => $this->environment,
|
||||
'kernel.debug' => $this->debug,
|
||||
'kernel.name' => $this->name,
|
||||
'kernel.cache_dir' => $this->getCacheDir(),
|
||||
'kernel.logs_dir' => $this->getLogDir(),
|
||||
'kernel.bundles' => $bundles,
|
||||
'kernel.charset' => $this->getCharset(),
|
||||
'kernel.container_class' => $this->getContainerClass(),
|
||||
),
|
||||
$this->getEnvParameters()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the environment parameters.
|
||||
*
|
||||
* Only the parameters starting with "SYMFONY__" are considered.
|
||||
*
|
||||
* @return array An array of parameters
|
||||
*/
|
||||
protected function getEnvParameters()
|
||||
{
|
||||
$parameters = array();
|
||||
foreach ($_SERVER as $key => $value) {
|
||||
if (0 === strpos($key, 'SYMFONY__')) {
|
||||
$parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the service container.
|
||||
*
|
||||
* @return ContainerBuilder The compiled service container
|
||||
*/
|
||||
protected function buildContainer()
|
||||
{
|
||||
foreach (array('cache' => $this->getCacheDir(), 'logs' => $this->getLogDir()) as $name => $dir) {
|
||||
if (!is_dir($dir)) {
|
||||
if (false === @mkdir($dir, 0777, true)) {
|
||||
throw new \RuntimeException(sprintf("Unable to create the %s directory (%s)\n", $name, $dir));
|
||||
}
|
||||
} elseif (!is_writable($dir)) {
|
||||
throw new \RuntimeException(sprintf("Unable to write in the %s directory (%s)\n", $name, $dir));
|
||||
}
|
||||
}
|
||||
|
||||
$container = $this->getContainerBuilder();
|
||||
$extensions = array();
|
||||
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);
|
||||
}
|
||||
|
||||
$container->addObjectResource($this);
|
||||
|
||||
// ensure these extensions are implicitly loaded
|
||||
$container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions));
|
||||
|
||||
if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) {
|
||||
$container->merge($cont);
|
||||
}
|
||||
|
||||
$container->addCompilerPass(new AddClassesToCachePass($this));
|
||||
$container->compile();
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new ContainerBuilder instance used to build the service container.
|
||||
*
|
||||
* @return ContainerBuilder
|
||||
*/
|
||||
protected function getContainerBuilder()
|
||||
{
|
||||
return new ContainerBuilder(new ParameterBag($this->getKernelParameters()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the service container to PHP code in the cache.
|
||||
*
|
||||
* @param ConfigCache $cache The config cache
|
||||
* @param ContainerBuilder $container The service container
|
||||
* @param string $class The name of the class to generate
|
||||
* @param string $baseClass The name of the container's base class
|
||||
*/
|
||||
protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container, $class, $baseClass)
|
||||
{
|
||||
// cache the container
|
||||
$dumper = new PhpDumper($container);
|
||||
$content = $dumper->dump(array('class' => $class, 'base_class' => $baseClass));
|
||||
if (!$this->debug) {
|
||||
$content = self::stripComments($content);
|
||||
}
|
||||
|
||||
$cache->write($content, $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(
|
||||
new XmlFileLoader($container, $locator),
|
||||
new YamlFileLoader($container, $locator),
|
||||
new IniFileLoader($container, $locator),
|
||||
new PhpFileLoader($container, $locator),
|
||||
new ClosureLoader($container),
|
||||
));
|
||||
|
||||
return new DelegatingLoader($resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes comments from a PHP source string.
|
||||
*
|
||||
* We don't use the PHP php_strip_whitespace() function
|
||||
* as we want the content to be readable and well-formatted.
|
||||
*
|
||||
* @param string $source A PHP string
|
||||
*
|
||||
* @return string The PHP string with the comments removed
|
||||
*/
|
||||
public static function stripComments($source)
|
||||
{
|
||||
if (!function_exists('token_get_all')) {
|
||||
return $source;
|
||||
}
|
||||
|
||||
$output = '';
|
||||
foreach (token_get_all($source) as $token) {
|
||||
if (is_string($token)) {
|
||||
$output .= $token;
|
||||
} elseif (!in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) {
|
||||
$output .= $token[1];
|
||||
}
|
||||
}
|
||||
|
||||
// replace multiple new lines with a single newline
|
||||
$output = preg_replace(array('/\s+$/Sm', '/\n+/S'), "\n", $output);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return serialize(array($this->environment, $this->debug));
|
||||
}
|
||||
|
||||
public function unserialize($data)
|
||||
{
|
||||
list($environment, $debug) = unserialize($data);
|
||||
|
||||
$this->__construct($environment, $debug);
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel;
|
||||
|
||||
/**
|
||||
* Contains all events thrown in the HttpKernel component
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
final class KernelEvents
|
||||
{
|
||||
/**
|
||||
* The REQUEST event occurs at the very beginning of request
|
||||
* dispatching
|
||||
*
|
||||
* This event allows you to create a response for a request before any
|
||||
* other code in the framework is executed. The event listener method
|
||||
* receives a Symfony\Component\HttpKernel\Event\GetResponseEvent
|
||||
* instance.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
const REQUEST = 'kernel.request';
|
||||
|
||||
/**
|
||||
* The EXCEPTION event occurs when an uncaught exception appears
|
||||
*
|
||||
* This event allows you to create a response for a thrown exception or
|
||||
* to modify the thrown exception. The event listener method receives
|
||||
* a Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent
|
||||
* instance.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
const EXCEPTION = 'kernel.exception';
|
||||
|
||||
/**
|
||||
* The VIEW event occurs when the return value of a controller
|
||||
* is not a Response instance
|
||||
*
|
||||
* This event allows you to create a response for the return value of the
|
||||
* controller. The event listener method receives a
|
||||
* Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent
|
||||
* instance.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
const VIEW = 'kernel.view';
|
||||
|
||||
/**
|
||||
* The CONTROLLER event occurs once a controller was found for
|
||||
* handling a request
|
||||
*
|
||||
* This event allows you to change the controller that will handle the
|
||||
* request. The event listener method receives a
|
||||
* Symfony\Component\HttpKernel\Event\FilterControllerEvent instance.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
const CONTROLLER = 'kernel.controller';
|
||||
|
||||
/**
|
||||
* The RESPONSE event occurs once a response was created for
|
||||
* replying to a request
|
||||
*
|
||||
* This event allows you to modify or replace the response that will be
|
||||
* replied. The event listener method receives a
|
||||
* Symfony\Component\HttpKernel\Event\FilterResponseEvent instance.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
const RESPONSE = 'kernel.response';
|
||||
|
||||
/**
|
||||
* The TERMINATE event occurs once a response was sent
|
||||
*
|
||||
* This event allows you to run expensive post-response jobs.
|
||||
* The event listener method receives a
|
||||
* Symfony\Component\HttpKernel\Event\PostResponseEvent instance.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TERMINATE = 'kernel.terminate';
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
|
||||
/**
|
||||
* The Kernel is the heart of the Symfony system.
|
||||
*
|
||||
* It manages an environment made of bundles.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
interface KernelInterface extends HttpKernelInterface, \Serializable
|
||||
{
|
||||
/**
|
||||
* Returns an array of bundles to registers.
|
||||
*
|
||||
* @return array An array of bundle instances.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function registerBundles();
|
||||
|
||||
/**
|
||||
* Loads the container configuration
|
||||
*
|
||||
* @param LoaderInterface $loader A LoaderInterface instance
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function registerContainerConfiguration(LoaderInterface $loader);
|
||||
|
||||
/**
|
||||
* Boots the current kernel.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function boot();
|
||||
|
||||
/**
|
||||
* Shutdowns the kernel.
|
||||
*
|
||||
* This method is mainly useful when doing functional testing.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function shutdown();
|
||||
|
||||
/**
|
||||
* Gets the registered bundle instances.
|
||||
*
|
||||
* @return array An array of registered bundle instances
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getBundles();
|
||||
|
||||
/**
|
||||
* Checks if a given class name belongs to an active bundle.
|
||||
*
|
||||
* @param string $class A class name
|
||||
*
|
||||
* @return Boolean true if the class belongs to an active bundle, false otherwise
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function isClassInActiveBundle($class);
|
||||
|
||||
/**
|
||||
* Returns a bundle and optionally its descendants by its name.
|
||||
*
|
||||
* @param string $name Bundle name
|
||||
* @param Boolean $first Whether to return the first bundle only or together with its descendants
|
||||
*
|
||||
* @return BundleInterface|Array A BundleInterface instance or an array of BundleInterface instances if $first is false
|
||||
*
|
||||
* @throws \InvalidArgumentException when the bundle is not enabled
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getBundle($name, $first = true);
|
||||
|
||||
/**
|
||||
* Returns the file path for a given resource.
|
||||
*
|
||||
* A Resource can be a file or a directory.
|
||||
*
|
||||
* The resource name must follow the following pattern:
|
||||
*
|
||||
* @BundleName/path/to/a/file.something
|
||||
*
|
||||
* where BundleName is the name of the bundle
|
||||
* and the remaining part is the relative path in the bundle.
|
||||
*
|
||||
* If $dir is passed, and the first segment of the path is Resources,
|
||||
* this method will look for a file named:
|
||||
*
|
||||
* $dir/BundleName/path/without/Resources
|
||||
*
|
||||
* @param string $name A resource name to locate
|
||||
* @param string $dir A directory where to look for the resource first
|
||||
* @param Boolean $first Whether to return the first path or paths for all matching bundles
|
||||
*
|
||||
* @return string|array The absolute path of the resource or an array if $first is false
|
||||
*
|
||||
* @throws \InvalidArgumentException if the file cannot be found or the name is not valid
|
||||
* @throws \RuntimeException if the name contains invalid/unsafe characters
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function locateResource($name, $dir = null, $first = true);
|
||||
|
||||
/**
|
||||
* Gets the name of the kernel
|
||||
*
|
||||
* @return string The kernel name
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getName();
|
||||
|
||||
/**
|
||||
* Gets the environment.
|
||||
*
|
||||
* @return string The current environment
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getEnvironment();
|
||||
|
||||
/**
|
||||
* Checks if debug mode is enabled.
|
||||
*
|
||||
* @return Boolean true if debug mode is enabled, false otherwise
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function isDebug();
|
||||
|
||||
/**
|
||||
* Gets the application root dir.
|
||||
*
|
||||
* @return string The application root dir
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getRootDir();
|
||||
|
||||
/**
|
||||
* Gets the current container.
|
||||
*
|
||||
* @return ContainerInterface A ContainerInterface instance
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getContainer();
|
||||
|
||||
/**
|
||||
* Gets the request start time (not available if debug is disabled).
|
||||
*
|
||||
* @return integer The request start timestamp
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getStartTime();
|
||||
|
||||
/**
|
||||
* Gets the cache directory.
|
||||
*
|
||||
* @return string The cache directory
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getCacheDir();
|
||||
|
||||
/**
|
||||
* Gets the log directory.
|
||||
*
|
||||
* @return string The log directory
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getLogDir();
|
||||
|
||||
/**
|
||||
* Gets the charset of the application.
|
||||
*
|
||||
* @return string The charset
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function getCharset();
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2004-2012 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Log;
|
||||
|
||||
/**
|
||||
* DebugLoggerInterface.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface DebugLoggerInterface
|
||||
{
|
||||
/**
|
||||
* Returns an array of logs.
|
||||
*
|
||||
* A log is an array with the following mandatory keys:
|
||||
* timestamp, message, priority, and priorityName.
|
||||
* It can also have an optional context key containing an array.
|
||||
*
|
||||
* @return array An array of logs
|
||||
*/
|
||||
public function getLogs();
|
||||
|
||||
/**
|
||||
* Returns the number of errors.
|
||||
*
|
||||
* @return integer The number of errors
|
||||
*/
|
||||
public function countErrors();
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Log;
|
||||
|
||||
/**
|
||||
* LoggerInterface.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
interface LoggerInterface
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function emerg($message, array $context = array());
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function alert($message, array $context = array());
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function crit($message, array $context = array());
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function err($message, array $context = array());
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function warn($message, array $context = array());
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function notice($message, array $context = array());
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function info($message, array $context = array());
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function debug($message, array $context = array());
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Log;
|
||||
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* NullLogger.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
class NullLogger implements LoggerInterface
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function emerg($message, array $context = array())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function alert($message, array $context = array())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function crit($message, array $context = array())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function err($message, array $context = array())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function warn($message, array $context = array())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function notice($message, array $context = array())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function info($message, array $context = array())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function debug($message, array $context = array())
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Profiler;
|
||||
|
||||
/**
|
||||
* Base Memcache storage for profiling information in a Memcache.
|
||||
*
|
||||
* @author Andrej Hudec <pulzarraider@gmail.com>
|
||||
*/
|
||||
abstract class BaseMemcacheProfilerStorage implements ProfilerStorageInterface
|
||||
{
|
||||
const TOKEN_PREFIX = 'sf_profiler_';
|
||||
|
||||
protected $dsn;
|
||||
protected $lifetime;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $dsn A data source name
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param int $lifetime The lifetime to use for the purge
|
||||
*/
|
||||
public function __construct($dsn, $username = '', $password = '', $lifetime = 86400)
|
||||
{
|
||||
$this->dsn = $dsn;
|
||||
$this->lifetime = (int) $lifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function find($ip, $url, $limit, $method)
|
||||
{
|
||||
$indexName = $this->getIndexName();
|
||||
|
||||
$indexContent = $this->getValue($indexName);
|
||||
if (!$indexContent) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$profileList = explode("\n", $indexContent);
|
||||
$result = array();
|
||||
|
||||
foreach ($profileList as $item) {
|
||||
|
||||
if ($limit === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($item=='') {
|
||||
continue;
|
||||
}
|
||||
|
||||
list($itemToken, $itemIp, $itemMethod, $itemUrl, $itemTime, $itemParent) = explode("\t", $item, 6);
|
||||
|
||||
if ($ip && false === strpos($itemIp, $ip) || $url && false === strpos($itemUrl, $url) || $method && false === strpos($itemMethod, $method)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$itemToken] = array(
|
||||
'token' => $itemToken,
|
||||
'ip' => $itemIp,
|
||||
'method' => $itemMethod,
|
||||
'url' => $itemUrl,
|
||||
'time' => $itemTime,
|
||||
'parent' => $itemParent,
|
||||
);
|
||||
--$limit;
|
||||
}
|
||||
|
||||
usort($result, function($a, $b) {
|
||||
if ($a['time'] === $b['time']) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $a['time'] > $b['time'] ? -1 : 1;
|
||||
});
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
// delete only items from index
|
||||
$indexName = $this->getIndexName();
|
||||
|
||||
$indexContent = $this->getValue($indexName);
|
||||
|
||||
if (!$indexContent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$profileList = explode("\n", $indexContent);
|
||||
|
||||
foreach ($profileList as $item) {
|
||||
if ($item == '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (false !== $pos = strpos($item, "\t")) {
|
||||
$this->delete($this->getItemName(substr($item, 0, $pos)));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->delete($indexName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($token)
|
||||
{
|
||||
if (empty($token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$profile = $this->getValue($this->getItemName($token));
|
||||
|
||||
if (false !== $profile) {
|
||||
$profile = $this->createProfileFromData($token, $profile);
|
||||
}
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write(Profile $profile)
|
||||
{
|
||||
$data = array(
|
||||
'token' => $profile->getToken(),
|
||||
'parent' => $profile->getParentToken(),
|
||||
'children' => array_map(function ($p) { return $p->getToken(); }, $profile->getChildren()),
|
||||
'data' => $profile->getCollectors(),
|
||||
'ip' => $profile->getIp(),
|
||||
'method' => $profile->getMethod(),
|
||||
'url' => $profile->getUrl(),
|
||||
'time' => $profile->getTime(),
|
||||
);
|
||||
|
||||
$profileIndexed = false !== $this->getValue($this->getItemName($profile->getToken()));
|
||||
|
||||
if ($this->setValue($this->getItemName($profile->getToken()), $data, $this->lifetime)) {
|
||||
|
||||
if (!$profileIndexed) {
|
||||
// Add to index
|
||||
$indexName = $this->getIndexName();
|
||||
|
||||
$indexRow = implode("\t", array(
|
||||
$profile->getToken(),
|
||||
$profile->getIp(),
|
||||
$profile->getMethod(),
|
||||
$profile->getUrl(),
|
||||
$profile->getTime(),
|
||||
$profile->getParentToken(),
|
||||
))."\n";
|
||||
|
||||
return $this->appendValue($indexName, $indexRow, $this->lifetime);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve item from the memcache server
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function getValue($key);
|
||||
|
||||
/**
|
||||
* Store an item on the memcache server under the specified key
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $expiration
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract protected function setValue($key, $value, $expiration = 0);
|
||||
|
||||
/**
|
||||
* Delete item from the memcache server
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract protected function delete($key);
|
||||
|
||||
/**
|
||||
* Append data to an existing item on the memcache server
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @param int $expiration
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract protected function appendValue($key, $value, $expiration = 0);
|
||||
|
||||
private function createProfileFromData($token, $data, $parent = null)
|
||||
{
|
||||
$profile = new Profile($token);
|
||||
$profile->setIp($data['ip']);
|
||||
$profile->setMethod($data['method']);
|
||||
$profile->setUrl($data['url']);
|
||||
$profile->setTime($data['time']);
|
||||
$profile->setCollectors($data['data']);
|
||||
|
||||
if (!$parent && $data['parent']) {
|
||||
$parent = $this->read($data['parent']);
|
||||
}
|
||||
|
||||
if ($parent) {
|
||||
$profile->setParent($parent);
|
||||
}
|
||||
|
||||
foreach ($data['children'] as $token) {
|
||||
if (!$token) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$childProfileData = $this->getValue($this->getItemName($token))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$profile->addChild($this->createProfileFromData($token, $childProfileData, $profile));
|
||||
}
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item name
|
||||
*
|
||||
* @param string $token
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getItemName($token)
|
||||
{
|
||||
$name = self::TOKEN_PREFIX . $token;
|
||||
|
||||
if ($this->isItemNameValid($name)) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name of index
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getIndexName()
|
||||
{
|
||||
$name = self::TOKEN_PREFIX . 'index';
|
||||
|
||||
if ($this->isItemNameValid($name)) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isItemNameValid($name)
|
||||
{
|
||||
$length = strlen($name);
|
||||
|
||||
if ($length > 250) {
|
||||
throw new \RuntimeException(sprintf('The memcache item key "%s" is too long (%s bytes). Allowed maximum size is 250 bytes.', $name, $length));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Profiler;
|
||||
|
||||
/**
|
||||
* Storage for profiler using files.
|
||||
*
|
||||
* @author Alexandre Salomé <alexandre.salome@gmail.com>
|
||||
*/
|
||||
class FileProfilerStorage implements ProfilerStorageInterface
|
||||
{
|
||||
/**
|
||||
* Folder where profiler data are stored.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $folder;
|
||||
|
||||
/**
|
||||
* Constructs the file storage using a "dsn-like" path.
|
||||
*
|
||||
* Example : "file:/path/to/the/storage/folder"
|
||||
*
|
||||
* @param string $dsn The DSN
|
||||
*/
|
||||
public function __construct($dsn)
|
||||
{
|
||||
if (0 !== strpos($dsn, 'file:')) {
|
||||
throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use FileStorage with an invalid dsn "%s". The expected format is "file:/path/to/the/storage/folder".', $dsn));
|
||||
}
|
||||
$this->folder = substr($dsn, 5);
|
||||
|
||||
if (!is_dir($this->folder)) {
|
||||
mkdir($this->folder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function find($ip, $url, $limit, $method)
|
||||
{
|
||||
$file = $this->getIndexFilename();
|
||||
|
||||
if (!file_exists($file)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$file = fopen($file, 'r');
|
||||
fseek($file, 0, SEEK_END);
|
||||
|
||||
$result = array();
|
||||
|
||||
while ($limit > 0) {
|
||||
$line = $this->readLineFromFile($file);
|
||||
|
||||
if (false === $line) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($line === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
list($csvToken, $csvIp, $csvMethod, $csvUrl, $csvTime, $csvParent) = str_getcsv($line);
|
||||
|
||||
if ($ip && false === strpos($csvIp, $ip) || $url && false === strpos($csvUrl, $url) || $method && false === strpos($csvMethod, $method)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$csvToken] = array(
|
||||
'token' => $csvToken,
|
||||
'ip' => $csvIp,
|
||||
'method' => $csvMethod,
|
||||
'url' => $csvUrl,
|
||||
'time' => $csvTime,
|
||||
'parent' => $csvParent,
|
||||
);
|
||||
--$limit;
|
||||
}
|
||||
|
||||
fclose($file);
|
||||
|
||||
return array_values($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
$flags = \FilesystemIterator::SKIP_DOTS;
|
||||
$iterator = new \RecursiveDirectoryIterator($this->folder, $flags);
|
||||
$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if (is_file($file)) {
|
||||
unlink($file);
|
||||
} else {
|
||||
rmdir($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($token)
|
||||
{
|
||||
if (!$token || !file_exists($file = $this->getFilename($token))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->createProfileFromData($token, unserialize(file_get_contents($file)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write(Profile $profile)
|
||||
{
|
||||
$file = $this->getFilename($profile->getToken());
|
||||
|
||||
$profileIndexed = is_file($file);
|
||||
if (!$profileIndexed) {
|
||||
// Create directory
|
||||
$dir = dirname($file);
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0777, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Store profile
|
||||
$data = array(
|
||||
'token' => $profile->getToken(),
|
||||
'parent' => $profile->getParentToken(),
|
||||
'children' => array_map(function ($p) { return $p->getToken(); }, $profile->getChildren()),
|
||||
'data' => $profile->getCollectors(),
|
||||
'ip' => $profile->getIp(),
|
||||
'method' => $profile->getMethod(),
|
||||
'url' => $profile->getUrl(),
|
||||
'time' => $profile->getTime(),
|
||||
);
|
||||
|
||||
if (false === file_put_contents($file, serialize($data))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$profileIndexed) {
|
||||
// Add to index
|
||||
if (false === $file = fopen($this->getIndexFilename(), 'a')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fputcsv($file, array(
|
||||
$profile->getToken(),
|
||||
$profile->getIp(),
|
||||
$profile->getMethod(),
|
||||
$profile->getUrl(),
|
||||
$profile->getTime(),
|
||||
$profile->getParentToken(),
|
||||
));
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets filename to store data, associated to the token.
|
||||
*
|
||||
* @return string The profile filename
|
||||
*/
|
||||
protected function getFilename($token)
|
||||
{
|
||||
// Uses 4 last characters, because first are mostly the same.
|
||||
$folderA = substr($token, -2, 2);
|
||||
$folderB = substr($token, -4, 2);
|
||||
|
||||
return $this->folder.'/'.$folderA.'/'.$folderB.'/'.$token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index filename.
|
||||
*
|
||||
* @return string The index filename
|
||||
*/
|
||||
protected function getIndexFilename()
|
||||
{
|
||||
return $this->folder.'/index.csv';
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a line in the file, ending with the current position.
|
||||
*
|
||||
* This function automatically skips the empty lines and do not include the line return in result value.
|
||||
*
|
||||
* @param resource $file The file resource, with the pointer placed at the end of the line to read
|
||||
*
|
||||
* @return mixed A string representing the line or FALSE if beginning of file is reached
|
||||
*/
|
||||
protected function readLineFromFile($file)
|
||||
{
|
||||
if (ftell($file) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek($file, -1, SEEK_CUR);
|
||||
$str = '';
|
||||
|
||||
while (true) {
|
||||
$char = fgetc($file);
|
||||
|
||||
if ($char === "\n") {
|
||||
// Leave the file with cursor before the line return
|
||||
fseek($file, -1, SEEK_CUR);
|
||||
break;
|
||||
}
|
||||
|
||||
$str = $char.$str;
|
||||
|
||||
if (ftell($file) === 1) {
|
||||
// All file is read, so we move cursor to the position 0
|
||||
fseek($file, -1, SEEK_CUR);
|
||||
break;
|
||||
}
|
||||
|
||||
fseek($file, -2, SEEK_CUR);
|
||||
}
|
||||
|
||||
return $str === '' ? $this->readLineFromFile($file) : $str;
|
||||
}
|
||||
|
||||
protected function createProfileFromData($token, $data, $parent = null)
|
||||
{
|
||||
$profile = new Profile($token);
|
||||
$profile->setIp($data['ip']);
|
||||
$profile->setMethod($data['method']);
|
||||
$profile->setUrl($data['url']);
|
||||
$profile->setTime($data['time']);
|
||||
$profile->setCollectors($data['data']);
|
||||
|
||||
if (!$parent && $data['parent']) {
|
||||
$parent = $this->read($data['parent']);
|
||||
}
|
||||
|
||||
if ($parent) {
|
||||
$profile->setParent($parent);
|
||||
}
|
||||
|
||||
foreach ($data['children'] as $token) {
|
||||
if (!$token || !file_exists($file = $this->getFilename($token))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$profile->addChild($this->createProfileFromData($token, unserialize(file_get_contents($file)), $profile));
|
||||
}
|
||||
|
||||
return $profile;
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Profiler;
|
||||
|
||||
use Memcache;
|
||||
|
||||
/**
|
||||
* Memcache Profiler Storage
|
||||
*
|
||||
* @author Andrej Hudec <pulzarraider@gmail.com>
|
||||
*/
|
||||
class MemcacheProfilerStorage extends BaseMemcacheProfilerStorage
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Memcache
|
||||
*/
|
||||
private $memcache;
|
||||
|
||||
/**
|
||||
* Internal convenience method that returns the instance of the Memcache
|
||||
*
|
||||
* @return Memcache
|
||||
*/
|
||||
protected function getMemcache()
|
||||
{
|
||||
if (null === $this->memcache) {
|
||||
if (!preg_match('#^memcache://(?(?=\[.*\])\[(.*)\]|(.*)):(.*)$#', $this->dsn, $matches)) {
|
||||
throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Memcache with an invalid dsn "%s". The expected format is "memcache://[host]:port".', $this->dsn));
|
||||
}
|
||||
|
||||
$host = $matches[1] ?: $matches[2];
|
||||
$port = $matches[3];
|
||||
|
||||
$memcache = new Memcache;
|
||||
$memcache->addServer($host, $port);
|
||||
|
||||
$this->memcache = $memcache;
|
||||
}
|
||||
|
||||
return $this->memcache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set instance of the Memcache
|
||||
*
|
||||
* @param Memcache $memcache
|
||||
*/
|
||||
public function setMemcache($memcache)
|
||||
{
|
||||
$this->memcache = $memcache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getValue($key)
|
||||
{
|
||||
return $this->getMemcache()->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setValue($key, $value, $expiration = 0)
|
||||
{
|
||||
return $this->getMemcache()->set($key, $value, false, time() + $expiration);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function delete($key)
|
||||
{
|
||||
return $this->getMemcache()->delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function appendValue($key, $value, $expiration = 0)
|
||||
{
|
||||
$memcache = $this->getMemcache();
|
||||
|
||||
if (method_exists($memcache, 'append')) {
|
||||
|
||||
//Memcache v3.0
|
||||
if (!$result = $memcache->append($key, $value, false, $expiration)) {
|
||||
return $memcache->set($key, $value, false, $expiration);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
//simulate append in Memcache <3.0
|
||||
$content = $memcache->get($key);
|
||||
|
||||
return $memcache->set($key, $content . $value, false, $expiration);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Profiler;
|
||||
|
||||
use Memcached;
|
||||
|
||||
/**
|
||||
* Memcached Profiler Storage
|
||||
*
|
||||
* @author Andrej Hudec <pulzarraider@gmail.com>
|
||||
*/
|
||||
class MemcachedProfilerStorage extends BaseMemcacheProfilerStorage
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Memcached
|
||||
*/
|
||||
private $memcached;
|
||||
|
||||
/**
|
||||
* Internal convenience method that returns the instance of the Memcached
|
||||
*
|
||||
* @return Memcached
|
||||
*/
|
||||
protected function getMemcached()
|
||||
{
|
||||
if (null === $this->memcached) {
|
||||
if (!preg_match('#^memcached://(?(?=\[.*\])\[(.*)\]|(.*)):(.*)$#', $this->dsn, $matches)) {
|
||||
throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Memcached with an invalid dsn "%s". The expected format is "memcached://[host]:port".', $this->dsn));
|
||||
}
|
||||
|
||||
$host = $matches[1] ?: $matches[2];
|
||||
$port = $matches[3];
|
||||
|
||||
$memcached = new Memcached;
|
||||
|
||||
//disable compression to allow appending
|
||||
$memcached->setOption(Memcached::OPT_COMPRESSION, false);
|
||||
|
||||
$memcached->addServer($host, $port);
|
||||
|
||||
$this->memcached = $memcached;
|
||||
}
|
||||
|
||||
return $this->memcached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set instance of the Memcached
|
||||
*
|
||||
* @param Memcached $memcached
|
||||
*/
|
||||
public function setMemcached($memcached)
|
||||
{
|
||||
$this->memcached = $memcached;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getValue($key)
|
||||
{
|
||||
return $this->getMemcached()->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setValue($key, $value, $expiration = 0)
|
||||
{
|
||||
return $this->getMemcached()->set($key, $value, time() + $expiration);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function delete($key)
|
||||
{
|
||||
return $this->getMemcached()->delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function appendValue($key, $value, $expiration = 0)
|
||||
{
|
||||
$memcached = $this->getMemcached();
|
||||
|
||||
if (!$result = $memcached->append($key, $value)) {
|
||||
return $memcached->set($key, $value, $expiration);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Profiler;
|
||||
|
||||
class MongoDbProfilerStorage implements ProfilerStorageInterface
|
||||
{
|
||||
protected $dsn;
|
||||
protected $lifetime;
|
||||
private $mongo;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $dsn A data source name
|
||||
* @param string $username Not used
|
||||
* @param string $password Not used
|
||||
* @param integer $lifetime The lifetime to use for the purge
|
||||
*/
|
||||
public function __construct($dsn, $username = '', $password = '', $lifetime = 86400)
|
||||
{
|
||||
$this->dsn = $dsn;
|
||||
$this->lifetime = (int) $lifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds profiler tokens for the given criteria.
|
||||
*
|
||||
* @param string $ip The IP
|
||||
* @param string $url The URL
|
||||
* @param string $limit The maximum number of tokens to return
|
||||
* @param string $method The request method
|
||||
*
|
||||
* @return array An array of tokens
|
||||
*/
|
||||
public function find($ip, $url, $limit, $method)
|
||||
{
|
||||
$cursor = $this->getMongo()->find($this->buildQuery($ip, $url, $method), array('_id', 'parent', 'ip', 'method', 'url', 'time'))->sort(array('time' => -1))->limit($limit);
|
||||
|
||||
$tokens = array();
|
||||
foreach ($cursor as $profile) {
|
||||
$tokens[] = $this->getData($profile);
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges all data from the database.
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
$this->getMongo()->remove(array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data associated with the given token.
|
||||
*
|
||||
* The method returns false if the token does not exists in the storage.
|
||||
*
|
||||
* @param string $token A token
|
||||
*
|
||||
* @return Profile The profile associated with token
|
||||
*/
|
||||
public function read($token)
|
||||
{
|
||||
$profile = $this->getMongo()->findOne(array('_id' => $token, 'data' => array('$exists' => true)));
|
||||
|
||||
if (null !== $profile) {
|
||||
$profile = $this->createProfileFromData($this->getData($profile));
|
||||
}
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a Profile.
|
||||
*
|
||||
* @param Profile $profile A Profile instance
|
||||
*
|
||||
* @return Boolean Write operation successful
|
||||
*/
|
||||
public function write(Profile $profile)
|
||||
{
|
||||
$this->cleanup();
|
||||
|
||||
$record = array(
|
||||
'_id' => $profile->getToken(),
|
||||
'parent' => $profile->getParentToken(),
|
||||
'data' => base64_encode(serialize($profile->getCollectors())),
|
||||
'ip' => $profile->getIp(),
|
||||
'method' => $profile->getMethod(),
|
||||
'url' => $profile->getUrl(),
|
||||
'time' => $profile->getTime()
|
||||
);
|
||||
|
||||
return $this->getMongo()->update(array('_id' => $profile->getToken()), array_filter($record, function ($v) { return !empty($v); }), array('upsert' => true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal convenience method that returns the instance of the MongoDB Collection
|
||||
*
|
||||
* @return \MongoCollection
|
||||
*/
|
||||
protected function getMongo()
|
||||
{
|
||||
if ($this->mongo === null) {
|
||||
if (preg_match('#^(mongodb://.*)/(.*)/(.*)$#', $this->dsn, $matches)) {
|
||||
$mongo = new \Mongo($matches[1] . (!empty($matches[2]) ? '/' . $matches[2] : ''));
|
||||
$database = $matches[2];
|
||||
$collection = $matches[3];
|
||||
$this->mongo = $mongo->selectCollection($database, $collection);
|
||||
} else {
|
||||
throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use MongoDB with an invalid dsn "%s". The expected format is "mongodb://user:pass@location/database/collection"', $this->dsn));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->mongo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return Profile
|
||||
*/
|
||||
protected function createProfileFromData(array $data)
|
||||
{
|
||||
$profile = $this->getProfile($data);
|
||||
|
||||
if ($data['parent']) {
|
||||
$parent = $this->getMongo()->findOne(array('_id' => $data['parent'], 'data' => array('$exists' => true)));
|
||||
if ($parent) {
|
||||
$profile->setParent($this->getProfile($this->getData($parent)));
|
||||
}
|
||||
}
|
||||
|
||||
$profile->setChildren($this->readChildren($data['token']));
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @return array
|
||||
*/
|
||||
protected function readChildren($token)
|
||||
{
|
||||
$profiles = array();
|
||||
|
||||
$cursor = $this->getMongo()->find(array('parent' => $token, 'data' => array('$exists' => true)));
|
||||
foreach ($cursor as $d) {
|
||||
$profiles[] = $this->getProfile($this->getData($d));
|
||||
}
|
||||
|
||||
return $profiles;
|
||||
}
|
||||
|
||||
protected function cleanup()
|
||||
{
|
||||
$this->getMongo()->remove(array('time' => array('$lt' => time() - $this->lifetime)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $ip
|
||||
* @param string $url
|
||||
* @param string $method
|
||||
* @return array
|
||||
*/
|
||||
private function buildQuery($ip, $url, $method)
|
||||
{
|
||||
$query = array();
|
||||
|
||||
if (!empty($ip)) {
|
||||
$query['ip'] = $ip;
|
||||
}
|
||||
|
||||
if (!empty($url)) {
|
||||
$query['url'] = $url;
|
||||
}
|
||||
|
||||
if (!empty($method)) {
|
||||
$query['method'] = $method;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
private function getData(array $data)
|
||||
{
|
||||
return array(
|
||||
'token' => $data['_id'],
|
||||
'parent' => isset($data['parent']) ? $data['parent'] : null,
|
||||
'ip' => isset($data['ip']) ? $data['ip'] : null,
|
||||
'method' => isset($data['method']) ? $data['method'] : null,
|
||||
'url' => isset($data['url']) ? $data['url'] : null,
|
||||
'time' => isset($data['time']) ? $data['time'] : null,
|
||||
'data' => isset($data['data']) ? $data['data'] : null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
* @return Profile
|
||||
*/
|
||||
private function getProfile(array $data)
|
||||
{
|
||||
$profile = new Profile($data['token']);
|
||||
$profile->setIp($data['ip']);
|
||||
$profile->setMethod($data['method']);
|
||||
$profile->setUrl($data['url']);
|
||||
$profile->setTime($data['time']);
|
||||
$profile->setCollectors(unserialize(base64_decode($data['data'])));
|
||||
|
||||
return $profile;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Profiler;
|
||||
|
||||
/**
|
||||
* A ProfilerStorage for Mysql
|
||||
*
|
||||
* @author Jan Schumann <js@schumann-it.com>
|
||||
*/
|
||||
class MysqlProfilerStorage extends PdoProfilerStorage
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initDb()
|
||||
{
|
||||
if (null === $this->db) {
|
||||
if (0 !== strpos($this->dsn, 'mysql')) {
|
||||
throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Mysql with an invalid dsn "%s". The expected format is "mysql:dbname=database_name;host=host_name".', $this->dsn));
|
||||
}
|
||||
|
||||
if (!class_exists('PDO') || !in_array('mysql', \PDO::getAvailableDrivers(), true)) {
|
||||
throw new \RuntimeException('You need to enable PDO_Mysql extension for the profiler to run properly.');
|
||||
}
|
||||
|
||||
$db = new \PDO($this->dsn, $this->username, $this->password);
|
||||
$db->exec('CREATE TABLE IF NOT EXISTS sf_profiler_data (token VARCHAR(255) PRIMARY KEY, data LONGTEXT, ip VARCHAR(64), method VARCHAR(6), url VARCHAR(255), time INTEGER UNSIGNED, parent VARCHAR(255), created_at INTEGER UNSIGNED, KEY (created_at), KEY (ip), KEY (method), KEY (url), KEY (parent))');
|
||||
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildCriteria($ip, $url, $limit, $method)
|
||||
{
|
||||
$criteria = array();
|
||||
$args = array();
|
||||
|
||||
if ($ip = preg_replace('/[^\d\.]/', '', $ip)) {
|
||||
$criteria[] = 'ip LIKE :ip';
|
||||
$args[':ip'] = '%'.$ip.'%';
|
||||
}
|
||||
|
||||
if ($url) {
|
||||
$criteria[] = 'url LIKE :url';
|
||||
$args[':url'] = '%'.addcslashes($url, '%_\\').'%';
|
||||
}
|
||||
|
||||
if ($method) {
|
||||
$criteria[] = 'method = :method';
|
||||
$args[':method'] = $method;
|
||||
}
|
||||
|
||||
return array($criteria, $args);
|
||||
}
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Profiler;
|
||||
|
||||
/**
|
||||
* Base PDO storage for profiling information in a PDO database.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jan Schumann <js@schumann-it.com>
|
||||
*/
|
||||
abstract class PdoProfilerStorage implements ProfilerStorageInterface
|
||||
{
|
||||
protected $dsn;
|
||||
protected $username;
|
||||
protected $password;
|
||||
protected $lifetime;
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $dsn A data source name
|
||||
* @param string $username The username for the database
|
||||
* @param string $password The password for the database
|
||||
* @param integer $lifetime The lifetime to use for the purge
|
||||
*/
|
||||
public function __construct($dsn, $username = '', $password = '', $lifetime = 86400)
|
||||
{
|
||||
$this->dsn = $dsn;
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
$this->lifetime = (int) $lifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function find($ip, $url, $limit, $method)
|
||||
{
|
||||
list($criteria, $args) = $this->buildCriteria($ip, $url, $limit, $method);
|
||||
|
||||
$criteria = $criteria ? 'WHERE '.implode(' AND ', $criteria) : '';
|
||||
|
||||
$db = $this->initDb();
|
||||
$tokens = $this->fetch($db, 'SELECT token, ip, method, url, time, parent FROM sf_profiler_data '.$criteria.' ORDER BY time DESC LIMIT '.((integer) $limit), $args);
|
||||
$this->close($db);
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($token)
|
||||
{
|
||||
$db = $this->initDb();
|
||||
$args = array(':token' => $token);
|
||||
$data = $this->fetch($db, 'SELECT data, parent, ip, method, url, time FROM sf_profiler_data WHERE token = :token LIMIT 1', $args);
|
||||
$this->close($db);
|
||||
if (isset($data[0]['data'])) {
|
||||
return $this->createProfileFromData($token, $data[0]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write(Profile $profile)
|
||||
{
|
||||
$db = $this->initDb();
|
||||
$args = array(
|
||||
':token' => $profile->getToken(),
|
||||
':parent' => $profile->getParentToken(),
|
||||
':data' => base64_encode(serialize($profile->getCollectors())),
|
||||
':ip' => $profile->getIp(),
|
||||
':method' => $profile->getMethod(),
|
||||
':url' => $profile->getUrl(),
|
||||
':time' => $profile->getTime(),
|
||||
':created_at' => time(),
|
||||
);
|
||||
|
||||
try {
|
||||
if ($this->has($profile->getToken())) {
|
||||
$this->exec($db, 'UPDATE sf_profiler_data SET parent = :parent, data = :data, ip = :ip, method = :method, url = :url, time = :time, created_at = :created_at WHERE token = :token', $args);
|
||||
} else {
|
||||
$this->exec($db, 'INSERT INTO sf_profiler_data (token, parent, data, ip, method, url, time, created_at) VALUES (:token, :parent, :data, :ip, :method, :url, :time, :created_at)', $args);
|
||||
}
|
||||
$this->cleanup();
|
||||
$status = true;
|
||||
} catch (\Exception $e) {
|
||||
$status = false;
|
||||
}
|
||||
|
||||
$this->close($db);
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
$db = $this->initDb();
|
||||
$this->exec($db, 'DELETE FROM sf_profiler_data');
|
||||
$this->close($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build SQL criteria to fetch records by ip and url
|
||||
*
|
||||
* @param string $ip The IP
|
||||
* @param string $url The URL
|
||||
* @param string $limit The maximum number of tokens to return
|
||||
* @param string $method The request method
|
||||
*
|
||||
* @return array An array with (criteria, args)
|
||||
*/
|
||||
abstract protected function buildCriteria($ip, $url, $limit, $method);
|
||||
|
||||
/**
|
||||
* Initializes the database
|
||||
*
|
||||
* @throws \RuntimeException When the requested database driver is not installed
|
||||
*/
|
||||
abstract protected function initDb();
|
||||
|
||||
protected function cleanup()
|
||||
{
|
||||
$db = $this->initDb();
|
||||
$this->exec($db, 'DELETE FROM sf_profiler_data WHERE created_at < :time', array(':time' => time() - $this->lifetime));
|
||||
$this->close($db);
|
||||
}
|
||||
|
||||
protected function exec($db, $query, array $args = array())
|
||||
{
|
||||
$stmt = $this->prepareStatement($db, $query);
|
||||
|
||||
foreach ($args as $arg => $val) {
|
||||
$stmt->bindValue($arg, $val, is_int($val) ? \PDO::PARAM_INT : \PDO::PARAM_STR);
|
||||
}
|
||||
$success = $stmt->execute();
|
||||
if (!$success) {
|
||||
throw new \RuntimeException(sprintf('Error executing query "%s"', $query));
|
||||
}
|
||||
}
|
||||
|
||||
protected function prepareStatement($db, $query)
|
||||
{
|
||||
try {
|
||||
$stmt = $db->prepare($query);
|
||||
} catch (\Exception $e) {
|
||||
$stmt = false;
|
||||
}
|
||||
|
||||
if (false === $stmt) {
|
||||
throw new \RuntimeException('The database cannot successfully prepare the statement');
|
||||
}
|
||||
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
protected function fetch($db, $query, array $args = array())
|
||||
{
|
||||
$stmt = $this->prepareStatement($db, $query);
|
||||
|
||||
foreach ($args as $arg => $val) {
|
||||
$stmt->bindValue($arg, $val, is_int($val) ? \PDO::PARAM_INT : \PDO::PARAM_STR);
|
||||
}
|
||||
$stmt->execute();
|
||||
$return = $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
protected function close($db)
|
||||
{
|
||||
}
|
||||
|
||||
protected function createProfileFromData($token, $data, $parent = null)
|
||||
{
|
||||
$profile = new Profile($token);
|
||||
$profile->setIp($data['ip']);
|
||||
$profile->setMethod($data['method']);
|
||||
$profile->setUrl($data['url']);
|
||||
$profile->setTime($data['time']);
|
||||
$profile->setCollectors(unserialize(base64_decode($data['data'])));
|
||||
|
||||
if (!$parent && !empty($data['parent'])) {
|
||||
$parent = $this->read($data['parent']);
|
||||
}
|
||||
|
||||
if ($parent) {
|
||||
$profile->setParent($parent);
|
||||
}
|
||||
|
||||
$profile->setChildren($this->readChildren($token, $profile));
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the child profiles for the given token.
|
||||
*
|
||||
* @param string $token The parent token
|
||||
* @param string $parent The parent instance
|
||||
*
|
||||
* @return array An array of Profile instance
|
||||
*/
|
||||
protected function readChildren($token, $parent)
|
||||
{
|
||||
$db = $this->initDb();
|
||||
$data = $this->fetch($db, 'SELECT token, data, ip, method, url, time FROM sf_profiler_data WHERE parent = :token', array(':token' => $token));
|
||||
$this->close($db);
|
||||
|
||||
if (!$data) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$profiles = array();
|
||||
foreach ($data as $d) {
|
||||
$profiles[] = $this->createProfileFromData($d['token'], $d, $parent);
|
||||
}
|
||||
|
||||
return $profiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether data for the given token already exists in storage.
|
||||
*
|
||||
* @param string $token The profile token
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
protected function has($token)
|
||||
{
|
||||
$db = $this->initDb();
|
||||
$tokenExists = $this->fetch($db, 'SELECT 1 FROM sf_profiler_data WHERE token = :token LIMIT 1', array(':token' => $token));
|
||||
$this->close($db);
|
||||
|
||||
return !empty($tokenExists);
|
||||
}
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Profiler;
|
||||
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
|
||||
|
||||
/**
|
||||
* Profile.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Profile
|
||||
{
|
||||
private $token;
|
||||
private $collectors;
|
||||
private $ip;
|
||||
private $method;
|
||||
private $url;
|
||||
private $time;
|
||||
private $parent;
|
||||
private $children;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $token The token
|
||||
*/
|
||||
public function __construct($token)
|
||||
{
|
||||
$this->token = $token;
|
||||
$this->collectors = array();
|
||||
$this->children = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the token.
|
||||
*
|
||||
* @param string $token The token
|
||||
*/
|
||||
public function setToken($token)
|
||||
{
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token.
|
||||
*
|
||||
* @return string The token
|
||||
*/
|
||||
public function getToken()
|
||||
{
|
||||
return $this->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent token
|
||||
*
|
||||
* @param Profile $parent The parent Profile
|
||||
*/
|
||||
public function setParent(Profile $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent profile.
|
||||
*
|
||||
* @return Profile The parent profile
|
||||
*/
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent token.
|
||||
*
|
||||
* @return null|string The parent token
|
||||
*/
|
||||
public function getParentToken()
|
||||
{
|
||||
return $this->parent ? $this->parent->getToken() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IP.
|
||||
*
|
||||
* @return string The IP
|
||||
*/
|
||||
public function getIp()
|
||||
{
|
||||
return $this->ip;
|
||||
}
|
||||
|
||||
public function setIp($ip)
|
||||
{
|
||||
$this->ip = $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the request method.
|
||||
*
|
||||
* @return string The request method
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
public function setMethod($method)
|
||||
{
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL.
|
||||
*
|
||||
* @return string The URL
|
||||
*/
|
||||
public function getUrl()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function setUrl($url)
|
||||
{
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time.
|
||||
*
|
||||
* @return string The time
|
||||
*/
|
||||
public function getTime()
|
||||
{
|
||||
return $this->time;
|
||||
}
|
||||
|
||||
public function setTime($time)
|
||||
{
|
||||
$this->time = $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds children profilers.
|
||||
*
|
||||
* @return array An array of Profile
|
||||
*/
|
||||
public function getChildren()
|
||||
{
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
public function setChildren(array $children)
|
||||
{
|
||||
$this->children = array();
|
||||
foreach ($children as $child) {
|
||||
$this->addChild($child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the child token
|
||||
*
|
||||
* @param Profile $child The child Profile
|
||||
*/
|
||||
public function addChild(Profile $child)
|
||||
{
|
||||
$this->children[] = $child;
|
||||
$child->setParent($this);
|
||||
}
|
||||
|
||||
public function getCollector($name)
|
||||
{
|
||||
if (!isset($this->collectors[$name])) {
|
||||
throw new \InvalidArgumentException(sprintf('Collector "%s" does not exist.', $name));
|
||||
}
|
||||
|
||||
return $this->collectors[$name];
|
||||
}
|
||||
|
||||
public function getCollectors()
|
||||
{
|
||||
return $this->collectors;
|
||||
}
|
||||
|
||||
public function setCollectors(array $collectors)
|
||||
{
|
||||
$this->collectors = array();
|
||||
foreach ($collectors as $collector) {
|
||||
$this->addCollector($collector);
|
||||
}
|
||||
}
|
||||
|
||||
public function addCollector(DataCollectorInterface $collector)
|
||||
{
|
||||
$this->collectors[$collector->getName()] = $collector;
|
||||
}
|
||||
|
||||
public function hasCollector($name)
|
||||
{
|
||||
return isset($this->collectors[$name]);
|
||||
}
|
||||
|
||||
public function __sleep()
|
||||
{
|
||||
return array('token', 'parent', 'children', 'collectors', 'ip', 'method', 'url', 'time');
|
||||
}
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Profiler;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
|
||||
use Symfony\Component\HttpKernel\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Profiler.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Profiler
|
||||
{
|
||||
private $storage;
|
||||
private $collectors;
|
||||
private $logger;
|
||||
private $enabled;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ProfilerStorageInterface $storage A ProfilerStorageInterface instance
|
||||
* @param LoggerInterface $logger A LoggerInterface instance
|
||||
*/
|
||||
public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = null)
|
||||
{
|
||||
$this->storage = $storage;
|
||||
$this->logger = $logger;
|
||||
$this->collectors = array();
|
||||
$this->enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the profiler.
|
||||
*/
|
||||
public function disable()
|
||||
{
|
||||
$this->enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Profile for the given Response.
|
||||
*
|
||||
* @param Response $response A Response instance
|
||||
*
|
||||
* @return Profile A Profile instance
|
||||
*/
|
||||
public function loadProfileFromResponse(Response $response)
|
||||
{
|
||||
if (!$token = $response->headers->get('X-Debug-Token')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->loadProfile($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the Profile for the given token.
|
||||
*
|
||||
* @param string $token A token
|
||||
*
|
||||
* @return Profile A Profile instance
|
||||
*/
|
||||
public function loadProfile($token)
|
||||
{
|
||||
return $this->storage->read($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a Profile.
|
||||
*
|
||||
* @param Profile $profile A Profile instance
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function saveProfile(Profile $profile)
|
||||
{
|
||||
if (!($ret = $this->storage->write($profile)) && null !== $this->logger) {
|
||||
$this->logger->warn('Unable to store the profiler information.');
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges all data from the storage.
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
$this->storage->purge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the current profiler data.
|
||||
*
|
||||
* @param Profile $profile A Profile instance
|
||||
*
|
||||
* @return string The exported data
|
||||
*/
|
||||
public function export(Profile $profile)
|
||||
{
|
||||
return base64_encode(serialize($profile));
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports data into the profiler storage.
|
||||
*
|
||||
* @param string $data A data string as exported by the export() method
|
||||
*
|
||||
* @return Profile A Profile instance
|
||||
*/
|
||||
public function import($data)
|
||||
{
|
||||
$profile = unserialize(base64_decode($data));
|
||||
|
||||
if ($this->storage->read($profile->getToken())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->saveProfile($profile);
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds profiler tokens for the given criteria.
|
||||
*
|
||||
* @param string $ip The IP
|
||||
* @param string $url The URL
|
||||
* @param string $limit The maximum number of tokens to return
|
||||
* @param string $method The request method
|
||||
*
|
||||
* @return array An array of tokens
|
||||
*/
|
||||
public function find($ip, $url, $limit, $method)
|
||||
{
|
||||
return $this->storage->find($ip, $url, $limit, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects data for the given Response.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Response $response A Response instance
|
||||
* @param \Exception $exception An exception instance if the request threw one
|
||||
*
|
||||
* @return Profile|null A Profile instance or null if the profiler is disabled
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Exception $exception = null)
|
||||
{
|
||||
if (false === $this->enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$profile = new Profile(uniqid());
|
||||
$profile->setTime(time());
|
||||
$profile->setUrl($request->getUri());
|
||||
$profile->setIp($request->getClientIp());
|
||||
$profile->setMethod($request->getMethod());
|
||||
|
||||
$response->headers->set('X-Debug-Token', $profile->getToken());
|
||||
|
||||
foreach ($this->collectors as $collector) {
|
||||
$collector->collect($request, $response, $exception);
|
||||
|
||||
// forces collectors to become "read/only" (they loose their object dependencies)
|
||||
$profile->addCollector(unserialize(serialize($collector)));
|
||||
}
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Collectors associated with this profiler.
|
||||
*
|
||||
* @return array An array of collectors
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
return $this->collectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Collectors associated with this profiler.
|
||||
*
|
||||
* @param array $collectors An array of collectors
|
||||
*/
|
||||
public function set(array $collectors = array())
|
||||
{
|
||||
$this->collectors = array();
|
||||
foreach ($collectors as $collector) {
|
||||
$this->add($collector);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Collector.
|
||||
*
|
||||
* @param DataCollectorInterface $collector A DataCollectorInterface instance
|
||||
*/
|
||||
public function add(DataCollectorInterface $collector)
|
||||
{
|
||||
$this->collectors[$collector->getName()] = $collector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a Collector for the given name exists.
|
||||
*
|
||||
* @param string $name A collector name
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function has($name)
|
||||
{
|
||||
return isset($this->collectors[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Collector by name.
|
||||
*
|
||||
* @param string $name A collector name
|
||||
*
|
||||
* @return DataCollectorInterface A DataCollectorInterface instance
|
||||
*
|
||||
* @throws \InvalidArgumentException if the collector does not exist
|
||||
*/
|
||||
public function get($name)
|
||||
{
|
||||
if (!isset($this->collectors[$name])) {
|
||||
throw new \InvalidArgumentException(sprintf('Collector "%s" does not exist.', $name));
|
||||
}
|
||||
|
||||
return $this->collectors[$name];
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Profiler;
|
||||
|
||||
/**
|
||||
* ProfilerStorageInterface.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface ProfilerStorageInterface
|
||||
{
|
||||
/**
|
||||
* Finds profiler tokens for the given criteria.
|
||||
*
|
||||
* @param string $ip The IP
|
||||
* @param string $url The URL
|
||||
* @param string $limit The maximum number of tokens to return
|
||||
* @param string $method The request method
|
||||
*
|
||||
* @return array An array of tokens
|
||||
*/
|
||||
public function find($ip, $url, $limit, $method);
|
||||
|
||||
/**
|
||||
* Reads data associated with the given token.
|
||||
*
|
||||
* The method returns false if the token does not exists in the storage.
|
||||
*
|
||||
* @param string $token A token
|
||||
*
|
||||
* @return Profile The profile associated with token
|
||||
*/
|
||||
public function read($token);
|
||||
|
||||
/**
|
||||
* Saves a Profile.
|
||||
*
|
||||
* @param Profile $profile A Profile instance
|
||||
*
|
||||
* @return Boolean Write operation successful
|
||||
*/
|
||||
public function write(Profile $profile);
|
||||
|
||||
/**
|
||||
* Purges all data from the database.
|
||||
*/
|
||||
public function purge();
|
||||
}
|
||||
@@ -1,381 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Profiler;
|
||||
|
||||
use Redis;
|
||||
|
||||
/**
|
||||
* RedisProfilerStorage stores profiling information in Redis.
|
||||
*
|
||||
* @author Andrej Hudec <pulzarraider@gmail.com>
|
||||
*/
|
||||
class RedisProfilerStorage implements ProfilerStorageInterface
|
||||
{
|
||||
const TOKEN_PREFIX = 'sf_profiler_';
|
||||
|
||||
const REDIS_OPT_SERIALIZER = 1;
|
||||
const REDIS_OPT_PREFIX = 2;
|
||||
const REDIS_SERIALIZER_NONE = 0;
|
||||
const REDIS_SERIALIZER_PHP = 1;
|
||||
|
||||
protected $dsn;
|
||||
protected $lifetime;
|
||||
|
||||
/**
|
||||
* @var Redis
|
||||
*/
|
||||
private $redis;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $dsn A data source name
|
||||
* @param string $username Not used
|
||||
* @param string $password Not used
|
||||
* @param int $lifetime The lifetime to use for the purge
|
||||
*/
|
||||
public function __construct($dsn, $username = '', $password = '', $lifetime = 86400)
|
||||
{
|
||||
$this->dsn = $dsn;
|
||||
$this->lifetime = (int) $lifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function find($ip, $url, $limit, $method)
|
||||
{
|
||||
$indexName = $this->getIndexName();
|
||||
|
||||
if (!$indexContent = $this->getValue($indexName, self::REDIS_SERIALIZER_NONE)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$profileList = explode("\n", $indexContent);
|
||||
$result = array();
|
||||
|
||||
foreach ($profileList as $item) {
|
||||
if ($limit === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($item == '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
list($itemToken, $itemIp, $itemMethod, $itemUrl, $itemTime, $itemParent) = explode("\t", $item, 6);
|
||||
|
||||
if ($ip && false === strpos($itemIp, $ip) || $url && false === strpos($itemUrl, $url) || $method && false === strpos($itemMethod, $method)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$itemToken] = array(
|
||||
'token' => $itemToken,
|
||||
'ip' => $itemIp,
|
||||
'method' => $itemMethod,
|
||||
'url' => $itemUrl,
|
||||
'time' => $itemTime,
|
||||
'parent' => $itemParent,
|
||||
);
|
||||
--$limit;
|
||||
}
|
||||
|
||||
usort($result, function($a, $b) {
|
||||
if ($a['time'] === $b['time']) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $a['time'] > $b['time'] ? -1 : 1;
|
||||
});
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
// delete only items from index
|
||||
$indexName = $this->getIndexName();
|
||||
|
||||
$indexContent = $this->getValue($indexName, self::REDIS_SERIALIZER_NONE);
|
||||
|
||||
if (!$indexContent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$profileList = explode("\n", $indexContent);
|
||||
|
||||
$result = array();
|
||||
|
||||
foreach ($profileList as $item) {
|
||||
if ($item == '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (false !== $pos = strpos($item, "\t")) {
|
||||
$result[] = $this->getItemName(substr($item, 0, $pos));
|
||||
}
|
||||
}
|
||||
|
||||
$result[] = $indexName;
|
||||
|
||||
return $this->delete($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($token)
|
||||
{
|
||||
if (empty($token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$profile = $this->getValue($this->getItemName($token), self::REDIS_SERIALIZER_PHP);
|
||||
|
||||
if (false !== $profile) {
|
||||
$profile = $this->createProfileFromData($token, $profile);
|
||||
}
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write(Profile $profile)
|
||||
{
|
||||
$data = array(
|
||||
'token' => $profile->getToken(),
|
||||
'parent' => $profile->getParentToken(),
|
||||
'children' => array_map(function ($p) { return $p->getToken(); }, $profile->getChildren()),
|
||||
'data' => $profile->getCollectors(),
|
||||
'ip' => $profile->getIp(),
|
||||
'method' => $profile->getMethod(),
|
||||
'url' => $profile->getUrl(),
|
||||
'time' => $profile->getTime(),
|
||||
);
|
||||
|
||||
$profileIndexed = false !== $this->getValue($this->getItemName($profile->getToken()));
|
||||
|
||||
if ($this->setValue($this->getItemName($profile->getToken()), $data, $this->lifetime, self::REDIS_SERIALIZER_PHP)) {
|
||||
|
||||
if (!$profileIndexed) {
|
||||
// Add to index
|
||||
$indexName = $this->getIndexName();
|
||||
|
||||
$indexRow = implode("\t", array(
|
||||
$profile->getToken(),
|
||||
$profile->getIp(),
|
||||
$profile->getMethod(),
|
||||
$profile->getUrl(),
|
||||
$profile->getTime(),
|
||||
$profile->getParentToken(),
|
||||
))."\n";
|
||||
|
||||
return $this->appendValue($indexName, $indexRow, $this->lifetime);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal convenience method that returns the instance of Redis.
|
||||
*
|
||||
* @return Redis
|
||||
*/
|
||||
protected function getRedis()
|
||||
{
|
||||
if (null === $this->redis) {
|
||||
if (!preg_match('#^redis://(?(?=\[.*\])\[(.*)\]|(.*)):(.*)$#', $this->dsn, $matches)) {
|
||||
throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Redis with an invalid dsn "%s". The expected format is "redis://[host]:port".', $this->dsn));
|
||||
}
|
||||
|
||||
$host = $matches[1] ?: $matches[2];
|
||||
$port = $matches[3];
|
||||
|
||||
if (!extension_loaded('redis')) {
|
||||
throw new \RuntimeException('RedisProfilerStorage requires that the redis extension is loaded.');
|
||||
}
|
||||
|
||||
$redis = new Redis;
|
||||
$redis->connect($host, $port);
|
||||
|
||||
$redis->setOption(self::REDIS_OPT_PREFIX, self::TOKEN_PREFIX);
|
||||
|
||||
$this->redis = $redis;
|
||||
}
|
||||
|
||||
return $this->redis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set instance of the Redis
|
||||
*
|
||||
* @param Redis $redis
|
||||
*/
|
||||
public function setRedis($redis)
|
||||
{
|
||||
$this->redis = $redis;
|
||||
}
|
||||
|
||||
private function createProfileFromData($token, $data, $parent = null)
|
||||
{
|
||||
$profile = new Profile($token);
|
||||
$profile->setIp($data['ip']);
|
||||
$profile->setMethod($data['method']);
|
||||
$profile->setUrl($data['url']);
|
||||
$profile->setTime($data['time']);
|
||||
$profile->setCollectors($data['data']);
|
||||
|
||||
if (!$parent && $data['parent']) {
|
||||
$parent = $this->read($data['parent']);
|
||||
}
|
||||
|
||||
if ($parent) {
|
||||
$profile->setParent($parent);
|
||||
}
|
||||
|
||||
foreach ($data['children'] as $token) {
|
||||
if (!$token) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$childProfileData = $this->getValue($this->getItemName($token), self::REDIS_SERIALIZER_PHP)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$profile->addChild($this->createProfileFromData($token, $childProfileData, $profile));
|
||||
}
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item name.
|
||||
*
|
||||
* @param string $token
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getItemName($token)
|
||||
{
|
||||
$name = $token;
|
||||
|
||||
if ($this->isItemNameValid($name)) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the index.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getIndexName()
|
||||
{
|
||||
$name = 'index';
|
||||
|
||||
if ($this->isItemNameValid($name)) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isItemNameValid($name)
|
||||
{
|
||||
$length = strlen($name);
|
||||
|
||||
if ($length > 2147483648) {
|
||||
throw new \RuntimeException(sprintf('The Redis item key "%s" is too long (%s bytes). Allowed maximum size is 2^31 bytes.', $name, $length));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an item from the Redis server.
|
||||
*
|
||||
* @param string $key
|
||||
* @param int $serializer
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function getValue($key, $serializer = self::REDIS_SERIALIZER_NONE)
|
||||
{
|
||||
$redis = $this->getRedis();
|
||||
$redis->setOption(self::REDIS_OPT_SERIALIZER, $serializer);
|
||||
|
||||
return $redis->get($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores an item on the Redis server under the specified key.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param int $expiration
|
||||
* @param int $serializer
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
private function setValue($key, $value, $expiration = 0, $serializer = self::REDIS_SERIALIZER_NONE)
|
||||
{
|
||||
$redis = $this->getRedis();
|
||||
$redis->setOption(self::REDIS_OPT_SERIALIZER, $serializer);
|
||||
|
||||
return $redis->setex($key, $expiration, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends data to an existing item on the Redis server.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @param int $expiration
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
private function appendValue($key, $value, $expiration = 0)
|
||||
{
|
||||
$redis = $this->getRedis();
|
||||
$redis->setOption(self::REDIS_OPT_SERIALIZER, self::REDIS_SERIALIZER_NONE);
|
||||
|
||||
if ($redis->exists($key)) {
|
||||
$redis->append($key, $value);
|
||||
|
||||
return $redis->setTimeout($key, $expiration);
|
||||
}
|
||||
|
||||
return $redis->setex($key, $expiration, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified keys.
|
||||
*
|
||||
* @param array $keys
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
private function delete(array $keys)
|
||||
{
|
||||
return (bool) $this->getRedis()->delete($keys);
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Profiler;
|
||||
|
||||
/**
|
||||
* SqliteProfilerStorage stores profiling information in a SQLite database.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class SqliteProfilerStorage extends PdoProfilerStorage
|
||||
{
|
||||
/**
|
||||
* @throws \RuntimeException When neither of SQLite3 or PDO_SQLite extension is enabled
|
||||
*/
|
||||
protected function initDb()
|
||||
{
|
||||
if (null === $this->db || $this->db instanceof \SQLite3) {
|
||||
if (0 !== strpos($this->dsn, 'sqlite')) {
|
||||
throw new \RuntimeException(sprintf('Please check your configuration. You are trying to use Sqlite with an invalid dsn "%s". The expected format is "sqlite:/path/to/the/db/file".', $this->dsn));
|
||||
}
|
||||
if (class_exists('SQLite3')) {
|
||||
$db = new \SQLite3(substr($this->dsn, 7, strlen($this->dsn)), \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE);
|
||||
if (method_exists($db, 'busyTimeout')) {
|
||||
// busyTimeout only exists for PHP >= 5.3.3
|
||||
$db->busyTimeout(1000);
|
||||
}
|
||||
} elseif (class_exists('PDO') && in_array('sqlite', \PDO::getAvailableDrivers(), true)) {
|
||||
$db = new \PDO($this->dsn);
|
||||
} else {
|
||||
throw new \RuntimeException('You need to enable either the SQLite3 or PDO_SQLite extension for the profiler to run properly.');
|
||||
}
|
||||
|
||||
$db->exec('PRAGMA temp_store=MEMORY; PRAGMA journal_mode=MEMORY;');
|
||||
$db->exec('CREATE TABLE IF NOT EXISTS sf_profiler_data (token STRING, data STRING, ip STRING, method STRING, url STRING, time INTEGER, parent STRING, created_at INTEGER)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_created_at ON sf_profiler_data (created_at)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_ip ON sf_profiler_data (ip)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_method ON sf_profiler_data (method)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_url ON sf_profiler_data (url)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_parent ON sf_profiler_data (parent)');
|
||||
$db->exec('CREATE UNIQUE INDEX IF NOT EXISTS data_token ON sf_profiler_data (token)');
|
||||
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
protected function exec($db, $query, array $args = array())
|
||||
{
|
||||
if ($db instanceof \SQLite3) {
|
||||
$stmt = $this->prepareStatement($db, $query);
|
||||
foreach ($args as $arg => $val) {
|
||||
$stmt->bindValue($arg, $val, is_int($val) ? \SQLITE3_INTEGER : \SQLITE3_TEXT);
|
||||
}
|
||||
|
||||
$res = $stmt->execute();
|
||||
if (false === $res) {
|
||||
throw new \RuntimeException(sprintf('Error executing SQLite query "%s"', $query));
|
||||
}
|
||||
$res->finalize();
|
||||
} else {
|
||||
parent::exec($db, $query, $args);
|
||||
}
|
||||
}
|
||||
|
||||
protected function fetch($db, $query, array $args = array())
|
||||
{
|
||||
$return = array();
|
||||
|
||||
if ($db instanceof \SQLite3) {
|
||||
$stmt = $this->prepareStatement($db, $query, true);
|
||||
foreach ($args as $arg => $val) {
|
||||
$stmt->bindValue($arg, $val, is_int($val) ? \SQLITE3_INTEGER : \SQLITE3_TEXT);
|
||||
}
|
||||
$res = $stmt->execute();
|
||||
while ($row = $res->fetchArray(\SQLITE3_ASSOC)) {
|
||||
$return[] = $row;
|
||||
}
|
||||
$res->finalize();
|
||||
$stmt->close();
|
||||
} else {
|
||||
$return = parent::fetch($db, $query, $args);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildCriteria($ip, $url, $limit, $method)
|
||||
{
|
||||
$criteria = array();
|
||||
$args = array();
|
||||
|
||||
if ($ip = preg_replace('/[^\d\.]/', '', $ip)) {
|
||||
$criteria[] = 'ip LIKE :ip';
|
||||
$args[':ip'] = '%'.$ip.'%';
|
||||
}
|
||||
|
||||
if ($url) {
|
||||
$criteria[] = 'url LIKE :url ESCAPE "\"';
|
||||
$args[':url'] = '%'.addcslashes($url, '%_\\').'%';
|
||||
}
|
||||
|
||||
if ($method) {
|
||||
$criteria[] = 'method = :method';
|
||||
$args[':method'] = $method;
|
||||
}
|
||||
|
||||
return array($criteria, $args);
|
||||
}
|
||||
|
||||
protected function close($db)
|
||||
{
|
||||
if ($db instanceof \SQLite3) {
|
||||
$db->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
HttpKernel Component
|
||||
====================
|
||||
|
||||
HttpKernel provides the building blocks to create flexible and fast HTTP-based
|
||||
frameworks.
|
||||
|
||||
``HttpKernelInterface`` is the core interface of the Symfony2 full-stack
|
||||
framework:
|
||||
|
||||
interface HttpKernelInterface
|
||||
{
|
||||
/**
|
||||
* Handles a Request to convert it to a Response.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
*
|
||||
* @return Response A Response instance
|
||||
*/
|
||||
function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true);
|
||||
}
|
||||
|
||||
It takes a ``Request`` as an input and should return a ``Response`` as an
|
||||
output. Using this interface makes your code compatible with all frameworks
|
||||
using the Symfony2 components. And this will give you many cool features for
|
||||
free.
|
||||
|
||||
Creating a framework based on the Symfony2 components is really easy. Here is
|
||||
a very simple, but fully-featured framework based on the Symfony2 components:
|
||||
|
||||
$routes = new RouteCollection();
|
||||
$routes->add('hello', new Route('/hello', array('_controller' =>
|
||||
function (Request $request) {
|
||||
return new Response(sprintf("Hello %s", $request->get('name')));
|
||||
}
|
||||
)));
|
||||
|
||||
$request = Request::createFromGlobals();
|
||||
|
||||
$context = new RequestContext();
|
||||
$context->fromRequest($request);
|
||||
|
||||
$matcher = new UrlMatcher($routes, $context);
|
||||
|
||||
$dispatcher = new EventDispatcher();
|
||||
$dispatcher->addSubscriber(new RouterListener($matcher));
|
||||
|
||||
$resolver = new ControllerResolver();
|
||||
|
||||
$kernel = new HttpKernel($dispatcher, $resolver);
|
||||
|
||||
$kernel->handle($request)->send();
|
||||
|
||||
This is all you need to create a flexible framework with the Symfony2
|
||||
components.
|
||||
|
||||
Want to add an HTTP reverse proxy and benefit from HTTP caching and Edge Side
|
||||
Includes?
|
||||
|
||||
$kernel = new HttpKernel($dispatcher, $resolver);
|
||||
|
||||
$kernel = new HttpCache($kernel, new Store(__DIR__.'/cache'));
|
||||
|
||||
Want to functional test this small framework?
|
||||
|
||||
$client = new Client($kernel);
|
||||
$crawler = $client->request('GET', '/hello/Fabien');
|
||||
|
||||
$this->assertEquals('Fabien', $crawler->filter('p > span')->text());
|
||||
|
||||
Want nice error pages instead of ugly PHP exceptions?
|
||||
|
||||
$dispatcher->addSubscriber(new ExceptionListener(function (Request $request) {
|
||||
$msg = 'Something went wrong! ('.$request->get('exception')->getMessage().')';
|
||||
|
||||
return new Response($msg, 500);
|
||||
}));
|
||||
|
||||
And that's why the simple looking ``HttpKernelInterface`` is so powerful. It
|
||||
gives you access to a lot of cool features, ready to be used out of the box,
|
||||
with no efforts.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
You can run the unit tests with the following command:
|
||||
|
||||
phpunit
|
||||
|
||||
If you also want to run the unit tests that depend on other Symfony
|
||||
Components, install dev dependencies before running PHPUnit:
|
||||
|
||||
php composer.phar install --dev
|
||||
@@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Terminable extends the Kernel request/response cycle with dispatching a post
|
||||
* response event after sending the response and before shutting down the kernel.
|
||||
*
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Pierre Minnieur <pierre.minnieur@sensiolabs.de>
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
interface TerminableInterface
|
||||
{
|
||||
/**
|
||||
* Terminates a request/response cycle.
|
||||
*
|
||||
* Should be called after sending the response and before shutting down the kernel.
|
||||
*
|
||||
* @param Request $request A Request instance
|
||||
* @param Response $response A Response instance
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
public function terminate(Request $request, Response $response);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"name": "symfony/http-kernel",
|
||||
"type": "library",
|
||||
"description": "Symfony HttpKernel Component",
|
||||
"keywords": [],
|
||||
"homepage": "http://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "http://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"symfony/event-dispatcher": "2.1.*",
|
||||
"symfony/http-foundation": "2.1.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/browser-kit": "2.1.*",
|
||||
"symfony/class-loader": "2.1.*",
|
||||
"symfony/config": "2.1.*",
|
||||
"symfony/console": "2.1.*",
|
||||
"symfony/dependency-injection": "2.1.*",
|
||||
"symfony/finder": "2.1.*",
|
||||
"symfony/process": "2.1.*",
|
||||
"symfony/routing": "2.1.*"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/browser-kit": "2.1.*",
|
||||
"symfony/class-loader": "2.1.*",
|
||||
"symfony/config": "2.1.*",
|
||||
"symfony/console": "2.1.*",
|
||||
"symfony/dependency-injection": "2.1.*",
|
||||
"symfony/finder": "2.1.*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": { "Symfony\\Component\\HttpKernel": "" }
|
||||
},
|
||||
"target-dir": "Symfony/Component/HttpKernel",
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.1-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user