slimphp / slim-psr7 Goto Github PK
View Code? Open in Web Editor NEWPSR-7 implementation for use with Slim 4
License: MIT License
PSR-7 implementation for use with Slim 4
License: MIT License
I have noticed when we call the detach
method of a stream the cache
property stay attached to some resource. Accroding to Psr7/StreamInterface
a stream is unusable after that. Maybe, it makes sense to assign the cache
property to null
.
Port slimphp/Slim-Http#27 to this repo
I just cloned the repository, run composer install
and then tried phpunit
. I got the following error and was wondering, if that is only in my environment?
1) Slim\Tests\Psr7\Integration\ServerRequestTest::testGetUploadedFiles
RuntimeException: Could not create Stream. Check your config
...\vendor\php-http\psr7-integration-tests\src\BaseTest.php:111
...\vendor\php-http\psr7-integration-tests\src\ServerRequestIntegrationTest.php:83
Note the plural s in the class name of the test. Shouldn't we change the test class name into singular?
I looked at the source code of various Stream
class implementations. In particular, the __toString
method.
I noticed inconsistent behavior when working with non-seekable resources. For instance, a simple pipe - popen('echo 12', 'r')
.
$fd = popen('echo 12', 'r');
$stream = new Stream($fd);
(string) $stream; // implementation dependent
zend-diactoros
&& nyholm/psr7
They try to rewind a pipe according to standart, but ignore seek
operation and return content, because a pipe is non-seekable.
guzzlehttp/psr7
&& slim/psr7
They try to rewind a stream then as result get RuntimeException and return the empty string.
In my opinion, it will be more logical to use the first strategy. What do you thing about it?
Status code is 200 on Docker. But POST JSON by Postman return 500.
Trying to access array offset on value of type bool on line 389 in vendor/slim/psr7/src/Stream.php
class PostAction extends Action
{
protected function action(): Response
{
$this->logger->debug('debug', [(string)$this->request->getBody()]);
return $this->response;
}
}
Given the following slim route:
// Echo back the request been sent to us
$app->any('/check/request', function (Request $request, Response $response, array $args) use ($logger) {
$request->getBody()->rewind();
$payload = [
'method' => $request->getMethod(),
'body' => $request->getBody()->getContents(),
'headers' => $request->getHeaders(),
'uri_path' => $request->getUri()->getPath(),
'query_parameters' => $request->getQueryParams(),
];
return $response
->withHeader('content-type', 'application/json')
->withStatus(200)
->withBody(stream_for(json_encode($payload)));
});
And the following curl request:
curl "localhost:8080/check/request?foo=bar" -XPUT -H"cov: fefe" -d"lalala"
The following comes out as a response:
{
"method": "PUT",
"body": "lalala",
"headers": {
"HTTP_HOST": [
"localhost:8080"
],
"HTTP_USER_AGENT": [
"curl\/7.64.0"
],
"HTTP_ACCEPT": [
"*\/*"
],
"HTTP_COV": [
"fefe"
]
},
"uri_path": "\/check\/request",
"query_parameters": {
"foo": "bar"
}
}
As you can see, the cov
header comes out as HTTP_COV
.
Slim 3.12.1
The docblock is missing.
Context
Currently, HTTP POST request parameters are detected when the ContentType of the HTTP Request equals either application/x-www-form-urlencoded
or multipart/form-data
. After this "detection", they can be access using $request->getParsedBody()
.
When using jQuery $.ajax to dispatch an HTTP POST request, the default ContentType of the HTTP Request is application/x-www-form-urlencoded; charset=UTF-8
, which is valid syntax for this header.
Problem
When using jQuery $.ajax to dispatch an HTTP POST request, the current approach does not properly detect this as being a HTTP POST request and $request->getParsedBody()
remains empty, while $request->getBody()
contains content like parameter1=value1¶meter2=value2¶meterN=valueN
.
Workaround
Currently the following workarounds can be considered:
application/x-www-form-urlencoded; charset=UTF-8
, but this requires the complete set of Zend-Expressive Middleware helpers to be installed.application/x-www-form-urlencoded
Possible solution
Maybe the current exact match for application/x-www-form-urlencoded
can be loosened a bit? At least this is how the FormUrlEncodedStrategy
of the BodyParsingMiddleware does it
A more robust / future proof approach might be to adopt a similar "strategy" mechanism as the BodyParsingMiddleware, which also has a strategy for detecting JSON payloads, also see https://github.com/zendframework/zend-expressive-helpers/tree/master/src/BodyParams
According to the documentation
The PSR-7 Request object’s URI is itself an object that provides the following methods to inspect the HTTP request’s URL parts:
getScheme()
getAuthority()
getUserInfo()
getHost()
getPort()
getPath()
getBasePath()
getQuery() (returns the full query string, e.g. a=1&b=2)
getFragment()
getBaseUrl()
You can get the query parameters as an associative array on the Request object using getQueryParams().
Base Path
If your Slim application's front-controller lives in a physical subdirectory beneath your document root directory, you can fetch the HTTP request's physical base path (relative to the document root) with the Uri object's getBasePath() method. This will be an empty string if the Slim application is installed in the document root's top-most directory.
Slim v3 implemented the basePath
processing as per https://github.com/slimphp/Slim/blob/3.x/Slim/Http/Uri.php#L204
$msg = new Message();
$msg->withHeader("name", "value");
[calls]
Message::withHeader($name, $string) [Message.php]
Headers::validateHeaderName($name);
Headers::validateHeaderValue($value);
...
$clone->headers->setHeader($name, $value)
Headers::setHeader($name, $value) [Headers.php]
$values = $this->validateAndTrimHeader($name, $values);
...
Headers::validateHeaderName($name);
Headers::validateHeaderValue($value);
...
The same is true for the withAddedHeader
method. I don't know how to redesign the code to create pull request.
Slim-Psr7 (for Slim 4.x) version of slimphp/Slim#2481.
Not sure whether that is an issue. But the example in the PSR7 specification has not only a comma as separator in a multivalue header, but also a white-space.
Quote from https://www.php-fig.org/psr/psr-7/
$header = $message->getHeaderLine('foo');
// $header contains: 'bar, baz'
In a quick test I have recognized, that other implementations (Nyholm, Guzzle) do this as written in the example.
What is the expected value? With or without white-space after the comma?
Hello there, I'm using Slim 4.5.0.
I'm PUTting a binary file to one of my routes but the Slim\Psr7\Stream
instance I get from the Slim\Http\ServerRequest
object won't tell me the content size. I'm NOT sending a multipart/form-data
request. I'm just PUTting a file with curl -T "filename" "http://my-host/my-route"
Here's my PHP code. $request->getBody()->getSize()
is null even though the stream is reported seekable and the Content-Length
header was provided.
public function putMediaContent(ServerRequestInterface $request, ResponseInterface $response, array $args) : ResponseInterface {
//body is a Slim\Psr7\Stream instance
$body = $request->getBody();
//size is null
$size = $body->getSize();
//isSeekable is true
$isSeekable = $body->isSeekable();
//contentLength has the correct uploaded file size
$contentLength = $request->getHeaders()['Content-Length'][0];
//...
}
This is a problem since I then have to pass this stream to an S3Client for cloud upload and it complains about the fact the size is unknown.
Is this intended?
Thanks.
You can reproduce the error, by enabling the BodyParsingMiddleware and submit a post form without named input fields.
I used Slim 4, all latest packages and PHP 7.4.
Error message
Details
Type: ErrorException
Code: 0
Message: Trying to access array offset on value of type bool
File: \path\to\project\vendor\slim\psr7\src\Stream.php
Line: 351
Trace#0 C:\dev\Projekte\Tasky\vendor\slim\psr7\src\Stream.php(351): {closure}(8, 'Trying to acces...', '\path\to\project...', 351, Array)
#1 C:\dev\Projekte\Tasky\vendor\slim\psr7\src\Stream.php(256): Slim\Psr7\Stream->isPipe()
#2 C:\dev\Projekte\Tasky\vendor\slim\psr7\src\Stream.php(278): Slim\Psr7\Stream->isSeekable()
#3 C:\dev\Projekte\Tasky\vendor\slim\psr7\src\Stream.php(141): Slim\Psr7\Stream->rewind()
#4 C:\dev\Projekte\Tasky\vendor\slim\slim\Slim\Middleware\BodyParsingMiddleware.php(161): Slim\Psr7\Stream->__toString()
#5 C:\dev\Projekte\Tasky\vendor\slim\slim\Slim\Middleware\BodyParsingMiddleware.php(62): Slim\Middleware\BodyParsingMiddleware->parseBody(Object(Slim\Psr7\Request))
#6 C:\dev\Projekte\Tasky\vendor\slim\slim\Slim\MiddlewareDispatcher.php(140): Slim\Middleware\BodyParsingMiddleware->process(Object(Slim\Psr7\Request), Object(class@anonymous))
#7 C:\dev\Projekte\Tasky\vendor\slim\slim\Slim\Middleware\RoutingMiddleware.php(60): class@anonymous->handle(Object(Slim\Psr7\Request))
#8 C:\dev\Projekte\Tasky\vendor\slim\slim\Slim\MiddlewareDispatcher.php(140): Slim\Middleware\RoutingMiddleware->process(Object(Slim\Psr7\Request), Object(class@anonymous))
#9 C:\dev\Projekte\Tasky\vendor\slim\slim\Slim\Middleware\ErrorMiddleware.php(107): class@anonymous->handle(Object(Slim\Psr7\Request))
#10 C:\dev\Projekte\Tasky\vendor\slim\slim\Slim\MiddlewareDispatcher.php(140): Slim\Middleware\ErrorMiddleware->process(Object(Slim\Psr7\Request), Object(class@anonymous))
#11 C:\dev\Projekte\Tasky\vendor\slim\slim\Slim\MiddlewareDispatcher.php(81): class@anonymous->handle(Object(Slim\Psr7\Request))
#12 C:\dev\Projekte\Tasky\vendor\slim\slim\Slim\App.php(215): Slim\MiddlewareDispatcher->handle(Object(Slim\Psr7\Request))
#13 C:\dev\Projekte\Tasky\vendor\slim\slim\Slim\App.php(199): Slim\App->handle(Object(Slim\Psr7\Request))
#14 C:\dev\Projekte\Tasky\public\index.php(86): Slim\App->run(Object(Slim\Psr7\Request))
#15 {main}
I'm removing non-PSR-7 methods from Slim-Psr7
However, when it comes to the parsed body of the Request object, Should Slim-Psr7 continue to handle it's own decoding of body using the media types or should this functionality be moved elsewhere?
According to the php documentation, the function getallheaders()
might return false
. In fact, on my hosting, the function even returns null
.
Lines 78 to 79 in 662e23f
See also slimphp/Slim#2632
We can't use Slim\Http
as we need that for Slim itself.
Options:
Slim\Psr7
Slim\HttpMessage
Since PHP 7.1
is used, nullable and void
return types could be adopted.
I was wondering: what is the level of ambition regarding this possibility for some more strictness by adopting type hints and return types?
For an overview see:
When calling a "with" method on a Response (and also Request) object, the returned type is not Response (or Request) but rather Message.
public function home(Request $request, Response $response, array $args){
//Type of $response is Response (Slim\Psr7\Response)
$response= $response->withHeader("test", "value");
//Type of $response is now Message (Slim\Psr7\Message)
....
}
This is not an issue until you need to use Response (or Request) methods that are not common to the Message object.
Cheers,
Noah
Investigate https://github.com/php-fig/http-message-util and see if we need it.
Currently we do not parse any incoming data in the Request
object. All that functionality has been moved to the Slim-Http
decorators repository.
We should by default at least support the incoming form data in the $_POST
object and JSON.
Shouldn't we change that line such that in case of https the default port would be 443?
Slim-Psr7/src/Factory/UriFactory.php
Line 81 in 495595f
Isn't the Body class now basically obsolete?
Migrated from slimphp/Slim-Http#51
Can I use ext-psr
to replace this in slim?
https://github.com/jbboehr/php-psr
getHeader()
and getHeaders()
are consistent. See slimphp/Slim#2400Migrated from slimphp/Slim-Http#61
Slim-Psr7 does only show the Host header on systems where getallheaders()
is missing. My setup is Apache + PHP 7.2 via FPM. But I think there are many other cases, for example using nginx.
Slim3 has a fallback to parse the headers from environment in this case. And so do other PSR7 implementations like Nyholm/PSR7: https://github.com/Nyholm/psr7-server/blob/0.3.0/src/ServerRequestCreator.php#L52
composer require slim/slim
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\App;
require 'vendor/autoload.php';
$app = new App();
$app->get('/', function (Request $request, Response $response, array $args) {
var_dump($request->getHeaders());
return $response;
});
$app->run();
Result:
array(10) {
["HTTP_UPGRADE_INSECURE_REQUESTS"]=>(...)
["HTTP_DNT"]=>(...)
["HTTP_ACCEPT_ENCODING"]=>(...)
["HTTP_ACCEPT_LANGUAGE"]=>(...)
["HTTP_ACCEPT"]=>(...)
["HTTP_USER_AGENT"]=>(...)
["HTTP_CONNECTION"]=>(...)
["HTTP_X_ACCEL_INTERNAL"]=>(...)
["HTTP_X_REAL_IP"]=>(...)
["HTTP_HOST"]=>(...)
}
composer require slim/slim:4.0.0-beta
composer require slim/psr7
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
require 'vendor/autoload.php';
$app = AppFactory::create();
$app->get('/', function (Request $request, Response $response, array $args) {
var_dump($request->getHeaders());
return $response;
});
$app->run();
Result:
array(1) {
["Host"]=>(...)
}
The Response constructor will by default create a Headers object to collect headers to be sent. The Headers constructor will call setHeaders() which will call parseAuthorizationHeader(). parseAuthorizationHeader() will try to create an Authorization header from data found in $_SERVER.
The result of this is that the Response headers include a copy of the Authorization header received in the request being processed.
The code comment for parseAuthorizationHeader() reads: "Parse incoming headers and determine Authorization header from original headers". My interpretation is that this code was never intended to be used for a Response.
For example $response->withJson()
.
Or this package will never have any non-standard method at all?
This component doesn't need any non-PSR-7 functionality as that's now in Slim-Http.
I am trying to upgrade Slim framework used in my project from version 3 to version 4 and during the process I suddenly found out that withJson
method has gone from the Response class. I want to understand why it is gone? I understand that Response class implements PSR-7 ResponseInterface, where withJson method is not included. But I don't think implementing ResponseInterface forbids Response class having some helper methods which will be very helpful.
The abstract class Message
, among others, defines two protected properties - $headers
& $body
. Any derived class will be inherit them. In paticular, two classes Request
& Response
. But classes define constructor parameters differently.
Request::__construct(..., HeadersInterface $headers, StreamInterface $body)
Response::__construct(..., ?HeadersInterface $headers, ?StreamInterface $body)
Message
) defines the constructor, what would be the default behavior ??HeadersInterface or HeadersInterface
& ?StreamInterface or StreamInterface
.The class Cookies
does not extend nor implement anything. But there are some docblocks like the following
Lines 68 to 71 in 9cdb71a
Where do they inherit from?
Given a REQUEST_URI
of /baz?foo=bar
, the path in the Uri
should be /baz
, not /baz?foo=bar
.
This is because in UriFactory::createFromGlobals()
, we do:
$requestUri = $env->get('REQUEST_URI');
In Slim 3 we do:
$requestUri = parse_url('http://example.com' . $env->get('REQUEST_URI'), PHP_URL_PATH);
Nyholm does:
$uri = $uri->withPath(\current(\explode('?', $server['REQUEST_URI'])));
To be able to test the following case, should we override the function is_readable()
? Or do you prefer a real non-readable file?
https://github.com/slimphp/Slim-Psr7/blob/master/src/Factory/UploadedFileFactory.php#L33
If override, where do you want to have the file for overrides in the Slim\Psr7\Factory
namespace?
https://github.com/slimphp/Slim-Psr7/blob/master/src/Factory/ServerRequestFactory.php#L93
'php://input' is missing
$body = (new StreamFactory())->createStreamFromFile('php://input');
We need to remove 7.4
from the allow_failures
array in the Travis config when phpspec/prophecy#432 is merged
The library provides all required PSR-17 interfaces. Shouldn't it be able to add "psr/http-factory-implementation": "1.0"
to the provide
key in composer.json
?
Just like Guzzle and Diactoros do. See https://packagist.org/providers/psr/http-factory-implementation.
I want to use a library that requires psr/http-factory-implementation ^1.0
and I want to use this package.
In #70 the file tests/Assets/PhpGetAllHeaders.php
had been removed. This breaks PHPUnit on my system (Win10, PHP 7.1), because the following condition
Line 266 in 3f9b6ae
Slim\Psr7
namespace via the file tests/Assets/PhpFunctionOverrides.php
).
There were 4 failures:
1) Slim\Tests\Psr7\Factory\ServerRequestFactoryTest::testCreateFromGlobals
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'application/json'
+''
D:\GitHub\Slim-Psr7\tests\Factory\ServerRequestFactoryTest.php:82
2) Slim\Tests\Psr7\Factory\ServerRequestFactoryTest::testCreateFromGlobalsWithParsedBody
null does not match expected type "array".
D:\GitHub\Slim-Psr7\tests\Factory\ServerRequestFactoryTest.php:110
3) Slim\Tests\Psr7\Factory\ServerRequestFactoryTest::testCreateFromGlobalsParsesBodyWithFragmentedContentType
null does not match expected type "array".
D:\GitHub\Slim-Psr7\tests\Factory\ServerRequestFactoryTest.php:172
4) Slim\Tests\Psr7\HeadersTest::testCreateFromGlobals
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
Array (
- 'accept' => Array (...)
)
D:\GitHub\Slim-Psr7\tests\HeadersTest.php:27
It seems that only failures 1 and 4 are directly connected, as the other failures remain even if I add the function getallheaders()
to the root namespace.
In \Slim\Psr7\NonBufferedBody::write()
there are four function calls to php built-in functions. All of them are root-namespaced.
ob_get_level
ob_get_clean
flush
strlen
Is there any reason for that?
Slim-Psr7/src/NonBufferedBody.php
Lines 99 to 107 in 636bea0
Port slimphp/Slim#2543 to this component.
Running 4.5.0, I'm consistently getting the following error w/ PHP 7.4.5:
Notice: Trying to access array offset on value of type bool in .../vendor/slim/psr7/src/Stream.php on line 389
The request seems to work, but I get Notice output in the result, ahead of my JSON body.
( ! ) Notice: Trying to access array offset on value of type bool in .../vendor/slim/psr7/src/Stream.php on line 389
--
1 | 0.0072 | 361776 | {main}( ) | .../index.php:0
2 | 0.0714 | 467296 | Slim\App->run( ??? ) | .../index.php:30
3 | 3.7541 | 564504 | Slim\App->handle( ??? ) | .../App.php:199
4 | 3.7542 | 564504 | Slim\MiddlewareDispatcher->handle( ??? ) | .../App.php:215
5 | 3.7542 | 564504 | {anonymous-class:.../vendor/slim/slim/Slim/MiddlewareDispatcher.php:127-142}->handle( ??? ) | .../MiddlewareDispatcher.php:81
6 | 3.7542 | 564504 | Slim\Middleware\ErrorMiddleware->process( ???, ??? ) | .../MiddlewareDispatcher.php:140
7 | 3.7542 | 564504 | {anonymous-class:.../vendor/slim/slim/Slim/MiddlewareDispatcher.php:127-142}->handle( ??? ) | .../ErrorMiddleware.php:107
8 | 3.7542 | 564504 | Slim\Middleware\RoutingMiddleware->process( ???, ??? ) | .../MiddlewareDispatcher.php:140
9 | 3.7573 | 573144 | {anonymous-class:.../vendor/slim/slim/Slim/MiddlewareDispatcher.php:127-142}->handle( ??? ) | .../RoutingMiddleware.php:60
10 | 3.7573 | 573144 | Slim\Middleware\BodyParsingMiddleware->process( ???, ??? ) | .../MiddlewareDispatcher.php:140
11 | 3.7574 | 573144 | Slim\Middleware\BodyParsingMiddleware->parseBody( ??? ) | .../BodyParsingMiddleware.php:62
12 | 3.7574 | 573144 | Slim\Psr7\Stream->__toString( ) | .../BodyParsingMiddleware.php:161
13 | 3.7574 | 573144 | Slim\Psr7\Stream->rewind( ) | .../Stream.php:162
14 | 3.7574 | 573144 | Slim\Psr7\Stream->isSeekable( ) | .../Stream.php:299
15 | 3.7574 | 573144 | Slim\Psr7\Stream->isPipe( ) | .../Stream.php:277
{ ... my JSON response ... }
The line of code is:
$this->isPipe = (fstat($this->stream)['mode'] & self::FSTAT_MODE_S_IFIFO) !== 0;
I'm expecting that this error means $this->stream
is a boolean, but in the debugger I never see this to be the case. It's reliably an object. The error appears to occur on the second call to isPipe().
Note that this all works when errors are disabled in production. It's just causing trouble in dev.
Need to pass https://github.com/php-http/psr7-integration-tests
Migrated from slimphp/Slim-Http#65
Should we implement this test?
Slim-Psr7/tests/UploadedFilesTest.php
Lines 216 to 222 in 662e23f
Not sure it that is an issue, but the following test fails.
$cookies = new Cookies(['expires' => null]);
$this->assertFalse($cookies->get('not-set', false));
$this->assertNull($cookies->get('expires', 'Wed, 26 Jan 2020 12:00:00 GMT'));
We pass expires
to be null
in the constructor. The method $cookies->get()
would check if there is a value defined for the given name and if not, it returns the second given parameter (default-fallback). The first assert-line is working as expected, but the second one would not return null
, which it should in my opinion.
This is because the method $cookies->get()
uses isset
to check the existence of an array key. This function returns false
if the key does not exist or if the value is null
. See
Lines 90 to 93 in e4be5e0
Should we change that to array_key_exists
?
How should we construct a Uri
object?
Our current constructor is:
public function __construct($scheme, $host, $port = null, $path = '/', $query = '', $fragment = '', $user = '', $password = '')
Should it be changed?
I think that the most common use-case is that the user has a string representation of a URI (e.g. "http://example.com/foo") & currently we use a factory called createFromString
for this case.
Should we update the way we create Uri
s for Slim 4? These are the choices for construction of a Uri
:
$uri = new Uri('http', 'example.com', '/foo'):
(as current)$uri = new Uri("http://example.com/foo"):
(constructor takes a string)$uri = Uri::createFromString("http://example.com.foo"):
(static factory in Uri
class)Note that we also want to support the upcoming http-interop's factories (PSR-17), so we'll also have this way to create a Uri
from a string:
$uri = UriFactory::createUri("http://example.com/foo");
Any preferences to how we should create Uri
s in addition to UriFactory::createUri()
?
Related: slimphp/Slim#2310
Also see https://github.com/php-fig/http-message-util/blob/master/src/StatusCodeInterface.php
Migrated from slimphp/Slim-Http#40
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.