Full Code of auraphp/Aura.Session for AI

6.x fb2751a4256d cached
31 files
69.7 KB
18.1k tokens
134 symbols
1 requests
Download .txt
Repository: auraphp/Aura.Session
Branch: 6.x
Commit: fb2751a4256d
Files: 31
Total size: 69.7 KB

Directory structure:
gitextract_njjloc6y/

├── .github/
│   └── workflows/
│       └── continuous-integration.yml
├── .gitignore
├── .scrutinizer.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── codecov.yml
├── composer.json
├── docs/
│   ├── _bookdown.json
│   └── getting-started.md
├── phpunit.php
├── phpunit.xml.dist
├── src/
│   ├── CsrfToken.php
│   ├── CsrfTokenFactory.php
│   ├── Exception/
│   │   └── SessionAlreadyStarted.php
│   ├── Exception.php
│   ├── Phpfunc.php
│   ├── Randval.php
│   ├── RandvalInterface.php
│   ├── Segment.php
│   ├── SegmentFactory.php
│   ├── SegmentInterface.php
│   ├── Session.php
│   └── SessionFactory.php
└── tests/
    ├── CsrfTokenTest.php
    ├── FakeSessionHandler.php
    ├── Issue23Test.php
    ├── SegmentTest.php
    ├── SessionFactoryTest.php
    └── SessionTest.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/continuous-integration.yml
================================================
name: Continuous Integration

on:
  push:
  pull_request:
  workflow_dispatch:

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        operating-system:
          - ubuntu-latest
        php-version:
          - '8.1'
          - '8.2'
          - '8.3'
          - '8.4'
          - '8.5'
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup PHP ${{ matrix.php-version }}
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-version }}
          coverage: pcov
          tools: none
          ini-values: assert.exception=1, zend.assertions=1

      - name: Get composer cache directory
        id: composer-cache
        run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-

      - name: Install dependencies
        run: composer install --no-interaction --prefer-dist

      - name: Run test suite
        run: ./vendor/bin/phpunit --coverage-clover=coverage.xml

      - name: Upload coverage report
        uses: codecov/codecov-action@v5
        with:
          fail_ci_if_error: false


================================================
FILE: .gitignore
================================================
/tests/container/vendor
/tests/container/composer.*
/composer.lock
/vendor
/.phpunit.cache


================================================
FILE: .scrutinizer.yml
================================================
build:
  image: default-jammy
  environment:
    php: 8.4
  nodes:
    analysis:
      tests:
        override:
          - php-scrutinizer-run

filter:
  paths: ["src/*"]


================================================
FILE: CHANGELOG.md
================================================
# CHANGELOG

## 6.0.0

- PHP 8.1+ is now required.
- (CHG) Add `string` type declarations to `$key` parameters in `Segment`, `SegmentInterface` to fix PHP 8.5 deprecation of null as array offset.
- (CHG) Change `remove()` parameter to `?string $key = null` in `Segment` and `SegmentInterface`.

## 4.0.0

- PHP 7.2+ is now required.
- Github CI workflow added.
- Scrutinizer CI updated.
- MIT License.

## 2.1.0

- (ADD) Add support for hash_equals() in CsrfToken::isValid()
- (ADD) Add support for random_bytes() in Randval::generate()
- (TST) Update tests and test support files
- (DOC) Update license year and remove branch alias

## 2.0.1

This release modifies the testing structure and updates other support files.


## 2.0.0

This is the first stable release of Aura.Session 2.0.0.

- Fix #23

- Merge pull request #33 from harikt/issue-23.

- Merge pull request #35 from iansltx/php7-compat: Fix FakeSessionHandler::write() (fixes PHP7 tests)

- Merge pull request #36 from fiahfy/spike: fix param type

- Merge pull request #37 from tomkyle/develop-2: Removed redundant paragraph

- Merge pull request #38 from tomkyle/develop-2: Clarified parameter descriptions

- Updated documentation and support files.

## 2.0.0-beta2

- TST: Update testing structure, use plain old PHPUnit for tests

- CHG: Use new service naming rules

- CHG: Disable auto-resolve for container tests

## 2.0.0-beta1

First 2.0.0 beta release.

## 1.0.2

Hygiene release.

- Fix #8 related to unit tests failing because of ini_set values. Thanks @mindplay-dk

- Merge pull request #12 from harikt/v2config, adds v2 config files.

## 1.0.1

- [CHG] Manager::destroy() now checks whether the session is started; if not,
  starts it, and then destroys.  This is because sessions are lazy-loading.

- [DOC] Add PHP 5.5 to the Travis build and update docs

## 1.0.0

There are BC breaks in this release, but it's a Google beta, so ...

- [SEC] Based on conversation at
  http://www.eschrade.com/page/generating-secure-cross-site-request-forgery-tokens-csrf/
  start using openssl and mcrypt for CSRF tokens instead of mt_rand().

- [NEW] SegmentInterface, Randval, RandvalInterface.

- [BRK] The Manager now requires $_COOKIE as its third param to Manager.

- [CHG] Segments now lazy-load themselves. On reads, they will reactivate an
  available session, but will not start a new one. On writes, they will
  reactivate an available session, or start a new one if one is not available.
  This means that creating a segment object no longer starts a session; you
  have to read from or write to one for the session to start.

- [BRK] Renamed Manager::isActive() to isAvailable(), to differentiate from
  PHP_SESSION_ACTIVE. ( Previously, isActive() only told you if a session had
  started, not if one was available to be activated.)

- [CHG] Manager::getSegment() no longer starts a session

- [CHG] Manager::isStarted() now checks getStatus() for PHP_SESSION_ACTIVE
  instead of session_id().

- [CHG] Segment::__get() no longer returns a reference

- [BRK] Renamed Manager::getSegment() to newSegment()

- [CHG] Manager no longer retains session segments

- [CHG] Various typo and doc fixes by Akihito Koriyama


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

We are happy to review any contributions you want to make. When contributing, please follow the rules outlined at <http://auraphp.com/contributing>.

The time between submitting a contribution and its review one may be extensive; do not be discouraged if there is not immediate feedback.

Thanks!


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2011-2022, Aura for PHP

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
# Aura Session

Provides session management functionality, including lazy session starting,
session segments, next-request-only ("flash") values, and CSRF tools.

## Foreword

### Installation

This 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.

It is installable and autoloadable via Composer as [aura/session](https://packagist.org/packages/aura/session).

### Quality

[![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)
[![codecov](https://codecov.io/gh/auraphp/Aura.Session/branch/4.x/graph/badge.svg)](https://codecov.io/gh/auraphp/Aura.Session)
[![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)

To 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`.

This library attempts to comply with [PSR-1][], [PSR-12][], and [PSR-4][]. If
you notice compliance oversights, please send a patch via pull request.

[PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md
[PSR-12]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-12-extended-coding-style-guide.md
[PSR-4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md

### Community

To 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).


## Documentation

This package is fully documented [here](./docs/getting-started.md).


================================================
FILE: codecov.yml
================================================
comment: false


================================================
FILE: composer.json
================================================
{
    "name": "aura/session",
    "type": "library",
    "description": "Provides session management functionality, including lazy session starting, session segments, next-request-only (\"flash\") values, and CSRF tools.",
    "keywords": [
        "session",
        "sessions",
        "flash",
        "flash message",
        "csrf"
    ],
    "homepage": "https://github.com/auraphp/Aura.Session",
    "license": "MIT",
    "authors": [
        {
            "name": "Aura.Session Contributors",
            "homepage": "https://github.com/auraphp/Aura.Session/contributors"
        }
    ],
    "require": {
        "php": "^8.1"
    },
    "autoload": {
        "psr-4": {
            "Aura\\Session\\": "src/"
        }
    },
    "suggest": {
        "ext-openssl": "OpenSSL generates the best secure CSRF tokens."
    },
    "require-dev": {
        "phpunit/phpunit": "^10.5 || ^11.0"
    },
    "autoload-dev": {
        "psr-4": {
            "Aura\\Session\\": "tests/"
        }
    }
}


================================================
FILE: docs/_bookdown.json
================================================
{
    "title": "Aura.Session",
    "content": [
        "getting-started.md"
    ]
}

================================================
FILE: docs/getting-started.md
================================================
## Getting Started

### Instantiation

The easiest way to get started is to use the _SessionFactory_ to create a _Session_ manager object.

```php
<?php
$session_factory = new \Aura\Session\SessionFactory;
$session = $session_factory->newInstance($_COOKIE);
?>
```

We 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.)

### Segments

In 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.

For 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.

```php
<?php
// get a _Segment_ object
$segment = $session->getSegment('Vendor\Package\ClassName');

// try to get a value from the segment;
// if it does not exist, return an alternative value
echo $segment->get('foo'); // null
echo $segment->get('baz', 'not set'); // 'not set'

// set some values on the segment
$segment->set('foo', 'bar');
$segment->set('baz', 'dib');

// the $_SESSION array is now:
// $_SESSION = array(
//      'Vendor\Package\ClassName' => array(
//          'foo' => 'bar',
//          'baz' => 'dib',
//      ),
// );

// try again to get a value from the segment
echo $segment->get('foo'); // 'bar'

// because the segment is a reference to $_SESSION, we can modify
// the superglobal directly and the segment values will also change
$_SESSION['Vendor\Package\ClassName']['zim'] = 'gir'
echo $segment->get('zim'); // 'gir'
?>
```

The benefit of a session segment is that we can deconflict the keys in the
`$_SESSION` superglobal by using class names (or some other unique name) for
the segment names. With segments, different packages can use the `$_SESSION`
superglobal without stepping on each other's toes.

To clear all the values on a _Segment_, use the `clear()` method.

### Flash Values

_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.

#### Setting And Getting Flash Values

To set a flash value on a _Segment_, use the `setFlash()` method.

```php
<?php
$segment = $session->getSegment('Vendor\Package\ClassName');
$segment->setFlash('message', 'Hello world!');
?>
```

Then, in subsequent requests, we can read the flash value using `getFlash()`:

```php
<?php
$segment = $session->getSegment('Vendor\Package\ClassName');
$message = $segment->getFlash('message'); // 'Hello world!'
?>
```

> 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.

Using `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)`.

Using `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)`.

#### Keeping and Clearing Flash Values

Sometimes 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.

Similarly, we can clear flash values just for that _Segment_ with `clearFlash()` method.

### Lazy Session Starting

Merely instantiating the _Session_ manager and getting a _Segment_ from it does *not* call `session_start()`. Instead, `session_start()` occurs only in certain circumstances:

- 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()`.

- 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.

This 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.

Of course, we can force a session start or reactivation by calling the _Session_ `start()` method, but that defeats the purpose of lazy-loaded sessions.


### Saving, Clearing, and Destroying Sessions

> N.b.: These methods apply to all session data and flashes across all segments.

To save the session data and end its use during the current request, call the `commit()` method on the _Session_ manager:

```php
<?php
$session->commit();
?>
```

> N.b.: Per <http://php.net/manual/en/session.examples.basic.php>, "Sessions
> normally shutdown automatically when PHP is finished executing a script, but
> can be manually shutdown using the session_write_close() function."
> The `commit()` method is the equivalent of `session_write_close()`.

To clear all session data, but leave the session active during the current request, use the `clear()` method on the _Session_ manager.

```php
<?php
$session->clear();
?>
```

To clear all flash values on a segment, use the `clearFlash()` method:

To clear the data *and* terminate the session for this and future requests, thereby destroying it completely, call the `destroy()` method:

```php
<?php
$session->destroy(); // equivalent of session_destroy()
?>
```

Calling `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.

```php
<?php
// assume $response is a framework response object.
// this will be used to delete the session cookie.
$delete_cookie = function ($name, $path, $domain) use ($response) {
    $response->cookies->delete($name, $path, $domain);
}

$session = $session_factory->newInstance($_COOKIE, $delete_cookie);
?>
```

## Session Security

### Session ID Regeneration

Any time a user has a change in privilege (that is, gaining or losing access
rights within a system) be sure to regenerate the session ID:

```php
<?php
$session->regenerateId();
?>
```

> N.b.: The `regenerateId()` method also regenerates the CSRF token value.

### Cross-Site Request Forgery

A "cross-site request forgery" is a security issue where the attacker, via
malicious JavaScript or other means, issues a request in-the-blind from a
client browser to a server where the user has already authenticated. The
request *looks* valid to the server, but in fact is a forgery, since the user
did not actually make the request (the malicious JavaScript did).

<http://en.wikipedia.org/wiki/Cross-site_request_forgery>

#### Defending Against CSRF

To defend against CSRF attacks, server-side logic should:

1. Place a token value unique to each authenticated user session in each form;
   and

2. Check that all incoming POST/PUT/DELETE (i.e., "unsafe") requests contain
   that value.

> N.b.: If our application uses GET requests to modify resources (which
> incidentally is an improper use of GET), we should also check for CSRF on
> GET requests from authenticated users.

For this example, the form field name will be `__csrf_value`. In each form
we want to protect against CSRF, we use the session CSRF token value for that
field:

```php
<?php
/**
 * @var Vendor\Package\User $user A user-authentication object.
 * @var Aura\Session\Session $session A session management object.
 */
?>
<form method="post">

    <?php if ($user->auth->isValid()) {
        $csrf_value = $session->getCsrfToken()->getValue();
        echo '<input type="hidden" name="__csrf_value" value="'
           . htmlspecialchars($csrf_value, ENT_QUOTES, 'UTF-8')
           . '"></input>';
    } ?>

    <!-- other form fields -->

</form>
```

When processing the request, check to see if the incoming CSRF token is valid
for the authenticated user:

```php
<?php
/**
 * @var Vendor\Package\User $user A user-authentication object.
 * @var Aura\Session\Session $session A session management object.
 */

$unsafe = $_SERVER['REQUEST_METHOD'] == 'POST'
       || $_SERVER['REQUEST_METHOD'] == 'PUT'
       || $_SERVER['REQUEST_METHOD'] == 'DELETE';

if ($unsafe && $user->auth->isValid()) {
    $csrf_value = $_POST['__csrf_value'];
    $csrf_token = $session->getCsrfToken();
    if (! $csrf_token->isValid($csrf_value)) {
        echo "This looks like a cross-site request forgery.";
    } else {
        echo "This looks like a valid request.";
    }
} else {
    echo "CSRF attacks only affect unsafe requests by authenticated users.";
}
?>
```

> Note : By default the above code only works for single form. If you need
multiple tokens, pass different keys to `getValue` and `isValid` methods.
For examples please look into the unit test class `CsrfTokenTest`.

#### CSRF Value Generation

For a CSRF token to be useful, its random value must be cryptographically
secure. Aura.Session comes with a `Randval` class that implements a
`RandvalInterface`, and uses default `random_bytes` to generate a
random value. If you are not satisified,
you can create your own implementation of the `RandvalInterface`.

### Session Lifetime

We 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:

```
<?php
$session->setCookieParams(array('lifetime' => '1209600'));
?>
```

> 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.


================================================
FILE: phpunit.php
================================================
<?php
error_reporting(E_ALL);
$autoloader = __DIR__ . '/vendor/autoload.php';
if (! file_exists($autoloader)) {
    echo "Composer autoloader not found: $autoloader" . PHP_EOL;
    echo "Please issue 'composer install' and try again." . PHP_EOL;
    exit(1);
}
require $autoloader;


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="phpunit.php"
         colors="true"
         cacheDirectory=".phpunit.cache">
    <testsuites>
        <testsuite name="aura/session">
            <directory>tests</directory>
        </testsuite>
    </testsuites>
    <source>
        <include>
            <directory suffix=".php">src</directory>
        </include>
    </source>
</phpunit>


================================================
FILE: src/CsrfToken.php
================================================
<?php
/**
 *
 * This file is part of Aura for PHP.
 *
 * @license http://opensource.org/licenses/mit-license.php MIT
 *
 */
namespace Aura\Session;

/**
 *
 * Cross-site request forgery token tools.
 *
 * @package Aura.Session
 *
 */
class CsrfToken
{
    /**
     *
     * A cryptographically-secure random value generator.
     *
     * @var RandvalInterface
     *
     */
    protected $randval;

    /**
     *
     * Session segment for values in this class.
     *
     * @var SegmentInterface
     *
     */
    protected $segment;

    /**
     *
     * Constructor.
     *
     * @param SegmentInterface $segment A segment for values in this class.
     *
     * @param RandvalInterface $randval A cryptographically-secure random
     * value generator.
     *
     */
    public function __construct(SegmentInterface $segment, RandvalInterface $randval)
    {
        $this->segment = $segment;
        $this->randval = $randval;
    }

    /**
     *
     * Checks whether an incoming CSRF token value is valid.
     *
     * @param string $value The incoming token value.
     *
     * @param string $key  A string key name which session value is saved.
     *
     * @return bool True if valid, false if not.
     *
     */
    public function isValid($value, $key = 'value')
    {
        return hash_equals($value, $this->getValue($key));
    }

    /**
     *
     * Gets the value of the outgoing CSRF token.
     *
     * @param string $key  A string key name which session value is saved.
     *
     * @return string
     *
     */
    public function getValue($key = 'value')
    {
        if ($this->segment->get($key) == null ) {
            $this->regenerateValue($key);
        }

        return $this->segment->get($key);
    }

    /**
     *
     * Regenerates the value of the outgoing CSRF token.
     *
     * @param string $key  A string key name which session value is saved.
     *
     * @return string
     *
     */
    public function regenerateValue($key = 'value')
    {
        $hash = hash('sha512', $this->randval->generate());
        $this->segment->set($key, $hash);

        return $this->segment->get($key);
    }


    /**
     * Regenerates all csrf tokens
     *
     * @return void
     */
    public function regenerateAllKeyValues()
    {
        $segment = $this->segment->getSegment();

        if ($segment) {
            foreach ($segment as $key => $value) {
                $this->regenerateValue($key);
            }
        }
    }

}


================================================
FILE: src/CsrfTokenFactory.php
================================================
<?php
/**
 *
 * This file is part of Aura for PHP.
 *
 * @license http://opensource.org/licenses/mit-license.php MIT
 *
 */
namespace Aura\Session;

/**
 *
 * A factory to create CSRF token objects.
 *
 * @package Aura.Session
 *
 */
class CsrfTokenFactory
{
    /**
     *
     * A cryptographically-secure random value generator.
     *
     * @var RandvalInterface
     *
     */
    protected $randval;

    /**
     *
     * Constructor.
     *
     * @param RandvalInterface $randval A cryptographically-secure random
     * value generator.
     *
     */
    public function __construct(RandvalInterface $randval)
    {
        $this->randval = $randval;
    }

    /**
     *
     * Creates a CsrfToken object.
     *
     * @param Session $session The session manager.
     *
     * @return CsrfToken
     *
     */
    public function newInstance(Session $session)
    {
        $segment = $session->getSegment('Aura\Session\CsrfToken');
        return new CsrfToken($segment, $this->randval);
    }
}


================================================
FILE: src/Exception/SessionAlreadyStarted.php
================================================
<?php

namespace Aura\Session\Exception;

use Aura\Session\Exception;

class SessionAlreadyStarted extends Exception
{
}


================================================
FILE: src/Exception.php
================================================
<?php
/**
 *
 * This file is part of Aura for PHP.
 *
 * @license http://opensource.org/licenses/mit-license.php MIT
 *
 */
namespace Aura\Session;

/**
 *
 * Package-level exception.
 *
 * @package Aura.Session
 *
 */
class Exception extends \Exception
{
}


================================================
FILE: src/Phpfunc.php
================================================
<?php
/**
 *
 * This file is part of Aura for PHP.
 *
 * @license http://opensource.org/licenses/mit-license.php MIT
 *
 */
namespace Aura\Session;

/**
 *
 * Intercept calls to PHP functions.
 *
 * @package Aura.Session
 *
 */
class Phpfunc
{
    /**
     *
     * Magic call to intercept any function pass to it.
     *
     * @param string $func The function to call.
     *
     * @param array $args Arguments passed to the function.
     *
     * @return mixed The result of the function call.
     *
     */
    public function __call($func, $args)
    {
        return call_user_func_array($func, $args);
    }
}


================================================
FILE: src/Randval.php
================================================
<?php
/**
 *
 * This file is part of Aura for PHP.
 *
 * @license http://opensource.org/licenses/mit-license.php MIT
 *
 */
namespace Aura\Session;

use Aura\Session\Exception;

/**
 *
 * Generates cryptographically-secure random values.
 *
 * @package Aura.Session
 *
 */
class Randval implements RandvalInterface
{
    /**
     *
     * Returns a cryptographically secure random value.
     *
     * @return string
     *
     */
    public function generate()
    {
        return random_bytes(32);
    }
}


================================================
FILE: src/RandvalInterface.php
================================================
<?php
/**
 *
 * This file is part of Aura for PHP.
 *
 * @license http://opensource.org/licenses/mit-license.php MIT
 *
 */
namespace Aura\Session;

/**
 *
 * Interface for generating cryptographically-secure random values.
 *
 * @package Aura.Session
 *
 */
interface RandvalInterface
{
    /**
     *
     * Returns a cryptographically secure random value.
     *
     * @return string
     *
     */
    public function generate();
}


================================================
FILE: src/Segment.php
================================================
<?php
/**
 *
 * This file is part of Aura for PHP.
 *
 * @license http://opensource.org/licenses/mit-license.php MIT
 *
 */
namespace Aura\Session;

/**
 *
 * A session segment; lazy-loads from the session.
 *
 * @package Aura.Session
 *
 */
class Segment implements SegmentInterface
{
    /**
     *
     * The session manager.
     *
     * @var Session
     *
     */
    protected $session;

    /**
     *
     * The segment name.
     *
     * @var string
     *
     */
    protected $name;

    /**
     *
     * Constructor.
     *
     * @param Session $session The session manager.
     *
     * @param string $name The segment name.
     *
     */
    public function __construct(Session $session, $name)
    {
        $this->session = $session;
        $this->name = $name;
    }

    /**
     *
     * Returns the value of a key in the segment.
     *
     * @param string $key The key in the segment.
     *
     * @param mixed $alt An alternative value to return if the key is not set.
     *
     * @return mixed
     *
     */
    public function get(string $key, $alt = null)
    {
        $this->resumeSession();
        return isset($_SESSION[$this->name][$key])
             ? $_SESSION[$this->name][$key]
             : $alt;
    }

    /**
     *
     * Returns the entire segment.
     *
     * @return mixed
     *
     */
    public function getSegment()
    {
        $this->resumeSession();
        return isset($_SESSION[$this->name])
            ? $_SESSION[$this->name]
            : null;
    }

    /**
     *
     * Sets the value of a key in the segment.
     *
     * @param string $key The key to set.
     *
     * @param mixed $val The value to set it to.
     *
     */
    public function set(string $key, $val)
    {
        $this->resumeOrStartSession();
        $_SESSION[$this->name][$key] = $val;
    }

    /**
     *
     * Clear all data from the segment.
     *
     * @return null
     *
     */
    public function clear()
    {
        if ($this->resumeSession()) {
            $_SESSION[$this->name] = array();
        }
    }


    /**
     * Remove a key from the segment, or remove the entire segment (including key) from the session
     *
     * @param string|null $key The key to remove, or null to clear the entire segment.
     */
    public function remove(?string $key = null) {
        if (! $this->resumeSession()) {
            return;
        }
        if ($key === null) {
            unset($_SESSION[$this->name]);
            unset($_SESSION[Session::FLASH_NOW][$this->name]);
            unset($_SESSION[Session::FLASH_NEXT][$this->name]);
            return;
        }
        if (array_key_exists($key, $_SESSION[$this->name])) {
            unset($_SESSION[$this->name][$key]);
        }
    }

    /**
     *
     * Sets a flash value for the *next* request.
     *
     * @param string $key The key for the flash value.
     *
     * @param mixed $val The flash value itself.
     *
     */
    public function setFlash(string $key, $val)
    {
        $this->resumeOrStartSession();
        $_SESSION[Session::FLASH_NEXT][$this->name][$key] = $val;
    }

    /**
     *
     * Gets the flash value for a key in the *current* request.
     *
     * @param string $key The key for the flash value.
     *
     * @param mixed $alt An alternative value to return if the key is not set.
     *
     * @return mixed The flash value itself.
     *
     */
    public function getFlash(string $key, $alt = null)
    {
        $this->resumeSession();
        return isset($_SESSION[Session::FLASH_NOW][$this->name][$key])
             ? $_SESSION[Session::FLASH_NOW][$this->name][$key]
             : $alt;
    }

    /**
     *
     * Clears flash values for *only* the next request.
     *
     * @return null
     *
     */
    public function clearFlash()
    {
        if ($this->resumeSession()) {
            $_SESSION[Session::FLASH_NEXT][$this->name] = array();
        }
    }

    /**
     *
     * Gets the flash value for a key in the *next* request.
     *
     * @param string $key The key for the flash value.
     *
     * @param mixed $alt An alternative value to return if the key is not set.
     *
     * @return mixed The flash value itself.
     *
     */
    public function getFlashNext(string $key, $alt = null)
    {
        $this->resumeSession();
        return isset($_SESSION[Session::FLASH_NEXT][$this->name][$key])
             ? $_SESSION[Session::FLASH_NEXT][$this->name][$key]
             : $alt;
    }

    /**
     *
     * Sets a flash value for the *next* request *and* the current one.
     *
     * @param string $key The key for the flash value.
     *
     * @param mixed $val The flash value itself.
     *
     */
    public function setFlashNow(string $key, $val)
    {
        $this->resumeOrStartSession();
        $_SESSION[Session::FLASH_NOW][$this->name][$key] = $val;
        $_SESSION[Session::FLASH_NEXT][$this->name][$key] = $val;
    }

    /**
     *
     * Clears flash values for *both* the next request *and* the current one.
     *
     * @return null
     *
     */
    public function clearFlashNow()
    {
        if ($this->resumeSession()) {
            $_SESSION[Session::FLASH_NOW][$this->name] = array();
            $_SESSION[Session::FLASH_NEXT][$this->name] = array();
        }
    }

    /**
     *
     * Retains all the current flash values for the next request; values that
     * already exist for the next request take precedence.
     *
     * @return null
     *
     */
    public function keepFlash()
    {
        if ($this->resumeSession()) {
            $_SESSION[Session::FLASH_NEXT][$this->name] = array_merge(
                $_SESSION[Session::FLASH_NEXT][$this->name],
                $_SESSION[Session::FLASH_NOW][$this->name]
            );
        }
    }

    /**
     *
     * Loads the segment only if the session has already been started, or if
     * a session is available (in which case it resumes the session first).
     *
     * @return bool
     *
     */
    protected function resumeSession()
    {
        if ($this->session->isStarted() || $this->session->resume()) {
            $this->load();
            return true;
        }

        return false;
    }

    /**
     *
     * Sets the segment properties to $_SESSION references.
     *
     * @return null
     *
     */
    protected function load()
    {
        if (! isset($_SESSION[$this->name])) {
            $_SESSION[$this->name] = array();
        }

        if (! isset($_SESSION[Session::FLASH_NOW][$this->name])) {
            $_SESSION[Session::FLASH_NOW][$this->name] = array();
        }

        if (! isset($_SESSION[Session::FLASH_NEXT][$this->name])) {
            $_SESSION[Session::FLASH_NEXT][$this->name] = array();
        }
    }

    /**
     *
     * Resumes a previous session, or starts a new one, and loads the segment.
     *
     * @return null
     *
     */
    protected function resumeOrStartSession()
    {
        if (! $this->resumeSession()) {
            $this->session->start();
            $this->load();
        }
    }
}


================================================
FILE: src/SegmentFactory.php
================================================
<?php
/**
 *
 * This file is part of Aura for PHP.
 *
 * @license http://opensource.org/licenses/mit-license.php MIT
 *
 */
namespace Aura\Session;

/**
 *
 * A factory to create session segment objects.
 *
 * @package Aura.Session
 *
 */
class SegmentFactory
{
    /**
     *
     * Creates a session segment object.
     *
     * @param Session $session
     * @param string  $name
     *
     * @return Segment
     */
    public function newInstance(Session $session, $name)
    {
        return new Segment($session, $name);
    }
}


================================================
FILE: src/SegmentInterface.php
================================================
<?php
/**
 *
 * This file is part of Aura for PHP.
 *
 * @license http://opensource.org/licenses/mit-license.php MIT
 *
 */
namespace Aura\Session;

/**
 *
 * An interface for session segment objects.
 *
 * @package Aura.Session
 *
 */
interface SegmentInterface
{
    /**
     *
     * Returns the value of a key in the segment.
     *
     * @param string $key The key in the segment.
     *
     * @param mixed $alt An alternative value to return if the key is not set.
     *
     * @return mixed
     *
     */
    public function get(string $key, $alt = null);

    /**
     *
     * Returns the entire segment.
     *
     * @return mixed
     *
     */
    public function getSegment();

    /**
     *
     * Sets the value of a key in the segment.
     *
     * @param string $key The key to set.
     *
     * @param mixed $val The value to set it to.
     *
     */
    public function set(string $key, $val);

    /**
     *
     * Clear all data from the segment.
     *
     * @return null
     *
     */
    public function clear();

    /**
     *
     * Sets a flash value for the *next* request.
     *
     * @param string $key The key for the flash value.
     *
     * @param mixed $val The flash value itself.
     *
     */
    public function setFlash(string $key, $val);

    /**
     *
     * Gets the flash value for a key in the *current* request.
     *
     * @param string $key The key for the flash value.
     *
     * @param mixed $alt An alternative value to return if the key is not set.
     *
     * @return mixed The flash value itself.
     *
     */
    public function getFlash(string $key, $alt = null);

    /**
     *
     * Clears flash values for *only* the next request.
     *
     * @return null
     *
     */
    public function clearFlash();

    /**
     *
     * Gets the flash value for a key in the *next* request.
     *
     * @param string $key The key for the flash value.
     *
     * @param mixed $alt An alternative value to return if the key is not set.
     *
     * @return mixed The flash value itself.
     *
     */
    public function getFlashNext(string $key, $alt = null);

    /**
     *
     * Sets a flash value for the *next* request *and* the current one.
     *
     * @param string $key The key for the flash value.
     *
     * @param mixed $val The flash value itself.
     *
     */
    public function setFlashNow(string $key, $val);

    /**
     *
     * Clears flash values for *both* the next request *and* the current one.
     *
     * @return null
     *
     */
    public function clearFlashNow();

    /**
     *
     * Retains all the current flash values for the next request; values that
     * already exist for the next request take precedence.
     *
     * @return null
     *
     */
    public function keepFlash();

    /**
     * Remove a key from the segment, or remove the entire segment (including key) from the session
     *
     * @param string|null $key The key to remove, or null to clear the entire segment.
     */
    public function remove(?string $key = null);
}


================================================
FILE: src/Session.php
================================================
<?php
/**
 *
 * This file is part of Aura for PHP.
 *
 * @license http://opensource.org/licenses/mit-license.php MIT
 *
 */
namespace Aura\Session;

use Aura\Session\Exception\SessionAlreadyStarted;

/**
 *
 * A central control point for new session segments, PHP session management
 * values, and CSRF token checking.
 *
 * @package Aura.Session
 *
 */
class Session
{
    /**
     *
     * Session key for the "next" flash values.
     *
     * @const string
     *
     */
    const FLASH_NEXT = 'Aura\Session\Flash\Next';

    /**
     *
     * Session key for the "current" flash values.
     *
     * @const string
     *
     */
    const FLASH_NOW = 'Aura\Session\Flash\Now';

    /**
     *
     * A session segment factory.
     *
     * @var SegmentFactory
     *
     */
    protected $segment_factory;

    /**
     *
     * The CSRF token for this session.
     *
     * @var CsrfToken
     *
     */
    protected $csrf_token;

    /**
     *
     * A CSRF token factory, for lazy-creating the CSRF token.
     *
     * @var CsrfTokenFactory
     *
     */
    protected $csrf_token_factory;

    /**
     *
     * Incoming cookies from the client, typically a copy of the $_COOKIE
     * superglobal.
     *
     * @var array
     *
     */
    protected $cookies;

    /**
     *
     * Session cookie parameters.
     *
     * @var array
     *
     */
    protected $cookie_params = array();

    /**
     *
     * An object to intercept PHP function calls; this makes testing easier.
     *
     * @var Phpfunc
     *
     */
    protected $phpfunc;

    /**
     *
     * A callable to invoke when deleting the session cookie. The callable
     * should have the signature ...
     *
     *      function ($cookie_name, $cookie_params)
     *
     * ... and return null.
     *
     * @var callable|null
     *
     * @see setDeleteCookie()
     *
     */
    protected $delete_cookie;

    /**
     *
     * Have the flash values been moved forward?
     *
     * @var bool
     *
     */
    protected $flash_moved = false;

    /**
     *
     * Constructor
     *
     * @param SegmentFactory $segment_factory A session segment factory.
     *
     * @param CsrfTokenFactory $csrf_token_factory A CSRF token factory.
     *
     * @param Phpfunc $phpfunc An object to intercept PHP function calls;
     * this makes testing easier.
     *
     * @param array $cookies Optional: An array of cookies from the client, typically a
     * copy of $_COOKIE. Empty array by default.
     *
     * @param callable|null $delete_cookie Optional: An alternative callable
     * to invoke when deleting the session cookie. Defaults to `null`.
     *
     */
    public function __construct(
        SegmentFactory $segment_factory,
        CsrfTokenFactory $csrf_token_factory,
        Phpfunc $phpfunc,
        array $cookies = array(),
        $delete_cookie = null
    ) {
        $this->segment_factory    = $segment_factory;
        $this->csrf_token_factory = $csrf_token_factory;
        $this->phpfunc            = $phpfunc;
        $this->cookies            = $cookies;

        $this->setDeleteCookie($delete_cookie);

        $this->cookie_params = $this->phpfunc->session_get_cookie_params();
    }

    /**
     *
     * Sets the delete-cookie callable.
     *
     * If parameter is `null`, the session cookie will be deleted using the
     * traditional way, i.e. using an expiration date in the past.
     *
     * @param callable|null $delete_cookie The callable to invoke when deleting the
     * session cookie.
     *
     */
    public function setDeleteCookie($delete_cookie)
    {
        $this->delete_cookie = $delete_cookie;
        if (! $this->delete_cookie) {
            $phpfunc = $this->phpfunc;
            $this->delete_cookie = function (
                $name,
                $params
            ) use ($phpfunc) {
                $phpfunc->setcookie(
                    $name,
                    '',
                    time() - 42000,
                    $params['path'],
                    $params['domain']
                );
            };
        }
    }

    /**
     *
     * Gets a new session segment instance by name. Segments with the same
     * name will be different objects but will reference the same $_SESSION
     * values, so it is possible to have two or more objects that share state.
     * For good or bad, this a function of how $_SESSION works.
     *
     * @param string $name The name of the session segment, typically a
     * fully-qualified class name.
     *
     * @return Segment New Segment instance.
     *
     */
    public function getSegment($name)
    {
        return $this->segment_factory->newInstance($this, $name);
    }

    /**
     *
     * Is a session available to be resumed?
     *
     * @return bool
     *
     */
    public function isResumable()
    {
        $name = $this->getName();
        return isset($this->cookies[$name]);
    }

    /**
     *
     * Is the session already started?
     *
     * @return bool
     *
     */
    public function isStarted()
    {
        $started = $this->phpfunc->session_status() === PHP_SESSION_ACTIVE;

        // if the session was started externally, move the flash values forward
        if ($started && ! $this->flash_moved) {
            $this->moveFlash();
        }

        // done
        return $started;
    }

    /**
     *
     * Starts a new or existing session.
     *
     * @return bool
     *
     */
    public function start()
    {
        $result = $this->phpfunc->session_start();
        if ($result && ! $this->flash_moved) {
            $this->moveFlash();
        }
        return $result;
    }

    /**
     *
     * Moves the "next" flash values to the "now" values, thereby clearing the
     * "next" values.
     *
     * @return null
     *
     */
    protected function moveFlash()
    {
        if (! isset($_SESSION[Session::FLASH_NEXT])) {
            $_SESSION[Session::FLASH_NEXT] = array();
        }
        $_SESSION[Session::FLASH_NOW] = $_SESSION[Session::FLASH_NEXT];
        $_SESSION[Session::FLASH_NEXT] = array();
        $this->flash_moved = true;
    }

    /**
     *
     * Resumes a session, but does not start a new one if there is no
     * existing one.
     *
     * @return bool
     *
     */
    public function resume()
    {
        if ($this->isStarted()) {
            return true;
        }

        if ($this->isResumable()) {
            return $this->start();
        }

        return false;
    }

    /**
     *
     * Clears all session variables across all segments.
     *
     * @return null
     *
     */
    public function clear()
    {
        return $this->phpfunc->session_unset();
    }

    /**
     *
     * Writes session data from all segments and ends the session.
     *
     * @return null
     *
     */
    public function commit()
    {
        return $this->phpfunc->session_write_close();
    }

    /**
     *
     * Destroys the session entirely.
     *
     * @return bool
     *
     * @see http://php.net/manual/en/function.session-destroy.php
     *
     */
    public function destroy()
    {
        if (! $this->isStarted()) {
            $this->start();
        }

        $name = $this->getName();
        $params = $this->getCookieParams();
        $this->clear();

        $destroyed = $this->phpfunc->session_destroy();
        if ($destroyed) {
            call_user_func($this->delete_cookie, $name, $params);
        }

        return $destroyed;
    }

    /**
     *
     * Returns the CSRF token, creating it if needed (and thereby starting a
     * session).
     *
     * @return CsrfToken
     *
     */
    public function getCsrfToken()
    {
        if (! $this->csrf_token) {
            $this->csrf_token = $this->csrf_token_factory->newInstance($this);
        }

        return $this->csrf_token;
    }

    // =======================================================================
    //
    // support and admin methods
    //

    /**
     *
     * Sets the session cache expire time.
     *
     * @param int $expire The expiration time in seconds.
     *
     * @return int
     *
     * @see session_cache_expire()
     *
     */
    public function setCacheExpire($expire)
    {
        return $this->phpfunc->session_cache_expire($expire);
    }

    /**
     *
     * Gets the session cache expire time.
     *
     * @return int The cache expiration time in seconds.
     *
     * @see session_cache_expire()
     *
     */
    public function getCacheExpire()
    {
        return $this->phpfunc->session_cache_expire();
    }

    /**
     *
     * Sets the session cache limiter value.
     *
     * @param string $limiter The limiter value.
     *
     * @return string
     *
     * @see session_cache_limiter()
     *
     */
    public function setCacheLimiter($limiter)
    {
        return $this->phpfunc->session_cache_limiter($limiter);
    }

    /**
     *
     * Gets the session cache limiter value.
     *
     * @return string The limiter value.
     *
     * @see session_cache_limiter()
     *
     */
    public function getCacheLimiter()
    {
        return $this->phpfunc->session_cache_limiter();
    }

    /**
     *
     * Sets the session cookie params.  Param array keys are:
     *
     * - `lifetime` : Lifetime of the session cookie, defined in seconds.
     *
     * - `path` : Path on the domain where the cookie will work.
     *   Use a single slash ('/') for all paths on the domain.
     *
     * - `domain` : Cookie domain, for example 'www.php.net'.
     *   To make cookies visible on all subdomains then the domain must be
     *   prefixed with a dot like '.php.net'.
     *
     * - `secure` : If TRUE cookie will only be sent over secure connections.
     *
     * - `httponly` : If set to TRUE then PHP will attempt to send the httponly
     *   flag when setting the session cookie.
     *
     * - `samesite` : Set if the cookie should be restricted to a first-party or same-site context.
     *   Possible values are 'Lax', 'Strict' or 'None'.
     *
     * @param array $params The array of session cookie param keys and values.
     *
     * @return null
     *
     * @throws SessionAlreadyStarted
     *
     * @see session_set_cookie_params()
     *
     */
    public function setCookieParams(array $params)
    {
        if ($this->isStarted()) {
            throw new SessionAlreadyStarted();
        }

        $this->cookie_params = array_merge($this->cookie_params, $params);
        $this->phpfunc->session_set_cookie_params($this->cookie_params);
    }

    /**
     *
     * Gets the session cookie params.
     *
     * @return array
     *
     */
    public function getCookieParams()
    {
        return $this->cookie_params;
    }

    /**
     *
     * Gets the current session id.
     *
     * @return string
     *
     */
    public function getId()
    {
        return $this->phpfunc->session_id();
    }

    /**
     *
     * Regenerates and replaces the current session id; also regenerates the
     * CSRF token value if one exists.
     *
     * @return bool True if regeneration worked, false if not.
     *
     */
    public function regenerateId()
    {
        $result = $this->phpfunc->session_regenerate_id(true);
        if ($result && $this->csrf_token) {
            $this->csrf_token->regenerateAllKeyValues();
        }
        return $result;
    }

    /**
     *
     * Sets the current session name.
     *
     * @param string $name The session name to use.
     *
     * @return string
     *
     * @see session_name()
     *
     */
    public function setName($name)
    {
        return $this->phpfunc->session_name($name);
    }

    /**
     *
     * Returns the current session name.
     *
     * @return string
     *
     */
    public function getName()
    {
        return $this->phpfunc->session_name();
    }

    /**
     *
     * Sets the session save path.
     *
     * @param string $path The new save path.
     *
     * @return string
     *
     * @see session_save_path()
     *
     */
    public function setSavePath($path)
    {
        return $this->phpfunc->session_save_path($path);
    }

    /**
     *
     * Gets the session save path.
     *
     * @return string
     *
     * @see session_save_path()
     *
     */
    public function getSavePath()
    {
        return $this->phpfunc->session_save_path();
    }
}


================================================
FILE: src/SessionFactory.php
================================================
<?php
/**
 *
 * This file is part of Aura for PHP.
 *
 * @license http://opensource.org/licenses/mit-license.php MIT
 *
 */
namespace Aura\Session;

/**
 *
 * A factory to create a Session manager.
 *
 * @package Aura.Session
 *
 */
class SessionFactory
{
    /**
     *
     * Creates a new Session manager.
     *
     * @param array $cookies An array of cookie values, typically $_COOKIE.
     *
     * @param callable|null $delete_cookie Optional: An alternative callable
     * to invoke when deleting the session cookie. Defaults to `null`.
     *
     * @return Session New Session manager instance
     */
    public function newInstance(array $cookies, $delete_cookie = null)
    {
        $phpfunc = new Phpfunc;
        return new Session(
            new SegmentFactory,
            new CsrfTokenFactory(new Randval()),
            $phpfunc,
            $cookies,
            $delete_cookie
        );
    }
}


================================================
FILE: tests/CsrfTokenTest.php
================================================
<?php
namespace Aura\Session;

use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use PHPUnit\Framework\TestCase;

#[RunTestsInSeparateProcesses]
class CsrfTokenTest extends TestCase
{
    protected $session;

    protected $csrf_token;

    protected $name = __CLASS__;

    protected $phpfunc;

    protected function setUp(): void
    {
        $this->phpfunc = new Phpfunc;

        $this->session = new Session(
            new SegmentFactory,
            new CsrfTokenFactory(new Randval()),
            $this->phpfunc,
            $_COOKIE
        );
    }

    public function teardown(): void
    {
        session_unset();
        if (session_id() !== '') {
            session_destroy();
        }
    }

    public function testLaziness()
    {
        $this->assertFalse($this->session->isStarted());
        $token = $this->session->getCsrfToken();
        $this->assertFalse($this->session->isStarted());
        $token->getValue('__csrf');
        $this->assertTrue($this->session->isStarted());
    }

    public function testGetAndRegenerateValue()
    {
        $token = $this->session->getCsrfToken();

        $old = $token->getValue();
        $this->assertNotEmpty($old);

        $token->regenerateValue();
        $new = $token->getValue();
        $this->assertNotEquals($old, $new);
    }

    public function testIsValid()
    {
        $token = $this->session->getCsrfToken();
        $value = $token->getValue();

        $this->assertTrue($token->isValid($value));
        $token->regenerateValue();
        $this->assertFalse($token->isValid($value));
    }

    public function testDifferentTokens()
    {
        $this->assertFalse($this->session->isStarted());
        $token = $this->session->getCsrfToken();

        $value1 = $token->getValue('__csrf1');
        $value2 = $token->getValue('__csrf2');
        $value3 = $token->getValue('__csrf3');

        $this->assertTrue($token->isValid($value1, '__csrf1'));
        $this->assertTrue($token->isValid($value2, '__csrf2'));
        $this->assertTrue($token->isValid($value3, '__csrf3'));

        // After isValid call, the value stored in session will not be reset
        $this->assertEquals($value3, $token->getValue('__csrf3'));

        $this->assertNotEquals($value3, $token->regenerateValue('__csrf3'));
    }
}


================================================
FILE: tests/FakeSessionHandler.php
================================================
<?php
namespace Aura\Session;

use SessionHandlerInterface;

// a session handler that does nothing, for testing purposes only
class FakeSessionHandler implements SessionHandlerInterface
{
    public array $data = [];

    public function close(): bool
    {
        return true;
    }

    public function destroy(string $session_id): bool
    {
        unset($this->data[$session_id]);
        return true;
    }

    public function gc(int $maxlifetime): int|false
    {
        return 0;
    }

    public function open(string $save_path, string $session_id): bool
    {
        return true;
    }

    public function read(string $session_id): string|false
    {
        return $this->data[$session_id] ?? '';
    }

    public function write(string $session_id, string $session_data): bool
    {
        $this->data[$session_id] = $session_data;
        return true;
    }
}


================================================
FILE: tests/Issue23Test.php
================================================
<?php
namespace Aura\Session;

use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use PHPUnit\Framework\TestCase;

#[RunTestsInSeparateProcesses]
class Issue23Test extends TestCase
{
    protected $session;

    protected $segment;

    protected $name = __CLASS__;

    protected function setUp(): void
    {
        $this->session = $this->newSession();
        $this->segment = $this->session->getSegment($this->name);
    }

    protected function newSession(array $cookies = array())
    {
        // start session earlier
        session_start();
        return new Session(
            new SegmentFactory,
            new CsrfTokenFactory(new Randval()),
            new Phpfunc,
            $cookies
        );
    }

    public function testFlash()
    {
        // set a value
        $this->segment->setFlash('foo', 'bar');
        $expect = 'bar';
        $this->assertSame($expect, $this->segment->getFlashNext('foo'));
        $this->assertNull($this->segment->getFlash('foo'));

        // set a value and make it available now
        $this->segment->setFlashNow('baz', 'dib');
        $expect = 'dib';
        $this->assertSame($expect, $this->segment->getFlash('baz'));
        $this->assertSame($expect, $this->segment->getFlashNext('baz'));

        // clear the next values
        $this->segment->clearFlash();
        $this->assertNull($this->segment->getFlashNext('foo'));
        $this->assertNull($this->segment->getFlashNext('baz'));
        $expect = 'dib';
        $this->assertSame($expect, $this->segment->getFlash('baz'));

        // set some current values and make sure they get kept
        $now =& $_SESSION[Session::FLASH_NOW][$this->name];
        $now['foo'] = 'bar';
        $now['baz'] = 'dib';
        $this->segment->keepFlash();
        $this->assertSame('bar', $this->segment->getFlashNext('foo'));
        $this->assertSame('dib', $this->segment->getFlashNext('baz'));

        // clear the current and future values
        $this->segment->clearFlashNow();
        $this->assertNull($this->segment->getFlash('foo'));
        $this->assertNull($this->segment->getFlashNext('foo'));
        $this->assertNull($this->segment->getFlash('baz'));
        $this->assertNull($this->segment->getFlashNext('baz'));
    }
}


================================================
FILE: tests/SegmentTest.php
================================================
<?php
namespace Aura\Session;

use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use PHPUnit\Framework\TestCase;

#[RunTestsInSeparateProcesses]
class SegmentTest extends TestCase
{
    protected $session;

    protected $segment;

    protected $name = __CLASS__;

    protected function setUp(): void
    {
        $this->session = $this->newSession();
        $this->segment = $this->session->getSegment($this->name);
    }

    protected function newSession(array $cookies = array())
    {
        return new Session(
            new SegmentFactory,
            new CsrfTokenFactory(new Randval()),
            new Phpfunc,
            $cookies
        );
    }

    protected function getValue($key = null)
    {
        if ($key) {
            return $_SESSION[$this->name][$key];
        } else {
            return $_SESSION[$this->name];
        }
    }

    protected function setValue($key, $val)
    {
        $_SESSION[$this->name][$key] = $val;
    }

    public function testMagicMethods()
    {
        $this->assertNull($this->segment->get('foo'));

        $this->segment->set('foo', 'bar');
        $this->assertSame('bar', $this->segment->get('foo'));
        $this->assertSame('bar', $this->getValue('foo'));

        $this->setValue('foo', 'zim');
        $this->assertSame('zim', $this->segment->get('foo'));
    }

    public function testClear()
    {
        $this->segment->set('foo', 'bar');
        $this->segment->set('baz', 'dib');
        $this->assertSame('bar', $this->getValue('foo'));
        $this->assertSame('dib', $this->getValue('baz'));

        // now clear the data
        $this->segment->clear();
        $this->assertSame(array(), $this->getValue());
        $this->assertNull($this->segment->get('foo'));
        $this->assertNull($this->segment->get('baz'));
    }

    public function testGetSegment()
    {
        $this->segment->set('foo', 'bar');
        $this->segment->set('baz', 'dib');
        $this->assertSame('bar', $this->getValue('foo'));
        $this->assertSame('dib', $this->getValue('baz'));

        // now get the data
        $this->assertSame(array('foo' => 'bar', 'baz' => 'dib'), $this->segment->getSegment());
    }

    public function testFlash()
    {
        // set a value
        $this->segment->setFlash('foo', 'bar');
        $expect = 'bar';
        $this->assertSame($expect, $this->segment->getFlashNext('foo'));
        $this->assertNull($this->segment->getFlash('foo'));

        // set a value and make it available now
        $this->segment->setFlashNow('baz', 'dib');
        $expect = 'dib';
        $this->assertSame($expect, $this->segment->getFlash('baz'));
        $this->assertSame($expect, $this->segment->getFlashNext('baz'));

        // clear the next values
        $this->segment->clearFlash();
        $this->assertNull($this->segment->getFlashNext('foo'));
        $this->assertNull($this->segment->getFlashNext('baz'));
        $expect = 'dib';
        $this->assertSame($expect, $this->segment->getFlash('baz'));

        // set some current values and make sure they get kept
        $now =& $_SESSION[Session::FLASH_NOW][$this->name];
        $now['foo'] = 'bar';
        $now['baz'] = 'dib';
        $this->segment->keepFlash();
        $this->assertSame('bar', $this->segment->getFlashNext('foo'));
        $this->assertSame('dib', $this->segment->getFlashNext('baz'));

        // clear the current and future values
        $this->segment->clearFlashNow();
        $this->assertNull($this->segment->getFlash('foo'));
        $this->assertNull($this->segment->getFlashNext('foo'));
        $this->assertNull($this->segment->getFlash('baz'));
        $this->assertNull($this->segment->getFlashNext('baz'));
    }

    public function testGetDoesNotStartSession()
    {
        $this->assertFalse($this->session->isStarted());
        $foo = $this->segment->get('foo');
        $this->assertNull($foo);
        $this->assertFalse($this->session->isStarted());
    }

    public function testGetResumesSession()
    {
        // fake a cookie
        $cookies = array(
            $this->session->getName() => 'fake-cookie-value',
        );
        $this->session = $this->newSession($cookies);

        // should be active now, even though not started
        $this->assertTrue($this->session->isResumable());

        // reset the segment to use the new session manager
        $this->segment = $this->session->getSegment($this->name);

        // this should restart the session
        $foo = $this->segment->get('foo');
        $this->assertTrue($this->session->isStarted());
    }

    public function testSetStartsSessionAndCanReadAfter()
    {
        // no session yet
        $this->assertFalse($this->session->isStarted());

        // set it
        $this->segment->set('foo', 'bar');

        // session should have started
        $this->assertTrue($this->session->isStarted());

        // get it from the session
        $foo = $this->segment->get('foo');
        $this->assertSame('bar', $foo);

        // make sure it's actually in $_SESSION
        $this->assertSame($foo, $_SESSION[$this->name]['foo']);
    }

    public function testClearDoesNotStartSession()
    {
        $this->assertFalse($this->session->isStarted());
        $this->segment->clear();
        $this->assertFalse($this->session->isStarted());
    }

    public function testSetFlashStartsSessionAndCanReadAfter()
    {
        // no session yet
        $this->assertFalse($this->session->isStarted());

        // set it
        $this->segment->setFlash('foo', 'bar');

        // session should have started
        $this->assertTrue($this->session->isStarted());

        // should see it in the session
        $actual = $_SESSION[Session::FLASH_NEXT][$this->name]['foo'];
        $this->assertSame('bar', $actual);

    }

    public function testGetFlashDoesNotStartSession()
    {
        $this->assertFalse($this->session->isStarted());
        $this->assertNull($this->segment->getFlash('foo'));
        $this->assertFalse($this->session->isStarted());
    }

    public function testRemoveDoesNothingWhenSessionNotStarted(): void
    {
        $this->segment->remove('foo');
        $this->assertFalse($this->session->isStarted());
    }

    public function testRemoveKey(){
        $this->segment->set('foo', 'bar');
        $this->segment->set('baz', 'dib');
        $this->assertSame('bar', $this->getValue('foo'));
        $this->assertSame('dib', $this->getValue('baz'));

        // now remove the key
        $this->segment->remove('foo');
        $this->assertNull($this->segment->get('foo'));
        $this->assertArrayNotHasKey('foo', $_SESSION[$this->name]);
        $this->assertSame('dib', $this->getValue('baz'));
    }

    public function testRemoveSegment(){
        $this->segment->set('foo', 'bar');
        $this->segment->set('baz', 'dib');
        $this->assertSame('bar', $this->getValue('foo'));
        $this->assertSame('dib', $this->getValue('baz'));

        // now remove the key
        $this->segment->remove();
        $this->assertArrayNotHasKey($this->name, $_SESSION);
    }

    public function testRestartSessionFlashNotMove()
    {
        $this->assertFalse($this->session->isStarted());

        // set it
        $this->segment->setFlash('foo', 'bar');

        // session should have started
        $this->assertTrue($this->session->isStarted());

        // should see it in the session
        $actual = $_SESSION[Session::FLASH_NEXT][$this->name]['foo'];
        $this->assertSame('bar', $actual);

        $this->session->commit();

        $this->assertFalse($this->session->isStarted());

        $this->session->start();

        // should see it in the session
        $actual = $_SESSION[Session::FLASH_NEXT][$this->name]['foo'];
        $this->assertSame('bar', $actual);
    }
}


================================================
FILE: tests/SessionFactoryTest.php
================================================
<?php
namespace Aura\Session;

use PHPUnit\Framework\TestCase;

class SessionFactoryTest extends TestCase
{
    public function testNewInstance()
    {
        $session_factory = new SessionFactory;
        $session = $session_factory->newInstance($_COOKIE);
        $this->assertInstanceOf('Aura\Session\Session', $session);
    }
}


================================================
FILE: tests/SessionTest.php
================================================
<?php
namespace Aura\Session;

use Aura\Session\Exception\SessionAlreadyStarted;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
use PHPUnit\Framework\TestCase;

#[RunTestsInSeparateProcesses]
class SessionTest extends TestCase
{
    /** @var Phpfunc */
    private $phpfunc;

    // the session object
    protected $session;

    protected function setUp(): void
    {
        $this->phpfunc = new Phpfunc;
        $handler = new FakeSessionHandler();
        session_set_save_handler($handler, true);
        $this->session = $this->newSession();
    }

    protected function newSession(array $cookies = array())
    {
        return new Session(
            new SegmentFactory,
            new CsrfTokenFactory(new Randval()),
            $this->phpfunc,
            $cookies
        );
    }

    public function teardown(): void
    {
        session_unset();
        if (session_id() !== '') {
            session_destroy();
        }
    }

    public function testStart()
    {
        $this->session->start();
        $this->assertTrue($this->session->isStarted());
    }

    public function testClear()
    {
        // get a test segment and set some data
        $segment = $this->session->getSegment('test');
        $segment->set('foo', 'bar');
        $segment->set('baz', 'dib');

        $expect = array(
            Session::FLASH_NEXT => array(
                'test' => array(),
            ),
            Session::FLASH_NOW => array(
                'test' => array(),
            ),
            'test' => array(
                'foo' => 'bar',
                'baz' => 'dib',
            ),
        );

        $this->assertSame($expect, $_SESSION);

        // now clear it
        $this->session->clear();
        $this->assertSame(array(), $_SESSION);
    }

    public function testDestroy()
    {
        // get a test segment and set some data
        $segment = $this->session->getSegment('test');
        $segment->set('foo', 'bar');
        $segment->set('baz', 'dib');

        $this->assertTrue($this->session->isStarted());

        $expect = array(
            Session::FLASH_NEXT => array(
                'test' => array(),
            ),
            Session::FLASH_NOW => array(
                    'test' => array(),
            ),
            'test' => array(
                'foo' => 'bar',
                'baz' => 'dib',
            ),
        );

        $this->assertSame($expect, $_SESSION);

        // now destroy it
        $this->session->destroy();
        $this->assertFalse($this->session->isStarted());
    }

    public function testCommit()
    {
        $this->session->commit();
        $this->assertFalse($this->session->isStarted());
    }

    public function testCommitAndDestroy()
    {
        // get a test segment and set some data
        $segment = $this->session->getSegment('test');
        $segment->set('foo', 'bar');
        $segment->set('baz', 'dib');

        $this->assertTrue($this->session->isStarted());

        $expect = array(
            Session::FLASH_NEXT => array(
                'test' => array(),
            ),
            Session::FLASH_NOW => array(
                'test' => array(),
            ),
            'test' => array(
                'foo' => 'bar',
                'baz' => 'dib',
            ),
        );

        $this->assertSame($expect, $_SESSION);

        $this->session->commit();
        $this->session->destroy();
        $segment = $this->session->getSegment('test');
        $this->assertSame(array(), $_SESSION);
    }

    public function testGetSegment()
    {
        $segment = $this->session->getSegment('test');
        $this->assertInstanceof('Aura\Session\Segment', $segment);
    }

    public function testGetCsrfToken()
    {
        $actual = $this->session->getCsrfToken();
        $expect = 'Aura\Session\CsrfToken';
        $this->assertInstanceOf($expect, $actual);
    }

    public function testisResumable()
    {
        // should not look active
        $this->assertFalse($this->session->isResumable());

        // fake a cookie
        $cookies = array(
            $this->session->getName() => 'fake-cookie-value',
        );
        $this->session = $this->newSession($cookies);

        // now it should look active
        $this->assertTrue($this->session->isResumable());
    }

    public function testGetAndRegenerateId()
    {
        $this->session->start();
        $old_id = $this->session->getId();
        $this->session->regenerateId();
        $new_id = $this->session->getId();
        $this->assertTrue($old_id != $new_id);

        // check the csrf token as well
        $old_value = $this->session->getCsrfToken()->getValue();
        $this->session->regenerateId();
        $new_value = $this->session->getCsrfToken()->getValue();
        $this->assertNotEquals($old_value, $new_value);

        // Regenerates multiple keys
        $csrf1_old_value = $this->session->getCsrfToken('csrf1')->getValue();
        $csrf2_old_value = $this->session->getCsrfToken('csrf2')->getValue();
        $this->session->regenerateId();
        $csrf1_new_value = $this->session->getCsrfToken('csrf1')->getValue();
        $csrf2_new_value = $this->session->getCsrfToken('csrf2')->getValue();
        $this->assertNotSame($csrf1_old_value, $csrf1_new_value);
        $this->assertNotSame($csrf2_old_value, $csrf2_new_value);
    }

    public function testSetAndGetName()
    {
        $expect = 'new_name';
        $this->session->setName($expect);
        $actual = $this->session->getName();
        $this->assertSame($expect, $actual);
    }

    public function testSetAndGetSavePath()
    {
        $expect = '/new/save/path';
        $this->session->setSavePath($expect);
        $actual = $this->session->getSavePath();
        $this->assertSame($expect, $actual);
    }

    public function testSetAndGetCookieParams()
    {
        $expect = $this->session->getCookieParams();
        $expect['lifetime'] = '999';
        $this->session->setCookieParams($expect);
        $actual = $this->session->getCookieParams();
        $this->assertSame($expect, $actual);

        // Cannot change session cookie parameters when session is active
        $this->expectException(SessionAlreadyStarted::class);
        $this->session->start();
        $newParams = $expect;
        $newParams['lifetime'] = '0';
        $this->session->setCookieParams($newParams);
    }

    public function testSetAndGetCacheExpire()
    {
        $expect = 123;
        $this->session->setCacheExpire($expect);
        $actual = $this->session->getCacheExpire();
        $this->assertSame($expect, $actual);
    }

    public function testSetAndGetCacheLimiter()
    {
        $expect = 'private_no_cache';
        $this->session->setCacheLimiter($expect);
        $actual = $this->session->getCacheLimiter();
        $this->assertSame($expect, $actual);
    }

    public function testResume()
    {
        // should not look active
        $this->assertFalse($this->session->isResumable());
        $this->assertFalse($this->session->resume());

        // fake a cookie so a session looks available
        $cookies = array(
            $this->session->getName() => 'fake-cookie-value',
        );
        $this->session = $this->newSession($cookies);
        $this->assertTrue($this->session->resume());

        // now it should already active
        $this->assertTrue($this->session->resume());
    }

}
Download .txt
gitextract_njjloc6y/

├── .github/
│   └── workflows/
│       └── continuous-integration.yml
├── .gitignore
├── .scrutinizer.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── codecov.yml
├── composer.json
├── docs/
│   ├── _bookdown.json
│   └── getting-started.md
├── phpunit.php
├── phpunit.xml.dist
├── src/
│   ├── CsrfToken.php
│   ├── CsrfTokenFactory.php
│   ├── Exception/
│   │   └── SessionAlreadyStarted.php
│   ├── Exception.php
│   ├── Phpfunc.php
│   ├── Randval.php
│   ├── RandvalInterface.php
│   ├── Segment.php
│   ├── SegmentFactory.php
│   ├── SegmentInterface.php
│   ├── Session.php
│   └── SessionFactory.php
└── tests/
    ├── CsrfTokenTest.php
    ├── FakeSessionHandler.php
    ├── Issue23Test.php
    ├── SegmentTest.php
    ├── SessionFactoryTest.php
    └── SessionTest.php
Download .txt
SYMBOL INDEX (134 symbols across 18 files)

FILE: src/CsrfToken.php
  class CsrfToken (line 18) | class CsrfToken
    method __construct (line 48) | public function __construct(SegmentInterface $segment, RandvalInterfac...
    method isValid (line 65) | public function isValid($value, $key = 'value')
    method getValue (line 79) | public function getValue($key = 'value')
    method regenerateValue (line 97) | public function regenerateValue($key = 'value')
    method regenerateAllKeyValues (line 111) | public function regenerateAllKeyValues()

FILE: src/CsrfTokenFactory.php
  class CsrfTokenFactory (line 18) | class CsrfTokenFactory
    method __construct (line 37) | public function __construct(RandvalInterface $randval)
    method newInstance (line 51) | public function newInstance(Session $session)

FILE: src/Exception.php
  class Exception (line 18) | class Exception extends \Exception

FILE: src/Exception/SessionAlreadyStarted.php
  class SessionAlreadyStarted (line 7) | class SessionAlreadyStarted extends Exception

FILE: src/Phpfunc.php
  class Phpfunc (line 18) | class Phpfunc
    method __call (line 31) | public function __call($func, $args)

FILE: src/Randval.php
  class Randval (line 20) | class Randval implements RandvalInterface
    method generate (line 29) | public function generate()

FILE: src/RandvalInterface.php
  type RandvalInterface (line 18) | interface RandvalInterface
    method generate (line 27) | public function generate();

FILE: src/Segment.php
  class Segment (line 18) | class Segment implements SegmentInterface
    method __construct (line 47) | public function __construct(Session $session, $name)
    method get (line 64) | public function get(string $key, $alt = null)
    method getSegment (line 79) | public function getSegment()
    method set (line 96) | public function set(string $key, $val)
    method clear (line 109) | public function clear()
    method remove (line 122) | public function remove(?string $key = null) {
    method setFlash (line 146) | public function setFlash(string $key, $val)
    method getFlash (line 163) | public function getFlash(string $key, $alt = null)
    method clearFlash (line 178) | public function clearFlash()
    method getFlashNext (line 196) | public function getFlashNext(string $key, $alt = null)
    method setFlashNow (line 213) | public function setFlashNow(string $key, $val)
    method clearFlashNow (line 227) | public function clearFlashNow()
    method keepFlash (line 243) | public function keepFlash()
    method resumeSession (line 261) | protected function resumeSession()
    method load (line 278) | protected function load()
    method resumeOrStartSession (line 300) | protected function resumeOrStartSession()

FILE: src/SegmentFactory.php
  class SegmentFactory (line 18) | class SegmentFactory
    method newInstance (line 29) | public function newInstance(Session $session, $name)

FILE: src/SegmentInterface.php
  type SegmentInterface (line 18) | interface SegmentInterface
    method get (line 31) | public function get(string $key, $alt = null);
    method getSegment (line 40) | public function getSegment();
    method set (line 51) | public function set(string $key, $val);
    method clear (line 60) | public function clear();
    method setFlash (line 71) | public function setFlash(string $key, $val);
    method getFlash (line 84) | public function getFlash(string $key, $alt = null);
    method clearFlash (line 93) | public function clearFlash();
    method getFlashNext (line 106) | public function getFlashNext(string $key, $alt = null);
    method setFlashNow (line 117) | public function setFlashNow(string $key, $val);
    method clearFlashNow (line 126) | public function clearFlashNow();
    method keepFlash (line 136) | public function keepFlash();
    method remove (line 143) | public function remove(?string $key = null);

FILE: src/Session.php
  class Session (line 21) | class Session
    method __construct (line 139) | public function __construct(
    method setDeleteCookie (line 167) | public function setDeleteCookie($delete_cookie)
    method getSegment (line 200) | public function getSegment($name)
    method isResumable (line 212) | public function isResumable()
    method isStarted (line 225) | public function isStarted()
    method start (line 245) | public function start()
    method moveFlash (line 262) | protected function moveFlash()
    method resume (line 280) | public function resume()
    method clear (line 300) | public function clear()
    method commit (line 312) | public function commit()
    method destroy (line 326) | public function destroy()
    method getCsrfToken (line 352) | public function getCsrfToken()
    method setCacheExpire (line 377) | public function setCacheExpire($expire)
    method getCacheExpire (line 391) | public function getCacheExpire()
    method setCacheLimiter (line 407) | public function setCacheLimiter($limiter)
    method getCacheLimiter (line 421) | public function getCacheLimiter()
    method setCookieParams (line 456) | public function setCookieParams(array $params)
    method getCookieParams (line 473) | public function getCookieParams()
    method getId (line 485) | public function getId()
    method regenerateId (line 498) | public function regenerateId()
    method setName (line 518) | public function setName($name)
    method getName (line 530) | public function getName()
    method setSavePath (line 546) | public function setSavePath($path)
    method getSavePath (line 560) | public function getSavePath()

FILE: src/SessionFactory.php
  class SessionFactory (line 18) | class SessionFactory
    method newInstance (line 31) | public function newInstance(array $cookies, $delete_cookie = null)

FILE: tests/CsrfTokenTest.php
  class CsrfTokenTest (line 7) | #[RunTestsInSeparateProcesses]
    method setUp (line 18) | protected function setUp(): void
    method teardown (line 30) | public function teardown(): void
    method testLaziness (line 38) | public function testLaziness()
    method testGetAndRegenerateValue (line 47) | public function testGetAndRegenerateValue()
    method testIsValid (line 59) | public function testIsValid()
    method testDifferentTokens (line 69) | public function testDifferentTokens()

FILE: tests/FakeSessionHandler.php
  class FakeSessionHandler (line 7) | class FakeSessionHandler implements SessionHandlerInterface
    method close (line 11) | public function close(): bool
    method destroy (line 16) | public function destroy(string $session_id): bool
    method gc (line 22) | public function gc(int $maxlifetime): int|false
    method open (line 27) | public function open(string $save_path, string $session_id): bool
    method read (line 32) | public function read(string $session_id): string|false
    method write (line 37) | public function write(string $session_id, string $session_data): bool

FILE: tests/Issue23Test.php
  class Issue23Test (line 7) | #[RunTestsInSeparateProcesses]
    method setUp (line 16) | protected function setUp(): void
    method newSession (line 22) | protected function newSession(array $cookies = array())
    method testFlash (line 34) | public function testFlash()

FILE: tests/SegmentTest.php
  class SegmentTest (line 7) | #[RunTestsInSeparateProcesses]
    method setUp (line 16) | protected function setUp(): void
    method newSession (line 22) | protected function newSession(array $cookies = array())
    method getValue (line 32) | protected function getValue($key = null)
    method setValue (line 41) | protected function setValue($key, $val)
    method testMagicMethods (line 46) | public function testMagicMethods()
    method testClear (line 58) | public function testClear()
    method testGetSegment (line 72) | public function testGetSegment()
    method testFlash (line 83) | public function testFlash()
    method testGetDoesNotStartSession (line 120) | public function testGetDoesNotStartSession()
    method testGetResumesSession (line 128) | public function testGetResumesSession()
    method testSetStartsSessionAndCanReadAfter (line 147) | public function testSetStartsSessionAndCanReadAfter()
    method testClearDoesNotStartSession (line 166) | public function testClearDoesNotStartSession()
    method testSetFlashStartsSessionAndCanReadAfter (line 173) | public function testSetFlashStartsSessionAndCanReadAfter()
    method testGetFlashDoesNotStartSession (line 190) | public function testGetFlashDoesNotStartSession()
    method testRemoveDoesNothingWhenSessionNotStarted (line 197) | public function testRemoveDoesNothingWhenSessionNotStarted(): void
    method testRemoveKey (line 203) | public function testRemoveKey(){
    method testRemoveSegment (line 216) | public function testRemoveSegment(){
    method testRestartSessionFlashNotMove (line 227) | public function testRestartSessionFlashNotMove()

FILE: tests/SessionFactoryTest.php
  class SessionFactoryTest (line 6) | class SessionFactoryTest extends TestCase
    method testNewInstance (line 8) | public function testNewInstance()

FILE: tests/SessionTest.php
  class SessionTest (line 8) | #[RunTestsInSeparateProcesses]
    method setUp (line 17) | protected function setUp(): void
    method newSession (line 25) | protected function newSession(array $cookies = array())
    method teardown (line 35) | public function teardown(): void
    method testStart (line 43) | public function testStart()
    method testClear (line 49) | public function testClear()
    method testDestroy (line 76) | public function testDestroy()
    method testCommit (line 105) | public function testCommit()
    method testCommitAndDestroy (line 111) | public function testCommitAndDestroy()
    method testGetSegment (line 141) | public function testGetSegment()
    method testGetCsrfToken (line 147) | public function testGetCsrfToken()
    method testisResumable (line 154) | public function testisResumable()
    method testGetAndRegenerateId (line 169) | public function testGetAndRegenerateId()
    method testSetAndGetName (line 193) | public function testSetAndGetName()
    method testSetAndGetSavePath (line 201) | public function testSetAndGetSavePath()
    method testSetAndGetCookieParams (line 209) | public function testSetAndGetCookieParams()
    method testSetAndGetCacheExpire (line 225) | public function testSetAndGetCacheExpire()
    method testSetAndGetCacheLimiter (line 233) | public function testSetAndGetCacheLimiter()
    method testResume (line 241) | public function testResume()
Condensed preview — 31 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (76K chars).
[
  {
    "path": ".github/workflows/continuous-integration.yml",
    "chars": 1374,
    "preview": "name: Continuous Integration\n\non:\n  push:\n  pull_request:\n  workflow_dispatch:\n\njobs:\n  build:\n\n    runs-on: ubuntu-late"
  },
  {
    "path": ".gitignore",
    "chars": 91,
    "preview": "/tests/container/vendor\n/tests/container/composer.*\n/composer.lock\n/vendor\n/.phpunit.cache\n"
  },
  {
    "path": ".scrutinizer.yml",
    "chars": 172,
    "preview": "build:\n  image: default-jammy\n  environment:\n    php: 8.4\n  nodes:\n    analysis:\n      tests:\n        override:\n        "
  },
  {
    "path": "CHANGELOG.md",
    "chars": 3191,
    "preview": "# CHANGELOG\n\n## 6.0.0\n\n- PHP 8.1+ is now required.\n- (CHG) Add `string` type declarations to `$key` parameters in `Segme"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 313,
    "preview": "# Contributing\n\nWe are happy to review any contributions you want to make. When contributing, please follow the rules ou"
  },
  {
    "path": "LICENSE",
    "chars": 1085,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2011-2022, Aura for PHP\n\nPermission is hereby granted, free of charge, to any perso"
  },
  {
    "path": "README.md",
    "chars": 2061,
    "preview": "# Aura Session\n\nProvides session management functionality, including lazy session starting,\nsession segments, next-reque"
  },
  {
    "path": "codecov.yml",
    "chars": 15,
    "preview": "comment: false\n"
  },
  {
    "path": "composer.json",
    "chars": 1002,
    "preview": "{\n    \"name\": \"aura/session\",\n    \"type\": \"library\",\n    \"description\": \"Provides session management functionality, incl"
  },
  {
    "path": "docs/_bookdown.json",
    "chars": 84,
    "preview": "{\n    \"title\": \"Aura.Session\",\n    \"content\": [\n        \"getting-started.md\"\n    ]\n}"
  },
  {
    "path": "docs/getting-started.md",
    "chars": 10459,
    "preview": "## Getting Started\n\n### Instantiation\n\nThe easiest way to get started is to use the _SessionFactory_ to create a _Sessio"
  },
  {
    "path": "phpunit.php",
    "chars": 282,
    "preview": "<?php\nerror_reporting(E_ALL);\n$autoloader = __DIR__ . '/vendor/autoload.php';\nif (! file_exists($autoloader)) {\n    echo"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 541,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNam"
  },
  {
    "path": "src/CsrfToken.php",
    "chars": 2498,
    "preview": "<?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"
  },
  {
    "path": "src/CsrfTokenFactory.php",
    "chars": 1013,
    "preview": "<?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"
  },
  {
    "path": "src/Exception/SessionAlreadyStarted.php",
    "chars": 121,
    "preview": "<?php\n\nnamespace Aura\\Session\\Exception;\n\nuse Aura\\Session\\Exception;\n\nclass SessionAlreadyStarted extends Exception\n{\n}"
  },
  {
    "path": "src/Exception.php",
    "chars": 258,
    "preview": "<?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"
  },
  {
    "path": "src/Phpfunc.php",
    "chars": 620,
    "preview": "<?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"
  },
  {
    "path": "src/Randval.php",
    "chars": 510,
    "preview": "<?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"
  },
  {
    "path": "src/RandvalInterface.php",
    "chars": 437,
    "preview": "<?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"
  },
  {
    "path": "src/Segment.php",
    "chars": 7105,
    "preview": "<?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"
  },
  {
    "path": "src/SegmentFactory.php",
    "chars": 538,
    "preview": "<?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"
  },
  {
    "path": "src/SegmentInterface.php",
    "chars": 3084,
    "preview": "<?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"
  },
  {
    "path": "src/Session.php",
    "chars": 12449,
    "preview": "<?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"
  },
  {
    "path": "src/SessionFactory.php",
    "chars": 922,
    "preview": "<?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"
  },
  {
    "path": "tests/CsrfTokenTest.php",
    "chars": 2322,
    "preview": "<?php\nnamespace Aura\\Session;\n\nuse PHPUnit\\Framework\\Attributes\\RunTestsInSeparateProcesses;\nuse PHPUnit\\Framework\\TestC"
  },
  {
    "path": "tests/FakeSessionHandler.php",
    "chars": 881,
    "preview": "<?php\nnamespace Aura\\Session;\n\nuse SessionHandlerInterface;\n\n// a session handler that does nothing, for testing purpose"
  },
  {
    "path": "tests/Issue23Test.php",
    "chars": 2272,
    "preview": "<?php\nnamespace Aura\\Session;\n\nuse PHPUnit\\Framework\\Attributes\\RunTestsInSeparateProcesses;\nuse PHPUnit\\Framework\\TestC"
  },
  {
    "path": "tests/SegmentTest.php",
    "chars": 7859,
    "preview": "<?php\nnamespace Aura\\Session;\n\nuse PHPUnit\\Framework\\Attributes\\RunTestsInSeparateProcesses;\nuse PHPUnit\\Framework\\TestC"
  },
  {
    "path": "tests/SessionFactoryTest.php",
    "chars": 334,
    "preview": "<?php\nnamespace Aura\\Session;\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass SessionFactoryTest extends TestCase\n{\n    public f"
  },
  {
    "path": "tests/SessionTest.php",
    "chars": 7461,
    "preview": "<?php\nnamespace Aura\\Session;\n\nuse Aura\\Session\\Exception\\SessionAlreadyStarted;\nuse PHPUnit\\Framework\\Attributes\\RunTes"
  }
]

About this extraction

This page contains the full source code of the auraphp/Aura.Session GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 31 files (69.7 KB), approximately 18.1k tokens, and a symbol index with 134 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!