- Added abstract PDO profiler storage, updated sqlite storage and added a mysql storage.
- Updated profiler config in framework bundle
This commit is contained in:
parent
e5fad94482
commit
d1ebc8da9f
@ -7,7 +7,9 @@
|
||||
<parameters>
|
||||
<parameter key="profiler.class">Symfony\Component\HttpKernel\Profiler\Profiler</parameter>
|
||||
<parameter key="profiler.storage.class">Symfony\Component\HttpKernel\Profiler\SqliteProfilerStorage</parameter>
|
||||
<parameter key="profiler.storage.file">%kernel.cache_dir%/profiler.db</parameter>
|
||||
<parameter key="profiler.storage.dsn">sqlite:%kernel.cache_dir%/profiler.db</parameter>
|
||||
<parameter key="profiler.storage.username"></parameter>
|
||||
<parameter key="profiler.storage.password"></parameter>
|
||||
<parameter key="profiler.storage.lifetime">86400</parameter>
|
||||
<parameter key="profiler_listener.class">Symfony\Bundle\FrameworkBundle\Profiler\ProfilerListener</parameter>
|
||||
<parameter key="profiler_listener.only_exceptions">false</parameter>
|
||||
@ -20,7 +22,9 @@
|
||||
</service>
|
||||
|
||||
<service id="profiler.storage" class="%profiler.storage.class%" public="false">
|
||||
<argument>%profiler.storage.file%</argument>
|
||||
<argument>%profiler.storage.dsn%</argument>
|
||||
<argument>%profiler.storage.username%</argument>
|
||||
<argument>%profiler.storage.password%</argument>
|
||||
<argument>%profiler.storage.lifetime%</argument>
|
||||
</service>
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.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 (is_null($this->db))
|
||||
{
|
||||
if ('mysql' !== substr($this->dsn, 0, 5))
|
||||
{
|
||||
throw new \RuntimeException('Please check your configuration. You are trying to use Mysql with a wrong dsn. "' . $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 data (token VARCHAR(255) PRIMARY KEY, data LONGTEXT, ip VARCHAR(64), url VARCHAR(255), time INTEGER UNSIGNED, parent VARCHAR(255), created_at INTEGER UNSIGNED, KEY (created_at), KEY (ip), KEY (url), KEY (parent))');
|
||||
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
return $this->db;
|
||||
}
|
||||
}
|
194
src/Symfony/Component/HttpKernel/Profiler/PdoProfilerStorage.php
Normal file
194
src/Symfony/Component/HttpKernel/Profiler/PdoProfilerStorage.php
Normal file
@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.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;
|
||||
|
||||
/**
|
||||
* Base PDO storage for profiling information in a PDO database.
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
list($criteria, $args) = $this->buildCriteria($ip, $url, $limit);
|
||||
|
||||
$criteria = $criteria ? 'WHERE '.implode(' AND ', $criteria) : '';
|
||||
|
||||
$db = $this->initDb();
|
||||
$tokens = $this->fetch($db, 'SELECT token, ip, url, time FROM data '.$criteria.' ORDER BY time DESC LIMIT '.((integer) $limit), $args);
|
||||
$this->close($db);
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
protected function buildCriteria($ip, $url, $limit)
|
||||
{
|
||||
$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, '%_\\').'%';
|
||||
}
|
||||
|
||||
return array($criteria, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($token)
|
||||
{
|
||||
$db = $this->initDb();
|
||||
$args = array(':token' => $token);
|
||||
$data = $this->fetch($db, 'SELECT data, ip, url, time FROM data WHERE token = :token LIMIT 1', $args);
|
||||
$this->close($db);
|
||||
if (isset($data[0]['data'])) {
|
||||
return array($data[0]['data'], $data[0]['ip'], $data[0]['url'], $data[0]['time']);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($token, $parent, $data, $ip, $url, $time)
|
||||
{
|
||||
$db = $this->initDb();
|
||||
$args = array(
|
||||
':token' => $token,
|
||||
':parent' => $parent,
|
||||
':data' => $data,
|
||||
':ip' => $ip,
|
||||
':url' => $url,
|
||||
':time' => $time,
|
||||
':created_at' => time(),
|
||||
);
|
||||
try {
|
||||
$this->exec($db, 'INSERT INTO data (token, parent, data, ip, url, time, created_at) VALUES (:token, :parent, :data, :ip, :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 data');
|
||||
$this->close($db);
|
||||
}
|
||||
|
||||
protected function cleanup()
|
||||
{
|
||||
$db = $this->initDb();
|
||||
$this->exec($db, 'DELETE FROM data WHERE created_at < :time', array(':time' => time() - $this->lifetime));
|
||||
$this->close($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \RuntimeException When the requeted database driver is not installed
|
||||
*/
|
||||
abstract protected function initDb();
|
||||
|
||||
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())
|
||||
{
|
||||
$return = 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)
|
||||
{
|
||||
if ($db instanceof \SQLite3) {
|
||||
$db->close();
|
||||
}
|
||||
}
|
||||
}
|
@ -18,27 +18,9 @@ use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class SqliteProfilerStorage implements ProfilerStorageInterface
|
||||
class SqliteProfilerStorage extends PdoProfilerStorage
|
||||
{
|
||||
protected $store;
|
||||
protected $lifetime;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $store The path to the SQLite DB
|
||||
* @param integer $lifetime The lifetime to use for the purge
|
||||
*/
|
||||
public function __construct($store, $lifetime = 86400)
|
||||
{
|
||||
$this->store = $store;
|
||||
$this->lifetime = (int) $lifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function find($ip, $url, $limit)
|
||||
protected function buildCriteria($ip, $url, $limit)
|
||||
{
|
||||
$criteria = array();
|
||||
$args = array();
|
||||
@ -53,73 +35,7 @@ class SqliteProfilerStorage implements ProfilerStorageInterface
|
||||
$args[':url'] = '%'.addcslashes($url, '%_\\').'%';
|
||||
}
|
||||
|
||||
$criteria = $criteria ? 'WHERE '.implode(' AND ', $criteria) : '';
|
||||
|
||||
$db = $this->initDb();
|
||||
$tokens = $this->fetch($db, 'SELECT token, ip, url, time FROM 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, ip, url, time FROM data WHERE token = :token LIMIT 1', $args);
|
||||
$this->close($db);
|
||||
if (isset($data[0]['data'])) {
|
||||
return array($data[0]['data'], $data[0]['ip'], $data[0]['url'], $data[0]['time']);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($token, $parent, $data, $ip, $url, $time)
|
||||
{
|
||||
$db = $this->initDb();
|
||||
$args = array(
|
||||
':token' => $token,
|
||||
':parent' => $parent,
|
||||
':data' => $data,
|
||||
':ip' => $ip,
|
||||
':url' => $url,
|
||||
':time' => $time,
|
||||
':created_at' => time(),
|
||||
);
|
||||
try {
|
||||
$this->exec($db, 'INSERT INTO data (token, parent, data, ip, url, time, created_at) VALUES (:token, :parent, :data, :ip, :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 data');
|
||||
$this->close($db);
|
||||
}
|
||||
|
||||
protected function cleanup()
|
||||
{
|
||||
$db = $this->initDb();
|
||||
$this->exec($db, 'DELETE FROM data WHERE created_at < :time', array(':time' => time() - $this->lifetime));
|
||||
$this->close($db);
|
||||
return array($criteria, $args);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,33 +43,35 @@ class SqliteProfilerStorage implements ProfilerStorageInterface
|
||||
*/
|
||||
protected function initDb()
|
||||
{
|
||||
if (class_exists('SQLite3')) {
|
||||
$db = new \SQLite3($this->store, \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE);
|
||||
} elseif (class_exists('PDO') && in_array('sqlite', \PDO::getAvailableDrivers(), true)) {
|
||||
$db = new \PDO('sqlite:'.$this->store);
|
||||
} else {
|
||||
throw new \RuntimeException('You need to enable either the SQLite or PDO_SQLite extension for the profiler to run properly.');
|
||||
if (is_null($this->db) || $this->db instanceof \SQLite3) {
|
||||
if ('sqlite' !== substr($this->dsn, 0, 6 )) {
|
||||
throw new \RuntimeException('You are trying to use Sqlite with a wrong dsn. "' . $this->dsn . '"');
|
||||
}
|
||||
if (class_exists('SQLite3')) {
|
||||
$db = new \SQLite3(substr($this->dsn, 7, strlen($this->dsn)), \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE);
|
||||
} 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 SQLite or PDO_SQLite extension for the profiler to run properly.');
|
||||
}
|
||||
|
||||
$db->exec('CREATE TABLE IF NOT EXISTS data (token STRING, data STRING, ip STRING, url STRING, time INTEGER, parent STRING, created_at INTEGER)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_created_at ON data (created_at)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_ip ON data (ip)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_url ON data (url)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_parent ON data (parent)');
|
||||
$db->exec('CREATE UNIQUE INDEX IF NOT EXISTS data_token ON data (token)');
|
||||
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
$db->exec('CREATE TABLE IF NOT EXISTS data (token STRING, data STRING, ip STRING, url STRING, time INTEGER, parent STRING, created_at INTEGER)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_created_at ON data (created_at)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_ip ON data (ip)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_url ON data (url)');
|
||||
$db->exec('CREATE INDEX IF NOT EXISTS data_parent ON data (parent)');
|
||||
$db->exec('CREATE UNIQUE INDEX IF NOT EXISTS data_token ON data (token)');
|
||||
|
||||
return $db;
|
||||
return $this->db;
|
||||
}
|
||||
|
||||
protected function exec($db, $query, array $args = array())
|
||||
{
|
||||
$stmt = $db->prepare($query);
|
||||
|
||||
if (false === $stmt) {
|
||||
throw new \RuntimeException('The database cannot successfully prepare the statement');
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -164,22 +82,16 @@ class SqliteProfilerStorage implements ProfilerStorageInterface
|
||||
}
|
||||
$res->finalize();
|
||||
} else {
|
||||
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 SQLite query "%s"', $query));
|
||||
}
|
||||
parent::exec($db, $query, $args);
|
||||
}
|
||||
}
|
||||
|
||||
protected function fetch($db, $query, array $args = array())
|
||||
{
|
||||
$return = array();
|
||||
$stmt = $db->prepare($query);
|
||||
|
||||
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);
|
||||
}
|
||||
@ -190,11 +102,7 @@ class SqliteProfilerStorage implements ProfilerStorageInterface
|
||||
$res->finalize();
|
||||
$stmt->close();
|
||||
} else {
|
||||
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 = parent::fetch($db, $query, $args);
|
||||
}
|
||||
|
||||
return $return;
|
||||
|
@ -30,7 +30,7 @@ class ProfilerTest extends \PHPUnit_Framework_TestCase
|
||||
if (file_exists($tmp)) {
|
||||
@unlink($tmp);
|
||||
}
|
||||
$storage = new SqliteProfilerStorage($tmp);
|
||||
$storage = new SqliteProfilerStorage('sqlite:' . $tmp);
|
||||
$storage->purge();
|
||||
|
||||
$profiler = new Profiler($storage);
|
||||
|
@ -24,7 +24,7 @@ class SqliteProfilerStorageTest extends \PHPUnit_Framework_TestCase
|
||||
if (file_exists(self::$dbFile)) {
|
||||
@unlink(self::$dbFile);
|
||||
}
|
||||
self::$storage = new SqliteProfilerStorage(self::$dbFile);
|
||||
self::$storage = new SqliteProfilerStorage('sqlite:' . self::$dbFile);
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
@ -52,7 +52,7 @@ class SqliteProfilerStorageTest extends \PHPUnit_Framework_TestCase
|
||||
self::$storage->write('simple_quote', '', 'data', '127.0.0.1', 'http://foo.bar/\'', time());
|
||||
self::$storage->write('double_quote', '', 'data', '127.0.0.1', 'http://foo.bar/"', time());
|
||||
self::$storage->write('backslash', '', 'data', '127.0.0.1', 'http://foo.bar/\\', time());
|
||||
|
||||
|
||||
$this->assertTrue(false !== self::$storage->read('simple_quote'), '->write() accepts single quotes in URL');
|
||||
$this->assertTrue(false !== self::$storage->read('double_quote'), '->write() accepts double quotes in URL');
|
||||
$this->assertTrue(false !== self::$storage->read('backslash'), '->write() accpets backslash in URL');
|
||||
|
Reference in New Issue
Block a user