Skip to content

Commit f918438

Browse files
authored
Merge pull request #33 from karriereat/feature/v4
Version 4
2 parents b059976 + 5c590ff commit f918438

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1367
-888
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ indent_size = 2
2929

3030
[composer.json]
3131
indent_style = space
32-
indent_size = 4
32+
indent_size = 4

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ jobs:
3232
run: composer install --prefer-dist --no-progress --no-suggest
3333

3434
- name: Run test suite
35-
run: vendor/bin/phpspec run --config=phpspec-coverage.yml
35+
run: composer coverage
3636

3737
- uses: codecov/codecov-action@v1

.phpspec-watcher.yml

Lines changed: 0 additions & 11 deletions
This file was deleted.

composer.json

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,22 @@
1515
"Karriere\\JsonDecoder\\": "src/"
1616
}
1717
},
18+
"autoload-dev": {
19+
"psr-4": {
20+
"Karriere\\JsonDecoder\\Tests\\": "tests/"
21+
}
22+
},
1823
"require": {
19-
"php": ">=7.2"
24+
"php": ">=7.2",
25+
"php-di/phpdoc-reader": "^2.1"
2026
},
2127
"require-dev": {
22-
"karriere/code-quality": "^5.0",
23-
"sebastian/comparator": "^3.0",
24-
"sebastian/exporter": "^3.0",
25-
"sebastian/recursion-context": "^3.0"
28+
"phpunit/phpunit": "^8.0 || ^9.0",
29+
"squizlabs/php_codesniffer": "^3.0"
2630
},
2731
"scripts": {
28-
"test": "vendor/bin/phpspec run",
32+
"test": "vendor/bin/phpunit",
33+
"coverage": "vendor/bin/phpunit --coverage-clover coverage.xml",
2934
"lint": "vendor/bin/phpcs src/ --standard=PSR12"
3035
}
3136
}

phpspec-coverage.yml

Lines changed: 0 additions & 17 deletions
This file was deleted.

phpspec.yml

Lines changed: 0 additions & 9 deletions
This file was deleted.

phpunit.xml.dist

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit bootstrap="vendor/autoload.php"
3+
backupGlobals="false"
4+
backupStaticAttributes="false"
5+
colors="true"
6+
verbose="true"
7+
convertErrorsToExceptions="true"
8+
convertNoticesToExceptions="true"
9+
convertWarningsToExceptions="true"
10+
processIsolation="false"
11+
stopOnFailure="false">
12+
<testsuites>
13+
<testsuite name="JSON Decoder Test Suite">
14+
<directory>tests</directory>
15+
</testsuite>
16+
</testsuites>
17+
<filter>
18+
<whitelist>
19+
<directory suffix=".php">src/</directory>
20+
</whitelist>
21+
</filter>
22+
</phpunit>

readme.md

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,30 @@
44
![](https://github.com/karriereat/json-decoder/workflows/lint/badge.svg)
55
[![codecov](https://codecov.io/gh/karriereat/json-decoder/branch/master/graph/badge.svg)](https://codecov.io/gh/karriereat/json-decoder)
66

7-
87
# JsonDecoder for PHP
98

109
This package contains a JsonDecoder implementation that allows you to convert your JSON data into php class objects other than `stdclass`.
1110

1211
## Installation
12+
1313
You can install the package via composer
14+
1415
```
1516
composer require karriere/json-decoder
1617
```
1718

1819
## Usage
19-
By default all public properties of the class will be inspected. For all properties that have a JSON key with the same name the according value will be set.
20+
21+
By default the Decoder will iterate over all JSON fields defined and will try to set this values on the given class type instance. This change in behavior allows the use of `json-decoder` on classes that use the **magic** `__get` and `__set` functions like Laravel's Eloquent models.
22+
23+
If a property equally named like the JSON field is found or a explicit `Binding` is defined for the JSON field it will be decoded into the defined place. Otherwise the property will just be created and assigned.
24+
25+
The `JsonDecoder` class can receive one parameter called `shouldAutoCase`. If set to true it will try to find the camel-case version from either snake-case or kebap-case automatically if no other binding was registered for the field and it will use an `AliasBinding` if one of the variants can be found.
2026

2127
### A simple example
28+
2229
Assume you have a class `Person` that looks like this:
30+
2331
```php
2432
class Person
2533
{
@@ -37,8 +45,38 @@ $jsonData = '{"id": 1, "name": "John Doe"}';
3745
$person = $jsonDecoder->decode($jsonData, Person::class);
3846
```
3947

48+
### More complex use case
49+
50+
Let's extend the previous example with a property called address. This address field should contain an instance of `Address`. In the prior versions of `json-decoder` it was necessary to define a custom `Transformer` to be able to handle this situation. As of version 4 you can use the introduced method `scanAndRegister` to automatically generate the transformer based on class annotations.
51+
52+
```php
53+
class Person
54+
{
55+
public $id;
56+
public $name;
57+
58+
/**
59+
* @var Address
60+
*/
61+
public $address;
62+
}
63+
```
64+
65+
For this class definition we can decode JSON data as follows:
66+
67+
```php
68+
$jsonDecoder = new JsonDecoder();
69+
$jsonDecoder->scanAndRegister(Person::class);
70+
71+
$jsonData = '{"id": 1, "name": "John Doe", "address": {"street": "Samplestreet", "city": "Samplecity"}}';
72+
73+
$person = $jsonDecoder->decode($jsonData, Person::class);
74+
```
75+
4076
### Defining a Transformer
41-
Let's extend the previous example with a property called address. This address field should contain an instance of `Address`.
77+
78+
If you don't use annotations or need a more flexible `Transformer` you can also create a custom transformer. Let's look at the previous example without annotation.
79+
4280
```php
4381
class Person
4482
{
@@ -52,8 +90,9 @@ To be able to transform the address data into an `Address` class object you need
5290

5391
The transformer interface defines two methods:
5492

55-
* register: here you register your field, array, alias and callback bindings
56-
* transforms: gives you the full qualified class name e.g.: Your\Namespace\Class
93+
- register: here you register your field, array, alias and callback bindings
94+
- transforms: gives you the full qualified class name e.g.: Your\Namespace\Class
95+
5796
```php
5897
class PersonTransformer implements Transformer
5998
{
@@ -70,6 +109,7 @@ class PersonTransformer implements Transformer
70109
```
71110

72111
After registering the transformer the `JsonDecoder` will use the defined transformer:
112+
73113
```php
74114
$jsonDecoder = new JsonDecoder();
75115
$jsonDecoder->register(new PersonTransformer());
@@ -80,11 +120,11 @@ $person = $jsonDecoder->decode($jsonData, Person::class);
80120
```
81121

82122
### Handling private and protected properties
83-
The `JsonDecoder` class accepts two boolean constructor parameters to enable the handling of private and protected properties.
84123

85-
To do so a so called `PropertyAccessor` will be installed and on property set the proxy will set the property to accessible, set the according value and then will set the property to not accessible again.
124+
As of version 4 the `JsonDecoder` class will handle `private` and `protected` properties out of the box.
86125

87126
### Transforming an array of elements
127+
88128
If your JSON contains an array of elements at the root level you can use the `decodeMultiple` method to transform the JSON data into an array of class type objects.
89129

90130
```php
@@ -98,52 +138,65 @@ $personArray = $jsonDecoder->decodeMultiple($jsonData, Person::class);
98138
## Documentation
99139

100140
### Transformer Bindings
141+
101142
The following `Binding` implementations are available
102143

103-
* [FieldBinding](#fieldbinding)
104-
* [ArrayBinding](#arraybinding)
105-
* [AliasBinding](#aliasbinding)
106-
* [DateTimeBinding](#datetimebinding)
107-
* [CallbackBinding](#callbackbinding)
144+
- [FieldBinding](#fieldbinding)
145+
- [ArrayBinding](#arraybinding)
146+
- [AliasBinding](#aliasbinding)
147+
- [DateTimeBinding](#datetimebinding)
148+
- [CallbackBinding](#callbackbinding)
108149

109150
#### FieldBinding
151+
110152
Defines a JSON field to property binding for the given type.
111153

112154
**Signature:**
155+
113156
```php
114157
new FieldBinding($property, $jsonField, $type);
115158
```
159+
116160
This defines a field mapping for the property `$property` to a class instance of type `$type` with data in `$jsonField`.
117161

118162
#### ArrayBinding
163+
119164
Defines a array field binding for the given type.
120165

121166
**Signature:**
167+
122168
```php
123169
new ArrayBinding($property, $jsonField, $type);
124170
```
171+
125172
This defines a field mapping for the property `$property` to an array of class instance of type `$type` with data in `$jsonField`.
126173

127174
#### AliasBinding
175+
128176
Defines a JSON field to property binding.
129177

130178
**Signature:**
179+
131180
```php
132181
new AliasBinding($property, $jsonField);
133182
```
134183

135184
#### DateTimeBinding
185+
136186
Defines a JSON field to property binding and converts the given string to a `DateTime` instance.
137187

138188
**Signature:**
189+
139190
```php
140191
new new DateTimeBinding($property, $jsonField, $isRequired = false, $dateTimeFormat = DateTime::ATOM);
141192
```
142193

143194
#### CallbackBinding
195+
144196
Defines a property binding that gets the callback result set as its value.
145197

146198
**Signature:**
199+
147200
```php
148201
new CallbackBinding($property, $callback);
149202
```

src/Binding.php

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,27 +47,35 @@ public function __construct($property, $jsonField, $type, $isRequired = false)
4747
*
4848
* @return bool
4949
*/
50-
public function validate($jsonData): bool
50+
public function validate(array $jsonData): bool
5151
{
5252
return !$this->isRequired || array_key_exists($this->jsonField, $jsonData);
5353
}
5454

5555
/**
5656
* @return string the name of the property to bind
5757
*/
58-
public function property()
58+
public function property(): string
5959
{
6060
return $this->property;
6161
}
6262

63+
/**
64+
* @return string the name of the json field to bind
65+
*/
66+
public function jsonField(): string
67+
{
68+
return $this->jsonField ?? $this->property;
69+
}
70+
6371
/**
6472
* executes the defined binding method on the class instance.
6573
*
66-
* @param JsonDecoder $jsonDecoder
67-
* @param mixed $jsonData
68-
* @param PropertyAccessor $propertyAccessor the class instance to bind to
74+
* @param JsonDecoder $jsonDecoder
75+
* @param mixed $jsonData
76+
* @param Property $property the class instance to bind to
6977
*
7078
* @return mixed
7179
*/
72-
abstract public function bind($jsonDecoder, $jsonData, $propertyAccessor);
80+
abstract public function bind(JsonDecoder $jsonDecoder, ?array $jsonData, Property $property);
7381
}

src/Bindings/AliasBinding.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace Karriere\JsonDecoder\Bindings;
44

55
use Karriere\JsonDecoder\Binding;
6+
use Karriere\JsonDecoder\JsonDecoder;
7+
use Karriere\JsonDecoder\Property;
68

79
class AliasBinding extends Binding
810
{
@@ -13,18 +15,18 @@ class AliasBinding extends Binding
1315
* @param string $jsonField the json field
1416
* @param bool $isRequired defines if the field value is required during decoding
1517
*/
16-
public function __construct($property, $jsonField, $isRequired = false)
18+
public function __construct(string $property, string $jsonField, bool $isRequired = false)
1719
{
1820
parent::__construct($property, $jsonField, null, $isRequired);
1921
}
2022

2123
/**
2224
* {@inheritdoc}
2325
*/
24-
public function bind($jsonDecoder, $jsonData, $propertyAccessor)
26+
public function bind(JsonDecoder $jsonDecoder, ?array $jsonData, Property $property)
2527
{
2628
if (array_key_exists($this->jsonField, $jsonData)) {
27-
$propertyAccessor->set($jsonData[$this->jsonField]);
29+
$property->set($jsonData[$this->jsonField]);
2830
}
2931
}
3032
}

0 commit comments

Comments
 (0)