Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Http Message Implementation Laravel Package

psr-mock/http-message-implementation

Lightweight PSR-7 HTTP message implementation mock for testing libraries and SDKs without hard dependencies. Strictly follows PSR behavior and includes a developer-friendly API to debug and fix failing tests faster. For dev use only (PHP 8.1+).

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation**
   ```bash
   composer require --dev psr-mock/http-message-implementation:^1.0
  • Note: Requires PHP 8.1+ (updated in v1.0.1).
  • Add to composer.json under require-dev if not using autoloader.
  1. First Use Case: Mocking a Request

    use PsrMock\Http\Message\MockRequest;
    use Psr\Http\Message\RequestInterface;
    
    $request = MockRequest::create()
        ->withMethod('GET')
        ->withUri('https://example.com/api')
        ->withHeader('Accept', 'application/json');
    
  2. First Use Case: Mocking a Response

    use PsrMock\Http\Message\MockResponse;
    
    $response = MockResponse::create()
        ->withStatus(200)
        ->withHeader('Content-Type', 'application/json')
        ->withBody('{"status": "success"}');
    
  3. Where to Look First

    • PSR-7 HTTP Message Interface for reference.
    • src/ directory for core classes (MockRequest, MockResponse, MockStream).
    • tests/ for usage examples and edge cases.

Implementation Patterns

Common Workflows

1. Mocking Requests for Testing Controllers

public function testGetUserProfile()
{
    $request = MockRequest::create()
        ->withMethod('GET')
        ->withUri('/users/123')
        ->withHeader('Authorization', 'Bearer token123');

    $response = $this->app->handle($request);
    $this->assertEquals(200, $response->getStatusCode());
}

2. Mocking Responses in Service Layer

public function testFetchDataFromExternalApi()
{
    $mockResponse = MockResponse::create()
        ->withStatus(200)
        ->withBody('{"data": "test"}');

    // Mock GuzzleHttp\Client or similar to return $mockResponse.
    $service = new ExternalApiService($mockClient);
    $result = $service->fetchData();
    $this->assertEquals(['data' => 'test'], json_decode($result, true));
}

3. Chaining Methods for Readability

$request = MockRequest::create()
    ->withMethod('POST')
    ->withUri('/submit')
    ->withHeader('Content-Type', 'application/json')
    ->withBody(json_encode(['key' => 'value']));

4. Reusing Mocks with Data Providers

public function testMultipleRequestScenarios(array $method, string $uri, array $headers)
{
    $request = MockRequest::create()
        ->withMethod($method)
        ->withUri($uri)
        ->withHeaders($headers);

    // Test logic here...
}

public function requestProvider()
{
    return [
        ['GET', '/users', ['Accept' => 'application/json']],
        ['POST', '/users', ['Content-Type' => 'application/json']],
    ];
}

5. Integration with Laravel HTTP Tests

public function testLaravelHttpResponse()
{
    $response = MockResponse::create()
        ->withStatus(200)
        ->withHeader('Content-Type', 'text/html')
        ->withBody('<html>Test</html>');

    $this->get('/test')
        ->assertOk()
        ->assertSee('<html>Test</html>');
}

Integration Tips

Mocking Streams for File Uploads

use PsrMock\Http\Message\MockStream;

$stream = MockStream::createFromContent('file_content')
    ->withSize(1024)
    ->withMetadata(['custom' => 'metadata']);

$request = MockRequest::create()
    ->withUploadedFile('file', $stream);

Extending Mocks for Custom Logic

class CustomMockRequest extends MockRequest
{
    public function withCustomHeader(string $name, string $value): self
    {
        return $this->withAddedHeader($name, $value);
    }
}

Using with PHPUnit Data Providers

public function testRequestHeadersProvider()
{
    $this->assertRequestHeaders(
        MockRequest::create()->withHeader('X-Test', '1'),
        ['X-Test' => '1']
    );
}

Gotchas and Tips

Pitfalls

1. Immutable Objects (PHP 8.1+)

  • All methods return a new instance (fluent interface). Avoid chaining incorrectly:
    // ❌ Wrong: Modifies nothing
    $request->withMethod('GET')->withUri('/test'); // Returns void if not assigned
    
    // ✅ Correct: Assign result
    $request = $request->withMethod('GET')->withUri('/test');
    

2. Header Case Sensitivity

  • Headers are case-insensitive by PSR-7, but the mock preserves the exact case you set. Normalize if needed:
    $request->withHeader('content-type', 'text/plain'); // Preserves case
    $request->getHeader('Content-Type'); // Returns 'content-type'
    

3. Stream Detachment

  • Detached streams cannot be reused. If you detach a stream from a request/response, it’s no longer tied to the message:
    $stream = $request->getBody();
    $detached = $stream->detach(); // $stream is now detached
    $request->getBody(); // Returns $detached
    

4. URI Parsing Quirks

  • The mock uses PHP’s parse_url() under the hood. Ensure URIs are valid:
    // ❌ May throw exception
    $request->withUri('invalid:uri');
    
    // ✅ Safe
    $request->withUri('https://example.com');
    

5. Body Consumption

  • Stream bodies are consumable once. Reset or recreate if needed:
    $body = $response->getBody();
    $body->rewind(); // Reset pointer
    $content = $body->getContents(); // Read again
    

6. PHP 8.1+ Requirements

  • Breaking Change: Minimum PHP version is now 8.1. Ensure your project complies:
    # Check PHP version
    php -v
    
  • If using older PHP, downgrade to ^0.9 (last version supporting PHP 7.4+).

Debugging Tips

1. Dump Headers/Body

$headers = $request->getHeaders();
$body = $request->getBody()->getContents();
dd($headers, $body);

2. Validate PSR-7 Compliance

$this->assertInstanceOf(RequestInterface::class, $request);
$this->assertInstanceOf(StreamInterface::class, $request->getBody());

3. Check for Detached Streams

if ($request->getBody()->isSeekable()) {
    // Stream is still attached
}

Extension Points

1. Custom Assertions

use PHPUnit\Framework\Assert;

public function assertRequestMatches(RequestInterface $request, string $method, string $uri, array $headers = [])
{
    Assert::assertEquals($method, $request->getMethod());
    Assert::assertEquals($uri, (string) $request->getUri());
    foreach ($headers as $name => $value) {
        Assert::assertEquals($value, $request->getHeaderLine($name));
    }
}

2. Mock Factories

class RequestFactory
{
    public static function createGet(string $uri, array $headers = []): MockRequest
    {
        return MockRequest::create()
            ->withMethod('GET')
            ->withUri($uri)
            ->withHeaders($headers);
    }
}

3. Override Default Behavior

class LoggingMockRequest extends MockRequest
{
    public function withMethod(string $method): self
    {
        error_log("Method changed to: {$method}");
        return parent::withMethod($method);
    }
}

4. Use with Laravel’s Http Facade

$mockResponse = MockResponse::create()->withStatus(200);
Http::fake($mockResponse);

Config Quirks

  • No Configuration: This is a runtime-only library. No config/ or service provider setup is needed.
  • Dependencies: Ensure your project adheres to PSR-7 (e.g., nyholm/psr7 or guzzlehttp/psr7). The mock implements the interfaces but relies on underlying implementations for some features (e.g., streams).
  • PHP 8.1+: All new features/
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nasirkhan/laravel-sharekit
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony