jsonSerialize
Ik gebruik voor het eerst jsonSerialize in een model class. Omdat ik een customer object moet creeeren. Die ik meestuur met een api. In de class gebruik ik uiteraard de functie jsonSerlize. Die automatisch wordt aangeroepen bij `new Customer($data)`. Toch vraag ik me af, waarom ik met het implementeren van dit, nog steeds `json_encode(new Customer($data))` moet doen.
Ik verwacht namelijk, dat het object wat in php gemaakt word al een json object is, anders snap ik die hele class implementatie verkeerd.
jsonSerialize() bepaalt wat er wordt geserialiseerd en json_encode() bepaalt hoe dat wordt geserialiseerd. jsonSerialize() selecteert alleen de data in een array en json_encode() regelt de datastructuur en datacodering van een string.
Ward van der Put op 27/05/2022 13:12:45:
jsonSerialize() bepaalt wat er wordt geserialiseerd en json_encode() bepaalt hoe dat wordt geserialiseerd. jsonSerialize() selecteert alleen de data in een array en json_encode() regelt de datastructuur en datacodering van een string.
Ah oke. Ik snap alleen het woordje geserialiseerd niet zo goed. Ligt aan mij hoor. Overigens vind ik dit er met json_encode lelijk uitzien. Aangezien die api alleen maar JSON kan ontvangen, heb ik json_encode in mijn api class om het te ontvangen array object gezet, zodat altijd als je een array mee stuurt, dat dat json word.
serialiseren bedoelen we het omzetten van een dataobject in een vorm die geschikt is voor sequentiële verwerking. Bij JSON is dat een serie karakters in UTF-8 en dat is gewoon een ‘tekenreeks’ of string in PHP.
json_encode() voor JSON is vergelijkbaar met serialize() voor PHP: beide creëren een stringrepresentatie van een object. jsonSerialize() geeft je daarbij een voorbereidende fase voor het selecteren van de elementen van het dataobject die je wilt meenemen in de serialisatie. jsonSerialize() voor json_encode() is vergelijkbaar met de magische methode __serialize() voor serialize().
Je gebruikt jsonSerialize() in de praktijk vooral om een interne datastructuur in PHP te mappen naar een API in JSON-LD, bijvoorbeeld:
Dat geeft je in JSON-LD:
Met json_encode() voor JSON is vergelijkbaar met serialize() voor PHP: beide creëren een stringrepresentatie van een object. jsonSerialize() geeft je daarbij een voorbereidende fase voor het selecteren van de elementen van het dataobject die je wilt meenemen in de serialisatie. jsonSerialize() voor json_encode() is vergelijkbaar met de magische methode __serialize() voor serialize().
Je gebruikt jsonSerialize() in de praktijk vooral om een interne datastructuur in PHP te mappen naar een API in JSON-LD, bijvoorbeeld:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
declare(strict_types=1);
class Person implements \JsonSerializable
{
public ?string $firstName = null;
public ?string $lastName = null;
public function jsonSerialize(): array
{
$result = [
'@context' => 'https://schema.org',
'@type' => 'Person',
];
if ($this->firstName !== null && $this->lastName !== null) {
$result['name'] = $this->firstName . ' ' . $this->lastName;
}
return $result;
}
}
$person = new Person();
$person->firstName = 'Jane';
$person->lastName = 'Doe';
$json = json_encode($person, \JSON_UNESCAPED_SLASHES);
var_dump($json);
?>
declare(strict_types=1);
class Person implements \JsonSerializable
{
public ?string $firstName = null;
public ?string $lastName = null;
public function jsonSerialize(): array
{
$result = [
'@context' => 'https://schema.org',
'@type' => 'Person',
];
if ($this->firstName !== null && $this->lastName !== null) {
$result['name'] = $this->firstName . ' ' . $this->lastName;
}
return $result;
}
}
$person = new Person();
$person->firstName = 'Jane';
$person->lastName = 'Doe';
$json = json_encode($person, \JSON_UNESCAPED_SLASHES);
var_dump($json);
?>
Dat geeft je in JSON-LD:
Ik gebruik het inderdaad voor een api. Ik was bezig met een WP plugin, en in de core file handel ik meestal de ajax calls af, met PHP. Maar op het moment dat ik daar een customer object/array aanmaakte, werd de code zo rommelig en veel. Daarom besloot ik om meer met classes te werken, 1 die de customer valideert, en 1 die het object aanmaakt, en een object terug geeft, hierdoor kwam ik terecht bij JsonSerliaze.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
declare(strict_types=1);
class Person implements \JsonSerializable
{
public ?string $firstName = null;
public ?string $lastName = null;
public function jsonSerialize(): array
{
$result = [
'@context' => 'https://schema.org',
'@type' => 'Person',
];
return $result + $this->toArray();
}
public function toArray(): array
{
$result = get_object_vars($this);
$result = array_filter($result);
return $result;
}
}
$person = new Person();
$person->firstName = 'Jane';
$person->lastName = 'Doe';
$json = json_encode($person, \JSON_UNESCAPED_SLASHES);
var_dump($json);
?>
declare(strict_types=1);
class Person implements \JsonSerializable
{
public ?string $firstName = null;
public ?string $lastName = null;
public function jsonSerialize(): array
{
$result = [
'@context' => 'https://schema.org',
'@type' => 'Person',
];
return $result + $this->toArray();
}
public function toArray(): array
{
$result = get_object_vars($this);
$result = array_filter($result);
return $result;
}
}
$person = new Person();
$person->firstName = 'Jane';
$person->lastName = 'Doe';
$json = json_encode($person, \JSON_UNESCAPED_SLASHES);
var_dump($json);
?>
De array uit toArray() kun je daarnaast gebruiken als key/value pairs voor een rij in een databasetabel.
Toevoeging op 31/05/2022 09:46:30:
Ik heb nog een ander vraagje wat betreft het jsonSerializen.
Ik heb hier dus die Customer class:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php
class Customer implements JsonSerializable {
public $firstName;
public $middleName;
public $lastName;
public $email;
public $country = 'Nederland';
public $phoneMobile;
public $gender;
public $zipcode;
public $city;
public $address;
public $houseNumber;
public $houseNumberExtension;
public $birthDate;
public $iban;
public $language = 'nl_NL';
public $setAVG;
public function __construct($data){
$this->firstName = $data['fname'];
$this->middleName = $data['prefix'];
$this->lastName = $data['lname'];
$this->email = $data['email'];
$this->phoneMobile = $data['phone'];
$this->gender = $data['gender'];
$this->zipcode = $data['zipcode'];
$this->city = $data['city'];
$this->address = $data['address'];
$this->houseNumber = $data['housenumber'];
$this->houseNumberExtension = $data['extension'];
$this->birthDate = $data['birthdate'];
$this->iban = $data['iban'];
$this->setAVG = $data['avg'];
}
public function jsonSerialize() {
return [
"FirstName"=> $this->firstName,
"MiddleName"=> $this->middleName,
"LastName"=> $this->lastName,
"Email"=> $this->email,
"Country"=> $this->country,
"PhoneMobile"=> $this->phoneMobile,
"Gender"=> $this->gender,
"Zipcode" => $this->zipcode,
"City"=> $this->city,
"Address" => $this->address,
"HouseNumber" => $this->houseNumber,
"HouseNumberExtension" => $this->houseNumberExtension,
"BirthDate"=> $this->birthDate,
"IBAN"=> $this->iban,
"Language"=> $this->language,
"SetAVG"=> false,
];
}
}
?>
class Customer implements JsonSerializable {
public $firstName;
public $middleName;
public $lastName;
public $email;
public $country = 'Nederland';
public $phoneMobile;
public $gender;
public $zipcode;
public $city;
public $address;
public $houseNumber;
public $houseNumberExtension;
public $birthDate;
public $iban;
public $language = 'nl_NL';
public $setAVG;
public function __construct($data){
$this->firstName = $data['fname'];
$this->middleName = $data['prefix'];
$this->lastName = $data['lname'];
$this->email = $data['email'];
$this->phoneMobile = $data['phone'];
$this->gender = $data['gender'];
$this->zipcode = $data['zipcode'];
$this->city = $data['city'];
$this->address = $data['address'];
$this->houseNumber = $data['housenumber'];
$this->houseNumberExtension = $data['extension'];
$this->birthDate = $data['birthdate'];
$this->iban = $data['iban'];
$this->setAVG = $data['avg'];
}
public function jsonSerialize() {
return [
"FirstName"=> $this->firstName,
"MiddleName"=> $this->middleName,
"LastName"=> $this->lastName,
"Email"=> $this->email,
"Country"=> $this->country,
"PhoneMobile"=> $this->phoneMobile,
"Gender"=> $this->gender,
"Zipcode" => $this->zipcode,
"City"=> $this->city,
"Address" => $this->address,
"HouseNumber" => $this->houseNumber,
"HouseNumberExtension" => $this->houseNumberExtension,
"BirthDate"=> $this->birthDate,
"IBAN"=> $this->iban,
"Language"=> $this->language,
"SetAVG"=> false,
];
}
}
?>
Voordat dit object word aangemaakt, gebruik ik eerst een validator class die alle velden valideert, op bijvoorbeeld verplichtheid, email, telefoon etc. Maar wat het niet doet is het purifyen van de velden. Zoals je ziet in de constructor pak ik zo alleen de velden die ik nodig heb, dus `new Customer($_REQUEST)`, en in de constructor haal ik alleen het nodige er uit. Is het dan nog handig om in de constructor om elk veld even `htmlspecialchars()` te zetten, om XSS te voorkomen? Aangezien die validator niks doet met de data zelf, behalve dan valideren of ze aan voorwaarden voldoen?
Gewijzigd op 31/05/2022 09:47:07 door Jorn Reed
Jorn Reed op 31/05/2022 08:56:04:
Zoals je ziet in de constructor pak ik zo alleen de velden die ik nodig heb, dus `new Customer($_REQUEST)`, en in de constructor haal ik alleen het nodige er uit. Is het dan nog handig om in de constructor om elk veld even `htmlspecialchars()` te zetten, om XSS te voorkomen? Aangezien die validator niks doet met de data zelf, behalve dan valideren of ze aan voorwaarden voldoen?
In het algemeen zou ik daarop "Nee" antwoorden, maar uiteindelijk hangt het vooral af van waar je de klasse voor gebruikt. Een self-validating class kán handig zijn, bijvoorbeeld wanneer je deze class Customer uitsluitend gebruikt om via een webformulier ontvangen klantgegevens als JSON door te sluizen naar een API.
Gebruik je dezelfde class Customer echter ook in andere situaties, dan is het verstandiger om de validaties buiten de klasse te houden. Dat geldt bijvoorbeeld wanneer je de klantgegevens uit betrouwbare bron haalt, zoals je eigen database. Eerder gecontroleerde gegevens steeds opnieuw valideren is een verspilling van tijd en systeembronnen, en een potentiële bron van lastig op te sporen bugs.
Ward van der Put op 31/05/2022 11:49:04:
In het algemeen zou ik daarop "Nee" antwoorden, maar uiteindelijk hangt het vooral af van waar je de klasse voor gebruikt. Een self-validating class kán handig zijn, bijvoorbeeld wanneer je deze class Customer uitsluitend gebruikt om via een webformulier ontvangen klantgegevens als JSON door te sluizen naar een API.
Gebruik je dezelfde class Customer echter ook in andere situaties, dan is het verstandiger om de validaties buiten de klasse te houden. Dat geldt bijvoorbeeld wanneer je de klantgegevens uit betrouwbare bron haalt, zoals je eigen database. Eerder gecontroleerde gegevens steeds opnieuw valideren is een verspilling van tijd en systeembronnen, en een potentiële bron van lastig op te sporen bugs.
Jorn Reed op 31/05/2022 08:56:04:
Zoals je ziet in de constructor pak ik zo alleen de velden die ik nodig heb, dus `new Customer($_REQUEST)`, en in de constructor haal ik alleen het nodige er uit. Is het dan nog handig om in de constructor om elk veld even `htmlspecialchars()` te zetten, om XSS te voorkomen? Aangezien die validator niks doet met de data zelf, behalve dan valideren of ze aan voorwaarden voldoen?
In het algemeen zou ik daarop "Nee" antwoorden, maar uiteindelijk hangt het vooral af van waar je de klasse voor gebruikt. Een self-validating class kán handig zijn, bijvoorbeeld wanneer je deze class Customer uitsluitend gebruikt om via een webformulier ontvangen klantgegevens als JSON door te sluizen naar een API.
Gebruik je dezelfde class Customer echter ook in andere situaties, dan is het verstandiger om de validaties buiten de klasse te houden. Dat geldt bijvoorbeeld wanneer je de klantgegevens uit betrouwbare bron haalt, zoals je eigen database. Eerder gecontroleerde gegevens steeds opnieuw valideren is een verspilling van tijd en systeembronnen, en een potentiële bron van lastig op te sporen bugs.
In dit geval heb ik ook een CustomerValidator class. Die loopt door alle gegeven velden heen, en returned validatie meldingen indien aanwezig. Zodra `isSuccess()` gelijk is aan true. Dan pas maakt hij de Customer aan, zoals in mijn bericht hier boven, en dat object word via json naar een api gestuurd. Dan gaat dat toch goed zo?
Je kunt validaties op verschillende niveaus uitvoeren. Het eenvoudigste of ?laagste? niveau zijn guards voor bijvoorbeeld het datatype. Dankzij strict typing kun je die validaties in PHP 8 gebruiken voor eigenschappen en mutators.
Je gebruikt nu bijvoorbeeld nog:
public $email;
maar je kunt expliciet aangeven dat dit een string moet zijn:
public string $email;
Is het e-mailadres niet vereist, dan kun je ook dat aangeven door de eigenschap nullable te maken:
public ?string $email = null;
of:
public string|null $email = null;
Guards zijn dus poortwachters die je code sterker en veiliger maken.
Ah oke, ja die zijn wel handig, ondanks dat ik ze nooit gebruikt heb, ga ik er in de toekomst toch meer opletten. Toch vroeg ik me dan nog 1 ding af. In die customerValidator, valideer ik alleen de velden, dus doe ik verder niks met de waardes zelf. Waar kan ik dan het beste bijvoorbeeld `htmlspecialchars` op de velden gooien, zodat ze tegen XSS werken?