Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
37 changes: 35 additions & 2 deletions src/Datastore/DatastoreSessionHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,21 +130,48 @@ 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 +231,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 +250,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