To make something that is compatible with unknown third-party 
implementations of PSR, I would do something like this:

class MySpecialRequestHandler
{
    private $streamFactory;
    public function __construct(StreamFactoryInterface $streamFactory)
    {
        $this->streamFactory = $streamFactory;
    }
    public function handleRequest(RequestInterface $request): 
RequestInterface
    {
        $newBody = $this->streamFactory->createStream('New body!');
        return $request->withBody($newBody);
    }
}


This way the user’s dependency injection can take care of providing the 
correct StreamFactory implementation. Alternatively you could use something 
like middlewares/utils <https://github.com/middlewares/utils> which 
includes a Factory class <https://github.com/middlewares/utils#factory> 
that tries to find the currently available implementation without the need 
for injection by the user.

There are a lot of reasons why you would want to switch out the body 
completely. You can for example not guarantee that a truncate() or 
withContents() method will work on every Stream implementation. How will 
you hande read-only Streams? Are unseekable Streams a problem? (There are 
also numerous discussions about cursor placement that may or may not make 
things hard, just search through the mailing list.) Instead of trying to 
change the contents of a Stream, it is much better to initiate a new Stream 
and use that through the withBody() method. This is especially true if you 
do not know the underlying Stream architecture!

Also note that you can use *any* StreamInterface implementation. It does 
not have to be the same implementation as the third-party code is using. If 
you cannot get access to a StreamFactoryInterface implementation and you 
cannot detect the constructor of the StreamInterface implementation, you 
can always chose to supply your own StreamInterface implementation instead. 
PSR-7 allows full mix-and-matching of implementations.

On Saturday, 12 October 2019 15:01:36 UTC+2, Anton Fedonjuk wrote:
>
> These  are not copies of the current StreamtInterface implementation 
> returned $this->request->getBody()
>
> I am trying to create a class/method that works with any third-party 
> RequestInterface implementation.
>
> I do not know the architecture of StreamtInterface implementation used in 
> the RequestInterface implementation.
>
> class Stream implements StreamtInterface
> {
>     protected $handler;
>    
>     public function __construct()
>     {
>         $this->handler = tmpfile();
>     }
>    
>     public function write($str)
>     {
>         fwrite($this->handler, $str);
>     }
> }
>
> class FilteredStream extends Stream
> {
>     protected $filter;
>    
>     public function __construct(callable $filter)
>     {
>         parent::__construct();
>         $this->filter = $filter;
>     }
>    
>     public function write($str)
>     {
>         parent::write(($this->filter)($str));
>     }
> }
>
> $request->withBody(new Stream());
> $myAbstractObject->setRequest($request);
> // in other case
> $request->withBody(new FilteredStream('myFilterFunct'));
> $myAbstractObject->setRequest($request);
>
>
>
> I think need add "truncate" or/and "withContents" methods to the 
> StreamInterface:
>
> interface StreamInterface
> {
>    /**
>     * Truncates a stream data to a given length.
>     *
>     * @see https://www.php.net/manual/en/function.ftruncate.php
>     *
>     * @param int $size The size to truncate to.
>     * @throws \InvalidArgumentException Size not integer or less zero.
>     * @throws \RuntimeException Not seekable.
>     */
>     public function truncate($size);
>
>     /**
>      * Returns new instance with the specified contents.
>      *
>      * @param string $str New contents.
>      * @return static New instance.
>      * @throws \RuntimeException On failure.
>      */
>     public function withContents($str);
> }
>
> class Stream implements StreamInterface
> {
>     public function truncate($size)
>     {
>         if (intval($size) != $size || $size < 0) {
>             throw new \InvalidArgumentException('Size must be not 
> negative integer');
>         }
>         if (! $this->isSeekable()) {
>             throw new \RuntimeException('Stream not seekable');
>         }
>         ftruncate($this->handle, $size);
>         if ($this->tell() > $size) {
>             // Fix cursor position
>             $this->seek(0, SEEK_END);
>         }
>     }
>    
>     public function withContents($str)
>     {
>         $clone = clone $this;
>         $clone->rewind();
>         $clone->write($str);
>         ftruncate($this->handle, strlen($str));
>         // or $this->truncate(strlen($str));
>         return $clone;
>     }
> }
>
>
>
> суббота, 12 октября 2019 г., 2:42:32 UTC+3 пользователь Matthew Weier 
> O'Phinney написал:
>>
>>
>>
>> On Thu, Oct 10, 2019, 6:42 PM Anton Fedonjuk <antonf...@gmail.com> wrote:
>>
>>> Class contains method setRequest(RequestInterface $request), his abstact 
>>> code:
>>> Request body already sets: $request->getBody()->write('...');
>>> I want send this request with other body content, but dont know how:
>>> 1. StreamInterface don't have methods as PHP ftruncate()
>>> 2. Constructor not defined in StreamInterface: can't use 
>>> $request->withBody() because dont know how create clone of that Steam 
>>> object without contents.
>>>
>>
>> To provide new contents, you provide a new body to the request, which 
>> means using a new StreamInterface instance. 
>>
>> To get a new StreamInterface instance, you either instantiate I've 
>> directly, per the PSR-7 *implementation* you have installed, or you use a 
>> PSR-17 StreamFactoryInterface instance to create it. This is something you 
>> would compose into your class as a constructor dependency. 
>>
>> In each case, you need an *implementation* of one or both of the specs 
>> installed, which is already likely the case. 
>>
>> In the first case, if using Diactoros, your code might look like this:
>>
>>     $request = $this->getRequest()
>>         ->withBody(new \Zend\Diactoros\Stream('php://temp', 'wb+'));
>>
>> In the second, it would look like this:
>>
>>     $request = $this->getRequest()
>>         ->withBody($this->streamFactory->createStream());
>>
>> From there, you can call on the stream's write() method. Alternately, if 
>> using the factory, create the entire stream *with contents* to save 
>> yourself the step. 
>>
>> Regarding your point that there is no StreamInterface constructor,
>> the constructor is not defined in any of the interfaces because:
>>
>> - We did not want to restrict how implementations wrote constructors.
>> - PHP ALWAYS allows classes implementing interfaces to override the 
>> signature of a constructor. 
>>
>> As such, you always have to look at how the *implementation* to determine 
>> the signature of constructors. 
>>
>> This is one large reason PSR-17 exists - to standardize how instances of 
>> psr-7 interfaces are created. 
>>
>> Regardless, each of the above examples give you an instance with an empty 
>> body. 
>>
>

-- 
You received this message because you are subscribed to the Google Groups "PHP 
Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to php-fig+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/php-fig/af12d6c6-d127-45e7-b43b-a7507c1abfcc%40googlegroups.com.

Reply via email to