Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge 9ad2c05 into 985438c
Browse files Browse the repository at this point in the history
  • Loading branch information
weierophinney committed Oct 25, 2018
2 parents 985438c + 9ad2c05 commit 9caf853
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 4 deletions.
9 changes: 9 additions & 0 deletions docs/book/v1/config.md
Expand Up @@ -8,6 +8,8 @@ This package allows configuring the following items:
- The cache limiter (which controls how resources using sessions are cached by the browser).
- When the session expires.
- When the resource using a session was last modified.
- Whether or not to create a _persistent_ session cookie (i.e., one that will
not expire when the browser is closed).

This document details how to configure each of these items.

Expand Down Expand Up @@ -111,6 +113,13 @@ return [
// - the index.php file of the current working directory
// - the current working directory
'last_modified' => null,

// A boolean value indicating whether or not the session cookie
// should persist. By default, this is disabled (false); passing
// a boolean true value will enable the feature. When enabled, the
// cookie will be generated with a Max-Age directive equal to the
// cache_expire value as noted above.
'persistent' => false,
],
];
```
Expand Down
2 changes: 1 addition & 1 deletion docs/book/v1/intro.md
@@ -1,4 +1,4 @@
# zend-expressive-session-cache
# Introduction

This component provides a [PSR-6](https://www.php-fig.org/psr/psr-6/) session
persistence adapter for use with [zend-expressive-session](https://docs.zendframework.com/zend-expressive-session/).
Expand Down
6 changes: 5 additions & 1 deletion docs/book/v1/manual.md
Expand Up @@ -19,14 +19,18 @@ The following details the constructor of the `Zend\Expressive\Session\Cache\Cach
* modified. If not provided, this will look for each of
* public/index.php, index.php, and finally the current working
* directory, using the filemtime() of the first found.
* @param bool $persistent Whether or not to create a persistent cookie. If
* provided, this sets the Max-Age for the cookie to the value of
* $cacheExpire.
*/
public function __construct(
\Psr\Cache\CacheItemPoolInterface $cache,
string $cookieName,
string $cookiePath = '/',
string $cacheLimiter = 'nocache',
int $cacheExpire = 10800,
?int $lastModified = null
?int $lastModified = null,
bool $persistent = false
) {
```

Expand Down
15 changes: 14 additions & 1 deletion src/CacheSessionPersistence.php
Expand Up @@ -75,6 +75,9 @@ class CacheSessionPersistence implements SessionPersistenceInterface
/** @var false|string */
private $lastModified;

/** @var bool */
private $persistent;

/**
* Prepare session cache and default HTTP caching headers.
*
Expand All @@ -91,14 +94,18 @@ class CacheSessionPersistence implements SessionPersistenceInterface
* modified. If not provided, this will look for each of
* public/index.php, index.php, and finally the current working
* directory, using the filemtime() of the first found.
* @param bool $persistent Whether or not to create a persistent cookie. If
* provided, this sets the Max-Age for the cookie to the value of
* $cacheExpire.
*/
public function __construct(
CacheItemPoolInterface $cache,
string $cookieName,
string $cookiePath = '/',
string $cacheLimiter = 'nocache',
int $cacheExpire = 10800,
?int $lastModified = null
?int $lastModified = null,
bool $persistent = false
) {
$this->cache = $cache;

Expand All @@ -118,6 +125,8 @@ public function __construct(
$this->lastModified = $lastModified
? gmdate(self::HTTP_DATE_FORMAT, $lastModified)
: $this->determineLastModifiedValue();

$this->persistent = $persistent;
}

public function initializeSessionFromRequest(ServerRequestInterface $request) : SessionInterface
Expand Down Expand Up @@ -152,6 +161,10 @@ public function persistSession(SessionInterface $session, ResponseInterface $res
->withValue($id)
->withPath($this->cookiePath);

if ($this->persistent) {
$sessionCookie = $sessionCookie->withMaxAge($this->cacheExpire);
}

$response = FigResponseCookies::set($response, $sessionCookie);

if ($this->responseAlreadyHasCacheHeaders($response)) {
Expand Down
4 changes: 3 additions & 1 deletion src/CacheSessionPersistenceFactory.php
Expand Up @@ -30,14 +30,16 @@ public function __invoke(ContainerInterface $container)
$cacheLimiter = $config['cache_limiter'] ?? 'nocache';
$cacheExpire = $config['cache_expire'] ?? 10800;
$lastModified = $config['last_modified'] ?? null;
$persistent = $config['persistent'] ?? false;

return new CacheSessionPersistence(
$container->get($cacheService),
$cookieName,
$cookiePath,
$cacheLimiter,
$cacheExpire,
$lastModified
$lastModified,
$persistent
);
}
}
3 changes: 3 additions & 0 deletions test/CacheSessionPersistenceFactoryTest.php
Expand Up @@ -58,6 +58,7 @@ public function testFactoryUsesSaneDefaultsForConstructorArguments()
$this->assertAttributeSame('nocache', 'cacheLimiter', $persistence);
$this->assertAttributeSame(10800, 'cacheExpire', $persistence);
$this->assertAttributeNotEmpty('lastModified', $persistence);
$this->assertAttributeSame(false, 'persistent', $persistence);
}

public function testFactoryAllowsConfiguringAllConstructorArguments()
Expand All @@ -74,6 +75,7 @@ public function testFactoryAllowsConfiguringAllConstructorArguments()
'cache_limiter' => 'public',
'cache_expire' => 300,
'last_modified' => $lastModified,
'persistent' => true,
],
]);
$this->container->has(CacheItemPoolInterface::class)->willReturn(true);
Expand All @@ -92,6 +94,7 @@ public function testFactoryAllowsConfiguringAllConstructorArguments()
'lastModified',
$persistence
);
$this->assertAttributeSame(true, 'persistent', $persistence);
}

public function testFactoryAllowsConfiguringCacheAdapterServiceName()
Expand Down
42 changes: 42 additions & 0 deletions test/CacheSessionPersistenceTest.php
Expand Up @@ -63,6 +63,20 @@ public function assertSetCookieUsesNewIdentifier(string $identifier, Response $r
);
}

public function assertCookieMaxAgeMirrorsExpiry(int $expiry, Response $response)
{
$setCookie = $response->getHeaderLine('Set-Cookie');
$parts = explode(';', $setCookie);
$parts = array_map(function ($value) {
return trim($value);
}, $parts);
$this->assertContains(
'Max-Age=' . $expiry,
$parts,
sprintf('Could not find Max-Age=%d within set-cookie header: %s', $expiry, $setCookie)
);
}

public function assertCacheHeaders(string $cacheLimiter, Response $response)
{
switch ($cacheLimiter) {
Expand Down Expand Up @@ -621,4 +635,32 @@ public function testPersistSessionWithAnyExistingCacheHeadersDoesNotRepopulateCa
$this->assertSetCookieUsesNewIdentifier('', $result);
$this->assertNotCacheHeaders([$header], $result);
}

public function testPersistentSessionCookieIncludesExpiration()
{
$session = new Session(['foo' => 'bar'], 'identifier');
$response = new Response();
$persistence = new CacheSessionPersistence(
$this->cachePool->reveal(),
'test',
'/',
'nocache',
600, // expiry
time(),
true // mark session cookie as persistent
);

$cacheItem = $this->prophesize(CacheItemInterface::class);
$cacheItem->set(['foo' => 'bar'])->shouldBeCalled();
$cacheItem->expiresAfter(Argument::type('int'))->shouldBeCalled();
$this->cachePool
->getItem('identifier')
->will([$cacheItem, 'reveal']);
$this->cachePool->save(Argument::that([$cacheItem, 'reveal']))->shouldBeCalled();

$result = $persistence->persistSession($session, $response);

$this->assertNotSame($response, $result);
$this->assertCookieMaxAgeMirrorsExpiry(600, $result);
}
}

0 comments on commit 9caf853

Please sign in to comment.