First Commit

This commit is contained in:
marco
2026-09-04 14:54:13 -03:00
commit 9c54bd3ac7
4914 changed files with 789004 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
# editorconfig.org
root = true
[*]
charset = utf-8
indent_style = tab
trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true
[*.md]
indent_style = space
indent_size = 4
[*.yml]
indent_style = space
indent_size = 2
+6
View File
@@ -0,0 +1,6 @@
# IntelliJ
.idea/
# Composer
vendor/
composer.phar
+9
View File
@@ -0,0 +1,9 @@
<VirtualHost *:80>
DocumentRoot %TRAVIS_BUILD_DIR%
<IfModule mod_fastcgi.c>
AddHandler php5-fcgi .php
Action php5-fcgi /php5-fcgi
Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi
FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -host 127.0.0.1:9000 -pass-header Authorization
</IfModule>
</VirtualHost>
+29
View File
@@ -0,0 +1,29 @@
language: php
php:
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
cache:
directories:
- $HOME/.composer/cache
install:
- composer install
before_script:
- sudo apt-get update
- sudo apt-get install apache2 libapache2-mod-fastcgi
- sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf
- if [[ ${TRAVIS_PHP_VERSION:0:2} == "7." ]]; then sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.d/www.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.d/www.conf; fi
- sudo cp ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf.default ~/.phpenv/versions/$(phpenv version-name)/etc/php-fpm.conf
- sudo a2enmod actions fastcgi alias
- ~/.phpenv/versions/$(phpenv version-name)/sbin/php-fpm
- sudo cp -f .travis-ci-apache /etc/apache2/sites-available/default
- sudo sed -e "s?%TRAVIS_BUILD_DIR%?$(pwd)?g" --in-place /etc/apache2/sites-available/default
- sudo service apache2 restart
script: test_result="$(curl -s http://localhost/tests/)" && echo "$test_result" && echo "$test_result" | grep -q 'ALL TESTS PASSED' || exit 1
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) delight.im (https://www.delight.im/)
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.
+17
View File
@@ -0,0 +1,17 @@
# Migration
## General
Update your version of this library using Composer and its `composer update` or `composer require` commands [[?]](https://github.com/delight-im/Knowledge/blob/master/Composer%20(PHP).md#how-do-i-update-libraries-or-modules-within-my-application).
## From `v2.x.x` to `v3.x.x`
* The default domain scope for new `Cookie` instances is still the current host, but it does not include subdomains anymore, unless explicitly specified otherwise via the `Cookie#setDomain` method.
* For the domain scope, `www` subdomains are not automatically widened to the bare domain anymore. If you want to include the bare domain and all subdomains in addition to the `www` subdomain, you must now explicitly specifiy the bare domain instead of the `www` subdomain as the scope.
* When managing sessions via the methods `Session#start` or `Session#regenerate`, the session configuration is now correctly respected with regard to whether subdomains should be included in the domain scope or not. The scope is not automatically widened to include subdomains anymore.
* The second parameter of the `Cookie#setDomain` method, which was named `$keepWww`, has been removed.
* When creating `Cookie` instances from a string using the `Cookie#parse` method, the source’s decision of whether to include subdomains in the domain scope or not is now correctly respected. The scope is not automatically widened to include subdomains anymore.
## From `v1.x.x` to `v2.x.x`
* The license has been changed from the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0) to the [MIT License](https://opensource.org/licenses/MIT).
+203
View File
@@ -0,0 +1,203 @@
# PHP-Cookie
Modern cookie management for PHP
## Requirements
* PHP 5.4.0+
## Installation
1. Include the library via Composer [[?]](https://github.com/delight-im/Knowledge/blob/master/Composer%20(PHP).md):
```
$ composer require delight-im/cookie
```
1. Include the Composer autoloader:
```php
require __DIR__ . '/vendor/autoload.php';
```
## Upgrading
Migrating from an earlier version of this project? See our [upgrade guide](Migration.md) for help.
## Usage
### Static method
This library provides a static method that is compatible to PHP’s built-in `setcookie(...)` function but includes support for more recent features such as the `SameSite` attribute:
```php
\Delight\Cookie\Cookie::setcookie('SID', '31d4d96e407aad42');
// or
\Delight\Cookie\Cookie::setcookie('SID', '31d4d96e407aad42', time() + 3600, '/~rasmus/', 'example.com', true, true, 'Lax');
```
### Builder pattern
Instances of the `Cookie` class let you build a cookie conveniently by setting individual properties. This class uses reasonable defaults that may differ from defaults of the `setcookie` function.
```php
$cookie = new \Delight\Cookie\Cookie('SID');
$cookie->setValue('31d4d96e407aad42');
$cookie->setMaxAge(60 * 60 * 24);
// $cookie->setExpiryTime(time() + 60 * 60 * 24);
$cookie->setPath('/~rasmus/');
$cookie->setDomain('example.com');
$cookie->setHttpOnly(true);
$cookie->setSecureOnly(true);
$cookie->setSameSiteRestriction('Strict');
// echo $cookie;
// or
$cookie->save();
// or
// $cookie->saveAndSet();
```
The method calls can also be chained:
```php
(new \Delight\Cookie\Cookie('SID'))->setValue('31d4d96e407aad42')->setMaxAge(60 * 60 * 24)->setSameSiteRestriction('None')->save();
```
A cookie can later be deleted simply like this:
```php
$cookie->delete();
// or
$cookie->deleteAndUnset();
```
**Note:** For the deletion to work, the cookie must have the same settings as the cookie that was originally saved – except for its value, which doesn’t need to be set. So you should remember to pass appropriate values to `setPath(...)`, `setDomain(...)`, `setHttpOnly(...)` and `setSecureOnly(...)` again.
### Reading cookies
* Checking whether a cookie exists:
```php
\Delight\Cookie\Cookie::exists('first_visit');
```
* Reading a cookie’s value (with optional default value):
```php
\Delight\Cookie\Cookie::get('first_visit');
// or
\Delight\Cookie\Cookie::get('first_visit', \time());
```
### Managing sessions
Using the `Session` class, you can start and resume sessions in a way that is compatible to PHP’s built-in `session_start()` function, while having access to the improved cookie handling from this library as well:
```php
// start session and have session cookie with 'lax' same-site restriction
\Delight\Cookie\Session::start();
// or
\Delight\Cookie\Session::start('Lax');
// start session and have session cookie with 'strict' same-site restriction
\Delight\Cookie\Session::start('Strict');
// start session and have session cookie without any same-site restriction
\Delight\Cookie\Session::start(null);
// or
\Delight\Cookie\Session::start('None'); // Chrome 80+
```
All three calls respect the settings from PHP’s `session_set_cookie_params(...)` function and the configuration options `session.name`, `session.cookie_lifetime`, `session.cookie_path`, `session.cookie_domain`, `session.cookie_secure`, `session.cookie_httponly` and `session.use_cookies`.
Likewise, replacements for
```php
session_regenerate_id();
// and
session_regenerate_id(true);
```
are available via
```php
\Delight\Cookie\Session::regenerate();
// and
\Delight\Cookie\Session::regenerate(true);
```
if you want protection against session fixation attacks that comes with improved cookie handling.
Additionally, access to the current internal session ID is provided via
```php
\Delight\Cookie\Session::id();
```
as a replacement for
```php
session_id();
```
### Reading and writing session data
* Read a value from the session (with optional default value):
```php
$value = \Delight\Cookie\Session::get($key);
// or
$value = \Delight\Cookie\Session::get($key, $defaultValue);
```
* Write a value to the session:
```php
\Delight\Cookie\Session::set($key, $value);
```
* Check whether a value exists in the session:
```php
if (\Delight\Cookie\Session::has($key)) {
// ...
}
```
* Remove a value from the session:
```php
\Delight\Cookie\Session::delete($key);
```
* Read *and then* immediately remove a value from the session:
```php
$value = \Delight\Cookie\Session::take($key);
$value = \Delight\Cookie\Session::take($key, $defaultValue);
```
This is often useful for flash messages, e.g. in combination with the `has(...)` method.
### Parsing cookies
```php
$cookieHeader = 'Set-Cookie: test=php.net; expires=Thu, 09-Jun-2016 16:30:32 GMT; Max-Age=3600; path=/~rasmus/; secure';
$cookieInstance = \Delight\Cookie\Cookie::parse($cookieHeader);
```
## Specifications
* [RFC 2109](https://tools.ietf.org/html/rfc2109)
* [RFC 6265](https://tools.ietf.org/html/rfc6265)
* [Same-site Cookies](https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-04) (formerly [2016-06-20](https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00) and [2016-04-06](https://tools.ietf.org/html/draft-west-first-party-cookies-07))
* [Amendment](https://tools.ietf.org/html/draft-west-cookie-incrementalism-00): [Default to `Lax`](https://chromestatus.com/feature/5088147346030592) and [require `secure` attribute for `None`](https://chromestatus.com/feature/5633521622188032) (Note: There are [incompatible clients](https://www.chromium.org/updates/same-site/incompatible-clients))
## Contributing
All contributions are welcome! If you wish to contribute, please create an issue first so that your feature, problem or question can be discussed.
## License
This project is licensed under the terms of the [MIT License](https://opensource.org/licenses/MIT).
+17
View File
@@ -0,0 +1,17 @@
{
"name": "delight-im/cookie",
"description": "Modern cookie management for PHP",
"require": {
"php": ">=5.4.0",
"delight-im/http": "^2.0"
},
"type": "library",
"keywords": [ "cookie", "cookies", "http", "csrf", "xss", "samesite", "same-site" ],
"homepage": "https://github.com/delight-im/PHP-Cookie",
"license": "MIT",
"autoload": {
"psr-4": {
"Delight\\Cookie\\": "src/"
}
}
}
Generated Vendored Executable
+56
View File
@@ -0,0 +1,56 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "660f7f831eec915a00485a0dd67853d0",
"packages": [
{
"name": "delight-im/http",
"version": "v2.0.0",
"source": {
"type": "git",
"url": "https://github.com/delight-im/PHP-HTTP.git",
"reference": "0a19a72a7eac8b1301aa972fb20cff494ac43e09"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/delight-im/PHP-HTTP/zipball/0a19a72a7eac8b1301aa972fb20cff494ac43e09",
"reference": "0a19a72a7eac8b1301aa972fb20cff494ac43e09",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Delight\\Http\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Hypertext Transfer Protocol (HTTP) utilities for PHP",
"homepage": "https://github.com/delight-im/PHP-HTTP",
"keywords": [
"headers",
"http",
"https"
],
"time": "2016-07-21T15:05:01+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.4.0"
},
"platform-dev": []
}
+584
View File
@@ -0,0 +1,584 @@
<?php
/*
* PHP-Cookie (https://github.com/delight-im/PHP-Cookie)
* Copyright (c) delight.im (https://www.delight.im/)
* Licensed under the MIT License (https://opensource.org/licenses/MIT)
*/
namespace Delight\Cookie;
/**
* Modern cookie management for PHP
*
* Cookies are a mechanism for storing data in the client's web browser and identifying returning clients on subsequent visits
*
* All cookies that have successfully been set will automatically be included in the global `$_COOKIE` array with future requests
*
* You can set a new cookie using the static method `Cookie::setcookie(...)` which is compatible to PHP's built-in `setcookie(...)` function
*
* Alternatively, you can construct an instance of this class, set properties individually, and finally call `save()`
*
* Note that cookies must always be set before the HTTP headers are sent to the client, i.e. before the actual output starts
*/
final class Cookie {
/** @var string name prefix indicating that the cookie must be from a secure origin (i.e. HTTPS) and the 'secure' attribute must be set */
const PREFIX_SECURE = '__Secure-';
/** @var string name prefix indicating that the 'domain' attribute must *not* be set, the 'path' attribute must be '/' and the effects of {@see PREFIX_SECURE} apply as well */
const PREFIX_HOST = '__Host-';
const HEADER_PREFIX = 'Set-Cookie: ';
const SAME_SITE_RESTRICTION_NONE = 'None';
const SAME_SITE_RESTRICTION_LAX = 'Lax';
const SAME_SITE_RESTRICTION_STRICT = 'Strict';
/** @var string the name of the cookie which is also the key for future accesses via `$_COOKIE[...]` */
private $name;
/** @var mixed|null the value of the cookie that will be stored on the client's machine */
private $value;
/** @var int the Unix timestamp indicating the time that the cookie will expire at, i.e. usually `time() + $seconds` */
private $expiryTime;
/** @var string the path on the server that the cookie will be valid for (including all sub-directories), e.g. an empty string for the current directory or `/` for the root directory */
private $path;
/** @var string|null the domain that the cookie will be valid for (including subdomains) or `null` for the current host (excluding subdomains) */
private $domain;
/** @var bool indicates that the cookie should be accessible through the HTTP protocol only and not through scripting languages */
private $httpOnly;
/** @var bool indicates that the cookie should be sent back by the client over secure HTTPS connections only */
private $secureOnly;
/** @var string|null indicates that the cookie should not be sent along with cross-site requests (either `null`, `None`, `Lax` or `Strict`) */
private $sameSiteRestriction;
/**
* Prepares a new cookie
*
* @param string $name the name of the cookie which is also the key for future accesses via `$_COOKIE[...]`
*/
public function __construct($name) {
$this->name = $name;
$this->value = null;
$this->expiryTime = 0;
$this->path = '/';
$this->domain = null;
$this->httpOnly = true;
$this->secureOnly = false;
$this->sameSiteRestriction = self::SAME_SITE_RESTRICTION_LAX;
}
/**
* Returns the name of the cookie
*
* @return string the name of the cookie which is also the key for future accesses via `$_COOKIE[...]`
*/
public function getName() {
return $this->name;
}
/**
* Returns the value of the cookie
*
* @return mixed|null the value of the cookie that will be stored on the client's machine
*/
public function getValue() {
return $this->value;
}
/**
* Sets the value for the cookie
*
* @param mixed|null $value the value of the cookie that will be stored on the client's machine
* @return static this instance for chaining
*/
public function setValue($value) {
$this->value = $value;
return $this;
}
/**
* Returns the expiry time of the cookie
*
* @return int the Unix timestamp indicating the time that the cookie will expire at, i.e. usually `time() + $seconds`
*/
public function getExpiryTime() {
return $this->expiryTime;
}
/**
* Sets the expiry time for the cookie
*
* @param int $expiryTime the Unix timestamp indicating the time that the cookie will expire at, i.e. usually `time() + $seconds`
* @return static this instance for chaining
*/
public function setExpiryTime($expiryTime) {
$this->expiryTime = $expiryTime;
return $this;
}
/**
* Returns the maximum age of the cookie (i.e. the remaining lifetime)
*
* @return int the maximum age of the cookie in seconds
*/
public function getMaxAge() {
return $this->expiryTime - \time();
}
/**
* Sets the expiry time for the cookie based on the specified maximum age (i.e. the remaining lifetime)
*
* @param int $maxAge the maximum age for the cookie in seconds
* @return static this instance for chaining
*/
public function setMaxAge($maxAge) {
$this->expiryTime = \time() + $maxAge;
return $this;
}
/**
* Returns the path of the cookie
*
* @return string the path on the server that the cookie will be valid for (including all sub-directories), e.g. an empty string for the current directory or `/` for the root directory
*/
public function getPath() {
return $this->path;
}
/**
* Sets the path for the cookie
*
* @param string $path the path on the server that the cookie will be valid for (including all sub-directories), e.g. an empty string for the current directory or `/` for the root directory
* @return static this instance for chaining
*/
public function setPath($path) {
$this->path = $path;
return $this;
}
/**
* Returns the domain of the cookie
*
* @return string|null the domain that the cookie will be valid for (including subdomains) or `null` for the current host (excluding subdomains)
*/
public function getDomain() {
return $this->domain;
}
/**
* Sets the domain for the cookie
*
* @param string|null $domain the domain that the cookie will be valid for (including subdomains) or `null` for the current host (excluding subdomains)
* @return static this instance for chaining
*/
public function setDomain($domain = null) {
$this->domain = self::normalizeDomain($domain);
return $this;
}
/**
* Returns whether the cookie should be accessible through HTTP only
*
* @return bool whether the cookie should be accessible through the HTTP protocol only and not through scripting languages
*/
public function isHttpOnly() {
return $this->httpOnly;
}
/**
* Sets whether the cookie should be accessible through HTTP only
*
* @param bool $httpOnly indicates that the cookie should be accessible through the HTTP protocol only and not through scripting languages
* @return static this instance for chaining
*/
public function setHttpOnly($httpOnly) {
$this->httpOnly = $httpOnly;
return $this;
}
/**
* Returns whether the cookie should be sent over HTTPS only
*
* @return bool whether the cookie should be sent back by the client over secure HTTPS connections only
*/
public function isSecureOnly() {
return $this->secureOnly;
}
/**
* Sets whether the cookie should be sent over HTTPS only
*
* @param bool $secureOnly indicates that the cookie should be sent back by the client over secure HTTPS connections only
* @return static this instance for chaining
*/
public function setSecureOnly($secureOnly) {
$this->secureOnly = $secureOnly;
return $this;
}
/**
* Returns the same-site restriction of the cookie
*
* @return string|null whether the cookie should not be sent along with cross-site requests (either `null`, `None`, `Lax` or `Strict`)
*/
public function getSameSiteRestriction() {
return $this->sameSiteRestriction;
}
/**
* Sets the same-site restriction for the cookie
*
* @param string|null $sameSiteRestriction indicates that the cookie should not be sent along with cross-site requests (either `null`, `None`, `Lax` or `Strict`)
* @return static this instance for chaining
*/
public function setSameSiteRestriction($sameSiteRestriction) {
$this->sameSiteRestriction = $sameSiteRestriction;
return $this;
}
/**
* Saves the cookie
*
* @return bool whether the cookie header has successfully been sent (and will *probably* cause the client to set the cookie)
*/
public function save() {
return self::addHttpHeader((string) $this);
}
/**
* Saves the cookie and immediately creates the corresponding variable in the superglobal `$_COOKIE` array
*
* The variable would otherwise only be available starting from the next HTTP request
*
* @return bool whether the cookie header has successfully been sent (and will *probably* cause the client to set the cookie)
*/
public function saveAndSet() {
$_COOKIE[$this->name] = $this->value;
return $this->save();
}
/**
* Deletes the cookie
*
* @return bool whether the cookie header has successfully been sent (and will *probably* cause the client to delete the cookie)
*/
public function delete() {
// create a temporary copy of this cookie so that it isn't corrupted
$copiedCookie = clone $this;
// set the copied cookie's value to an empty string which internally sets the required options for a deletion
$copiedCookie->setValue('');
// save the copied "deletion" cookie
return $copiedCookie->save();
}
/**
* Deletes the cookie and immediately removes the corresponding variable from the superglobal `$_COOKIE` array
*
* The variable would otherwise only be deleted at the start of the next HTTP request
*
* @return bool whether the cookie header has successfully been sent (and will *probably* cause the client to delete the cookie)
*/
public function deleteAndUnset() {
unset($_COOKIE[$this->name]);
return $this->delete();
}
public function __toString() {
return self::buildCookieHeader($this->name, $this->value, $this->expiryTime, $this->path, $this->domain, $this->secureOnly, $this->httpOnly, $this->sameSiteRestriction);
}
/**
* Sets a new cookie in a way compatible to PHP's `setcookie(...)` function
*
* @param string $name the name of the cookie which is also the key for future accesses via `$_COOKIE[...]`
* @param mixed|null $value the value of the cookie that will be stored on the client's machine
* @param int $expiryTime the Unix timestamp indicating the time that the cookie will expire at, i.e. usually `time() + $seconds`
* @param string|null $path the path on the server that the cookie will be valid for (including all sub-directories), e.g. an empty string for the current directory or `/` for the root directory
* @param string|null $domain the domain that the cookie will be valid for (including subdomains) or `null` for the current host (excluding subdomains)
* @param bool $secureOnly indicates that the cookie should be sent back by the client over secure HTTPS connections only
* @param bool $httpOnly indicates that the cookie should be accessible through the HTTP protocol only and not through scripting languages
* @param string|null $sameSiteRestriction indicates that the cookie should not be sent along with cross-site requests (either `null`, `None`, `Lax` or `Strict`)
* @return bool whether the cookie header has successfully been sent (and will *probably* cause the client to set the cookie)
*/
public static function setcookie($name, $value = null, $expiryTime = 0, $path = null, $domain = null, $secureOnly = false, $httpOnly = false, $sameSiteRestriction = null) {
return self::addHttpHeader(
self::buildCookieHeader($name, $value, $expiryTime, $path, $domain, $secureOnly, $httpOnly, $sameSiteRestriction)
);
}
/**
* Builds the HTTP header that can be used to set a cookie with the specified options
*
* @param string $name the name of the cookie which is also the key for future accesses via `$_COOKIE[...]`
* @param mixed|null $value the value of the cookie that will be stored on the client's machine
* @param int $expiryTime the Unix timestamp indicating the time that the cookie will expire at, i.e. usually `time() + $seconds`
* @param string|null $path the path on the server that the cookie will be valid for (including all sub-directories), e.g. an empty string for the current directory or `/` for the root directory
* @param string|null $domain the domain that the cookie will be valid for (including subdomains) or `null` for the current host (excluding subdomains)
* @param bool $secureOnly indicates that the cookie should be sent back by the client over secure HTTPS connections only
* @param bool $httpOnly indicates that the cookie should be accessible through the HTTP protocol only and not through scripting languages
* @param string|null $sameSiteRestriction indicates that the cookie should not be sent along with cross-site requests (either `null`, `None`, `Lax` or `Strict`)
* @return string the HTTP header
*/
public static function buildCookieHeader($name, $value = null, $expiryTime = 0, $path = null, $domain = null, $secureOnly = false, $httpOnly = false, $sameSiteRestriction = null) {
if (self::isNameValid($name)) {
$name = (string) $name;
}
else {
return null;
}
if (self::isExpiryTimeValid($expiryTime)) {
$expiryTime = (int) $expiryTime;
}
else {
return null;
}
$forceShowExpiry = false;
if (\is_null($value) || $value === false || $value === '') {
$value = 'deleted';
$expiryTime = 0;
$forceShowExpiry = true;
}
$maxAgeStr = self::formatMaxAge($expiryTime, $forceShowExpiry);
$expiryTimeStr = self::formatExpiryTime($expiryTime, $forceShowExpiry);
$headerStr = self::HEADER_PREFIX . $name . '=' . \urlencode($value);
if (!\is_null($expiryTimeStr)) {
$headerStr .= '; expires=' . $expiryTimeStr;
}
// The `Max-Age` property is supported on PHP 5.5+ only (https://bugs.php.net/bug.php?id=23955).
if (\PHP_VERSION_ID >= 50500) {
if (!\is_null($maxAgeStr)) {
$headerStr .= '; Max-Age=' . $maxAgeStr;
}
}
if (!empty($path) || $path === 0) {
$headerStr .= '; path=' . $path;
}
if (!empty($domain) || $domain === 0) {
$headerStr .= '; domain=' . $domain;
}
if ($secureOnly) {
$headerStr .= '; secure';
}
if ($httpOnly) {
$headerStr .= '; httponly';
}
if ($sameSiteRestriction === self::SAME_SITE_RESTRICTION_NONE) {
// if the 'secure' attribute is missing
if (!$secureOnly) {
\trigger_error('When the \'SameSite\' attribute is set to \'None\', the \'secure\' attribute should be set as well', \E_USER_WARNING);
}
$headerStr .= '; SameSite=None';
}
elseif ($sameSiteRestriction === self::SAME_SITE_RESTRICTION_LAX) {
$headerStr .= '; SameSite=Lax';
}
elseif ($sameSiteRestriction === self::SAME_SITE_RESTRICTION_STRICT) {
$headerStr .= '; SameSite=Strict';
}
return $headerStr;
}
/**
* Parses the given cookie header and returns an equivalent cookie instance
*
* @param string $cookieHeader the cookie header to parse
* @return \Delight\Cookie\Cookie|null the cookie instance or `null`
*/
public static function parse($cookieHeader) {
if (empty($cookieHeader)) {
return null;
}
if (\preg_match('/^' . self::HEADER_PREFIX . '(.*?)=(.*?)(?:; (.*?))?$/i', $cookieHeader, $matches)) {
$cookie = new self($matches[1]);
$cookie->setPath(null);
$cookie->setHttpOnly(false);
$cookie->setValue(
\urldecode($matches[2])
);
$cookie->setSameSiteRestriction(null);
if (\count($matches) >= 4) {
$attributes = \explode('; ', $matches[3]);
foreach ($attributes as $attribute) {
if (\strcasecmp($attribute, 'HttpOnly') === 0) {
$cookie->setHttpOnly(true);
}
elseif (\strcasecmp($attribute, 'Secure') === 0) {
$cookie->setSecureOnly(true);
}
elseif (\stripos($attribute, 'Expires=') === 0) {
$cookie->setExpiryTime((int) \strtotime(\substr($attribute, 8)));
}
elseif (\stripos($attribute, 'Domain=') === 0) {
$cookie->setDomain(\substr($attribute, 7));
}
elseif (\stripos($attribute, 'Path=') === 0) {
$cookie->setPath(\substr($attribute, 5));
}
elseif (\stripos($attribute, 'SameSite=') === 0) {
$cookie->setSameSiteRestriction(\substr($attribute, 9));
}
}
}
return $cookie;
}
else {
return null;
}
}
/**
* Checks whether a cookie with the specified name exists
*
* @param string $name the name of the cookie to check
* @return bool whether there is a cookie with the specified name
*/
public static function exists($name) {
return isset($_COOKIE[$name]);
}
/**
* Returns the value from the requested cookie or, if not found, the specified default value
*
* @param string $name the name of the cookie to retrieve the value from
* @param mixed $defaultValue the default value to return if the requested cookie cannot be found
* @return mixed the value from the requested cookie or the default value
*/
public static function get($name, $defaultValue = null) {
if (isset($_COOKIE[$name])) {
return $_COOKIE[$name];
}
else {
return $defaultValue;
}
}
private static function isNameValid($name) {
$name = (string) $name;
// The name of a cookie must not be empty on PHP 7+ (https://bugs.php.net/bug.php?id=69523).
if ($name !== '' || \PHP_VERSION_ID < 70000) {
if (!\preg_match('/[=,; \\t\\r\\n\\013\\014]/', $name)) {
return true;
}
}
return false;
}
private static function isExpiryTimeValid($expiryTime) {
return \is_numeric($expiryTime) || \is_null($expiryTime) || \is_bool($expiryTime);
}
private static function calculateMaxAge($expiryTime) {
if ($expiryTime === 0) {
return 0;
}
else {
$maxAge = $expiryTime - \time();
// The value of the `Max-Age` property must not be negative on PHP 7.0.19+ (< 7.1) and
// PHP 7.1.5+ (https://bugs.php.net/bug.php?id=72071).
if ((\PHP_VERSION_ID >= 70019 && \PHP_VERSION_ID < 70100) || \PHP_VERSION_ID >= 70105) {
if ($maxAge < 0) {
$maxAge = 0;
}
}
return $maxAge;
}
}
private static function formatExpiryTime($expiryTime, $forceShow = false) {
if ($expiryTime > 0 || $forceShow) {
if ($forceShow) {
$expiryTime = 1;
}
return \gmdate('D, d-M-Y H:i:s T', $expiryTime);
}
else {
return null;
}
}
private static function formatMaxAge($expiryTime, $forceShow = false) {
if ($expiryTime > 0 || $forceShow) {
return (string) self::calculateMaxAge($expiryTime);
}
else {
return null;
}
}
private static function normalizeDomain($domain = null) {
// make sure that the domain is a string
$domain = (string) $domain;
// if the cookie should be valid for the current host only
if ($domain === '') {
// no need for further normalization
return null;
}
// if the provided domain is actually an IP address
if (\filter_var($domain, \FILTER_VALIDATE_IP) !== false) {
// let the cookie be valid for the current host
return null;
}
// for local hostnames (which either have no dot at all or a leading dot only)
if (\strpos($domain, '.') === false || \strrpos($domain, '.') === 0) {
// let the cookie be valid for the current host while ensuring maximum compatibility
return null;
}
// unless the domain already starts with a dot
if ($domain[0] !== '.') {
// prepend a dot for maximum compatibility (e.g. with RFC 2109)
$domain = '.' . $domain;
}
// return the normalized domain
return $domain;
}
private static function addHttpHeader($header) {
if (!\headers_sent()) {
if (!empty($header)) {
\header($header, false);
return true;
}
}
return false;
}
}
+170
View File
@@ -0,0 +1,170 @@
<?php
/*
* PHP-Cookie (https://github.com/delight-im/PHP-Cookie)
* Copyright (c) delight.im (https://www.delight.im/)
* Licensed under the MIT License (https://opensource.org/licenses/MIT)
*/
namespace Delight\Cookie;
use Delight\Http\ResponseHeader;
/**
* Session management with improved cookie handling
*
* You can start a session using the static method `Session::start(...)` which is compatible to PHP's built-in `session_start()` function
*
* Note that sessions must always be started before the HTTP headers are sent to the client, i.e. before the actual output starts
*/
final class Session {
private function __construct() { }
/**
* Starts or resumes a session in a way compatible to PHP's built-in `session_start()` function
*
* @param string|null $sameSiteRestriction indicates that the cookie should not be sent along with cross-site requests (either `null`, `None`, `Lax` or `Strict`)
*/
public static function start($sameSiteRestriction = Cookie::SAME_SITE_RESTRICTION_LAX) {
// run PHP's built-in equivalent
\session_start();
// intercept the cookie header (if any) and rewrite it
self::rewriteCookieHeader($sameSiteRestriction);
}
/**
* Returns or sets the ID of the current session
*
* In order to change the current session ID, pass the new ID as the only argument to this method
*
* Please note that there is rarely a need for the version of this method that *updates* the ID
*
* For most purposes, you may find the method `regenerate` from this same class more helpful
*
* @param string|null $newId (optional) a new session ID to replace the current session ID
* @return string the (old) session ID or an empty string
*/
public static function id($newId = null) {
if ($newId === null) {
return \session_id();
}
else {
return \session_id($newId);
}
}
/**
* Re-generates the session ID in a way compatible to PHP's built-in `session_regenerate_id()` function
*
* @param bool $deleteOldSession whether to delete the old session or not
* @param string|null $sameSiteRestriction indicates that the cookie should not be sent along with cross-site requests (either `null`, `None`, `Lax` or `Strict`)
*/
public static function regenerate($deleteOldSession = false, $sameSiteRestriction = Cookie::SAME_SITE_RESTRICTION_LAX) {
// run PHP's built-in equivalent
\session_regenerate_id($deleteOldSession);
// intercept the cookie header (if any) and rewrite it
self::rewriteCookieHeader($sameSiteRestriction);
}
/**
* Checks whether a value for the specified key exists in the session
*
* @param string $key the key to check
* @return bool whether there is a value for the specified key or not
*/
public static function has($key) {
return isset($_SESSION[$key]);
}
/**
* Returns the requested value from the session or, if not found, the specified default value
*
* @param string $key the key to retrieve the value for
* @param mixed $defaultValue the default value to return if the requested value cannot be found
* @return mixed the requested value or the default value
*/
public static function get($key, $defaultValue = null) {
if (isset($_SESSION[$key])) {
return $_SESSION[$key];
}
else {
return $defaultValue;
}
}
/**
* Returns the requested value and removes it from the session
*
* This is identical to calling `get` first and then `remove` for the same key
*
* @param string $key the key to retrieve and remove the value for
* @param mixed $defaultValue the default value to return if the requested value cannot be found
* @return mixed the requested value or the default value
*/
public static function take($key, $defaultValue = null) {
if (isset($_SESSION[$key])) {
$value = $_SESSION[$key];
unset($_SESSION[$key]);
return $value;
}
else {
return $defaultValue;
}
}
/**
* Sets the value for the specified key to the given value
*
* Any data that already exists for the specified key will be overwritten
*
* @param string $key the key to set the value for
* @param mixed $value the value to set
*/
public static function set($key, $value) {
$_SESSION[$key] = $value;
}
/**
* Removes the value for the specified key from the session
*
* @param string $key the key to remove the value for
*/
public static function delete($key) {
unset($_SESSION[$key]);
}
/**
* Intercepts and rewrites the session cookie header
*
* @param string|null $sameSiteRestriction indicates that the cookie should not be sent along with cross-site requests (either `null`, `None`, `Lax` or `Strict`)
*/
private static function rewriteCookieHeader($sameSiteRestriction = Cookie::SAME_SITE_RESTRICTION_LAX) {
// get and remove the original cookie header set by PHP
$originalCookieHeader = ResponseHeader::take('Set-Cookie', \session_name() . '=');
// if a cookie header has been found
if (isset($originalCookieHeader)) {
// parse it into a cookie instance
$parsedCookie = Cookie::parse($originalCookieHeader);
// if the cookie has successfully been parsed
if (isset($parsedCookie)) {
// apply the supplied same-site restriction
$parsedCookie->setSameSiteRestriction($sameSiteRestriction);
if ($parsedCookie->getSameSiteRestriction() === Cookie::SAME_SITE_RESTRICTION_NONE && !$parsedCookie->isSecureOnly()) {
\trigger_error('You may have to enable the \'session.cookie_secure\' directive in the configuration in \'php.ini\' or via the \'ini_set\' function', \E_USER_WARNING);
}
// save the cookie
$parsedCookie->save();
}
}
}
}
+320
View File
@@ -0,0 +1,320 @@
<?php
/*
* PHP-Cookie (https://github.com/delight-im/PHP-Cookie)
* Copyright (c) delight.im (https://www.delight.im/)
* Licensed under the MIT License (https://opensource.org/licenses/MIT)
*/
// enable error reporting
\error_reporting(\E_ALL);
\ini_set('display_errors', 'stdout');
\header('Content-type: text/plain; charset=utf-8');
require __DIR__.'/../vendor/autoload.php';
/* BEGIN TEST COOKIES */
// start output buffering
\ob_start();
\testCookie(null);
\testCookie(false);
\testCookie('');
\testCookie(0);
\testCookie('hello');
\testCookie('hello', false);
\testCookie('hello', true);
\testCookie('hello', null);
\testCookie('hello', '');
\testCookie('hello', 0);
\testCookie('hello', 1);
\testCookie('hello', 'world');
\testCookie('hello', 123);
\testCookie(123, 'world');
\testCookie('greeting', '¡Buenos días!');
\testCookie('¡Buenos días!', 'greeting');
\testCookie('%a|b}c_$d!f"g-h(i)j$', 'value value value');
\testCookie('%a|b}c_$d!f"g-h(i)j$', '%a|b}c_$d!f"g-h(i)j$');
\testCookie('hello', 'world', '!');
\testCookie('hello', 'world', '');
\testCookie('hello', 'world', false);
\testCookie('hello', 'world', null);
\testCookie('hello', 'world', true);
\testCookie('hello', 'world', 0);
\testCookie('hello', 'world', '');
\testCookie('hello', 'world', -1);
\testCookie('hello', 'world', 234234);
\testCookie('hello', 'world', \time() + 60 * 60 * 24);
\testCookie('hello', 'world', \time() + 60 * 60 * 24 * 30);
\testCookie('hello', 'world', \time() + 86400, null);
\testCookie('hello', 'world', \time() + 86400, false);
\testCookie('hello', 'world', \time() + 86400, true);
\testCookie('hello', 'world', \time() + 86400, 0);
\testCookie('hello', 'world', \time() + 86400, '');
\testCookie('hello', 'world', \time() + 86400, '/');
\testCookie('hello', 'world', \time() + 86400, '/foo');
\testCookie('hello', 'world', \time() + 86400, '/foo/');
\testCookie('hello', 'world', \time() + 86400, '/buenos/días/');
\testCookie('hello', 'world', \time() + 86400, '/buenos días/');
\testCookie('hello', 'world', \time() + 86400, '/foo/', null);
\testCookie('hello', 'world', \time() + 86400, '/foo/', false);
\testCookie('hello', 'world', \time() + 86400, '/foo/', true);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 0);
\testCookie('hello', 'world', \time() + 86400, '/foo/', '');
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com');
\testCookie('hello', 'world', \time() + 86400, '/foo/', '.example.com');
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'www.example.com');
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'días.example.com');
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'localhost');
\testCookie('hello', 'world', \time() + 86400, '/foo/', '127.0.0.1');
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', null);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', false);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', true);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', 0);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', '');
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', 'hello');
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', 7);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', -7);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', false, null);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', false, false);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', false, true);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', false, 0);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', false, '');
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', false, 'hello');
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', false, 5);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', false, -5);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', true, null);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', true, false);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', true, true);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', true, 0);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', true, '');
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', true, 'hello');
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', true, 5);
\testCookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', true, -5);
\testCookie('TestCookie', 'php.net');
\testCookie('TestCookie', 'php.net', \time() + 3600);
\testCookie('TestCookie', 'php.net', \time() + 3600, '/~rasmus/', 'example.com', 1);
\testCookie('TestCookie', '', \time() - 3600);
\testCookie('TestCookie', '', \time() - 3600, '/~rasmus/', 'example.com', 1);
\testCookie('cookie[three]', 'cookiethree');
\testCookie('cookie[two]', 'cookietwo');
\testCookie('cookie[one]', 'cookieone');
\testEqual((new \Delight\Cookie\Cookie('SID'))->setValue('31d4d96e407aad42'), 'Set-Cookie: SID=31d4d96e407aad42; path=/; httponly; SameSite=Lax');
@\testEqual((new \Delight\Cookie\Cookie('SID'))->setValue('31d4d96e407aad42')->setSameSiteRestriction('None'), 'Set-Cookie: SID=31d4d96e407aad42; path=/; httponly; SameSite=None');
\testEqual((new \Delight\Cookie\Cookie('SID'))->setValue('31d4d96e407aad42')->setSameSiteRestriction('None')->setSecureOnly(true), 'Set-Cookie: SID=31d4d96e407aad42; path=/; secure; httponly; SameSite=None');
@\testEqual((new \Delight\Cookie\Cookie('SID'))->setValue('31d4d96e407aad42')->setDomain('localhost')->setSameSiteRestriction('None'), 'Set-Cookie: SID=31d4d96e407aad42; path=/; httponly; SameSite=None');
\testEqual((new \Delight\Cookie\Cookie('SID'))->setValue('31d4d96e407aad42')->setDomain('localhost')->setSameSiteRestriction('None')->setSecureOnly(true), 'Set-Cookie: SID=31d4d96e407aad42; path=/; secure; httponly; SameSite=None');
\testEqual((new \Delight\Cookie\Cookie('SID'))->setValue('31d4d96e407aad42')->setSameSiteRestriction('Strict'), 'Set-Cookie: SID=31d4d96e407aad42; path=/; httponly; SameSite=Strict');
\testEqual((new \Delight\Cookie\Cookie('SID'))->setValue('31d4d96e407aad42')->setDomain('localhost')->setSameSiteRestriction('Strict'), 'Set-Cookie: SID=31d4d96e407aad42; path=/; httponly; SameSite=Strict');
\testEqual((new \Delight\Cookie\Cookie('key'))->setValue('value')->setDomain('localhost'), 'Set-Cookie: key=value; path=/; httponly; SameSite=Lax');
\testEqual((new \Delight\Cookie\Cookie('key'))->setValue('value')->setDomain('.localhost'), 'Set-Cookie: key=value; path=/; httponly; SameSite=Lax');
\testEqual((new \Delight\Cookie\Cookie('key'))->setValue('value')->setDomain('127.0.0.1'), 'Set-Cookie: key=value; path=/; httponly; SameSite=Lax');
\testEqual((new \Delight\Cookie\Cookie('key'))->setValue('value')->setDomain('.local'), 'Set-Cookie: key=value; path=/; httponly; SameSite=Lax');
\testEqual((new \Delight\Cookie\Cookie('key'))->setValue('value')->setDomain('example.com'), 'Set-Cookie: key=value; path=/; domain=.example.com; httponly; SameSite=Lax');
\testEqual((new \Delight\Cookie\Cookie('key'))->setValue('value')->setDomain('.example.com'), 'Set-Cookie: key=value; path=/; domain=.example.com; httponly; SameSite=Lax');
\testEqual((new \Delight\Cookie\Cookie('key'))->setValue('value')->setDomain('www.example.com'), 'Set-Cookie: key=value; path=/; domain=.www.example.com; httponly; SameSite=Lax');
\testEqual((new \Delight\Cookie\Cookie('key'))->setValue('value')->setDomain('.www.example.com'), 'Set-Cookie: key=value; path=/; domain=.www.example.com; httponly; SameSite=Lax');
\testEqual((new \Delight\Cookie\Cookie('key'))->setValue('value')->setDomain('blog.example.com'), 'Set-Cookie: key=value; path=/; domain=.blog.example.com; httponly; SameSite=Lax');
\testEqual((new \Delight\Cookie\Cookie('key'))->setValue('value')->setDomain('.blog.example.com'), 'Set-Cookie: key=value; path=/; domain=.blog.example.com; httponly; SameSite=Lax');
\testEqual(\Delight\Cookie\Cookie::parse('Set-Cookie: SID'), '');
\testEqual(\Delight\Cookie\Cookie::parse('Set-Cookie: SID=31d4d96e407aad42'), 'Set-Cookie: SID=31d4d96e407aad42');
\testEqual(\Delight\Cookie\Cookie::parse('Set-Cookie: SID=31d4d96e407aad42; path=/; httponly'), 'Set-Cookie: SID=31d4d96e407aad42; path=/; httponly');
@\testEqual(\Delight\Cookie\Cookie::parse('Set-Cookie: SID=31d4d96e407aad42; path=/; httponly; SameSite=None'), 'Set-Cookie: SID=31d4d96e407aad42; path=/; httponly; SameSite=None');
\testEqual(\Delight\Cookie\Cookie::parse('Set-Cookie: SID=31d4d96e407aad42; path=/; secure; httponly; SameSite=None'), 'Set-Cookie: SID=31d4d96e407aad42; path=/; secure; httponly; SameSite=None');
\testEqual(\Delight\Cookie\Cookie::parse('Set-Cookie: SID=31d4d96e407aad42; path=/; httponly; SameSite=Strict'), 'Set-Cookie: SID=31d4d96e407aad42; path=/; httponly; SameSite=Strict');
\setcookie('hello', 'world', \time() + 86400, '/foo/', 'example.com', true, true);
$cookie = \Delight\Cookie\Cookie::parse(\Delight\Http\ResponseHeader::take('Set-Cookie'));
\testEqual($cookie, (new \Delight\Cookie\Cookie('hello'))->setValue('world')->setMaxAge(86400)->setPath('/foo/')->setDomain('example.com')->setHttpOnly(true)->setSecureOnly(true)->setSameSiteRestriction(null));
($cookie->getName() === 'hello') or \fail(__LINE__);
($cookie->getValue() === 'world') or \fail(__LINE__);
($cookie->getExpiryTime() === \time() + 86400) or \fail(__LINE__);
($cookie->getMaxAge() === 86400) or \fail(__LINE__);
($cookie->getPath() === '/foo/') or \fail(__LINE__);
($cookie->getDomain() === '.example.com') or \fail(__LINE__);
($cookie->isHttpOnly() === true) or \fail(__LINE__);
($cookie->isSecureOnly() === true) or \fail(__LINE__);
($cookie->getSameSiteRestriction() === null) or \fail(__LINE__);
\testEqual(\Delight\Cookie\Cookie::exists('SESSID'), isset($_COOKIE['SESSID']));
\testEqual(\Delight\Cookie\Cookie::exists('other'), isset($_COOKIE['other']));
\testEqual(\Delight\Cookie\Cookie::get('SESSID'), (isset($_COOKIE['SESSID']) ? $_COOKIE['SESSID'] : null));
\testEqual(\Delight\Cookie\Cookie::get('SESSID', 42), (isset($_COOKIE['SESSID']) ? $_COOKIE['SESSID'] : 42));
\testEqual(\Delight\Cookie\Cookie::get('other'), (isset($_COOKIE['other']) ? $_COOKIE['other'] : null));
\testEqual(\Delight\Cookie\Cookie::get('other', 42), (isset($_COOKIE['other']) ? $_COOKIE['other'] : 42));
!isset($_COOKIE['cefa389e458b']) or \fail(__LINE__);
(new \Delight\Cookie\Cookie('cefa389e458b'))->setValue('a858c4a03224')->save();
\testEqual(\Delight\Http\ResponseHeader::take('Set-Cookie'), 'Set-Cookie: cefa389e458b=a858c4a03224; path=/; httponly; SameSite=Lax');
!isset($_COOKIE['cefa389e458b']) or \fail(__LINE__);
!isset($_COOKIE['c0f3a2ea81aa']) or \fail(__LINE__);
(new \Delight\Cookie\Cookie('c0f3a2ea81aa'))->setValue('dabf2c1490a5')->setDomain('example.com')->save();
\testEqual(\Delight\Http\ResponseHeader::take('Set-Cookie'), 'Set-Cookie: c0f3a2ea81aa=dabf2c1490a5; path=/; domain=.example.com; httponly; SameSite=Lax');
!isset($_COOKIE['c0f3a2ea81aa']) or \fail(__LINE__);
!isset($_COOKIE['c19718a89afd']) or \fail(__LINE__);
(new \Delight\Cookie\Cookie('c19718a89afd'))->setValue('f638cfcf49a8')->saveAndSet();
\testEqual(\Delight\Http\ResponseHeader::take('Set-Cookie'), 'Set-Cookie: c19718a89afd=f638cfcf49a8; path=/; httponly; SameSite=Lax');
(isset($_COOKIE['c19718a89afd']) && $_COOKIE['c19718a89afd'] === 'f638cfcf49a8') or \fail(__LINE__);
!isset($_COOKIE['b377b18ca9e6']) or \fail(__LINE__);
(new \Delight\Cookie\Cookie('b377b18ca9e6'))->setValue('dbeaebb92e43')->setDomain('example.com')->saveAndSet();
\testEqual(\Delight\Http\ResponseHeader::take('Set-Cookie'), 'Set-Cookie: b377b18ca9e6=dbeaebb92e43; path=/; domain=.example.com; httponly; SameSite=Lax');
(isset($_COOKIE['b377b18ca9e6']) && $_COOKIE['b377b18ca9e6'] === 'dbeaebb92e43') or \fail(__LINE__);
$_COOKIE['a8a78153b857'] = 'ed2e8ff8629f';
(isset($_COOKIE['a8a78153b857']) && $_COOKIE['a8a78153b857'] === 'ed2e8ff8629f') or \fail(__LINE__);
(new \Delight\Cookie\Cookie('a8a78153b857'))->delete();
\testEqual(\Delight\Http\ResponseHeader::take('Set-Cookie'), 'Set-Cookie: a8a78153b857=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; httponly; SameSite=Lax');
(isset($_COOKIE['a8a78153b857']) && $_COOKIE['a8a78153b857'] === 'ed2e8ff8629f') or \fail(__LINE__);
unset($_COOKIE['a8a78153b857']);
$_COOKIE['c5dd45883e4b'] = 'b42f7c9de3dd';
(isset($_COOKIE['c5dd45883e4b']) && $_COOKIE['c5dd45883e4b'] === 'b42f7c9de3dd') or \fail(__LINE__);
(new \Delight\Cookie\Cookie('c5dd45883e4b'))->setDomain('example.com')->delete();
\testEqual(\Delight\Http\ResponseHeader::take('Set-Cookie'), 'Set-Cookie: c5dd45883e4b=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=.example.com; httponly; SameSite=Lax');
(isset($_COOKIE['c5dd45883e4b']) && $_COOKIE['c5dd45883e4b'] === 'b42f7c9de3dd') or \fail(__LINE__);
unset($_COOKIE['c5dd45883e4b']);
$_COOKIE['ed050c7d7e62'] = 'a8b362c78a04';
(isset($_COOKIE['ed050c7d7e62']) && $_COOKIE['ed050c7d7e62'] === 'a8b362c78a04') or \fail(__LINE__);
(new \Delight\Cookie\Cookie('ed050c7d7e62'))->deleteAndUnset();
\testEqual(\Delight\Http\ResponseHeader::take('Set-Cookie'), 'Set-Cookie: ed050c7d7e62=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; httponly; SameSite=Lax');
!isset($_COOKIE['ed050c7d7e62']) or \fail(__LINE__);
unset($_COOKIE['ed050c7d7e62']);
$_COOKIE['c8f13ff2a8ad'] = 'cd0a70b224e8';
(isset($_COOKIE['c8f13ff2a8ad']) && $_COOKIE['c8f13ff2a8ad'] === 'cd0a70b224e8') or \fail(__LINE__);
(new \Delight\Cookie\Cookie('c8f13ff2a8ad'))->setDomain('example.com')->deleteAndUnset();
\testEqual(\Delight\Http\ResponseHeader::take('Set-Cookie'), 'Set-Cookie: c8f13ff2a8ad=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=0; path=/; domain=.example.com; httponly; SameSite=Lax');
!isset($_COOKIE['c8f13ff2a8ad']) or \fail(__LINE__);
unset($_COOKIE['c8f13ff2a8ad']);
/* END TEST COOKIES */
/* BEGIN TEST SESSION */
(isset($_SESSION) === false) or \fail(__LINE__);
(\Delight\Cookie\Session::id() === '') or \fail(__LINE__);
\Delight\Cookie\Session::start();
$sessionCookieReferenceHeader = \Delight\Http\ResponseHeader::take('Set-Cookie');
session_write_close();
\Delight\Cookie\Session::start(null);
\testEqual(\Delight\Http\ResponseHeader::take('Set-Cookie'), \str_replace('; SameSite=Lax', '', $sessionCookieReferenceHeader));
session_write_close();
@\Delight\Cookie\Session::start('None');
\testEqual(\Delight\Http\ResponseHeader::take('Set-Cookie'), \str_replace('; SameSite=Lax', '; SameSite=None', $sessionCookieReferenceHeader));
session_write_close();
\Delight\Cookie\Session::start('Lax');
\testEqual(\Delight\Http\ResponseHeader::take('Set-Cookie'), $sessionCookieReferenceHeader);
session_write_close();
\Delight\Cookie\Session::start('Strict');
\testEqual(\Delight\Http\ResponseHeader::take('Set-Cookie'), \str_replace('; SameSite=Lax', '; SameSite=Strict', $sessionCookieReferenceHeader));
session_write_close();
\Delight\Cookie\Session::start();
(isset($_SESSION) === true) or \fail(__LINE__);
(\Delight\Cookie\Session::id() !== '') or \fail(__LINE__);
$oldSessionId = \Delight\Cookie\Session::id();
\Delight\Cookie\Session::regenerate();
(\Delight\Cookie\Session::id() !== $oldSessionId) or \fail(__LINE__);
(\Delight\Cookie\Session::id() !== null) or \fail(__LINE__);
\session_unset();
(isset($_SESSION['key1']) === false) or \fail(__LINE__);
(\Delight\Cookie\Session::has('key1') === false) or \fail(__LINE__);
(\Delight\Cookie\Session::get('key1') === null) or \fail(__LINE__);
(\Delight\Cookie\Session::get('key1', 5) === 5) or \fail(__LINE__);
(\Delight\Cookie\Session::get('key1', 'monkey') === 'monkey') or \fail(__LINE__);
\Delight\Cookie\Session::set('key1', 'value1');
(isset($_SESSION['key1']) === true) or \fail(__LINE__);
(\Delight\Cookie\Session::has('key1') === true) or \fail(__LINE__);
(\Delight\Cookie\Session::get('key1') === 'value1') or \fail(__LINE__);
(\Delight\Cookie\Session::get('key1', 5) === 'value1') or \fail(__LINE__);
(\Delight\Cookie\Session::get('key1', 'monkey') === 'value1') or \fail(__LINE__);
(\Delight\Cookie\Session::take('key1') === 'value1') or \fail(__LINE__);
(\Delight\Cookie\Session::take('key1') === null) or \fail(__LINE__);
(\Delight\Cookie\Session::take('key1', 'value2') === 'value2') or \fail(__LINE__);
(isset($_SESSION['key1']) === false) or \fail(__LINE__);
(\Delight\Cookie\Session::has('key1') === false) or \fail(__LINE__);
\Delight\Cookie\Session::set('key2', 'value3');
(isset($_SESSION['key2']) === true) or \fail(__LINE__);
(\Delight\Cookie\Session::has('key2') === true) or \fail(__LINE__);
(\Delight\Cookie\Session::get('key2', 'value4') === 'value3') or \fail(__LINE__);
\Delight\Cookie\Session::delete('key2');
(\Delight\Cookie\Session::get('key2', 'value4') === 'value4') or \fail(__LINE__);
(\Delight\Cookie\Session::get('key2') === null) or \fail(__LINE__);
(\Delight\Cookie\Session::has('key2') === false) or \fail(__LINE__);
\session_destroy();
\Delight\Http\ResponseHeader::take('Set-Cookie');
/* END TEST SESSION */
echo 'ALL TESTS PASSED' . "\n";
function testCookie($name, $value = null, $expire = 0, $path = null, $domain = null, $secure = false, $httpOnly = false) {
$actualValue = \Delight\Cookie\Cookie::buildCookieHeader($name, $value, $expire, $path, $domain, $secure, $httpOnly);
if (\is_null($actualValue)) {
$expectedValue = @\simulateSetCookie($name, $value, $expire, $path, $domain, $secure, $httpOnly);
}
else {
$expectedValue = \simulateSetCookie($name, $value, $expire, $path, $domain, $secure, $httpOnly);
}
\testEqual($actualValue, $expectedValue);
}
function testEqual($actualValue, $expectedValue) {
$actualValue = (string) $actualValue;
$expectedValue = (string) $expectedValue;
echo '[';
echo $expectedValue;
echo ']';
echo "\n";
if (\strcasecmp($actualValue, $expectedValue) !== 0) {
echo 'FAILED: ';
echo '[';
echo $actualValue;
echo ']';
echo ' !== ';
echo '[';
echo $expectedValue;
echo ']';
echo "\n";
exit;
}
}
function simulateSetCookie($name, $value = null, $expire = 0, $path = null, $domain = null, $secure = false, $httpOnly = false) {
\setcookie($name, $value, $expire, $path, $domain, $secure, $httpOnly);
return \Delight\Http\ResponseHeader::take('Set-Cookie');
}
function fail($lineNumber) {
exit('Error in line ' . $lineNumber);
}