mirror of
https://github.com/predis/predis.git
synced 2026-08-20 09:12:13 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa1b070f9b | |||
| 3e125c964c | |||
| e9fdc47d3f | |||
| d3930ea298 | |||
| 0efcbb7992 | |||
| f2af247c63 | |||
| a5cf6d72cd | |||
| 7173f0c80c |
@@ -1,3 +1,18 @@
|
||||
v0.8.2 (2013-02-03)
|
||||
===============================================================================
|
||||
|
||||
- Added `Predis\Session\SessionHandler` to make it easy to store PHP sessions
|
||||
on Redis using Predis. Please note that this class needs either PHP >= 5.4.0
|
||||
or a polyfill for PHP's `SessionHandlerInterface`.
|
||||
|
||||
- Added the ability to get the default value of a client option directly from
|
||||
`Predis\Option\ClientOption` using the `getDefault()` method by passing the
|
||||
option name or its instance.
|
||||
|
||||
- __FIX__: the standard pipeline executor was not using the response parser
|
||||
methods associated to commands to process raw responses (ISSUE #101).
|
||||
|
||||
|
||||
v0.8.1 (2013-01-19)
|
||||
===============================================================================
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ $client->hmset('my:hash', array('field1'=>'value1', 'field2'=>'value2'); // valu
|
||||
```
|
||||
|
||||
The only exception to this _rule_ is the [SORT](http://redis.io/commands/sort) command for which modifiers are
|
||||
[passed using a named array](https://github.com/nrk/predis/blob/master/tests/Predis/Command/KeySortTest.php#L56-77).
|
||||
[passed using a named array](tests/Predis/Command/KeySortTest.php#L56-77).
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ $client = new Predis\Client('tcp://127.0.0.1', array(
|
||||
```
|
||||
|
||||
For a more in-depth insight on how to create new connection backends you can look at the actual
|
||||
implementation of the classes contained in `Predis\Connection` namespace.
|
||||
implementation of the classes contained in the `Predis\Connection` namespace.
|
||||
|
||||
|
||||
### Defining and registering new commands on the client at runtime ###
|
||||
@@ -214,7 +214,7 @@ Redis. If you do not have Redis up and running, integration tests can be disable
|
||||
suite is configured to execute integration tests using the server profile for Redis v2.4 (which is the
|
||||
current stable version of Redis). You can optionally run the suite against a Redis instance built from
|
||||
the `unstable` branch with the development profile by changing the `REDIS_SERVER_VERSION` to `dev` in
|
||||
the `phpunit.xml` file. More details about testing Predis are available in `tests/README.md`.
|
||||
the `phpunit.xml` file. More details on testing Predis can be found in [the tests README](tests/README.md).
|
||||
|
||||
Predis uses Travis CI for continuous integration. You can find the results of the test suite and the build
|
||||
history [on its project page](http://travis-ci.org/nrk/predis).
|
||||
@@ -252,4 +252,4 @@ history [on its project page](http://travis-ci.org/nrk/predis).
|
||||
|
||||
## License ##
|
||||
|
||||
The code for Predis is distributed under the terms of the MIT license (see LICENSE).
|
||||
The code for Predis is distributed under the terms of the MIT license (see [LICENSE](LICENSE)).
|
||||
|
||||
@@ -51,8 +51,8 @@ $parameters = array(
|
||||
);
|
||||
|
||||
$options = array(
|
||||
'profile' => function ($options) {
|
||||
$profile = ServerProfile::get('2.6');
|
||||
'profile' => function ($options, $option) {
|
||||
$profile = $options->getDefault($option);
|
||||
$profile->defineCommand('hmgetall', 'HashMultipleGetAll');
|
||||
|
||||
return $profile;
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
require 'SharedConfigurations.php';
|
||||
|
||||
// This example demonstrates how to leverage Predis to save PHP sessions on Redis.
|
||||
//
|
||||
// The value of `session.gc_maxlifetime` in `php.ini` will be used by default as the
|
||||
// the TTL for keys holding session data on Redis, but this value can be overridden
|
||||
// when creating the session handler instance with the `gc_maxlifetime` option.
|
||||
//
|
||||
// Note that this class needs PHP >= 5.4 but can be used on PHP 5.3 if a polyfill for
|
||||
// SessionHandlerInterface (see http://www.php.net/class.sessionhandlerinterface.php)
|
||||
// is provided either by you or an external package like `symfony/http-foundation`.
|
||||
|
||||
if (!interface_exists('SessionHandlerInterface')) {
|
||||
die("ATTENTION: the session handler implemented by Predis needs PHP >= 5.4.0 or a polyfill ".
|
||||
"for \SessionHandlerInterface either provided by you or an external package.\n");
|
||||
}
|
||||
|
||||
// Instantiate a new client just like you would normally do. We'll prefix our session keys here.
|
||||
$client = new Predis\Client($single_server, array('prefix' => 'sessions:'));
|
||||
|
||||
// Set `gc_maxlifetime` so that a session will be expired after 5 seconds since last access.
|
||||
$handler = new Predis\Session\SessionHandler($client, array('gc_maxlifetime' => 5));
|
||||
|
||||
// Register our session handler (it uses `session_set_save_handler()` internally).
|
||||
$handler->register();
|
||||
|
||||
// Set a fixed session ID just for the sake of our example.
|
||||
session_id('example_session_id');
|
||||
|
||||
session_start();
|
||||
|
||||
if (isset($_SESSION['foo'])) {
|
||||
echo "Session has `foo` set to {$_SESSION['foo']}\n";
|
||||
} else {
|
||||
$_SESSION['foo'] = $value = mt_rand();
|
||||
echo "Empty session, `foo` has been set with $value\n";
|
||||
}
|
||||
@@ -33,7 +33,7 @@ use Predis\Transaction\MultiExecContext;
|
||||
*/
|
||||
class Client implements ClientInterface
|
||||
{
|
||||
const VERSION = '0.8.1';
|
||||
const VERSION = '0.8.2';
|
||||
|
||||
private $options;
|
||||
private $profile;
|
||||
|
||||
@@ -103,4 +103,23 @@ class ClientOptions implements ClientOptionsInterface
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value for the specified option.
|
||||
*
|
||||
* @param string|OptionInterface $option Name or instance of the option.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDefault($option)
|
||||
{
|
||||
if ($option instanceof OptionInterface) {
|
||||
return $option->getDefault($this);
|
||||
}
|
||||
|
||||
$options = $this->getDefaultOptions();
|
||||
|
||||
if (isset($options[$option])) {
|
||||
return $options[$option]->getDefault($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,12 @@
|
||||
|
||||
namespace Predis\Pipeline;
|
||||
|
||||
use Iterator;
|
||||
use SplQueue;
|
||||
use Predis\ResponseErrorInterface;
|
||||
use Predis\ResponseObjectInterface;
|
||||
use Predis\ServerException;
|
||||
use Predis\Command\CommandInterface;
|
||||
use Predis\Connection\ConnectionInterface;
|
||||
use Predis\Connection\ReplicationConnectionInterface;
|
||||
|
||||
@@ -50,6 +53,27 @@ class StandardExecutor implements PipelineExecutorInterface
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a response object.
|
||||
*
|
||||
* @param ConnectionInterface $connection
|
||||
* @param CommandInterface $command
|
||||
* @param ResponseObjectInterface $response
|
||||
* @return mixed
|
||||
*/
|
||||
protected function onResponseObject(ConnectionInterface $connection, CommandInterface $command, ResponseObjectInterface $response)
|
||||
{
|
||||
if ($response instanceof ResponseErrorInterface) {
|
||||
return $this->onResponseError($connection, $response);
|
||||
}
|
||||
|
||||
if ($response instanceof Iterator) {
|
||||
return $command->parseResponse(iterator_to_array($response));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles -ERR responses returned by Redis.
|
||||
*
|
||||
@@ -58,6 +82,10 @@ class StandardExecutor implements PipelineExecutorInterface
|
||||
*/
|
||||
protected function onResponseError(ConnectionInterface $connection, ResponseErrorInterface $response)
|
||||
{
|
||||
if (!$this->exceptions) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Force disconnection to prevent protocol desynchronization.
|
||||
$connection->disconnect();
|
||||
$message = $response->getMessage();
|
||||
@@ -70,24 +98,23 @@ class StandardExecutor implements PipelineExecutorInterface
|
||||
*/
|
||||
public function execute(ConnectionInterface $connection, SplQueue $commands)
|
||||
{
|
||||
$size = count($commands);
|
||||
$values = array();
|
||||
$exceptions = $this->exceptions;
|
||||
|
||||
$this->checkConnection($connection);
|
||||
|
||||
foreach ($commands as $command) {
|
||||
$connection->writeCommand($command);
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $size; $i++) {
|
||||
$response = $connection->readResponse($commands->dequeue());
|
||||
$values = array();
|
||||
|
||||
if ($response instanceof ResponseErrorInterface && $exceptions === true) {
|
||||
$this->onResponseError($connection, $response);
|
||||
while (!$commands->isEmpty()) {
|
||||
$command = $commands->dequeue();
|
||||
$response = $connection->readResponse($command);
|
||||
|
||||
if ($response instanceof ResponseObjectInterface) {
|
||||
$values[] = $this->onResponseObject($connection, $command, $response);
|
||||
} else {
|
||||
$values[] = $command->parseResponse($response);
|
||||
}
|
||||
|
||||
$values[$i] = $response instanceof \Iterator ? iterator_to_array($response) : $response;
|
||||
}
|
||||
|
||||
return $values;
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Predis package.
|
||||
*
|
||||
* (c) Daniele Alessandri <suppakilla@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Predis\Session;
|
||||
|
||||
use SessionHandlerInterface;
|
||||
use Predis\ClientInterface;
|
||||
|
||||
/**
|
||||
* Session handler class that relies on Predis\Client to store PHP's sessions
|
||||
* data into one or multiple Redis servers.
|
||||
*
|
||||
* This class is mostly intended for PHP 5.4 but it can be used under PHP 5.3 provided
|
||||
* that a polyfill for `SessionHandlerInterface` is defined by either you or an external
|
||||
* package such as `symfony/http-foundation`.
|
||||
*
|
||||
* @author Daniele Alessandri <suppakilla@gmail.com>
|
||||
*/
|
||||
class SessionHandler implements SessionHandlerInterface
|
||||
{
|
||||
protected $client;
|
||||
protected $ttl;
|
||||
|
||||
/**
|
||||
* @param ClientInterface $client Fully initialized client instance.
|
||||
* @param array $options Session handler options.
|
||||
*/
|
||||
public function __construct(ClientInterface $client, Array $options = array())
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->ttl = (int) (isset($options['gc_maxlifetime']) ? $options['gc_maxlifetime'] : ini_get('session.gc_maxlifetime'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the handler instance as the current session handler.
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
|
||||
session_set_save_handler($this, true);
|
||||
} else {
|
||||
session_set_save_handler(
|
||||
array($this, 'open'),
|
||||
array($this, 'close'),
|
||||
array($this, 'read'),
|
||||
array($this, 'write'),
|
||||
array($this, 'destroy'),
|
||||
array($this, 'gc')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($save_path, $session_id)
|
||||
{
|
||||
// NOOP
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
// NOOP
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
// NOOP
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($session_id)
|
||||
{
|
||||
if ($data = $this->client->get($session_id)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($session_id, $session_data)
|
||||
{
|
||||
$this->client->setex($session_id, $this->ttl, $session_data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($session_id)
|
||||
{
|
||||
$this->client->del($session_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying client instance.
|
||||
*
|
||||
* @return ClientInterface
|
||||
*/
|
||||
public function getClient()
|
||||
{
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session max lifetime value.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxLifeTime()
|
||||
{
|
||||
return $this->ttl;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -10,7 +10,7 @@ name = "Predis"
|
||||
desc = "Flexible and feature-complete PHP client library for Redis"
|
||||
homepage = "http://github.com/nrk/predis"
|
||||
license = "MIT"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
stability = "stable"
|
||||
channel = "pear.nrk.io"
|
||||
|
||||
|
||||
@@ -78,4 +78,51 @@ class ClientOptionsTest extends StandardTestCase
|
||||
$this->assertTrue(isset($options->custom));
|
||||
$this->assertFalse(isset($options->profile));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetDefaultUsingOptionName()
|
||||
{
|
||||
$options = new ClientOptions();
|
||||
|
||||
$this->assertInstanceOf('Predis\Connection\PredisCluster', $options->getDefault('cluster'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetDefaultUsingUnhandledOptionName()
|
||||
{
|
||||
$options = new ClientOptions();
|
||||
$option = new ClientCluster();
|
||||
|
||||
$this->assertNull($options->getDefault('foo'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetDefaultUsingOptionInstance()
|
||||
{
|
||||
$options = new ClientOptions();
|
||||
$option = new ClientCluster();
|
||||
|
||||
$this->assertInstanceOf('Predis\Connection\PredisCluster', $options->getDefault($option));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testGetDefaultUsingUnhandledOptionInstance()
|
||||
{
|
||||
$options = new ClientOptions();
|
||||
$option = new CustomOption(array(
|
||||
'default' => function ($options) {
|
||||
return 'foo';
|
||||
},
|
||||
));
|
||||
|
||||
$this->assertSame('foo', $options->getDefault($option));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use \PHPUnit_Framework_TestCase as StandardTestCase;
|
||||
|
||||
use SplQueue;
|
||||
use Predis\ResponseError;
|
||||
use Predis\ResponseObjectInterface;
|
||||
use Predis\Profile\ServerProfile;
|
||||
|
||||
/**
|
||||
@@ -40,7 +41,7 @@ class StandardExecutorTest extends StandardTestCase
|
||||
$replies = $executor->execute($connection, $pipeline);
|
||||
|
||||
$this->assertTrue($pipeline->isEmpty());
|
||||
$this->assertSame(array('PONG', 'PONG', 'PONG'), $replies);
|
||||
$this->assertSame(array(true, true, true), $replies);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,7 +65,29 @@ class StandardExecutorTest extends StandardTestCase
|
||||
$replies = $executor->execute($connection, $pipeline);
|
||||
|
||||
$this->assertTrue($pipeline->isEmpty());
|
||||
$this->assertSame(array('PONG', 'PONG', 'PONG'), $replies);
|
||||
$this->assertSame(array(true, true, true), $replies);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testExecutorDoesNotParseResponseObjects()
|
||||
{
|
||||
$executor = new StandardExecutor();
|
||||
$response = $this->getMock('Predis\ResponseObjectInterface');
|
||||
|
||||
$this->simpleResponseObjectTest($executor, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group disconnected
|
||||
*/
|
||||
public function testExecutorCanReturnRedisErrors()
|
||||
{
|
||||
$executor = new StandardExecutor(false);
|
||||
$response = $this->getMock('Predis\ResponseErrorInterface');
|
||||
|
||||
$this->simpleResponseObjectTest($executor, $response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,6 +113,34 @@ class StandardExecutorTest extends StandardTestCase
|
||||
// ---- HELPER METHODS ------------------------------------------------ //
|
||||
// ******************************************************************** //
|
||||
|
||||
/**
|
||||
* Executes a test for the Predis\ResponseObjectInterface type.
|
||||
*
|
||||
* @param PipelineExecutorInterface $executor
|
||||
* @param ResponseObjectInterface $response
|
||||
*/
|
||||
protected function simpleResponseObjectTest(PipelineExecutorInterface $executor, ResponseObjectInterface $response)
|
||||
{
|
||||
$pipeline = new SplQueue();
|
||||
|
||||
$command = $this->getMock('Predis\Command\CommandInterface');
|
||||
$command->expects($this->never())
|
||||
->method('parseResponse');
|
||||
|
||||
$connection = $this->getMock('Predis\Connection\SingleConnectionInterface');
|
||||
$connection->expects($this->once())
|
||||
->method('writeCommand');
|
||||
$connection->expects($this->once())
|
||||
->method('readResponse')
|
||||
->will($this->returnValue($response));
|
||||
|
||||
$pipeline->enqueue($command);
|
||||
$replies = $executor->execute($connection, $pipeline);
|
||||
|
||||
$this->assertTrue($pipeline->isEmpty());
|
||||
$this->assertSame(array($response), $replies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of queued command instances.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user