mirror of
https://gitee.com/hyperf/hyperf.git
synced 2024-12-03 12:17:48 +08:00
commit
c407b1e09a
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,6 +5,7 @@
|
||||
.idea/
|
||||
.git/
|
||||
runtime/
|
||||
codeCoverage/
|
||||
vendor/
|
||||
.phpintel/
|
||||
.env
|
||||
|
@ -102,6 +102,7 @@
|
||||
"hyperf/redis": "self.version",
|
||||
"hyperf/server": "self.version",
|
||||
"hyperf/service-governance": "self.version",
|
||||
"hyperf/session": "self.version",
|
||||
"hyperf/swagger": "self.version",
|
||||
"hyperf/swoole-enterprise": "self.version",
|
||||
"hyperf/task": "self.version",
|
||||
@ -173,6 +174,7 @@
|
||||
"Hyperf\\Rpc\\": "src/rpc/src/",
|
||||
"Hyperf\\Server\\": "src/server/src/",
|
||||
"Hyperf\\ServiceGovernance\\": "src/service-governance/src/",
|
||||
"Hyperf\\Session\\": "src/session/src/",
|
||||
"Hyperf\\Snowflake\\": "src/snowflake/src/",
|
||||
"Hyperf\\Socket\\": "src/socket/src/",
|
||||
"Hyperf\\Swagger\\": "src/swagger/src/",
|
||||
@ -233,6 +235,7 @@
|
||||
"HyperfTest\\Rpc\\": "src/rpc/tests/",
|
||||
"HyperfTest\\Server\\": "src/server/tests/",
|
||||
"HyperfTest\\ServiceGovernance\\": "src/service-governance/tests/",
|
||||
"HyperfTest\\Session\\": "src/session/tests/",
|
||||
"HyperfTest\\Snowflake\\": "src/snowflake/tests/",
|
||||
"HyperfTest\\Socket\\": "src/socket/tests/",
|
||||
"HyperfTest\\Task\\": "src/task/tests/",
|
||||
@ -293,6 +296,7 @@
|
||||
"Hyperf\\RpcServer\\ConfigProvider",
|
||||
"Hyperf\\Server\\ConfigProvider",
|
||||
"Hyperf\\ServiceGovernance\\ConfigProvider",
|
||||
"Hyperf\\Session\\ConfigProvider",
|
||||
"Hyperf\\Snowflake\\ConfigProvider",
|
||||
"Hyperf\\Socket\\ConfigProvider",
|
||||
"Hyperf\\Swagger\\ConfigProvider",
|
||||
|
31
phpunit.xml
31
phpunit.xml
@ -41,6 +41,7 @@
|
||||
<directory suffix="Test.php">./src/rpc/tests</directory>
|
||||
<directory suffix="Test.php">./src/server/tests</directory>
|
||||
<directory suffix="Test.php">./src/service-governance/tests</directory>
|
||||
<directory suffix="Test.php">./src/session/tests</directory>
|
||||
<directory suffix="Test.php">./src/snowflake/tests</directory>
|
||||
<directory suffix="Test.php">./src/socket/tests</directory>
|
||||
<directory suffix="Test.php">./src/task/tests</directory>
|
||||
@ -54,7 +55,35 @@
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./src/database</directory>
|
||||
<directory suffix=".php">./src/amqp/src</directory>
|
||||
<directory suffix=".php">./src/async-queue/src</directory>
|
||||
<directory suffix=".php">./src/cache/src</directory>
|
||||
<directory suffix=".php">./src/config/src</directory>
|
||||
<directory suffix=".php">./src/constants/src</directory>
|
||||
<directory suffix=".php">./src/consul/src</directory>
|
||||
<directory suffix=".php">./src/database/src</directory>
|
||||
<directory suffix=".php">./src/db-connection/src</directory>
|
||||
<directory suffix=".php">./src/di/src</directory>
|
||||
<directory suffix=".php">./src/dispatcher/src</directory>
|
||||
<directory suffix=".php">./src/elasticsearch/src</directory>
|
||||
<directory suffix=".php">./src/event/src</directory>
|
||||
<directory suffix=".php">./src/grpc-client/src</directory>
|
||||
<directory suffix=".php">./src/guzzle/src</directory>
|
||||
<directory suffix=".php">./src/http-message/src</directory>
|
||||
<directory suffix=".php">./src/http-server/src</directory>
|
||||
<directory suffix=".php">./src/json-rpc/src</directory>
|
||||
<directory suffix=".php">./src/logger/src</directory>
|
||||
<directory suffix=".php">./src/model-cache/src</directory>
|
||||
<directory suffix=".php">./src/paginator/src</directory>
|
||||
<directory suffix=".php">./src/redis/src</directory>
|
||||
<directory suffix=".php">./src/rpc/src</directory>
|
||||
<directory suffix=".php">./src/server/src</directory>
|
||||
<directory suffix=".php">./src/service-governance/src</directory>
|
||||
<directory suffix=".php">./src/session/src</directory>
|
||||
<directory suffix=".php">./src/snowflake/src</directory>
|
||||
<directory suffix=".php">./src/task/src</directory>
|
||||
<directory suffix=".php">./src/utils/src</directory>
|
||||
<directory suffix=".php">./src/websocket-client/src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
|
159
src/contract/src/SessionInterface.php
Normal file
159
src/contract/src/SessionInterface.php
Normal file
@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Contract;
|
||||
|
||||
interface SessionInterface
|
||||
{
|
||||
/**
|
||||
* Starts the session storage.
|
||||
*
|
||||
* @throws \RuntimeException if session fails to start
|
||||
* @return bool True if session started
|
||||
*/
|
||||
public function start(): bool;
|
||||
|
||||
/**
|
||||
* Returns the session ID.
|
||||
*
|
||||
* @return string The session ID
|
||||
*/
|
||||
public function getId(): string;
|
||||
|
||||
/**
|
||||
* Sets the session ID.
|
||||
*/
|
||||
public function setId(string $id);
|
||||
|
||||
/**
|
||||
* Returns the session name.
|
||||
*/
|
||||
public function getName(): string;
|
||||
|
||||
/**
|
||||
* Sets the session name.
|
||||
*/
|
||||
public function setName(string $name);
|
||||
|
||||
/**
|
||||
* Invalidates the current session.
|
||||
*
|
||||
* Clears all session attributes and flashes and regenerates the
|
||||
* session and deletes the old session from persistence.
|
||||
*
|
||||
* @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
|
||||
* will leave the system settings unchanged, 0 sets the cookie
|
||||
* to expire with browser session. Time is in seconds, and is
|
||||
* not a Unix timestamp.
|
||||
*
|
||||
* @return bool True if session invalidated, false if error
|
||||
*/
|
||||
public function invalidate(?int $lifetime = null): bool;
|
||||
|
||||
/**
|
||||
* Migrates the current session to a new session id while maintaining all
|
||||
* session attributes.
|
||||
*
|
||||
* @param bool $destroy Whether to delete the old session or leave it to garbage collection
|
||||
* @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
|
||||
* will leave the system settings unchanged, 0 sets the cookie
|
||||
* to expire with browser session. Time is in seconds, and is
|
||||
* not a Unix timestamp.
|
||||
*
|
||||
* @return bool True if session migrated, false if error
|
||||
*/
|
||||
public function migrate(bool $destroy = false, ?int $lifetime = null): bool;
|
||||
|
||||
/**
|
||||
* Force the session to be saved and closed.
|
||||
*
|
||||
* This method is generally not required for real sessions as
|
||||
* the session will be automatically saved at the end of
|
||||
* code execution.
|
||||
*/
|
||||
public function save(): void;
|
||||
|
||||
/**
|
||||
* Checks if an attribute is defined.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
*
|
||||
* @return bool true if the attribute is defined, false otherwise
|
||||
*/
|
||||
public function has(string $name): bool;
|
||||
|
||||
/**
|
||||
* Returns an attribute.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
* @param mixed $default The default value if not found
|
||||
*/
|
||||
public function get(string $name, $default = null);
|
||||
|
||||
/**
|
||||
* Sets an attribute.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function set(string $name, $value): void;
|
||||
|
||||
/**
|
||||
* Put a key / value pair or array of key / value pairs in the session.
|
||||
*
|
||||
* @param array|string $key
|
||||
* @param null|mixed $value
|
||||
*/
|
||||
public function put($key, $value = null): void;
|
||||
|
||||
/**
|
||||
* Returns attributes.
|
||||
*/
|
||||
public function all(): array;
|
||||
|
||||
/**
|
||||
* Sets attributes.
|
||||
*/
|
||||
public function replace(array $attributes): void;
|
||||
|
||||
/**
|
||||
* Removes an attribute, returning its value.
|
||||
*
|
||||
* @return mixed The removed value or null when it does not exist
|
||||
*/
|
||||
public function remove(string $name);
|
||||
|
||||
/**
|
||||
* Remove one or many items from the session.
|
||||
*
|
||||
* @param array|string $keys
|
||||
*/
|
||||
public function forget($keys): void;
|
||||
|
||||
/**
|
||||
* Clears all attributes.
|
||||
*/
|
||||
public function clear(): void;
|
||||
|
||||
/**
|
||||
* Checks if the session was started.
|
||||
*/
|
||||
public function isStarted(): bool;
|
||||
|
||||
/**
|
||||
* Get the previous URL from the session.
|
||||
*/
|
||||
public function previousUrl(): ?string;
|
||||
|
||||
/**
|
||||
* Set the "previous" URL in the session.
|
||||
*/
|
||||
public function setPreviousUrl(string $url): void;
|
||||
}
|
@ -71,6 +71,14 @@ class Response extends \Hyperf\HttpMessage\Base\Response implements Sendable
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all cookies.
|
||||
*/
|
||||
public function getCookies(): array
|
||||
{
|
||||
return $this->cookies;
|
||||
}
|
||||
|
||||
public function getSwooleResponse(): ?\Swoole\Http\Response
|
||||
{
|
||||
return $this->swooleResponse;
|
||||
|
@ -38,7 +38,7 @@ class RedisFactory
|
||||
{
|
||||
$proxy = $this->proxies[$poolName] ?? null;
|
||||
if (! $proxy instanceof RedisProxy) {
|
||||
throw new InvalidRedisProxyException('Redis proxy is invalid.');
|
||||
throw new InvalidRedisProxyException('Invalid Redis proxy.');
|
||||
}
|
||||
|
||||
return $proxy;
|
||||
|
1
src/session/.gitattributes
vendored
Normal file
1
src/session/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
/tests export-ignore
|
9
src/session/LICENSE.md
Normal file
9
src/session/LICENSE.md
Normal file
@ -0,0 +1,9 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Hyperf
|
||||
|
||||
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.
|
53
src/session/composer.json
Normal file
53
src/session/composer.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "hyperf/session",
|
||||
"description": "A session library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"php",
|
||||
"session"
|
||||
],
|
||||
"support": {
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"psr/http-server-middleware": "^1.0",
|
||||
"hyperf/utils": "^1.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^2.9",
|
||||
"malukenho/docheader": "^0.1.6",
|
||||
"mockery/mockery": "^1.0",
|
||||
"phpunit/phpunit": "^7.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/container": "Required to use SessionFactory.",
|
||||
"hyperf/config": "Required to load session config."
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Hyperf\\Session\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"HyperfTest\\Session\\": "tests/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1-dev"
|
||||
},
|
||||
"hyperf": {
|
||||
"config": "Hyperf\\Session\\ConfigProvider"
|
||||
}
|
||||
},
|
||||
"bin": [
|
||||
],
|
||||
"scripts": {
|
||||
"cs-fix": "php-cs-fixer fix $1",
|
||||
"test": "phpunit --colors=always"
|
||||
}
|
||||
}
|
22
src/session/publish/session.php
Normal file
22
src/session/publish/session.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
use Hyperf\Session\Handler;
|
||||
|
||||
return [
|
||||
'handler' => Handler\FileHandler::class,
|
||||
'options' => [
|
||||
'connection' => 'default',
|
||||
'path' => BASE_PATH . '/runtime/session',
|
||||
'gc_lifetime' => 1200,
|
||||
],
|
||||
];
|
48
src/session/src/ConfigProvider.php
Normal file
48
src/session/src/ConfigProvider.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Session;
|
||||
|
||||
use Hyperf\Contract\SessionInterface;
|
||||
use Hyperf\Session\Handler\FileHandler;
|
||||
use Hyperf\Session\Handler\FileHandlerFactory;
|
||||
use Hyperf\Session\Handler\RedisHandler;
|
||||
use Hyperf\Session\Handler\RedisHandlerFactory;
|
||||
|
||||
class ConfigProvider
|
||||
{
|
||||
public function __invoke(): array
|
||||
{
|
||||
return [
|
||||
'annotations' => [
|
||||
'scan' => [
|
||||
'paths' => [
|
||||
__DIR__,
|
||||
],
|
||||
],
|
||||
],
|
||||
'dependencies' => [
|
||||
FileHandler::class => FileHandlerFactory::class,
|
||||
RedisHandler::class => RedisHandlerFactory::class,
|
||||
SessionInterface::class => SessionProxy::class,
|
||||
],
|
||||
'publish' => [
|
||||
[
|
||||
'id' => 'config',
|
||||
'description' => 'The config of session.',
|
||||
'source' => __DIR__ . '/../publish/session.php',
|
||||
'destination' => BASE_PATH . '/config/autoload/session.php',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
100
src/session/src/FlashTrait.php
Normal file
100
src/session/src/FlashTrait.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Session;
|
||||
|
||||
trait FlashTrait
|
||||
{
|
||||
/**
|
||||
* Flash a key / value pair to the session.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function flash(string $key, $value = true): void
|
||||
{
|
||||
$this->put($key, $value);
|
||||
|
||||
$this->push('_flash.new', $key);
|
||||
|
||||
$this->removeFromOldFlashData([$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flash a key / value pair to the session for immediate use.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function now(string $key, $value): void
|
||||
{
|
||||
$this->put($key, $value);
|
||||
|
||||
$this->push('_flash.old', $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflash all of the session flash data.
|
||||
*/
|
||||
public function reflash(): void
|
||||
{
|
||||
$this->mergeNewFlashes($this->get('_flash.old', []));
|
||||
|
||||
$this->put('_flash.old', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflash a subset of the current flash data.
|
||||
*
|
||||
* @param array|mixed $keys
|
||||
*/
|
||||
public function keep($keys = null): void
|
||||
{
|
||||
$this->mergeNewFlashes($keys = is_array($keys) ? $keys : func_get_args());
|
||||
|
||||
$this->removeFromOldFlashData($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flash an input array to the session.
|
||||
*/
|
||||
public function flashInput(array $value): void
|
||||
{
|
||||
$this->flash('_old_input', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Age the flash data for the session.
|
||||
*/
|
||||
public function ageFlashData(): void
|
||||
{
|
||||
$this->forget($this->get('_flash.old', []));
|
||||
|
||||
$this->put('_flash.old', $this->get('_flash.new', []));
|
||||
|
||||
$this->put('_flash.new', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge new flash keys into the new flash array.
|
||||
*/
|
||||
protected function mergeNewFlashes(array $keys): void
|
||||
{
|
||||
$values = array_unique(array_merge($this->get('_flash.new', []), $keys));
|
||||
|
||||
$this->put('_flash.new', $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given keys from the old flash data.
|
||||
*/
|
||||
protected function removeFromOldFlashData(array $keys): void
|
||||
{
|
||||
$this->put('_flash.old', array_diff($this->get('_flash.old', []), $keys));
|
||||
}
|
||||
}
|
176
src/session/src/Handler/FileHandler.php
Normal file
176
src/session/src/Handler/FileHandler.php
Normal file
@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Session\Handler;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Hyperf\Utils\Filesystem\Filesystem;
|
||||
use SessionHandlerInterface;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
class FileHandler implements SessionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* The number of minutes the session should be valid.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $minutes;
|
||||
|
||||
/**
|
||||
* @var Filesystem
|
||||
*/
|
||||
private $files;
|
||||
|
||||
/**
|
||||
* The path where sessions should be stored.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $path;
|
||||
|
||||
public function __construct(Filesystem $files, string $path, int $minutes)
|
||||
{
|
||||
$this->files = $files;
|
||||
$this->path = $path;
|
||||
$this->minutes = $minutes;
|
||||
if (! file_exists($path)) {
|
||||
$files->makeDirectory($path, 0755, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the session.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.close.php
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a session.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.destroy.php
|
||||
* @param string $session_id the session ID being destroyed
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function destroy($session_id)
|
||||
{
|
||||
$this->files->delete($this->path . '/' . $session_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup old sessions.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.gc.php
|
||||
* @param int $maxlifetime <p>
|
||||
* Sessions that have not updated for
|
||||
* the last maxlifetime seconds will be removed.
|
||||
* </p>
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
$files = Finder::create()
|
||||
->in($this->path)
|
||||
->files()
|
||||
->ignoreDotFiles(true)
|
||||
->date('<= now - ' . $maxlifetime . ' seconds');
|
||||
|
||||
/** @var \SplFileInfo $file */
|
||||
foreach ($files as $file) {
|
||||
$this->files->delete($file->getRealPath());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize session.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.open.php
|
||||
* @param string $save_path the path where to store/retrieve the session
|
||||
* @param string $name the session name
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function open($save_path, $name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read session data.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.read.php
|
||||
* @param string $session_id the session id to read data for
|
||||
* @return string <p>
|
||||
* Returns an encoded string of the read data.
|
||||
* If nothing was read, it must return an empty string.
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function read($session_id)
|
||||
{
|
||||
if ($this->files->isFile($path = $this->path . '/' . $session_id)) {
|
||||
if ($this->files->lastModified($path) >= Carbon::now()->subMinutes($this->minutes)->getTimestamp()) {
|
||||
return $this->files->sharedGet($path);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Write session data.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.write.php
|
||||
* @param string $session_id the session id
|
||||
* @param string $session_data <p>
|
||||
* The encoded session data. This data is the
|
||||
* result of the PHP internally encoding
|
||||
* the $_SESSION superglobal to a serialized
|
||||
* string and passing it as this parameter.
|
||||
* Please note sessions use an alternative serialization method.
|
||||
* </p>
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function write($session_id, $session_data)
|
||||
{
|
||||
$this->files->put($this->path . '/' . $session_id, $session_data, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
31
src/session/src/Handler/FileHandlerFactory.php
Normal file
31
src/session/src/Handler/FileHandlerFactory.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Session\Handler;
|
||||
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\Utils\Filesystem\Filesystem;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class FileHandlerFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$config = $container->get(ConfigInterface::class);
|
||||
$path = $config->get('session.options.path');
|
||||
$minutes = $config->get('session.options.gc_maxlifetime', 1200);
|
||||
if (! $path) {
|
||||
throw new \InvalidArgumentException('Invalid session path.');
|
||||
}
|
||||
return new FileHandler($container->get(Filesystem::class), $path, $minutes);
|
||||
}
|
||||
}
|
125
src/session/src/Handler/NullHandler.php
Normal file
125
src/session/src/Handler/NullHandler.php
Normal file
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Session\Handler;
|
||||
|
||||
use SessionHandlerInterface;
|
||||
|
||||
class NullHandler implements SessionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Close the session.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.close.php
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a session.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.destroy.php
|
||||
* @param string $session_id the session ID being destroyed
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function destroy($session_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup old sessions.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.gc.php
|
||||
* @param int $maxlifetime <p>
|
||||
* Sessions that have not updated for
|
||||
* the last maxlifetime seconds will be removed.
|
||||
* </p>
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize session.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.open.php
|
||||
* @param string $save_path the path where to store/retrieve the session
|
||||
* @param string $name the session name
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function open($save_path, $name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read session data.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.read.php
|
||||
* @param string $session_id the session id to read data for
|
||||
* @return string <p>
|
||||
* Returns an encoded string of the read data.
|
||||
* If nothing was read, it must return an empty string.
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function read($session_id)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Write session data.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.write.php
|
||||
* @param string $session_id the session id
|
||||
* @param string $session_data <p>
|
||||
* The encoded session data. This data is the
|
||||
* result of the PHP internally encoding
|
||||
* the $_SESSION superglobal to a serialized
|
||||
* string and passing it as this parameter.
|
||||
* Please note sessions use an alternative serialization method.
|
||||
* </p>
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function write($session_id, $session_data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
147
src/session/src/Handler/RedisHandler.php
Normal file
147
src/session/src/Handler/RedisHandler.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Session\Handler;
|
||||
|
||||
use Hyperf\Redis\Redis as RedisProxy;
|
||||
use SessionHandlerInterface;
|
||||
|
||||
class RedisHandler implements SessionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var \Hyperf\Redis\Redis|\Predis\Client|\Redis|\RedisArray|\RedisCluster
|
||||
*/
|
||||
protected $redis;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $gcMaxLifeTime = 1200;
|
||||
|
||||
public function __construct($redis, int $gcMaxLifeTime)
|
||||
{
|
||||
if (! $redis instanceof \Redis && ! $redis instanceof \RedisArray && ! $redis instanceof \RedisCluster && ! $redis instanceof \Predis\Client && ! $redis instanceof RedisProxy) {
|
||||
throw new \InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster, Predis\Client or Hyperf\Redis\Redis, %s given', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis)));
|
||||
}
|
||||
|
||||
$this->redis = $redis;
|
||||
$this->gcMaxLifeTime = $gcMaxLifeTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the session.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.close.php
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a session.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.destroy.php
|
||||
* @param string $session_id the session ID being destroyed
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function destroy($session_id)
|
||||
{
|
||||
$this->redis->del($session_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup old sessions.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.gc.php
|
||||
* @param int $maxlifetime <p>
|
||||
* Sessions that have not updated for
|
||||
* the last maxlifetime seconds will be removed.
|
||||
* </p>
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize session.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.open.php
|
||||
* @param string $save_path the path where to store/retrieve the session
|
||||
* @param string $name the session name
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function open($save_path, $name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read session data.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.read.php
|
||||
* @param string $session_id the session id to read data for
|
||||
* @return string <p>
|
||||
* Returns an encoded string of the read data.
|
||||
* If nothing was read, it must return an empty string.
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function read($session_id)
|
||||
{
|
||||
return $this->redis->get($session_id) ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Write session data.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.write.php
|
||||
* @param string $session_id the session id
|
||||
* @param string $session_data <p>
|
||||
* The encoded session data. This data is the
|
||||
* result of the PHP internally encoding
|
||||
* the $_SESSION superglobal to a serialized
|
||||
* string and passing it as this parameter.
|
||||
* Please note sessions use an alternative serialization method.
|
||||
* </p>
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function write($session_id, $session_data)
|
||||
{
|
||||
return (bool) $this->redis->setEx($session_id, (int) $this->gcMaxLifeTime, $session_data);
|
||||
}
|
||||
}
|
30
src/session/src/Handler/RedisHandlerFactory.php
Normal file
30
src/session/src/Handler/RedisHandlerFactory.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Session\Handler;
|
||||
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\Redis\RedisFactory;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
class RedisHandlerFactory
|
||||
{
|
||||
public function __invoke(ContainerInterface $container)
|
||||
{
|
||||
$config = $container->get(ConfigInterface::class);
|
||||
$connection = $config->get('session.options.connection');
|
||||
$gcMaxLifetime = $config->get('session.options.gc_maxlifetime', 1200);
|
||||
$redisFactory = $container->get(RedisFactory::class);
|
||||
$redis = $redisFactory->get($connection);
|
||||
return new RedisHandler($redis, $gcMaxLifetime);
|
||||
}
|
||||
}
|
137
src/session/src/Middleware/SessionMiddleware.php
Normal file
137
src/session/src/Middleware/SessionMiddleware.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Session\Middleware;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\Contract\SessionInterface;
|
||||
use Hyperf\HttpMessage\Cookie\Cookie;
|
||||
use Hyperf\Session\SessionManager;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
class SessionMiddleware implements MiddlewareInterface
|
||||
{
|
||||
/**
|
||||
* @var SessionManager
|
||||
*/
|
||||
private $sessionManager;
|
||||
|
||||
/**
|
||||
* @var ConfigInterface
|
||||
*/
|
||||
private $config;
|
||||
|
||||
public function __construct(SessionManager $sessionManager, ConfigInterface $config)
|
||||
{
|
||||
$this->sessionManager = $sessionManager;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an incoming server request.
|
||||
* Processes an incoming server request in order to produce a response.
|
||||
* If unable to produce the response itself, it may delegate to the provided
|
||||
* request handler to do so.
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
if (! $this->isSessionAvailable()) {
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
$session = $this->sessionManager->start($request);
|
||||
|
||||
$response = $handler->handle($request);
|
||||
|
||||
$this->storeCurrentUrl($request, $session);
|
||||
|
||||
$response = $this->addCookieToResponse($request, $response, $session);
|
||||
|
||||
// @TODO Use defer
|
||||
$this->sessionManager->end($session);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function isSessionAvailable(): bool
|
||||
{
|
||||
return $this->config->has('session.handler');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL (no query string) for the request.
|
||||
*/
|
||||
private function url(RequestInterface $request): string
|
||||
{
|
||||
return rtrim(preg_replace('/\?.*/', '', $request->getUri()), '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the current URL for the request if necessary.
|
||||
*/
|
||||
private function storeCurrentUrl(RequestInterface $request, SessionInterface $session)
|
||||
{
|
||||
if ($request->getMethod() === 'GET') {
|
||||
$session->setPreviousUrl($this->fullUrl($request));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the session lifetime in seconds.
|
||||
*/
|
||||
private function getCookieExpirationDate(): int
|
||||
{
|
||||
if ($this->config->get('session.options.expire_on_close')) {
|
||||
$expirationDate = 0;
|
||||
} else {
|
||||
$expirationDate = Carbon::now()->addMinutes(5 * 60)->getTimestamp();
|
||||
}
|
||||
return $expirationDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the session cookie to the response·.
|
||||
*/
|
||||
private function addCookieToResponse(
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
SessionInterface $session
|
||||
): ResponseInterface {
|
||||
$uri = $request->getUri();
|
||||
$path = '/';
|
||||
$domain = $uri->getHost();
|
||||
$secure = strtolower($uri->getScheme()) === 'https';
|
||||
$httpOnly = true;
|
||||
if (! method_exists($response, 'withCookie')) {
|
||||
// @TODO Adapte original response object.
|
||||
throw new \RuntimeException('Unsupport response object.');
|
||||
}
|
||||
/* @var \Hyperf\HttpMessage\Server\Response $response */
|
||||
return $response->withCookie(new Cookie($session->getName(), $session->getId(), $this->getCookieExpirationDate(), $path, $domain, $secure, $httpOnly));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full URL for the request.
|
||||
*/
|
||||
private function fullUrl(RequestInterface $request): string
|
||||
{
|
||||
$uri = $request->getUri();
|
||||
$query = $uri->getQuery();
|
||||
$question = $uri->getHost() . $uri->getPath() == '/' ? '/?' : '?';
|
||||
return $query ? $this->url($request) . $question . $query : $this->url($request);
|
||||
}
|
||||
}
|
372
src/session/src/Session.php
Normal file
372
src/session/src/Session.php
Normal file
@ -0,0 +1,372 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Session;
|
||||
|
||||
use Hyperf\Contract\SessionInterface;
|
||||
use Hyperf\Utils\Arr;
|
||||
use Hyperf\Utils\Str;
|
||||
use SessionHandlerInterface;
|
||||
|
||||
/**
|
||||
* This's a data class, please create an new instance for each requests.
|
||||
*/
|
||||
class Session implements SessionInterface
|
||||
{
|
||||
use FlashTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* Session store started status.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $started = false;
|
||||
|
||||
/**
|
||||
* @var \SessionHandlerInterface
|
||||
*/
|
||||
protected $handler;
|
||||
|
||||
public function __construct($name, SessionHandlerInterface $handler, $id = null)
|
||||
{
|
||||
if (! is_string($id) || ! $this->isValidId($id)) {
|
||||
$id = $this->generateSessionId();
|
||||
}
|
||||
$this->setId($id);
|
||||
$this->setName($name);
|
||||
$this->handler = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this is a valid session ID.
|
||||
*/
|
||||
public function isValidId(string $id): bool
|
||||
{
|
||||
return is_string($id) && ctype_alnum($id) && strlen($id) === 40;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the session storage.
|
||||
*/
|
||||
public function start(): bool
|
||||
{
|
||||
$this->loadSession();
|
||||
|
||||
return $this->started = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session ID.
|
||||
*
|
||||
* @return string The session ID
|
||||
*/
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session ID.
|
||||
*/
|
||||
public function setId(string $id): void
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session name.
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the session name.
|
||||
*/
|
||||
public function setName(string $name): void
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the current session.
|
||||
* Clears all session attributes and flashes and regenerates the
|
||||
* session and deletes the old session from persistence.
|
||||
*
|
||||
* @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
|
||||
* will leave the system settings unchanged, 0 sets the cookie
|
||||
* to expire with browser session. Time is in seconds, and is
|
||||
* not a Unix timestamp.
|
||||
* @return bool True if session invalidated, false if error
|
||||
*/
|
||||
public function invalidate(?int $lifetime = null): bool
|
||||
{
|
||||
$this->clear();
|
||||
|
||||
return $this->migrate(true, $lifetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the current session to a new session id while maintaining all
|
||||
* session attributes.
|
||||
*
|
||||
* @param bool $destroy Whether to delete the old session or leave it to garbage collection
|
||||
* @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
|
||||
* will leave the system settings unchanged, 0 sets the cookie
|
||||
* to expire with browser session. Time is in seconds, and is
|
||||
* not a Unix timestamp.
|
||||
* @return bool True if session migrated, false if error
|
||||
*/
|
||||
public function migrate(bool $destroy = false, ?int $lifetime = null): bool
|
||||
{
|
||||
if ($destroy) {
|
||||
$this->handler->destroy($this->getId());
|
||||
}
|
||||
|
||||
$this->setId($this->generateSessionId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the session to be saved and closed.
|
||||
* This method is generally not required for real sessions as
|
||||
* the session will be automatically saved at the end of
|
||||
* code execution.
|
||||
*/
|
||||
public function save(): void
|
||||
{
|
||||
$this->ageFlashData();
|
||||
|
||||
$this->handler->write($this->getId(), $this->prepareForStorage(serialize($this->attributes)));
|
||||
|
||||
$this->started = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an attribute is defined.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
* @return bool true if the attribute is defined, false otherwise
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return Arr::exists($this->attributes, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an attribute.
|
||||
*
|
||||
* @param string $name The attribute name
|
||||
* @param mixed $default The default value if not found
|
||||
*/
|
||||
public function get(string $name, $default = null)
|
||||
{
|
||||
return data_get($this->attributes, $name, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function set(string $name, $value): void
|
||||
{
|
||||
data_set($this->attributes, $name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a key / value pair or array of key / value pairs in the session.
|
||||
*
|
||||
* @param array|string $key
|
||||
* @param null|mixed $value
|
||||
*/
|
||||
public function put($key, $value = null): void
|
||||
{
|
||||
if (! is_array($key)) {
|
||||
$key = [$key => $value];
|
||||
}
|
||||
|
||||
foreach ($key as $arrayKey => $arrayValue) {
|
||||
Arr::set($this->attributes, $arrayKey, $arrayValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns attributes.
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets attributes.
|
||||
*/
|
||||
public function replace(array $attributes): void
|
||||
{
|
||||
foreach ($attributes as $name => $value) {
|
||||
data_set($this->attributes, $name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an attribute, returning its value.
|
||||
*
|
||||
* @return mixed The removed value or null when it does not exist
|
||||
*/
|
||||
public function remove(string $name)
|
||||
{
|
||||
return Arr::pull($this->attributes, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one or many items from the session.
|
||||
*
|
||||
* @param array|string $keys
|
||||
*/
|
||||
public function forget($keys): void
|
||||
{
|
||||
Arr::forget($this->attributes, $keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all attributes.
|
||||
*/
|
||||
public function clear(): void
|
||||
{
|
||||
$this->attributes = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the session was started.
|
||||
*/
|
||||
public function isStarted(): bool
|
||||
{
|
||||
return $this->started;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSRF token value.
|
||||
*/
|
||||
public function token(): string
|
||||
{
|
||||
return (string) $this->get('_token');
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate the CSRF token value.
|
||||
*/
|
||||
public function regenerateToken(): string
|
||||
{
|
||||
$this->put('_token', $token = Str::random(40));
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the previous URL from the session.
|
||||
*/
|
||||
public function previousUrl(): ?string
|
||||
{
|
||||
$previousUrl = $this->get('_previous.url');
|
||||
if (! is_string($previousUrl)) {
|
||||
$previousUrl = null;
|
||||
}
|
||||
return $previousUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "previous" URL in the session.
|
||||
*/
|
||||
public function setPreviousUrl(string $url): void
|
||||
{
|
||||
$this->set('_previous.url', $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a value onto a session array.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function push(string $key, $value): void
|
||||
{
|
||||
$array = $this->get($key, []);
|
||||
|
||||
$array[] = $value;
|
||||
|
||||
$this->put($key, $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new random sessoion ID.
|
||||
*/
|
||||
protected function generateSessionId(): string
|
||||
{
|
||||
return Str::random(40);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the session data from the handler.
|
||||
*/
|
||||
protected function loadSession(): void
|
||||
{
|
||||
$this->attributes = array_merge($this->attributes, $this->readFromHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the session data from the handler.
|
||||
*/
|
||||
protected function readFromHandler(): array
|
||||
{
|
||||
if ($data = $this->handler->read($this->getId())) {
|
||||
$data = @unserialize($this->prepareForUnserialize($data));
|
||||
|
||||
if ($data !== false && ! is_null($data) && is_array($data)) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the raw string data from the session for unserialization.
|
||||
*/
|
||||
protected function prepareForUnserialize(string $data): string
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the serialized session data for storage.
|
||||
*/
|
||||
protected function prepareForStorage(string $data): string
|
||||
{
|
||||
return $data;
|
||||
}
|
||||
}
|
92
src/session/src/SessionManager.php
Normal file
92
src/session/src/SessionManager.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Session;
|
||||
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\Contract\SessionInterface;
|
||||
use Hyperf\Utils\Context;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use SessionHandlerInterface;
|
||||
|
||||
class SessionManager
|
||||
{
|
||||
/**
|
||||
* @var \Psr\Container\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var ConfigInterface
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
public function __construct(ContainerInterface $container, ConfigInterface $config)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function getSessionName(): string
|
||||
{
|
||||
return 'HYPERF_SESSION_ID';
|
||||
}
|
||||
|
||||
public function start(ServerRequestInterface $request): SessionInterface
|
||||
{
|
||||
$sessionId = $this->parseSessionId($request);
|
||||
// @TODO Use make() function to create Session object.
|
||||
$session = new Session($this->getSessionName(), $this->buildSessionHandler(), $sessionId);
|
||||
if (! $session->start()) {
|
||||
throw new \RuntimeException('Start session failed.');
|
||||
}
|
||||
$this->setSession($session);
|
||||
return $session;
|
||||
}
|
||||
|
||||
public function end(SessionInterface $session): void
|
||||
{
|
||||
$session->save();
|
||||
}
|
||||
|
||||
public function getSession(): SessionInterface
|
||||
{
|
||||
return Context::get(SessionInterface::class);
|
||||
}
|
||||
|
||||
public function setSession(SessionInterface $session): self
|
||||
{
|
||||
Context::set(SessionInterface::class, $session);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function parseSessionId(ServerRequestInterface $request): ?string
|
||||
{
|
||||
$cookies = $request->getCookieParams();
|
||||
foreach ($cookies as $key => $value) {
|
||||
if ($key === $this->getSessionName()) {
|
||||
return (string) $value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function buildSessionHandler(): SessionHandlerInterface
|
||||
{
|
||||
$handler = $this->config->get('session.handler');
|
||||
if (! $handler || ! class_exists($handler)) {
|
||||
throw new \InvalidArgumentException('Invalid handler of session');
|
||||
}
|
||||
return $this->container->get($handler);
|
||||
}
|
||||
}
|
214
src/session/src/SessionProxy.php
Normal file
214
src/session/src/SessionProxy.php
Normal file
@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace Hyperf\Session;
|
||||
|
||||
use Hyperf\Contract\SessionInterface;
|
||||
use Hyperf\Utils\Context;
|
||||
|
||||
class SessionProxy extends Session
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public function flash(string $key, $value = true): void
|
||||
{
|
||||
$this->getSession()->flash($key, $value);
|
||||
}
|
||||
|
||||
public function now(string $key, $value): void
|
||||
{
|
||||
$this->getSession()->now($key, $value);
|
||||
}
|
||||
|
||||
public function reflash(): void
|
||||
{
|
||||
$this->getSession()->reflash();
|
||||
}
|
||||
|
||||
public function keep($keys = null): void
|
||||
{
|
||||
$this->getSession()->keep($keys);
|
||||
}
|
||||
|
||||
public function flashInput(array $value): void
|
||||
{
|
||||
$this->getSession()->flashInput($value);
|
||||
}
|
||||
|
||||
public function ageFlashData(): void
|
||||
{
|
||||
$this->getSession()->ageFlashData();
|
||||
}
|
||||
|
||||
public function isValidId(string $id): bool
|
||||
{
|
||||
return $this->getSession()->isValidId($id);
|
||||
}
|
||||
|
||||
public function start(): bool
|
||||
{
|
||||
return $this->getSession()->start();
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return $this->getSession()->getId();
|
||||
}
|
||||
|
||||
public function setId(string $id): void
|
||||
{
|
||||
$this->getSession()->setId($id);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->getSession()->getName();
|
||||
}
|
||||
|
||||
public function setName(string $name): void
|
||||
{
|
||||
$this->getSession()->setName($name);
|
||||
}
|
||||
|
||||
public function invalidate(?int $lifetime = null): bool
|
||||
{
|
||||
return $this->getSession()->invalidate($lifetime);
|
||||
}
|
||||
|
||||
public function migrate(bool $destroy = false, ?int $lifetime = null): bool
|
||||
{
|
||||
return $this->getSession->migrate($destroy, $lifetime);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$this->getSession->save();
|
||||
}
|
||||
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return $this->getSession->has($name);
|
||||
}
|
||||
|
||||
public function get(string $name, $default = null)
|
||||
{
|
||||
return $this->getSession()->get($name, $default);
|
||||
}
|
||||
|
||||
public function set(string $name, $value): void
|
||||
{
|
||||
$this->getSession()->set($name, $value);
|
||||
}
|
||||
|
||||
public function put($key, $value = null): void
|
||||
{
|
||||
$this->getSession()->put($key, $value);
|
||||
}
|
||||
|
||||
public function all(): array
|
||||
{
|
||||
return $this->getSession()->all();
|
||||
}
|
||||
|
||||
public function replace(array $attributes): void
|
||||
{
|
||||
$this->getSession()->replace($attributes);
|
||||
}
|
||||
|
||||
public function remove(string $name)
|
||||
{
|
||||
return $this->getSession()->remove($name);
|
||||
}
|
||||
|
||||
public function forget($keys): void
|
||||
{
|
||||
$this->getSession()->forget($keys);
|
||||
}
|
||||
|
||||
public function clear(): void
|
||||
{
|
||||
$this->getSession()->clear();
|
||||
}
|
||||
|
||||
public function isStarted(): bool
|
||||
{
|
||||
return $this->getSession()->isStarted();
|
||||
}
|
||||
|
||||
public function token(): string
|
||||
{
|
||||
return $this->getSession()->token();
|
||||
}
|
||||
|
||||
public function regenerateToken(): string
|
||||
{
|
||||
return $this->getSession()->regenerateToken();
|
||||
}
|
||||
|
||||
public function previousUrl(): ?string
|
||||
{
|
||||
return $this->getSession()->previousUrl();
|
||||
}
|
||||
|
||||
public function setPreviousUrl(string $url): void
|
||||
{
|
||||
$this->getSession()->setPreviousUrl($url);
|
||||
}
|
||||
|
||||
public function push(string $key, $value): void
|
||||
{
|
||||
$this->getSession()->push($key, $value);
|
||||
}
|
||||
|
||||
protected function getSession(): Session
|
||||
{
|
||||
return Context::get(SessionInterface::class);
|
||||
}
|
||||
|
||||
protected function mergeNewFlashes(array $keys): void
|
||||
{
|
||||
$this->getSession()->mergeNewFlashes($keys);
|
||||
}
|
||||
|
||||
protected function removeFromOldFlashData(array $keys): void
|
||||
{
|
||||
$this->getSession()->removeFromOldFlashData($keys);
|
||||
}
|
||||
|
||||
protected function generateSessionId(): string
|
||||
{
|
||||
return $this->getSession()->generateSessionId();
|
||||
}
|
||||
|
||||
protected function loadSession(): void
|
||||
{
|
||||
$this->getSession()->loadSession();
|
||||
}
|
||||
|
||||
protected function readFromHandler(): array
|
||||
{
|
||||
return $this->getSession()->readFromHandler();
|
||||
}
|
||||
|
||||
protected function prepareForUnserialize(string $data): string
|
||||
{
|
||||
return $this->getSession()->prepareForUnserialize($data);
|
||||
}
|
||||
|
||||
protected function prepareForStorage(string $data): string
|
||||
{
|
||||
return $this->getSession()->prepareForStorage($data);
|
||||
}
|
||||
}
|
30
src/session/tests/ConfigProviderTest.php
Normal file
30
src/session/tests/ConfigProviderTest.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace HyperfTest\Session;
|
||||
|
||||
use Hyperf\Session\ConfigProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @covers \Hyperf\Session\ConfigProvider
|
||||
*/
|
||||
class ConfigProviderTest extends TestCase
|
||||
{
|
||||
public function testConfigProvider()
|
||||
{
|
||||
$provider = new ConfigProvider();
|
||||
$this->assertArrayHasKey('annotations', $provider());
|
||||
$this->assertArrayHasKey('publish', $provider());
|
||||
}
|
||||
}
|
73
src/session/tests/FileHandlerTest.php
Normal file
73
src/session/tests/FileHandlerTest.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace HyperfTest\Session;
|
||||
|
||||
use Hyperf\Session\Handler\FileHandler;
|
||||
use Hyperf\Utils\Filesystem\Filesystem;
|
||||
use Hyperf\Utils\Str;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @coversNothing
|
||||
*/
|
||||
class FileHandlerTest extends TestCase
|
||||
{
|
||||
public function testReadAndWrite()
|
||||
{
|
||||
$handler = new FileHandler(new Filesystem(), $path = '/tmp', 10);
|
||||
|
||||
// Useless methods of FileHandler.
|
||||
$this->assertTrue($handler->open('', ''));
|
||||
$this->assertTrue($handler->close('', ''));
|
||||
|
||||
$id = Str::random(40);
|
||||
$data = [
|
||||
'int' => 1,
|
||||
'true' => true,
|
||||
'false' => false,
|
||||
'float' => 1.23,
|
||||
'string' => 'foo',
|
||||
0 => 1,
|
||||
'array' => [
|
||||
'int' => 1,
|
||||
'true' => true,
|
||||
'false' => false,
|
||||
'float' => 1.23,
|
||||
'string' => 'foo',
|
||||
0 => 1,
|
||||
],
|
||||
];
|
||||
$this->assertTrue($handler->write($id, serialize($data)));
|
||||
$this->assertSame($data, unserialize($handler->read($id)));
|
||||
$this->assertFileExists('/tmp/' . $id);
|
||||
$handler->destroy($id);
|
||||
$this->assertFileNotExists('/tmp/' . $id);
|
||||
}
|
||||
|
||||
public function testReadNotExistsSessionId()
|
||||
{
|
||||
$handler = new FileHandler(new Filesystem(), $path = '/tmp', 10);
|
||||
$this->assertSame('', $handler->read('not-exist'));
|
||||
}
|
||||
|
||||
public function testGc()
|
||||
{
|
||||
$handler = new FileHandler(new Filesystem(), $path = __DIR__ . '/runtime/session', 1);
|
||||
$id = Str::random(40);
|
||||
$handler->write($id, 'foo');
|
||||
sleep(1);
|
||||
$handler->gc(1);
|
||||
$this->assertFileNotExists($path . '/' . $id);
|
||||
}
|
||||
}
|
60
src/session/tests/SessionManagerTest.php
Normal file
60
src/session/tests/SessionManagerTest.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace HyperfTest\Session;
|
||||
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\HttpMessage\Server\Request;
|
||||
use Hyperf\Session\Session;
|
||||
use Hyperf\Session\SessionManager;
|
||||
use Hyperf\Utils\Str;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @covers \Hyperf\Session\SessionManager
|
||||
*/
|
||||
class SessionManagerTest extends TestCase
|
||||
{
|
||||
public function testSetterAndGetter()
|
||||
{
|
||||
$sessionManager = new SessionManager(Mockery::mock(ContainerInterface::class), Mockery::mock(ConfigInterface::class));
|
||||
$sessionManager = $sessionManager->setSession($mockSession = Mockery::mock(Session::class));
|
||||
$this->assertInstanceOf(SessionManager::class, $sessionManager);
|
||||
|
||||
$session = $sessionManager->getSession();
|
||||
$this->assertInstanceOf(Session::class, $session);
|
||||
$this->assertSame($mockSession, $session);
|
||||
}
|
||||
|
||||
public function testParseSessionId()
|
||||
{
|
||||
$request = new Request('get', '/');
|
||||
$sessionManager = new SessionManager(Mockery::mock(ContainerInterface::class), Mockery::mock(ConfigInterface::class));
|
||||
$reflectionClass = new ReflectionClass(SessionManager::class);
|
||||
$parseSessionIdMethod = $reflectionClass->getMethod('parseSessionId');
|
||||
$parseSessionIdMethod->setAccessible(true);
|
||||
$id = Str::random(40);
|
||||
$this->assertSame($id, $parseSessionIdMethod->invoke($sessionManager, $request->withCookieParams([
|
||||
'HYPERF_SESSION_ID' => $id,
|
||||
])));
|
||||
$this->assertSame('123', $parseSessionIdMethod->invoke($sessionManager, $request->withCookieParams([
|
||||
'HYPERF_SESSION_ID' => 123,
|
||||
])));
|
||||
$this->assertNull($parseSessionIdMethod->invoke($sessionManager, $request->withCookieParams([
|
||||
'foo' => 'bar',
|
||||
])));
|
||||
}
|
||||
}
|
112
src/session/tests/SessionMiddlewareTest.php
Normal file
112
src/session/tests/SessionMiddlewareTest.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace HyperfTest\Session;
|
||||
|
||||
use Hyperf\Contract\ConfigInterface;
|
||||
use Hyperf\Contract\SessionInterface;
|
||||
use Hyperf\HttpMessage\Cookie\Cookie;
|
||||
use Hyperf\HttpMessage\Server\Request;
|
||||
use Hyperf\HttpMessage\Server\Response;
|
||||
use Hyperf\Session\Handler\FileHandler;
|
||||
use Hyperf\Session\Middleware\SessionMiddleware;
|
||||
use Hyperf\Session\Session;
|
||||
use Hyperf\Session\SessionManager;
|
||||
use Hyperf\Utils\Context;
|
||||
use Hyperf\Utils\Filesystem\Filesystem;
|
||||
use HyperfTest\Session\Stub\FooHandler;
|
||||
use Mockery;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @covers \Hyperf\Session\Middleware\SessionMiddleware
|
||||
* @covers \Hyperf\Session\SessionManager
|
||||
*/
|
||||
class SessionMiddlewareTest extends TestCase
|
||||
{
|
||||
public function testHandle()
|
||||
{
|
||||
$request = new Request('GET', '/test');
|
||||
$request = $request->withCookieParams(['HYPERF_SESSION_ID' => 'foo123']);
|
||||
$requestHandler = Mockery::mock(RequestHandlerInterface::class);
|
||||
$requestHandler->shouldReceive('handle')->andReturn(new Response());
|
||||
|
||||
$container = Mockery::mock(ContainerInterface::class);
|
||||
$container->shouldReceive('has')->with(SessionInterface::class)->andReturnTrue();
|
||||
$container->shouldReceive('has')->with(FileHandler::class)->andReturnTrue();
|
||||
$fileHandler = new FileHandler(new Filesystem(), '/tmp', 10);
|
||||
$container->shouldReceive('get')->with(FileHandler::class)->andReturn($fileHandler);
|
||||
/** @var Session $session */
|
||||
$session = new Session('HYPERF_SESSION_ID', $fileHandler);
|
||||
$container->shouldReceive('get')->with(SessionInterface::class)->andReturn($session);
|
||||
$container->shouldReceive('get')->with(FooHandler::class)->andReturn(new FooHandler());
|
||||
|
||||
$config = Mockery::mock(ConfigInterface::class);
|
||||
$config->shouldReceive('get')->with('session.handler')->andReturn(FooHandler::class);
|
||||
$config->shouldReceive('has')->with('session.handler')->andReturn(true);
|
||||
$config->shouldReceive('get')->with('session.options.expire_on_close')->andReturn(1);
|
||||
$sessionManager = new SessionManager($container, $config);
|
||||
$middleware = new SessionMiddleware($sessionManager, $config);
|
||||
$response = $middleware->process($request, $requestHandler);
|
||||
|
||||
$this->assertInstanceOf(ResponseInterface::class, $response);
|
||||
$this->assertInstanceOf(SessionInterface::class, $session = Context::get(SessionInterface::class));
|
||||
$this->assertIsString($session->getId());
|
||||
$this->assertTrue(ctype_alnum($session->getId()));
|
||||
$this->assertSame(40, strlen($session->getId()));
|
||||
$this->assertArrayHasKey('', $response->getCookies());
|
||||
$this->assertArrayHasKey('/', $response->getCookies()['']);
|
||||
$this->assertArrayHasKey($session->getName(), $response->getCookies()['']['/']);
|
||||
$this->assertInstanceOf(Cookie::class, $response->getCookies()['']['/'][$session->getName()]);
|
||||
/** @var Cookie $cookie */
|
||||
$cookie = $response->getCookies()['']['/'][$session->getName()];
|
||||
$this->assertSame('HYPERF_SESSION_ID', $cookie->getName());
|
||||
$this->assertSame($session->getId(), $cookie->getValue());
|
||||
$this->assertSame('/', $cookie->getPath());
|
||||
$this->assertSame(0, $cookie->getExpiresTime());
|
||||
}
|
||||
|
||||
public function testSessionWithExpiresTime()
|
||||
{
|
||||
$request = new Request('GET', '/test');
|
||||
$request = $request->withCookieParams(['HYPERF_SESSION_ID' => 'foo123']);
|
||||
$requestHandler = Mockery::mock(RequestHandlerInterface::class);
|
||||
$requestHandler->shouldReceive('handle')->andReturn(new Response());
|
||||
|
||||
$container = Mockery::mock(ContainerInterface::class);
|
||||
$container->shouldReceive('has')->with(SessionInterface::class)->andReturnTrue();
|
||||
$container->shouldReceive('has')->with(FileHandler::class)->andReturnTrue();
|
||||
$fileHandler = new FileHandler(new Filesystem(), '/tmp', 10);
|
||||
$container->shouldReceive('get')->with(FileHandler::class)->andReturn($fileHandler);
|
||||
/** @var Session $session */
|
||||
$session = new Session('HYPERF_SESSION_ID', $fileHandler);
|
||||
$container->shouldReceive('get')->with(SessionInterface::class)->andReturn($session);
|
||||
$container->shouldReceive('get')->with(FooHandler::class)->andReturn(new FooHandler());
|
||||
|
||||
$config = Mockery::mock(ConfigInterface::class);
|
||||
$config->shouldReceive('get')->with('session.handler')->andReturn(FooHandler::class);
|
||||
$config->shouldReceive('has')->with('session.handler')->andReturn(true);
|
||||
$config->shouldReceive('get')->with('session.options.expire_on_close')->andReturn(0);
|
||||
$sessionManager = new SessionManager($container, $config);
|
||||
$middleware = new SessionMiddleware($sessionManager, $config);
|
||||
$time = time();
|
||||
$response = $middleware->process($request, $requestHandler);
|
||||
|
||||
/** @var Cookie $cookie */
|
||||
$cookie = $response->getCookies()['']['/'][$session->getName()];
|
||||
$this->assertSame($time + (5 * 60 * 60), $cookie->getExpiresTime());
|
||||
}
|
||||
}
|
161
src/session/tests/SessionTest.php
Normal file
161
src/session/tests/SessionTest.php
Normal file
@ -0,0 +1,161 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace HyperfTest\Session;
|
||||
|
||||
use Hyperf\Session\Handler\FileHandler;
|
||||
use Hyperf\Session\Handler\NullHandler;
|
||||
use Hyperf\Session\Session;
|
||||
use Hyperf\Utils\Filesystem\Filesystem;
|
||||
use Hyperf\Utils\Str;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @covers \Hyperf\Session\Session
|
||||
*/
|
||||
class SessionTest extends TestCase
|
||||
{
|
||||
public function testSession()
|
||||
{
|
||||
$id = Str::random(40);
|
||||
$session = new Session($name = 'HYPERF_SESSION_ID', new NullHandler(), $id);
|
||||
$this->assertSame($name, $session->getName());
|
||||
$this->assertSame($id, $session->getId());
|
||||
$this->assertTrue($session->isValidId($id));
|
||||
|
||||
$session = new Session('HYPERF_SESSION_ID', new NullHandler());
|
||||
$this->assertTrue($session->isValidId($session->getId()));
|
||||
}
|
||||
|
||||
public function testSessionAttributes()
|
||||
{
|
||||
$id = Str::random(40);
|
||||
$session = new Session('HYPERF_SESSION_ID', new NullHandler(), $id);
|
||||
$data = [
|
||||
'int' => 1,
|
||||
'true' => true,
|
||||
'false' => false,
|
||||
'float' => 1.23,
|
||||
'string' => 'foo',
|
||||
];
|
||||
foreach ($data as $key => $value) {
|
||||
$session->set($key, $value);
|
||||
}
|
||||
foreach ($data as $key => $value) {
|
||||
$this->assertTrue($session->has($key));
|
||||
$this->assertSame($value, $session->get($key));
|
||||
}
|
||||
$this->assertFalse($session->has('not-exist'));
|
||||
$this->assertSame($data, $session->all());
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$this->assertSame($value, $session->remove($key));
|
||||
}
|
||||
|
||||
$session->put($data);
|
||||
$this->assertSame($data, $session->all());
|
||||
|
||||
$session->clear();
|
||||
$this->assertSame([], $session->all());
|
||||
|
||||
$session->put('foo', 'bar');
|
||||
$this->assertSame(['foo' => 'bar'], $session->all());
|
||||
|
||||
$session->put($data);
|
||||
$session->forget('foo');
|
||||
$this->assertSame($data, $session->all());
|
||||
|
||||
$session->replace(['int' => 2, 'foo' => 'baz']);
|
||||
$this->assertSame([
|
||||
'int' => 2,
|
||||
'true' => true,
|
||||
'false' => false,
|
||||
'float' => 1.23,
|
||||
'string' => 'foo',
|
||||
'foo' => 'baz',
|
||||
], $session->all());
|
||||
}
|
||||
|
||||
public function testSessionPush()
|
||||
{
|
||||
$session = new Session('HYPERF_SESSION_ID', new NullHandler());
|
||||
$session->push('foo', 'bar');
|
||||
$this->assertSame([
|
||||
'foo' => ['bar'],
|
||||
], $session->all());
|
||||
$session->push('foo', 'baz');
|
||||
$this->assertSame([
|
||||
'foo' => ['bar', 'baz'],
|
||||
], $session->all());
|
||||
}
|
||||
|
||||
public function testToken()
|
||||
{
|
||||
$session = new Session('HYPERF_SESSION_ID', new NullHandler());
|
||||
$this->assertSame('', $session->token());
|
||||
|
||||
$token = $session->regenerateToken();
|
||||
$this->assertSame($token, $session->token());
|
||||
$this->assertTrue($session->isValidId($token));
|
||||
}
|
||||
|
||||
public function testPreviousUrl()
|
||||
{
|
||||
$session = new Session('HYPERF_SESSION_ID', new NullHandler());
|
||||
$url = 'http://127.0.0.1:9501/foo/bar';
|
||||
$session->setPreviousUrl($url);
|
||||
$this->assertSame($url, $session->previousUrl());
|
||||
|
||||
$session->set('_previous.url', 123);
|
||||
$this->assertNull($session->previousUrl());
|
||||
}
|
||||
|
||||
public function testStartSession()
|
||||
{
|
||||
$fileHandler = new FileHandler(new Filesystem(), '/tmp', 1);
|
||||
$session = new Session('HYPERF_SESSION_ID', $fileHandler);
|
||||
$this->assertTrue($session->start());
|
||||
$this->assertTrue($session->isStarted());
|
||||
|
||||
$id = $session->getId();
|
||||
$session->set('foo', 'bar');
|
||||
$session->save();
|
||||
$this->assertFileExists('/tmp/' . $id);
|
||||
|
||||
$session = new Session('HYPERF_SESSION_ID', $fileHandler, $id);
|
||||
$session->start();
|
||||
$this->assertSame('bar', $session->get('foo'));
|
||||
|
||||
$this->assertTrue($session->invalidate());
|
||||
$this->assertFileNotExists('/tmp/' . $id);
|
||||
}
|
||||
|
||||
public function testFlash()
|
||||
{
|
||||
$fileHandler = new FileHandler(new Filesystem(), '/tmp', 1);
|
||||
$session = new Session('HYPERF_SESSION_ID', $fileHandler);
|
||||
$id = $session->getId();
|
||||
$session->flash('foo', 'bar');
|
||||
$this->assertSame([
|
||||
'foo' => 'bar',
|
||||
'_flash' => [
|
||||
'new' => ['foo'],
|
||||
'old' => [],
|
||||
],
|
||||
], $session->all());
|
||||
$session->save();
|
||||
|
||||
$session = new Session('HYPERF_SESSION_ID', $fileHandler, $id);
|
||||
$this->assertSame([], $session->all());
|
||||
}
|
||||
}
|
119
src/session/tests/Stub/FooHandler.php
Normal file
119
src/session/tests/Stub/FooHandler.php
Normal file
@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace HyperfTest\Session\Stub;
|
||||
|
||||
use SessionHandlerInterface;
|
||||
|
||||
class FooHandler implements SessionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Close the session.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.close.php
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a session.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.destroy.php
|
||||
* @param string $session_id the session ID being destroyed
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function destroy($session_id)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup old sessions.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.gc.php
|
||||
* @param int $maxlifetime <p>
|
||||
* Sessions that have not updated for
|
||||
* the last maxlifetime seconds will be removed.
|
||||
* </p>
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize session.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.open.php
|
||||
* @param string $save_path the path where to store/retrieve the session
|
||||
* @param string $name the session name
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function open($save_path, $name)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Read session data.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.read.php
|
||||
* @param string $session_id the session id to read data for
|
||||
* @return string <p>
|
||||
* Returns an encoded string of the read data.
|
||||
* If nothing was read, it must return an empty string.
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function read($session_id)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Write session data.
|
||||
*
|
||||
* @see https://php.net/manual/en/sessionhandlerinterface.write.php
|
||||
* @param string $session_id the session id
|
||||
* @param string $session_data <p>
|
||||
* The encoded session data. This data is the
|
||||
* result of the PHP internally encoding
|
||||
* the $_SESSION superglobal to a serialized
|
||||
* string and passing it as this parameter.
|
||||
* Please note sessions use an alternative serialization method.
|
||||
* </p>
|
||||
* @return bool <p>
|
||||
* The return value (usually TRUE on success, FALSE on failure).
|
||||
* Note this value is returned internally to PHP for processing.
|
||||
* </p>
|
||||
* @since 5.4.0
|
||||
*/
|
||||
public function write($session_id, $session_data)
|
||||
{
|
||||
}
|
||||
}
|
17
src/session/tests/Stub/NonSessionHandler.php
Normal file
17
src/session/tests/Stub/NonSessionHandler.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://doc.hyperf.io
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
namespace HyperfTest\Session\Stub;
|
||||
|
||||
class NonSessionHandler
|
||||
{
|
||||
}
|
Loading…
Reference in New Issue
Block a user