merged branch pulzarraider/memcache_profiler_storage (PR #2766)
Commits
-------
7474293
memcache profiler storage support added
Discussion
----------
[HttpKernel] [FrameworkBundle] Memcache(d) Profiler Storage added
Bug fix: no
Feature addition: yes
Backwards compatibility break: no
Symfony2 tests pass: yes
Fixes the following tickets: -
Todo: -
There are 2 memcache PHP extensions: Memcache and MemcacheD (with "D" at the end) - both are supported.
How to use Memcache Profiler Storage (Memcache php extension is used):
change (or add if there isn't) "dsn" in framework/profiler section in config_dev.yml
```
...
framework:
...
profiler:
...
dsn: memcache://127.0.0.1/11211
...
```
How to use Memcached Profiler Storage (MemcacheD php extension is used):
change "dsn" in framework/profiler section in config_dev.yml
```
...
framework:
...
profiler:
...
dsn: memcached://127.0.0.1/11211
...
```
Last changes:
- memcached support addedd
- optimized performance (serialization done in extension, index is created with ```append``` function)
- updated to last version of Profiler (find by method, avoid duplications)
- done squash on commits
---------------------------------------------------------------------------
by stloyd at 2011-12-01T23:36:02Z
You need to add check for index name size, AFAIK memcache will fail if key is longer than 250 characters.
Also please do an `squash` for all those commits.
---------------------------------------------------------------------------
by pulzarraider at 2011-12-02T00:15:28Z
@stloyd Thanks. I will add the check for key length.
I am just starting with git. Could you please add some tutorial about squash to a documentation page: http://symfony.com/doc/2.0/contributing/code/patches.html ? It will help me (and maybe some others) to do it correct way.
---------------------------------------------------------------------------
by stof at 2011-12-02T00:19:01Z
http://help.github.com/rebase/
---------------------------------------------------------------------------
by pulzarraider at 2011-12-03T18:56:11Z
Thanks @stof, rebase done.
---------------------------------------------------------------------------
by dlsniper at 2011-12-11T14:00:17Z
Hi,
Would it be possible to either use Memcached instead of Memcache or make it configurable to use either Memcache or Memcached?
I've did a little digging on the benefits of using Memcached over Memcache (like for example: http://stackoverflow.com/questions/1442411/using-memcache-vs-memcached-with-php http://devzone.zend.com/1869/zendcon-sessions-episode-040-memcached-the-better-memcache-interface/ ) and maybe this will also help in not having two extensions installed for people who are using Memcached already.
Regards.
---------------------------------------------------------------------------
by pulzarraider at 2011-12-11T16:15:58Z
@dlsniper thanks for great comment. I will add memcached support.
---------------------------------------------------------------------------
by stof at 2011-12-12T20:49:00Z
@pulzarraider what is the status of this PR ? Is it still a WIP ?
---------------------------------------------------------------------------
by pulzarraider at 2011-12-12T22:58:48Z
@stof Yes, it's still WIP. I'm working on a memcached (with D at the end) support. It will be finished in the next few days.
---------------------------------------------------------------------------
by dlsniper at 2011-12-15T12:51:52Z
@pulzarraider if I can help you with the PR let me know.
---------------------------------------------------------------------------
by pulzarraider at 2012-01-08T20:22:24Z
@dlsniper @stof I've finally added memcached support and done some optimizations. Memcache(d) profiler storage is now ready.
---------------------------------------------------------------------------
by dlsniper at 2012-01-08T22:12:29Z
I'm glad you finished this @pulzarraider
Thanks! for your hard work!
+1 for this PR
@stof, @fabpot is it good to go on master?
---------------------------------------------------------------------------
by pulzarraider at 2012-01-28T19:45:56Z
@stof, @fabpot ping
This commit is contained in:
commit
e986b9b7e5
@ -202,10 +202,12 @@ class FrameworkExtension extends Extension
|
||||
|
||||
// Choose storage class based on the DSN
|
||||
$supported = array(
|
||||
'sqlite' => 'Symfony\Component\HttpKernel\Profiler\SqliteProfilerStorage',
|
||||
'mysql' => 'Symfony\Component\HttpKernel\Profiler\MysqlProfilerStorage',
|
||||
'file' => 'Symfony\Component\HttpKernel\Profiler\FileProfilerStorage',
|
||||
'mongodb' => 'Symfony\Component\HttpKernel\Profiler\MongoDbProfilerStorage',
|
||||
'sqlite' => 'Symfony\Component\HttpKernel\Profiler\SqliteProfilerStorage',
|
||||
'mysql' => 'Symfony\Component\HttpKernel\Profiler\MysqlProfilerStorage',
|
||||
'file' => 'Symfony\Component\HttpKernel\Profiler\FileProfilerStorage',
|
||||
'mongodb' => 'Symfony\Component\HttpKernel\Profiler\MongoDbProfilerStorage',
|
||||
'memcache' => 'Symfony\Component\HttpKernel\Profiler\MemcacheProfilerStorage',
|
||||
'memcached' => 'Symfony\Component\HttpKernel\Profiler\MemcachedProfilerStorage',
|
||||
);
|
||||
list($class, ) = explode(':', $config['dsn'], 2);
|
||||
if (!isset($supported[$class])) {
|
||||
|
@ -0,0 +1,260 @@
|
||||
<?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;
|
||||
}
|
||||
|
||||
return array_values($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function purge()
|
||||
{
|
||||
$this->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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(),
|
||||
);
|
||||
|
||||
if ($this->setValue($this->getItemName($profile->getToken()), $data, $this->lifetime)) {
|
||||
// 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 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);
|
||||
|
||||
/**
|
||||
* Flush all existing items at the memcache server
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract protected function flush();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
<?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('Please check your configuration. You are trying to use Memcache with an invalid dsn. "' . $this->dsn . '"');
|
||||
}
|
||||
|
||||
$host = $matches[1];
|
||||
$port = $matches[2];
|
||||
|
||||
$memcache = new Memcache;
|
||||
$memcache->addServer($host, $port);
|
||||
|
||||
$this->memcache = $memcache;
|
||||
}
|
||||
|
||||
return $this->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, $expiration);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function flush()
|
||||
{
|
||||
return $this->getMemcache()->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
<?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('Please check your configuration. You are trying to use Memcached with an invalid dsn. "' . $this->dsn . '"');
|
||||
}
|
||||
|
||||
$host = $matches[1];
|
||||
$port = $matches[2];
|
||||
|
||||
$memcached = new Memcached;
|
||||
|
||||
//disable compression to allow appending
|
||||
$memcached->setOption(Memcached::OPT_COMPRESSION, false);
|
||||
|
||||
$memcached->addServer($host, $port);
|
||||
|
||||
$this->memcached = $memcached;
|
||||
}
|
||||
|
||||
return $this->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, false, $expiration);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function flush()
|
||||
{
|
||||
return $this->getMemcached()->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
<?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\Tests\Component\HttpKernel\Profiler;
|
||||
|
||||
use Symfony\Component\HttpKernel\Profiler\MemcacheProfilerStorage;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
|
||||
class DummyMemcacheProfilerStorage extends MemcacheProfilerStorage
|
||||
{
|
||||
public function getMemcache()
|
||||
{
|
||||
return parent::getMemcache();
|
||||
}
|
||||
}
|
||||
|
||||
class MemcacheProfilerStorageTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected static $storage;
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
if (self::$storage) {
|
||||
self::$storage->purge();
|
||||
}
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
if (!extension_loaded('memcache')) {
|
||||
$this->markTestSkipped('MemcacheProfilerStorageTest requires that the extension memcache is loaded');
|
||||
}
|
||||
|
||||
self::$storage = new DummyMemcacheProfilerStorage('memcache://127.0.0.1/11211', '', '', 86400);
|
||||
try {
|
||||
self::$storage->getMemcache();
|
||||
} catch(\Exception $e) {
|
||||
$this->markTestSkipped('MemcacheProfilerStorageTest requires that there is a Memcache server present on localhost');
|
||||
}
|
||||
|
||||
if (self::$storage) {
|
||||
self::$storage->purge();
|
||||
}
|
||||
}
|
||||
|
||||
public function testStore()
|
||||
{
|
||||
for ($i = 0; $i < 10; $i ++) {
|
||||
$profile = new Profile('token_'.$i);
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
}
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo.bar', 20, 'GET')), 10, '->write() stores data in the Memcache');
|
||||
}
|
||||
|
||||
|
||||
public function testChildren()
|
||||
{
|
||||
$parentProfile = new Profile('token_parent');
|
||||
$parentProfile->setIp('127.0.0.1');
|
||||
$parentProfile->setUrl('http://foo.bar/parent');
|
||||
|
||||
$childProfile = new Profile('token_child');
|
||||
$childProfile->setIp('127.0.0.1');
|
||||
$childProfile->setUrl('http://foo.bar/child');
|
||||
|
||||
$parentProfile->addChild($childProfile);
|
||||
|
||||
self::$storage->write($parentProfile);
|
||||
self::$storage->write($childProfile);
|
||||
|
||||
// Load them from storage
|
||||
$parentProfile = self::$storage->read('token_parent');
|
||||
$childProfile = self::$storage->read('token_child');
|
||||
|
||||
// Check child has link to parent
|
||||
$this->assertNotNull($childProfile->getParent());
|
||||
$this->assertEquals($parentProfile->getToken(), $childProfile->getParentToken());
|
||||
|
||||
// Check parent has child
|
||||
$children = $parentProfile->getChildren();
|
||||
$this->assertEquals(1, count($children));
|
||||
$this->assertEquals($childProfile->getToken(), $children[0]->getToken());
|
||||
}
|
||||
|
||||
public function testStoreSpecialCharsInUrl()
|
||||
{
|
||||
// The SQLite storage accepts special characters in URLs (Even though URLs are not
|
||||
// supposed to contain them)
|
||||
$profile = new Profile('simple_quote');
|
||||
$profile->setUrl('127.0.0.1', 'http://foo.bar/\'');
|
||||
self::$storage->write($profile);
|
||||
$this->assertTrue(false !== self::$storage->read('simple_quote'), '->write() accepts single quotes in URL');
|
||||
|
||||
$profile = new Profile('double_quote');
|
||||
$profile->setUrl('127.0.0.1', 'http://foo.bar/"');
|
||||
self::$storage->write($profile);
|
||||
$this->assertTrue(false !== self::$storage->read('double_quote'), '->write() accepts double quotes in URL');
|
||||
|
||||
$profile = new Profile('backslash');
|
||||
$profile->setUrl('127.0.0.1', 'http://foo.bar/\\');
|
||||
self::$storage->write($profile);
|
||||
$this->assertTrue(false !== self::$storage->read('backslash'), '->write() accepts backslash in URL');
|
||||
|
||||
$profile = new Profile('comma');
|
||||
$profile->setUrl('127.0.0.1', 'http://foo.bar/,');
|
||||
self::$storage->write($profile);
|
||||
$this->assertTrue(false !== self::$storage->read('comma'), '->write() accepts comma in URL');
|
||||
}
|
||||
|
||||
public function testStoreDuplicateToken()
|
||||
{
|
||||
$profile = new Profile('token');
|
||||
$profile->setUrl('http://example.com/');
|
||||
|
||||
$this->assertTrue(self::$storage->write($profile), '->write() returns true when the token is unique');
|
||||
|
||||
$profile->setUrl('http://example.net/');
|
||||
|
||||
$this->assertTrue(self::$storage->write($profile), '->write() returns true when the token is already present in the Memcache');
|
||||
$this->assertEquals('http://example.net/', self::$storage->read('token')->getUrl(), '->write() overwrites the current profile data');
|
||||
|
||||
$this->assertCount(1, self::$storage->find('', '', 1000, ''), '->find() does not return the same profile twice');
|
||||
}
|
||||
|
||||
public function testRetrieveByIp()
|
||||
{
|
||||
$profile = new Profile('token');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setMethod('GET');
|
||||
|
||||
self::$storage->write($profile);
|
||||
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', '', 10, 'GET')), 1, '->find() retrieve a record by IP');
|
||||
$this->assertEquals(count(self::$storage->find('127.0.%.1', '', 10, 'GET')), 0, '->find() does not interpret a "%" as a wildcard in the IP');
|
||||
$this->assertEquals(count(self::$storage->find('127.0._.1', '', 10, 'GET')), 0, '->find() does not interpret a "_" as a wildcard in the IP');
|
||||
}
|
||||
|
||||
public function testRetrieveByUrl()
|
||||
{
|
||||
$profile = new Profile('simple_quote');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar/\'');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
|
||||
$profile = new Profile('double_quote');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar/"');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
|
||||
$profile = new Profile('backslash');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo\\bar/');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
|
||||
$profile = new Profile('percent');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar/%');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
|
||||
$profile = new Profile('underscore');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar/_');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
|
||||
$profile = new Profile('semicolon');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar/;');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo.bar/\'', 10, 'GET')), 1, '->find() accepts single quotes in URLs');
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo.bar/"', 10, 'GET')), 1, '->find() accepts double quotes in URLs');
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo\\bar/', 10, 'GET')), 1, '->find() accepts backslash in URLs');
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo.bar/;', 10, 'GET')), 1, '->find() accepts semicolon in URLs');
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo.bar/%', 10, 'GET')), 1, '->find() does not interpret a "%" as a wildcard in the URL');
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo.bar/_', 10, 'GET')), 1, '->find() does not interpret a "_" as a wildcard in the URL');
|
||||
}
|
||||
}
|
@ -0,0 +1,194 @@
|
||||
<?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\Tests\Component\HttpKernel\Profiler;
|
||||
|
||||
use Symfony\Component\HttpKernel\Profiler\MemcachedProfilerStorage;
|
||||
use Symfony\Component\HttpKernel\Profiler\Profile;
|
||||
|
||||
class DummyMemcachedProfilerStorage extends MemcachedProfilerStorage
|
||||
{
|
||||
public function getMemcached()
|
||||
{
|
||||
return parent::getMemcached();
|
||||
}
|
||||
}
|
||||
|
||||
class MemcachedProfilerStorageTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected static $storage;
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
if (self::$storage) {
|
||||
self::$storage->purge();
|
||||
}
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
if (!extension_loaded('memcached')) {
|
||||
$this->markTestSkipped('MemcachedProfilerStorageTest requires that the extension memcached is loaded');
|
||||
}
|
||||
|
||||
self::$storage = new DummyMemcachedProfilerStorage('memcached://127.0.0.1/11211', '', '', 86400);
|
||||
try {
|
||||
self::$storage->getMemcached();
|
||||
} catch(\Exception $e) {
|
||||
$this->markTestSkipped('MemcachedProfilerStorageTest requires that there is a Memcache server present on localhost');
|
||||
}
|
||||
|
||||
if (self::$storage) {
|
||||
self::$storage->purge();
|
||||
}
|
||||
}
|
||||
|
||||
public function testStore()
|
||||
{
|
||||
for ($i = 0; $i < 10; $i ++) {
|
||||
$profile = new Profile('token_'.$i);
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
}
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo.bar', 20, 'GET')), 10, '->write() stores data in the Memcache');
|
||||
}
|
||||
|
||||
|
||||
public function testChildren()
|
||||
{
|
||||
$parentProfile = new Profile('token_parent');
|
||||
$parentProfile->setIp('127.0.0.1');
|
||||
$parentProfile->setUrl('http://foo.bar/parent');
|
||||
|
||||
$childProfile = new Profile('token_child');
|
||||
$childProfile->setIp('127.0.0.1');
|
||||
$childProfile->setUrl('http://foo.bar/child');
|
||||
|
||||
$parentProfile->addChild($childProfile);
|
||||
|
||||
self::$storage->write($parentProfile);
|
||||
self::$storage->write($childProfile);
|
||||
|
||||
// Load them from storage
|
||||
$parentProfile = self::$storage->read('token_parent');
|
||||
$childProfile = self::$storage->read('token_child');
|
||||
|
||||
// Check child has link to parent
|
||||
$this->assertNotNull($childProfile->getParent());
|
||||
$this->assertEquals($parentProfile->getToken(), $childProfile->getParentToken());
|
||||
|
||||
// Check parent has child
|
||||
$children = $parentProfile->getChildren();
|
||||
$this->assertEquals(1, count($children));
|
||||
$this->assertEquals($childProfile->getToken(), $children[0]->getToken());
|
||||
}
|
||||
|
||||
public function testStoreSpecialCharsInUrl()
|
||||
{
|
||||
// The SQLite storage accepts special characters in URLs (Even though URLs are not
|
||||
// supposed to contain them)
|
||||
$profile = new Profile('simple_quote');
|
||||
$profile->setUrl('127.0.0.1', 'http://foo.bar/\'');
|
||||
self::$storage->write($profile);
|
||||
$this->assertTrue(false !== self::$storage->read('simple_quote'), '->write() accepts single quotes in URL');
|
||||
|
||||
$profile = new Profile('double_quote');
|
||||
$profile->setUrl('127.0.0.1', 'http://foo.bar/"');
|
||||
self::$storage->write($profile);
|
||||
$this->assertTrue(false !== self::$storage->read('double_quote'), '->write() accepts double quotes in URL');
|
||||
|
||||
$profile = new Profile('backslash');
|
||||
$profile->setUrl('127.0.0.1', 'http://foo.bar/\\');
|
||||
self::$storage->write($profile);
|
||||
$this->assertTrue(false !== self::$storage->read('backslash'), '->write() accepts backslash in URL');
|
||||
|
||||
$profile = new Profile('comma');
|
||||
$profile->setUrl('127.0.0.1', 'http://foo.bar/,');
|
||||
self::$storage->write($profile);
|
||||
$this->assertTrue(false !== self::$storage->read('comma'), '->write() accepts comma in URL');
|
||||
}
|
||||
|
||||
public function testStoreDuplicateToken()
|
||||
{
|
||||
$profile = new Profile('token');
|
||||
$profile->setUrl('http://example.com/');
|
||||
|
||||
$this->assertTrue(self::$storage->write($profile), '->write() returns true when the token is unique');
|
||||
|
||||
$profile->setUrl('http://example.net/');
|
||||
|
||||
$this->assertTrue(self::$storage->write($profile), '->write() returns true when the token is already present in the Memcache');
|
||||
$this->assertEquals('http://example.net/', self::$storage->read('token')->getUrl(), '->write() overwrites the current profile data');
|
||||
|
||||
$this->assertCount(1, self::$storage->find('', '', 1000, ''), '->find() does not return the same profile twice');
|
||||
}
|
||||
|
||||
public function testRetrieveByIp()
|
||||
{
|
||||
$profile = new Profile('token');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setMethod('GET');
|
||||
|
||||
self::$storage->write($profile);
|
||||
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', '', 10, 'GET')), 1, '->find() retrieve a record by IP');
|
||||
$this->assertEquals(count(self::$storage->find('127.0.%.1', '', 10, 'GET')), 0, '->find() does not interpret a "%" as a wildcard in the IP');
|
||||
$this->assertEquals(count(self::$storage->find('127.0._.1', '', 10, 'GET')), 0, '->find() does not interpret a "_" as a wildcard in the IP');
|
||||
}
|
||||
|
||||
public function testRetrieveByUrl()
|
||||
{
|
||||
$profile = new Profile('simple_quote');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar/\'');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
|
||||
$profile = new Profile('double_quote');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar/"');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
|
||||
$profile = new Profile('backslash');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo\\bar/');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
|
||||
$profile = new Profile('percent');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar/%');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
|
||||
$profile = new Profile('underscore');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar/_');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
|
||||
$profile = new Profile('semicolon');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar/;');
|
||||
$profile->setMethod('GET');
|
||||
self::$storage->write($profile);
|
||||
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo.bar/\'', 10, 'GET')), 1, '->find() accepts single quotes in URLs');
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo.bar/"', 10, 'GET')), 1, '->find() accepts double quotes in URLs');
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo\\bar/', 10, 'GET')), 1, '->find() accepts backslash in URLs');
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo.bar/;', 10, 'GET')), 1, '->find() accepts semicolon in URLs');
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo.bar/%', 10, 'GET')), 1, '->find() does not interpret a "%" as a wildcard in the URL');
|
||||
$this->assertEquals(count(self::$storage->find('127.0.0.1', 'http://foo.bar/_', 10, 'GET')), 1, '->find() does not interpret a "_" as a wildcard in the URL');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user