tobento/service-session

PHP应用程序的会话支持。

1.0.2 2024-02-17 17:32 UTC

This package is auto-updated.

Last update: 2024-09-17 18:43:27 UTC


README

任何PHP应用程序的会话支持。

目录

入门

使用此命令添加运行中的 Session 服务项目的最新版本。

composer require tobento/service-session

需求

  • PHP 8.0 或更高版本

亮点

  • 框架无关,可与任何项目一起使用
  • 解耦设计
  • 原生会话或使用保存处理器
  • 闪存数据
  • 会话 PSR-15 中间件

文档

创建会话

use Tobento\Service\Session\Session;
use Tobento\Service\Session\SessionInterface;
use Tobento\Service\Session\SaveHandlerInterface;
use Tobento\Service\Session\ValidationInterface;

$session = new Session(
    name: 'sess',
    maxlifetime: 1800,
    cookiePath: '/',
    cookieDomain: '',
    cookieSamesite: 'Strict',
    secure: true,
    httpOnly: true,
    saveHandler: null, // null|SaveHandlerInterface
    validation: null, // null|ValidationInterface
);

var_dump($session instanceof SessionInterface);
// bool(true)

如果 saveHandler 参数为 null,则使用原生会话。

会话工厂

您可以使用会话工厂来创建会话。

use Tobento\Service\Session\SessionFactory;
use Tobento\Service\Session\SessionFactoryInterface;
use Tobento\Service\Session\SessionInterface;
use Tobento\Service\Session\SaveHandlerInterface;
use Tobento\Service\Session\ValidationInterface;

$sessionFactory = new SessionFactory();

$session = $sessionFactory->createSession('name', [
    'maxlifetime' => 1800,
    'cookiePath' => '/',
    'cookieDomain' => '',
    'cookieSamesite' => 'Strict',
    'secure' => true,
    'httpOnly' => true,
    'saveHandler' => null, // null|SaveHandlerInterface
    'validation' => null, // null|ValidationInterface
]);

var_dump($session instanceof SessionInterface);
// bool(true)

保存处理器

PDO MySql 保存处理器

您可以使用 PDO mysql 保存处理器将会话数据存储在数据库中。

数据库表

CREATE TABLE `session` (
    `id` varchar(128),
    `data` text,
    `expiry` int(14) UNSIGNED,
     PRIMARY KEY (`id`)
);

创建保存处理器

use Tobento\Service\Session\PdoMySqlSaveHandler;
use Tobento\Service\Session\SaveHandlerInterface;
use PDO;

$pdo = new PDO(
    dsn: 'mysql:host=localhost;dbname=db_name',
    username: 'root',
    password: '',
    options: [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_EMULATE_PREPARES => false,
    ],
);

$saveHandler = new PdoMySqlSaveHandler(
    table: 'session',
    pdo: $pdo
);

var_dump($saveHandler instanceof SaveHandlerInterface);
// bool(true)

空保存处理器

您可以使用空保存处理器进行测试。

use Tobento\Service\Session\NullSaveHandler;
use Tobento\Service\Session\SaveHandlerInterface;

$saveHandler = new NullSaveHandler();

var_dump($saveHandler instanceof SaveHandlerInterface);
// bool(true)

验证

远程地址验证

use Tobento\Service\Session\RemoteAddrValidation;
use Tobento\Service\Session\ValidationInterface;

$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? null;

// PSR-7
// $remoteAddr = $serverRequest->getServerParams()['REMOTE_ADDR'] ?? null;

$validation = new RemoteAddrValidation(
    remoteAddr: $remoteAddr,
    trustedProxies: ['192.168.1.1'], // or null
    remoteAddrKey: '_session_remoteAddr', // is default
);

var_dump($validation instanceof ValidationInterface);
// bool(true)

var_dump($validation->remoteAddr());
// string(9) "127.0.0.1"

var_dump($validation->trustedProxies());
// array(1) { [0]=> string(11) "192.168.1.1" }

Http 用户代理验证

use Tobento\Service\Session\HttpUserAgentValidation;
use Tobento\Service\Session\ValidationInterface;

$validation = new HttpUserAgentValidation(
    httpUserAgent: $_SERVER['HTTP_USER_AGENT'] ?? null,
    httpUserAgentKey: '_session_httpUserAgent' // default
);

var_dump($validation instanceof ValidationInterface);
// bool(true)

var_dump($validation->httpUserAgent());
// string(78) "Mozilla/5.0 ..."

堆栈验证

您可以使用 Validations::class 来处理多个验证。

use Tobento\Service\Session\Validations;
use Tobento\Service\Session\RemoteAddrValidation;
use Tobento\Service\Session\HttpUserAgentValidation;
use Tobento\Service\Session\ValidationInterface;

$validation = new Validations(
    new RemoteAddrValidation($_SERVER['REMOTE_ADDR'] ?? null),
    new HttpUserAgentValidation($_SERVER['HTTP_USER_AGENT'] ?? null),
);

var_dump($validation instanceof ValidationInterface);
// bool(true)

$validations = $validation->validations();
// array<int, ValidationInterface>

自定义验证

您可以通过实现 ValidationInterface 来编写自己的验证。

use Tobento\Service\Session\ValidationInterface;
use Tobento\Service\Session\SessionInterface;
use Tobento\Service\Session\SessionValidationException;

/**
 * ValidationInterface
 */
interface ValidationInterface
{
    /**
     * Process the validation.
     *
     * @param SessionInterface $session
     * @return void
     * @throws SessionValidationException
     */
    public function process(SessionInterface $session): void;
}

开始会话

在存储或从会话检索数据之前,您需要启动会话。您可以使用会话中间件 PSR-15 来完成此操作。

use Tobento\Service\Session\SessionStartException;
use Tobento\Service\Session\SessionExpiredException;
use Tobento\Service\Session\SessionValidationException;

try {
    $session->start();
} catch (SessionStartException $e) {
    // handle
} catch (SessionExpiredException $e) {
    $session->destroy();
    
    // You might to restart session and regenerate id
    // on the current request.
    $session->start();
    $session->regenerateId();
    
    // Or you might send a message to the user instead.
    
} catch (SessionValidationException $e) {
    // handle
}

与数据交互

set

将数据存储在会话中。

$session->set('key', 'value');

// using dot notation:
$session->set('meta.color', 'color');

get

从会话中获取数据。

$value = $session->get('key');

// using dot notation:
$value = $session->get('meta.color');

// using a default value if key does not exist
$value = $session->get('key', 'default');

has

确定一个项目是否存在于会话中,返回一个布尔值。

$has = $session->has('key');

// using dot notation:
$has = $session->has('meta.color');

// using multiple keys.
$has = $session->has('key', 'meta.color');

$has = $session->has(['key', 'meta.color']);

delete

从会话中删除数据。

$session->delete('key');

// using dot notation:
$session->delete('meta.color');

deleteAll

从会话中删除所有数据。

$session->deleteAll();

all

从会话中获取所有数据。

$data = $session->all();

与 Flash 数据交互

仅当在每次请求结束时调用保存会话方法时才有效。

flash

将数据存储在会话中,用于当前和下一个请求。

$session->flash('key', 'value');

// using dot notation:
$session->flash('message.success', 'Message');

now

将数据存储在会话中,用于当前请求。

$session->now('key', 'value');

// using dot notation:
$session->now('message.success', 'Message');

once

将数据存储在会话中。在首次检索数据后,闪存数据将被删除。

$session->once('key', 'value');

// using dot notation:
$session->once('message.success', 'Message');

杂项

regenerateId

重新生成新的会话 ID。在更改重要用户状态(如登录和注销)时使用。

$session->regenerateId();

destroy

完全销毁会话,但不会重新生成其 ID。

$session->destroy();

name

获取会话名称。

$name = $session->name();

id

获取会话 ID。

$id = $session->id();

保存会话

保存和闪存会话数据。您可以使用会话中间件 PSR-15 来完成此操作。

use Tobento\Service\Session\SessionSaveException;

try {
    $session->save();
} catch (SessionSaveException $e) {
    // handle
}

会话中间件 PSR-15

您可以使用会话中间件,它将启动会话,将会话添加为请求属性,并最终调用保存方法来保存会话数据。
请注意,会话中间件仅处理 SessionExpiredException。
您可以通过应用程序的错误处理中间件或编写自己的适合应用程序的会话中间件来处理 SessionStartException 和 SessionValidationException。

use Tobento\Service\Middleware\MiddlewareDispatcher;
use Tobento\Service\Middleware\AutowiringMiddlewareFactory;
use Tobento\Service\Middleware\FallbackHandler;
use Tobento\Service\Container\Container;
use Tobento\Service\Session\Session;
use Tobento\Service\Session\SessionInterface;
use Tobento\Service\Session\Middleware\Session as SessionMiddleware;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Nyholm\Psr7\Factory\Psr17Factory;

// create middleware dispatcher.
$dispatcher = new MiddlewareDispatcher(
    new FallbackHandler((new Psr17Factory())->createResponse(404)),
    new AutowiringMiddlewareFactory(new Container()) // any PSR-11 container
);

$session = new Session(
    name: 'sess',
    maxlifetime: 1800,
    cookiePath: '/',
    cookieDomain: '',
    cookieSamesite: 'Strict',
    secure: true,
    httpOnly: true,
    saveHandler: null,
    validation: null,
);

$dispatcher->add(function(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
    
    $session = $request->getAttribute(SessionInterface::class);
    
    var_dump($session instanceof SessionInterface);
    // bool(false)
    
    return $handler->handle($request);
});

$dispatcher->add(new SessionMiddleware($session));

$dispatcher->add(function(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
    
    $session = $request->getAttribute(SessionInterface::class);
    
    var_dump($session instanceof SessionInterface);
    // bool(true)
    
    return $handler->handle($request);
});

$request = (new Psr17Factory())->createServerRequest('GET', 'https://example.com');

$response = $dispatcher->handle($request);

致谢