remove dependencies

This commit is contained in:
Manuel Raynaud
2012-12-18 10:57:59 +01:00
parent 647424d649
commit 0fc2551b21
1729 changed files with 1 additions and 317650 deletions

View File

@@ -1,2 +0,0 @@
/Tests export-ignore
phpunit.xml.dist export-ignore

View File

@@ -1,2 +0,0 @@
vendor/
composer.lock

View File

@@ -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());
}
}
}
}

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View 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;
}
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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';
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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';
}
}

View File

@@ -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';
}
}

View File

@@ -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;
}
}

View File

@@ -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';
}
}

View File

@@ -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;
}
}

View File

@@ -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';
}
}

View File

@@ -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';
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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)));
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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',
);
}
}

View File

@@ -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),
);
}
}

View File

@@ -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)),
);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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',
);
}
}

View File

@@ -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)),
);
}
}

View File

@@ -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),
);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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'
);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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';
}

View File

@@ -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();
}

View File

@@ -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.

View File

@@ -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();
}

View File

@@ -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());
}

View File

@@ -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())
{
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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');
}
}

View File

@@ -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];
}
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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"
}
}
}