lezhnev74/psr-recording-middleware 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

lezhnev74/psr-recording-middleware

Composer 安装命令:

composer require --dev lezhnev74/psr-recording-middleware

包简介

A recording middleware for any PSR-7 HTTP client that records every request/response exchange and can replay them, turning recorded traffic into a predictable fixture for integration tests. Built on PSR-7, PSR-17 and PSR-3.

README 文档

README

tests codecov PHPStan

A recording middleware for any PSR-7 HTTP client that records every request and response passing through it, and can switch to replay mode to serve the recorded responses back - turning real HTTP traffic into a predictable fixture for integration tests.

It is built on PSR-7 (messages), PSR-17 (message factories) and PSR-3 (logging), so it stays implementation-agnostic: it works with any PSR-7 implementation, none of which is a hard dependency. Guzzle is the primary test target, not a runtime dependency.

Requirements

  • PHP 8.1 - 8.5
  • Any PSR-7 / PSR-17 implementation installed in your app (e.g. guzzlehttp/psr7 or nyholm/psr7) - discovered automatically via php-http/discovery.

Installation

composer require lezhnev74/psr-recording-middleware

Usage

Configure a run, open a named session, and attach it to your client - via the Guzzle-style handler middleware or the PSR-18 decorator:

use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Promise\Create;
use Lezhnev74\PsrRecordingMiddleware\{HandlerMiddleware, Mode, RecordingClient, RecordingRun, RunConfig};

// The run's default mode seeds every session it opens.
$run = RecordingRun::fromConfig(RunConfig::create('/path/to/store', Mode::Replay));
$session = $run->openSession('example.com');       // inherits Replay

// Guzzle handler stack:
$stack = HandlerStack::create();
$stack->push(HandlerMiddleware::for($session, Create::promiseFor(...)));
$client = new Client(['handler' => $stack]);

// ...or wrap any PSR-18 client:
$client = new RecordingClient($psr18Client, $session);

// Replay (the default): requests are served from the store, network untouched.
// Requests are matched byte-for-byte, in recorded order; a mismatch throws.
$client->get('https://example.com/');

// Mode lives on the session, so one session can be flipped without touching
// the run's default or any sibling session - either seed it when opening:
$recording = $run->openSession('example.com', Mode::Record);
// ...or flip an already-open session (chains off openSession):
$run->openSession('example.com')->setMode(Mode::Record);
// A Record session hits the network and saves every turn to the store.

Full working examples live in the tests: tests/KnownHostRecordReplayTest.php (end-to-end record/replay with a real Guzzle client), tests/HandlerMiddlewareTest.php and tests/RecordingClientTest.php.

PHPUnit integration

Drive the mode from an env var, keep the fixture store in a committed directory, and give each test its own session. Tests then need no record/replay branching - run once with HTTP_RECORD=1 against the real network, commit tests/fixtures/http/, and every ordinary run replays offline. In your base TestCase:

protected function httpSession(): Session
{
    $run = RecordingRun::fromConfig(RunConfig::create(
        __DIR__ . '/fixtures/http',
        getenv('HTTP_RECORD') !== false ? Mode::Record : Mode::Replay,
        $this->masker(), // see below
    ));

    // One fixture dir per test; session names may not contain "\".
    return $run->openSession(str_replace('\\', '.', static::class) . '.' . $this->name());
}

Attach the session to the client your code under test uses (handler stack or RecordingClient, as above). For integration tests, do it at the DI seam, e.g. in Laravel: $this->app->extend(ClientInterface::class, fn ($inner) => new RecordingClient($inner, $this->httpSession())).

To re-prove a single test still works against the live network without re-recording the whole suite, flip only that test's session: $this->httpSession()->setMode(Mode::Record).

Masking volatile fields

Replay matches requests byte-for-byte, so any field that changes between runs (rotating tokens, a default User-Agent embedding PHP/Guzzle versions) breaks committed fixtures - and secrets must not land in git. Mask them: masking applies identically when recording and when matching, so a masked field is stored as ***, never mismatches, and never leaks.

private function masker(): Masker
{
    return RecordingMasker::create(MaskingConfig::create(
        ['Authorization', 'User-Agent'], // headers
        ['token'],                       // query args
        ['password'],                    // JSON/form body fields
    ));
}

The full runnable pattern - env-driven mode, session per test, masking proven against changed tokens and User-Agent - is tests/PhpUnitIntegrationExampleTest.php.

Local development

All QA tooling runs inside a PHP 8.5 Docker container (with Xdebug) via the dev wrapper script - no PHP is needed on the host. Each command tunnels to the matching Composer script inside the container, so ./dev <script> is just composer <script> run in Docker.

cp .env.dist .env  # set DOCKER_USER_ID / DOCKER_GROUP_ID (see `id -u` / `id -g`)
./dev install      # composer install
./dev test         # run the test suite
./dev check        # code style + static analysis + tests
./dev cs-fix       # auto-fix code style
./dev debug-test   # run tests under Xdebug (step-debugging)

Anything unrecognized is passed to composer, so ./dev require <pkg>, ./dev stan, ./dev rector etc. all work. Use ./dev php ... for a raw PHP call, ./dev sh for a shell, and ./dev compose ... for raw docker compose.

Contributing

A few conventions, kept light:

  • TDD. Every new branch needs a covering test; the recording/replay suite must stay green against both Guzzle and Nyholm PSR-7 impls.
  • Conventional Commits (spec) for messages, e.g. feat(recording): ..., fix: ..., test: ....
  • Semantic versioning. Tags are vMAJOR.MINOR.PATCH; the commit type drives the bump (fix → patch, feat → minor, a !/BREAKING CHANGE → major).
  • Run ./dev check (style + static analysis + tests) before opening a PR.

License

MIT - see LICENSE.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 3
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-08

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固