A robust, layered HTTP Client wrapper designed for extensibility, strict typing, and high performance. Built with a "Clean Architecture" approach, decoupling the business logic from the underlying Guzzle transport.
- Strictly Typed: Configuration object (
ClientConfig) ensures type safety before requests start. - Layered Architecture: Adapters (Guzzle) are isolated from Core Logic.
- Resilience: Built-in Retry (Backoff/Jitter) and Circuit Breaker.
- Async & Concurrency: Support for non-blocking requests (
getAsync) and Batch Processing. - Observability: detailed Logging and PSR-16 Caching integration.
- Performance: < 10μs overhead per request.
composer require jooservices/clientUse the ClientBuilder to create an instance.
use JOOservices\Client\Client\ClientBuilder;
$client = ClientBuilder::create()
->withBaseUri('https://api.example.com')
->withTimeout(5)
->withHeader('Authorization', 'Bearer token')
->build();
$response = $client->get('/users/1');
echo $response->status(); // 200
print_r($response->json()); // ['id' => 1, ...]// Single Async Request
$promise = $client->getAsync('/users/1');
$response = $promise->wait();
// Batch Processing (Concurrent)
$results = $client->batch([
'user1' => fn() => $client->getAsync('/users/1'),
'user2' => fn() => $client->getAsync('/users/2'),
]);
print_r($results['user1']->json());use JOOservices\Client\Resilience\RetryConfig;
use JOOservices\Client\Resilience\CircuitBreakerConfig;
$client = ClientBuilder::create()
->withRetry(new RetryConfig(
maxAttempts: 3,
baseDelayMs: 100
))
->withCircuitBreaker(new CircuitBreakerConfig(
failureThreshold: 5,
recoveryTimeoutMs: 10000
))
->build();use JOOservices\Client\Logging\MonologFactory;
use JOOservices\Client\Cache\FilesystemCache;
$logger = MonologFactory::createDaily('my-app', __DIR__ . '/logs');
$cache = new FilesystemCache(__DIR__ . '/cache');
$client = ClientBuilder::create()
->withLogger($logger, logBodies: true)
->withCache($cache, defaultTtl: 3600)
->build();The repository uses the DTO-style quality contract with a few client-specific additions.
composer lint:all
composer testAdditional validation commands:
composer lint:fixcomposer test:coveragevendor/bin/phpbench run --report=default
Intentional client-specific differences from the DTO baseline:
- 98% coverage gate on
composer test:coverage - dedicated benchmark workflow with PHPBench
- optional live-network workflow for real external IP logging checks
- active CI secret scanning via
secret-scanning.yml
Repository-standard auxiliary automation now also matches DTO more closely:
- semantic PR titles require an uppercase subject
- pull requests are auto-labeled with DTO-style label categories
- releases validate tags before publishing GitHub releases and can notify Packagist when credentials are configured
Coverage remains an intentional client-specific divergence: this repo keeps a 98% gate and a narrower excluded-source set so the enforced threshold stays meaningful for the exercised client runtime surface.
This package includes AI-oriented scaffolding to keep delivery consistent with quality gates.
- Agent guidance: AGENTS.md, CLAUDE.md
- Tooling folders:
.claude/commands,.cursor/rules,ai/skills,antigravity/prompts,jetbrains/prompts - Development process references: docs/04-development
When AI changes code, run:
composer lint:all
composer testIf PHP is not installed locally, run everything in Docker.
docker compose up -d --build mongodb
docker compose run --rm php composer install
docker compose run --rm php composer testFor live network integration tests (real sites), run:
docker compose run --rm -e JOOCLIENT_RUN_LIVE_NETWORK_TESTS=1 php \
vendor/bin/phpunit tests/Feature/Logging/RealSiteIpLoggingTest.phpThis test hits:
https://httpbin.org/gethttps://example.comhttps://google.com
See CONTRIBUTING.md for details.