Skip to content

其他 PSR 规范概览

除了 PSR-1、PSR-4 和 PSR-12 之外,PHP-FIG 还制定了多个重要规范,覆盖日志记录、依赖注入容器、HTTP 消息、中间件等常见场景。这些规范使得不同框架和库之间的互操作性成为可能。本节将系统介绍每个重要 PSR 规范的核心内容、接口定义和实际应用。

前置知识

阅读本节前,建议先了解:

基础概念

PSR 规范分类总览

类别PSR状态说明
编码标准PSR-1已接受基础编码标准
编码标准PSR-12已接受扩展编码样式
自动加载PSR-4已接受自动加载标准
日志PSR-3已接受日志接口
HTTPPSR-7已接受HTTP 消息接口
HTTPPSR-17已接受HTTP 工厂接口
HTTPPSR-18已接受HTTP 客户端接口
容器PSR-11已接受容器接口
事件PSR-14已接受事件分发器
缓存PSR-6已接受缓存接口
链接PSR-13已接受链接定义
中间件PSR-15已接受HTTP 中间件
简易缓存PSR-16已接受简易缓存接口

PSR-3:日志接口

核心接口

php
<?php
namespace Psr\Log;

interface LoggerInterface
{
    public function emergency(string|\Stringable $message, array $context = []): void;
    public function alert(string|\Stringable $message, array $context = []): void;
    public function critical(string|\Stringable $message, array $context = []): void;
    public function error(string|\Stringable $message, array $context = []): void;
    public function warning(string|\Stringable $message, array $context = []): void;
    public function notice(string|\Stringable $message, array $context = []): void;
    public function info(string|\Stringable $message, array $context = []): void;
    public function debug(string|\Stringable $message, array $context = []): void;

    public function log(mixed $level, string|\Stringable $message, array $context = []): void;
}

日志级别

级别数值用途
DEBUG100详细的调试信息
INFO200有意义的事件
NOTICE250正常但值得注意的事件
WARNING300非致命异常情况
ERROR400运行时错误
CRITICAL500需要立即处理的错误
ALERT550必须立即采取行动
EMERGENCY600系统不可用

实际实现

php
<?php
declare(strict_types=1);

use Psr\Log\LoggerInterface;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// Monolog 实现了 PSR-3
$logger = new Logger('app');
$logger->pushHandler(new StreamHandler('logs/app.log', Logger::DEBUG));

$logger->info('用户登录', ['user_id' => 123, 'ip' => '192.168.1.1']);
$logger->error('支付失败', ['order_id' => 456, 'error' => '余额不足']);
$logger->warning('API 请求超时', ['url' => '/api/users', 'timeout' => 30]);

PSR-6:缓存接口

核心接口

php
<?php
namespace Psr\Cache;

interface CacheItemPoolInterface
{
    public function getItem(string $key): CacheItemInterface;
    public function getItems(array $keys = []): iterable;
    public function hasItem(string $key): bool;
    public function clear(): bool;
    public function deleteItem(string $key): bool;
    public function deleteItems(array $keys): bool;
    public function save(CacheItemInterface $item): bool;
    public function saveDeferred(CacheItemInterface $item): bool;
    public function commit(): bool;
}

interface CacheItemInterface
{
    public function getKey(): string;
    public function get(): mixed;
    public function isHit(): bool;
    public function set(mixed $value): static;
    public function expiresAt(?\DateTimeInterface $expiration): static;
    public function expiresAfter(int|\DateInterval|null $time): static;
}

使用示例

php
<?php
declare(strict_types=1);

use Psr\Cache\CacheItemPoolInterface;

class UserService
{
    public function __construct(
        private readonly CacheItemPoolInterface $cache
    ) {}

    public function getUserById(int $id): array
    {
        $cacheKey = "user:{$id}";
        $cacheItem = $this->cache->getItem($cacheKey);

        if ($cacheItem->isHit()) {
            return $cacheItem->get();
        }

        $user = $this->fetchFromDatabase($id);
        $cacheItem->set($user)->expiresAfter(3600);
        $this->cache->save($cacheItem);

        return $user;
    }
}

PSR-7:HTTP 消息接口

核心接口

php
<?php
namespace Psr\Http\Message;

interface RequestInterface extends MessageInterface
{
    public function getRequestTarget(): string;
    public function withRequestTarget(string $requestTarget): self;
    public function getMethod(): string;
    public function withMethod(string $method): self;
    public function getUri(): UriInterface;
    public function withUri(UriInterface $uri, bool $preserveHost = false): self;
}

interface ResponseInterface extends MessageInterface
{
    public function getStatusCode(): int;
    public function withStatus(int $code, string $reasonPhrase = ''): self;
    public function getReasonPhrase(): string;
}

interface MessageInterface
{
    public function getProtocolVersion(): string;
    public function withProtocolVersion(string $version): self;
    public function getHeaders(): array;
    public function hasHeader(string $name): bool;
    public function getHeader(string $name): array;
    public function getHeaderLine(string $name): string;
    public function withHeader(string $name, string|string[] $value): self;
    public function withAddedHeader(string $name, string|string[] $value): self;
    public function withoutHeader(string $name): self;
    public function getBody(): StreamInterface;
    public function withBody(StreamInterface $body): self;
}

使用示例

php
<?php
declare(strict_types=1);

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Nyholm\Psr7\Factory\Psr17Factory;

$psrFactory = new Psr17Factory();

// 创建请求
$request = $psrFactory->createRequest('GET', 'https://api.example.com/users')
    ->withHeader('Accept', 'application/json')
    ->withHeader('Authorization', 'Bearer token123');

// 创建响应
$response = $psrFactory->createResponse(200)
    ->withHeader('Content-Type', 'application/json')
    ->withBody($psrFactory->createStream(json_encode(['status' => 'ok'])));

PSR-11:容器接口

核心接口

php
<?php
namespace Psr\Container;

interface ContainerInterface
{
    public function get(string $id): object;
    public function has(string $id): bool;
}

interface ContainerExceptionInterface
{
}

interface NotFoundExceptionInterface extends ContainerExceptionInterface
{
}

使用示例

php
<?php
declare(strict_types=1);

use Psr\Container\ContainerInterface;

class Application
{
    public function __construct(
        private readonly ContainerInterface $container
    ) {}

    public function handle(string $serviceId): mixed
    {
        if (!$this->container->has($serviceId)) {
            throw new \RuntimeException("服务 {$serviceId} 未注册");
        }

        return $this->container->get($serviceId);
    }
}

PSR-14:事件分发器

核心接口

php
<?php
namespace Psr\EventDispatcher;

interface EventDispatcherInterface
{
    public function dispatch(object $event): object;
}

interface ListenerProviderInterface
{
    public function getListenersForEvent(object $event): iterable;
}

interface StoppableEventInterface
{
    public function isPropagationStopped(): bool;
    public function stopPropagation(): void;
}

PSR-15:HTTP 中间件

核心接口

php
<?php
namespace Psr\Http\Server;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

interface MiddlewareInterface
{
    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler
    ): ResponseInterface;
}

interface RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request): ResponseInterface;
}

中间件链示例

php
<?php
declare(strict_types=1);

namespace App\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class CorsMiddleware implements MiddlewareInterface
{
    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler
    ): ResponseInterface {
        $response = $handler->handle($request);
        return $response
            ->withHeader('Access-Control-Allow-Origin', '*')
            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
            ->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    }
}

class AuthMiddleware implements MiddlewareInterface
{
    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler
    ): ResponseInterface {
        $token = $request->getHeaderLine('Authorization');

        if (!$token || !$this->validateToken($token)) {
            return new \Nyholm\Psr7\Response(401, [], 'Unauthorized');
        }

        return $handler->handle($request);
    }

    private function validateToken(string $token): bool
    {
        return str_starts_with($token, 'Bearer ');
    }
}

PSR-16:简易缓存接口

php
<?php
namespace Psr\SimpleCache;

interface CacheInterface
{
    public function get(string $key, mixed $default = null): mixed;
    public function set(string $key, mixed $value, null|int|\DateInterval $ttl = null): bool;
    public function delete(string $key): bool;
    public function clear(): bool;
    public function getMultiple(iterable $keys, mixed $default = null): iterable;
    public function setMultiple(iterable $values, null|int|\DateInterval $ttl = null): bool;
    public function deleteMultiple(iterable $keys): bool;
    public function has(string $key): bool;
}

PSR-17:HTTP 工厂接口

php
<?php
namespace Psr\Http\Message;

interface RequestFactoryInterface
{
    public function createRequest(string $method, $uri): RequestInterface;
}

interface ResponseFactoryInterface
{
    public function createResponse(int $code = 200, string $reasonPhrase = ''): ResponseInterface;
}

interface StreamFactoryInterface
{
    public function createStream(string $content = ''): StreamInterface;
    public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface;
    public function createStreamFromResource(mixed $resource): StreamInterface;
}

interface UriFactoryInterface
{
    public function createUri(string $uri = ''): UriInterface;
}

interface UploadedFileFactoryInterface
{
    public function createUploadedFile(
        StreamInterface $stream,
        ?int $size = null,
        int $error = \UPLOAD_ERR_OK,
        ?string $clientFilename = null,
        ?string $clientMediaType = null
    ): UploadedFileInterface;
}

interface ServerRequestFactoryInterface
{
    public function createServerRequest(
        string $method,
        $uri,
        array $serverParams = []
    ): ServerRequestInterface;
}

PSR-18:HTTP 客户端接口

php
<?php
namespace Psr\Http\Client;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

interface ClientInterface
{
    public function sendRequest(RequestInterface $request): ResponseInterface;
}

interface NetworkExceptionInterface extends \RuntimeException
{
    public function getRequest(): RequestInterface;
}

interface RequestExceptionInterface extends \RuntimeException
{
    public function getRequest(): RequestInterface;
    public function getResponse(): ?ResponseInterface;
}

实战示例

场景一:使用 PSR 规范构建服务

php
<?php
declare(strict_types=1);

namespace App\Services;

use Psr\Log\LoggerInterface;
use Psr\SimpleCache\CacheInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\ResponseFactoryInterface;

class WeatherService
{
    public function __construct(
        private readonly ClientInterface $httpClient,
        private readonly RequestFactoryInterface $requestFactory,
        private readonly ResponseFactoryInterface $responseFactory,
        private readonly CacheInterface $cache,
        private readonly LoggerInterface $logger
    ) {}

    public function getWeather(string $city): array
    {
        $cacheKey = "weather:{$city}";
        $cached = $this->cache->get($cacheKey);

        if ($cached !== null) {
            return $cached;
        }

        $request = $this->requestFactory
            ->createRequest('GET', "https://api.weather.com/v1/current?city={$city}")
            ->withHeader('Accept', 'application/json');

        try {
            $response = $this->httpClient->sendRequest($request);
            $data = json_decode((string) $response->getBody(), true);

            $this->cache->set($cacheKey, $data, 1800);
            $this->logger->info("Weather fetched", ['city' => $city]);

            return $data;
        } catch (\Throwable $e) {
            $this->logger->error("Weather API error", ['city' => $city, 'error' => $e->getMessage()]);
            throw $e;
        }
    }
}

最佳实践

1. 面向接口编程

php
<?php
// ✅ 推荐:依赖 PSR 接口
use Psr\Log\LoggerInterface;
use Psr\Http\Client\ClientInterface;

class MyService
{
    public function __construct(
        private readonly LoggerInterface $logger,
        private readonly ClientInterface $httpClient
    ) {}
}

// ❌ 不推荐:依赖具体实现
use Monolog\Logger;
use GuzzleHttp\Client;

class MyService
{
    public function __construct(
        private readonly Logger $logger,
        private readonly Client $httpClient
    ) {}
}

2. 常用 PSR 实现库

PSR常用实现
PSR-3Monolog
PSR-6Symfony Cache、Cache
PSR-7Nyholm/PSR-7、Guzzle PSR-7
PSR-11PHP-DI、Symfony DI、Laravel Container
PSR-14Symfony EventDispatcher
PSR-15Laminas Stratigility、Relay
PSR-16Symfony Cache、Filecache
PSR-17Nyholm/PSR-17、Guzzle PSR-17
PSR-18Guzzle、Symfony HTTP Client

下一节

继续学习:Laravel 框架 — 了解 PHP 最流行的全栈框架。

参考链接