├── CHANGELOG.md
├── LICENSE
├── README.md
├── composer.json
├── doc
├── context.md
├── definition
│ ├── accessor.md
│ ├── alias.md
│ ├── exclusion_policy.md
│ ├── groups.md
│ ├── max_depth.md
│ ├── mutator.md
│ ├── order.md
│ ├── readable.md
│ ├── type.md
│ ├── version.md
│ ├── writable.md
│ ├── xml_attribute.md
│ ├── xml_collection.md
│ ├── xml_root.md
│ └── xml_value.md
├── docker.md
├── event.md
├── installation.md
├── mapping.md
├── type.md
├── usage.md
└── visitor.md
└── src
├── Accessor
├── AccessorInterface.php
├── ReflectionAccessor.php
└── SymfonyAccessor.php
├── Context
├── Context.php
└── ContextInterface.php
├── Direction.php
├── Event
├── AbstractClassMetadataEvent.php
├── AbstractEvent.php
├── AbstractPreEvent.php
├── ClassMetadataLoadEvent.php
├── ClassMetadataNotFoundEvent.php
├── PostDeserializeEvent.php
├── PostSerializeEvent.php
├── PreDeserializeEvent.php
├── PreSerializeEvent.php
└── SerializerEvents.php
├── Exclusion
├── ChainExclusionStrategy.php
├── ExclusionPolicy.php
├── ExclusionStrategy.php
├── ExclusionStrategyInterface.php
├── GroupsExclusionStrategy.php
├── MaxDepthExclusionStrategy.php
└── VersionExclusionStrategy.php
├── Format.php
├── Instantiator
├── DoctrineInstantiator.php
└── InstantiatorInterface.php
├── Mapping
├── Annotation
│ ├── Accessor.php
│ ├── Alias.php
│ ├── Exclude.php
│ ├── ExclusionPolicy.php
│ ├── Expose.php
│ ├── Groups.php
│ ├── MaxDepth.php
│ ├── Mutator.php
│ ├── Order.php
│ ├── Readable.php
│ ├── Since.php
│ ├── Type.php
│ ├── Until.php
│ ├── Writable.php
│ ├── XmlAttribute.php
│ ├── XmlCollection.php
│ ├── XmlRoot.php
│ └── XmlValue.php
├── ClassMetadata.php
├── ClassMetadataInterface.php
├── Factory
│ ├── AbstractClassMetadataFactory.php
│ ├── CacheClassMetadataFactory.php
│ ├── ClassMetadataFactory.php
│ ├── ClassMetadataFactoryInterface.php
│ └── EventClassMetadataFactory.php
├── Loader
│ ├── AbstractClassMetadataLoader.php
│ ├── AbstractFileClassMetadataLoader.php
│ ├── AbstractReflectionClassMetadataLoader.php
│ ├── AnnotationClassMetadataLoader.php
│ ├── ChainClassMetadataLoader.php
│ ├── ClassMetadataLoaderInterface.php
│ ├── DirectoryClassMetadataLoader.php
│ ├── FileClassMetadataLoader.php
│ ├── JsonClassMetadataLoader.php
│ ├── MappedClassMetadataLoaderInterface.php
│ ├── ReflectionClassMetadataLoader.php
│ ├── XmlClassMetadataLoader.php
│ └── YamlClassMetadataLoader.php
├── MetadataInterface.php
├── PropertyMetadata.php
├── PropertyMetadataInterface.php
├── Resource
│ └── mapping.xsd
├── TypeMetadata.php
└── TypeMetadataInterface.php
├── Mutator
├── MutatorInterface.php
├── ReflectionMutator.php
└── SymfonyMutator.php
├── Naming
├── AbstractNamingStrategy.php
├── AbstractSeparatorNamingStrategy.php
├── CacheNamingStrategy.php
├── CamelCaseNamingStrategy.php
├── IdenticalNamingStrategy.php
├── KebabCaseNamingStrategy.php
├── NamingStrategyInterface.php
├── SnakeCaseNamingStrategy.php
├── SpaceNamingStrategy.php
└── StudlyCapsNamingStrategy.php
├── Navigator
├── EventNavigator.php
├── Navigator.php
└── NavigatorInterface.php
├── Registry
├── TypeRegistry.php
├── TypeRegistryInterface.php
├── VisitorRegistry.php
└── VisitorRegistryInterface.php
├── Serializer.php
├── SerializerInterface.php
├── Type
├── ArrayType.php
├── BooleanType.php
├── ClosureType.php
├── DateTimeType.php
├── ExceptionType.php
├── FloatType.php
├── Guesser
│ ├── TypeGuesser.php
│ └── TypeGuesserInterface.php
├── IntegerType.php
├── NullType.php
├── ObjectType.php
├── Parser
│ ├── TypeLexer.php
│ ├── TypeParser.php
│ └── TypeParserInterface.php
├── ResourceType.php
├── StdClassType.php
├── StringType.php
├── Type.php
└── TypeInterface.php
└── Visitor
├── AbstractDeserializationVisitor.php
├── AbstractGenericVisitor.php
├── AbstractSerializationVisitor.php
├── AbstractVisitor.php
├── Csv
├── CsvDeserializationVisitor.php
└── CsvSerializationVisitor.php
├── Json
├── JsonDeserializationVisitor.php
└── JsonSerializationVisitor.php
├── VisitorInterface.php
├── Xml
├── XmlDeserializationVisitor.php
└── XmlSerializationVisitor.php
└── Yaml
├── YamlDeserializationVisitor.php
└── YamlSerializationVisitor.php
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | ### 1.0.0 (2017-02-27)
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-2017 Eric GELOEN
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
6 | persons to whom the Software is furnished to do so, subject to the following conditions:
7 |
8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
9 | Software.
10 |
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
12 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
13 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | [](http://travis-ci.org/egeloen/ivory-serializer)
4 | [](https://ci.appveyor.com/project/egeloen/ivory-serializer/branch/master)
5 | [](https://scrutinizer-ci.com/g/egeloen/ivory-serializer/?branch=master)
6 | [](https://scrutinizer-ci.com/g/egeloen/ivory-serializer/?branch=master)
7 | [](http://www.versioneye.com/php/egeloen:serializer)
8 |
9 | [](https://packagist.org/packages/egeloen/serializer)
10 | [](https://packagist.org/packages/egeloen/serializer)
11 | [](https://packagist.org/packages/egeloen/serializer)
12 | [](https://packagist.org/packages/egeloen/serializer)
13 |
14 | ## Overview
15 |
16 | The Ivory Serializer is a PHP 5.6+ library allowing you to (de)-serialize complex data using the visitor pattern
17 | recursively on each node of the graph. It supports the CSV, JSON, XML and YAML formats. It also supports features such
18 | as exclusion strategies (groups, max depth, circular reference, version, ...), naming strategies (camel case, snake
19 | case, studly caps), automatic/explicit mapping (reflection, annotation, XML, YAML, JSON) and many others...
20 |
21 | ``` php
22 | use Ivory\Serializer\Format;
23 | use Ivory\Serializer\Serializer;
24 |
25 | $stdClass = new \stdClass();
26 | $stdClass->foo = true;
27 | $stdClass->bar = ['foo', [123, 432.1]];
28 |
29 | $serializer = new Serializer();
30 |
31 | echo $serializer->serialize($stdClass, Format::JSON);
32 | // {"foo": true,"bar": ["foo", [123, 432.1]]}
33 |
34 | $deserialize = $serializer->deserialize($json, \stdClass::class, Format::JSON);
35 | // $deserialize == $stdClass
36 | ```
37 |
38 | ## Documentation
39 |
40 | - [Installation](/doc/installation.md)
41 | - [Usage](/doc/usage.md)
42 | - [Mapping](/doc/mapping.md)
43 | - [Type](/doc/type.md)
44 | - [Event](/doc/event.md)
45 | - [Visitor](/doc/visitor.md)
46 | - [Context](/doc/context.md)
47 | - [Exclusion strategies](/doc/context.md#exclusion-strategies)
48 | - [Naming strategies](/doc/context.md#naming-strategies)
49 |
50 | ## Testing
51 |
52 | The library is fully unit tested by [PHPUnit](http://www.phpunit.de/) with a code coverage close to **100%**. To
53 | execute the test suite, check the travis [configuration](/.travis.yml).
54 |
55 | ## Contribute
56 |
57 | We love contributors! Ivory is an open source project. If you'd like to contribute, feel free to propose a PR! You
58 | can follow the [CONTRIBUTING](/CONTRIBUTING.md) file which will explain you how to set up the project.
59 |
60 | ## License
61 |
62 | The Ivory Serializer is under the MIT license. For the full copyright and license information, please read the
63 | [LICENSE](/LICENSE) file that was distributed with this source code.
64 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "egeloen/serializer",
3 | "description": "Serializer for PHP 5.6+ supporting JSON, XML, YAML & CSV",
4 | "keywords": [ "serializer" ],
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Eric GELOEN",
9 | "email": "geloen.eric@gmail.com"
10 | }
11 | ],
12 | "require": {
13 | "php": "^5.6|^7.0",
14 | "doctrine/instantiator": "^1.0",
15 | "doctrine/lexer": "^1.0",
16 | "symfony/options-resolver": "^2.7|^3.0"
17 | },
18 | "require-dev": {
19 | "doctrine/annotations": "^1.2",
20 | "ext-json": "*",
21 | "ext-dom": "*",
22 | "ext-simplexml": "*",
23 | "friendsofphp/php-cs-fixer": "^2.0",
24 | "nikic/php-parser": "^1.4",
25 | "phpdocumentor/reflection": "^3.0",
26 | "phpunit/phpunit": "^5.4",
27 | "phpunit/phpunit-mock-objects": "^3.2.3",
28 | "psr/cache": "^1.0",
29 | "symfony/event-dispatcher": "^2.7|^3.0",
30 | "symfony/phpunit-bridge": "^2.7|^3.0",
31 | "symfony/property-info": "^2.7|^3.0",
32 | "symfony/property-access": "^2.7|^3.0",
33 | "symfony/yaml": "^2.7|^3.0"
34 | },
35 | "suggest": {
36 | "doctrine/annotations": "Allow you to use the mapping annotations.",
37 | "ext-dom": "Allow you to use the XML serialization visitor and mapping.",
38 | "ext-json": "Allow you to use the JSON serialization/deserialization visitors and mapping.",
39 | "ext-simplexml": "Allow you to use the XML deserialization visitor and mapping.",
40 | "phpdocumentor/reflection": "Allow you to parse PHP type more efficiently",
41 | "symfony/event-dispatcher": "Allow you to use the event dispatching system.",
42 | "symfony/property-access": "Allow you to use the Symfony accessor/mutator.",
43 | "symfony/property-info": "Allow you to use the mapping type auto discovery.",
44 | "symfony/yaml": "Allow you to use the YAML serialization/deserialization visitors and mapping."
45 | },
46 | "autoload": {
47 | "psr-4": { "Ivory\\Serializer\\": "src/" }
48 | },
49 | "autoload-dev": {
50 | "psr-4": { "Ivory\\Tests\\Serializer\\": "tests/" }
51 | },
52 | "extra": {
53 | "branch-alias": {
54 | "dev-master": "1.0-dev"
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/doc/definition/accessor.md:
--------------------------------------------------------------------------------
1 | # Accessor
2 |
3 | The accessor allows you to configure how to get a property.
4 |
5 | ## Example
6 |
7 | In this example, we configure the username property to use the getUsername method when serializing.
8 |
9 | ``` php
10 | namespace Acme;
11 |
12 | use Ivory\Serializer\Mapping\Annotation as Serializer;
13 |
14 | class User
15 | {
16 | /**
17 | * @Serializer\Accessor("getUsername")
18 | *
19 | * @var string
20 | */
21 | public $username;
22 |
23 | /**
24 | * @return string
25 | */
26 | public function getUsername()
27 | {
28 | return strtolower($this->username);
29 | }
30 | }
31 | ```
32 |
33 | ## Usage
34 |
35 | ``` php
36 | use Acme\User;
37 |
38 | $user = new User();
39 | $user->username = 'GeLo';
40 |
41 | $serialize = $serializer->serialize($user, $format);
42 | // echo $serialize;
43 |
44 | $deserialize = $serializer->deserialize($serialize, User::class, $format);
45 | // $deserialize->username === 'gelo'
46 | ```
47 |
48 | ## Results
49 |
50 | ### JSON
51 |
52 | ``` json
53 | {
54 | "username": "gelo"
55 | }
56 | ```
57 |
58 | ### XML
59 |
60 | ``` xml
61 |
62 |
63 | gelo
64 |
65 | ```
66 |
67 | ### YAML
68 |
69 | ``` yaml
70 | username: gelo
71 | ```
72 |
73 | ### CSV
74 |
75 | ``` csv
76 | username
77 | gelo
78 | ```
79 |
80 | ## Definitions
81 |
82 | If you prefer use an other definition format, the following examples are identical.
83 |
84 | ### XML
85 |
86 | ``` xml
87 |
88 |
89 |
95 |
96 |
97 |
98 |
99 | ```
100 |
101 | ### YAML
102 |
103 | ``` yaml
104 | Acme\User:
105 | properties:
106 | username:
107 | accessor: getUsername
108 | ```
109 |
110 | ### JSON
111 |
112 | ``` json
113 | {
114 | "Acme\\User": {
115 | "properties": {
116 | "username": {
117 | "accessor": "getUsername"
118 | }
119 | }
120 | }
121 | }
122 | ```
123 |
--------------------------------------------------------------------------------
/doc/definition/alias.md:
--------------------------------------------------------------------------------
1 | # Alias
2 |
3 | The alias allows you rename a property.
4 |
5 | ## Example
6 |
7 | In this example, we rename the username property to login.
8 |
9 | ``` php
10 | namespace Acme;
11 |
12 | use Ivory\Serializer\Mapping\Annotation as Serializer;
13 |
14 | class User
15 | {
16 | /**
17 | * @Serializer\Alias("login")
18 | *
19 | * @var string
20 | */
21 | public $username;
22 | }
23 | ```
24 |
25 | ## Usage
26 |
27 | ``` php
28 | use Acme\User;
29 |
30 | $user = new User();
31 | $user->username = 'GeLo';
32 |
33 | $serialize = $serializer->serialize($user, $format);
34 | // echo $serialize;
35 |
36 | $deserialize = $serializer->deserialize($serialize, User::class, $format);
37 | // $deserialize == $user
38 | ```
39 |
40 | ## Results
41 |
42 | ### JSON
43 |
44 | ``` json
45 | {
46 | "login": "GeLo"
47 | }
48 | ```
49 |
50 | ### XML
51 |
52 | ``` xml
53 |
54 |
55 | GeLo
56 |
57 | ```
58 |
59 | ### YAML
60 |
61 | ``` yaml
62 | login: GeLo
63 | ```
64 |
65 | ### CSV
66 |
67 | ``` csv
68 | login
69 | GeLo
70 | ```
71 |
72 | ## Definitions
73 |
74 | If you prefer use an other definition format, the following examples are identical.
75 |
76 | ### XML
77 |
78 | ``` xml
79 |
80 |
81 |
87 |
88 |
89 |
90 |
91 | ```
92 |
93 | ### YAML
94 |
95 | ``` yaml
96 | Acme\User:
97 | properties:
98 | username:
99 | alias: login
100 | ```
101 |
102 | ### JSON
103 |
104 | ``` json
105 | {
106 | "Acme\\User": {
107 | "properties": {
108 | "username": {
109 | "alias": "login"
110 | }
111 | }
112 | }
113 | }
114 | ```
115 |
--------------------------------------------------------------------------------
/doc/definition/groups.md:
--------------------------------------------------------------------------------
1 | # Groups
2 |
3 | The groups allows you include properties based on the runtime groups provided via the context. Be aware that if you
4 | don't configure groups, the serializer will automatically use the group `Default` for your property.
5 |
6 | ## Example
7 |
8 | In this example, we only (de)-serialize the username property for groups: group1 or group2.
9 |
10 | ``` php
11 | namespace Acme;
12 |
13 | use Ivory\Serializer\Mapping\Annotation as Serializer;
14 |
15 | class User
16 | {
17 | /**
18 | * @Serializer\Groups(["group1", "group2"])
19 | *
20 | * @var string
21 | */
22 | public $username;
23 |
24 | /**
25 | * @var string|null
26 | */
27 | publiic $plainPassword;
28 | }
29 | ```
30 |
31 | ## Usage
32 |
33 | ``` php
34 | use Acme\User;
35 | use Ivory\Serializer\Context\Context;
36 | use Ivory\Serializer\Exclusion\GroupsExclusionStrategy;
37 |
38 | $user = new User();
39 | $user->username = 'GeLo';
40 | $user->plainPassword = 'azerty';
41 |
42 | $context = new Context();
43 | $context->setExclusionStrategy(new GroupsExclusionStrategy(['group1']));
44 |
45 | $serialize = $serializer->serialize($user, $format, $context);
46 | // echo $serialize;
47 |
48 | $deserialize = $serializer->deserialize($serialize, User::class, $format, $context);
49 | // $deserialize->username === $user->username
50 | // $deserialize->plainPassword === null
51 | ```
52 |
53 | **If you don't use the groups exclusion strategy, all properties are (de)-serialized regardless configured groups.**
54 |
55 | ## Results
56 |
57 | ### JSON
58 |
59 | ``` json
60 | {
61 | "username": "GeLo"
62 | }
63 | ```
64 |
65 | ### XML
66 |
67 | ``` xml
68 |
69 |
70 | GeLo
71 |
72 | ```
73 |
74 | ### YAML
75 |
76 | ``` yaml
77 | username: GeLo
78 | ```
79 |
80 | ### CSV
81 |
82 | ``` csv
83 | username
84 | GeLo
85 | ```
86 |
87 | ## Definitions
88 |
89 | If you prefer use an other definition format, the following examples are identical.
90 |
91 | ### XML
92 |
93 | ``` xml
94 |
95 |
96 |
102 |
103 |
104 |
105 |
106 |
107 | ```
108 |
109 | ### YAML
110 |
111 | ``` yaml
112 | Acme\User:
113 | properties:
114 | username:
115 | groups: [group1, group2]
116 | plainPassword: ~
117 | ```
118 |
119 | ### JSON
120 |
121 | ``` json
122 | {
123 | "Acme\\User": {
124 | "properties": {
125 | "username": {
126 | "groups": ["group1", "group2"]
127 | },
128 | "plainPassword": {}
129 | }
130 | }
131 | }
132 | ```
133 |
--------------------------------------------------------------------------------
/doc/definition/max_depth.md:
--------------------------------------------------------------------------------
1 | # Max Depth
2 |
3 | The max depth allows you limit the depth of the graph traversal for a property and also limit circular references.
4 |
5 | ## Example
6 |
7 | In this example, we only (de)-serialize one level of friends.
8 |
9 | ``` php
10 | namespace Acme;
11 |
12 | use Ivory\Serializer\Mapping\Annotation as Serializer;
13 |
14 | class User
15 | {
16 | /**
17 | * @var string
18 | */
19 | public $username;
20 |
21 | /**
22 | * @Serializer\MaxDepth(2)
23 | *
24 | * @var User[]
25 | */
26 | publiic $friends = [];
27 | }
28 | ```
29 |
30 | ## Usage
31 |
32 | ``` php
33 | use Acme\User;
34 | use Ivory\Serializer\Context\Context;
35 | use Ivory\Serializer\Exclusion\MapDepthExclusionStrategy;
36 |
37 | $friend2 = new User();
38 | $friend2->username = 'Ben';
39 |
40 | $friend1 = new User();
41 | $friend1->username = 'Tim';
42 | $friend1->friends[] = $friend2;
43 |
44 | $user = new User();
45 | $user->username = 'GeLo';
46 | $user->friends[] = $friend1;
47 |
48 | $context = new Context();
49 | $context->setExclusionStrategy(new MapDepthExclusionStrategy());
50 |
51 | $serialize = $serializer->serialize($user, $format, $context);
52 | // echo $serialize;
53 |
54 | $deserialize = $serializer->deserialize($serialize, User::class, $format, $context);
55 | // $deserialize->username === $user->username
56 | // $deserialize->plainPassword === null
57 | ```
58 |
59 | **If you don't use the max depth exclusion strategy, all properties are (de)-serialized regardless configured max
60 | depths.**
61 |
62 | ## Results
63 |
64 | ### JSON
65 |
66 | ``` json
67 | {
68 | "username": "GeLo"
69 | "friends": [
70 | {
71 | "username": "Tim",
72 | "friends": []
73 | }
74 | ]
75 | }
76 | ```
77 |
78 | ### XML
79 |
80 | ``` xml
81 |
82 |
83 | GeLo
84 |
85 |
86 | Tim
87 |
88 |
89 |
90 |
91 | ```
92 |
93 | ### YAML
94 |
95 | ``` yaml
96 | username: GeLo
97 | friends:
98 | -
99 | username: Tim
100 | friends: []
101 | ```
102 |
103 | ### CSV
104 |
105 | ``` csv
106 | username;friends.0.username;friends.0.friends
107 | GeLo;Tim;[]
108 | ```
109 |
110 | ## Definitions
111 |
112 | If you prefer use an other definition format, the following examples are identical.
113 |
114 | ### XML
115 |
116 | ``` xml
117 |
118 |
119 |
125 |
126 |
127 |
128 |
129 |
130 | ```
131 |
132 | ### YAML
133 |
134 | ``` yaml
135 | Acme\User:
136 | properties:
137 | username:
138 | groups: [group1, group2]
139 | plainPassword: ~
140 | ```
141 |
142 | ### JSON
143 |
144 | ``` json
145 | {
146 | "Acme\\User": {
147 | "properties": {
148 | "username": {
149 | "groups": ["group1", "group2"]
150 | },
151 | "plainPassword": {}
152 | }
153 | }
154 | }
155 | ```
156 |
--------------------------------------------------------------------------------
/doc/definition/mutator.md:
--------------------------------------------------------------------------------
1 | # Mutator
2 |
3 | The mutator allows you to configure how to set a property.
4 |
5 | ## Example
6 |
7 | In this example, we configure the username property to use the setUsername method when de-serializing.
8 |
9 | ``` php
10 | namespace Acme;
11 |
12 | use Ivory\Serializer\Mapping\Annotation as Serializer;
13 |
14 | class User
15 | {
16 | /**
17 | * @Serializer\Mutator("setUsername")
18 | *
19 | * @var string
20 | */
21 | public $username;
22 |
23 | /**
24 | * @return string
25 | */
26 | public function setUsername($username)
27 | {
28 | $this->username = strtolower($username);
29 | }
30 | }
31 | ```
32 |
33 | ## Usage
34 |
35 | ``` php
36 | use Acme\User;
37 |
38 | $user = new User();
39 | $user->username = 'GeLo';
40 |
41 | $serialize = $serializer->serialize($user, $format);
42 | // echo $serialize;
43 |
44 | $deserialize = $serializer->deserialize($serialize, User::class, $format);
45 | // $deserialize->username === 'gelo'
46 | ```
47 |
48 | ## Definitions
49 |
50 | If you prefer use an other definition format, the following examples are identical.
51 |
52 | ### XML
53 |
54 | ``` xml
55 |
56 |
57 |
63 |
64 |
65 |
66 |
67 | ```
68 |
69 | ### YAML
70 |
71 | ``` yaml
72 | Acme\User:
73 | properties:
74 | username:
75 | mutator: setUsername
76 | ```
77 |
78 | ### JSON
79 |
80 | ``` json
81 | {
82 | "Acme\\User": {
83 | "properties": {
84 | "username": {
85 | "mutator": "setUsername"
86 | }
87 | }
88 | }
89 | }
90 | ```
91 |
--------------------------------------------------------------------------------
/doc/definition/order.md:
--------------------------------------------------------------------------------
1 | # Order
2 |
3 | The order allows you to order properties.
4 |
5 | ## Example
6 |
7 | In this example, we configure a custom ordering for the User class. The order supports custom ordering or alphabetical
8 | ordering via `ASC` or `DESC`.
9 |
10 | ### Alphabetical Ordering
11 |
12 | ``` php
13 | namespace Acme;
14 |
15 | use Ivory\Serializer\Mapping\Annotation as Serializer;
16 |
17 | /**
18 | * @Serializer\Order("ASC")
19 | */
20 | class User
21 | {
22 | /**
23 | * @var string
24 | */
25 | public $username;
26 |
27 | /**
28 | * @var bool
29 | */
30 | public $active;
31 | }
32 | ```
33 |
34 | ### Custom Ordering
35 |
36 | ``` php
37 | namespace Acme;
38 |
39 | use Ivory\Serializer\Mapping\Annotation as Serializer;
40 |
41 | /**
42 | * @Serializer\Order("active,username")
43 | */
44 | class User
45 | {
46 | /**
47 | * @var string
48 | */
49 | public $username;
50 |
51 | /**
52 | * @var bool
53 | */
54 | public $active;
55 | }
56 | ```
57 |
58 | ## Usage
59 |
60 | ``` php
61 | use Acme\User;
62 |
63 | $user = new User();
64 | $user->username = 'GeLo';
65 | user->active = true;
66 |
67 | $serialize = $serializer->serialize($user, $format);
68 | // echo $serialize;
69 |
70 | $deserialize = $serializer->deserialize($serialize, User::class, $format);
71 | // $deserialize == $user
72 | ```
73 |
74 | ## Results
75 |
76 | ### JSON
77 |
78 | ``` json
79 | {
80 | "active": true,
81 | "username": "GeLo"
82 | }
83 | ```
84 |
85 | ### XML
86 |
87 | ``` xml
88 |
89 |
90 | true
91 | GeLo
92 |
93 | ```
94 |
95 | ### YAML
96 |
97 | ``` yaml
98 | active: true
99 | username: GeLo
100 | ```
101 |
102 | ### CSV
103 |
104 | ``` csv
105 | active;username
106 | true;GeLo
107 | ```
108 |
109 | ## Definitions
110 |
111 | If you prefer use an other definition format, the following examples are identical.
112 |
113 | ### XML
114 |
115 | #### Alphabetical Ordering
116 |
117 | ``` xml
118 |
119 |
120 |
126 |
127 |
128 |
129 |
130 |
131 | ```
132 |
133 | #### Custom Ordering
134 |
135 | ``` xml
136 |
137 |
138 |
144 |
145 |
146 |
147 |
148 |
149 | ```
150 |
151 | ### YAML
152 |
153 | #### Alphabetical Ordering
154 |
155 | ``` yaml
156 | Acme\User:
157 | order: "ASC"
158 | properties:
159 | username: ~
160 | active: ~
161 | ```
162 |
163 | #### Custom Ordering
164 |
165 | ``` yaml
166 | Acme\User:
167 | order: ["active", "username"]
168 | properties:
169 | username: ~
170 | active: ~
171 | ```
172 |
173 | ### JSON
174 |
175 | #### Alphabetical Ordering
176 |
177 | ``` json
178 | {
179 | "Acme\\User": {
180 | "order": "ASC",
181 | "properties": {
182 | "username": {},
183 | "active": {}
184 | }
185 | }
186 | }
187 | ```
188 |
189 | #### Custom Ordering
190 |
191 | ``` json
192 | {
193 | "Acme\\User": {
194 | "order": ["active", "username"],
195 | "properties": {
196 | "username": {},
197 | "active": {}
198 | }
199 | }
200 | }
201 | ```
202 |
--------------------------------------------------------------------------------
/doc/definition/readable.md:
--------------------------------------------------------------------------------
1 | # Readable
2 |
3 | The readable allows you configure if a property is serializable (by default, it is). This property is useful if you
4 | want to not serialize a property but still de-serialize it.
5 |
6 | ## Example
7 |
8 | In this example, we configure the plainPassword property to not be serializable.
9 |
10 | ### Property Readable
11 |
12 | ``` php
13 | namespace Acme;
14 |
15 | use Ivory\Serializer\Mapping\Annotation as Serializer;
16 |
17 | class User
18 | {
19 | /**
20 | * @var string
21 | */
22 | public $username;
23 |
24 | /**
25 | * @Serializer\Readable(false)
26 | *
27 | * @var string|null
28 | */
29 | public $plainPassword;
30 | }
31 | ```
32 |
33 | ### Class Readable
34 |
35 | By default, all properties are readable but you can alter this behavior by putting the readable option directly on the
36 | class:
37 |
38 | ``` php
39 | namespace Acme;
40 |
41 | use Ivory\Serializer\Mapping\Annotation as Serializer;
42 |
43 | /**
44 | * @Serializer\Readable(false)
45 | */
46 | class User
47 | {
48 | /**
49 | * @Serializer\Readable(true)
50 | *
51 | * @var string
52 | */
53 | public $username;
54 |
55 | /**
56 | * @var string|null
57 | */
58 | public $plainPassword;
59 | }
60 | ```
61 |
62 | ## Usage
63 |
64 | ``` php
65 | use Acme\User;
66 |
67 | $user = new User();
68 | $user->username = 'GeLo';
69 | user->plainPassword = 'azerty';
70 |
71 | $serialize = $serializer->serialize($user, $format);
72 | // echo $serialize;
73 |
74 | $deserialize = $serializer->deserialize($serialize, User::class, $format);
75 | // $deserialize->username === $user->username
76 | // $deserialize->plainPassword === null
77 | ```
78 |
79 | ## Results
80 |
81 | ### JSON
82 |
83 | ``` json
84 | {
85 | "username": "GeLo"
86 | }
87 | ```
88 |
89 | ### XML
90 |
91 | ``` xml
92 |
93 |
94 | GeLo
95 |
96 | ```
97 |
98 | ### YAML
99 |
100 | ``` yaml
101 | username: GeLo
102 | ```
103 |
104 | ### CSV
105 |
106 | ``` csv
107 | username
108 | GeLo
109 | ```
110 |
111 | ## Definitions
112 |
113 | If you prefer use an other definition format, the following examples are identical.
114 |
115 | ### XML
116 |
117 | #### Property Readable
118 |
119 | ``` xml
120 |
121 |
122 |
128 |
129 |
130 |
131 |
132 |
133 | ```
134 |
135 | #### Class Readable
136 |
137 | ``` xml
138 |
139 |
140 |
146 |
147 |
148 |
149 |
150 |
151 | ```
152 |
153 | ### YAML
154 |
155 | #### Property Readable
156 |
157 | ``` yaml
158 | Acme\User:
159 | properties:
160 | username: ~
161 | plainPassword:
162 | readable: false
163 | ```
164 |
165 | #### Class Readable
166 |
167 | ``` yaml
168 | Acme\User:
169 | readable: false
170 | properties:
171 | username:
172 | readable: true
173 | plainPassword: ~
174 | ```
175 |
176 | ### JSON
177 |
178 | #### Property Readable
179 |
180 | ``` json
181 | {
182 | "Acme\\User": {
183 | "properties": {
184 | "username": {},
185 | "plainPassword": {
186 | "readable": false
187 | }
188 | }
189 | }
190 | }
191 | ```
192 |
193 | #### Class Readable
194 |
195 | ``` json
196 | {
197 | "Acme\\User": {
198 | "readable": false,
199 | "properties": {
200 | "username": {
201 | "reaadable": true
202 | },
203 | "plainPassword": {}
204 | }
205 | }
206 | }
207 | ```
208 |
--------------------------------------------------------------------------------
/doc/definition/type.md:
--------------------------------------------------------------------------------
1 | # Type
2 |
3 | The type allows you to configure a property type. You can find the list of supported types [here](/doc/type.md).
4 |
5 | ## Example
6 |
7 | In this example, we configure the type string on the username property.
8 |
9 | ``` php
10 | namespace Acme;
11 |
12 | use Ivory\Serializer\Mapping\Annotation as Serializer;
13 |
14 | class User
15 | {
16 | /**
17 | * @Serializer\Type("string")
18 | */
19 | public $username;
20 | }
21 | ```
22 |
23 | ## Usage
24 |
25 | ``` php
26 | use Acme\User;
27 |
28 | $user = new User();
29 | $user->username = 'GeLo';
30 |
31 | $serialize = $serializer->serialize($user, $format);
32 | // echo $serialize;
33 |
34 | $deserialize = $serializer->deserialize($serialize, User::class, $format);
35 | // $deserialize == $user
36 | ```
37 |
38 | ## Results
39 |
40 | ### JSON
41 |
42 | ``` json
43 | {
44 | "login": "GeLo"
45 | }
46 | ```
47 |
48 | ### XML
49 |
50 | ``` xml
51 |
52 |
53 | GeLo
54 |
55 | ```
56 |
57 | ### YAML
58 |
59 | ``` yaml
60 | login: GeLo
61 | ```
62 |
63 | ### CSV
64 |
65 | ``` csv
66 | login
67 | GeLo
68 | ```
69 |
70 | ## Definitions
71 |
72 | If you prefer use an other definition format, the following examples are identical.
73 |
74 | ### XML
75 |
76 | ``` xml
77 |
78 |
79 |
85 |
86 |
87 |
88 |
89 | ```
90 |
91 | ### YAML
92 |
93 | ``` yaml
94 | Acme\User:
95 | properties:
96 | username:
97 | type: string
98 | ```
99 |
100 | ### JSON
101 |
102 | ``` json
103 | {
104 | "Acme\\User": {
105 | "properties": {
106 | "username": {
107 | "type": "string"
108 | }
109 | }
110 | }
111 | }
112 | ```
113 |
--------------------------------------------------------------------------------
/doc/definition/version.md:
--------------------------------------------------------------------------------
1 | # Version
2 |
3 | The since & until allows you include properties based on the context version provided via the context.
4 |
5 | ## Example
6 |
7 | In this example, we only include the username property since the 1.0.0 version and include the plainPassword until the
8 | 2.0.0 version.
9 |
10 | ``` php
11 | namespace Acme;
12 |
13 | use Ivory\Serializer\Mapping\Annotation as Serializer;
14 |
15 | class User
16 | {
17 | /**
18 | * @Serializer\Since("1.0.0")
19 | *
20 | * @var string
21 | */
22 | public $username;
23 |
24 | /**
25 | * @Serializer\Until("2.0.0")
26 | *
27 | * @var string|null
28 | */
29 | publiic $plainPassword;
30 | }
31 | ```
32 |
33 | ## Usage
34 |
35 | ``` php
36 | use Acme\User;
37 | use Ivory\Serializer\Context\Context;
38 | use Ivory\Serializer\Exclusion\VersionExclusionStrategy;
39 |
40 | $user = new User();
41 | $user->username = 'GeLo';
42 | $user->plainPassword = 'azerty';
43 |
44 | $context = new Context();
45 | $context->setExclusionStrategy(new VersionExclusionStrategy('2.1.0'));
46 |
47 | $serialize = $serializer->serialize($user, $format, $context);
48 | // echo $serialize;
49 |
50 | $deserialize = $serializer->deserialize($serialize, User::class, $format, $context);
51 | // $deserialize->username === $user->username
52 | // $deserialize->plainPassword === null
53 | ```
54 |
55 | **If you don't use the version exclusion strategy, all properties are (de)-serialized regardless configured versions.**
56 |
57 | ## Results
58 |
59 | ### JSON
60 |
61 | ``` json
62 | {
63 | "username": "GeLo"
64 | }
65 | ```
66 |
67 | ### XML
68 |
69 | ``` xml
70 |
71 |
72 | GeLo
73 |
74 | ```
75 |
76 | ### YAML
77 |
78 | ``` yaml
79 | username: GeLo
80 | ```
81 |
82 | ### CSV
83 |
84 | ``` csv
85 | username
86 | GeLo
87 | ```
88 |
89 | ## Definitions
90 |
91 | If you prefer use an other definition format, the following examples are identical.
92 |
93 | ### XML
94 |
95 | ``` xml
96 |
97 |
98 |
104 |
105 |
106 |
107 |
108 |
109 | ```
110 |
111 | ### YAML
112 |
113 | ``` yaml
114 | Acme\User:
115 | properties:
116 | username:
117 | since: "1.0.0"
118 | plainPassword:
119 | until: "2.0.0"
120 | ```
121 |
122 | ### JSON
123 |
124 | ``` json
125 | {
126 | "Acme\\User": {
127 | "properties": {
128 | "username": {
129 | "since": "1.0.0"
130 | },
131 | "plainPassword": {
132 | "until": "2.0.0"
133 | }
134 | }
135 | }
136 | }
137 | ```
138 |
--------------------------------------------------------------------------------
/doc/definition/writable.md:
--------------------------------------------------------------------------------
1 | # Writable
2 |
3 | The writable allows you configure if a property is de-serializable (by default, it is). This property is useful if you
4 | want to not de-serialize a property but still serialize it.
5 |
6 | ## Example
7 |
8 | In this example, we configure the plainPassword property to not be de-serializable.
9 |
10 | ### Property Writable
11 |
12 | ``` php
13 | namespace Acme;
14 |
15 | use Ivory\Serializer\Mapping\Annotation as Serializer;
16 |
17 | class User
18 | {
19 | /**
20 | * @var string
21 | */
22 | public $username;
23 |
24 | /**
25 | * @Serializer\Writable(false)
26 | *
27 | * @var string|null
28 | */
29 | public $plainPassword;
30 | }
31 | ```
32 |
33 | ### Class Writable
34 |
35 | By default, all properties are writable but you can alter this behavior by putting the writable option directly on the
36 | class:
37 |
38 | ``` php
39 | namespace Acme;
40 |
41 | use Ivory\Serializer\Mapping\Annotation as Serializer;
42 |
43 | /**
44 | * @Serializer\Writable(false)
45 | */
46 | class User
47 | {
48 | /**
49 | * @Serializer\Writable(true)
50 | *
51 | * @var string
52 | */
53 | public $username;
54 |
55 | /**
56 | * @var string|null
57 | */
58 | public $plainPassword;
59 | }
60 | ```
61 |
62 | ## Usage
63 |
64 | ``` php
65 | use Acme\User;
66 |
67 | $user = new User();
68 | $user->username = 'GeLo';
69 | user->plainPassword = 'azerty';
70 |
71 | $serialize = $serializer->serialize($user, $format);
72 | // echo $serialize;
73 |
74 | $deserialize = $serializer->deserialize($serialize, User::class, $format);
75 | // $deserialize->username === $user->username
76 | // $deserialize->plainPassword === null
77 | ```
78 |
79 | ## Definitions
80 |
81 | If you prefer use an other definition format, the following examples are identical.
82 |
83 | ### XML
84 |
85 | #### Property Writable
86 |
87 | ``` xml
88 |
89 |
90 |
96 |
97 |
98 |
99 |
100 |
101 | ```
102 |
103 | #### Class Writable
104 |
105 | ``` xml
106 |
107 |
108 |
114 |
115 |
116 |
117 |
118 |
119 | ```
120 |
121 | ### YAML
122 |
123 | #### Property Writable
124 |
125 | ``` yaml
126 | Acme\User:
127 | properties:
128 | username: ~
129 | plainPassword:
130 | writable: false
131 | ```
132 |
133 | #### Class Writable
134 |
135 | ``` yaml
136 | Acme\User:
137 | writable: false
138 | properties:
139 | username:
140 | writable: true
141 | plainPassword: ~
142 | ```
143 |
144 | ### JSON
145 |
146 | #### Property Writable
147 |
148 | ``` json
149 | {
150 | "Acme\\User": {
151 | "properties": {
152 | "username": {},
153 | "plainPassword": {
154 | "writable": false
155 | }
156 | }
157 | }
158 | }
159 | ```
160 |
161 | #### Class Writable
162 |
163 | ``` json
164 | {
165 | "Acme\\User": {
166 | "writable": false,
167 | "properties": {
168 | "username": {
169 | "reaadable": true
170 | },
171 | "plainPassword": {}
172 | }
173 | }
174 | }
175 | ```
176 |
--------------------------------------------------------------------------------
/doc/definition/xml_attribute.md:
--------------------------------------------------------------------------------
1 | # XML Attribute
2 |
3 | The XML attribute is dedicated to XML and allow you to put a property as XML attribute.
4 |
5 | ## Example
6 |
7 | In this example, we configure the username property to be put as XML attribute.
8 |
9 | ``` php
10 | namespace Acme;
11 |
12 | use Ivory\Serializer\Mapping\Annotation as Serializer;
13 |
14 | class User
15 | {
16 | /**
17 | * @Serializer\XmlAttribute
18 | *
19 | * @var string
20 | */
21 | public $username;
22 | }
23 | ```
24 |
25 | ## Usage
26 |
27 | ``` php
28 | use Acme\User;
29 |
30 | $user = new User();
31 | $user->username = 'GeLo';
32 |
33 | $serialize = $serializer->serialize($user, $format);
34 | // echo $serialize;
35 |
36 | $deserialize = $serializer->deserialize($serialize, User::class, $format);
37 | // $deserialize == $user
38 | ```
39 |
40 | ## Result
41 |
42 | ``` xml
43 |
44 |
45 | ```
46 |
47 | ## Definitions
48 |
49 | If you prefer use an other definition format, the following examples are identical.
50 |
51 | ### XML
52 |
53 | ``` xml
54 |
55 |
56 |
62 |
63 |
64 |
65 |
66 | ```
67 |
68 | ### YAML
69 |
70 | ``` yaml
71 | Acme\User:
72 | properties:
73 | username:
74 | xml_attribute: true
75 | ```
76 |
77 | ### JSON
78 |
79 | ``` json
80 | {
81 | "Acme\\User": {
82 | "properties": {
83 | "username": {
84 | "xml_attribute": true
85 | }
86 | }
87 | }
88 | }
89 | ```
90 |
91 |
--------------------------------------------------------------------------------
/doc/definition/xml_collection.md:
--------------------------------------------------------------------------------
1 | # XML Collection
2 |
3 | The XML collection is dedicated to XML and allow you to control how a collection is (de)-serialized in XML.
4 |
5 | ## Example
6 |
7 | In this example, we configure the roles property.
8 |
9 | ``` php
10 | namespace Acme;
11 |
12 | use Ivory\Serializer\Mapping\Annotation as Serializer;
13 |
14 | class User
15 | {
16 | /**
17 | * @Serializer\XmlCollection({
18 | * entry = "entry",
19 | * entryAttribute = "key",
20 | * keyAsNode = true,
21 | * keyAsAttibute = false,
22 | * inline = false
23 | * })
24 | *
25 | * @var string[]
26 | */
27 | public $roles = [];
28 | }
29 | ```
30 |
31 | ## Usage
32 |
33 | ``` php
34 | use Acme\User;
35 |
36 | $user = new User();
37 | $user->roles[] = 'reader';
38 | $user->roles[] = 'writer';
39 |
40 | $serialize = $serializer->serialize($user, $format);
41 | // echo $serialize;
42 |
43 | $deserialize = $serializer->deserialize($serialize, User::class, $format);
44 | // $deserialize == $user
45 | ```
46 |
47 | ## Results
48 |
49 | ``` xml
50 |
51 |
52 |
53 | reader
54 | writer
55 |
56 |
57 | ```
58 |
59 | ### Entry
60 |
61 | The entry option allows you to configure the node name of each entry of the collection (default "entry").
62 |
63 | ### Entry Attribute
64 |
65 | The entry attribute option allows you to configure the name of each entry attribute of the collection (default "key").
66 |
67 | ### Key As Node
68 |
69 | The key as node option allows you to configure if each keys of the collection should put the as node. If the key is
70 | not valid, then the key will be put as attribute using the entry attribute option.
71 |
72 | ### Key As Attribute
73 |
74 | The key as attribute option allows you to configure if each keys of the collection should put as attribute using the
75 | entry attribute option.
76 |
77 | ### Inline
78 |
79 | The inline option allows you to configure if the collection should be inlined.
80 |
81 | ``` php
82 | namespace Acme;
83 |
84 | use Ivory\Serializer\Mapping\Annotation as Serializer;
85 |
86 | class User
87 | {
88 | /**
89 | * @Serializer\XmlCollection({
90 | * entry = "role",
91 | * inline = true
92 | * })
93 | *
94 | * @var string[]
95 | */
96 | public $roles = [];
97 | }
98 | ```
99 |
100 | ``` xml
101 |
102 |
103 | reader
104 | writer
105 |
106 | ```
107 |
108 | ## Definitions
109 |
110 | If you prefer use an other definition format, the following examples are identical.
111 |
112 | ### XML
113 |
114 | ``` xml
115 |
116 |
117 |
123 |
124 |
132 |
133 |
134 | ```
135 |
136 | ### YAML
137 |
138 | ``` yaml
139 | Acme\User:
140 | properties:
141 | roles:
142 | xml_entry: entry
143 | xml_entry_attribute: key
144 | xml_key_as_node: true
145 | xml_key_as_attribute: false
146 | xml_inline: false
147 | ```
148 |
149 | ### JSON
150 |
151 | ``` json
152 | {
153 | "Acme\\User": {
154 | "properties": {
155 | "username": {
156 | "xml_entry": "entry",
157 | "xml_entry_attribute": "key",
158 | "xml_key_as_node": true,
159 | "xml_key_as_attribute": false,
160 | "xml_inline": false
161 | }
162 | }
163 | }
164 | }
165 | ```
166 |
167 |
168 |
--------------------------------------------------------------------------------
/doc/definition/xml_root.md:
--------------------------------------------------------------------------------
1 | # XML Root
2 |
3 | The XML root is dedicated to XML and allows you to configure the top XML root node name.
4 |
5 | ## Example
6 |
7 | In this example, we configure the XML top root node name for the User class.
8 |
9 | ``` php
10 | namespace Acme;
11 |
12 | use Ivory\Serializer\Mapping\Annotation as Serializer;
13 |
14 | /**
15 | * @Serializer\XmlRoot("user")
16 | */
17 | class User
18 | {
19 | /**
20 | * @var string
21 | */
22 | public $username;
23 | }
24 | ```
25 |
26 | **The XML root node is only used for the TOP root node and is not used for collection. If you want to configure the
27 | collection entry, use the [`XmlCollection`](/doc/definition/xml_collection.md) instead.**
28 |
29 | ## Usage
30 |
31 | ``` php
32 | use Acme\User;
33 |
34 | $user = new User();
35 | $user->username = 'GeLo';
36 |
37 | $serialize = $serializer->serialize($user, $format);
38 | // echo $serialize;
39 |
40 | $deserialize = $serializer->deserialize($serialize, User::class, $format);
41 | // $deserialize == $user
42 | ```
43 |
44 | ## Result
45 |
46 | ``` xml
47 |
48 |
49 | GeLo
50 |
51 | ```
52 |
53 | ## Definitions
54 |
55 | If you prefer use an other definition format, the following examples are identical.
56 |
57 | ### XML
58 |
59 | ``` xml
60 |
61 |
62 |
68 |
69 |
70 |
71 |
72 | ```
73 |
74 | ### YAML
75 |
76 | ``` yaml
77 | Acme\User:
78 | xml_root: user
79 | properties:
80 | username: ~
81 | ```
82 |
83 | ### JSON
84 |
85 | ``` json
86 | {
87 | "Acme\\User": {
88 | "xml_root": "user",
89 | "properties": {
90 | "username": {}
91 | }
92 | }
93 | }
94 | ```
95 |
--------------------------------------------------------------------------------
/doc/definition/xml_value.md:
--------------------------------------------------------------------------------
1 | # XML Value
2 |
3 | The XML value is dedicated to XML and allow you to put a property as XML value.
4 |
5 | ## Example
6 |
7 | In this example, we configure the username property to be put as XML value.
8 |
9 | ``` php
10 | namespace Acme;
11 |
12 | use Ivory\Serializer\Mapping\Annotation as Serializer;
13 |
14 | class User
15 | {
16 | /**
17 | * @Serializer\XmlValue
18 | *
19 | * @var string
20 | */
21 | public $username;
22 | }
23 | ```
24 |
25 | ## Usage
26 |
27 | ``` php
28 | use Acme\User;
29 |
30 | $user = new User();
31 | $user->username = 'GeLo';
32 |
33 | $serialize = $serializer->serialize($user, $format);
34 | // echo $serialize;
35 |
36 | $deserialize = $serializer->deserialize($serialize, User::class, $format);
37 | // $deserialize == $user
38 | ```
39 |
40 | ## Result
41 |
42 | ``` xml
43 |
44 | GeLo
45 | ```
46 |
47 | ## Definitions
48 |
49 | If you prefer use an other definition format, the following examples are identical.
50 |
51 | ### XML
52 |
53 | ``` xml
54 |
55 |
56 |
62 |
63 |
64 |
65 |
66 | ```
67 |
68 | ### YAML
69 |
70 | ``` yaml
71 | Acme\User:
72 | properties:
73 | username:
74 | xml_value: true
75 | ```
76 |
77 | ### JSON
78 |
79 | ``` json
80 | {
81 | "Acme\\User": {
82 | "properties": {
83 | "username": {
84 | "xml_value": true
85 | }
86 | }
87 | }
88 | }
89 | ```
90 |
91 |
--------------------------------------------------------------------------------
/doc/docker.md:
--------------------------------------------------------------------------------
1 | # Docker
2 |
3 | The most easy way to set up the project is to install [Docker](https://www.docker.com) and
4 | [Docker Composer](https://docs.docker.com/compose/) and build the project.
5 |
6 | ## Configure
7 |
8 | The configuration is shipped with a distribution environment file allowing you to customize your IDE and XDebug
9 | settings as well as your current user/group ID:
10 |
11 | ``` bash
12 | $ cp .env.dist .env
13 | ```
14 |
15 | **The most important part is the `USER_ID` and `GROUP_ID` which should match your current user/group.**
16 |
17 | ## Build
18 |
19 | Once you have configured your environment, you can build the project:
20 |
21 | ``` bash
22 | $ docker-compose build
23 | ```
24 |
25 | ## Composer
26 |
27 | Install the dependencies via [Composer](https://getcomposer.org/):
28 |
29 | ``` bash
30 | $ docker-compose run --rm php composer install
31 | ```
32 |
33 | ## Tests
34 |
35 | To run the test suite, you can use:
36 |
37 | ``` bash
38 | $ docker-compose run --rm php vendor/bin/phpunit
39 | ```
40 |
41 | If you want to run the test suite against [HHVM](http://hhvm.com/), you can use:
42 |
43 | ``` bash
44 | $ docker-compose run --rm hhvm vendor/bin/phpunit
45 | ```
46 |
47 | ## XDebug
48 |
49 | If you want to use XDebug, make sure you have fully configured your `.env` file and use:
50 |
51 | ``` bash
52 | $ docker-compose run --rm -e XDEBUG=1 php vendor/bin/phpunit
53 | ```
54 |
--------------------------------------------------------------------------------
/doc/installation.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | To install the Ivory Serializer library, you will need [Composer](http://getcomposer.org). It's a PHP 5.3+ dependency
4 | manager which allows you to declare the dependent libraries your project needs and it will install & autoload them for
5 | you.
6 |
7 | ## Set up Composer
8 |
9 | Composer comes with a simple phar file. To easily access it from anywhere on your system, you can execute:
10 |
11 | ``` bash
12 | $ curl -s https://getcomposer.org/installer | php
13 | $ sudo mv composer.phar /usr/local/bin/composer
14 | ```
15 |
16 | ## Download the library
17 |
18 | Require the library in your `composer.json` file:
19 |
20 | ``` bash
21 | $ composer require egeloen/serializer
22 | ```
23 |
24 | ## Download additional libraries (optional)
25 |
26 | The Ivory Serializer relies on third libraries in order to not reinvent the wheel...
27 |
28 | ### Doctrine annotations
29 |
30 | The Doctrine annotations library allows you to use the annotations mapping on your classes.
31 |
32 | ``` bash
33 | $ composer require doctrine/annotations
34 | ```
35 |
36 | ### Symfony event dispatcher
37 |
38 | The Symfony event dispatcher library allows you to hook into the (de)-serialization.
39 |
40 | ``` bash
41 | $ composer require symfony/event-dispatcher
42 | ```
43 |
44 | ### Symfony property info
45 |
46 | The Symfony property info library allows you to get a more efficient mapping discovery.
47 |
48 | ``` bash
49 | $ composer require symfony/property-info
50 | ```
51 |
52 | ### Symfony property access
53 |
54 | The Symfony property access library allows you to get/set values from your objects.
55 |
56 | ``` bash
57 | $ composer require symfony/property-access
58 | ```
59 |
60 | ### Symfony yaml
61 |
62 | The Symfony yaml library allows you to use the yaml format in your mapping or your (de)-serialization.
63 |
64 | ``` bash
65 | $ composer require symfony/yaml
66 | ```
67 |
68 | ## Autoload
69 |
70 | So easy, you just have to require the generated autoload file and you are already ready to play:
71 |
72 | ``` php
73 | serialize(new AcmeObject(), Format::JSON);
21 | ```
22 |
23 | The `serialize` method also accepts a context as third argument which is useful if you want to exclude properties
24 | according to groups or API version. If you want to learn more about it, you can read this
25 | [documentation](/doc/context.md).
26 |
27 | ``` php
28 | use Acme\Model\AcmeObject;
29 | use Ivory\Serializer\Context\Context;
30 | use Ivory\Serializer\Format;
31 |
32 | $context = new Context();
33 | // Configure the context...
34 |
35 | $json = $serializer->serialize(
36 | new AcmeObject(),
37 | Format::JSON,
38 | $context
39 | );
40 | ```
41 |
42 | ## Deserialization
43 |
44 | For deserializing data, call `deserialize` with your data as first argument, your type as second argument and your
45 | format as third argument:
46 |
47 | ``` php
48 | use Acme\Model\AcmeObject;
49 | use Ivory\Serializer\Format;
50 |
51 | $object = $serializer->deserialize('{"foo":"bar"}', AcmeObject::class, Format::JSON);
52 | ```
53 |
54 | The `deserialize` method also accepts a context as fourth argument which is useful if you want to exclude properties
55 | according to groups or API version. If you want to learn more about it, you can read this
56 | [documentation](/doc/context.md).
57 |
58 | ``` php
59 | use Acme\Model\AcmeObject;
60 | use Ivory\Serializer\Context\Context;
61 | use Ivory\Serializer\Format;
62 |
63 | $context = new Context();
64 | // Configure the context...
65 |
66 | $object = $serializer->deserialize(
67 | '{"foo":"bar"}',
68 | AcmeObject::class,
69 | Format::JSON,
70 | $context
71 | );
72 | ```
73 |
--------------------------------------------------------------------------------
/doc/visitor.md:
--------------------------------------------------------------------------------
1 | # Visitor
2 |
3 | When you (de)-serialize your data, the serializer will choose a visitor according to your format (csv, json, ...) and
4 | your direction (serialization or deserialization). Each format/direction have a dedicated visitor in order to
5 | handle this specific use case.
6 |
7 | ## Built-in
8 |
9 | The library is shipped with some built-in visitors:
10 |
11 | | Name | Serialization | Deserialization |
12 | | ---- | ------------------------ | -------------------------- |
13 | | CSV | CsvSerializationVisitor | CsvDeserializationVisitor |
14 | | JSON | JsonSerializationVisitor | JsonDeserializationVisitor |
15 | | XML | XmlSerializationVisitor | XmlDeserializationVisitor |
16 | | YAML | YamlSerializationVisitor | YamlDeserializationVisitor |
17 |
18 | ## Custom
19 |
20 | If you want to create your own visitor for a new format for example, you can implement the
21 | `Ivory\Serializer\Visitor\VisitorInterface` or extend the `Ivory\Serializer\Visitor\AbstractSerializationVisitor`
22 | for serializing or the `Ivory\Serializer\Visitor\AbstractDeserializationVisitor` for deserializing.
23 |
24 | **Implementing a visitor is a tedious work and require to master the internal of the library...**
25 |
26 | Once you have created your visitor, you need to register it on the Serializer in order to use it:
27 |
28 | ``` php
29 | use Acme\Serializer\Visitor\CustomDeserializationVisitor;
30 | use Acme\Serializer\Visitor\CustomSerializationVisitor;
31 | use Ivory\Serializer\Direction;
32 | use Ivory\Serializer\Serializer;
33 | use Ivory\Serializer\Visitor\VisitorRegistry;
34 |
35 | $format = 'custom';
36 |
37 | $visitorRegistry = VisitorRegistry::create([
38 | Direction::SERIALIZATION => [
39 | $format => new CustomSerializationVisitor(),
40 | ],
41 | Direction::DESERIALIZATION => [
42 | $format => new CustomDeserializationVisitor(),
43 | ],
44 | ]);
45 |
46 | $serializer = new Serializer(null, $visitorRegistry);
47 |
48 | $stdClass = new \stdClass();
49 | $stdClass->foo = true;
50 | $stdClass->bar = ['foo', [123, 432.1]];
51 |
52 | echo $serializer->serialize($stdClass, $format);
53 | ```
54 |
--------------------------------------------------------------------------------
/src/Accessor/AccessorInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Accessor;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | interface AccessorInterface
18 | {
19 | /**
20 | * @param object $object
21 | * @param string $property
22 | *
23 | * @return mixed
24 | */
25 | public function getValue($object, $property);
26 | }
27 |
--------------------------------------------------------------------------------
/src/Accessor/ReflectionAccessor.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Accessor;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class ReflectionAccessor implements AccessorInterface
18 | {
19 | /**
20 | * @var \ReflectionProperty[]
21 | */
22 | private $properties = [];
23 |
24 | /**
25 | * @var \ReflectionMethod[]
26 | */
27 | private $methods = [];
28 |
29 | /**
30 | * {@inheritdoc}
31 | */
32 | public function getValue($object, $property)
33 | {
34 | if (property_exists($object, $property)) {
35 | return $this->getReflectionProperty($object, $property)->getValue($object);
36 | }
37 |
38 | return $this->getMethodValue($object, $property);
39 | }
40 |
41 | /**
42 | * @param object $object
43 | * @param string $property
44 | *
45 | * @return mixed
46 | */
47 | private function getMethodValue($object, $property)
48 | {
49 | $methods = [$property];
50 |
51 | if (method_exists($object, $property)) {
52 | return $this->getReflectionMethod($object, $property)->invoke($object);
53 | }
54 |
55 | $methodSuffix = ucfirst($property);
56 |
57 | foreach (['get', 'has', 'is'] as $methodPrefix) {
58 | $methods[] = $method = $methodPrefix.$methodSuffix;
59 |
60 | if (method_exists($object, $method)) {
61 | return $this->getReflectionMethod($object, $method)->invoke($object);
62 | }
63 | }
64 |
65 | throw new \InvalidArgumentException(sprintf(
66 | 'The property "%s" or methods %s don\'t exist on class "%s".',
67 | $property,
68 | '"'.implode('", "', $methods).'"',
69 | get_class($object)
70 | ));
71 | }
72 |
73 | /**
74 | * @param object $object
75 | * @param string $property
76 | *
77 | * @return \ReflectionProperty
78 | */
79 | private function getReflectionProperty($object, $property)
80 | {
81 | if (isset($this->properties[$key = $this->getCacheKey($object, $property)])) {
82 | return $this->properties[$key];
83 | }
84 |
85 | $reflection = new \ReflectionProperty($object, $property);
86 | $reflection->setAccessible(true);
87 |
88 | return $this->properties[$key] = $reflection;
89 | }
90 |
91 | /**
92 | * @param object $object
93 | * @param string $method
94 | *
95 | * @return \ReflectionMethod
96 | */
97 | private function getReflectionMethod($object, $method)
98 | {
99 | if (isset($this->methods[$key = $this->getCacheKey($object, $method)])) {
100 | return $this->methods[$key];
101 | }
102 |
103 | $reflection = new \ReflectionMethod($object, $method);
104 | $reflection->setAccessible(true);
105 |
106 | return $this->methods[$key] = $reflection;
107 | }
108 |
109 | /**
110 | * @param object $object
111 | * @param string $key
112 | *
113 | * @return string
114 | */
115 | private function getCacheKey($object, $key)
116 | {
117 | return get_class($object).'::'.$key;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/Accessor/SymfonyAccessor.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Accessor;
13 |
14 | use Symfony\Component\PropertyAccess\PropertyAccess;
15 | use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class SymfonyAccessor implements AccessorInterface
21 | {
22 | /**
23 | * @var PropertyAccessorInterface
24 | */
25 | private $propertyAccessor;
26 |
27 | /**
28 | * @param PropertyAccessorInterface|null $propertyAccessor
29 | */
30 | public function __construct(PropertyAccessorInterface $propertyAccessor = null)
31 | {
32 | $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
33 | }
34 |
35 | /**
36 | * {@inheritdoc}
37 | */
38 | public function getValue($object, $property)
39 | {
40 | return $this->propertyAccessor->getValue($object, $property);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Direction.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | final class Direction
18 | {
19 | const SERIALIZATION = 1;
20 | const DESERIALIZATION = 2;
21 |
22 | /**
23 | * @codeCoverageIgnore
24 | */
25 | private function __construct()
26 | {
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Event/AbstractClassMetadataEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Event;
13 |
14 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
15 | use Symfony\Component\EventDispatcher\Event;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class AbstractClassMetadataEvent extends Event
21 | {
22 | /**
23 | * @var ClassMetadataInterface|null
24 | */
25 | protected $classMetadata;
26 |
27 | /**
28 | * @return ClassMetadataInterface
29 | */
30 | public function getClassMetadata()
31 | {
32 | return $this->classMetadata;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Event/AbstractEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Event;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 | use Symfony\Component\EventDispatcher\Event;
17 |
18 | /**
19 | * @author GeLo
20 | */
21 | abstract class AbstractEvent extends Event
22 | {
23 | /**
24 | * @var mixed
25 | */
26 | protected $data;
27 |
28 | /**
29 | * @var TypeMetadataInterface
30 | */
31 | protected $type;
32 |
33 | /**
34 | * @var ContextInterface
35 | */
36 | private $context;
37 |
38 | /**
39 | * @param mixed $data
40 | * @param TypeMetadataInterface $type
41 | * @param ContextInterface $context
42 | */
43 | public function __construct($data, TypeMetadataInterface $type, ContextInterface $context)
44 | {
45 | $this->data = $data;
46 | $this->type = $type;
47 | $this->context = $context;
48 | }
49 |
50 | /**
51 | * @return mixed
52 | */
53 | public function getData()
54 | {
55 | return $this->data;
56 | }
57 |
58 | /**
59 | * @return TypeMetadataInterface
60 | */
61 | public function getType()
62 | {
63 | return $this->type;
64 | }
65 |
66 | /**
67 | * @return ContextInterface
68 | */
69 | public function getContext()
70 | {
71 | return $this->context;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Event/AbstractPreEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Event;
13 |
14 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | abstract class AbstractPreEvent extends AbstractEvent
20 | {
21 | /**
22 | * @param mixed $data
23 | */
24 | public function setData($data)
25 | {
26 | $this->data = $data;
27 | }
28 |
29 | /**
30 | * @param TypeMetadataInterface $type
31 | */
32 | public function setType(TypeMetadataInterface $type)
33 | {
34 | $this->type = $type;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Event/ClassMetadataLoadEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Event;
13 |
14 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | class ClassMetadataLoadEvent extends AbstractClassMetadataEvent
20 | {
21 | /**
22 | * @param ClassMetadataInterface $classMetadata
23 | */
24 | public function __construct(ClassMetadataInterface $classMetadata)
25 | {
26 | $this->classMetadata = $classMetadata;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Event/ClassMetadataNotFoundEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Event;
13 |
14 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | class ClassMetadataNotFoundEvent extends AbstractClassMetadataEvent
20 | {
21 | /**
22 | * @var string
23 | */
24 | private $class;
25 |
26 | /**
27 | * @param string $class
28 | */
29 | public function __construct($class)
30 | {
31 | $this->class = $class;
32 | }
33 |
34 | /**
35 | * @return string
36 | */
37 | public function getClass()
38 | {
39 | return $this->class;
40 | }
41 |
42 | /**
43 | * @param ClassMetadataInterface $classMetadata
44 | */
45 | public function setClassMetadata(ClassMetadataInterface $classMetadata)
46 | {
47 | $this->classMetadata = $classMetadata;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Event/PostDeserializeEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Event;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class PostDeserializeEvent extends AbstractEvent
18 | {
19 | }
20 |
--------------------------------------------------------------------------------
/src/Event/PostSerializeEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Event;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class PostSerializeEvent extends AbstractEvent
18 | {
19 | }
20 |
--------------------------------------------------------------------------------
/src/Event/PreDeserializeEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Event;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class PreDeserializeEvent extends AbstractPreEvent
18 | {
19 | }
20 |
--------------------------------------------------------------------------------
/src/Event/PreSerializeEvent.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Event;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class PreSerializeEvent extends AbstractPreEvent
18 | {
19 | }
20 |
--------------------------------------------------------------------------------
/src/Event/SerializerEvents.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Event;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | final class SerializerEvents
18 | {
19 | const CLASS_METADATA_LOAD = 'serializer.class_metadata.load';
20 | const CLASS_METADATA_NOT_FOUND = 'serializer.class_metadata.not_found';
21 |
22 | const PRE_SERIALIZE = 'serializer.pre_serialize';
23 | const POST_SERIALIZE = 'serializer.post_serialize';
24 |
25 | const PRE_DESERIALIZE = 'serializer.pre_deserialize';
26 | const POST_DESERIALIZE = 'serializer.post_deserialize';
27 |
28 | private function __construct()
29 | {
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Exclusion/ChainExclusionStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Exclusion;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
16 | use Ivory\Serializer\Mapping\PropertyMetadataInterface;
17 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
18 |
19 | /**
20 | * @author GeLo
21 | */
22 | class ChainExclusionStrategy implements ExclusionStrategyInterface
23 | {
24 | /**
25 | * @var ExclusionStrategyInterface[]
26 | */
27 | private $strategies = [];
28 |
29 | /**
30 | * @param ExclusionStrategyInterface[] $strategies
31 | */
32 | public function __construct(array $strategies)
33 | {
34 | $this->strategies = $strategies;
35 | }
36 |
37 | /**
38 | * {@inheritdoc}
39 | */
40 | public function skipClass(ClassMetadataInterface $class, ContextInterface $context)
41 | {
42 | foreach ($this->strategies as $strategy) {
43 | if ($strategy->skipClass($class, $context)) {
44 | return true;
45 | }
46 | }
47 |
48 | return false;
49 | }
50 |
51 | /**
52 | * {@inheritdoc}
53 | */
54 | public function skipProperty(PropertyMetadataInterface $property, ContextInterface $context)
55 | {
56 | foreach ($this->strategies as $strategy) {
57 | if ($strategy->skipProperty($property, $context)) {
58 | return true;
59 | }
60 | }
61 |
62 | return false;
63 | }
64 |
65 | /**
66 | * {@inheritdoc}
67 | */
68 | public function skipType(TypeMetadataInterface $type, ContextInterface $context)
69 | {
70 | foreach ($this->strategies as $strategy) {
71 | if ($strategy->skipType($type, $context)) {
72 | return true;
73 | }
74 | }
75 |
76 | return false;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/Exclusion/ExclusionPolicy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Exclusion;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | final class ExclusionPolicy
18 | {
19 | const ALL = 'all';
20 | const NONE = 'none';
21 |
22 | /**
23 | * @codeCoverageIgnore
24 | */
25 | private function __construct()
26 | {
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Exclusion/ExclusionStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Exclusion;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
16 | use Ivory\Serializer\Mapping\PropertyMetadataInterface;
17 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
18 |
19 | /**
20 | * @author GeLo
21 | */
22 | class ExclusionStrategy implements ExclusionStrategyInterface
23 | {
24 | /**
25 | * {@inheritdoc}
26 | */
27 | public function skipClass(ClassMetadataInterface $class, ContextInterface $context)
28 | {
29 | return false;
30 | }
31 |
32 | /**
33 | * {@inheritdoc}
34 | */
35 | public function skipProperty(PropertyMetadataInterface $property, ContextInterface $context)
36 | {
37 | return false;
38 | }
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | public function skipType(TypeMetadataInterface $type, ContextInterface $context)
44 | {
45 | return false;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Exclusion/ExclusionStrategyInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Exclusion;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
16 | use Ivory\Serializer\Mapping\PropertyMetadataInterface;
17 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
18 |
19 | /**
20 | * @author GeLo
21 | */
22 | interface ExclusionStrategyInterface
23 | {
24 | /**
25 | * @param ClassMetadataInterface $class
26 | * @param ContextInterface $context
27 | *
28 | * @return bool
29 | */
30 | public function skipClass(ClassMetadataInterface $class, ContextInterface $context);
31 |
32 | /**
33 | * @param PropertyMetadataInterface $property
34 | * @param ContextInterface $context
35 | *
36 | * @return bool
37 | */
38 | public function skipProperty(PropertyMetadataInterface $property, ContextInterface $context);
39 |
40 | /**
41 | * @param TypeMetadataInterface $type
42 | * @param ContextInterface $context
43 | *
44 | * @return bool
45 | */
46 | public function skipType(TypeMetadataInterface $type, ContextInterface $context);
47 | }
48 |
--------------------------------------------------------------------------------
/src/Exclusion/GroupsExclusionStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Exclusion;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\PropertyMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class GroupsExclusionStrategy extends ExclusionStrategy
21 | {
22 | const GROUP_DEFAULT = 'Default';
23 |
24 | /**
25 | * @var string[]
26 | */
27 | private $groups;
28 |
29 | /**
30 | * @param string[] $groups
31 | */
32 | public function __construct(array $groups)
33 | {
34 | $this->groups = $groups;
35 | }
36 |
37 | /**
38 | * {@inheritdoc}
39 | */
40 | public function skipProperty(PropertyMetadataInterface $property, ContextInterface $context)
41 | {
42 | if (!$property->hasGroups()) {
43 | return !in_array(self::GROUP_DEFAULT, $this->groups, true);
44 | }
45 |
46 | foreach ($this->groups as $group) {
47 | if ($property->hasGroup($group)) {
48 | return false;
49 | }
50 | }
51 |
52 | return true;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Exclusion/MaxDepthExclusionStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Exclusion;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
16 | use Ivory\Serializer\Mapping\PropertyMetadataInterface;
17 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
18 |
19 | /**
20 | * @author GeLo
21 | */
22 | class MaxDepthExclusionStrategy extends ExclusionStrategy
23 | {
24 | /**
25 | * @var int
26 | */
27 | private $circularReferenceLimit;
28 |
29 | /**
30 | * @param int $circularReferenceLimit
31 | */
32 | public function __construct($circularReferenceLimit = 1)
33 | {
34 | $this->circularReferenceLimit = $circularReferenceLimit;
35 | }
36 |
37 | /**
38 | * {@inheritdoc}
39 | */
40 | public function skipClass(ClassMetadataInterface $class, ContextInterface $context)
41 | {
42 | return $this->skip($context);
43 | }
44 |
45 | /**
46 | * {@inheritdoc}
47 | */
48 | public function skipType(TypeMetadataInterface $type, ContextInterface $context)
49 | {
50 | return $this->skip($context);
51 | }
52 |
53 | /**
54 | * @param ContextInterface $context
55 | *
56 | * @return bool
57 | */
58 | private function skip(ContextInterface $context)
59 | {
60 | $dataStack = $context->getDataStack();
61 | $metadataStack = $context->getMetadataStack();
62 | $references = [];
63 | $depth = 0;
64 |
65 | for ($index = count($metadataStack) - 1; $index >= 0; --$index) {
66 | $metadata = $metadataStack[$index];
67 | $data = $dataStack[$index];
68 |
69 | if ($metadata instanceof ClassMetadataInterface) {
70 | $hash = spl_object_hash($data);
71 |
72 | if (!isset($references[$hash])) {
73 | $references[$hash] = 0;
74 | }
75 |
76 | if (++$references[$hash] > $this->circularReferenceLimit) {
77 | return true;
78 | }
79 | }
80 |
81 | if ($metadata instanceof TypeMetadataInterface) {
82 | ++$depth;
83 | }
84 |
85 | if (!$metadata instanceof PropertyMetadataInterface) {
86 | continue;
87 | }
88 |
89 | ++$depth;
90 |
91 | if (!$metadata->hasMaxDepth()) {
92 | continue;
93 | }
94 |
95 | if ($depth > $metadata->getMaxDepth()) {
96 | return true;
97 | }
98 | }
99 |
100 | return false;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/Exclusion/VersionExclusionStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Exclusion;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\PropertyMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class VersionExclusionStrategy extends ExclusionStrategy
21 | {
22 | /**
23 | * @var string
24 | */
25 | private $version;
26 |
27 | /**
28 | * @param string $version
29 | */
30 | public function __construct($version)
31 | {
32 | $this->version = $version;
33 | }
34 |
35 | /**
36 | * {@inheritdoc}
37 | */
38 | public function skipProperty(PropertyMetadataInterface $property, ContextInterface $context)
39 | {
40 | if ($property->hasSinceVersion() && version_compare($property->getSinceVersion(), $this->version, '>')) {
41 | return true;
42 | }
43 |
44 | if ($property->hasUntilVersion() && version_compare($property->getUntilVersion(), $this->version, '<')) {
45 | return true;
46 | }
47 |
48 | return false;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Format.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | final class Format
18 | {
19 | const CSV = 'csv';
20 | const JSON = 'json';
21 | const XML = 'xml';
22 | const YAML = 'yaml';
23 |
24 | /**
25 | * @codeCoverageIgnore
26 | */
27 | private function __construct()
28 | {
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Instantiator/DoctrineInstantiator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Instantiator;
13 |
14 | use Doctrine\Instantiator\Instantiator;
15 | use Doctrine\Instantiator\InstantiatorInterface as DoctrineInstantiatorInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class DoctrineInstantiator implements InstantiatorInterface
21 | {
22 | /**
23 | * @var DoctrineInstantiatorInterface
24 | */
25 | private $instantiator;
26 |
27 | /**
28 | * @param DoctrineInstantiatorInterface|null $instantiator
29 | */
30 | public function __construct(DoctrineInstantiatorInterface $instantiator = null)
31 | {
32 | $this->instantiator = $instantiator ?: new Instantiator();
33 | }
34 |
35 | /**
36 | * {@inheritdoc}
37 | */
38 | public function instantiate($class)
39 | {
40 | return $this->instantiator->instantiate($class);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Instantiator/InstantiatorInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Instantiator;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | interface InstantiatorInterface
18 | {
19 | /**
20 | * @param string $class
21 | *
22 | * @return object
23 | */
24 | public function instantiate($class);
25 | }
26 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/Accessor.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class Accessor
21 | {
22 | /**
23 | * @Required
24 | *
25 | * @var string
26 | */
27 | public $accessor;
28 | }
29 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/Alias.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class Alias
21 | {
22 | /**
23 | * @Required
24 | *
25 | * @var string
26 | */
27 | public $alias;
28 | }
29 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/Exclude.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class Exclude
21 | {
22 | }
23 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/ExclusionPolicy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"CLASS"})
17 | *
18 | * @author GeLo
19 | */
20 | class ExclusionPolicy
21 | {
22 | /**
23 | * @Required
24 | *
25 | * @var string
26 | */
27 | public $policy;
28 | }
29 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/Expose.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class Expose
21 | {
22 | }
23 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/Groups.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class Groups
21 | {
22 | /**
23 | * @Required
24 | *
25 | * @var string[]
26 | */
27 | public $groups;
28 | }
29 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/MaxDepth.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class MaxDepth
21 | {
22 | /**
23 | * @Required
24 | *
25 | * @var int
26 | */
27 | public $maxDepth;
28 | }
29 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/Mutator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class Mutator
21 | {
22 | /**
23 | * @Required
24 | *
25 | * @var string
26 | */
27 | public $mutator;
28 | }
29 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/Order.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"CLASS"})
17 | *
18 | * @author GeLo
19 | */
20 | class Order
21 | {
22 | /**
23 | * @Required
24 | *
25 | * @var string|string[]
26 | */
27 | public $order;
28 |
29 | /**
30 | * @param mixed[] $data
31 | */
32 | public function __construct(array $data)
33 | {
34 | $this->order = $data['value'];
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/Readable.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"CLASS", "PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class Readable
21 | {
22 | /**
23 | * @var bool
24 | */
25 | public $readable = true;
26 | }
27 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/Since.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class Since
21 | {
22 | /**
23 | * @Required
24 | *
25 | * @var string
26 | */
27 | public $version;
28 | }
29 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/Type.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class Type
21 | {
22 | /**
23 | * @Required
24 | *
25 | * @var string
26 | */
27 | public $type;
28 | }
29 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/Until.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class Until
21 | {
22 | /**
23 | * @Required
24 | *
25 | * @var string
26 | */
27 | public $version;
28 | }
29 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/Writable.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"CLASS", "PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class Writable
21 | {
22 | /**
23 | * @var bool
24 | */
25 | public $writable = true;
26 | }
27 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/XmlAttribute.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class XmlAttribute
21 | {
22 | }
23 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/XmlCollection.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class XmlCollection
21 | {
22 | /**
23 | * @var string
24 | */
25 | public $entry;
26 |
27 | /**
28 | * @var string
29 | */
30 | public $entryAttribute;
31 |
32 | /**
33 | * @var bool
34 | */
35 | public $keyAsAttribute;
36 |
37 | /**
38 | * @var bool
39 | */
40 | public $keyAsNode;
41 |
42 | /**
43 | * @var bool
44 | */
45 | public $inline;
46 | }
47 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/XmlRoot.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"CLASS"})
17 | *
18 | * @author GeLo
19 | */
20 | class XmlRoot
21 | {
22 | /**
23 | * @Required
24 | *
25 | * @var string
26 | */
27 | public $name;
28 | }
29 |
--------------------------------------------------------------------------------
/src/Mapping/Annotation/XmlValue.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Annotation;
13 |
14 | /**
15 | * @Annotation
16 | * @Target({"PROPERTY", "METHOD"})
17 | *
18 | * @author GeLo
19 | */
20 | class XmlValue
21 | {
22 | }
23 |
--------------------------------------------------------------------------------
/src/Mapping/ClassMetadata.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class ClassMetadata implements ClassMetadataInterface
18 | {
19 | /**
20 | * @var string
21 | */
22 | private $name;
23 |
24 | /**
25 | * @var PropertyMetadataInterface[]
26 | */
27 | private $properties = [];
28 |
29 | /**
30 | * @var string|null
31 | */
32 | private $xmlRoot;
33 |
34 | /**
35 | * @param string $name
36 | */
37 | public function __construct($name)
38 | {
39 | $this->setName($name);
40 | }
41 |
42 | /**
43 | * {@inheritdoc}
44 | */
45 | public function getName()
46 | {
47 | return $this->name;
48 | }
49 |
50 | /**
51 | * {@inheritdoc}
52 | */
53 | public function setName($name)
54 | {
55 | if (!class_exists($name)) {
56 | throw new \InvalidArgumentException(sprintf('The class "%s" does not exist.', $name));
57 | }
58 |
59 | $this->name = $name;
60 | }
61 |
62 | /**
63 | * {@inheritdoc}
64 | */
65 | public function hasProperties()
66 | {
67 | return !empty($this->properties);
68 | }
69 |
70 | /**
71 | * {@inheritdoc}
72 | */
73 | public function getProperties()
74 | {
75 | return $this->properties;
76 | }
77 |
78 | /**
79 | * {@inheritdoc}
80 | */
81 | public function setProperties(array $properties)
82 | {
83 | $this->properties = [];
84 |
85 | foreach ($properties as $property) {
86 | $this->addProperty($property);
87 | }
88 | }
89 |
90 | /**
91 | * {@inheritdoc}
92 | */
93 | public function hasProperty($name)
94 | {
95 | return isset($this->properties[$name]);
96 | }
97 |
98 | /**
99 | * {@inheritdoc}
100 | */
101 | public function getProperty($name)
102 | {
103 | return $this->hasProperty($name) ? $this->properties[$name] : null;
104 | }
105 |
106 | /**
107 | * {@inheritdoc}
108 | */
109 | public function addProperty(PropertyMetadataInterface $property)
110 | {
111 | $name = $property->getName();
112 |
113 | if ($this->hasProperty($name)) {
114 | $this->getProperty($name)->merge($property);
115 | } else {
116 | $this->properties[$name] = $property;
117 | }
118 | }
119 |
120 | /**
121 | * {@inheritdoc}
122 | */
123 | public function removeProperty($name)
124 | {
125 | unset($this->properties[$name]);
126 | }
127 |
128 | /**
129 | * {@inheritdoc}
130 | */
131 | public function hasXmlRoot()
132 | {
133 | return $this->xmlRoot !== null;
134 | }
135 |
136 | /**
137 | * {@inheritdoc}
138 | */
139 | public function getXmlRoot()
140 | {
141 | return $this->xmlRoot;
142 | }
143 |
144 | /**
145 | * {@inheritdoc}
146 | */
147 | public function setXmlRoot($xmlRoot)
148 | {
149 | $this->xmlRoot = $xmlRoot;
150 | }
151 |
152 | /**
153 | * {@inheritdoc}
154 | */
155 | public function merge(ClassMetadataInterface $classMetadata)
156 | {
157 | if ($classMetadata->hasXmlRoot()) {
158 | $this->setXmlRoot($classMetadata->getXmlRoot());
159 | }
160 |
161 | foreach ($classMetadata->getProperties() as $property) {
162 | $this->addProperty($property);
163 | }
164 | }
165 |
166 | /**
167 | * {@inheritdoc}
168 | */
169 | public function serialize()
170 | {
171 | return serialize([
172 | $this->name,
173 | $this->properties,
174 | $this->xmlRoot,
175 | ]);
176 | }
177 |
178 | /**
179 | * {@inheritdoc}
180 | */
181 | public function unserialize($serialized)
182 | {
183 | list(
184 | $this->name,
185 | $this->properties,
186 | $this->xmlRoot
187 | ) = unserialize($serialized);
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/src/Mapping/ClassMetadataInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | interface ClassMetadataInterface extends MetadataInterface
18 | {
19 | /**
20 | * @return bool
21 | */
22 | public function hasProperties();
23 |
24 | /**
25 | * @return PropertyMetadataInterface[]
26 | */
27 | public function getProperties();
28 |
29 | /**
30 | * @param PropertyMetadataInterface[] $properties
31 | */
32 | public function setProperties(array $properties);
33 |
34 | /**
35 | * @param string $name
36 | *
37 | * @return bool
38 | */
39 | public function hasProperty($name);
40 |
41 | /**
42 | * @param string $name
43 | *
44 | * @return PropertyMetadataInterface|null
45 | */
46 | public function getProperty($name);
47 |
48 | /**
49 | * @param PropertyMetadataInterface $property
50 | */
51 | public function addProperty(PropertyMetadataInterface $property);
52 |
53 | /**
54 | * @param string $name
55 | */
56 | public function removeProperty($name);
57 |
58 | /**
59 | * @return bool
60 | */
61 | public function hasXmlRoot();
62 |
63 | /**
64 | * @return string|null
65 | */
66 | public function getXmlRoot();
67 |
68 | /**
69 | * @param string|null $xmlRoot
70 | */
71 | public function setXmlRoot($xmlRoot);
72 |
73 | /**
74 | * @param ClassMetadataInterface $classMetadata
75 | */
76 | public function merge(ClassMetadataInterface $classMetadata);
77 | }
78 |
--------------------------------------------------------------------------------
/src/Mapping/Factory/AbstractClassMetadataFactory.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Factory;
13 |
14 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | abstract class AbstractClassMetadataFactory implements ClassMetadataFactoryInterface
20 | {
21 | /**
22 | * @var ClassMetadataInterface[]
23 | */
24 | private $classMetadatas = [];
25 |
26 | /**
27 | * {@inheritdoc}
28 | */
29 | public function getClassMetadata($class)
30 | {
31 | return array_key_exists($class, $this->classMetadatas)
32 | ? $this->classMetadatas[$class]
33 | : $this->classMetadatas[$class] = $this->fetchClassMetadata($class);
34 | }
35 |
36 | /**
37 | * @param string $class
38 | *
39 | * @return ClassMetadataInterface|null
40 | */
41 | abstract protected function fetchClassMetadata($class);
42 | }
43 |
--------------------------------------------------------------------------------
/src/Mapping/Factory/CacheClassMetadataFactory.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Factory;
13 |
14 | use Psr\Cache\CacheItemPoolInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | class CacheClassMetadataFactory extends AbstractClassMetadataFactory
20 | {
21 | /**
22 | * @var ClassMetadataFactoryInterface
23 | */
24 | private $factory;
25 |
26 | /**
27 | * @var CacheItemPoolInterface
28 | */
29 | private $pool;
30 |
31 | /**
32 | * @var string|null
33 | */
34 | private $prefix;
35 |
36 | /**
37 | * @param ClassMetadataFactoryInterface $factory
38 | * @param CacheItemPoolInterface $pool
39 | * @param string|null $prefix
40 | */
41 | public function __construct(ClassMetadataFactoryInterface $factory, CacheItemPoolInterface $pool, $prefix = null)
42 | {
43 | $this->factory = $factory;
44 | $this->pool = $pool;
45 | $this->prefix = $prefix;
46 | }
47 |
48 | /**
49 | * {@inheritdoc}
50 | */
51 | protected function fetchClassMetadata($class)
52 | {
53 | $item = $this->pool->getItem(strtr($this->prefix.$class, '\\', '_'));
54 |
55 | if ($item->isHit()) {
56 | return $item->get();
57 | }
58 |
59 | $classMetadata = $this->factory->getClassMetadata($class);
60 | $this->pool->save($item->set($classMetadata));
61 |
62 | return $classMetadata;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Mapping/Factory/ClassMetadataFactory.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Factory;
13 |
14 | use Doctrine\Common\Annotations\AnnotationReader;
15 | use Ivory\Serializer\Mapping\ClassMetadata;
16 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
17 | use Ivory\Serializer\Mapping\Loader\AnnotationClassMetadataLoader;
18 | use Ivory\Serializer\Mapping\Loader\ChainClassMetadataLoader;
19 | use Ivory\Serializer\Mapping\Loader\ClassMetadataLoaderInterface;
20 | use Ivory\Serializer\Mapping\Loader\ReflectionClassMetadataLoader;
21 | use phpDocumentor\Reflection\ClassReflector;
22 | use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
23 | use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
24 | use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
25 |
26 | /**
27 | * @author GeLo
28 | */
29 | class ClassMetadataFactory extends AbstractClassMetadataFactory
30 | {
31 | /**
32 | * @var ClassMetadataLoaderInterface
33 | */
34 | private $loader;
35 |
36 | /**
37 | * @param ClassMetadataLoaderInterface $loader
38 | */
39 | public function __construct(ClassMetadataLoaderInterface $loader)
40 | {
41 | $this->loader = $loader;
42 | }
43 |
44 | /**
45 | * @param ClassMetadataLoaderInterface[] $loaders
46 | *
47 | * @return ClassMetadataFactoryInterface
48 | */
49 | public static function create(array $loaders = [])
50 | {
51 | if (empty($loaders)) {
52 | $extractor = null;
53 |
54 | if (class_exists(PropertyInfoExtractor::class)) {
55 | $extractors = $typeExtractors = [new ReflectionExtractor()];
56 |
57 | if (class_exists(ClassReflector::class)) {
58 | array_unshift($typeExtractors, new PhpDocExtractor());
59 | }
60 |
61 | $extractor = new PropertyInfoExtractor($extractors, $typeExtractors, [], $extractors);
62 | }
63 |
64 | $loaders = [new ReflectionClassMetadataLoader($extractor)];
65 |
66 | if (class_exists(AnnotationReader::class)) {
67 | $loaders[] = new AnnotationClassMetadataLoader(new AnnotationReader());
68 | }
69 | }
70 |
71 | return new static(count($loaders) > 1 ? new ChainClassMetadataLoader($loaders) : array_shift($loaders));
72 | }
73 |
74 | /**
75 | * {@inheritdoc}
76 | */
77 | protected function fetchClassMetadata($class)
78 | {
79 | $classMetadata = new ClassMetadata($class);
80 | $found = false;
81 |
82 | if (($parentMetadata = $this->getParentClassMetadata($class)) !== null) {
83 | $classMetadata->merge($parentMetadata);
84 | $found = true;
85 | }
86 |
87 | $found = $this->loader->loadClassMetadata($classMetadata) || $found;
88 |
89 | return $found ? $classMetadata : null;
90 | }
91 |
92 | /**
93 | * @param string $class
94 | *
95 | * @return ClassMetadataInterface|null
96 | */
97 | private function getParentClassMetadata($class)
98 | {
99 | if (($parent = get_parent_class($class)) !== false) {
100 | return $this->getClassMetadata($parent);
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/Mapping/Factory/ClassMetadataFactoryInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Factory;
13 |
14 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | interface ClassMetadataFactoryInterface
20 | {
21 | /**
22 | * @param string $class
23 | *
24 | * @return ClassMetadataInterface|null
25 | */
26 | public function getClassMetadata($class);
27 | }
28 |
--------------------------------------------------------------------------------
/src/Mapping/Factory/EventClassMetadataFactory.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Factory;
13 |
14 | use Ivory\Serializer\Event\ClassMetadataLoadEvent;
15 | use Ivory\Serializer\Event\ClassMetadataNotFoundEvent;
16 | use Ivory\Serializer\Event\SerializerEvents;
17 | use Symfony\Component\EventDispatcher\EventDispatcherInterface;
18 |
19 | /**
20 | * @author GeLo
21 | */
22 | class EventClassMetadataFactory implements ClassMetadataFactoryInterface
23 | {
24 | /**
25 | * @var ClassMetadataFactoryInterface
26 | */
27 | private $factory;
28 |
29 | /**
30 | * @var EventDispatcherInterface
31 | */
32 | private $dispatcher;
33 |
34 | /**
35 | * @param ClassMetadataFactoryInterface $factory
36 | * @param EventDispatcherInterface $dispatcher
37 | */
38 | public function __construct(ClassMetadataFactoryInterface $factory, EventDispatcherInterface $dispatcher)
39 | {
40 | $this->factory = $factory;
41 | $this->dispatcher = $dispatcher;
42 | }
43 |
44 | /**
45 | * {@inheritdoc}
46 | */
47 | public function getClassMetadata($class)
48 | {
49 | $classMetadata = $this->factory->getClassMetadata($class);
50 |
51 | if ($classMetadata === null) {
52 | $this->dispatcher->dispatch(
53 | SerializerEvents::CLASS_METADATA_NOT_FOUND,
54 | $event = new ClassMetadataNotFoundEvent($class)
55 | );
56 | } else {
57 | $this->dispatcher->dispatch(
58 | SerializerEvents::CLASS_METADATA_LOAD,
59 | $event = new ClassMetadataLoadEvent($classMetadata)
60 | );
61 | }
62 |
63 | return $event->getClassMetadata();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Mapping/Loader/AbstractFileClassMetadataLoader.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Loader;
13 |
14 | use Ivory\Serializer\Type\Parser\TypeParserInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | abstract class AbstractFileClassMetadataLoader extends AbstractClassMetadataLoader implements MappedClassMetadataLoaderInterface
20 | {
21 | /**
22 | * @var string
23 | */
24 | private $file;
25 |
26 | /**
27 | * @var mixed[]
28 | */
29 | private $data;
30 |
31 | /**
32 | * @param string $file
33 | * @param TypeParserInterface|null $typeParser
34 | */
35 | public function __construct($file, TypeParserInterface $typeParser = null)
36 | {
37 | parent::__construct($typeParser);
38 |
39 | if (!is_file($file)) {
40 | throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $file));
41 | }
42 |
43 | if (!is_readable($file)) {
44 | throw new \InvalidArgumentException(sprintf('The file "%s" is not readable.', $file));
45 | }
46 |
47 | $this->file = $file;
48 | }
49 |
50 | /**
51 | * {@inheritdoc}
52 | */
53 | public function getMappedClasses()
54 | {
55 | if ($this->data === null) {
56 | $this->data = $this->loadFile($this->file);
57 | }
58 |
59 | return array_keys($this->data);
60 | }
61 |
62 | /**
63 | * @param string $file
64 | *
65 | * @return mixed[]
66 | */
67 | abstract protected function loadFile($file);
68 |
69 | /**
70 | * {@inheritdoc}
71 | */
72 | protected function loadData($class)
73 | {
74 | if ($this->data === null) {
75 | $this->data = $this->loadFile($this->file);
76 | }
77 |
78 | if (isset($this->data[$class])) {
79 | return $this->data[$class];
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Mapping/Loader/AbstractReflectionClassMetadataLoader.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Loader;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | abstract class AbstractReflectionClassMetadataLoader extends AbstractClassMetadataLoader
18 | {
19 | /**
20 | * @param \ReflectionProperty $property
21 | *
22 | * @return mixed[]|null
23 | */
24 | abstract protected function loadProperty(\ReflectionProperty $property);
25 |
26 | /**
27 | * {@inheritdoc}
28 | */
29 | protected function loadData($class)
30 | {
31 | $reflection = new \ReflectionClass($class);
32 | $result = $this->loadClass($reflection);
33 | $properties = [];
34 |
35 | foreach ($reflection->getMethods() as $method) {
36 | if ($method->class !== $class) {
37 | continue;
38 | }
39 |
40 | if (($methodName = $this->validateMethod($method->name)) === null) {
41 | continue;
42 | }
43 |
44 | $data = $this->loadMethod($method);
45 |
46 | if (is_array($data)) {
47 | $properties[$methodName] = $data;
48 | }
49 | }
50 |
51 | foreach ($reflection->getProperties() as $property) {
52 | if ($property->class !== $class) {
53 | continue;
54 | }
55 |
56 | $data = $this->loadProperty($property);
57 |
58 | if (is_array($data)) {
59 | $name = $property->getName();
60 |
61 | $properties[$name] = isset($properties[$name])
62 | ? array_merge_recursive($properties[$name], $data)
63 | : $data;
64 | }
65 | }
66 |
67 | if (!empty($properties)) {
68 | $result['properties'] = $properties;
69 | }
70 |
71 | if (!empty($result)) {
72 | return $result;
73 | }
74 | }
75 |
76 | /**
77 | * @param \ReflectionClass $class
78 | *
79 | * @return mixed[]
80 | */
81 | protected function loadClass(\ReflectionClass $class)
82 | {
83 | return [];
84 | }
85 |
86 | /**
87 | * @param \ReflectionMethod $method
88 | *
89 | * @return mixed[]|null
90 | */
91 | protected function loadMethod(\ReflectionMethod $method)
92 | {
93 | }
94 |
95 | /**
96 | * @param string $method
97 | *
98 | * @return string|null
99 | */
100 | private function validateMethod($method)
101 | {
102 | $prefix = substr($method, 0, 3);
103 |
104 | if ($prefix === 'get' || $prefix === 'set' || $prefix === 'has') {
105 | return lcfirst(substr($method, 3));
106 | }
107 |
108 | $prefix = substr($prefix, 0, 2);
109 |
110 | if ($prefix === 'is') {
111 | return lcfirst(substr($method, 2));
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/Mapping/Loader/ChainClassMetadataLoader.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Loader;
13 |
14 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | class ChainClassMetadataLoader implements MappedClassMetadataLoaderInterface
20 | {
21 | /**
22 | * @var ClassMetadataLoaderInterface[]
23 | */
24 | private $loaders;
25 |
26 | /**
27 | * @param ClassMetadataLoaderInterface[] $loaders
28 | */
29 | public function __construct(array $loaders)
30 | {
31 | $this->loaders = $loaders;
32 | }
33 |
34 | /**
35 | * {@inheritdoc}
36 | */
37 | public function loadClassMetadata(ClassMetadataInterface $classMetadata)
38 | {
39 | $result = false;
40 |
41 | foreach ($this->loaders as $loader) {
42 | $result = $loader->loadClassMetadata($classMetadata) || $result;
43 | }
44 |
45 | return $result;
46 | }
47 |
48 | /**
49 | * {@inheritdoc}
50 | */
51 | public function getMappedClasses()
52 | {
53 | $classes = [];
54 |
55 | foreach ($this->loaders as $loader) {
56 | if ($loader instanceof MappedClassMetadataLoaderInterface) {
57 | $classes = array_merge($classes, $loader->getMappedClasses());
58 | }
59 | }
60 |
61 | return array_unique($classes);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Mapping/Loader/ClassMetadataLoaderInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Loader;
13 |
14 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | interface ClassMetadataLoaderInterface
20 | {
21 | /**
22 | * @param ClassMetadataInterface $classMetadata
23 | *
24 | * @return bool
25 | */
26 | public function loadClassMetadata(ClassMetadataInterface $classMetadata);
27 | }
28 |
--------------------------------------------------------------------------------
/src/Mapping/Loader/DirectoryClassMetadataLoader.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Loader;
13 |
14 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
15 | use Ivory\Serializer\Type\Parser\TypeParser;
16 | use Ivory\Serializer\Type\Parser\TypeParserInterface;
17 |
18 | /**
19 | * @author GeLo
20 | */
21 | class DirectoryClassMetadataLoader implements MappedClassMetadataLoaderInterface
22 | {
23 | /**
24 | * @var string[]
25 | */
26 | private $directories;
27 |
28 | /**
29 | * @var TypeParserInterface
30 | */
31 | private $typeParser;
32 |
33 | /**
34 | * @var MappedClassMetadataLoaderInterface|null
35 | */
36 | private $loader;
37 |
38 | /**
39 | * @var bool
40 | */
41 | private $initialized = false;
42 |
43 | /**
44 | * @param string|string[] $directories
45 | * @param TypeParserInterface|null $typeParser
46 | */
47 | public function __construct($directories, TypeParserInterface $typeParser = null)
48 | {
49 | $directories = is_array($directories) ? $directories : [$directories];
50 |
51 | foreach ($directories as $directory) {
52 | if (!is_dir($directory)) {
53 | throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $directory));
54 | }
55 |
56 | if (!is_readable($directory)) {
57 | throw new \InvalidArgumentException(sprintf('The directory "%s" is not readable.', $directory));
58 | }
59 | }
60 |
61 | $this->directories = $directories;
62 | $this->typeParser = $typeParser ?: new TypeParser();
63 | }
64 |
65 | /**
66 | * {@inheritdoc}
67 | */
68 | public function loadClassMetadata(ClassMetadataInterface $classMetadata)
69 | {
70 | return ($loader = $this->getLoader()) !== null && $loader->loadClassMetadata($classMetadata);
71 | }
72 |
73 | /**
74 | * {@inheritdoc}
75 | */
76 | public function getMappedClasses()
77 | {
78 | return ($loader = $this->getLoader()) !== null ? $loader->getMappedClasses() : [];
79 | }
80 |
81 | /**
82 | * @return MappedClassMetadataLoaderInterface|null
83 | */
84 | private function getLoader()
85 | {
86 | if (!$this->initialized) {
87 | $this->createLoader();
88 | $this->initialized = true;
89 | }
90 |
91 | return $this->loader;
92 | }
93 |
94 | private function createLoader()
95 | {
96 | $extensions = [
97 | FileClassMetadataLoader::EXTENSION_JSON,
98 | FileClassMetadataLoader::EXTENSION_XML,
99 | FileClassMetadataLoader::EXTENSION_YAML,
100 | ];
101 |
102 | $loaders = [];
103 |
104 | foreach ($this->directories as $directory) {
105 | $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory));
106 |
107 | foreach ($iterator as $file) {
108 | if ($file->isDir()) {
109 | continue;
110 | }
111 |
112 | $path = $file->getRealPath();
113 |
114 | if (!in_array(pathinfo($path, PATHINFO_EXTENSION), $extensions, true)) {
115 | continue;
116 | }
117 |
118 | $loaders[] = new FileClassMetadataLoader($path, $this->typeParser);
119 | }
120 | }
121 |
122 | if (!empty($loaders)) {
123 | $this->loader = count($loaders) > 1 ? new ChainClassMetadataLoader($loaders) : array_shift($loaders);
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/Mapping/Loader/FileClassMetadataLoader.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Loader;
13 |
14 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
15 | use Ivory\Serializer\Type\Parser\TypeParserInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class FileClassMetadataLoader implements MappedClassMetadataLoaderInterface
21 | {
22 | const EXTENSION_JSON = 'json';
23 | const EXTENSION_XML = 'xml';
24 | const EXTENSION_YAML = 'yml';
25 |
26 | /**
27 | * @var MappedClassMetadataLoaderInterface
28 | */
29 | private $loader;
30 |
31 | /**
32 | * @param string $file
33 | * @param TypeParserInterface|null $typeParser
34 | */
35 | public function __construct($file, TypeParserInterface $typeParser = null)
36 | {
37 | if (!file_exists($file)) {
38 | throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $file));
39 | }
40 |
41 | switch (pathinfo($file, PATHINFO_EXTENSION)) {
42 | case self::EXTENSION_JSON:
43 | $this->loader = new JsonClassMetadataLoader($file, $typeParser);
44 | break;
45 |
46 | case self::EXTENSION_XML:
47 | $this->loader = new XmlClassMetadataLoader($file, $typeParser);
48 | break;
49 |
50 | case self::EXTENSION_YAML:
51 | $this->loader = new YamlClassMetadataLoader($file, $typeParser);
52 | break;
53 | }
54 |
55 | if ($this->loader === null) {
56 | throw new \InvalidArgumentException(sprintf('The file "%s" is not supported.', $file));
57 | }
58 | }
59 |
60 | /**
61 | * {@inheritdoc}
62 | */
63 | public function loadClassMetadata(ClassMetadataInterface $classMetadata)
64 | {
65 | return $this->loader->loadClassMetadata($classMetadata);
66 | }
67 |
68 | /**
69 | * {@inheritdoc}
70 | */
71 | public function getMappedClasses()
72 | {
73 | return $this->loader->getMappedClasses();
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/Mapping/Loader/JsonClassMetadataLoader.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Loader;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class JsonClassMetadataLoader extends AbstractFileClassMetadataLoader
18 | {
19 | /**
20 | * {@inheritdoc}
21 | */
22 | protected function loadFile($file)
23 | {
24 | $data = @json_decode(@file_get_contents($file), true);
25 |
26 | if (json_last_error() !== JSON_ERROR_NONE) {
27 | throw new \InvalidArgumentException(json_last_error_msg());
28 | }
29 |
30 | if (!is_array($data)) {
31 | throw new \InvalidArgumentException(sprintf('The json mapping file "%s" is not valid.', $file));
32 | }
33 |
34 | return $data;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Mapping/Loader/MappedClassMetadataLoaderInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Loader;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | interface MappedClassMetadataLoaderInterface extends ClassMetadataLoaderInterface
18 | {
19 | /**
20 | * @return string[]
21 | */
22 | public function getMappedClasses();
23 | }
24 |
--------------------------------------------------------------------------------
/src/Mapping/Loader/ReflectionClassMetadataLoader.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Loader;
13 |
14 | use Ivory\Serializer\Type\Parser\TypeParserInterface;
15 | use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class ReflectionClassMetadataLoader extends AbstractReflectionClassMetadataLoader
21 | {
22 | /**
23 | * @var PropertyInfoExtractorInterface|null
24 | */
25 | private $extractor;
26 |
27 | /**
28 | * @param PropertyInfoExtractorInterface|null $extractor
29 | * @param TypeParserInterface|null $typeParser
30 | */
31 | public function __construct(
32 | PropertyInfoExtractorInterface $extractor = null,
33 | TypeParserInterface $typeParser = null
34 | ) {
35 | parent::__construct($typeParser);
36 |
37 | $this->extractor = $extractor;
38 | }
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | protected function loadProperty(\ReflectionProperty $property)
44 | {
45 | $result = [];
46 | $type = $this->loadPropertyType($property);
47 |
48 | if ($type !== null) {
49 | $result['type'] = $type;
50 | }
51 |
52 | return $result;
53 | }
54 |
55 | /**
56 | * @param \ReflectionProperty $property
57 | *
58 | * @return string|null
59 | */
60 | private function loadPropertyType(\ReflectionProperty $property)
61 | {
62 | if ($this->extractor === null) {
63 | return;
64 | }
65 |
66 | $types = $this->extractor->getTypes($property->class, $property->name);
67 |
68 | if (empty($types)) {
69 | return;
70 | }
71 |
72 | $extractedType = current($types);
73 | $type = $extractedType->getBuiltinType();
74 |
75 | if ($type === 'object') {
76 | $type = $extractedType->getClassName();
77 | }
78 |
79 | return $type;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/Mapping/Loader/YamlClassMetadataLoader.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping\Loader;
13 |
14 | use Symfony\Component\Yaml\Exception\ParseException;
15 | use Symfony\Component\Yaml\Yaml;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class YamlClassMetadataLoader extends AbstractFileClassMetadataLoader
21 | {
22 | /**
23 | * {@inheritdoc}
24 | */
25 | protected function loadFile($file)
26 | {
27 | try {
28 | return Yaml::parse(file_get_contents($file));
29 | } catch (ParseException $e) {
30 | throw new \InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Mapping/MetadataInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | interface MetadataInterface extends \Serializable
18 | {
19 | /**
20 | * @return string
21 | */
22 | public function getName();
23 |
24 | /**
25 | * @param string $name
26 | */
27 | public function setName($name);
28 | }
29 |
--------------------------------------------------------------------------------
/src/Mapping/Resource/mapping.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 |
62 |
63 |
--------------------------------------------------------------------------------
/src/Mapping/TypeMetadata.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class TypeMetadata implements TypeMetadataInterface
18 | {
19 | /**
20 | * @var string
21 | */
22 | private $name;
23 |
24 | /**
25 | * @var mixed[]
26 | */
27 | private $options = [];
28 |
29 | /**
30 | * @param string $name
31 | * @param mixed[] $options
32 | */
33 | public function __construct($name, array $options = [])
34 | {
35 | $this->setName($name);
36 | $this->setOptions($options);
37 | }
38 |
39 | /**
40 | * {@inheritdoc}
41 | */
42 | public function getName()
43 | {
44 | return $this->name;
45 | }
46 |
47 | /**
48 | * {@inheritdoc}
49 | */
50 | public function setName($name)
51 | {
52 | $this->name = $name;
53 | }
54 |
55 | /**
56 | * @return bool
57 | */
58 | public function hasOptions()
59 | {
60 | return !empty($this->options);
61 | }
62 |
63 | /**
64 | * @return mixed[]
65 | */
66 | public function getOptions()
67 | {
68 | return $this->options;
69 | }
70 |
71 | /**
72 | * @param mixed[] $options
73 | */
74 | public function setOptions(array $options)
75 | {
76 | foreach ($options as $option => $value) {
77 | $this->setOption($option, $value);
78 | }
79 | }
80 |
81 | /**
82 | * @param string $option
83 | *
84 | * @return bool
85 | */
86 | public function hasOption($option)
87 | {
88 | return isset($this->options[$option]);
89 | }
90 |
91 | /**
92 | * @param string $option
93 | * @param mixed $default
94 | *
95 | * @return mixed
96 | */
97 | public function getOption($option, $default = null)
98 | {
99 | return $this->hasOption($option) ? $this->options[$option] : $default;
100 | }
101 |
102 | /**
103 | * @param string $option
104 | * @param mixed $value
105 | */
106 | public function setOption($option, $value)
107 | {
108 | $this->options[$option] = $value;
109 | }
110 |
111 | /**
112 | * @param string $option
113 | */
114 | public function removeOption($option)
115 | {
116 | unset($this->options[$option]);
117 | }
118 |
119 | /**
120 | * {@inheritdoc}
121 | */
122 | public function serialize()
123 | {
124 | return serialize([
125 | $this->name,
126 | $this->options,
127 | ]);
128 | }
129 |
130 | /**
131 | * {@inheritdoc}
132 | */
133 | public function unserialize($serialized)
134 | {
135 | list(
136 | $this->name,
137 | $this->options
138 | ) = unserialize($serialized);
139 | }
140 |
141 | /**
142 | * {@inheritdoc}
143 | */
144 | public function __toString()
145 | {
146 | $name = (string) $this->getName();
147 |
148 | if (!$this->hasOptions()) {
149 | return $name;
150 | }
151 |
152 | $options = $this->getOptions();
153 |
154 | array_walk($options, function (&$value, $option) {
155 | if (is_string($value)) {
156 | $value = '\''.$value.'\'';
157 | }
158 |
159 | $value = $option.'='.$value;
160 | });
161 |
162 | return $name.'<'.implode(', ', $options).'>';
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/src/Mapping/TypeMetadataInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mapping;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | interface TypeMetadataInterface extends MetadataInterface
18 | {
19 | /**
20 | * @return bool
21 | */
22 | public function hasOptions();
23 |
24 | /**
25 | * @return mixed[]
26 | */
27 | public function getOptions();
28 |
29 | /**
30 | * @param mixed[] $options
31 | */
32 | public function setOptions(array $options);
33 |
34 | /**
35 | * @param string $option
36 | *
37 | * @return bool
38 | */
39 | public function hasOption($option);
40 |
41 | /**
42 | * @param string $option
43 | * @param mixed $default
44 | *
45 | * @return mixed
46 | */
47 | public function getOption($option, $default = null);
48 |
49 | /**
50 | * @param string $option
51 | * @param mixed $value
52 | */
53 | public function setOption($option, $value);
54 |
55 | /**
56 | * @param string $option
57 | */
58 | public function removeOption($option);
59 | }
60 |
--------------------------------------------------------------------------------
/src/Mutator/MutatorInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mutator;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | interface MutatorInterface
18 | {
19 | /**
20 | * @param object $object
21 | * @param string $property
22 | * @param mixed $value
23 | */
24 | public function setValue($object, $property, $value);
25 | }
26 |
--------------------------------------------------------------------------------
/src/Mutator/ReflectionMutator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mutator;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class ReflectionMutator implements MutatorInterface
18 | {
19 | /**
20 | * @var \ReflectionProperty[]
21 | */
22 | private $properties = [];
23 |
24 | /**
25 | * @var \ReflectionMethod[]
26 | */
27 | private $methods = [];
28 |
29 | /**
30 | * {@inheritdoc}
31 | */
32 | public function setValue($object, $property, $value)
33 | {
34 | if (property_exists($object, $property)) {
35 | $this->getReflectionProperty($object, $property)->setValue($object, $value);
36 | } else {
37 | $this->setMethodValue($object, $property, $value);
38 | }
39 | }
40 |
41 | /**
42 | * @param object $object
43 | * @param string $property
44 | * @param mixed $value
45 | */
46 | private function setMethodValue($object, $property, $value)
47 | {
48 | $methods = [$property];
49 |
50 | if (method_exists($object, $property)) {
51 | $this->getReflectionMethod($object, $property)->invoke($object, $value);
52 |
53 | return;
54 | }
55 |
56 | $methodSuffix = ucfirst($property);
57 |
58 | foreach (['get', 'has', 'is'] as $methodPrefix) {
59 | $methods[] = $method = $methodPrefix.$methodSuffix;
60 |
61 | if (method_exists($object, $method)) {
62 | $this->getReflectionMethod($object, $method)->invoke($object, $value);
63 |
64 | return;
65 | }
66 | }
67 |
68 | throw new \InvalidArgumentException(sprintf(
69 | 'The property "%s" or methods %s don\'t exist on class "%s".',
70 | $property,
71 | '"'.implode('", "', $methods).'"',
72 | get_class($object)
73 | ));
74 | }
75 |
76 | /**
77 | * @param object $object
78 | * @param string $property
79 | *
80 | * @return \ReflectionProperty
81 | */
82 | private function getReflectionProperty($object, $property)
83 | {
84 | if (isset($this->properties[$key = $this->getCacheKey($object, $property)])) {
85 | return $this->properties[$key];
86 | }
87 |
88 | $reflection = new \ReflectionProperty($object, $property);
89 | $reflection->setAccessible(true);
90 |
91 | return $this->properties[$key] = $reflection;
92 | }
93 |
94 | /**
95 | * @param object $object
96 | * @param string $method
97 | *
98 | * @return \ReflectionMethod
99 | */
100 | private function getReflectionMethod($object, $method)
101 | {
102 | if (isset($this->methods[$key = $this->getCacheKey($object, $method)])) {
103 | return $this->methods[$key];
104 | }
105 |
106 | $reflection = new \ReflectionMethod($object, $method);
107 | $reflection->setAccessible(true);
108 |
109 | return $this->methods[$key] = $reflection;
110 | }
111 |
112 | /**
113 | * @param object $object
114 | * @param string $key
115 | *
116 | * @return string
117 | */
118 | private function getCacheKey($object, $key)
119 | {
120 | return get_class($object).'::'.$key;
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/Mutator/SymfonyMutator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Mutator;
13 |
14 | use Symfony\Component\PropertyAccess\PropertyAccess;
15 | use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class SymfonyMutator implements MutatorInterface
21 | {
22 | /**
23 | * @var PropertyAccessorInterface
24 | */
25 | private $propertyAccessor;
26 |
27 | /**
28 | * @param PropertyAccessorInterface|null $propertyAccessor
29 | */
30 | public function __construct(PropertyAccessorInterface $propertyAccessor = null)
31 | {
32 | $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
33 | }
34 |
35 | /**
36 | * {@inheritdoc}
37 | */
38 | public function setValue($object, $property, $value)
39 | {
40 | $this->propertyAccessor->setValue($object, $property, $value);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Naming/AbstractNamingStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Naming;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | abstract class AbstractNamingStrategy implements NamingStrategyInterface
18 | {
19 | /**
20 | * @var string[]
21 | */
22 | private $names = [];
23 |
24 | /**
25 | * {@inheritdoc}
26 | */
27 | public function convert($name)
28 | {
29 | return isset($this->names[$name])
30 | ? $this->names[$name]
31 | : $this->names[$name] = $this->doConvert($name);
32 | }
33 |
34 | /**
35 | * @param string $name
36 | *
37 | * @return string
38 | */
39 | abstract protected function doConvert($name);
40 | }
41 |
--------------------------------------------------------------------------------
/src/Naming/AbstractSeparatorNamingStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Naming;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class AbstractSeparatorNamingStrategy extends AbstractNamingStrategy
18 | {
19 | /**
20 | * @var string
21 | */
22 | private $separator;
23 |
24 | /**
25 | * @param string $separator
26 | */
27 | public function __construct($separator)
28 | {
29 | $this->separator = $separator;
30 | }
31 |
32 | /**
33 | * {@inheritdoc}
34 | */
35 | protected function doConvert($name)
36 | {
37 | $name = str_replace(['--', '__', ' '], ' ', $name);
38 | $name = lcfirst(str_replace(['-', '_', ' '], $this->separator, $name));
39 |
40 | return strtolower(preg_replace('/([A-Z])/', $this->separator.'$1', $name));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Naming/CacheNamingStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Naming;
13 |
14 | use Psr\Cache\CacheItemPoolInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | class CacheNamingStrategy extends AbstractNamingStrategy
20 | {
21 | /**
22 | * @var NamingStrategyInterface
23 | */
24 | private $strategy;
25 |
26 | /**
27 | * @var CacheItemPoolInterface
28 | */
29 | private $cache;
30 |
31 | /**
32 | * @param NamingStrategyInterface $strategy
33 | * @param CacheItemPoolInterface $cache
34 | */
35 | public function __construct(NamingStrategyInterface $strategy, CacheItemPoolInterface $cache)
36 | {
37 | $this->strategy = $strategy;
38 | $this->cache = $cache;
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | protected function doConvert($name)
45 | {
46 | $item = $this->cache->getItem($name);
47 |
48 | if ($item->isHit()) {
49 | return $item->get();
50 | }
51 |
52 | $result = $this->strategy->convert($name);
53 | $this->cache->save($item->set($result));
54 |
55 | return $result;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Naming/CamelCaseNamingStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Naming;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class CamelCaseNamingStrategy extends StudlyCapsNamingStrategy
18 | {
19 | /**
20 | * {@inheritdoc}
21 | */
22 | protected function doConvert($name)
23 | {
24 | return lcfirst(parent::doConvert($name));
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Naming/IdenticalNamingStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Naming;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class IdenticalNamingStrategy implements NamingStrategyInterface
18 | {
19 | /**
20 | * {@inheritdoc}
21 | */
22 | public function convert($name)
23 | {
24 | return $name;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Naming/KebabCaseNamingStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Naming;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class KebabCaseNamingStrategy extends AbstractSeparatorNamingStrategy
18 | {
19 | public function __construct()
20 | {
21 | parent::__construct('-');
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Naming/NamingStrategyInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Naming;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | interface NamingStrategyInterface
18 | {
19 | /**
20 | * @param string $name
21 | *
22 | * @return string
23 | */
24 | public function convert($name);
25 | }
26 |
--------------------------------------------------------------------------------
/src/Naming/SnakeCaseNamingStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Naming;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class SnakeCaseNamingStrategy extends AbstractSeparatorNamingStrategy
18 | {
19 | public function __construct()
20 | {
21 | parent::__construct('_');
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Naming/SpaceNamingStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Naming;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class SpaceNamingStrategy extends AbstractSeparatorNamingStrategy
18 | {
19 | public function __construct()
20 | {
21 | parent::__construct(' ');
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Naming/StudlyCapsNamingStrategy.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Naming;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | class StudlyCapsNamingStrategy extends AbstractNamingStrategy
18 | {
19 | /**
20 | * {@inheritdoc}
21 | */
22 | protected function doConvert($name)
23 | {
24 | return str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $name)));
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Navigator/EventNavigator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Navigator;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Direction;
16 | use Ivory\Serializer\Event\PostDeserializeEvent;
17 | use Ivory\Serializer\Event\PostSerializeEvent;
18 | use Ivory\Serializer\Event\PreDeserializeEvent;
19 | use Ivory\Serializer\Event\PreSerializeEvent;
20 | use Ivory\Serializer\Event\SerializerEvents;
21 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
22 | use Ivory\Serializer\Type\Guesser\TypeGuesser;
23 | use Ivory\Serializer\Type\Guesser\TypeGuesserInterface;
24 | use Symfony\Component\EventDispatcher\EventDispatcherInterface;
25 |
26 | /**
27 | * @author GeLo
28 | */
29 | class EventNavigator implements NavigatorInterface
30 | {
31 | /**
32 | * @var NavigatorInterface
33 | */
34 | private $navigator;
35 |
36 | /**
37 | * @var EventDispatcherInterface
38 | */
39 | private $dispatcher;
40 |
41 | /**
42 | * @var TypeGuesserInterface
43 | */
44 | private $typeGuesser;
45 |
46 | /**
47 | * @param NavigatorInterface $navigator
48 | * @param EventDispatcherInterface $dispatcher
49 | * @param TypeGuesserInterface|null $typeGuesser
50 | */
51 | public function __construct(
52 | NavigatorInterface $navigator,
53 | EventDispatcherInterface $dispatcher,
54 | TypeGuesserInterface $typeGuesser = null
55 | ) {
56 | $this->navigator = $navigator;
57 | $this->dispatcher = $dispatcher;
58 | $this->typeGuesser = $typeGuesser ?: new TypeGuesser();
59 | }
60 |
61 | /**
62 | * {@inheritdoc}
63 | */
64 | public function navigate($data, ContextInterface $context, TypeMetadataInterface $type = null)
65 | {
66 | $type = $type ?: $this->typeGuesser->guess($data);
67 | $serialization = $context->getDirection() === Direction::SERIALIZATION;
68 |
69 | if ($serialization) {
70 | $this->dispatcher->dispatch(
71 | SerializerEvents::PRE_SERIALIZE,
72 | $event = new PreSerializeEvent($data, $type, $context)
73 | );
74 | } else {
75 | $this->dispatcher->dispatch(
76 | SerializerEvents::PRE_DESERIALIZE,
77 | $event = new PreDeserializeEvent($data, $type, $context)
78 | );
79 | }
80 |
81 | $result = $this->navigator->navigate($data = $event->getData(), $context, $type = $event->getType());
82 |
83 | if ($serialization) {
84 | $this->dispatcher->dispatch(
85 | SerializerEvents::POST_SERIALIZE,
86 | new PostSerializeEvent($data, $type, $context)
87 | );
88 | } else {
89 | $this->dispatcher->dispatch(
90 | SerializerEvents::POST_DESERIALIZE,
91 | new PostDeserializeEvent($result, $type, $context)
92 | );
93 | }
94 |
95 | return $result;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/Navigator/Navigator.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Navigator;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 | use Ivory\Serializer\Registry\TypeRegistry;
17 | use Ivory\Serializer\Registry\TypeRegistryInterface;
18 | use Ivory\Serializer\Type\Guesser\TypeGuesser;
19 | use Ivory\Serializer\Type\Guesser\TypeGuesserInterface;
20 | use Ivory\Serializer\Type\Type;
21 |
22 | /**
23 | * @author GeLo
24 | */
25 | class Navigator implements NavigatorInterface
26 | {
27 | /**
28 | * @var TypeRegistryInterface
29 | */
30 | private $typeRegistry;
31 |
32 | /**
33 | * @var TypeGuesserInterface
34 | */
35 | private $typeGuesser;
36 |
37 | /**
38 | * @param TypeRegistryInterface|null $typeRegistry
39 | * @param TypeGuesserInterface|null $typeGuesser
40 | */
41 | public function __construct(TypeRegistryInterface $typeRegistry = null, TypeGuesserInterface $typeGuesser = null)
42 | {
43 | $this->typeRegistry = $typeRegistry ?: TypeRegistry::create();
44 | $this->typeGuesser = $typeGuesser ?: new TypeGuesser();
45 | }
46 |
47 | /**
48 | * {@inheritdoc}
49 | */
50 | public function navigate($data, ContextInterface $context, TypeMetadataInterface $type = null)
51 | {
52 | $type = $type ?: $this->typeGuesser->guess($data);
53 | $name = $type->getName();
54 |
55 | if ($data === null) {
56 | $name = Type::NULL;
57 | }
58 |
59 | return $this->typeRegistry->getType($name, $context->getDirection())->convert($data, $type, $context);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Navigator/NavigatorInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Navigator;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | interface NavigatorInterface
21 | {
22 | /**
23 | * @param mixed $data
24 | * @param ContextInterface $context
25 | * @param TypeMetadataInterface|null $type
26 | *
27 | * @return mixed
28 | */
29 | public function navigate($data, ContextInterface $context, TypeMetadataInterface $type = null);
30 | }
31 |
--------------------------------------------------------------------------------
/src/Registry/TypeRegistryInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Registry;
13 |
14 | use Ivory\Serializer\Type\TypeInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | interface TypeRegistryInterface
20 | {
21 | /**
22 | * @param string $name
23 | * @param int $direction
24 | * @param TypeInterface $type
25 | */
26 | public function registerType($name, $direction, TypeInterface $type);
27 |
28 | /**
29 | * @param string $name
30 | * @param int $direction
31 | *
32 | * @return TypeInterface
33 | */
34 | public function getType($name, $direction);
35 | }
36 |
--------------------------------------------------------------------------------
/src/Registry/VisitorRegistry.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Registry;
13 |
14 | use Ivory\Serializer\Accessor\AccessorInterface;
15 | use Ivory\Serializer\Accessor\ReflectionAccessor;
16 | use Ivory\Serializer\Direction;
17 | use Ivory\Serializer\Format;
18 | use Ivory\Serializer\Instantiator\DoctrineInstantiator;
19 | use Ivory\Serializer\Instantiator\InstantiatorInterface;
20 | use Ivory\Serializer\Mutator\MutatorInterface;
21 | use Ivory\Serializer\Mutator\ReflectionMutator;
22 | use Ivory\Serializer\Visitor\Csv\CsvDeserializationVisitor;
23 | use Ivory\Serializer\Visitor\Csv\CsvSerializationVisitor;
24 | use Ivory\Serializer\Visitor\Json\JsonDeserializationVisitor;
25 | use Ivory\Serializer\Visitor\Json\JsonSerializationVisitor;
26 | use Ivory\Serializer\Visitor\VisitorInterface;
27 | use Ivory\Serializer\Visitor\Xml\XmlDeserializationVisitor;
28 | use Ivory\Serializer\Visitor\Xml\XmlSerializationVisitor;
29 | use Ivory\Serializer\Visitor\Yaml\YamlDeserializationVisitor;
30 | use Ivory\Serializer\Visitor\Yaml\YamlSerializationVisitor;
31 |
32 | /**
33 | * @author GeLo
34 | */
35 | class VisitorRegistry implements VisitorRegistryInterface
36 | {
37 | /**
38 | * @var VisitorInterface[][]
39 | */
40 | private $visitors = [];
41 |
42 | /**
43 | * @param VisitorInterface[][] $visitors
44 | */
45 | public function __construct(array $visitors = [])
46 | {
47 | foreach ($visitors as $direction => $formattedVisitors) {
48 | foreach ($formattedVisitors as $format => $visitor) {
49 | $this->registerVisitor($direction, $format, $visitor);
50 | }
51 | }
52 | }
53 |
54 | /**
55 | * @param VisitorInterface[][] $visitors
56 | * @param InstantiatorInterface|null $instantiator
57 | * @param AccessorInterface|null $accessor
58 | * @param MutatorInterface|null $mutator
59 | *
60 | * @return VisitorRegistryInterface
61 | */
62 | public static function create(
63 | array $visitors = [],
64 | InstantiatorInterface $instantiator = null,
65 | AccessorInterface $accessor = null,
66 | MutatorInterface $mutator = null
67 | ) {
68 | $instantiator = $instantiator ?: new DoctrineInstantiator();
69 | $accessor = $accessor ?: new ReflectionAccessor();
70 | $mutator = $mutator ?: new ReflectionMutator();
71 |
72 | return new static(array_replace_recursive([
73 | Direction::SERIALIZATION => [
74 | Format::CSV => new CsvSerializationVisitor($accessor),
75 | Format::JSON => new JsonSerializationVisitor($accessor),
76 | Format::XML => new XmlSerializationVisitor($accessor),
77 | Format::YAML => new YamlSerializationVisitor($accessor),
78 | ],
79 | Direction::DESERIALIZATION => [
80 | Format::CSV => new CsvDeserializationVisitor($instantiator, $mutator),
81 | Format::JSON => new JsonDeserializationVisitor($instantiator, $mutator),
82 | Format::XML => new XmlDeserializationVisitor($instantiator, $mutator),
83 | Format::YAML => new YamlDeserializationVisitor($instantiator, $mutator),
84 | ],
85 | ], $visitors));
86 | }
87 |
88 | /**
89 | * {@inheritdoc}
90 | */
91 | public function registerVisitor($direction, $format, VisitorInterface $visitor)
92 | {
93 | if (!isset($this->visitors[$direction])) {
94 | $this->visitors[$direction] = [];
95 | }
96 |
97 | $this->visitors[$direction][$format] = $visitor;
98 | }
99 |
100 | /**
101 | * {@inheritdoc}
102 | */
103 | public function getVisitor($direction, $format)
104 | {
105 | if (!isset($this->visitors[$direction]) || !isset($this->visitors[$direction][$format])) {
106 | throw new \InvalidArgumentException(sprintf(
107 | 'The visitor for direction "%s" and format "%s" does not exist.',
108 | $direction === Direction::SERIALIZATION ? 'serialization' : 'deserialization',
109 | $format
110 | ));
111 | }
112 |
113 | return $this->visitors[$direction][$format];
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/Registry/VisitorRegistryInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Registry;
13 |
14 | use Ivory\Serializer\Visitor\VisitorInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | interface VisitorRegistryInterface
20 | {
21 | /**
22 | * @param int $direction
23 | * @param string $format
24 | * @param VisitorInterface $visitor
25 | */
26 | public function registerVisitor($direction, $format, VisitorInterface $visitor);
27 |
28 | /**
29 | * @param int $direction
30 | * @param string $format
31 | *
32 | * @return VisitorInterface
33 | */
34 | public function getVisitor($direction, $format);
35 | }
36 |
--------------------------------------------------------------------------------
/src/Serializer.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer;
13 |
14 | use Ivory\Serializer\Context\Context;
15 | use Ivory\Serializer\Context\ContextInterface;
16 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
17 | use Ivory\Serializer\Navigator\Navigator;
18 | use Ivory\Serializer\Navigator\NavigatorInterface;
19 | use Ivory\Serializer\Registry\VisitorRegistry;
20 | use Ivory\Serializer\Registry\VisitorRegistryInterface;
21 | use Ivory\Serializer\Type\Parser\TypeParser;
22 | use Ivory\Serializer\Type\Parser\TypeParserInterface;
23 |
24 | /**
25 | * @author GeLo
26 | */
27 | class Serializer implements SerializerInterface
28 | {
29 | /**
30 | * @var NavigatorInterface
31 | */
32 | private $navigator;
33 |
34 | /**
35 | * @var VisitorRegistryInterface
36 | */
37 | private $visitorRegistry;
38 |
39 | /**
40 | * @var TypeParserInterface
41 | */
42 | private $typeParser;
43 |
44 | /**
45 | * @param NavigatorInterface|null $navigator
46 | * @param VisitorRegistryInterface|null $visitorRegistry
47 | * @param TypeParserInterface|null $typeParser
48 | */
49 | public function __construct(
50 | NavigatorInterface $navigator = null,
51 | VisitorRegistryInterface $visitorRegistry = null,
52 | TypeParserInterface $typeParser = null
53 | ) {
54 | $this->navigator = $navigator ?: new Navigator();
55 | $this->visitorRegistry = $visitorRegistry ?: VisitorRegistry::create();
56 | $this->typeParser = $typeParser ?: new TypeParser();
57 | }
58 |
59 | /**
60 | * {@inheritdoc}
61 | */
62 | public function serialize($data, $format, ContextInterface $context = null)
63 | {
64 | return $this->navigate($data, Direction::SERIALIZATION, $format, $context);
65 | }
66 |
67 | /**
68 | * {@inheritdoc}
69 | */
70 | public function deserialize($data, $type, $format, ContextInterface $context = null)
71 | {
72 | if (is_string($type)) {
73 | $type = $this->typeParser->parse($type);
74 | }
75 |
76 | if (!$type instanceof TypeMetadataInterface) {
77 | throw new \InvalidArgumentException(sprintf(
78 | 'The type must be a string or a "%s", got "%s".',
79 | TypeMetadataInterface::class,
80 | is_object($type) ? get_class($type) : gettype($type)
81 | ));
82 | }
83 |
84 | return $this->navigate($data, Direction::DESERIALIZATION, $format, $context, $type);
85 | }
86 |
87 | /**
88 | * @param mixed $data
89 | * @param int $direction
90 | * @param string $format
91 | * @param ContextInterface|null $context
92 | * @param TypeMetadataInterface|null $type
93 | *
94 | * @return mixed
95 | */
96 | private function navigate(
97 | $data,
98 | $direction,
99 | $format,
100 | ContextInterface $context = null,
101 | TypeMetadataInterface $type = null
102 | ) {
103 | $visitor = $this->visitorRegistry->getVisitor($direction, $format);
104 |
105 | $context = $context ?: new Context();
106 | $context->initialize($this->navigator, $visitor, $direction, $format);
107 | $this->navigator->navigate($visitor->prepare($data, $context), $context, $type);
108 |
109 | return $visitor->getResult();
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/SerializerInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | interface SerializerInterface
21 | {
22 | /**
23 | * @param mixed $data
24 | * @param string $format
25 | * @param ContextInterface|null $context
26 | *
27 | * @return string
28 | */
29 | public function serialize($data, $format, ContextInterface $context = null);
30 |
31 | /**
32 | * @param string $data
33 | * @param TypeMetadataInterface|string $type
34 | * @param string $format
35 | * @param ContextInterface|null $context
36 | *
37 | * @return mixed
38 | */
39 | public function deserialize($data, $type, $format, ContextInterface $context = null);
40 | }
41 |
--------------------------------------------------------------------------------
/src/Type/ArrayType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class ArrayType implements TypeInterface
21 | {
22 | /**
23 | * {@inheritdoc}
24 | */
25 | public function convert($data, TypeMetadataInterface $type, ContextInterface $context)
26 | {
27 | return $context->getVisitor()->visitArray($data, $type, $context);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Type/BooleanType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class BooleanType implements TypeInterface
21 | {
22 | /**
23 | * {@inheritdoc}
24 | */
25 | public function convert($data, TypeMetadataInterface $type, ContextInterface $context)
26 | {
27 | return $context->getVisitor()->visitBoolean($data, $type, $context);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Type/ClosureType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class ClosureType implements TypeInterface
21 | {
22 | /**
23 | * {@inheritdoc}
24 | */
25 | public function convert($data, TypeMetadataInterface $type, ContextInterface $context)
26 | {
27 | throw new \RuntimeException('(De)Serializing a closure is not supported.');
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Type/DateTimeType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Direction;
16 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
17 |
18 | /**
19 | * @author GeLo
20 | */
21 | class DateTimeType implements TypeInterface
22 | {
23 | /**
24 | * @var string
25 | */
26 | private $format;
27 |
28 | /**
29 | * @var string
30 | */
31 | private $timeZone;
32 |
33 | /**
34 | * @param string $format
35 | * @param string|null $timeZone
36 | */
37 | public function __construct($format = \DateTime::RFC3339, $timeZone = null)
38 | {
39 | $this->format = $format;
40 | $this->timeZone = $timeZone ?: date_default_timezone_get();
41 | }
42 |
43 | /**
44 | * {@inheritdoc}
45 | */
46 | public function convert($data, TypeMetadataInterface $type, ContextInterface $context)
47 | {
48 | $result = $context->getDirection() === Direction::SERIALIZATION
49 | ? $this->serialize($data, $type, $context)
50 | : $this->deserialize($data, $type, $context);
51 |
52 | return $context->getVisitor()->visitData($result, $type, $context);
53 | }
54 |
55 | /**
56 | * @param \DateTimeInterface $data
57 | * @param TypeMetadataInterface $type
58 | * @param ContextInterface $context
59 | *
60 | * @return string
61 | */
62 | private function serialize($data, TypeMetadataInterface $type, ContextInterface $context)
63 | {
64 | $class = $type->getName();
65 |
66 | if (!$data instanceof $class) {
67 | throw new \InvalidArgumentException(sprintf(
68 | 'Expected a "%s", got "%s".',
69 | $class,
70 | is_object($data) ? get_class($data) : gettype($data)
71 | ));
72 | }
73 |
74 | $result = $data->format($format = $type->getOption('format', $this->format));
75 |
76 | if ($result === false) {
77 | throw new \InvalidArgumentException(sprintf('The date format "%s" is not valid.', $format));
78 | }
79 |
80 | return $result;
81 | }
82 |
83 | /**
84 | * @param mixed $data
85 | * @param TypeMetadataInterface $type
86 | * @param ContextInterface $context
87 | *
88 | * @return \DateTimeInterface
89 | */
90 | private function deserialize($data, TypeMetadataInterface $type, ContextInterface $context)
91 | {
92 | $class = $type->getName();
93 |
94 | if (!method_exists($class, 'createFromFormat')) {
95 | throw new \InvalidArgumentException(sprintf(
96 | 'The method "%s" does not exist on "%s".',
97 | 'createFromFormat',
98 | $class
99 | ));
100 | }
101 |
102 | if (!method_exists($class, 'getLastErrors')) {
103 | throw new \InvalidArgumentException(sprintf(
104 | 'The method "%s" does not exist on "%s".',
105 | 'getLastErrors',
106 | $class
107 | ));
108 | }
109 |
110 | $result = $class::createFromFormat(
111 | $format = $type->getOption('format', $this->format),
112 | (string) $data,
113 | $timezone = new \DateTimeZone($type->getOption('timezone', $this->timeZone))
114 | );
115 |
116 | $errors = $class::getLastErrors();
117 |
118 | if (!empty($errors['warnings']) || !empty($errors['errors'])) {
119 | throw new \InvalidArgumentException(sprintf(
120 | 'The date "%s" with format "%s" and timezone "%s" is not valid.',
121 | $data,
122 | $format,
123 | $timezone->getName()
124 | ));
125 | }
126 |
127 | return $result;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/Type/ExceptionType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Direction;
16 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
17 |
18 | /**
19 | * @author GeLo
20 | */
21 | class ExceptionType implements TypeInterface
22 | {
23 | /**
24 | * @var bool
25 | */
26 | private $debug;
27 |
28 | /**
29 | * @param bool $debug
30 | */
31 | public function __construct($debug = false)
32 | {
33 | $this->debug = $debug;
34 | }
35 |
36 | /**
37 | * {@inheritdoc}
38 | */
39 | public function convert($exception, TypeMetadataInterface $type, ContextInterface $context)
40 | {
41 | if ($context->getDirection() === Direction::DESERIALIZATION) {
42 | throw new \RuntimeException(sprintf('De-serializing an "Exception" is not supported.'));
43 | }
44 |
45 | $result = [
46 | 'code' => 500,
47 | 'message' => 'Internal Server Error',
48 | ];
49 |
50 | if ($this->debug) {
51 | $result['exception'] = $this->serializeException($exception);
52 | }
53 |
54 | return $context->getVisitor()->visitArray($result, $type, $context);
55 | }
56 |
57 | /**
58 | * @param \Exception $exception
59 | *
60 | * @return mixed[]
61 | */
62 | private function serializeException(\Exception $exception)
63 | {
64 | $result = [
65 | 'code' => $exception->getCode(),
66 | 'message' => $exception->getMessage(),
67 | 'file' => $exception->getFile(),
68 | 'line' => $exception->getLine(),
69 | 'trace' => $exception->getTraceAsString(),
70 | ];
71 |
72 | if ($exception->getPrevious() !== null) {
73 | $result['previous'] = $this->serializeException($exception->getPrevious());
74 | }
75 |
76 | return $result;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/Type/FloatType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class FloatType implements TypeInterface
21 | {
22 | /**
23 | * {@inheritdoc}
24 | */
25 | public function convert($data, TypeMetadataInterface $type, ContextInterface $context)
26 | {
27 | return $context->getVisitor()->visitFloat($data, $type, $context);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Type/Guesser/TypeGuesser.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type\Guesser;
13 |
14 | use Ivory\Serializer\Mapping\TypeMetadata;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | class TypeGuesser implements TypeGuesserInterface
20 | {
21 | /**
22 | * {@inheritdoc}
23 | */
24 | public function guess($data)
25 | {
26 | return new TypeMetadata(is_object($data) ? get_class($data) : strtolower(gettype($data)));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Type/Guesser/TypeGuesserInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type\Guesser;
13 |
14 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | interface TypeGuesserInterface
20 | {
21 | /**
22 | * @param mixed $data
23 | *
24 | * @return TypeMetadataInterface
25 | */
26 | public function guess($data);
27 | }
28 |
--------------------------------------------------------------------------------
/src/Type/IntegerType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class IntegerType implements TypeInterface
21 | {
22 | /**
23 | * {@inheritdoc}
24 | */
25 | public function convert($data, TypeMetadataInterface $type, ContextInterface $context)
26 | {
27 | return $context->getVisitor()->visitInteger($data, $type, $context);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Type/NullType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class NullType implements TypeInterface
21 | {
22 | /**
23 | * {@inheritdoc}
24 | */
25 | public function convert($data, TypeMetadataInterface $type, ContextInterface $context)
26 | {
27 | return $context->getVisitor()->visitNull($data, $type, $context);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Type/ObjectType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\Factory\ClassMetadataFactory;
16 | use Ivory\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
17 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
18 |
19 | /**
20 | * @author GeLo
21 | */
22 | class ObjectType implements TypeInterface
23 | {
24 | /**
25 | * @var ClassMetadataFactoryInterface
26 | */
27 | private $classMetadataFactory;
28 |
29 | /**
30 | * @param ClassMetadataFactoryInterface|null $classMetadataFactory
31 | */
32 | public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null)
33 | {
34 | $this->classMetadataFactory = $classMetadataFactory ?: ClassMetadataFactory::create();
35 | }
36 |
37 | /**
38 | * {@inheritdoc}
39 | */
40 | public function convert($data, TypeMetadataInterface $type, ContextInterface $context)
41 | {
42 | $class = $this->classMetadataFactory->getClassMetadata($type->getName());
43 |
44 | if ($class === null) {
45 | throw new \RuntimeException(sprintf('The class metadata "%s" does not exit.', $type->getName()));
46 | }
47 |
48 | $visitor = $context->getVisitor();
49 |
50 | if (!$visitor->startVisitingObject($data, $class, $context)) {
51 | return $visitor->visitNull(null, $type, $context);
52 | }
53 |
54 | foreach ($class->getProperties() as $property) {
55 | $visitor->visitObjectProperty($data, $property, $context);
56 | }
57 |
58 | return $visitor->finishVisitingObject($data, $class, $context);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Type/Parser/TypeLexer.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type\Parser;
13 |
14 | use Doctrine\Common\Lexer\AbstractLexer;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | class TypeLexer extends AbstractLexer
20 | {
21 | const T_NONE = 1;
22 | const T_NAME = 2;
23 | const T_STRING = 3;
24 | const T_GREATER_THAN = 4;
25 | const T_LOWER_THAN = 5;
26 | const T_COMMA = 6;
27 | const T_EQUAL = 7;
28 |
29 | /**
30 | * {@inheritdoc}
31 | */
32 | protected function getCatchablePatterns()
33 | {
34 | return [
35 | '\'(?:[^\']|\'\')*\'',
36 | '([a-z0-9\\\\]+)',
37 | ];
38 | }
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | protected function getNonCatchablePatterns()
44 | {
45 | return [
46 | '\s+',
47 | '(.)',
48 | ];
49 | }
50 |
51 | /**
52 | * {@inheritdoc}
53 | */
54 | protected function getType(&$value)
55 | {
56 | if (ctype_alpha($value[0])) {
57 | return self::T_NAME;
58 | }
59 |
60 | if ($value[0] === '\'') {
61 | $value = str_replace('\'\'', '\'', substr($value, 1, -1));
62 |
63 | return self::T_STRING;
64 | }
65 |
66 | if ($value === '>') {
67 | return self::T_GREATER_THAN;
68 | }
69 |
70 | if ($value === '<') {
71 | return self::T_LOWER_THAN;
72 | }
73 |
74 | if ($value === ',') {
75 | return self::T_COMMA;
76 | }
77 |
78 | if ($value === '=') {
79 | return self::T_EQUAL;
80 | }
81 |
82 | return self::T_NONE;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/Type/Parser/TypeParserInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type\Parser;
13 |
14 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
15 |
16 | /**
17 | * @author GeLo
18 | */
19 | interface TypeParserInterface
20 | {
21 | /**
22 | * @param string $type
23 | *
24 | * @return TypeMetadataInterface
25 | */
26 | public function parse($type);
27 | }
28 |
--------------------------------------------------------------------------------
/src/Type/ResourceType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class ResourceType implements TypeInterface
21 | {
22 | /**
23 | * {@inheritdoc}
24 | */
25 | public function convert($data, TypeMetadataInterface $type, ContextInterface $context)
26 | {
27 | return $context->getVisitor()->visitResource($data, $type, $context);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Type/StdClassType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Direction;
16 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
17 |
18 | /**
19 | * @author GeLo
20 | */
21 | class StdClassType implements TypeInterface
22 | {
23 | /**
24 | * {@inheritdoc}
25 | */
26 | public function convert($data, TypeMetadataInterface $type, ContextInterface $context)
27 | {
28 | $visitor = $context->getVisitor();
29 |
30 | if ($context->getDirection() === Direction::SERIALIZATION) {
31 | return $visitor->visitArray((array) $data, $type, $context);
32 | }
33 |
34 | return (object) $visitor->visitArray($data, $type, $context);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/Type/StringType.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | class StringType implements TypeInterface
21 | {
22 | /**
23 | * {@inheritdoc}
24 | */
25 | public function convert($data, TypeMetadataInterface $type, ContextInterface $context)
26 | {
27 | return $context->getVisitor()->visitString($data, $type, $context);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Type/Type.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | /**
15 | * @author GeLo
16 | */
17 | final class Type
18 | {
19 | const ARRAY_ = 'array';
20 | const BOOL = 'bool';
21 | const BOOLEAN = 'boolean';
22 | const CLOSURE = \Closure::class;
23 | const DATE_TIME = \DateTimeInterface::class;
24 | const DOUBLE = 'double';
25 | const EXCEPTION = \Exception::class;
26 | const FLOAT = 'float';
27 | const INT = 'int';
28 | const INTEGER = 'integer';
29 | const NULL = 'null';
30 | const NUMERIC = 'numeric';
31 | const OBJECT = 'object';
32 | const RESOURCE = 'resource';
33 | const STD_CLASS = \stdClass::class;
34 | const STRING = 'string';
35 |
36 | /**
37 | * @codeCoverageIgnore
38 | */
39 | private function __construct()
40 | {
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Type/TypeInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Type;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
16 |
17 | /**
18 | * @author GeLo
19 | */
20 | interface TypeInterface
21 | {
22 | /**
23 | * @param mixed $data
24 | * @param TypeMetadataInterface $type
25 | * @param ContextInterface $context
26 | *
27 | * @return mixed
28 | */
29 | public function convert($data, TypeMetadataInterface $type, ContextInterface $context);
30 | }
31 |
--------------------------------------------------------------------------------
/src/Visitor/AbstractDeserializationVisitor.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Visitor;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Instantiator\InstantiatorInterface;
16 | use Ivory\Serializer\Mapping\PropertyMetadataInterface;
17 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
18 | use Ivory\Serializer\Mutator\MutatorInterface;
19 |
20 | /**
21 | * @author GeLo
22 | */
23 | abstract class AbstractDeserializationVisitor extends AbstractGenericVisitor
24 | {
25 | /**
26 | * @var InstantiatorInterface
27 | */
28 | private $instantiator;
29 |
30 | /**
31 | * @var MutatorInterface
32 | */
33 | private $mutator;
34 |
35 | /**
36 | * @param InstantiatorInterface $instantiator
37 | * @param MutatorInterface $mutator
38 | */
39 | public function __construct(InstantiatorInterface $instantiator, MutatorInterface $mutator)
40 | {
41 | $this->instantiator = $instantiator;
42 | $this->mutator = $mutator;
43 | }
44 |
45 | /**
46 | * {@inheritdoc}
47 | */
48 | public function prepare($data, ContextInterface $context)
49 | {
50 | return $this->decode(parent::prepare($data, $context));
51 | }
52 |
53 | /**
54 | * {@inheritdoc}
55 | */
56 | public function visitBoolean($data, TypeMetadataInterface $type, ContextInterface $context)
57 | {
58 | return parent::visitBoolean(
59 | filter_var($data, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
60 | $type,
61 | $context
62 | );
63 | }
64 |
65 | /**
66 | * @param mixed $data
67 | *
68 | * @return mixed
69 | */
70 | abstract protected function decode($data);
71 |
72 | /**
73 | * {@inheritdoc}
74 | */
75 | protected function doVisitObjectProperty(
76 | $data,
77 | $name,
78 | PropertyMetadataInterface $property,
79 | ContextInterface $context
80 | ) {
81 | $type = $property->getType();
82 |
83 | if ($type === null) {
84 | throw new \RuntimeException(sprintf(
85 | 'You must define the type of the %s:%s.',
86 | $property->getClass(),
87 | $property->getName()
88 | ));
89 | }
90 |
91 | if (!$property->isWritable() || !isset($data[$name])) {
92 | return false;
93 | }
94 |
95 | $value = $this->navigator->navigate($data[$name], $context, $type);
96 |
97 | if ($value === null && $context->isNullIgnored()) {
98 | return false;
99 | }
100 |
101 | $this->mutator->setValue(
102 | $this->result,
103 | $property->hasMutator() ? $property->getMutator() : $property->getName(),
104 | $value
105 | );
106 |
107 | return true;
108 | }
109 |
110 | /**
111 | * {@inheritdoc}
112 | */
113 | protected function createResult($class)
114 | {
115 | return $this->instantiator->instantiate($class);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/src/Visitor/AbstractGenericVisitor.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Visitor;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
16 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
17 |
18 | /**
19 | * @author GeLo
20 | */
21 | abstract class AbstractGenericVisitor extends AbstractVisitor
22 | {
23 | /**
24 | * @var mixed[]
25 | */
26 | private $stack;
27 |
28 | /**
29 | * @var mixed
30 | */
31 | protected $result;
32 |
33 | /**
34 | * {@inheritdoc}
35 | */
36 | public function prepare($data, ContextInterface $context)
37 | {
38 | $this->stack = [];
39 | $this->result = null;
40 |
41 | return parent::prepare($data, $context);
42 | }
43 |
44 | /**
45 | * {@inheritdoc}
46 | */
47 | public function visitArray($data, TypeMetadataInterface $type, ContextInterface $context)
48 | {
49 | $result = [];
50 |
51 | if (!empty($data)) {
52 | $this->enterScope();
53 | $result = parent::visitArray($data, $type, $context);
54 | $this->leaveScope();
55 | }
56 |
57 | return $this->visitData($result, $type, $context);
58 | }
59 |
60 | /**
61 | * {@inheritdoc}
62 | */
63 | public function visitData($data, TypeMetadataInterface $type, ContextInterface $context)
64 | {
65 | if ($this->result === null) {
66 | $this->result = $data;
67 | }
68 |
69 | return $data;
70 | }
71 |
72 | /**
73 | * {@inheritdoc}
74 | */
75 | public function startVisitingObject($data, ClassMetadataInterface $class, ContextInterface $context)
76 | {
77 | if (!parent::startVisitingObject($data, $class, $context)) {
78 | return false;
79 | }
80 |
81 | $this->enterScope();
82 | $this->result = $this->createResult($class->getName());
83 |
84 | return true;
85 | }
86 |
87 | /**
88 | * {@inheritdoc}
89 | */
90 | public function finishVisitingObject($data, ClassMetadataInterface $class, ContextInterface $context)
91 | {
92 | parent::finishVisitingObject($data, $class, $context);
93 |
94 | return $this->leaveScope();
95 | }
96 |
97 | /**
98 | * {@inheritdoc}
99 | */
100 | public function getResult()
101 | {
102 | return $this->result;
103 | }
104 |
105 | /**
106 | * @param string $class
107 | *
108 | * @return mixed
109 | */
110 | abstract protected function createResult($class);
111 |
112 | /**
113 | * {@inheritdoc}
114 | */
115 | protected function doVisitArray($data, TypeMetadataInterface $type, ContextInterface $context)
116 | {
117 | $this->result = [];
118 |
119 | $keyType = $type->getOption('key');
120 | $valueType = $type->getOption('value');
121 | $ignoreNull = $context->isNullIgnored();
122 |
123 | foreach ($data as $key => $value) {
124 | $value = $this->navigator->navigate($value, $context, $valueType);
125 |
126 | if ($value === null && $ignoreNull) {
127 | continue;
128 | }
129 |
130 | $key = $this->navigator->navigate($key, $context, $keyType);
131 | $this->result[$key] = $value;
132 | }
133 |
134 | return $this->result;
135 | }
136 |
137 | private function enterScope()
138 | {
139 | $this->stack[] = $this->result;
140 | }
141 |
142 | /**
143 | * @return mixed
144 | */
145 | private function leaveScope()
146 | {
147 | $result = $this->result;
148 | $this->result = array_pop($this->stack);
149 |
150 | if ($this->result === null) {
151 | $this->result = $result;
152 | }
153 |
154 | return $result;
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/Visitor/AbstractSerializationVisitor.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Visitor;
13 |
14 | use Ivory\Serializer\Accessor\AccessorInterface;
15 | use Ivory\Serializer\Context\ContextInterface;
16 | use Ivory\Serializer\Mapping\PropertyMetadataInterface;
17 |
18 | /**
19 | * @author GeLo
20 | */
21 | abstract class AbstractSerializationVisitor extends AbstractGenericVisitor
22 | {
23 | /**
24 | * @var AccessorInterface
25 | */
26 | private $accessor;
27 |
28 | /**
29 | * @param AccessorInterface $accessor
30 | */
31 | public function __construct(AccessorInterface $accessor)
32 | {
33 | $this->accessor = $accessor;
34 | }
35 |
36 | /**
37 | * {@inheritdoc}
38 | */
39 | public function getResult()
40 | {
41 | return $this->encode($this->result);
42 | }
43 |
44 | /**
45 | * @param mixed $data
46 | *
47 | * @return mixed
48 | */
49 | abstract protected function encode($data);
50 |
51 | /**
52 | * {@inheritdoc}
53 | */
54 | protected function doVisitObjectProperty(
55 | $data,
56 | $name,
57 | PropertyMetadataInterface $property,
58 | ContextInterface $context
59 | ) {
60 | if (!$property->isReadable()) {
61 | return false;
62 | }
63 |
64 | $result = $this->navigator->navigate(
65 | $this->accessor->getValue(
66 | $data,
67 | $property->hasAccessor() ? $property->getAccessor() : $property->getName()
68 | ),
69 | $context,
70 | $property->getType()
71 | );
72 |
73 | if ($result === null && $context->isNullIgnored()) {
74 | return false;
75 | }
76 |
77 | $this->result[$name] = $result;
78 |
79 | return true;
80 | }
81 |
82 | /**
83 | * {@inheritdoc}
84 | */
85 | protected function createResult($class)
86 | {
87 | return [];
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/Visitor/Json/JsonDeserializationVisitor.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Visitor\Json;
13 |
14 | use Ivory\Serializer\Instantiator\InstantiatorInterface;
15 | use Ivory\Serializer\Mutator\MutatorInterface;
16 | use Ivory\Serializer\Visitor\AbstractDeserializationVisitor;
17 |
18 | /**
19 | * @author GeLo
20 | */
21 | class JsonDeserializationVisitor extends AbstractDeserializationVisitor
22 | {
23 | /**
24 | * @var int
25 | */
26 | private $maxDepth;
27 |
28 | /**
29 | * @var int
30 | */
31 | private $options;
32 |
33 | /**
34 | * @param InstantiatorInterface $instantiator
35 | * @param MutatorInterface $mutator
36 | * @param int $maxDepth
37 | * @param int $options
38 | */
39 | public function __construct(
40 | InstantiatorInterface $instantiator,
41 | MutatorInterface $mutator,
42 | $maxDepth = 512,
43 | $options = 0
44 | ) {
45 | parent::__construct($instantiator, $mutator);
46 |
47 | $this->maxDepth = $maxDepth;
48 | $this->options = $options;
49 | }
50 |
51 | /**
52 | * {@inheritdoc}
53 | */
54 | protected function decode($data)
55 | {
56 | $result = @json_decode($data, true, $this->maxDepth, $this->options);
57 |
58 | if (json_last_error() !== JSON_ERROR_NONE) {
59 | throw new \InvalidArgumentException(json_last_error_msg());
60 | }
61 |
62 | return $result;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Visitor/Json/JsonSerializationVisitor.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Visitor\Json;
13 |
14 | use Ivory\Serializer\Accessor\AccessorInterface;
15 | use Ivory\Serializer\Context\ContextInterface;
16 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
17 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
18 | use Ivory\Serializer\Visitor\AbstractSerializationVisitor;
19 |
20 | /**
21 | * @author GeLo
22 | */
23 | class JsonSerializationVisitor extends AbstractSerializationVisitor
24 | {
25 | /**
26 | * @var int
27 | */
28 | private $options;
29 |
30 | /**
31 | * @param AccessorInterface $accessor
32 | * @param int $options
33 | */
34 | public function __construct(AccessorInterface $accessor, $options = 0)
35 | {
36 | parent::__construct($accessor);
37 |
38 | if (defined('JSON_PRESERVE_ZERO_FRACTION')) {
39 | $options |= JSON_PRESERVE_ZERO_FRACTION;
40 | }
41 |
42 | $this->options = $options;
43 | }
44 |
45 | /**
46 | * {@inheritdoc}
47 | */
48 | public function visitData($data, TypeMetadataInterface $type, ContextInterface $context)
49 | {
50 | if ($data === [] && class_exists($type->getName())) {
51 | $data = (object) $data;
52 | }
53 |
54 | return parent::visitData($data, $type, $context);
55 | }
56 |
57 | /**
58 | * {@inheritdoc}
59 | */
60 | public function finishVisitingObject($data, ClassMetadataInterface $class, ContextInterface $context)
61 | {
62 | if ($this->result === []) {
63 | $this->result = (object) $this->result;
64 | }
65 |
66 | return parent::finishVisitingObject($data, $class, $context);
67 | }
68 |
69 | /**
70 | * {@inheritdoc}
71 | */
72 | protected function encode($data)
73 | {
74 | $result = @json_encode($data, $this->options);
75 |
76 | if (json_last_error() !== JSON_ERROR_NONE) {
77 | throw new \InvalidArgumentException(json_last_error_msg());
78 | }
79 |
80 | return $result;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Visitor/VisitorInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Visitor;
13 |
14 | use Ivory\Serializer\Context\ContextInterface;
15 | use Ivory\Serializer\Mapping\ClassMetadataInterface;
16 | use Ivory\Serializer\Mapping\PropertyMetadataInterface;
17 | use Ivory\Serializer\Mapping\TypeMetadataInterface;
18 |
19 | /**
20 | * @author GeLo
21 | */
22 | interface VisitorInterface
23 | {
24 | /**
25 | * @param mixed $data
26 | * @param ContextInterface $context
27 | *
28 | * @return mixed
29 | */
30 | public function prepare($data, ContextInterface $context);
31 |
32 | /**
33 | * @param mixed $data
34 | * @param TypeMetadataInterface $type
35 | * @param ContextInterface $context
36 | *
37 | * @return mixed
38 | */
39 | public function visitArray($data, TypeMetadataInterface $type, ContextInterface $context);
40 |
41 | /**
42 | * @param mixed $data
43 | * @param TypeMetadataInterface $type
44 | * @param ContextInterface $context
45 | *
46 | * @return mixed
47 | */
48 | public function visitBoolean($data, TypeMetadataInterface $type, ContextInterface $context);
49 |
50 | /**
51 | * @param mixed $data
52 | * @param TypeMetadataInterface $type
53 | * @param ContextInterface $context
54 | *
55 | * @return mixed
56 | */
57 | public function visitData($data, TypeMetadataInterface $type, ContextInterface $context);
58 |
59 | /**
60 | * @param mixed $data
61 | * @param TypeMetadataInterface $type
62 | * @param ContextInterface $context
63 | *
64 | * @return mixed
65 | */
66 | public function visitFloat($data, TypeMetadataInterface $type, ContextInterface $context);
67 |
68 | /**
69 | * @param mixed $data
70 | * @param TypeMetadataInterface $type
71 | * @param ContextInterface $context
72 | *
73 | * @return mixed
74 | */
75 | public function visitInteger($data, TypeMetadataInterface $type, ContextInterface $context);
76 |
77 | /**
78 | * @param mixed $data
79 | * @param TypeMetadataInterface $type
80 | * @param ContextInterface $context
81 | *
82 | * @return mixed
83 | */
84 | public function visitNull($data, TypeMetadataInterface $type, ContextInterface $context);
85 |
86 | /**
87 | * @param mixed $data
88 | * @param TypeMetadataInterface $type
89 | * @param ContextInterface $context
90 | *
91 | * @return mixed
92 | */
93 | public function visitResource($data, TypeMetadataInterface $type, ContextInterface $context);
94 |
95 | /**
96 | * @param mixed $data
97 | * @param TypeMetadataInterface $type
98 | * @param ContextInterface $context
99 | *
100 | * @return mixed
101 | */
102 | public function visitString($data, TypeMetadataInterface $type, ContextInterface $context);
103 |
104 | /**
105 | * @param mixed $data
106 | * @param ClassMetadataInterface $class
107 | * @param ContextInterface $context
108 | *
109 | * @return bool
110 | */
111 | public function startVisitingObject($data, ClassMetadataInterface $class, ContextInterface $context);
112 |
113 | /**
114 | * @param mixed $data
115 | * @param PropertyMetadataInterface $property
116 | * @param ContextInterface $context
117 | *
118 | * @return bool
119 | */
120 | public function visitObjectProperty($data, PropertyMetadataInterface $property, ContextInterface $context);
121 |
122 | /**
123 | * @param mixed $data
124 | * @param ClassMetadataInterface $class
125 | * @param ContextInterface $context
126 | *
127 | * @return mixed
128 | */
129 | public function finishVisitingObject($data, ClassMetadataInterface $class, ContextInterface $context);
130 |
131 | /**
132 | * @return mixed
133 | */
134 | public function getResult();
135 | }
136 |
--------------------------------------------------------------------------------
/src/Visitor/Yaml/YamlDeserializationVisitor.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Visitor\Yaml;
13 |
14 | use Ivory\Serializer\Instantiator\InstantiatorInterface;
15 | use Ivory\Serializer\Mutator\MutatorInterface;
16 | use Ivory\Serializer\Visitor\AbstractDeserializationVisitor;
17 | use Symfony\Component\Yaml\Yaml;
18 |
19 | /**
20 | * @author GeLo
21 | */
22 | class YamlDeserializationVisitor extends AbstractDeserializationVisitor
23 | {
24 | /**
25 | * @var int
26 | */
27 | private $options;
28 |
29 | /**
30 | * @param InstantiatorInterface $instantiator
31 | * @param MutatorInterface $mutator
32 | * @param int $options
33 | */
34 | public function __construct(InstantiatorInterface $instantiator, MutatorInterface $mutator, $options = 0)
35 | {
36 | parent::__construct($instantiator, $mutator);
37 |
38 | $this->options = $options;
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | protected function decode($data)
45 | {
46 | try {
47 | return Yaml::parse($data, $this->options);
48 | } catch (\Exception $e) {
49 | throw new \InvalidArgumentException('Unable to deserialize data.', 0, $e);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Visitor/Yaml/YamlSerializationVisitor.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please read the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Ivory\Serializer\Visitor\Yaml;
13 |
14 | use Ivory\Serializer\Accessor\AccessorInterface;
15 | use Ivory\Serializer\Visitor\AbstractSerializationVisitor;
16 | use Symfony\Component\Yaml\Yaml;
17 |
18 | /**
19 | * @author GeLo
20 | */
21 | class YamlSerializationVisitor extends AbstractSerializationVisitor
22 | {
23 | /**
24 | * @var int
25 | */
26 | private $inline;
27 |
28 | /**
29 | * @var int
30 | */
31 | private $indent;
32 |
33 | /**
34 | * @var int
35 | */
36 | private $options;
37 |
38 | /**
39 | * @param AccessorInterface $accessor
40 | * @param int $inline
41 | * @param int $indent
42 | * @param int $options
43 | */
44 | public function __construct(AccessorInterface $accessor, $inline = 2, $indent = 4, $options = 0)
45 | {
46 | parent::__construct($accessor);
47 |
48 | $this->inline = $inline;
49 | $this->indent = $indent;
50 | $this->options = $options;
51 | }
52 |
53 | /**
54 | * {@inheritdoc}
55 | */
56 | protected function encode($data)
57 | {
58 | try {
59 | return Yaml::dump($data, $this->inline, $this->indent, $this->options);
60 | } catch (\Exception $e) {
61 | throw new \InvalidArgumentException('Unable to serialize data.', 0, $e);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------