[
  {
    "path": ".github/workflows/continuous-integration.yml",
    "content": "name: Continuous Integration\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        operating-system:\n          - ubuntu-latest\n        php-version:\n          - '8.1'\n          - '8.2'\n          - '8.3'\n          - '8.4'\n          - '8.5'\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Setup PHP ${{ matrix.php-version }}\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php-version }}\n          coverage: pcov\n          tools: none\n          ini-values: assert.exception=1, zend.assertions=1\n\n      - name: Get composer cache directory\n        id: composer-cache\n        run: echo \"dir=$(composer config cache-files-dir)\" >> $GITHUB_OUTPUT\n\n      - name: Cache dependencies\n        uses: actions/cache@v4\n        with:\n          path: ${{ steps.composer-cache.outputs.dir }}\n          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}\n          restore-keys: ${{ runner.os }}-composer-\n\n      - name: Install dependencies\n        run: composer install --no-interaction --prefer-dist\n\n      - name: Run test suite\n        run: ./vendor/bin/phpunit --coverage-clover=coverage.xml\n\n      - name: Upload coverage report\n        uses: codecov/codecov-action@v5\n        with:\n          fail_ci_if_error: false\n"
  },
  {
    "path": ".gitignore",
    "content": "/tests/container/vendor\n/tests/container/composer.*\n/composer.lock\n/vendor\n/.phpunit.cache\n"
  },
  {
    "path": ".scrutinizer.yml",
    "content": "build:\n  image: default-jammy\n  environment:\n    php: 8.4\n  nodes:\n    analysis:\n      tests:\n        override:\n          - php-scrutinizer-run\n\nfilter:\n  paths: [\"src/*\"]\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# CHANGELOG\n\n## 6.0.0\n\n- PHP 8.1+ is now required.\n- (CHG) Add `string` type declarations to `$key` parameters in `Segment`, `SegmentInterface` to fix PHP 8.5 deprecation of null as array offset.\n- (CHG) Change `remove()` parameter to `?string $key = null` in `Segment` and `SegmentInterface`.\n\n## 4.0.0\n\n- PHP 7.2+ is now required.\n- Github CI workflow added.\n- Scrutinizer CI updated.\n- MIT License.\n\n## 2.1.0\n\n- (ADD) Add support for hash_equals() in CsrfToken::isValid()\n- (ADD) Add support for random_bytes() in Randval::generate()\n- (TST) Update tests and test support files\n- (DOC) Update license year and remove branch alias\n\n## 2.0.1\n\nThis release modifies the testing structure and updates other support files.\n\n\n## 2.0.0\n\nThis is the first stable release of Aura.Session 2.0.0.\n\n- Fix #23\n\n- Merge pull request #33 from harikt/issue-23.\n\n- Merge pull request #35 from iansltx/php7-compat: Fix FakeSessionHandler::write() (fixes PHP7 tests)\n\n- Merge pull request #36 from fiahfy/spike: fix param type\n\n- Merge pull request #37 from tomkyle/develop-2: Removed redundant paragraph\n\n- Merge pull request #38 from tomkyle/develop-2: Clarified parameter descriptions\n\n- Updated documentation and support files.\n\n## 2.0.0-beta2\n\n- TST: Update testing structure, use plain old PHPUnit for tests\n\n- CHG: Use new service naming rules\n\n- CHG: Disable auto-resolve for container tests\n\n## 2.0.0-beta1\n\nFirst 2.0.0 beta release.\n\n## 1.0.2\n\nHygiene release.\n\n- Fix #8 related to unit tests failing because of ini_set values. Thanks @mindplay-dk\n\n- Merge pull request #12 from harikt/v2config, adds v2 config files.\n\n## 1.0.1\n\n- [CHG] Manager::destroy() now checks whether the session is started; if not,\n  starts it, and then destroys.  This is because sessions are lazy-loading.\n\n- [DOC] Add PHP 5.5 to the Travis build and update docs\n\n## 1.0.0\n\nThere are BC breaks in this release, but it's a Google beta, so ...\n\n- [SEC] Based on conversation at\n  http://www.eschrade.com/page/generating-secure-cross-site-request-forgery-tokens-csrf/\n  start using openssl and mcrypt for CSRF tokens instead of mt_rand().\n\n- [NEW] SegmentInterface, Randval, RandvalInterface.\n\n- [BRK] The Manager now requires $_COOKIE as its third param to Manager.\n\n- [CHG] Segments now lazy-load themselves. On reads, they will reactivate an\n  available session, but will not start a new one. On writes, they will\n  reactivate an available session, or start a new one if one is not available.\n  This means that creating a segment object no longer starts a session; you\n  have to read from or write to one for the session to start.\n\n- [BRK] Renamed Manager::isActive() to isAvailable(), to differentiate from\n  PHP_SESSION_ACTIVE. ( Previously, isActive() only told you if a session had\n  started, not if one was available to be activated.)\n\n- [CHG] Manager::getSegment() no longer starts a session\n\n- [CHG] Manager::isStarted() now checks getStatus() for PHP_SESSION_ACTIVE\n  instead of session_id().\n\n- [CHG] Segment::__get() no longer returns a reference\n\n- [BRK] Renamed Manager::getSegment() to newSegment()\n\n- [CHG] Manager no longer retains session segments\n\n- [CHG] Various typo and doc fixes by Akihito Koriyama\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nWe are happy to review any contributions you want to make. When contributing, please follow the rules outlined at <http://auraphp.com/contributing>.\n\nThe time between submitting a contribution and its review one may be extensive; do not be discouraged if there is not immediate feedback.\n\nThanks!\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2011-2022, Aura for PHP\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Aura Session\n\nProvides session management functionality, including lazy session starting,\nsession segments, next-request-only (\"flash\") values, and CSRF tools.\n\n## Foreword\n\n### Installation\n\nThis library requires PHP 8.1 or later. It has been tested on PHP 8.1-8.5. We recommend using the latest available version of PHP as a matter of principle. It has no userland dependencies.\n\nIt is installable and autoloadable via Composer as [aura/session](https://packagist.org/packages/aura/session).\n\n### Quality\n\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/auraphp/Aura.Session/badges/quality-score.png?b=4.x)](https://scrutinizer-ci.com/g/auraphp/Aura.Session/?branch=4.x)\n[![codecov](https://codecov.io/gh/auraphp/Aura.Session/branch/4.x/graph/badge.svg)](https://codecov.io/gh/auraphp/Aura.Session)\n[![Continuous Integration](https://github.com/auraphp/Aura.Session/actions/workflows/continuous-integration.yml/badge.svg?branch=4.x)](https://github.com/auraphp/Aura.Session/actions/workflows/continuous-integration.yml)\n\nTo run the unit tests at the command line, issue `composer install` and then `phpunit` at the package root. This requires [Composer](http://getcomposer.org/) to be available as `composer`, and [PHPUnit](http://phpunit.de/manual/) to be available as `phpunit`.\n\nThis library attempts to comply with [PSR-1][], [PSR-12][], and [PSR-4][]. If\nyou notice compliance oversights, please send a patch via pull request.\n\n[PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md\n[PSR-12]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-12-extended-coding-style-guide.md\n[PSR-4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md\n\n### Community\n\nTo ask questions, provide feedback, or otherwise communicate with the Aura community, please join our [Google Group](http://groups.google.com/group/auraphp) or follow [@auraphp on X](https://x.com/auraphp).\n\n\n## Documentation\n\nThis package is fully documented [here](./docs/getting-started.md).\n"
  },
  {
    "path": "codecov.yml",
    "content": "comment: false\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"aura/session\",\n    \"type\": \"library\",\n    \"description\": \"Provides session management functionality, including lazy session starting, session segments, next-request-only (\\\"flash\\\") values, and CSRF tools.\",\n    \"keywords\": [\n        \"session\",\n        \"sessions\",\n        \"flash\",\n        \"flash message\",\n        \"csrf\"\n    ],\n    \"homepage\": \"https://github.com/auraphp/Aura.Session\",\n    \"license\": \"MIT\",\n    \"authors\": [\n        {\n            \"name\": \"Aura.Session Contributors\",\n            \"homepage\": \"https://github.com/auraphp/Aura.Session/contributors\"\n        }\n    ],\n    \"require\": {\n        \"php\": \"^8.1\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"Aura\\\\Session\\\\\": \"src/\"\n        }\n    },\n    \"suggest\": {\n        \"ext-openssl\": \"OpenSSL generates the best secure CSRF tokens.\"\n    },\n    \"require-dev\": {\n        \"phpunit/phpunit\": \"^10.5 || ^11.0\"\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"Aura\\\\Session\\\\\": \"tests/\"\n        }\n    }\n}\n"
  },
  {
    "path": "docs/_bookdown.json",
    "content": "{\n    \"title\": \"Aura.Session\",\n    \"content\": [\n        \"getting-started.md\"\n    ]\n}"
  },
  {
    "path": "docs/getting-started.md",
    "content": "## Getting Started\n\n### Instantiation\n\nThe easiest way to get started is to use the _SessionFactory_ to create a _Session_ manager object.\n\n```php\n<?php\n$session_factory = new \\Aura\\Session\\SessionFactory;\n$session = $session_factory->newInstance($_COOKIE);\n?>\n```\n\nWe can then use the _Session_ instance to create _Segment_ objects to manage session values and flashes. (In general, we should not need to manipulate the _Session_ manager directly -- we will work mostly with _Segment_ objects.)\n\n### Segments\n\nIn normal PHP, we keep session values in the `$_SESSION` array. However, when different libraries and projects try to modify the same keys, the resulting conflicts can result in unexpected behavior. To resolve this, we use _Segment_ objects. Each _Segment_ addresses a named key within the `$_SESSION` array for deconfliction purposes.\n\nFor example, if we get a _Segment_  for `Vendor\\Package\\ClassName`, that _Segment_ will contain a reference to `$_SESSION['Vendor\\Package\\ClassName']`. We can then `set()` and `get()` values on the _Segment_, and the values will reside in an array under that reference.\n\n```php\n<?php\n// get a _Segment_ object\n$segment = $session->getSegment('Vendor\\Package\\ClassName');\n\n// try to get a value from the segment;\n// if it does not exist, return an alternative value\necho $segment->get('foo'); // null\necho $segment->get('baz', 'not set'); // 'not set'\n\n// set some values on the segment\n$segment->set('foo', 'bar');\n$segment->set('baz', 'dib');\n\n// the $_SESSION array is now:\n// $_SESSION = array(\n//      'Vendor\\Package\\ClassName' => array(\n//          'foo' => 'bar',\n//          'baz' => 'dib',\n//      ),\n// );\n\n// try again to get a value from the segment\necho $segment->get('foo'); // 'bar'\n\n// because the segment is a reference to $_SESSION, we can modify\n// the superglobal directly and the segment values will also change\n$_SESSION['Vendor\\Package\\ClassName']['zim'] = 'gir'\necho $segment->get('zim'); // 'gir'\n?>\n```\n\nThe benefit of a session segment is that we can deconflict the keys in the\n`$_SESSION` superglobal by using class names (or some other unique name) for\nthe segment names. With segments, different packages can use the `$_SESSION`\nsuperglobal without stepping on each other's toes.\n\nTo clear all the values on a _Segment_, use the `clear()` method.\n\n### Flash Values\n\n_Segment_ values persist until the session is cleared or destroyed. However, sometimes it is useful to set a value that propagates only through the next request, and is then discarded. These are called \"flash\" values.\n\n#### Setting And Getting Flash Values\n\nTo set a flash value on a _Segment_, use the `setFlash()` method.\n\n```php\n<?php\n$segment = $session->getSegment('Vendor\\Package\\ClassName');\n$segment->setFlash('message', 'Hello world!');\n?>\n```\n\nThen, in subsequent requests, we can read the flash value using `getFlash()`:\n\n```php\n<?php\n$segment = $session->getSegment('Vendor\\Package\\ClassName');\n$message = $segment->getFlash('message'); // 'Hello world!'\n?>\n```\n\n> N.b. As with `get()`, we can provide an alternative value if the flash key does not exist. For example, `getFlash('foo', 'not set')` will return 'not set' if there is no 'foo' key available.\n\nUsing `setFlash()` makes the flash value available only in the *next* request, not the current one. To make the flash value available immediately as well as in the next request, use `setFlashNow($key, $val)`.\n\nUsing `getFlash()` returns only the values that are available now from having been set in the previous request. To read a value that will be available in the next request, use `getFlashNext($key, $alt)`.\n\n#### Keeping and Clearing Flash Values\n\nSometimes we will want to keep the flash values in the current request for the next request.  We can do so on a per-segment basis by calling the _Segment_ `keepFlash()` method.\n\nSimilarly, we can clear flash values just for that _Segment_ with `clearFlash()` method.\n\n### Lazy Session Starting\n\nMerely instantiating the _Session_ manager and getting a _Segment_ from it does *not* call `session_start()`. Instead, `session_start()` occurs only in certain circumstances:\n\n- If we *read* from a _Segment_ (e.g. with `get()`) the _Session_ looks to see if a session cookie has already been set. If so, it will call `session_start()` to resume the previously-started session. If not, it knows there are no previously existing `$_SESSION` values, so it will not call `session_start()`.\n\n- If we *write* to a _Segment_ (e.g. with `set()`) the _Session_ will always call `session_start()`. This will resume a previous session if it exists, or start a new one if it does not.\n\nThis means we can create each _Segment_ at will, and `session_start()` will not be invoked until we actually interact with a _Segment_ in a particular way. This helps to conserve the resources involved in starting a session.\n\nOf course, we can force a session start or reactivation by calling the _Session_ `start()` method, but that defeats the purpose of lazy-loaded sessions.\n\n\n### Saving, Clearing, and Destroying Sessions\n\n> N.b.: These methods apply to all session data and flashes across all segments.\n\nTo save the session data and end its use during the current request, call the `commit()` method on the _Session_ manager:\n\n```php\n<?php\n$session->commit();\n?>\n```\n\n> N.b.: Per <http://php.net/manual/en/session.examples.basic.php>, \"Sessions\n> normally shutdown automatically when PHP is finished executing a script, but\n> can be manually shutdown using the session_write_close() function.\"\n> The `commit()` method is the equivalent of `session_write_close()`.\n\nTo clear all session data, but leave the session active during the current request, use the `clear()` method on the _Session_ manager.\n\n```php\n<?php\n$session->clear();\n?>\n```\n\nTo clear all flash values on a segment, use the `clearFlash()` method:\n\nTo clear the data *and* terminate the session for this and future requests, thereby destroying it completely, call the `destroy()` method:\n\n```php\n<?php\n$session->destroy(); // equivalent of session_destroy()\n?>\n```\n\nCalling `destroy()` will also delete the session cookie via `setcookie()`. If we have an alternative means by which we delete cookies, we should pass a callable as the second argument to the _SessionFactory_ method `newInstance()`. The callable should take three parameters: the cookie name, path, and domain.\n\n```php\n<?php\n// assume $response is a framework response object.\n// this will be used to delete the session cookie.\n$delete_cookie = function ($name, $path, $domain) use ($response) {\n    $response->cookies->delete($name, $path, $domain);\n}\n\n$session = $session_factory->newInstance($_COOKIE, $delete_cookie);\n?>\n```\n\n## Session Security\n\n### Session ID Regeneration\n\nAny time a user has a change in privilege (that is, gaining or losing access\nrights within a system) be sure to regenerate the session ID:\n\n```php\n<?php\n$session->regenerateId();\n?>\n```\n\n> N.b.: The `regenerateId()` method also regenerates the CSRF token value.\n\n### Cross-Site Request Forgery\n\nA \"cross-site request forgery\" is a security issue where the attacker, via\nmalicious JavaScript or other means, issues a request in-the-blind from a\nclient browser to a server where the user has already authenticated. The\nrequest *looks* valid to the server, but in fact is a forgery, since the user\ndid not actually make the request (the malicious JavaScript did).\n\n<http://en.wikipedia.org/wiki/Cross-site_request_forgery>\n\n#### Defending Against CSRF\n\nTo defend against CSRF attacks, server-side logic should:\n\n1. Place a token value unique to each authenticated user session in each form;\n   and\n\n2. Check that all incoming POST/PUT/DELETE (i.e., \"unsafe\") requests contain\n   that value.\n\n> N.b.: If our application uses GET requests to modify resources (which\n> incidentally is an improper use of GET), we should also check for CSRF on\n> GET requests from authenticated users.\n\nFor this example, the form field name will be `__csrf_value`. In each form\nwe want to protect against CSRF, we use the session CSRF token value for that\nfield:\n\n```php\n<?php\n/**\n * @var Vendor\\Package\\User $user A user-authentication object.\n * @var Aura\\Session\\Session $session A session management object.\n */\n?>\n<form method=\"post\">\n\n    <?php if ($user->auth->isValid()) {\n        $csrf_value = $session->getCsrfToken()->getValue();\n        echo '<input type=\"hidden\" name=\"__csrf_value\" value=\"'\n           . htmlspecialchars($csrf_value, ENT_QUOTES, 'UTF-8')\n           . '\"></input>';\n    } ?>\n\n    <!-- other form fields -->\n\n</form>\n```\n\nWhen processing the request, check to see if the incoming CSRF token is valid\nfor the authenticated user:\n\n```php\n<?php\n/**\n * @var Vendor\\Package\\User $user A user-authentication object.\n * @var Aura\\Session\\Session $session A session management object.\n */\n\n$unsafe = $_SERVER['REQUEST_METHOD'] == 'POST'\n       || $_SERVER['REQUEST_METHOD'] == 'PUT'\n       || $_SERVER['REQUEST_METHOD'] == 'DELETE';\n\nif ($unsafe && $user->auth->isValid()) {\n    $csrf_value = $_POST['__csrf_value'];\n    $csrf_token = $session->getCsrfToken();\n    if (! $csrf_token->isValid($csrf_value)) {\n        echo \"This looks like a cross-site request forgery.\";\n    } else {\n        echo \"This looks like a valid request.\";\n    }\n} else {\n    echo \"CSRF attacks only affect unsafe requests by authenticated users.\";\n}\n?>\n```\n\n> Note : By default the above code only works for single form. If you need\nmultiple tokens, pass different keys to `getValue` and `isValid` methods.\nFor examples please look into the unit test class `CsrfTokenTest`.\n\n#### CSRF Value Generation\n\nFor a CSRF token to be useful, its random value must be cryptographically\nsecure. Aura.Session comes with a `Randval` class that implements a\n`RandvalInterface`, and uses default `random_bytes` to generate a\nrandom value. If you are not satisified,\nyou can create your own implementation of the `RandvalInterface`.\n\n### Session Lifetime\n\nWe can set the session lifetime to as long (or as short) as we like using the `setCookieParams` on _Session_ object. The lifetime is in seconds. To set the session cookie lifetime to two weeks:\n\n```\n<?php\n$session->setCookieParams(array('lifetime' => '1209600'));\n?>\n```\n\n> N.b: The `setCookieParams` method calls [session_set_cookie_params](http://php.net/session_set_cookie_params) internally. Thus, you need to call `setCookieParams` for every request and before session_start() is called.\n"
  },
  {
    "path": "phpunit.php",
    "content": "<?php\nerror_reporting(E_ALL);\n$autoloader = __DIR__ . '/vendor/autoload.php';\nif (! file_exists($autoloader)) {\n    echo \"Composer autoloader not found: $autoloader\" . PHP_EOL;\n    echo \"Please issue 'composer install' and try again.\" . PHP_EOL;\n    exit(1);\n}\nrequire $autoloader;\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"vendor/phpunit/phpunit/phpunit.xsd\"\n         bootstrap=\"phpunit.php\"\n         colors=\"true\"\n         cacheDirectory=\".phpunit.cache\">\n    <testsuites>\n        <testsuite name=\"aura/session\">\n            <directory>tests</directory>\n        </testsuite>\n    </testsuites>\n    <source>\n        <include>\n            <directory suffix=\".php\">src</directory>\n        </include>\n    </source>\n</phpunit>\n"
  },
  {
    "path": "src/CsrfToken.php",
    "content": "<?php\n/**\n *\n * This file is part of Aura for PHP.\n *\n * @license http://opensource.org/licenses/mit-license.php MIT\n *\n */\nnamespace Aura\\Session;\n\n/**\n *\n * Cross-site request forgery token tools.\n *\n * @package Aura.Session\n *\n */\nclass CsrfToken\n{\n    /**\n     *\n     * A cryptographically-secure random value generator.\n     *\n     * @var RandvalInterface\n     *\n     */\n    protected $randval;\n\n    /**\n     *\n     * Session segment for values in this class.\n     *\n     * @var SegmentInterface\n     *\n     */\n    protected $segment;\n\n    /**\n     *\n     * Constructor.\n     *\n     * @param SegmentInterface $segment A segment for values in this class.\n     *\n     * @param RandvalInterface $randval A cryptographically-secure random\n     * value generator.\n     *\n     */\n    public function __construct(SegmentInterface $segment, RandvalInterface $randval)\n    {\n        $this->segment = $segment;\n        $this->randval = $randval;\n    }\n\n    /**\n     *\n     * Checks whether an incoming CSRF token value is valid.\n     *\n     * @param string $value The incoming token value.\n     *\n     * @param string $key  A string key name which session value is saved.\n     *\n     * @return bool True if valid, false if not.\n     *\n     */\n    public function isValid($value, $key = 'value')\n    {\n        return hash_equals($value, $this->getValue($key));\n    }\n\n    /**\n     *\n     * Gets the value of the outgoing CSRF token.\n     *\n     * @param string $key  A string key name which session value is saved.\n     *\n     * @return string\n     *\n     */\n    public function getValue($key = 'value')\n    {\n        if ($this->segment->get($key) == null ) {\n            $this->regenerateValue($key);\n        }\n\n        return $this->segment->get($key);\n    }\n\n    /**\n     *\n     * Regenerates the value of the outgoing CSRF token.\n     *\n     * @param string $key  A string key name which session value is saved.\n     *\n     * @return string\n     *\n     */\n    public function regenerateValue($key = 'value')\n    {\n        $hash = hash('sha512', $this->randval->generate());\n        $this->segment->set($key, $hash);\n\n        return $this->segment->get($key);\n    }\n\n\n    /**\n     * Regenerates all csrf tokens\n     *\n     * @return void\n     */\n    public function regenerateAllKeyValues()\n    {\n        $segment = $this->segment->getSegment();\n\n        if ($segment) {\n            foreach ($segment as $key => $value) {\n                $this->regenerateValue($key);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/CsrfTokenFactory.php",
    "content": "<?php\n/**\n *\n * This file is part of Aura for PHP.\n *\n * @license http://opensource.org/licenses/mit-license.php MIT\n *\n */\nnamespace Aura\\Session;\n\n/**\n *\n * A factory to create CSRF token objects.\n *\n * @package Aura.Session\n *\n */\nclass CsrfTokenFactory\n{\n    /**\n     *\n     * A cryptographically-secure random value generator.\n     *\n     * @var RandvalInterface\n     *\n     */\n    protected $randval;\n\n    /**\n     *\n     * Constructor.\n     *\n     * @param RandvalInterface $randval A cryptographically-secure random\n     * value generator.\n     *\n     */\n    public function __construct(RandvalInterface $randval)\n    {\n        $this->randval = $randval;\n    }\n\n    /**\n     *\n     * Creates a CsrfToken object.\n     *\n     * @param Session $session The session manager.\n     *\n     * @return CsrfToken\n     *\n     */\n    public function newInstance(Session $session)\n    {\n        $segment = $session->getSegment('Aura\\Session\\CsrfToken');\n        return new CsrfToken($segment, $this->randval);\n    }\n}\n"
  },
  {
    "path": "src/Exception/SessionAlreadyStarted.php",
    "content": "<?php\n\nnamespace Aura\\Session\\Exception;\n\nuse Aura\\Session\\Exception;\n\nclass SessionAlreadyStarted extends Exception\n{\n}\n"
  },
  {
    "path": "src/Exception.php",
    "content": "<?php\n/**\n *\n * This file is part of Aura for PHP.\n *\n * @license http://opensource.org/licenses/mit-license.php MIT\n *\n */\nnamespace Aura\\Session;\n\n/**\n *\n * Package-level exception.\n *\n * @package Aura.Session\n *\n */\nclass Exception extends \\Exception\n{\n}\n"
  },
  {
    "path": "src/Phpfunc.php",
    "content": "<?php\n/**\n *\n * This file is part of Aura for PHP.\n *\n * @license http://opensource.org/licenses/mit-license.php MIT\n *\n */\nnamespace Aura\\Session;\n\n/**\n *\n * Intercept calls to PHP functions.\n *\n * @package Aura.Session\n *\n */\nclass Phpfunc\n{\n    /**\n     *\n     * Magic call to intercept any function pass to it.\n     *\n     * @param string $func The function to call.\n     *\n     * @param array $args Arguments passed to the function.\n     *\n     * @return mixed The result of the function call.\n     *\n     */\n    public function __call($func, $args)\n    {\n        return call_user_func_array($func, $args);\n    }\n}\n"
  },
  {
    "path": "src/Randval.php",
    "content": "<?php\n/**\n *\n * This file is part of Aura for PHP.\n *\n * @license http://opensource.org/licenses/mit-license.php MIT\n *\n */\nnamespace Aura\\Session;\n\nuse Aura\\Session\\Exception;\n\n/**\n *\n * Generates cryptographically-secure random values.\n *\n * @package Aura.Session\n *\n */\nclass Randval implements RandvalInterface\n{\n    /**\n     *\n     * Returns a cryptographically secure random value.\n     *\n     * @return string\n     *\n     */\n    public function generate()\n    {\n        return random_bytes(32);\n    }\n}\n"
  },
  {
    "path": "src/RandvalInterface.php",
    "content": "<?php\n/**\n *\n * This file is part of Aura for PHP.\n *\n * @license http://opensource.org/licenses/mit-license.php MIT\n *\n */\nnamespace Aura\\Session;\n\n/**\n *\n * Interface for generating cryptographically-secure random values.\n *\n * @package Aura.Session\n *\n */\ninterface RandvalInterface\n{\n    /**\n     *\n     * Returns a cryptographically secure random value.\n     *\n     * @return string\n     *\n     */\n    public function generate();\n}\n"
  },
  {
    "path": "src/Segment.php",
    "content": "<?php\n/**\n *\n * This file is part of Aura for PHP.\n *\n * @license http://opensource.org/licenses/mit-license.php MIT\n *\n */\nnamespace Aura\\Session;\n\n/**\n *\n * A session segment; lazy-loads from the session.\n *\n * @package Aura.Session\n *\n */\nclass Segment implements SegmentInterface\n{\n    /**\n     *\n     * The session manager.\n     *\n     * @var Session\n     *\n     */\n    protected $session;\n\n    /**\n     *\n     * The segment name.\n     *\n     * @var string\n     *\n     */\n    protected $name;\n\n    /**\n     *\n     * Constructor.\n     *\n     * @param Session $session The session manager.\n     *\n     * @param string $name The segment name.\n     *\n     */\n    public function __construct(Session $session, $name)\n    {\n        $this->session = $session;\n        $this->name = $name;\n    }\n\n    /**\n     *\n     * Returns the value of a key in the segment.\n     *\n     * @param string $key The key in the segment.\n     *\n     * @param mixed $alt An alternative value to return if the key is not set.\n     *\n     * @return mixed\n     *\n     */\n    public function get(string $key, $alt = null)\n    {\n        $this->resumeSession();\n        return isset($_SESSION[$this->name][$key])\n             ? $_SESSION[$this->name][$key]\n             : $alt;\n    }\n\n    /**\n     *\n     * Returns the entire segment.\n     *\n     * @return mixed\n     *\n     */\n    public function getSegment()\n    {\n        $this->resumeSession();\n        return isset($_SESSION[$this->name])\n            ? $_SESSION[$this->name]\n            : null;\n    }\n\n    /**\n     *\n     * Sets the value of a key in the segment.\n     *\n     * @param string $key The key to set.\n     *\n     * @param mixed $val The value to set it to.\n     *\n     */\n    public function set(string $key, $val)\n    {\n        $this->resumeOrStartSession();\n        $_SESSION[$this->name][$key] = $val;\n    }\n\n    /**\n     *\n     * Clear all data from the segment.\n     *\n     * @return null\n     *\n     */\n    public function clear()\n    {\n        if ($this->resumeSession()) {\n            $_SESSION[$this->name] = array();\n        }\n    }\n\n\n    /**\n     * Remove a key from the segment, or remove the entire segment (including key) from the session\n     *\n     * @param string|null $key The key to remove, or null to clear the entire segment.\n     */\n    public function remove(?string $key = null) {\n        if (! $this->resumeSession()) {\n            return;\n        }\n        if ($key === null) {\n            unset($_SESSION[$this->name]);\n            unset($_SESSION[Session::FLASH_NOW][$this->name]);\n            unset($_SESSION[Session::FLASH_NEXT][$this->name]);\n            return;\n        }\n        if (array_key_exists($key, $_SESSION[$this->name])) {\n            unset($_SESSION[$this->name][$key]);\n        }\n    }\n\n    /**\n     *\n     * Sets a flash value for the *next* request.\n     *\n     * @param string $key The key for the flash value.\n     *\n     * @param mixed $val The flash value itself.\n     *\n     */\n    public function setFlash(string $key, $val)\n    {\n        $this->resumeOrStartSession();\n        $_SESSION[Session::FLASH_NEXT][$this->name][$key] = $val;\n    }\n\n    /**\n     *\n     * Gets the flash value for a key in the *current* request.\n     *\n     * @param string $key The key for the flash value.\n     *\n     * @param mixed $alt An alternative value to return if the key is not set.\n     *\n     * @return mixed The flash value itself.\n     *\n     */\n    public function getFlash(string $key, $alt = null)\n    {\n        $this->resumeSession();\n        return isset($_SESSION[Session::FLASH_NOW][$this->name][$key])\n             ? $_SESSION[Session::FLASH_NOW][$this->name][$key]\n             : $alt;\n    }\n\n    /**\n     *\n     * Clears flash values for *only* the next request.\n     *\n     * @return null\n     *\n     */\n    public function clearFlash()\n    {\n        if ($this->resumeSession()) {\n            $_SESSION[Session::FLASH_NEXT][$this->name] = array();\n        }\n    }\n\n    /**\n     *\n     * Gets the flash value for a key in the *next* request.\n     *\n     * @param string $key The key for the flash value.\n     *\n     * @param mixed $alt An alternative value to return if the key is not set.\n     *\n     * @return mixed The flash value itself.\n     *\n     */\n    public function getFlashNext(string $key, $alt = null)\n    {\n        $this->resumeSession();\n        return isset($_SESSION[Session::FLASH_NEXT][$this->name][$key])\n             ? $_SESSION[Session::FLASH_NEXT][$this->name][$key]\n             : $alt;\n    }\n\n    /**\n     *\n     * Sets a flash value for the *next* request *and* the current one.\n     *\n     * @param string $key The key for the flash value.\n     *\n     * @param mixed $val The flash value itself.\n     *\n     */\n    public function setFlashNow(string $key, $val)\n    {\n        $this->resumeOrStartSession();\n        $_SESSION[Session::FLASH_NOW][$this->name][$key] = $val;\n        $_SESSION[Session::FLASH_NEXT][$this->name][$key] = $val;\n    }\n\n    /**\n     *\n     * Clears flash values for *both* the next request *and* the current one.\n     *\n     * @return null\n     *\n     */\n    public function clearFlashNow()\n    {\n        if ($this->resumeSession()) {\n            $_SESSION[Session::FLASH_NOW][$this->name] = array();\n            $_SESSION[Session::FLASH_NEXT][$this->name] = array();\n        }\n    }\n\n    /**\n     *\n     * Retains all the current flash values for the next request; values that\n     * already exist for the next request take precedence.\n     *\n     * @return null\n     *\n     */\n    public function keepFlash()\n    {\n        if ($this->resumeSession()) {\n            $_SESSION[Session::FLASH_NEXT][$this->name] = array_merge(\n                $_SESSION[Session::FLASH_NEXT][$this->name],\n                $_SESSION[Session::FLASH_NOW][$this->name]\n            );\n        }\n    }\n\n    /**\n     *\n     * Loads the segment only if the session has already been started, or if\n     * a session is available (in which case it resumes the session first).\n     *\n     * @return bool\n     *\n     */\n    protected function resumeSession()\n    {\n        if ($this->session->isStarted() || $this->session->resume()) {\n            $this->load();\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     *\n     * Sets the segment properties to $_SESSION references.\n     *\n     * @return null\n     *\n     */\n    protected function load()\n    {\n        if (! isset($_SESSION[$this->name])) {\n            $_SESSION[$this->name] = array();\n        }\n\n        if (! isset($_SESSION[Session::FLASH_NOW][$this->name])) {\n            $_SESSION[Session::FLASH_NOW][$this->name] = array();\n        }\n\n        if (! isset($_SESSION[Session::FLASH_NEXT][$this->name])) {\n            $_SESSION[Session::FLASH_NEXT][$this->name] = array();\n        }\n    }\n\n    /**\n     *\n     * Resumes a previous session, or starts a new one, and loads the segment.\n     *\n     * @return null\n     *\n     */\n    protected function resumeOrStartSession()\n    {\n        if (! $this->resumeSession()) {\n            $this->session->start();\n            $this->load();\n        }\n    }\n}\n"
  },
  {
    "path": "src/SegmentFactory.php",
    "content": "<?php\n/**\n *\n * This file is part of Aura for PHP.\n *\n * @license http://opensource.org/licenses/mit-license.php MIT\n *\n */\nnamespace Aura\\Session;\n\n/**\n *\n * A factory to create session segment objects.\n *\n * @package Aura.Session\n *\n */\nclass SegmentFactory\n{\n    /**\n     *\n     * Creates a session segment object.\n     *\n     * @param Session $session\n     * @param string  $name\n     *\n     * @return Segment\n     */\n    public function newInstance(Session $session, $name)\n    {\n        return new Segment($session, $name);\n    }\n}\n"
  },
  {
    "path": "src/SegmentInterface.php",
    "content": "<?php\n/**\n *\n * This file is part of Aura for PHP.\n *\n * @license http://opensource.org/licenses/mit-license.php MIT\n *\n */\nnamespace Aura\\Session;\n\n/**\n *\n * An interface for session segment objects.\n *\n * @package Aura.Session\n *\n */\ninterface SegmentInterface\n{\n    /**\n     *\n     * Returns the value of a key in the segment.\n     *\n     * @param string $key The key in the segment.\n     *\n     * @param mixed $alt An alternative value to return if the key is not set.\n     *\n     * @return mixed\n     *\n     */\n    public function get(string $key, $alt = null);\n\n    /**\n     *\n     * Returns the entire segment.\n     *\n     * @return mixed\n     *\n     */\n    public function getSegment();\n\n    /**\n     *\n     * Sets the value of a key in the segment.\n     *\n     * @param string $key The key to set.\n     *\n     * @param mixed $val The value to set it to.\n     *\n     */\n    public function set(string $key, $val);\n\n    /**\n     *\n     * Clear all data from the segment.\n     *\n     * @return null\n     *\n     */\n    public function clear();\n\n    /**\n     *\n     * Sets a flash value for the *next* request.\n     *\n     * @param string $key The key for the flash value.\n     *\n     * @param mixed $val The flash value itself.\n     *\n     */\n    public function setFlash(string $key, $val);\n\n    /**\n     *\n     * Gets the flash value for a key in the *current* request.\n     *\n     * @param string $key The key for the flash value.\n     *\n     * @param mixed $alt An alternative value to return if the key is not set.\n     *\n     * @return mixed The flash value itself.\n     *\n     */\n    public function getFlash(string $key, $alt = null);\n\n    /**\n     *\n     * Clears flash values for *only* the next request.\n     *\n     * @return null\n     *\n     */\n    public function clearFlash();\n\n    /**\n     *\n     * Gets the flash value for a key in the *next* request.\n     *\n     * @param string $key The key for the flash value.\n     *\n     * @param mixed $alt An alternative value to return if the key is not set.\n     *\n     * @return mixed The flash value itself.\n     *\n     */\n    public function getFlashNext(string $key, $alt = null);\n\n    /**\n     *\n     * Sets a flash value for the *next* request *and* the current one.\n     *\n     * @param string $key The key for the flash value.\n     *\n     * @param mixed $val The flash value itself.\n     *\n     */\n    public function setFlashNow(string $key, $val);\n\n    /**\n     *\n     * Clears flash values for *both* the next request *and* the current one.\n     *\n     * @return null\n     *\n     */\n    public function clearFlashNow();\n\n    /**\n     *\n     * Retains all the current flash values for the next request; values that\n     * already exist for the next request take precedence.\n     *\n     * @return null\n     *\n     */\n    public function keepFlash();\n\n    /**\n     * Remove a key from the segment, or remove the entire segment (including key) from the session\n     *\n     * @param string|null $key The key to remove, or null to clear the entire segment.\n     */\n    public function remove(?string $key = null);\n}\n"
  },
  {
    "path": "src/Session.php",
    "content": "<?php\n/**\n *\n * This file is part of Aura for PHP.\n *\n * @license http://opensource.org/licenses/mit-license.php MIT\n *\n */\nnamespace Aura\\Session;\n\nuse Aura\\Session\\Exception\\SessionAlreadyStarted;\n\n/**\n *\n * A central control point for new session segments, PHP session management\n * values, and CSRF token checking.\n *\n * @package Aura.Session\n *\n */\nclass Session\n{\n    /**\n     *\n     * Session key for the \"next\" flash values.\n     *\n     * @const string\n     *\n     */\n    const FLASH_NEXT = 'Aura\\Session\\Flash\\Next';\n\n    /**\n     *\n     * Session key for the \"current\" flash values.\n     *\n     * @const string\n     *\n     */\n    const FLASH_NOW = 'Aura\\Session\\Flash\\Now';\n\n    /**\n     *\n     * A session segment factory.\n     *\n     * @var SegmentFactory\n     *\n     */\n    protected $segment_factory;\n\n    /**\n     *\n     * The CSRF token for this session.\n     *\n     * @var CsrfToken\n     *\n     */\n    protected $csrf_token;\n\n    /**\n     *\n     * A CSRF token factory, for lazy-creating the CSRF token.\n     *\n     * @var CsrfTokenFactory\n     *\n     */\n    protected $csrf_token_factory;\n\n    /**\n     *\n     * Incoming cookies from the client, typically a copy of the $_COOKIE\n     * superglobal.\n     *\n     * @var array\n     *\n     */\n    protected $cookies;\n\n    /**\n     *\n     * Session cookie parameters.\n     *\n     * @var array\n     *\n     */\n    protected $cookie_params = array();\n\n    /**\n     *\n     * An object to intercept PHP function calls; this makes testing easier.\n     *\n     * @var Phpfunc\n     *\n     */\n    protected $phpfunc;\n\n    /**\n     *\n     * A callable to invoke when deleting the session cookie. The callable\n     * should have the signature ...\n     *\n     *      function ($cookie_name, $cookie_params)\n     *\n     * ... and return null.\n     *\n     * @var callable|null\n     *\n     * @see setDeleteCookie()\n     *\n     */\n    protected $delete_cookie;\n\n    /**\n     *\n     * Have the flash values been moved forward?\n     *\n     * @var bool\n     *\n     */\n    protected $flash_moved = false;\n\n    /**\n     *\n     * Constructor\n     *\n     * @param SegmentFactory $segment_factory A session segment factory.\n     *\n     * @param CsrfTokenFactory $csrf_token_factory A CSRF token factory.\n     *\n     * @param Phpfunc $phpfunc An object to intercept PHP function calls;\n     * this makes testing easier.\n     *\n     * @param array $cookies Optional: An array of cookies from the client, typically a\n     * copy of $_COOKIE. Empty array by default.\n     *\n     * @param callable|null $delete_cookie Optional: An alternative callable\n     * to invoke when deleting the session cookie. Defaults to `null`.\n     *\n     */\n    public function __construct(\n        SegmentFactory $segment_factory,\n        CsrfTokenFactory $csrf_token_factory,\n        Phpfunc $phpfunc,\n        array $cookies = array(),\n        $delete_cookie = null\n    ) {\n        $this->segment_factory    = $segment_factory;\n        $this->csrf_token_factory = $csrf_token_factory;\n        $this->phpfunc            = $phpfunc;\n        $this->cookies            = $cookies;\n\n        $this->setDeleteCookie($delete_cookie);\n\n        $this->cookie_params = $this->phpfunc->session_get_cookie_params();\n    }\n\n    /**\n     *\n     * Sets the delete-cookie callable.\n     *\n     * If parameter is `null`, the session cookie will be deleted using the\n     * traditional way, i.e. using an expiration date in the past.\n     *\n     * @param callable|null $delete_cookie The callable to invoke when deleting the\n     * session cookie.\n     *\n     */\n    public function setDeleteCookie($delete_cookie)\n    {\n        $this->delete_cookie = $delete_cookie;\n        if (! $this->delete_cookie) {\n            $phpfunc = $this->phpfunc;\n            $this->delete_cookie = function (\n                $name,\n                $params\n            ) use ($phpfunc) {\n                $phpfunc->setcookie(\n                    $name,\n                    '',\n                    time() - 42000,\n                    $params['path'],\n                    $params['domain']\n                );\n            };\n        }\n    }\n\n    /**\n     *\n     * Gets a new session segment instance by name. Segments with the same\n     * name will be different objects but will reference the same $_SESSION\n     * values, so it is possible to have two or more objects that share state.\n     * For good or bad, this a function of how $_SESSION works.\n     *\n     * @param string $name The name of the session segment, typically a\n     * fully-qualified class name.\n     *\n     * @return Segment New Segment instance.\n     *\n     */\n    public function getSegment($name)\n    {\n        return $this->segment_factory->newInstance($this, $name);\n    }\n\n    /**\n     *\n     * Is a session available to be resumed?\n     *\n     * @return bool\n     *\n     */\n    public function isResumable()\n    {\n        $name = $this->getName();\n        return isset($this->cookies[$name]);\n    }\n\n    /**\n     *\n     * Is the session already started?\n     *\n     * @return bool\n     *\n     */\n    public function isStarted()\n    {\n        $started = $this->phpfunc->session_status() === PHP_SESSION_ACTIVE;\n\n        // if the session was started externally, move the flash values forward\n        if ($started && ! $this->flash_moved) {\n            $this->moveFlash();\n        }\n\n        // done\n        return $started;\n    }\n\n    /**\n     *\n     * Starts a new or existing session.\n     *\n     * @return bool\n     *\n     */\n    public function start()\n    {\n        $result = $this->phpfunc->session_start();\n        if ($result && ! $this->flash_moved) {\n            $this->moveFlash();\n        }\n        return $result;\n    }\n\n    /**\n     *\n     * Moves the \"next\" flash values to the \"now\" values, thereby clearing the\n     * \"next\" values.\n     *\n     * @return null\n     *\n     */\n    protected function moveFlash()\n    {\n        if (! isset($_SESSION[Session::FLASH_NEXT])) {\n            $_SESSION[Session::FLASH_NEXT] = array();\n        }\n        $_SESSION[Session::FLASH_NOW] = $_SESSION[Session::FLASH_NEXT];\n        $_SESSION[Session::FLASH_NEXT] = array();\n        $this->flash_moved = true;\n    }\n\n    /**\n     *\n     * Resumes a session, but does not start a new one if there is no\n     * existing one.\n     *\n     * @return bool\n     *\n     */\n    public function resume()\n    {\n        if ($this->isStarted()) {\n            return true;\n        }\n\n        if ($this->isResumable()) {\n            return $this->start();\n        }\n\n        return false;\n    }\n\n    /**\n     *\n     * Clears all session variables across all segments.\n     *\n     * @return null\n     *\n     */\n    public function clear()\n    {\n        return $this->phpfunc->session_unset();\n    }\n\n    /**\n     *\n     * Writes session data from all segments and ends the session.\n     *\n     * @return null\n     *\n     */\n    public function commit()\n    {\n        return $this->phpfunc->session_write_close();\n    }\n\n    /**\n     *\n     * Destroys the session entirely.\n     *\n     * @return bool\n     *\n     * @see http://php.net/manual/en/function.session-destroy.php\n     *\n     */\n    public function destroy()\n    {\n        if (! $this->isStarted()) {\n            $this->start();\n        }\n\n        $name = $this->getName();\n        $params = $this->getCookieParams();\n        $this->clear();\n\n        $destroyed = $this->phpfunc->session_destroy();\n        if ($destroyed) {\n            call_user_func($this->delete_cookie, $name, $params);\n        }\n\n        return $destroyed;\n    }\n\n    /**\n     *\n     * Returns the CSRF token, creating it if needed (and thereby starting a\n     * session).\n     *\n     * @return CsrfToken\n     *\n     */\n    public function getCsrfToken()\n    {\n        if (! $this->csrf_token) {\n            $this->csrf_token = $this->csrf_token_factory->newInstance($this);\n        }\n\n        return $this->csrf_token;\n    }\n\n    // =======================================================================\n    //\n    // support and admin methods\n    //\n\n    /**\n     *\n     * Sets the session cache expire time.\n     *\n     * @param int $expire The expiration time in seconds.\n     *\n     * @return int\n     *\n     * @see session_cache_expire()\n     *\n     */\n    public function setCacheExpire($expire)\n    {\n        return $this->phpfunc->session_cache_expire($expire);\n    }\n\n    /**\n     *\n     * Gets the session cache expire time.\n     *\n     * @return int The cache expiration time in seconds.\n     *\n     * @see session_cache_expire()\n     *\n     */\n    public function getCacheExpire()\n    {\n        return $this->phpfunc->session_cache_expire();\n    }\n\n    /**\n     *\n     * Sets the session cache limiter value.\n     *\n     * @param string $limiter The limiter value.\n     *\n     * @return string\n     *\n     * @see session_cache_limiter()\n     *\n     */\n    public function setCacheLimiter($limiter)\n    {\n        return $this->phpfunc->session_cache_limiter($limiter);\n    }\n\n    /**\n     *\n     * Gets the session cache limiter value.\n     *\n     * @return string The limiter value.\n     *\n     * @see session_cache_limiter()\n     *\n     */\n    public function getCacheLimiter()\n    {\n        return $this->phpfunc->session_cache_limiter();\n    }\n\n    /**\n     *\n     * Sets the session cookie params.  Param array keys are:\n     *\n     * - `lifetime` : Lifetime of the session cookie, defined in seconds.\n     *\n     * - `path` : Path on the domain where the cookie will work.\n     *   Use a single slash ('/') for all paths on the domain.\n     *\n     * - `domain` : Cookie domain, for example 'www.php.net'.\n     *   To make cookies visible on all subdomains then the domain must be\n     *   prefixed with a dot like '.php.net'.\n     *\n     * - `secure` : If TRUE cookie will only be sent over secure connections.\n     *\n     * - `httponly` : If set to TRUE then PHP will attempt to send the httponly\n     *   flag when setting the session cookie.\n     *\n     * - `samesite` : Set if the cookie should be restricted to a first-party or same-site context.\n     *   Possible values are 'Lax', 'Strict' or 'None'.\n     *\n     * @param array $params The array of session cookie param keys and values.\n     *\n     * @return null\n     *\n     * @throws SessionAlreadyStarted\n     *\n     * @see session_set_cookie_params()\n     *\n     */\n    public function setCookieParams(array $params)\n    {\n        if ($this->isStarted()) {\n            throw new SessionAlreadyStarted();\n        }\n\n        $this->cookie_params = array_merge($this->cookie_params, $params);\n        $this->phpfunc->session_set_cookie_params($this->cookie_params);\n    }\n\n    /**\n     *\n     * Gets the session cookie params.\n     *\n     * @return array\n     *\n     */\n    public function getCookieParams()\n    {\n        return $this->cookie_params;\n    }\n\n    /**\n     *\n     * Gets the current session id.\n     *\n     * @return string\n     *\n     */\n    public function getId()\n    {\n        return $this->phpfunc->session_id();\n    }\n\n    /**\n     *\n     * Regenerates and replaces the current session id; also regenerates the\n     * CSRF token value if one exists.\n     *\n     * @return bool True if regeneration worked, false if not.\n     *\n     */\n    public function regenerateId()\n    {\n        $result = $this->phpfunc->session_regenerate_id(true);\n        if ($result && $this->csrf_token) {\n            $this->csrf_token->regenerateAllKeyValues();\n        }\n        return $result;\n    }\n\n    /**\n     *\n     * Sets the current session name.\n     *\n     * @param string $name The session name to use.\n     *\n     * @return string\n     *\n     * @see session_name()\n     *\n     */\n    public function setName($name)\n    {\n        return $this->phpfunc->session_name($name);\n    }\n\n    /**\n     *\n     * Returns the current session name.\n     *\n     * @return string\n     *\n     */\n    public function getName()\n    {\n        return $this->phpfunc->session_name();\n    }\n\n    /**\n     *\n     * Sets the session save path.\n     *\n     * @param string $path The new save path.\n     *\n     * @return string\n     *\n     * @see session_save_path()\n     *\n     */\n    public function setSavePath($path)\n    {\n        return $this->phpfunc->session_save_path($path);\n    }\n\n    /**\n     *\n     * Gets the session save path.\n     *\n     * @return string\n     *\n     * @see session_save_path()\n     *\n     */\n    public function getSavePath()\n    {\n        return $this->phpfunc->session_save_path();\n    }\n}\n"
  },
  {
    "path": "src/SessionFactory.php",
    "content": "<?php\n/**\n *\n * This file is part of Aura for PHP.\n *\n * @license http://opensource.org/licenses/mit-license.php MIT\n *\n */\nnamespace Aura\\Session;\n\n/**\n *\n * A factory to create a Session manager.\n *\n * @package Aura.Session\n *\n */\nclass SessionFactory\n{\n    /**\n     *\n     * Creates a new Session manager.\n     *\n     * @param array $cookies An array of cookie values, typically $_COOKIE.\n     *\n     * @param callable|null $delete_cookie Optional: An alternative callable\n     * to invoke when deleting the session cookie. Defaults to `null`.\n     *\n     * @return Session New Session manager instance\n     */\n    public function newInstance(array $cookies, $delete_cookie = null)\n    {\n        $phpfunc = new Phpfunc;\n        return new Session(\n            new SegmentFactory,\n            new CsrfTokenFactory(new Randval()),\n            $phpfunc,\n            $cookies,\n            $delete_cookie\n        );\n    }\n}\n"
  },
  {
    "path": "tests/CsrfTokenTest.php",
    "content": "<?php\nnamespace Aura\\Session;\n\nuse PHPUnit\\Framework\\Attributes\\RunTestsInSeparateProcesses;\nuse PHPUnit\\Framework\\TestCase;\n\n#[RunTestsInSeparateProcesses]\nclass CsrfTokenTest extends TestCase\n{\n    protected $session;\n\n    protected $csrf_token;\n\n    protected $name = __CLASS__;\n\n    protected $phpfunc;\n\n    protected function setUp(): void\n    {\n        $this->phpfunc = new Phpfunc;\n\n        $this->session = new Session(\n            new SegmentFactory,\n            new CsrfTokenFactory(new Randval()),\n            $this->phpfunc,\n            $_COOKIE\n        );\n    }\n\n    public function teardown(): void\n    {\n        session_unset();\n        if (session_id() !== '') {\n            session_destroy();\n        }\n    }\n\n    public function testLaziness()\n    {\n        $this->assertFalse($this->session->isStarted());\n        $token = $this->session->getCsrfToken();\n        $this->assertFalse($this->session->isStarted());\n        $token->getValue('__csrf');\n        $this->assertTrue($this->session->isStarted());\n    }\n\n    public function testGetAndRegenerateValue()\n    {\n        $token = $this->session->getCsrfToken();\n\n        $old = $token->getValue();\n        $this->assertNotEmpty($old);\n\n        $token->regenerateValue();\n        $new = $token->getValue();\n        $this->assertNotEquals($old, $new);\n    }\n\n    public function testIsValid()\n    {\n        $token = $this->session->getCsrfToken();\n        $value = $token->getValue();\n\n        $this->assertTrue($token->isValid($value));\n        $token->regenerateValue();\n        $this->assertFalse($token->isValid($value));\n    }\n\n    public function testDifferentTokens()\n    {\n        $this->assertFalse($this->session->isStarted());\n        $token = $this->session->getCsrfToken();\n\n        $value1 = $token->getValue('__csrf1');\n        $value2 = $token->getValue('__csrf2');\n        $value3 = $token->getValue('__csrf3');\n\n        $this->assertTrue($token->isValid($value1, '__csrf1'));\n        $this->assertTrue($token->isValid($value2, '__csrf2'));\n        $this->assertTrue($token->isValid($value3, '__csrf3'));\n\n        // After isValid call, the value stored in session will not be reset\n        $this->assertEquals($value3, $token->getValue('__csrf3'));\n\n        $this->assertNotEquals($value3, $token->regenerateValue('__csrf3'));\n    }\n}\n"
  },
  {
    "path": "tests/FakeSessionHandler.php",
    "content": "<?php\nnamespace Aura\\Session;\n\nuse SessionHandlerInterface;\n\n// a session handler that does nothing, for testing purposes only\nclass FakeSessionHandler implements SessionHandlerInterface\n{\n    public array $data = [];\n\n    public function close(): bool\n    {\n        return true;\n    }\n\n    public function destroy(string $session_id): bool\n    {\n        unset($this->data[$session_id]);\n        return true;\n    }\n\n    public function gc(int $maxlifetime): int|false\n    {\n        return 0;\n    }\n\n    public function open(string $save_path, string $session_id): bool\n    {\n        return true;\n    }\n\n    public function read(string $session_id): string|false\n    {\n        return $this->data[$session_id] ?? '';\n    }\n\n    public function write(string $session_id, string $session_data): bool\n    {\n        $this->data[$session_id] = $session_data;\n        return true;\n    }\n}\n"
  },
  {
    "path": "tests/Issue23Test.php",
    "content": "<?php\nnamespace Aura\\Session;\n\nuse PHPUnit\\Framework\\Attributes\\RunTestsInSeparateProcesses;\nuse PHPUnit\\Framework\\TestCase;\n\n#[RunTestsInSeparateProcesses]\nclass Issue23Test extends TestCase\n{\n    protected $session;\n\n    protected $segment;\n\n    protected $name = __CLASS__;\n\n    protected function setUp(): void\n    {\n        $this->session = $this->newSession();\n        $this->segment = $this->session->getSegment($this->name);\n    }\n\n    protected function newSession(array $cookies = array())\n    {\n        // start session earlier\n        session_start();\n        return new Session(\n            new SegmentFactory,\n            new CsrfTokenFactory(new Randval()),\n            new Phpfunc,\n            $cookies\n        );\n    }\n\n    public function testFlash()\n    {\n        // set a value\n        $this->segment->setFlash('foo', 'bar');\n        $expect = 'bar';\n        $this->assertSame($expect, $this->segment->getFlashNext('foo'));\n        $this->assertNull($this->segment->getFlash('foo'));\n\n        // set a value and make it available now\n        $this->segment->setFlashNow('baz', 'dib');\n        $expect = 'dib';\n        $this->assertSame($expect, $this->segment->getFlash('baz'));\n        $this->assertSame($expect, $this->segment->getFlashNext('baz'));\n\n        // clear the next values\n        $this->segment->clearFlash();\n        $this->assertNull($this->segment->getFlashNext('foo'));\n        $this->assertNull($this->segment->getFlashNext('baz'));\n        $expect = 'dib';\n        $this->assertSame($expect, $this->segment->getFlash('baz'));\n\n        // set some current values and make sure they get kept\n        $now =& $_SESSION[Session::FLASH_NOW][$this->name];\n        $now['foo'] = 'bar';\n        $now['baz'] = 'dib';\n        $this->segment->keepFlash();\n        $this->assertSame('bar', $this->segment->getFlashNext('foo'));\n        $this->assertSame('dib', $this->segment->getFlashNext('baz'));\n\n        // clear the current and future values\n        $this->segment->clearFlashNow();\n        $this->assertNull($this->segment->getFlash('foo'));\n        $this->assertNull($this->segment->getFlashNext('foo'));\n        $this->assertNull($this->segment->getFlash('baz'));\n        $this->assertNull($this->segment->getFlashNext('baz'));\n    }\n}\n"
  },
  {
    "path": "tests/SegmentTest.php",
    "content": "<?php\nnamespace Aura\\Session;\n\nuse PHPUnit\\Framework\\Attributes\\RunTestsInSeparateProcesses;\nuse PHPUnit\\Framework\\TestCase;\n\n#[RunTestsInSeparateProcesses]\nclass SegmentTest extends TestCase\n{\n    protected $session;\n\n    protected $segment;\n\n    protected $name = __CLASS__;\n\n    protected function setUp(): void\n    {\n        $this->session = $this->newSession();\n        $this->segment = $this->session->getSegment($this->name);\n    }\n\n    protected function newSession(array $cookies = array())\n    {\n        return new Session(\n            new SegmentFactory,\n            new CsrfTokenFactory(new Randval()),\n            new Phpfunc,\n            $cookies\n        );\n    }\n\n    protected function getValue($key = null)\n    {\n        if ($key) {\n            return $_SESSION[$this->name][$key];\n        } else {\n            return $_SESSION[$this->name];\n        }\n    }\n\n    protected function setValue($key, $val)\n    {\n        $_SESSION[$this->name][$key] = $val;\n    }\n\n    public function testMagicMethods()\n    {\n        $this->assertNull($this->segment->get('foo'));\n\n        $this->segment->set('foo', 'bar');\n        $this->assertSame('bar', $this->segment->get('foo'));\n        $this->assertSame('bar', $this->getValue('foo'));\n\n        $this->setValue('foo', 'zim');\n        $this->assertSame('zim', $this->segment->get('foo'));\n    }\n\n    public function testClear()\n    {\n        $this->segment->set('foo', 'bar');\n        $this->segment->set('baz', 'dib');\n        $this->assertSame('bar', $this->getValue('foo'));\n        $this->assertSame('dib', $this->getValue('baz'));\n\n        // now clear the data\n        $this->segment->clear();\n        $this->assertSame(array(), $this->getValue());\n        $this->assertNull($this->segment->get('foo'));\n        $this->assertNull($this->segment->get('baz'));\n    }\n\n    public function testGetSegment()\n    {\n        $this->segment->set('foo', 'bar');\n        $this->segment->set('baz', 'dib');\n        $this->assertSame('bar', $this->getValue('foo'));\n        $this->assertSame('dib', $this->getValue('baz'));\n\n        // now get the data\n        $this->assertSame(array('foo' => 'bar', 'baz' => 'dib'), $this->segment->getSegment());\n    }\n\n    public function testFlash()\n    {\n        // set a value\n        $this->segment->setFlash('foo', 'bar');\n        $expect = 'bar';\n        $this->assertSame($expect, $this->segment->getFlashNext('foo'));\n        $this->assertNull($this->segment->getFlash('foo'));\n\n        // set a value and make it available now\n        $this->segment->setFlashNow('baz', 'dib');\n        $expect = 'dib';\n        $this->assertSame($expect, $this->segment->getFlash('baz'));\n        $this->assertSame($expect, $this->segment->getFlashNext('baz'));\n\n        // clear the next values\n        $this->segment->clearFlash();\n        $this->assertNull($this->segment->getFlashNext('foo'));\n        $this->assertNull($this->segment->getFlashNext('baz'));\n        $expect = 'dib';\n        $this->assertSame($expect, $this->segment->getFlash('baz'));\n\n        // set some current values and make sure they get kept\n        $now =& $_SESSION[Session::FLASH_NOW][$this->name];\n        $now['foo'] = 'bar';\n        $now['baz'] = 'dib';\n        $this->segment->keepFlash();\n        $this->assertSame('bar', $this->segment->getFlashNext('foo'));\n        $this->assertSame('dib', $this->segment->getFlashNext('baz'));\n\n        // clear the current and future values\n        $this->segment->clearFlashNow();\n        $this->assertNull($this->segment->getFlash('foo'));\n        $this->assertNull($this->segment->getFlashNext('foo'));\n        $this->assertNull($this->segment->getFlash('baz'));\n        $this->assertNull($this->segment->getFlashNext('baz'));\n    }\n\n    public function testGetDoesNotStartSession()\n    {\n        $this->assertFalse($this->session->isStarted());\n        $foo = $this->segment->get('foo');\n        $this->assertNull($foo);\n        $this->assertFalse($this->session->isStarted());\n    }\n\n    public function testGetResumesSession()\n    {\n        // fake a cookie\n        $cookies = array(\n            $this->session->getName() => 'fake-cookie-value',\n        );\n        $this->session = $this->newSession($cookies);\n\n        // should be active now, even though not started\n        $this->assertTrue($this->session->isResumable());\n\n        // reset the segment to use the new session manager\n        $this->segment = $this->session->getSegment($this->name);\n\n        // this should restart the session\n        $foo = $this->segment->get('foo');\n        $this->assertTrue($this->session->isStarted());\n    }\n\n    public function testSetStartsSessionAndCanReadAfter()\n    {\n        // no session yet\n        $this->assertFalse($this->session->isStarted());\n\n        // set it\n        $this->segment->set('foo', 'bar');\n\n        // session should have started\n        $this->assertTrue($this->session->isStarted());\n\n        // get it from the session\n        $foo = $this->segment->get('foo');\n        $this->assertSame('bar', $foo);\n\n        // make sure it's actually in $_SESSION\n        $this->assertSame($foo, $_SESSION[$this->name]['foo']);\n    }\n\n    public function testClearDoesNotStartSession()\n    {\n        $this->assertFalse($this->session->isStarted());\n        $this->segment->clear();\n        $this->assertFalse($this->session->isStarted());\n    }\n\n    public function testSetFlashStartsSessionAndCanReadAfter()\n    {\n        // no session yet\n        $this->assertFalse($this->session->isStarted());\n\n        // set it\n        $this->segment->setFlash('foo', 'bar');\n\n        // session should have started\n        $this->assertTrue($this->session->isStarted());\n\n        // should see it in the session\n        $actual = $_SESSION[Session::FLASH_NEXT][$this->name]['foo'];\n        $this->assertSame('bar', $actual);\n\n    }\n\n    public function testGetFlashDoesNotStartSession()\n    {\n        $this->assertFalse($this->session->isStarted());\n        $this->assertNull($this->segment->getFlash('foo'));\n        $this->assertFalse($this->session->isStarted());\n    }\n\n    public function testRemoveDoesNothingWhenSessionNotStarted(): void\n    {\n        $this->segment->remove('foo');\n        $this->assertFalse($this->session->isStarted());\n    }\n\n    public function testRemoveKey(){\n        $this->segment->set('foo', 'bar');\n        $this->segment->set('baz', 'dib');\n        $this->assertSame('bar', $this->getValue('foo'));\n        $this->assertSame('dib', $this->getValue('baz'));\n\n        // now remove the key\n        $this->segment->remove('foo');\n        $this->assertNull($this->segment->get('foo'));\n        $this->assertArrayNotHasKey('foo', $_SESSION[$this->name]);\n        $this->assertSame('dib', $this->getValue('baz'));\n    }\n\n    public function testRemoveSegment(){\n        $this->segment->set('foo', 'bar');\n        $this->segment->set('baz', 'dib');\n        $this->assertSame('bar', $this->getValue('foo'));\n        $this->assertSame('dib', $this->getValue('baz'));\n\n        // now remove the key\n        $this->segment->remove();\n        $this->assertArrayNotHasKey($this->name, $_SESSION);\n    }\n\n    public function testRestartSessionFlashNotMove()\n    {\n        $this->assertFalse($this->session->isStarted());\n\n        // set it\n        $this->segment->setFlash('foo', 'bar');\n\n        // session should have started\n        $this->assertTrue($this->session->isStarted());\n\n        // should see it in the session\n        $actual = $_SESSION[Session::FLASH_NEXT][$this->name]['foo'];\n        $this->assertSame('bar', $actual);\n\n        $this->session->commit();\n\n        $this->assertFalse($this->session->isStarted());\n\n        $this->session->start();\n\n        // should see it in the session\n        $actual = $_SESSION[Session::FLASH_NEXT][$this->name]['foo'];\n        $this->assertSame('bar', $actual);\n    }\n}\n"
  },
  {
    "path": "tests/SessionFactoryTest.php",
    "content": "<?php\nnamespace Aura\\Session;\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass SessionFactoryTest extends TestCase\n{\n    public function testNewInstance()\n    {\n        $session_factory = new SessionFactory;\n        $session = $session_factory->newInstance($_COOKIE);\n        $this->assertInstanceOf('Aura\\Session\\Session', $session);\n    }\n}\n"
  },
  {
    "path": "tests/SessionTest.php",
    "content": "<?php\nnamespace Aura\\Session;\n\nuse Aura\\Session\\Exception\\SessionAlreadyStarted;\nuse PHPUnit\\Framework\\Attributes\\RunTestsInSeparateProcesses;\nuse PHPUnit\\Framework\\TestCase;\n\n#[RunTestsInSeparateProcesses]\nclass SessionTest extends TestCase\n{\n    /** @var Phpfunc */\n    private $phpfunc;\n\n    // the session object\n    protected $session;\n\n    protected function setUp(): void\n    {\n        $this->phpfunc = new Phpfunc;\n        $handler = new FakeSessionHandler();\n        session_set_save_handler($handler, true);\n        $this->session = $this->newSession();\n    }\n\n    protected function newSession(array $cookies = array())\n    {\n        return new Session(\n            new SegmentFactory,\n            new CsrfTokenFactory(new Randval()),\n            $this->phpfunc,\n            $cookies\n        );\n    }\n\n    public function teardown(): void\n    {\n        session_unset();\n        if (session_id() !== '') {\n            session_destroy();\n        }\n    }\n\n    public function testStart()\n    {\n        $this->session->start();\n        $this->assertTrue($this->session->isStarted());\n    }\n\n    public function testClear()\n    {\n        // get a test segment and set some data\n        $segment = $this->session->getSegment('test');\n        $segment->set('foo', 'bar');\n        $segment->set('baz', 'dib');\n\n        $expect = array(\n            Session::FLASH_NEXT => array(\n                'test' => array(),\n            ),\n            Session::FLASH_NOW => array(\n                'test' => array(),\n            ),\n            'test' => array(\n                'foo' => 'bar',\n                'baz' => 'dib',\n            ),\n        );\n\n        $this->assertSame($expect, $_SESSION);\n\n        // now clear it\n        $this->session->clear();\n        $this->assertSame(array(), $_SESSION);\n    }\n\n    public function testDestroy()\n    {\n        // get a test segment and set some data\n        $segment = $this->session->getSegment('test');\n        $segment->set('foo', 'bar');\n        $segment->set('baz', 'dib');\n\n        $this->assertTrue($this->session->isStarted());\n\n        $expect = array(\n            Session::FLASH_NEXT => array(\n                'test' => array(),\n            ),\n            Session::FLASH_NOW => array(\n                    'test' => array(),\n            ),\n            'test' => array(\n                'foo' => 'bar',\n                'baz' => 'dib',\n            ),\n        );\n\n        $this->assertSame($expect, $_SESSION);\n\n        // now destroy it\n        $this->session->destroy();\n        $this->assertFalse($this->session->isStarted());\n    }\n\n    public function testCommit()\n    {\n        $this->session->commit();\n        $this->assertFalse($this->session->isStarted());\n    }\n\n    public function testCommitAndDestroy()\n    {\n        // get a test segment and set some data\n        $segment = $this->session->getSegment('test');\n        $segment->set('foo', 'bar');\n        $segment->set('baz', 'dib');\n\n        $this->assertTrue($this->session->isStarted());\n\n        $expect = array(\n            Session::FLASH_NEXT => array(\n                'test' => array(),\n            ),\n            Session::FLASH_NOW => array(\n                'test' => array(),\n            ),\n            'test' => array(\n                'foo' => 'bar',\n                'baz' => 'dib',\n            ),\n        );\n\n        $this->assertSame($expect, $_SESSION);\n\n        $this->session->commit();\n        $this->session->destroy();\n        $segment = $this->session->getSegment('test');\n        $this->assertSame(array(), $_SESSION);\n    }\n\n    public function testGetSegment()\n    {\n        $segment = $this->session->getSegment('test');\n        $this->assertInstanceof('Aura\\Session\\Segment', $segment);\n    }\n\n    public function testGetCsrfToken()\n    {\n        $actual = $this->session->getCsrfToken();\n        $expect = 'Aura\\Session\\CsrfToken';\n        $this->assertInstanceOf($expect, $actual);\n    }\n\n    public function testisResumable()\n    {\n        // should not look active\n        $this->assertFalse($this->session->isResumable());\n\n        // fake a cookie\n        $cookies = array(\n            $this->session->getName() => 'fake-cookie-value',\n        );\n        $this->session = $this->newSession($cookies);\n\n        // now it should look active\n        $this->assertTrue($this->session->isResumable());\n    }\n\n    public function testGetAndRegenerateId()\n    {\n        $this->session->start();\n        $old_id = $this->session->getId();\n        $this->session->regenerateId();\n        $new_id = $this->session->getId();\n        $this->assertTrue($old_id != $new_id);\n\n        // check the csrf token as well\n        $old_value = $this->session->getCsrfToken()->getValue();\n        $this->session->regenerateId();\n        $new_value = $this->session->getCsrfToken()->getValue();\n        $this->assertNotEquals($old_value, $new_value);\n\n        // Regenerates multiple keys\n        $csrf1_old_value = $this->session->getCsrfToken('csrf1')->getValue();\n        $csrf2_old_value = $this->session->getCsrfToken('csrf2')->getValue();\n        $this->session->regenerateId();\n        $csrf1_new_value = $this->session->getCsrfToken('csrf1')->getValue();\n        $csrf2_new_value = $this->session->getCsrfToken('csrf2')->getValue();\n        $this->assertNotSame($csrf1_old_value, $csrf1_new_value);\n        $this->assertNotSame($csrf2_old_value, $csrf2_new_value);\n    }\n\n    public function testSetAndGetName()\n    {\n        $expect = 'new_name';\n        $this->session->setName($expect);\n        $actual = $this->session->getName();\n        $this->assertSame($expect, $actual);\n    }\n\n    public function testSetAndGetSavePath()\n    {\n        $expect = '/new/save/path';\n        $this->session->setSavePath($expect);\n        $actual = $this->session->getSavePath();\n        $this->assertSame($expect, $actual);\n    }\n\n    public function testSetAndGetCookieParams()\n    {\n        $expect = $this->session->getCookieParams();\n        $expect['lifetime'] = '999';\n        $this->session->setCookieParams($expect);\n        $actual = $this->session->getCookieParams();\n        $this->assertSame($expect, $actual);\n\n        // Cannot change session cookie parameters when session is active\n        $this->expectException(SessionAlreadyStarted::class);\n        $this->session->start();\n        $newParams = $expect;\n        $newParams['lifetime'] = '0';\n        $this->session->setCookieParams($newParams);\n    }\n\n    public function testSetAndGetCacheExpire()\n    {\n        $expect = 123;\n        $this->session->setCacheExpire($expect);\n        $actual = $this->session->getCacheExpire();\n        $this->assertSame($expect, $actual);\n    }\n\n    public function testSetAndGetCacheLimiter()\n    {\n        $expect = 'private_no_cache';\n        $this->session->setCacheLimiter($expect);\n        $actual = $this->session->getCacheLimiter();\n        $this->assertSame($expect, $actual);\n    }\n\n    public function testResume()\n    {\n        // should not look active\n        $this->assertFalse($this->session->isResumable());\n        $this->assertFalse($this->session->resume());\n\n        // fake a cookie so a session looks available\n        $cookies = array(\n            $this->session->getName() => 'fake-cookie-value',\n        );\n        $this->session = $this->newSession($cookies);\n        $this->assertTrue($this->session->resume());\n\n        // now it should already active\n        $this->assertTrue($this->session->resume());\n    }\n\n}\n"
  }
]