Completed
Push — 14.x ( 497b27...3dca04 )
by Tim
01:44
created

SerializerBuilder::setObjectConstructor()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
/*
4
 * Copyright 2013 Johannes M. Schmitt <[email protected]>
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace TechDivision\Import\Configuration\Jms\Serializer;
20
21
use Metadata\Cache\FileCache;
22
use Metadata\MetadataFactory;
23
use Doctrine\Common\Annotations\AnnotationReader;
24
use Doctrine\Common\Annotations\FileCacheReader;
25
use JMS\Serializer\Exception\RuntimeException;
26
use JMS\Serializer\Construction\UnserializeObjectConstructor;
27
use JMS\Serializer\Naming\CamelCaseNamingStrategy;
28
use JMS\Serializer\Naming\SerializedNameAnnotationStrategy;
29
use JMS\Serializer\Handler\HandlerRegistry;
30
use JMS\Serializer\EventDispatcher\EventDispatcher;
31
use JMS\Serializer\Builder\DefaultDriverFactory;
32
use JMS\Serializer\Builder\DriverFactoryInterface;
33
use JMS\Serializer\Handler\PhpCollectionHandler;
34
use JMS\Serializer\Handler\PropelCollectionHandler;
35
use PhpCollection\Map;
36
use JMS\Serializer\Handler\DateHandler;
37
use JMS\Serializer\Handler\ArrayCollectionHandler;
38
use JMS\Serializer\Construction\ObjectConstructorInterface;
39
use JMS\Serializer\EventDispatcher\Subscriber\DoctrineProxySubscriber;
40
use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
41
use Doctrine\Common\Annotations\Reader;
42
use JMS\Serializer\Exception\InvalidArgumentException;
43
use JMS\Serializer\XmlSerializationVisitor;
44
use JMS\Serializer\YamlSerializationVisitor;
45
use JMS\Serializer\JsonSerializationVisitor;
46
use JMS\Serializer\XmlDeserializationVisitor;
47
use JMS\Serializer\JsonDeserializationVisitor;
48
use JMS\Serializer\VisitorInterface;
49
50
/**
51
 * Builder for serializer instances.
52
 *
53
 * This object makes serializer construction a breeze for projects that do not use
54
 * any special dependency injection container.
55
 *
56
 * @author Johannes M. Schmitt <[email protected]>
57
 */
58
class SerializerBuilder
59
{
60
61
    private $metadataDirs = array();
62
    private $handlerRegistry;
63
    private $handlersConfigured = false;
64
    private $eventDispatcher;
65
    private $listenersConfigured = false;
66
    private $objectConstructor;
67
    private $serializationVisitors;
68
    private $deserializationVisitors;
69
    private $visitorsAdded = false;
70
    private $propertyNamingStrategy;
71
    private $debug = false;
72
    private $cacheDir;
73
    private $annotationReader;
74
    private $includeInterfaceMetadata = false;
75
    private $driverFactory;
76
    public static function create()
77
    {
78
        return new static();
79
    }
80
    public function __construct()
81
    {
82
        $this->handlerRegistry = new HandlerRegistry();
83
        $this->eventDispatcher = new EventDispatcher();
84
        $this->driverFactory = new DefaultDriverFactory();
85
        $this->serializationVisitors = new Map();
86
        $this->deserializationVisitors = new Map();
87
    }
88
    public function setAnnotationReader(Reader $reader)
89
    {
90
        $this->annotationReader = $reader;
91
        return $this;
92
    }
93
    public function setDebug($bool)
94
    {
95
        $this->debug = (boolean) $bool;
96
        return $this;
97
    }
98
    public function setCacheDir($dir)
99
    {
100
        if ( ! is_dir($dir)) {
101
            $this->createDir($dir);
102
        }
103
        if ( ! is_writable($dir)) {
104
            throw new InvalidArgumentException(sprintf('The cache directory "%s" is not writable.', $dir));
105
        }
106
        $this->cacheDir = $dir;
107
        return $this;
108
    }
109
    public function addDefaultHandlers()
110
    {
111
        $this->handlersConfigured = true;
112
        $this->handlerRegistry->registerSubscribingHandler(new DateHandler());
113
        $this->handlerRegistry->registerSubscribingHandler(new PhpCollectionHandler());
114
        $this->handlerRegistry->registerSubscribingHandler(new ArrayCollectionHandler());
115
        $this->handlerRegistry->registerSubscribingHandler(new PropelCollectionHandler());
116
        return $this;
117
    }
118
    public function configureHandlers(\Closure $closure)
119
    {
120
        $this->handlersConfigured = true;
121
        $closure($this->handlerRegistry);
122
        return $this;
123
    }
124
    public function addDefaultListeners()
125
    {
126
        $this->listenersConfigured = true;
127
        $this->eventDispatcher->addSubscriber(new DoctrineProxySubscriber());
128
        return $this;
129
    }
130
    public function configureListeners(\Closure $closure)
131
    {
132
        $this->listenersConfigured = true;
133
        $closure($this->eventDispatcher);
134
        return $this;
135
    }
136
    public function setObjectConstructor(ObjectConstructorInterface $constructor)
137
    {
138
        $this->objectConstructor = $constructor;
139
        return $this;
140
    }
141
    public function setPropertyNamingStrategy(PropertyNamingStrategyInterface $propertyNamingStrategy)
142
    {
143
        $this->propertyNamingStrategy = $propertyNamingStrategy;
144
        return $this;
145
    }
146
    public function setSerializationVisitor($format, VisitorInterface $visitor)
147
    {
148
        $this->visitorsAdded = true;
149
        $this->serializationVisitors->set($format, $visitor);
150
        return $this;
151
    }
152
    public function setDeserializationVisitor($format, VisitorInterface $visitor)
153
    {
154
        $this->visitorsAdded = true;
155
        $this->deserializationVisitors->set($format, $visitor);
156
        return $this;
157
    }
158
    public function addDefaultSerializationVisitors()
159
    {
160
        $this->initializePropertyNamingStrategy();
161
        $this->visitorsAdded = true;
162
        $this->serializationVisitors->setAll(array(
163
            'xml' => new XmlSerializationVisitor($this->propertyNamingStrategy),
164
            'yml' => new YamlSerializationVisitor($this->propertyNamingStrategy),
165
            'json' => new JsonSerializationVisitor($this->propertyNamingStrategy),
166
        ));
167
        return $this;
168
    }
169
    public function addDefaultDeserializationVisitors()
170
    {
171
        $this->initializePropertyNamingStrategy();
172
        $this->visitorsAdded = true;
173
        $this->deserializationVisitors->setAll(array(
174
            'xml' => new XmlDeserializationVisitor($this->propertyNamingStrategy),
175
            'json' => new JsonDeserializationVisitor($this->propertyNamingStrategy),
176
        ));
177
        return $this;
178
    }
179
    /**
180
     * @param Boolean $include Whether to include the metadata from the interfaces
181
     *
182
     * @return SerializerBuilder
183
     */
184
    public function includeInterfaceMetadata($include)
185
    {
186
        $this->includeInterfaceMetadata = (Boolean) $include;
187
        return $this;
188
    }
189
    /**
190
     * Sets a map of namespace prefixes to directories.
191
     *
192
     * This method overrides any previously defined directories.
193
     *
194
     * @param array<string,string> $namespacePrefixToDirMap
195
     *
196
     * @return SerializerBuilder
197
     *
198
     * @throws InvalidArgumentException When a directory does not exist
199
     */
200
    public function setMetadataDirs(array $namespacePrefixToDirMap)
201
    {
202
        foreach ($namespacePrefixToDirMap as $dir) {
203
            if ( ! is_dir($dir)) {
204
                throw new InvalidArgumentException(sprintf('The directory "%s" does not exist.', $dir));
205
            }
206
        }
207
        $this->metadataDirs = $namespacePrefixToDirMap;
208
        return $this;
209
    }
210
    /**
211
     * Adds a directory where the serializer will look for class metadata.
212
     *
213
     * The namespace prefix will make the names of the actual metadata files a bit shorter. For example, let's assume
214
     * that you have a directory where you only store metadata files for the ``MyApplication\Entity`` namespace.
215
     *
216
     * If you use an empty prefix, your metadata files would need to look like:
217
     *
218
     * ``my-dir/MyApplication.Entity.SomeObject.yml``
219
     * ``my-dir/MyApplication.Entity.OtherObject.xml``
220
     *
221
     * If you use ``MyApplication\Entity`` as prefix, your metadata files would need to look like:
222
     *
223
     * ``my-dir/SomeObject.yml``
224
     * ``my-dir/OtherObject.yml``
225
     *
226
     * Please keep in mind that you currently may only have one directory per namespace prefix.
227
     *
228
     * @param string $dir The directory where metadata files are located.
229
     * @param string $namespacePrefix An optional prefix if you only store metadata for specific namespaces in this directory.
230
     *
231
     * @return SerializerBuilder
232
     *
233
     * @throws InvalidArgumentException When a directory does not exist
234
     * @throws InvalidArgumentException When a directory has already been registered
235
     */
236 View Code Duplication
    public function addMetadataDir($dir, $namespacePrefix = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
237
    {
238
        if ( ! is_dir($dir)) {
239
            throw new InvalidArgumentException(sprintf('The directory "%s" does not exist.', $dir));
240
        }
241
        if (isset($this->metadataDirs[$namespacePrefix])) {
242
            throw new InvalidArgumentException(sprintf('There is already a directory configured for the namespace prefix "%s". Please use replaceMetadataDir() to override directories.', $namespacePrefix));
243
        }
244
        $this->metadataDirs[$namespacePrefix] = $dir;
245
        return $this;
246
    }
247
    /**
248
     * Adds a map of namespace prefixes to directories.
249
     *
250
     * @param array<string,string> $namespacePrefixToDirMap
251
     *
252
     * @return SerializerBuilder
253
     */
254
    public function addMetadataDirs(array $namespacePrefixToDirMap)
255
    {
256
        foreach ($namespacePrefixToDirMap as $prefix => $dir) {
257
            $this->addMetadataDir($dir, $prefix);
258
        }
259
        return $this;
260
    }
261
    /**
262
     * Similar to addMetadataDir(), but overrides an existing entry.
263
     *
264
     * @param string $dir
265
     * @param string $namespacePrefix
266
     *
267
     * @return SerializerBuilder
268
     *
269
     * @throws InvalidArgumentException When a directory does not exist
270
     * @throws InvalidArgumentException When no directory is configured for the ns prefix
271
     */
272 View Code Duplication
    public function replaceMetadataDir($dir, $namespacePrefix = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
273
    {
274
        if ( ! is_dir($dir)) {
275
            throw new InvalidArgumentException(sprintf('The directory "%s" does not exist.', $dir));
276
        }
277
        if ( ! isset($this->metadataDirs[$namespacePrefix])) {
278
            throw new InvalidArgumentException(sprintf('There is no directory configured for namespace prefix "%s". Please use addMetadataDir() for adding new directories.', $namespacePrefix));
279
        }
280
        $this->metadataDirs[$namespacePrefix] = $dir;
281
        return $this;
282
    }
283
    public function setMetadataDriverFactory(DriverFactoryInterface $driverFactory)
284
    {
285
        $this->driverFactory = $driverFactory;
286
    }
287
    public function build()
288
    {
289
        $annotationReader = $this->annotationReader;
290
        if (null === $annotationReader) {
291
            $annotationReader = new AnnotationReader();
292
            if (null !== $this->cacheDir) {
293
                $this->createDir($this->cacheDir.'/annotations');
294
                $annotationReader = new FileCacheReader($annotationReader, $this->cacheDir.'/annotations', $this->debug);
0 ignored issues
show
Deprecated Code introduced by
The class Doctrine\Common\Annotations\FileCacheReader has been deprecated with message: the FileCacheReader is deprecated and will be removed in version 2.0.0 of doctrine/annotations. Please use the {@see \Doctrine\Common\Annotations\CachedReader} instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
295
            }
296
        }
297
        $metadataDriver = $this->driverFactory->createDriver($this->metadataDirs, $annotationReader);
298
        $metadataFactory = new MetadataFactory($metadataDriver, null, $this->debug);
299
        $metadataFactory->setIncludeInterfaces($this->includeInterfaceMetadata);
300
        if (null !== $this->cacheDir) {
301
            $this->createDir($this->cacheDir.'/metadata');
302
            $metadataFactory->setCache(new FileCache($this->cacheDir.'/metadata'));
303
        }
304
        if ( ! $this->handlersConfigured) {
305
            $this->addDefaultHandlers();
306
        }
307
        if ( ! $this->listenersConfigured) {
308
            $this->addDefaultListeners();
309
        }
310
        if ( ! $this->visitorsAdded) {
311
            $this->addDefaultSerializationVisitors();
312
            $this->addDefaultDeserializationVisitors();
313
        }
314
        return new Serializer(
315
            $metadataFactory,
316
            $this->handlerRegistry,
317
            $this->objectConstructor ?: new UnserializeObjectConstructor(),
318
            $this->serializationVisitors,
319
            $this->deserializationVisitors,
320
            $this->eventDispatcher
321
            );
322
    }
323
    private function initializePropertyNamingStrategy()
324
    {
325
        if (null !== $this->propertyNamingStrategy) {
326
            return;
327
        }
328
        $this->propertyNamingStrategy = new SerializedNameAnnotationStrategy(new CamelCaseNamingStrategy());
329
    }
330
    private function createDir($dir)
331
    {
332
        if (is_dir($dir)) {
333
            return;
334
        }
335
        if (false === @mkdir($dir, 0777, true)) {
336
            throw new RuntimeException(sprintf('Could not create directory "%s".', $dir));
337
        }
338
    }
339
}
340