2015-08-17 17:00:26 -07:00
< ? 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 ;
2017-02-02 16:28:38 -08:00
use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException ;
2015-08-17 17:00:26 -07:00
use Symfony\Component\HttpFoundation\Request ;
use Symfony\Component\HttpFoundation\Response ;
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface ;
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface ;
use Psr\Log\LoggerInterface ;
/**
* Profiler .
*
* @ author Fabien Potencier < fabien @ symfony . com >
*/
class Profiler
{
/**
* @ var ProfilerStorageInterface
*/
private $storage ;
/**
* @ var DataCollectorInterface []
*/
private $collectors = array ();
/**
* @ var LoggerInterface
*/
private $logger ;
/**
* @ var bool
*/
private $enabled = true ;
/**
* 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 ;
}
/**
* Disables the profiler .
*/
public function disable ()
{
$this -> enabled = false ;
}
/**
* Enables the profiler .
*/
public function enable ()
{
$this -> enabled = true ;
}
/**
* Loads the Profile for the given Response .
*
* @ param Response $response A Response instance
*
2017-02-02 16:28:38 -08:00
* @ return Profile | false A Profile instance
2015-08-17 17:00:26 -07:00
*/
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 bool
*/
public function saveProfile ( Profile $profile )
{
// late collect
foreach ( $profile -> getCollectors () as $collector ) {
if ( $collector instanceof LateDataCollectorInterface ) {
$collector -> lateCollect ();
}
}
if ( ! ( $ret = $this -> storage -> write ( $profile )) && null !== $this -> logger ) {
$this -> logger -> warning ( 'Unable to store the profiler information.' , array ( 'configured_storage' => get_class ( $this -> storage )));
}
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
2016-04-20 09:56:34 -07:00
*
* @ deprecated since Symfony 2.8 , to be removed in 3.0 .
2015-08-17 17:00:26 -07:00
*/
public function export ( Profile $profile )
{
2016-04-20 09:56:34 -07:00
@ trigger_error ( 'The ' . __METHOD__ . ' method is deprecated since version 2.8 and will be removed in 3.0.' , E_USER_DEPRECATED );
2015-08-17 17:00:26 -07:00
return base64_encode ( serialize ( $profile ));
}
/**
* Imports data into the profiler storage .
*
* @ param string $data A data string as exported by the export () method
*
2017-02-02 16:28:38 -08:00
* @ return Profile | false A Profile instance
2016-04-20 09:56:34 -07:00
*
* @ deprecated since Symfony 2.8 , to be removed in 3.0 .
2015-08-17 17:00:26 -07:00
*/
public function import ( $data )
{
2016-04-20 09:56:34 -07:00
@ trigger_error ( 'The ' . __METHOD__ . ' method is deprecated since version 2.8 and will be removed in 3.0.' , E_USER_DEPRECATED );
2015-08-17 17:00:26 -07:00
$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
* @ param string $start The start date to search from
* @ param string $end The end date to search to
*
* @ return array An array of tokens
*
* @ see http :// php . net / manual / en / datetime . formats . php for the supported date / time formats
*/
public function find ( $ip , $url , $limit , $method , $start , $end )
{
return $this -> storage -> find ( $ip , $url , $limit , $method , $this -> getTimestamp ( $start ), $this -> getTimestamp ( $end ));
}
/**
* 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 ( substr ( hash ( 'sha256' , uniqid ( mt_rand (), true )), 0 , 6 ));
$profile -> setTime ( time ());
$profile -> setUrl ( $request -> getUri ());
$profile -> setMethod ( $request -> getMethod ());
$profile -> setStatusCode ( $response -> getStatusCode ());
2017-02-02 16:28:38 -08:00
try {
$profile -> setIp ( $request -> getClientIp ());
} catch ( ConflictingHeadersException $e ) {
$profile -> setIp ( 'Unknown' );
}
2015-08-17 17:00:26 -07:00
$response -> headers -> set ( 'X-Debug-Token' , $profile -> getToken ());
foreach ( $this -> collectors as $collector ) {
$collector -> collect ( $request , $response , $exception );
// we need to clone for sub-requests
$profile -> addCollector ( clone $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 DataCollectorInterface [] $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 bool
*/
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 ];
}
private function getTimestamp ( $value )
{
if ( null === $value || '' == $value ) {
return ;
}
try {
$value = new \DateTime ( is_numeric ( $value ) ? '@' . $value : $value );
} catch ( \Exception $e ) {
return ;
}
return $value -> getTimestamp ();
}
}