Skip to content

Commit 1b61f76

Browse files
authored
Merge pull request #1 from Lendable/lib
Initial implementation
2 parents a942c03 + df46a34 commit 1b61f76

10 files changed

+255
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/vendor
2+
phpunit.xml
3+
composer.lock

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,10 @@
11
Lendable JSON
22
=============
3+
Provides an object oriented interface for handling json in php.
4+
5+
Instead of failing silently, this lib will throw exceptions on json errors.
6+
7+
## Installation
8+
```bash
9+
composer require lendable/json-serializer
10+
```

composer.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "lendable/json-serializer",
3+
"description": "JSON serializer/deserializer with an OOP interface",
4+
"type": "library",
5+
"authors": [
6+
{
7+
"name": "Lendable Ltd",
8+
"email": "[email protected]"
9+
}
10+
],
11+
"autoload": {
12+
"psr-4": {
13+
"Lendable\\Json\\": "lib/"
14+
}
15+
},
16+
"autoload-dev": {
17+
"psr-4": {
18+
"Tests\\Lendable\\Json\\Unit\\": "tests/unit/"
19+
}
20+
},
21+
"require": {
22+
"php": ">=7.1",
23+
"ext-json": "*"
24+
},
25+
"require-dev": {
26+
"phpunit/phpunit": "^7.4"
27+
}
28+
}

lib/DeserializationFailed.php

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Lendable\Json;
6+
7+
final class DeserializationFailed extends \RuntimeException implements Failure
8+
{
9+
public function __construct(int $errorCode, string $errorMessage, ?\Throwable $previous = null)
10+
{
11+
parent::__construct(
12+
\sprintf(
13+
'Failed to deserialize data from JSON. Error code: %d, error message: %s.',
14+
$errorCode,
15+
$errorMessage
16+
),
17+
$errorCode,
18+
$previous
19+
);
20+
}
21+
}

lib/Failure.php

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Lendable\Json;
6+
7+
interface Failure extends \Throwable
8+
{
9+
}

lib/InvalidDeserializedData.php

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Lendable\Json;
6+
7+
class InvalidDeserializedData extends \RuntimeException implements Failure
8+
{
9+
public function __construct(string $unexpectedType)
10+
{
11+
parent::__construct(sprintf('Expected array when deserializing JSON, got "%s".', $unexpectedType));
12+
}
13+
}

lib/SerializationFailed.php

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Lendable\Json;
6+
7+
final class SerializationFailed extends \RuntimeException implements Failure
8+
{
9+
public function __construct(int $errorCode, string $errorMessage, ?\Throwable $previous = null)
10+
{
11+
parent::__construct(
12+
\sprintf(
13+
'Failed to serialize data to JSON. Error code: %d, error message: %s.',
14+
$errorCode,
15+
$errorMessage
16+
),
17+
$errorCode,
18+
$previous
19+
);
20+
}
21+
}

lib/Serializer.php

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Lendable\Json;
6+
7+
final class Serializer
8+
{
9+
/**
10+
* @throws SerializationFailure
11+
*/
12+
public function serialize(array $data): string
13+
{
14+
$serialized = \json_encode($data);
15+
16+
if (\json_last_error() !== JSON_ERROR_NONE) {
17+
throw new SerializationFailed(\json_last_error(), \json_last_error_msg());
18+
}
19+
20+
\assert(\is_string($serialized));
21+
22+
return $serialized;
23+
}
24+
25+
/**
26+
* @throws DeserializationFailure
27+
* @throws InvalidDeserializedData
28+
*/
29+
public function deserialize(string $json): array
30+
{
31+
$data = \json_decode($json, true);
32+
33+
if (\json_last_error() !== JSON_ERROR_NONE) {
34+
throw new DeserializationFailed(\json_last_error(), \json_last_error_msg());
35+
}
36+
37+
if (!\is_array($data)) {
38+
throw new InvalidDeserializedData(\gettype($data));
39+
}
40+
41+
return $data;
42+
}
43+
}

phpunit.xml.dist

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit colors="true"
3+
convertErrorsToExceptions="true"
4+
convertNoticesToExceptions="true"
5+
convertWarningsToExceptions="true"
6+
processIsolation="false"
7+
stopOnFailure="false"
8+
bootstrap="vendor/autoload.php">
9+
10+
<php>
11+
<ini name="error_reporting" value="E_ALL" />
12+
</php>
13+
14+
<testsuites>
15+
<testsuite name="unit">
16+
<directory>./tests/unit/</directory>
17+
</testsuite>
18+
</testsuites>
19+
20+
<filter>
21+
<whitelist>
22+
<directory>./lib/</directory>
23+
</whitelist>
24+
</filter>
25+
26+
</phpunit>

tests/unit/SerializerTest.php

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Lendable\Json\Unit;
6+
7+
use Lendable\Json\DeserializationFailed;
8+
use Lendable\Json\InvalidDeserializedData;
9+
use Lendable\Json\SerializationFailed;
10+
use Lendable\Json\Serializer;
11+
use PHPUnit\Framework\TestCase;
12+
13+
/**
14+
* @covers \Lendable\Json\Serializer
15+
* @covers \Lendable\Json\SerializationFailed
16+
* @covers \Lendable\Json\DeserializationFailed
17+
* @covers \Lendable\Json\InvalidDeserializedData
18+
*/
19+
final class SerializerTest extends TestCase
20+
{
21+
/**
22+
* @var Serializer
23+
*/
24+
private $serializer;
25+
26+
/**
27+
* @test
28+
*/
29+
public function it_can_serialize_an_array_of_scalars_to_json(): void
30+
{
31+
$result = $this->serializer->serialize(['foo' => 'bar', 'baz' => [1.03, true, 'foobar']]);
32+
33+
$this->assertSame('{"foo":"bar","baz":[1.03,true,"foobar"]}', $result);
34+
}
35+
36+
/**
37+
* @test
38+
*/
39+
public function it_throws_when_serializing_if_an_error_encountered(): void
40+
{
41+
$this->expectException(SerializationFailed::class);
42+
$this->expectExceptionMessage('Failed to serialize data to JSON. Error code: 5, error message: Malformed UTF-8 characters, possibly incorrectly encoded.');
43+
44+
$this->serializer->serialize(["\xf0\x28\x8c\xbc" => 'bar']);
45+
}
46+
47+
/**
48+
* @test
49+
*/
50+
public function it_can_deserialize_from_a_json_string_to_php_scalars(): void
51+
{
52+
$result = $this->serializer->deserialize('{"foo":"bar","baz":[1.03,true,"foobar"]}');
53+
54+
$this->assertSame(['foo' => 'bar', 'baz' => [1.03, true, 'foobar']], $result);
55+
}
56+
57+
/**
58+
* @test
59+
*/
60+
public function it_throws_when_deserializing_if_an_error_encountered(): void
61+
{
62+
$this->expectException(DeserializationFailed::class);
63+
$this->expectExceptionMessage('Failed to deserialize data from JSON. Error code: 4, error message: Syntax error.');
64+
65+
$this->serializer->deserialize('{"unclosed":"bad","object":"json"');
66+
}
67+
68+
/**
69+
* @test
70+
*/
71+
public function it_throws_when_deserializing_if_the_result_is_not_an_array(): void
72+
{
73+
$this->expectExceptionMessage(InvalidDeserializedData::class);
74+
$this->expectExceptionMessage('Expected array when deserializing JSON, got "boolean".');
75+
76+
$this->serializer->deserialize('true');
77+
}
78+
79+
protected function setUp(): void
80+
{
81+
$this->serializer = new Serializer();
82+
}
83+
}

0 commit comments

Comments
 (0)