Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ matrix:
fast_finish: true

before_script:
- pecl install grpc || echo 'Failed to install grpc'
- pecl install grpc-1.4.1 || echo 'Failed to install grpc'
- composer install
- if [[ $TRAVIS_PHP_VERSION =~ ^hhvm ]]; then composer --no-interaction --dev remove google/protobuf google/gax google/proto-client; fi
- ./dev/sh/system-test-credentials
Expand Down
36 changes: 34 additions & 2 deletions src/Datastore/DatastoreSessionHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,21 +130,47 @@ class DatastoreSessionHandler implements SessionHandlerInterface
/* @var Transaction */
private $transaction;

/* @var array */
private $options;

/**
* Create a custom session handler backed by Cloud Datastore.
*
* @param DatastoreClient $datastore Datastore client.
* @param int $gcLimit [optional] A number of entities to delete in the
* garbage collection. Defaults to 0 which means it does nothing.
* The value larger than 1000 will be cut down to 1000.
* @param array $options [optional] {
* Configuration Options
*
* @type array $entityOptions Default options to be passed to the
* {@see \Google\Cloud\Datastore\DatastoreClient::entity()} method when writing session data to Datastore.
* If not specified, defaults to `['excludeFromIndexes' => ['data']]`.
* }
*/
public function __construct(
DatastoreClient $datastore,
$gcLimit = self::DEFAULT_GC_LIMIT
$gcLimit = self::DEFAULT_GC_LIMIT,
array $options = []
) {
$this->datastore = $datastore;
// Cut down to 1000
$this->gcLimit = min($gcLimit, 1000);

if (!isset($options['entityOptions'])) {

This comment was marked as spam.

This comment was marked as spam.

$options['entityOptions'] = [
'excludeFromIndexes' => ['data']
];
}
if (!is_array($options['entityOptions'])) {
throw new InvalidArgumentException(
'Optional argument `entityOptions` must be an array, got ' .
(is_object($options['entityOptions'])
? get_class($options['entityOptions'])
: gettype($options['entityOptions'])));
}

$this->options = $options;
}

/**
Expand Down Expand Up @@ -204,6 +230,11 @@ public function read($id)

/**
* Write the session data to Cloud Datastore.
*
* @param string $id Identifier used to construct a {@see \Google\Cloud\Datastore\Key}
* for the {@see \Google\Cloud\Datastore\Entity} to be written.
* @param string $data The session data to write to the {@see \Google\Cloud\Datastore\Entity}.
* @return bool
*/
public function write($id, $data)
{
Expand All @@ -218,7 +249,8 @@ public function write($id, $data)
[
'data' => $data,
't' => time()
]
],
$this->options['entityOptions']
);
$this->transaction->upsert($entity);
$this->transaction->commit();
Expand Down
123 changes: 121 additions & 2 deletions tests/unit/Datastore/DatastoreSessionHandlerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,15 @@ public function testWrite()
->shouldBeCalledTimes(1)
->willReturn($key);
$that = $this;
$this->datastore->entity($key, Argument::type('array'))
$this->datastore->entity($key, Argument::type('array'), Argument::type('array'))
->will(function($args) use ($that, $key, $entity) {
$that->assertEquals($key, $args[0]);
$that->assertEquals('sessiondata', $args[1]['data']);
$that->assertInternalType('int', $args[1]['t']);
$that->assertTrue(time() >= $args[1]['t']);
// 2 seconds grace period should be enough
$that->assertTrue(time() - $args[1]['t'] <= 2);
$that->assertEquals(['excludeFromIndexes' => ['data']], $args[2]);
return $entity;
});
$datastoreSessionHandler = new DatastoreSessionHandler(
Expand Down Expand Up @@ -234,14 +235,15 @@ public function testWriteWithException()
->shouldBeCalledTimes(1)
->willReturn($key);
$that = $this;
$this->datastore->entity($key, Argument::type('array'))
$this->datastore->entity($key, Argument::type('array'), Argument::type('array'))
->will(function($args) use ($that, $key, $entity) {
$that->assertEquals($key, $args[0]);
$that->assertEquals('sessiondata', $args[1]['data']);
$that->assertInternalType('int', $args[1]['t']);
$that->assertTrue(time() >= $args[1]['t']);
// 2 seconds grace period should be enough
$that->assertTrue(time() - $args[1]['t'] <= 2);
$that->assertEquals(['excludeFromIndexes' => ['data']], $args[2]);
return $entity;
});

Expand All @@ -254,6 +256,123 @@ public function testWriteWithException()
$this->assertEquals(false, $ret);
}

public function testWriteWithEntityOptions()
{
$data = 'sessiondata';
$key = new Key('projectid');
$key->pathElement(self::KIND, 'sessionid');
$datastoreSessionHandlerOptions = [
'entityOptions' => ['excludeFromIndexes' => ['data', 'additional']],
];
$entity = new Entity($key, ['data' => $data]);
$this->transaction->upsert($entity)
->shouldBeCalledTimes(1);
$this->transaction->commit()
->shouldBeCalledTimes(1);
$this->datastore->transaction()
->shouldBeCalledTimes(1)
->willReturn($this->transaction->reveal());
$this->datastore->key(
self::KIND,
'sessionid',
['namespaceId' => self::NAMESPACE_ID]
)
->shouldBeCalledTimes(1)
->willReturn($key);
$that = $this;
$this->datastore->entity($key, Argument::type('array'), Argument::type('array'))
->will(function($args) use ($that, $key, $entity) {
$that->assertEquals($key, $args[0]);
$that->assertEquals('sessiondata', $args[1]['data']);
$that->assertInternalType('int', $args[1]['t']);
$that->assertTrue(time() >= $args[1]['t']);
// 2 seconds grace period should be enough
$that->assertTrue(time() - $args[1]['t'] <= 2);
$that->assertEquals(['excludeFromIndexes' => ['data', 'additional']], $args[2]);
return $entity;
});
$datastoreSessionHandler = new DatastoreSessionHandler(
$this->datastore->reveal(),
DatastoreSessionHandler::DEFAULT_GC_LIMIT,
$datastoreSessionHandlerOptions
);
$datastoreSessionHandler->open(self::NAMESPACE_ID, self::KIND);
$ret = $datastoreSessionHandler->write('sessionid', $data);

$this->assertEquals(true, $ret);
}

public function testWriteWithEmptyEntityOptions()
{
$data = 'sessiondata';
$key = new Key('projectid');
$key->pathElement(self::KIND, 'sessionid');
$datastoreSessionHandlerOptions = [
'entityOptions' => [],
];
$entity = new Entity($key, ['data' => $data]);
$this->transaction->upsert($entity)
->shouldBeCalledTimes(1);
$this->transaction->commit()
->shouldBeCalledTimes(1);
$this->datastore->transaction()
->shouldBeCalledTimes(1)
->willReturn($this->transaction->reveal());
$this->datastore->key(
self::KIND,
'sessionid',
['namespaceId' => self::NAMESPACE_ID]
)
->shouldBeCalledTimes(1)
->willReturn($key);
$that = $this;
$this->datastore->entity($key, Argument::type('array'), Argument::type('array'))
->will(function($args) use ($that, $key, $entity) {
$that->assertEquals($key, $args[0]);
$that->assertEquals('sessiondata', $args[1]['data']);
$that->assertInternalType('int', $args[1]['t']);
$that->assertTrue(time() >= $args[1]['t']);
// 2 seconds grace period should be enough
$that->assertTrue(time() - $args[1]['t'] <= 2);
$that->assertEquals([], $args[2]);
return $entity;
});
$datastoreSessionHandler = new DatastoreSessionHandler(
$this->datastore->reveal(),
DatastoreSessionHandler::DEFAULT_GC_LIMIT,
$datastoreSessionHandlerOptions
);
$datastoreSessionHandler->open(self::NAMESPACE_ID, self::KIND);
$ret = $datastoreSessionHandler->write('sessionid', $data);

$this->assertEquals(true, $ret);
}

/**
* @dataProvider invalidEntityOptions
* @expectedException InvalidArgumentException
*/
public function testInvalidEntityOptions($datastoreSessionHandlerOptions)
{
new DatastoreSessionHandler(
$this->datastore->reveal(),
DatastoreSessionHandler::DEFAULT_GC_LIMIT,
$datastoreSessionHandlerOptions
);
}

public function invalidEntityOptions()
{
return [
[
['entityOptions' => 1]
],
[
['entityOptions' => new \stdClass()]
],
];
}

public function testDestroy()
{
$key = new Key('projectid');
Expand Down