Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Serialize into array #219

Merged
merged 11 commits into from
Apr 6, 2017
Merged
42 changes: 42 additions & 0 deletions doc/book/serialization.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Serialization

## String

At times, it's useful to either create a string representation of a message (serialization), or to
cast a string or stream message to an object (deserialization). This package provides features for
this in `Zend\Diactoros\Request\Serializer` and `Zend\Diactoros\Response\Serializer`; each provides
Expand All @@ -15,3 +17,43 @@ the following static methods:
The deserialization methods (`from*()`) will raise exceptions if errors occur while parsing the
message. The serialization methods (`toString()`) will raise exceptions if required data for
serialization is not present in the message instance.

## Array

This package also provides features for array serialization using
`Zend\Diactoros\Request\ArraySerializer` and `Zend\Diactoros\Response\ArraySerializer`; each provides
the following static methods:

- `fromArray(array $message)` will create either a `Request` or `Response` instance (based on the
serializer used) from the array message.
- `toArray(Psr\Http\Message\RequestInterface|Psr\Http\Message\ResponseInterface $message)` will
create either a array from the provided message.

The deserialization methods (`fromArray()`) will raise exceptions if errors occur while parsing the
message.

### Example usage

Array serialization can be usesful for log messages:

```php
class LoggerMiddleware
{
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;

public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$response = $next($request, $response);

$this->logger->debug('Request/Response', [
'request' => \Zend\Diactoros\Request\ArraySerializer::toArray($request),
'response' => \Zend\Diactoros\Response\ArraySerializer::toArray($response),
]);

return $response;
}
}
```
92 changes: 92 additions & 0 deletions src/Request/ArraySerializer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
* @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
*/

namespace Zend\Diactoros\Request;

use Psr\Http\Message\RequestInterface;
use UnexpectedValueException;
use Zend\Diactoros\Request;
use Zend\Diactoros\Stream;

/**
* Serialize or deserialize request messages.
*
* This class provides functionality for serializing a RequestInterface instance
* to an array, as well as the reverse operation of creating a Request instance
* from an array representing a message.
*/
final class ArraySerializer
{
/**
* Serialize a request message to an array.
*
* @param RequestInterface $request
*
* @return array
*/
public static function toArray(RequestInterface $request)
{
return [
'method' => $request->getMethod(),
'request_target' => $request->getRequestTarget(),
'uri' => (string) $request->getUri(),
'protocol_version' => $request->getProtocolVersion(),
'headers' => $request->getHeaders(),
'body' => (string) $request->getBody(),
];
}

/**
* Deserialize a request array to a request instance.
*
* @param array $serializedRequest
*
* @return Request
*
* @throws UnexpectedValueException when cannot deserialize response
*/
public static function fromArray(array $serializedRequest)
{
try {
$uri = self::getValueFromKey($serializedRequest, 'uri');
$method = self::getValueFromKey($serializedRequest, 'method');
$body = new Stream('php://memory', 'wb+');
$body->write(self::getValueFromKey($serializedRequest, 'body'));
$headers = self::getValueFromKey($serializedRequest, 'headers');
$requestTarget = self::getValueFromKey($serializedRequest, 'request_target');
$protocolVersion = self::getValueFromKey($serializedRequest, 'protocol_version');

return (new Request($uri, $method, $body, $headers))
->withRequestTarget($requestTarget)
->withProtocolVersion($protocolVersion);
} catch (\Exception $exception) {
throw new UnexpectedValueException('Cannot deserialize request', null, $exception);
}
}

/**
* @param array $data
* @param string $key
* @param string $message
*
* @return mixed
*
* @throws UnexpectedValueException
*/
private static function getValueFromKey(array $data, $key, $message = null)
{
if (isset($data[$key])) {
return $data[$key];
}
if ($message === null) {
$message = sprintf('Missing "%s" key in serialized request', $key);
}
throw new UnexpectedValueException($message);
}
}
91 changes: 91 additions & 0 deletions src/Response/ArraySerializer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
* @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
*/

namespace Zend\Diactoros\Response;

use Psr\Http\Message\ResponseInterface;
use UnexpectedValueException;
use Zend\Diactoros\Response;
use Zend\Diactoros\Stream;

/**
* Serialize or deserialize response messages.
*
* This class provides functionality for serializing a ResponseInterface instance
* to an array, as well as the reverse operation of creating a Request instance
* from an array representing a message.
*/
final class ArraySerializer
{
/**
* Serialize a response message to an array.
*
* @param ResponseInterface $response
*
* @return array
*/
public static function toArray(ResponseInterface $response)
{
return [
'status_code' => $response->getStatusCode(),
'reason_phrase' => $response->getReasonPhrase(),
'protocol_version' => $response->getProtocolVersion(),
'headers' => $response->getHeaders(),
'body' => (string) $response->getBody(),
];
}

/**
* Deserialize a response array to a response instance.
*
* @param array $serializedResponse
*
* @return Response
*
* @throws UnexpectedValueException when cannot deserialize response
*/
public static function fromArray(array $serializedResponse)
{
try {
$body = new Stream('php://memory', 'wb+');
$body->write(self::getValueFromKey($serializedResponse, 'body'));

$statusCode = self::getValueFromKey($serializedResponse, 'status_code');
$headers = self::getValueFromKey($serializedResponse, 'headers');
$protocolVersion = self::getValueFromKey($serializedResponse, 'protocol_version');
$reasonPhrase = self::getValueFromKey($serializedResponse, 'reason_phrase');

return (new Response($body, $statusCode, $headers))
->withProtocolVersion($protocolVersion)
->withStatus($statusCode, $reasonPhrase);
} catch (\Exception $exception) {
throw new UnexpectedValueException('Cannot deserialize response', null, $exception);
}
}

/**
* @param array $data
* @param string $key
* @param string $message
*
* @return mixed
*
* @throws UnexpectedValueException
*/
private static function getValueFromKey(array $data, $key, $message = null)
{
if (isset($data[$key])) {
return $data[$key];
}
if ($message === null) {
$message = sprintf('Missing "%s" key in serialized request', $key);
}
throw new UnexpectedValueException($message);
}
}
119 changes: 119 additions & 0 deletions test/Request/ArraySerializerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @see http://github.com/zendframework/zend-diactoros for the canonical source repository
* @copyright Copyright (c) 2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
*/

namespace ZendTest\Diactoros\Request;

use PHPUnit_Framework_TestCase as TestCase;
use Zend\Diactoros\Request;
use Zend\Diactoros\Request\ArraySerializer;
use Zend\Diactoros\Stream;
use Zend\Diactoros\Uri;

class ArraySerializerTest extends TestCase
{
public function testSerializeToArray()
{
$stream = new Stream('php://memory', 'wb+');
$stream->write('{"test":"value"}');

$request = (new Request())
->withMethod('POST')
->withUri(new Uri('http://example.com/foo/bar?baz=bat'))
->withAddedHeader('Accept', 'application/json')
->withAddedHeader('X-Foo-Bar', 'Baz')
->withAddedHeader('X-Foo-Bar', 'Bat')
->withBody($stream);

$message = ArraySerializer::toArray($request);

$this->assertSame([
'method' => 'POST',
'request_target' => '/foo/bar?baz=bat',
'uri' => 'http://example.com/foo/bar?baz=bat',
'protocol_version' => '1.1',
'headers' => [
'Host' => [
'example.com',
],
'Accept' => [
'application/json',
],
'X-Foo-Bar' => [
'Baz',
'Bat'
],
],
'body' => '{"test":"value"}',
], $message);
}

public function testDeserializeFromArray()
{
$serializedRequest = [
'method' => 'POST',
'request_target' => '/foo/bar?baz=bat',
'uri' => 'http://example.com/foo/bar?baz=bat',
'protocol_version' => '1.1',
'headers' => [
'Host' => [
'example.com',
],
'Accept' => [
'application/json',
],
'X-Foo-Bar' => [
'Baz',
'Bat'
],
],
'body' => '{"test":"value"}',
];

$message = ArraySerializer::fromArray($serializedRequest);

$stream = new Stream('php://memory', 'wb+');
$stream->write('{"test":"value"}');

$request = (new Request())
->withMethod('POST')
->withUri(new Uri('http://example.com/foo/bar?baz=bat'))
->withAddedHeader('Accept', 'application/json')
->withAddedHeader('X-Foo-Bar', 'Baz')
->withAddedHeader('X-Foo-Bar', 'Bat')
->withBody($stream);

$this->assertSame(Request\Serializer::toString($request), Request\Serializer::toString($message));
}

public function testMissingBodyParamInSerializedRequestThrowsException()
{
$serializedRequest = [
'method' => 'POST',
'request_target' => '/foo/bar?baz=bat',
'uri' => 'http://example.com/foo/bar?baz=bat',
'protocol_version' => '1.1',
'headers' => [
'Host' => [
'example.com',
],
'Accept' => [
'application/json',
],
'X-Foo-Bar' => [
'Baz',
'Bat'
],
],
];

$this->setExpectedException('UnexpectedValueException');

ArraySerializer::fromArray($serializedRequest);
}
}
Loading