A super lightweight PSR-7 implementation

Related tags

HTTP psr-7 psr-17
Overview

PSR-7 implementation

Latest Version Build Status Code Coverage Quality Score Total Downloads Monthly Downloads Software License

A super lightweight PSR-7 implementation. Very strict and very fast.

Description Guzzle Laminas Slim Nyholm
Lines of code 3.300 3.100 1.900 1.000
PSR-7* 66% 100% 75% 100%
PSR-17 No Yes Yes Yes
HTTPlug No No No Yes
Performance (runs per second)** 14.553 14.703 13.416 17.734

* Percent of completed tests in https://github.com/php-http/psr7-integration-tests

** Benchmark with 50.000 runs. See https://github.com/devanych/psr-http-benchmark (higher is better)

Installation

composer require nyholm/psr7

If you are using Symfony Flex then you get all message factories registered as services.

Usage

The PSR-7 objects do not contain any other public methods than those defined in the PSR-7 specification.

Create objects

Use the PSR-17 factory to create requests, streams, URIs etc.

$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
$request = $psr17Factory->createRequest('GET', 'http://tnyholm.se');
$stream = $psr17Factory->createStream('foobar');

Sending a request

With HTTPlug or any other PSR-18 (HTTP client) you may send requests like:

composer require kriswallsmith/buzz
$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();
$psr18Client = new \Buzz\Client\Curl($psr17Factory);

$request = $psr17Factory->createRequest('GET', 'http://tnyholm.se');
$response = $psr18Client->sendRequest($request);

Create server requests

The nyholm/psr7-server package can be used to create server requests from PHP superglobals.

composer require nyholm/psr7-server
$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();

$creator = new \Nyholm\Psr7Server\ServerRequestCreator(
    $psr17Factory, // ServerRequestFactory
    $psr17Factory, // UriFactory
    $psr17Factory, // UploadedFileFactory
    $psr17Factory  // StreamFactory
);

$serverRequest = $creator->fromGlobals();

Emitting a response

composer require laminas/laminas-httphandlerrunner
$psr17Factory = new \Nyholm\Psr7\Factory\Psr17Factory();

$responseBody = $psr17Factory->createStream('Hello world');
$response = $psr17Factory->createResponse(200)->withBody($responseBody);
(new \Laminas\HttpHandlerRunner\Emitter\SapiEmitter())->emit($response);

Our goal

This package is currently maintained by Tobias Nyholm and Martijn van der Ven. They have decided that the goal of this library should be to provide a super strict implementation of PSR-7 that is blazing fast.

The package will never include any extra features nor helper methods. All our classes and functions exist because they are required to fulfill the PSR-7 specification.

Comments
  • Make message classes final

    Make message classes final

    From https://github.com/guzzle/psr7/pull/158#issuecomment-376237838

    I'm pretty confident that I will propose making all message classes in this project final if we ever hit a 2.0 version.

    Reason: HTTP messages are represented as value objects, implementing an interface. From this point I don't see any valid use cases for extending the message classes.

    FYI @sagikazarmark

    opened by Nyholm 21
  • Duplicated logic to avoid creating new instance of StreamFactory

    Duplicated logic to avoid creating new instance of StreamFactory

    Im not sure what to do here. Is this a good solution?

    The logic is copied from StreamFactory. The alternative would be to create a new public method on Stream and move the code from StreamFactory.

    opened by Nyholm 14
  • Empty file upload gives fatal error in PHP 8.0

    Empty file upload gives fatal error in PHP 8.0

    I have an HTML form with an optional <input type="file"> field.

    There is no problem with PHP 7.1-7.4, but with PHP 8.0 I get the following error.

    Uncaught ValueError: Path cannot be empty in /Users/gr4376/Projects/webtrees/vendor/nyholm/psr7/src/Factory/Psr17Factory.php:40
    Stack trace:
    #0 .../vendor/nyholm/psr7/src/Factory/Psr17Factory.php(40): fopen('', 'r')
    #1 .../vendor/nyholm/psr7-server/src/ServerRequestCreator.php(216): Nyholm\Psr7\Factory\Psr17Factory-&gt;createStreamFromFile('')
    #2 .../vendor/nyholm/psr7-server/src/ServerRequestCreator.php(188): Nyholm\Psr7Server\ServerRequestCreator-&gt;createUploadedFileFromSpec(Array)
    #3 .../vendor/nyholm/psr7-server/src/ServerRequestCreator.php(98): Nyholm\Psr7Server\ServerRequestCreator-&gt;normalizeFiles(Array)
    #4 .../vendor/nyholm/psr7-server/src/ServerRequestCreator.php(71): Nyholm\Psr7Server\ServerRequestCreator-&gt;fromArrays(Array, Array, Array, Array, Array, Array, Resource id #7)
    #5 .../index.php(55): Nyholm\Psr7Server\ServerRequestCreator->fromGlobals()
    

    This is because fopen('') gives a warning in PHP <=7.4, but a fatal error in PHP 8.0.

    See https://3v4l.org/cgYav

    opened by fisharebest 12
  • Fix for UTF-8 in hostname

    Fix for UTF-8 in hostname

    This patch replaces using strtolower() on hostnames as this cant handle utf-8 characters, I have opted to do this with a regex to prevent adding mbstring as a dependancy

    opened by cseufert 12
  • Response::__toString() should not hide exception

    Response::__toString() should not hide exception

    https://github.com/Nyholm/psr7/blob/d38874cbc289d25a309cefd04379e24352ced608/src/Stream.php#L98-L109

    I found this because of https://github.com/php-http/client-common/issues/186 In my application, I had a 200 with an empty body :exploding_head:

    IMHO, doing this is wrong as it hides the root cause.

    More over, in PHP 7.4 it's allowed to throw exception. And for PHP 7.3-, it prefer a fatal error than something wrong.

    WDYT?

    opened by lyrixx 11
  • Header values must be non-empty strings

    Header values must be non-empty strings

    If I request a file from AWS I get an empty header:

    x-amz-id-2: mhBUIOWKJ*()Z=*U?IPZJ))(J=OetWk=
    x-amz-meta-orientation:
    x-amz-request-id: 1ZUWIODKLD
    

    which leads to an exception:

    throw new \InvalidArgumentException('Header values must be non-empty strings');
    

    in the MessageTrait. So I have no chance to use this library with servers who may return an empty header in their response.

    Why is this psr7 implementation so strict with this? Can we change the behavior? Like for example just ignore empty headers and not throw an exception?

    opened by TiMESPLiNTER 10
  • Issue with Mockery and Stream::close()

    Issue with Mockery and Stream::close()

    In PHP8, https://github.com/Nyholm/psr7/blob/master/src/Stream.php#L128 causes an issue with mockery:

    Fatal error: Declaration of Mockery_0_Nyholm_Psr7_Stream_Nyholm_Psr7_Stream_Psr_Http_Message_StreamInterface::close() must be compatible with Nyholm\Psr7\Stream::close(): void
    
    help wanted 
    opened by designcise 9
  • enforce header value as string ?

    enforce header value as string ?

    Hi,

    Shouldn't we cast the header value as string in the trimHeaderValues() function ?

    A few http headers represent a number of seconds or bytes, and the user could easily does something like this : new Response(200, ['Age' => 60]);

    instead of using a string for the value. In this case the trimHeaderValues() will crash trying to da a trim on a non string.

    It seems more logical to cast the value as string before trying to do the trim, no ? or perhaps doesn't apply the trim if the value is not a string ? or just let it crash ?

    what is you opinion ?

    opened by ncou 9
  • Move instantiation code to factories.

    Move instantiation code to factories.

    WORK IN PROGRESS, DO NOT MERGE.

    First and foremost: this changes the public API for ServerRequest, BC breaks need to be documented. (Or not, seeing how we are at a version below 1.0.0.)

    This implements the changes from https://github.com/Nyholm/psr7/issues/3#issuecomment-292812159 but should be discussed further before merge.

    1. getUriFromGlobals has been renamed createUriFromArray and has been moved from ServerRequest to UriFactory.

      The signature has been changed to accept a single $_SERVER-structure as argument, instead of the method depending directly on the global $_SERVER variable.

    2. fromGlobals has been renamed createServerRequestFromGlobals and has been moved from ServerRequest to ServerRequestFactory.

      The signature has been changed to accept 5 separate array structures matching the different global variables.

    3. normalizeFiles, createUploadedFileFromSpec, and normalizeNestedFileSpec have been moved from ServerRequest to ServerRequestFactory. This is probably not the perfect place for them either, but gets them out of ServerRequest.

    4. Return type hinting has been added to the factories.

    createUriFromArray sets no default scheme value, but http-interop/http-factory-tests expects ServerRequests to default to http through the factory. I made createServerRequestFromGlobals follow the factory expectation and added the default scheme there as well. This is a change from the old fromGlobals. But I am not sure if it is a good idea or not. Is there a use-case for creating a ServerRequest without a scheme?

    The factory interface’s createServerRequestFromArray and our createServerRequestFromGlobals differ also on their requirement for a method. PSR-17 has us throw an InvalidArgumentException when the method can’t be found in the array, while createServerRequestFromGlobals defaults to GET. Should we follow the factory expectation here as well?

    If we are following the factory expectation on both counts, it might be best to have createServerRequestFromGlobals go through createServerRequestFromArray first. Then adding the other globals to the created ServerRequest.

    What would people expect to happen?

    Finally: why is normalizeFiles public? Do we mean to support its API during future updates? If not, it should really be marked private.

    opened by Zegnat 9
  • Fixed deprecation messages for PHP 8.1

    Fixed deprecation messages for PHP 8.1

    Just added return types to ServerRequest class where they were not specified. The goal is to fixes deprecation messages on PHP8.1 as the ones below:

     1x: Method "Psr\Http\Message\ServerRequestInterface::withUploadedFiles()" might add "static" as a native return type declaration in the future. Do the same in implementation "Nyholm\Psr7\ServerRequest" now to avoid errors or add an explicit @return annotation to suppress this message.
        1x in OAuth2GenerateTokenActionTest::testItCallsSuccessfullyOauth2GenerateTokenEndpointTest from App\Tests\Functional\Action
    
      1x: Method "Psr\Http\Message\ServerRequestInterface::withCookieParams()" might add "static" as a native return type declaration in the future. Do the same in implementation "Nyholm\Psr7\ServerRequest" now to avoid errors or add an explicit @return annotation to suppress this message.
        1x in OAuth2GenerateTokenActionTest::testItCallsSuccessfullyOauth2GenerateTokenEndpointTest from App\Tests\Functional\Action
    
      1x: Method "Psr\Http\Message\ServerRequestInterface::withQueryParams()" might add "static" as a native return type declaration in the future. Do the same in implementation "Nyholm\Psr7\ServerRequest" now to avoid errors or add an explicit @return annotation to suppress this message.
        1x in OAuth2GenerateTokenActionTest::testItCallsSuccessfullyOauth2GenerateTokenEndpointTest from App\Tests\Functional\Action
    
      1x: Method "Psr\Http\Message\ServerRequestInterface::getParsedBody()" might add "array|object|null" as a native return type declaration in the future. Do the same in implementation "Nyholm\Psr7\ServerRequest" now to avoid errors or add an explicit @return annotation to suppress this message.
        1x in OAuth2GenerateTokenActionTest::testItCallsSuccessfullyOauth2GenerateTokenEndpointTest from App\Tests\Functional\Action
    
      1x: Method "Psr\Http\Message\ServerRequestInterface::withParsedBody()" might add "static" as a native return type declaration in the future. Do the same in implementation "Nyholm\Psr7\ServerRequest" now to avoid errors or add an explicit @return annotation to suppress this message.
        1x in OAuth2GenerateTokenActionTest::testItCallsSuccessfullyOauth2GenerateTokenEndpointTest from App\Tests\Functional\Action
    

    Please let me know if adjustments are needed

    This will fix #195

    opened by luisfn 8
  • Revert #177

    Revert #177

    This will revert #177 so we don't introduce new behaviour. This will fix #180.

    FYI @Zegnat @dbu. The discussion in #180 stalled a bit. By reverting this we are predictable (but maybe wrong), we are also consistent between creating a stream from string compared to from resource.

    opened by Nyholm 7
  • Filter Uri->withUserInfo() input

    Filter Uri->withUserInfo() input

    I have a login, which contains @ in username and password. User info with this character is currently not encoded. I think there should be a filterUserInfo() method to correctly encode input as other with* methods do.

    I checked other popular implementations and all of them do that:

    • https://github.com/guzzle/psr7/blob/67c26b443f348a51926030c83481b85718457d3d/src/Uri.php#L436-L439
    • https://github.com/slimphp/Slim-Psr7/blob/7e7e63e3b624ed07bde14fbd9f91ab8904402375/src/Uri.php#L174-L177
    • https://github.com/laminas/laminas-diactoros/blob/6028af6c3b5ced4d063a680d2483cce67578b902/src/Uri.php#L275-L278

    Most accurate seem to be guzzle and laminas implementations which do this:

        private function filterUserInfo($userInfo): string
        {
            if (!is_string($userInfo)) {
                throw new \InvalidArgumentException('User info must be a string');
            }
    
            return preg_replace_callback(
                '/(?:[^%' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . ']+|%(?![A-Fa-f0-9]{2}))/',
                [$this, 'rawurlencodeMatchZero'],
                $userInfo
            );
        }
    

    Only difference is in laminas/laminas-diactoros which also checks for invalid UTF-8 characters

    opened by mabar 0
  • fix psr17factory create stream

    fix psr17factory create stream

    Closes #204 As a fix, when we have created the stream, we can set the seek again to start of the file so that when we invoke $req->getBody()->getContents(); we get the actual body and not empty string.

    opened by nawedr 0
  • Empty body when using psr17factory's createRequest

    Empty body when using psr17factory's createRequest

    Using the psr17factory for creating a request object using this syntax, when we try to get the contents of the body, we get empty string.

    $factory = new Psr17Factory();
    $req = $factory->createRequest('POST', 'https://nyholm.tech');
    $body = $factory->createStream('Foo');
    $req = $req->withBody($body);
    var_dump($req->getBody()->getContents()); // prints empty string.
    

    The problem is in the createStream method, while creating the stream the file seek goes to the end of the temp file created and when we try to get the contents it starts reading from end, hence the empty string.

    opened by nawedr 0
  • Improve extensibility by removing `@final` and making Stream's constructor public

    Improve extensibility by removing `@final` and making Stream's constructor public

    I'm working on a situation where I'd like to have control over the signature of the constructor of PSR-7 implementations. I managed to use inheritance for all implementations but nyholm/psr7 That's why I'm proposing this change. It improves extensibility without hurting maintainability IMHO.

    opened by nicolas-grekas 1
  • Fixed rawurlencode for userInfo in Uri

    Fixed rawurlencode for userInfo in Uri

    When you specify a username and password using Uri::withUserInfo() these are not encoded for URL (see here). For instance, if the username is foo@ and the password is bar the userInfo results in foo@:bar that is not a valid URL encoded string.

    Other PSR-7 implementations use rawurlencode() for encoding the characters in the URL (e.g. Guzzle, Laminas-diactoros).

    opened by ezimuel 6
Releases(1.5.1)
Owner
Tobias Nyholm
Symfony Core team, certified Symfony developer. Speaker, writer, podcaster. Maintainer for many awesome libraries.
Tobias Nyholm
PSR HTTP Message implementations

laminas-diactoros Diactoros (pronunciation: /dɪʌktɒrɒs/): an epithet for Hermes, meaning literally, "the messenger." This package supercedes and repla

Laminas Project 343 Dec 25, 2022
HTTP header kit for PHP 7.1+ (incl. PHP 8) based on PSR-7

HTTP header kit for PHP 7.1+ (incl. PHP 8) based on PSR-7 Installation composer require sunrise/http-header-kit How to use? HTTP Header Collection Mor

Sunrise // PHP 63 Dec 31, 2022
Finds installed HTTPlug implementations and PSR-7 message factories.

Finds installed HTTPlug implementations and PSR-7 message factories.

The PHP HTTP group 1.1k Dec 29, 2022
Simple HTTP cURL client for PHP 7.1+ based on PSR-18

Simple HTTP cURL client for PHP 7.1+ based on PSR-18 Installation composer require sunrise/http-client-curl QuickStart composer require sunrise/http-f

Sunrise // PHP 15 Sep 5, 2022
PHP's lightweight HTTP client

Buzz - Scripted HTTP browser Buzz is a lightweight (<1000 lines of code) PHP 7.1 library for issuing HTTP requests. The library includes three clients

Kris Wallsmith 1.9k Jan 4, 2023
Unirest in PHP: Simplified, lightweight HTTP client library.

Unirest for PHP Unirest is a set of lightweight HTTP libraries available in multiple languages, built and maintained by Mashape, who also maintain the

Kong 1.3k Dec 28, 2022
Unirest - a set of lightweight HTTP libraries available in multiple languages

Unirest is a set of lightweight HTTP libraries available in multiple languages, built and maintained by Mashape, who also maintain the open-source API Gateway Kong.

Kong 1.3k Dec 30, 2022
Unirest is a set of lightweight HTTP libraries available in multiple languages.

Unirest for PHP Unirest is a set of lightweight HTTP libraries available in multiple languages. This fork is maintained by APIMatic for its Code Gener

APIMatic 14 Oct 26, 2022
Retrofit implementation in PHP. A REST client for PHP.

Retrofit PHP Retrofit is a type-safe REST client. It is blatantly stolen from square/retrofit and implemented in PHP. ❗ UPGRADE NOTICE ❗ Version 3 int

null 153 Dec 21, 2022
Event-driven, streaming HTTP client and server implementation for ReactPHP

HTTP Event-driven, streaming HTTP client and server implementation for ReactPHP. This HTTP library provides re-usable implementations for an HTTP clie

ReactPHP 640 Dec 29, 2022
A simple and flexible PHP middleware dispatcher based on PSR-7, PSR-11, and PSR-15

Woohoo Labs. Harmony Woohoo Labs. Harmony is a PSR-15 compatible middleware dispatcher. Harmony was born to be a totally flexible and almost invisible

Woohoo Labs. 153 Sep 5, 2022
A small, modern, PSR-7 compatible PSR-17 and PSR-18 network library for PHP, inspired by Go's net package.

Net A small, modern, PSR-7 compatible PSR-17 and PSR-18 network library for PHP, inspired by Go's net package. Features: No hard dependencies; Favours

Minibase 16 Jun 7, 2022
A small, modern, PSR-7 compatible PSR-17 and PSR-18 network library for PHP, inspired by Go's net package.

Net A small, modern, PSR-7 compatible PSR-17 and PSR-18 network library for PHP, inspired by Go's net package. Features: No hard dependencies; Favours

Minibase 16 Jun 7, 2022
PSR-6 cache implementation adapting a given PSR-16 instance

PSR-6 cache implementation adapting PSR-16 This package provides a PSR-6 cache instance when you only have a PSR-16 cache at hand. As PSR-6 is more fe

null 1 Oct 15, 2021
a super lightweight markdown wiki/blog/tweeting system.

laMDWiki is a super light weight blog/wiki platform running on PHP!

null 4 Dec 24, 2021
A super lightweight Markdown parser for PHP projects and applications.

A speedy Markdown parser for PHP applications. This is a super lightweight Markdown parser for PHP projects and applications. It has a rather verbose

Ryan Chandler 15 Nov 8, 2022
A super fast, customizable and lightweight PHP MVC Starter Framework to extend for your own...

PHPMVC A super fast, customizable and lightweight PHP MVC Starter Framework to extend for your own... How to Start Clone this repo - git clone https:/

Maniruzzaman Akash 9 Dec 11, 2022
Middleware for PHP built on top of PSR-7 and PSR-15

zend-stratigility Repository abandoned 2019-12-31 This repository has moved to laminas/laminas-stratigility. From "Strata", Latin for "layer", and "ag

Zend Framework 236 Sep 9, 2022
Fast PSR-7 based routing and dispatch component including PSR-15 middleware, built on top of FastRoute.

Route This package is compliant with PSR-1, PSR-2, PSR-4, PSR-7, PSR-11 and PSR-15. If you notice compliance oversights, please send a patch via pull

The League of Extraordinary Packages 608 Dec 30, 2022
:tada: Release 2.0 is released! Very fast HTTP router for PHP 7.1+ (incl. PHP8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger)

HTTP router for PHP 7.1+ (incl. PHP 8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger) Installation compo

Sunrise // PHP 151 Jan 5, 2023