Skip to content

Commit

Permalink
Introduce persistent cookie jar
Browse files Browse the repository at this point in the history
  • Loading branch information
GeLoLabs committed Aug 24, 2014
1 parent 804741d commit f90ed76
Show file tree
Hide file tree
Showing 18 changed files with 938 additions and 49 deletions.
40 changes: 40 additions & 0 deletions doc/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,43 @@ $cookies = iterator_to_array($cookieJar);

Be aware that when you access cookies, the cookie jar clears expired cookies before serving them.

#### Persistent cookie jar

The persistent cookie jar is described by the `Ivory\HttpAdapter\Event\Cookie\PersistentCookieJarInterface` and its
default implementation is the `Ivory\HttpAdapter\Event\Cookie\AbstractPersistentCookieJar`. Basically, it allows you
to load/save cookies from/to somewhere. All persistent cookie jars share the following API:

``` php
// Loads the cookie jar from the underlying resource
$cookieJar->load();

// Saves the cookie jar on the underlying resource
$cookieJar->save();
```

Additionally, the persistent cookie jar will automatically try to load it when it is instantiated and will save it when
it is destroyed.

##### File cookie jar

The file cookie jar is a persistent cookie jar which stores/retrieves cookies from a file. To use it:

``` php
use Ivory\HttpAdapter\Event\Cookie\FileCookieJar;

$cookieJar = new FileCookieJar('path/to/the/file');
```

##### Session cookie jar

The session cookie jar is a persistent cookie jar which stores/retrieves cookies from the session. To use it:

``` php
use Ivory\HttpAdapter\Event\Cookie\SessionCookieJar;

$cookieJar = new SessionCookieJar('session_key');
```

#### Cookie factory

As already explained, the cookie factory is defined by the `Ivory\HttpAdapter\Event\Cookie\CookieFactoryInterface`
Expand Down Expand Up @@ -530,6 +567,9 @@ $expired = $cookie->isExpired();

$createdAt = $cookie->getCreatedAt();
$cookie->setCreatedAt($createdAt);

$array = $cookie->toArray();
$string = (string) $cookie;
```

All attribute names are described by the `Ivory\HttpAdapter\Event\Cookie\Cookie::ATTR_*` constants. Additionally, you
Expand Down
76 changes: 76 additions & 0 deletions src/Ivory/HttpAdapter/Event/Cookie/AbstractPersistentCookieJar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

/*
* This file is part of the Ivory Http Adapter package.
*
* (c) Eric GELOEN <geloen.eric@gmail.com>
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code.
*/

namespace Ivory\HttpAdapter\Event\Cookie;

/**
* Abstract persistent cookie jar.
*
* @author GeLo <geloen.eric@gmail.com>
*/
abstract class AbstractPersistentCookieJar extends CookieJar implements PersistentCookieJarInterface
{
/**
* Creates a persistent cookie jar.
*
* @param \Ivory\HttpAdapter\Event\Cookie\CookieFactoryInterface|null $cookieFactory The cookie factory.
* @param boolean $load TRUE if it should load the cookies else FALSE.
*/
public function __construct(CookieFactoryInterface $cookieFactory = null, $load = true)
{
parent::__construct($cookieFactory);

if ($load) {
$this->load();
}
}

/**
* Destructs the persistent cookie jar.
*/
public function __destruct()
{
$this->save();
}

/**
* {@inheritdoc}
*/
public function serialize()
{
return json_encode(array_map(function (CookieInterface $cookie) {
return $cookie->toArray();
}, $this->getCookies()));
}

/**
* {@inheritdoc}
*/
public function unserialize($serialized)
{
$data = json_decode($serialized, true);

if (empty($data)) {
$this->cookies = array();
} else {
$cookieFactory = $this->cookieFactory;

$this->cookies = array_map(function (array $cookie) use ($cookieFactory) {
return $cookieFactory->create(
$cookie['name'],
$cookie['value'],
$cookie['attributes'],
$cookie['created_at']
);
}, $data);
}
}
}
13 changes: 13 additions & 0 deletions src/Ivory/HttpAdapter/Event/Cookie/Cookie.php
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,19 @@ public function matchSecure(InternalRequestInterface $request)
return ($secure && $scheme === 'https') || (!$secure && (($scheme === 'http') || empty($scheme)));
}

/**
* {@inheritdoc}
*/
public function toArray()
{
return array(
'name' => $this->name,
'value' => $this->value,
'attributes' => $this->attributes,
'created_at' => $this->createdAt,
);
}

/**
* {@inheritdoc}
*/
Expand Down
7 changes: 7 additions & 0 deletions src/Ivory/HttpAdapter/Event/Cookie/CookieInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ public function matchPath(InternalRequestInterface $request);
*/
public function matchSecure(InternalRequestInterface $request);

/**
* Converts the cookie to array.
*
* @return array The converted cookie.
*/
public function toArray();

/**
* Converts the cookie to string.
*
Expand Down
82 changes: 82 additions & 0 deletions src/Ivory/HttpAdapter/Event/Cookie/FileCookieJar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

/*
* This file is part of the Ivory Http Adapter package.
*
* (c) Eric GELOEN <geloen.eric@gmail.com>
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code.
*/

namespace Ivory\HttpAdapter\Event\Cookie;

use Ivory\HttpAdapter\HttpAdapterException;

/**
* File cookie jar.
*
* @author GeLo <geloen.eric@gmail.com>
*/
class FileCookieJar extends AbstractPersistentCookieJar
{
/** @var string */
protected $file;

/**
* Creates a file cookie jar.
*
* @param string $file The file.
* @param \Ivory\HttpAdapter\Event\Cookie\CookieFactoryInterface|null $cookieFactory The cookie factory.
*/
public function __construct($file, CookieFactoryInterface $cookieFactory = null)
{
$this->setFile($file);

parent::__construct($cookieFactory, file_exists($file));
}

/**
* Gets the file.
*
* @return string The file.
*/
public function getFile()
{
return $this->file;
}

/**
* Sets the file.
*
* @param string $file The file.
*/
public function setFile($file)
{
$this->file = $file;
}

/**
* {@inheritdoc}
*/
public function load()
{
if (($data = @file_get_contents($this->file)) === false) {
$error = error_get_last();
throw HttpAdapterException::cannotLoadCookieJar($error['message']);
}

$this->unserialize($data);
}

/**
* {@inheritdoc}
*/
public function save()
{
if (@file_put_contents($this->file, $this->serialize()) === false) {
$error = error_get_last();
throw HttpAdapterException::cannotSaveCookieJar($error['message']);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

/*
* This file is part of the Ivory Http Adapter package.
*
* (c) Eric GELOEN <geloen.eric@gmail.com>
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code.
*/

namespace Ivory\HttpAdapter\Event\Cookie;

/**
* Persistent cookie jar.
*
* @author GeLo <geloen.eric@gmail.com>
*/
interface PersistentCookieJarInterface extends CookieJarInterface, \Serializable
{
/**
* Loads the cookie jar.
*
* @throws \Ivory\HttpAdapter\HttpAdapterException If an error occured.
*
* @return void No return value.
*/
public function load();

/**
* Saves the cookie jar.
*
* @throws \Ivory\HttpAdapter\HttpAdapterException If an error occured.
*
* @return void No return value.
*/
public function save();
}
72 changes: 72 additions & 0 deletions src/Ivory/HttpAdapter/Event/Cookie/SessionCookieJar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

/*
* This file is part of the Ivory Http Adapter package.
*
* (c) Eric GELOEN <geloen.eric@gmail.com>
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code.
*/

namespace Ivory\HttpAdapter\Event\Cookie;

/**
* Session cookie jar.
*
* @author GeLo <geloen.eric@gmail.com>
*/
class SessionCookieJar extends AbstractPersistentCookieJar
{
/** @var string */
protected $key;

/**
* Creates a session cookie jar.
*
* @param string $key The key.
* @param \Ivory\HttpAdapter\Event\Cookie\CookieFactoryInterface|null $cookieFactory The cookie factory.
*/
public function __construct($key, CookieFactoryInterface $cookieFactory = null)
{
$this->setKey($key);

parent::__construct($cookieFactory);
}

/**
* Gets the key.
*
* @return string The key.
*/
public function getKey()
{
return $this->key;
}

/**
* Sets the key.
*
* @param string $key The key.
*/
public function setKey($key)
{
$this->key = $key;
}

/**
* {@inheritdoc}
*/
public function load()
{
$this->unserialize(isset($_SESSION[$this->key]) ? $_SESSION[$this->key] : null);
}

/**
* {@inheritdoc}
*/
public function save()
{
$_SESSION[$this->key] = $this->serialize();
}
}
24 changes: 24 additions & 0 deletions src/Ivory/HttpAdapter/HttpAdapterException.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,30 @@ public static function cannotFetchUrl($url, $adapter, $error)
));
}

/**
* Gets the "CANNOT LOAD COOKIE JAR" exception.
*
* @param string $error The error.
*
* @return \Ivory\HttpAdapter\HttpAdapterException The "CANNOT LOAD COOKIE JAR" exception.
*/
public static function cannotLoadCookieJar($error)
{
return new self(sprintf('An error occurred when loading the cookie jar ("%s").', $error));
}

/**
* Gets the "CANNOT SAVE COOKIE JAR" exception.
*
* @param string $error The error.
*
* @return \Ivory\HttpAdapter\HttpAdapterException The "CANNOT SAVE COOKIE JAR" exception.
*/
public static function cannotSaveCookieJar($error)
{
return new self(sprintf('An error occurred when saving the cookie jar ("%s").', $error));
}

/**
* Gets the "DOES NOT SUPPORT BODY" exception.
*
Expand Down

0 comments on commit f90ed76

Please sign in to comment.