本文へ移動
cccskills
無料GitHub で公開

acc-create-psr18-http-client

Generates PSR-18 HTTP Client implementation for PHP 8.5. Creates ClientInterface with request sending and exception handling. Includes unit tests.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md7.0 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

PSR-18 HTTP Client Generator

Overview

Generates PSR-18 compliant HTTP client implementations for external API communication.

When to Use

  • External API integrations
  • Microservice communication
  • HTTP-based service calls
  • Building HTTP client wrappers

Template: HTTP Client

<?php

declare(strict_types=1);

namespace App\Infrastructure\Http\Client;

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

final readonly class CurlHttpClient implements ClientInterface
{
    public function __construct(
        private array $options = [],
    ) {
    }

    public function sendRequest(RequestInterface $request): ResponseInterface
    {
        $ch = curl_init();

        try {
            $this->configureCurl($ch, $request);
            $response = curl_exec($ch);

            if ($response === false) {
                throw new NetworkException(
                    $request,
                    curl_error($ch),
                    curl_errno($ch),
                );
            }

            $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
            $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

            $headerString = substr($response, 0, $headerSize);
            $body = substr($response, $headerSize);

            return $this->buildResponse($statusCode, $headerString, $body);
        } finally {
            curl_close($ch);
        }
    }

    private function configureCurl($ch, RequestInterface $request): void
    {
        $options = [
            CURLOPT_URL => (string) $request->getUri(),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_MAXREDIRS => 5,
            CURLOPT_TIMEOUT => $this->options['timeout'] ?? 30,
            CURLOPT_CONNECTTIMEOUT => $this->options['connect_timeout'] ?? 10,
            CURLOPT_CUSTOMREQUEST => $request->getMethod(),
        ];

        // Set headers
        $headers = [];
        foreach ($request->getHeaders() as $name => $values) {
            $headers[] = $name . ': ' . implode(', ', $values);
        }
        $options[CURLOPT_HTTPHEADER] = $headers;

        // Set body
        $body = (string) $request->getBody();
        if ($body !== '') {
            $options[CURLOPT_POSTFIELDS] = $body;
        }

        // SSL options
        if (isset($this->options['verify_ssl']) && !$this->options['verify_ssl']) {
            $options[CURLOPT_SSL_VERIFYPEER] = false;
            $options[CURLOPT_SSL_VERIFYHOST] = 0;
        }

        curl_setopt_array($ch, $options);
    }

    private function buildResponse(int $statusCode, string $headerString, string $body): ResponseInterface
    {
        $headers = $this->parseHeaders($headerString);

        return (new Response($statusCode))
            ->withBody(new Stream($body))
            ->withHeaders($headers);
    }

    private function parseHeaders(string $headerString): array
    {
        $headers = [];
        $lines = explode("\r\n", trim($headerString));

        foreach ($lines as $line) {
            if (str_contains($line, ':')) {
                [$name, $value] = explode(':', $line, 2);
                $headers[trim($name)] = [trim($value)];
            }
        }

        return $headers;
    }
}

Template: Exceptions

<?php

declare(strict_types=1);

namespace App\Infrastructure\Http\Client;

use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\NetworkExceptionInterface;
use Psr\Http\Client\RequestExceptionInterface;
use Psr\Http\Message\RequestInterface;

final class ClientException extends \RuntimeException implements ClientExceptionInterface
{
}

final class NetworkException extends \RuntimeException implements NetworkExceptionInterface
{
    public function __construct(
        private readonly RequestInterface $request,
        string $message = '',
        int $code = 0,
        ?\Throwable $previous = null,
    ) {
        parent::__construct($message, $code, $previous);
    }

    public function getRequest(): RequestInterface
    {
        return $this->request;
    }
}

final class RequestException extends \RuntimeException implements RequestExceptionInterface
{
    public function __construct(
        private readonly RequestInterface $request,
        string $message = '',
        int $code = 0,
        ?\Throwable $previous = null,
    ) {
        parent::__construct($message, $code, $previous);
    }

    public function getRequest(): RequestInterface
    {
        return $this->request;
    }
}

Template: Logging Client Decorator

<?php

declare(strict_types=1);

namespace App\Infrastructure\Http\Client;

use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;

final readonly class LoggingHttpClient implements ClientInterface
{
    public function __construct(
        private ClientInterface $client,
        private LoggerInterface $logger,
    ) {
    }

    public function sendRequest(RequestInterface $request): ResponseInterface
    {
        $context = [
            'method' => $request->getMethod(),
            'uri' => (string) $request->getUri(),
        ];

        $this->logger->info('HTTP request starting', $context);

        $startTime = microtime(true);

        try {
            $response = $this->client->sendRequest($request);
            $duration = microtime(true) - $startTime;

            $this->logger->info('HTTP request completed', [
                ...$context,
                'status' => $response->getStatusCode(),
                'duration_ms' => round($duration * 1000, 2),
            ]);

            return $response;
        } catch (\Throwable $e) {
            $this->logger->error('HTTP request failed', [
                ...$context,
                'error' => $e->getMessage(),
            ]);

            throw $e;
        }
    }
}

Usage Example

<?php

use App\Infrastructure\Http\Client\CurlHttpClient;
use App\Infrastructure\Http\Client\LoggingHttpClient;
use App\Infrastructure\Http\Factory\HttpFactory;

$factory = new HttpFactory();
$client = new LoggingHttpClient(
    new CurlHttpClient(['timeout' => 30]),
    $logger,
);

// Send GET request
$request = $factory->createRequest('GET', 'https://api.example.com/users')
    ->withHeader('Authorization', 'Bearer token');

$response = $client->sendRequest($request);
$data = json_decode((string) $response->getBody(), true);

// Send POST request
$request = $factory->createRequest('POST', 'https://api.example.com/users')
    ->withHeader('Content-Type', 'application/json')
    ->withBody($factory->createStream(json_encode(['name' => 'John'])));

$response = $client->sendRequest($request);

Requirements

{
    "require": {
        "psr/http-client": "^1.0",
        "psr/http-message": "^2.0"
    }
}

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Master backend architectures, REST API design, microservices, and server-side development across multiple languages. Use this as a central index to access specialized sub-skills.

日本語の概要は準備中です。原文の説明を表示しています。

MikeCheng1208/BattleTree22026年7月22日 更新

在每次回复中使用固定前缀并列出本次实际参考的指令/文件,便于校验是否遵循上下文与规则。

日本語の概要は準備中です。原文の説明を表示しています。

MikeCheng1208/BattleTree22026年7月22日 更新

Automate 21risk tasks via Rube MCP (Composio). Always search tools first for current schemas.

日本語の概要は準備中です。原文の説明を表示しています。

MikeCheng1208/BattleTree22026年7月22日 更新

Automate 2chat tasks via Rube MCP (Composio). Always search tools first for current schemas.

日本語の概要は準備中です。原文の説明を表示しています。

MikeCheng1208/BattleTree22026年7月22日 更新

Arquitecto de Soluciones Principal y Consultor Tecnológico de Andru.ia. Diagnostica y traza la hoja de ruta óptima para proyectos de IA en español.

日本語の概要は準備中です。原文の説明を表示しています。

MikeCheng1208/BattleTree22026年7月22日 更新

[00] META. Orchestrate skills into dynamic chains for complex tasks. Analyzes the task, discovers available skills, builds an optimal chain, explains why each skill is needed, and executes step-by-step with user confirmation. Use for any complex task requiring multiple thinking/research/analysis steps. Triggers on "plan this", "how to approach", "what's the strategy", "build a plan", or any multi-step problem requiring skill orchestration.

日本語の概要は準備中です。原文の説明を表示しています。

MikeCheng1208/BattleTree22026年7月22日 更新

MikeCheng1208 のスキルをすべて見る

このスキルの問題を報告する