Skip to content

Commit de02ef9

Browse files
committed
add binding structure
1 parent 7b43425 commit de02ef9

File tree

13 files changed

+594
-153
lines changed

13 files changed

+594
-153
lines changed

readme.md

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ composer require karriere/json-decoder
1212
```
1313

1414
## Usage
15-
By default all not defined fields of the input json will be stored in a class property with the same name.
15+
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.
1616

1717
### A simple example
1818
Assume you have a class `Person` that looks like this:
@@ -28,10 +28,98 @@ The following code will transform the given json data into an instance of `Perso
2828
```php
2929
$jsonDecoder = new JsonDecoder();
3030
$jsonData = '{"id": 1, "name": "John Doe"}';
31-
$arrayData = json_decode($jsonData, true);
3231

33-
$person = $jsonDecoder->decode($arrayData, Person::class);
32+
$person = $jsonDecoder->decode($jsonData, Person::class);
33+
```
34+
35+
### Defining a Transformer
36+
Let's extend the previous example with a property called address. This address field should contain an instance of `Address`.
37+
```php
38+
class Person {
39+
public $id;
40+
public $name;
41+
public $address;
42+
}
43+
```
44+
45+
To be able to transform the address data into an `Address` class object you need to define a transformer for `Person`:
46+
47+
The transformer interface defines two methods:
48+
49+
* register: here you register your field, array, alias and callback bindings
50+
* transforms: gives you the full qualified class name e.g.: Your\Namespace\Class
51+
```php
52+
class PersonTransformer implements Transformer {
53+
54+
public function register(ClassBindings $classBindings)
55+
{
56+
$classBindings->register(new FieldBinding('address', 'address', Address::class);
57+
}
58+
59+
public function transforms()
60+
{
61+
return Person::class;
62+
}
63+
}
64+
```
65+
66+
After registering the transformer the `JsonDecoder` will use the defined transformer:
67+
```php
68+
$jsonDecoder = new JsonDecoder();
69+
$jsonDecoder->register(new PersonTransformer());
70+
71+
$jsonData = '{"id": 1, "name": "John Doe"}';
72+
73+
$person = $jsonDecoder->decode($jsonData, Person::class);
74+
```
75+
76+
### Handling private and protected properties
77+
The `JsonDecoder` class accepts two boolean constructor parameters to enable the handling of private and protected properties.
78+
79+
To do so a so called `AccessProxy` 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.
80+
81+
## Documentation
82+
83+
### Transformer Bindings
84+
The following `Binding` implementations are available
85+
86+
* [FieldBinding](#fieldbinding)
87+
* [ArrayBinding](#arraybinding)
88+
* [AliasBinding](#aliasbinding)
89+
* [CallbackBinding](#callbackbinding)
90+
91+
#### FieldBinding
92+
Defines a json field to property binding for the given type.
93+
94+
**Signature:**
95+
```php
96+
new FieldBinding($property, $jsonField, $type);
97+
```
98+
This defines a field mapping for the property `$property` to a class instance of type `$type` with data in `$jsonField`.
3499

100+
#### ArrayBinding
101+
Defines a array field binding for the given type.
102+
103+
**Signature:**
104+
```php
105+
new ArrayBinding($property, $jsonField, $type);
106+
```
107+
This defines a field mapping for the property `$property` to an array of class instance of type `$type` with data in `$jsonField`.
108+
109+
### AliasBinding
110+
Defines a json field to property binding.
111+
112+
**Signature:**
113+
```php
114+
new AliasBinding($property, $jsonField);
115+
```
116+
117+
#### CallbackBinding
118+
Defines a property binding that gets the callback result set as its value.
119+
120+
**Signature:**
121+
```php
122+
new CallbackBinding($property, $callback);
35123
```
36124

37125
## License

src/AccessProxy.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace Karriere\JsonDecoder;
4+
5+
use ReflectionProperty;
6+
7+
class AccessProxy
8+
{
9+
/**
10+
* @var ReflectionProperty
11+
*/
12+
private $property;
13+
14+
private $instance;
15+
16+
public function __construct(ReflectionProperty $property, $instance)
17+
{
18+
$this->property = $property;
19+
$this->instance = $instance;
20+
}
21+
22+
public function __set($name, $value)
23+
{
24+
if ($name === $this->property->getName()) {
25+
$this->property->setAccessible(true);
26+
$this->property->setValue($this->instance, $value);
27+
$this->property->setAccessible(false);
28+
}
29+
}
30+
}

src/Binding.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Karriere\JsonDecoder;
4+
5+
interface Binding
6+
{
7+
/**
8+
* executes the defined binding method on the class instance
9+
*
10+
* @param JsonDecoder $jsonDecoder
11+
* @param mixed $jsonData
12+
* @param mixed $instance the class instance to bind to
13+
* @return mixed
14+
*/
15+
public function bind($jsonDecoder, $jsonData, $instance);
16+
17+
/**
18+
* @return string the name of the property to bind
19+
*/
20+
public function property();
21+
}

src/Bindings/AliasBinding.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
namespace Karriere\JsonDecoder\Bindings;
4+
5+
use Karriere\JsonDecoder\Binding;
6+
use Karriere\JsonDecoder\Exceptions\JsonValueException;
7+
use Karriere\JsonDecoder\JsonDecoder;
8+
9+
class AliasBinding implements Binding
10+
{
11+
/**
12+
* @var string
13+
*/
14+
private $property;
15+
16+
/**
17+
* @var string
18+
*/
19+
private $jsonField;
20+
21+
/**
22+
* AliasBinding constructor.
23+
*
24+
* @param string $property the property to bind to
25+
* @param string $jsonField the json field
26+
*/
27+
public function __construct($property, $jsonField)
28+
{
29+
$this->property = $property;
30+
$this->jsonField = $jsonField;
31+
}
32+
33+
/**
34+
* executes the defined binding method on the class instance
35+
*
36+
* @param JsonDecoder $jsonDecoder
37+
* @param mixed $jsonData
38+
* @param mixed $instance the class instance to bind to
39+
* @return mixed
40+
* @throws JsonValueException if given json field is not available
41+
*/
42+
public function bind($jsonDecoder, $jsonData, $instance)
43+
{
44+
if (!array_key_exists($this->jsonField, $jsonData)) {
45+
throw new JsonValueException(
46+
sprintf('the value "%s" for property "%s" does not exist', $this->jsonField, $this->property)
47+
);
48+
}
49+
50+
$instance->{$this->property} = $jsonData[$this->jsonField];
51+
}
52+
53+
/**
54+
* @return string the name of the property to bind
55+
*/
56+
public function property()
57+
{
58+
return $this->property;
59+
}
60+
}

src/Bindings/ArrayBinding.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace Karriere\JsonDecoder\Bindings;
4+
5+
use Karriere\JsonDecoder\Binding;
6+
use Karriere\JsonDecoder\Exceptions\JsonValueException;
7+
use Karriere\JsonDecoder\JsonDecoder;
8+
9+
class ArrayBinding implements Binding
10+
{
11+
/**
12+
* @var string
13+
*/
14+
private $property;
15+
16+
/**
17+
* @var string
18+
*/
19+
private $jsonField;
20+
21+
/**
22+
* @var string
23+
*/
24+
private $type;
25+
26+
/**
27+
* ArrayBinding constructor.
28+
*
29+
* @param string $property the property to bind to
30+
* @param string $jsonField the json field
31+
* @param string $type the desired type of the property
32+
*/
33+
public function __construct($property, $jsonField, $type)
34+
{
35+
$this->property = $property;
36+
$this->jsonField = $jsonField;
37+
$this->type = $type;
38+
}
39+
40+
/**
41+
* executes the defined binding method on the class instance
42+
*
43+
* @param JsonDecoder $jsonDecoder
44+
* @param array $jsonData
45+
* @param mixed $instance the class instance to bind to
46+
* @return mixed
47+
* @throws JsonValueException if given json field is not available
48+
*/
49+
public function bind($jsonDecoder, $jsonData, $instance)
50+
{
51+
if (!array_key_exists($this->jsonField, $jsonData)) {
52+
throw new JsonValueException(
53+
sprintf('the value "%s" for property "%s" does not exist', $this->jsonField, $this->property)
54+
);
55+
}
56+
57+
$data = $jsonData[$this->jsonField];
58+
$values = [];
59+
60+
foreach ($data as $item) {
61+
$values[] = $jsonDecoder->decodeArray($item, $this->type);
62+
}
63+
64+
$instance->{$this->property} = $values;
65+
}
66+
67+
/**
68+
* @return string the name of the property to bind
69+
*/
70+
public function property()
71+
{
72+
return $this->property;
73+
}
74+
}

src/Bindings/CallbackBinding.php

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
namespace Karriere\JsonDecoder\Bindings;
4+
5+
use Karriere\JsonDecoder\Binding;
6+
use Karriere\JsonDecoder\JsonDecoder;
7+
8+
class CallbackBinding implements Binding
9+
{
10+
/**
11+
* @var string
12+
*/
13+
private $property;
14+
15+
/**
16+
* @var callable
17+
*/
18+
private $callback;
19+
20+
/**
21+
* CallbackBinding constructor.
22+
* @param string $property
23+
* @param callable $callback
24+
*/
25+
public function __construct($property, $callback)
26+
{
27+
$this->property = $property;
28+
$this->callback = $callback;
29+
}
30+
31+
/**
32+
* executes the defined binding method on the class instance
33+
*
34+
* @param JsonDecoder $jsonDecoder
35+
* @param mixed $jsonData
36+
* @param mixed $instance the class instance to bind to
37+
* @return mixed
38+
*/
39+
public function bind($jsonDecoder, $jsonData, $instance)
40+
{
41+
$instance->{$this->property} = $this->callback->__invoke($jsonData);
42+
}
43+
44+
/**
45+
* @return string the name of the property to bind
46+
*/
47+
public function property()
48+
{
49+
return $this->property;
50+
}
51+
}

0 commit comments

Comments
 (0)