These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Hateoas; |
||
4 | |||
5 | use Doctrine\Common\Annotations\AnnotationReader; |
||
6 | use Doctrine\Common\Annotations\FileCacheReader; |
||
7 | use Hateoas\Configuration\Metadata\ConfigurationExtensionInterface; |
||
8 | use Hateoas\Configuration\Metadata\Driver\AnnotationDriver; |
||
9 | use Hateoas\Configuration\Metadata\Driver\ExtensionDriver; |
||
10 | use Hateoas\Configuration\Metadata\Driver\YamlDriver; |
||
11 | use Hateoas\Configuration\Metadata\Driver\XmlDriver; |
||
12 | use Hateoas\Configuration\Provider\Resolver\MethodResolver; |
||
13 | use Hateoas\Configuration\Provider\Resolver\ChainResolver; |
||
14 | use Hateoas\Configuration\Provider\RelationProvider; |
||
15 | use Hateoas\Configuration\Provider\Resolver\RelationProviderResolverInterface; |
||
16 | use Hateoas\Configuration\Provider\Resolver\StaticMethodResolver; |
||
17 | use Hateoas\Configuration\RelationsRepository; |
||
18 | use Hateoas\Expression\ExpressionEvaluator; |
||
19 | use Hateoas\Expression\ExpressionFunctionInterface; |
||
20 | use Hateoas\Expression\LinkExpressionFunction; |
||
21 | use Hateoas\Factory\EmbeddedsFactory; |
||
22 | use Hateoas\Factory\LinkFactory; |
||
23 | use Hateoas\Factory\LinksFactory; |
||
24 | use Hateoas\Helper\LinkHelper; |
||
25 | use Hateoas\UrlGenerator\UrlGeneratorInterface; |
||
26 | use Hateoas\UrlGenerator\UrlGeneratorRegistry; |
||
27 | use Hateoas\Serializer\EventSubscriber\JsonEventSubscriber; |
||
28 | use Hateoas\Serializer\EventSubscriber\XmlEventSubscriber; |
||
29 | use Hateoas\Serializer\ExclusionManager; |
||
30 | use Hateoas\Serializer\JsonHalSerializer; |
||
31 | use Hateoas\Serializer\JsonSerializerInterface; |
||
32 | use Hateoas\Serializer\JMSSerializerMetadataAwareInterface; |
||
33 | use Hateoas\Serializer\Metadata\InlineDeferrer; |
||
34 | use Hateoas\Serializer\XmlSerializer; |
||
35 | use Hateoas\Serializer\XmlSerializerInterface; |
||
36 | use JMS\Serializer\EventDispatcher\EventDispatcherInterface; |
||
37 | use JMS\Serializer\SerializerBuilder; |
||
38 | use Metadata\Cache\FileCache; |
||
39 | use Metadata\Driver\DriverChain; |
||
40 | use Metadata\Driver\FileLocator; |
||
41 | use Metadata\MetadataFactory; |
||
42 | use Symfony\Component\ExpressionLanguage\ExpressionLanguage; |
||
43 | |||
44 | /** |
||
45 | * @author Adrien Brault <[email protected]> |
||
46 | * |
||
47 | * Some code (metadata things) from this class has been taken from |
||
48 | * https://github.com/schmittjoh/serializer/blob/a29f1e5083654ba2c126acd94ddb2287069b0b5b/src/JMS/Serializer/SerializerBuilder.php |
||
49 | */ |
||
50 | class HateoasBuilder |
||
51 | { |
||
52 | /** |
||
53 | * @var SerializerBuilder |
||
54 | */ |
||
55 | private $serializerBuilder; |
||
56 | |||
57 | /** |
||
58 | * @var ExpressionLanguage |
||
59 | */ |
||
60 | private $expressionLanguage; |
||
61 | |||
62 | /** |
||
63 | * @var array |
||
64 | */ |
||
65 | private $contextVariables = array(); |
||
66 | |||
67 | /** |
||
68 | * ExpressionFunctionInterface[] |
||
69 | */ |
||
70 | private $expressionFunctions = array(); |
||
71 | |||
72 | /** |
||
73 | * @var XmlSerializerInterface |
||
74 | */ |
||
75 | private $xmlSerializer; |
||
76 | |||
77 | /** |
||
78 | * @var JsonSerializerInterface |
||
79 | */ |
||
80 | private $jsonSerializer; |
||
81 | |||
82 | /** |
||
83 | * @var UrlGeneratorRegistry |
||
84 | */ |
||
85 | private $urlGeneratorRegistry; |
||
86 | |||
87 | private $configurationExtensions = array(); |
||
88 | |||
89 | private $chainResolver; |
||
90 | |||
91 | private $metadataDirs = array(); |
||
92 | |||
93 | private $debug = false; |
||
94 | |||
95 | private $cacheDir; |
||
96 | |||
97 | private $annotationReader; |
||
98 | |||
99 | private $includeInterfaceMetadata = false; |
||
100 | |||
101 | /** |
||
102 | * @param SerializerBuilder $serializerBuilder |
||
103 | * |
||
104 | * @return HateoasBuilder |
||
105 | */ |
||
106 | public static function create(SerializerBuilder $serializerBuilder = null) |
||
107 | { |
||
108 | return new static($serializerBuilder); |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * @return Hateoas |
||
113 | */ |
||
114 | public static function buildHateoas() |
||
115 | { |
||
116 | $builder = static::create(); |
||
117 | |||
118 | return $builder->build(); |
||
119 | } |
||
120 | |||
121 | public function __construct(SerializerBuilder $serializerBuilder = null) |
||
122 | { |
||
123 | $this->serializerBuilder = $serializerBuilder ?: SerializerBuilder::create(); |
||
124 | $this->urlGeneratorRegistry = new UrlGeneratorRegistry(); |
||
125 | $this->chainResolver = new ChainResolver(array( |
||
126 | new MethodResolver(), |
||
127 | new StaticMethodResolver(), |
||
128 | )); |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * Build a configured Hateoas instance. |
||
133 | * |
||
134 | * @return Hateoas |
||
135 | */ |
||
136 | public function build() |
||
137 | { |
||
138 | $metadataFactory = $this->buildMetadataFactory(); |
||
139 | $relationProvider = new RelationProvider($metadataFactory, $this->chainResolver); |
||
140 | $relationsRepository = new RelationsRepository($metadataFactory, $relationProvider); |
||
141 | $expressionEvaluator = new ExpressionEvaluator($this->getExpressionLanguage(), $this->contextVariables); |
||
142 | $linkFactory = new LinkFactory($expressionEvaluator, $this->urlGeneratorRegistry); |
||
143 | $exclusionManager = new ExclusionManager($expressionEvaluator); |
||
144 | $linksFactory = new LinksFactory($relationsRepository, $linkFactory, $exclusionManager); |
||
145 | $embeddedsFactory = new EmbeddedsFactory($relationsRepository, $expressionEvaluator, $exclusionManager); |
||
146 | $linkHelper = new LinkHelper($linkFactory, $relationsRepository); |
||
147 | |||
148 | // Register Hateoas core functions |
||
149 | $expressionEvaluator->registerFunction(new LinkExpressionFunction($linkHelper)); |
||
150 | |||
151 | // Register user functions |
||
152 | foreach ($this->expressionFunctions as $expressionFunction) { |
||
153 | $expressionEvaluator->registerFunction($expressionFunction); |
||
154 | } |
||
155 | |||
156 | if (null === $this->xmlSerializer) { |
||
157 | $this->setDefaultXmlSerializer(); |
||
158 | } |
||
159 | |||
160 | if (null === $this->jsonSerializer) { |
||
161 | $this->setDefaultJsonSerializer(); |
||
162 | } |
||
163 | |||
164 | $inlineDeferrers = array(); |
||
165 | $eventSubscribers = array( |
||
166 | new XmlEventSubscriber($this->xmlSerializer, $linksFactory, $embeddedsFactory), |
||
167 | new JsonEventSubscriber( |
||
168 | $this->jsonSerializer, |
||
169 | $linksFactory, |
||
170 | $embeddedsFactory, |
||
171 | $inlineDeferrers[] = new InlineDeferrer(), |
||
172 | $inlineDeferrers[] = new InlineDeferrer() |
||
173 | ), |
||
174 | ); |
||
175 | |||
176 | $this->serializerBuilder |
||
177 | ->addDefaultListeners() |
||
178 | ->configureListeners(function (EventDispatcherInterface $dispatcher) use ($eventSubscribers) { |
||
179 | foreach ($eventSubscribers as $eventSubscriber) { |
||
180 | $dispatcher->addSubscriber($eventSubscriber); |
||
181 | } |
||
182 | }) |
||
183 | ; |
||
184 | |||
185 | $jmsSerializer = $this->serializerBuilder->build(); |
||
186 | foreach (array_merge($inlineDeferrers, array($this->jsonSerializer, $this->xmlSerializer)) as $serializer) { |
||
187 | if ($serializer instanceof JMSSerializerMetadataAwareInterface) { |
||
188 | $serializer->setMetadataFactory($jmsSerializer->getMetadataFactory()); |
||
189 | } |
||
190 | } |
||
191 | |||
192 | return new Hateoas($jmsSerializer, $linkHelper); |
||
193 | } |
||
194 | |||
195 | /** |
||
196 | * @param XmlSerializerInterface $xmlSerializer |
||
197 | * |
||
198 | * @return HateoasBuilder |
||
199 | */ |
||
200 | public function setXmlSerializer(XmlSerializerInterface $xmlSerializer) |
||
201 | { |
||
202 | $this->xmlSerializer = $xmlSerializer; |
||
203 | |||
204 | return $this; |
||
205 | } |
||
206 | |||
207 | /** |
||
208 | * Set the default XML serializer (`XmlSerializer`). |
||
209 | * |
||
210 | * @return HateoasBuilder |
||
211 | */ |
||
212 | public function setDefaultXmlSerializer() |
||
213 | { |
||
214 | return $this->setXmlSerializer(new XmlSerializer()); |
||
215 | } |
||
216 | |||
217 | /** |
||
218 | * @param JsonSerializerInterface $jsonSerializer |
||
219 | * |
||
220 | * @return HateoasBuilder |
||
221 | */ |
||
222 | public function setJsonSerializer(JsonSerializerInterface $jsonSerializer) |
||
223 | { |
||
224 | $this->jsonSerializer = $jsonSerializer; |
||
225 | |||
226 | return $this; |
||
227 | } |
||
228 | |||
229 | /** |
||
230 | * Set the default JSON serializer (`JsonHalSerializer`). |
||
231 | * |
||
232 | * @return HateoasBuilder |
||
233 | */ |
||
234 | public function setDefaultJsonSerializer() |
||
235 | { |
||
236 | return $this->setJsonSerializer(new JsonHalSerializer()); |
||
237 | } |
||
238 | |||
239 | /** |
||
240 | * Add a new URL generator. If you pass `null` as name, it will be the |
||
241 | * default URL generator. |
||
242 | * |
||
243 | * @param string|null $name |
||
244 | * @param UrlGeneratorInterface $urlGenerator |
||
245 | * |
||
246 | * @return HateoasBuilder |
||
247 | */ |
||
248 | public function setUrlGenerator($name, UrlGeneratorInterface $urlGenerator) |
||
249 | { |
||
250 | $this->urlGeneratorRegistry->set($name, $urlGenerator); |
||
251 | |||
252 | return $this; |
||
253 | } |
||
254 | |||
255 | /** |
||
256 | * Add a new expression context variable. |
||
257 | * |
||
258 | * @param string $name |
||
259 | * @param mixed $value |
||
260 | * |
||
261 | * @return HateoasBuilder |
||
262 | */ |
||
263 | public function setExpressionContextVariable($name, $value) |
||
264 | { |
||
265 | $this->contextVariables[$name] = $value; |
||
266 | |||
267 | return $this; |
||
268 | } |
||
269 | |||
270 | /** |
||
271 | * @param ExpressionLanguage $expressionLanguage |
||
272 | * |
||
273 | * @return HateoasBuilder |
||
274 | */ |
||
275 | public function setExpressionLanguage(ExpressionLanguage $expressionLanguage) |
||
276 | { |
||
277 | $this->expressionLanguage = $expressionLanguage; |
||
278 | |||
279 | return $this; |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * @param ExpressionFunctionInterface $expressionFunction |
||
284 | * |
||
285 | * @return HateoasBuilder |
||
286 | */ |
||
287 | public function registerExpressionFunction(ExpressionFunctionInterface $expressionFunction) |
||
288 | { |
||
289 | $this->expressionFunctions[] = $expressionFunction; |
||
290 | |||
291 | return $this; |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * Add a new relation provider resolver. |
||
296 | * |
||
297 | * @param RelationProviderResolverInterface $resolver |
||
298 | * |
||
299 | * @return HateoasBuilder |
||
300 | */ |
||
301 | public function addRelationProviderResolver(RelationProviderResolverInterface $resolver) |
||
302 | { |
||
303 | $this->chainResolver->addResolver($resolver); |
||
304 | |||
305 | return $this; |
||
306 | } |
||
307 | |||
308 | /** |
||
309 | * @param ConfigurationExtensionInterface $configurationExtension |
||
310 | * |
||
311 | * @return HateoasBuilder |
||
312 | */ |
||
313 | public function addConfigurationExtension(ConfigurationExtensionInterface $configurationExtension) |
||
314 | { |
||
315 | $this->configurationExtensions[] = $configurationExtension; |
||
316 | |||
317 | return $this; |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * @param boolean $debug |
||
322 | * |
||
323 | * @return HateoasBuilder |
||
324 | */ |
||
325 | public function setDebug($debug) |
||
326 | { |
||
327 | $this->debug = (boolean) $debug; |
||
328 | |||
329 | return $this; |
||
330 | } |
||
331 | |||
332 | /** |
||
333 | * @param string $dir |
||
334 | * |
||
335 | * @return HateoasBuilder |
||
336 | */ |
||
337 | public function setCacheDir($dir) |
||
338 | { |
||
339 | if (!is_dir($dir)) { |
||
340 | $this->createDir($dir); |
||
341 | } |
||
342 | |||
343 | if (!is_writable($dir)) { |
||
344 | throw new \InvalidArgumentException(sprintf('The cache directory "%s" is not writable.', $dir)); |
||
345 | } |
||
346 | |||
347 | $this->cacheDir = $dir; |
||
348 | |||
349 | return $this; |
||
350 | } |
||
351 | |||
352 | /** |
||
353 | * @param boolean $include Whether to include the metadata from the interfaces |
||
354 | * |
||
355 | * @return HateoasBuilder |
||
356 | */ |
||
357 | public function includeInterfaceMetadata($include) |
||
358 | { |
||
359 | $this->includeInterfaceMetadata = (boolean) $include; |
||
360 | |||
361 | return $this; |
||
362 | } |
||
363 | |||
364 | /** |
||
365 | * Set a map of namespace prefixes to directories. |
||
366 | * |
||
367 | * This method overrides any previously defined directories. |
||
368 | * |
||
369 | * @param array $namespacePrefixToDirMap |
||
370 | * |
||
371 | * @return HateoasBuilder |
||
372 | */ |
||
373 | public function setMetadataDirs(array $namespacePrefixToDirMap) |
||
374 | { |
||
375 | foreach ($namespacePrefixToDirMap as $dir) { |
||
376 | if (!is_dir($dir)) { |
||
377 | throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $dir)); |
||
378 | } |
||
379 | } |
||
380 | |||
381 | $this->metadataDirs = $namespacePrefixToDirMap; |
||
382 | |||
383 | return $this; |
||
384 | } |
||
385 | |||
386 | /** |
||
387 | * Add a directory where the serializer will look for class metadata. |
||
388 | * |
||
389 | * The namespace prefix will make the names of the actual metadata files a bit shorter. For example, let's assume |
||
390 | * that you have a directory where you only store metadata files for the ``MyApplication\Entity`` namespace. |
||
391 | * |
||
392 | * If you use an empty prefix, your metadata files would need to look like: |
||
393 | * |
||
394 | * ``my-dir/MyApplication.Entity.SomeObject.yml`` |
||
395 | * ``my-dir/MyApplication.Entity.OtherObject.yml`` |
||
396 | * |
||
397 | * If you use ``MyApplication\Entity`` as prefix, your metadata files would need to look like: |
||
398 | * |
||
399 | * ``my-dir/SomeObject.yml`` |
||
400 | * ``my-dir/OtherObject.yml`` |
||
401 | * |
||
402 | * Please keep in mind that you currently may only have one directory per namespace prefix. |
||
403 | * |
||
404 | * @param string $dir The directory where metadata files are located. |
||
405 | * @param string $namespacePrefix An optional prefix if you only store metadata for specific namespaces in this directory. |
||
406 | * |
||
407 | * @return HateoasBuilder |
||
408 | */ |
||
409 | View Code Duplication | public function addMetadataDir($dir, $namespacePrefix = '') |
|
410 | { |
||
411 | if (!is_dir($dir)) { |
||
412 | throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $dir)); |
||
413 | } |
||
414 | |||
415 | if (isset($this->metadataDirs[$namespacePrefix])) { |
||
416 | throw new \InvalidArgumentException(sprintf('There is already a directory configured for the namespace prefix "%s". Please use replaceMetadataDir() to override directories.', $namespacePrefix)); |
||
417 | } |
||
418 | |||
419 | $this->metadataDirs[$namespacePrefix] = $dir; |
||
420 | |||
421 | return $this; |
||
422 | } |
||
423 | |||
424 | /** |
||
425 | * Add a map of namespace prefixes to directories. |
||
426 | * |
||
427 | * @param array $namespacePrefixToDirMap |
||
428 | * |
||
429 | * @return HateoasBuilder |
||
430 | */ |
||
431 | public function addMetadataDirs(array $namespacePrefixToDirMap) |
||
432 | { |
||
433 | foreach ($namespacePrefixToDirMap as $prefix => $dir) { |
||
434 | $this->addMetadataDir($dir, $prefix); |
||
435 | } |
||
436 | |||
437 | return $this; |
||
438 | } |
||
439 | |||
440 | /** |
||
441 | * Similar to addMetadataDir(), but overrides an existing entry. |
||
442 | * |
||
443 | * @param string $dir |
||
444 | * @param string $namespacePrefix |
||
445 | * |
||
446 | * @return HateoasBuilder |
||
447 | */ |
||
448 | View Code Duplication | public function replaceMetadataDir($dir, $namespacePrefix = '') |
|
449 | { |
||
450 | if (!is_dir($dir)) { |
||
451 | throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $dir)); |
||
452 | } |
||
453 | |||
454 | if (!isset($this->metadataDirs[$namespacePrefix])) { |
||
455 | throw new \InvalidArgumentException(sprintf('There is no directory configured for namespace prefix "%s". Please use addMetadataDir() for adding new directories.', $namespacePrefix)); |
||
456 | } |
||
457 | |||
458 | $this->metadataDirs[$namespacePrefix] = $dir; |
||
459 | |||
460 | return $this; |
||
461 | } |
||
462 | |||
463 | private function buildMetadataFactory() |
||
464 | { |
||
465 | $annotationReader = $this->annotationReader; |
||
466 | |||
467 | if (null === $annotationReader) { |
||
468 | $annotationReader = new AnnotationReader(); |
||
469 | |||
470 | if (null !== $this->cacheDir) { |
||
471 | $this->createDir($this->cacheDir.'/annotations'); |
||
472 | $annotationReader = new FileCacheReader($annotationReader, $this->cacheDir.'/annotations', $this->debug); |
||
0 ignored issues
–
show
|
|||
473 | } |
||
474 | } |
||
475 | |||
476 | if (!empty($this->metadataDirs)) { |
||
477 | $fileLocator = new FileLocator($this->metadataDirs); |
||
478 | $metadataDriver = new DriverChain(array( |
||
479 | new YamlDriver($fileLocator), |
||
480 | new XmlDriver($fileLocator), |
||
481 | new AnnotationDriver($annotationReader), |
||
482 | )); |
||
483 | } else { |
||
484 | $metadataDriver = new AnnotationDriver($annotationReader); |
||
485 | } |
||
486 | |||
487 | $metadataDriver = new ExtensionDriver($metadataDriver, $this->configurationExtensions); |
||
488 | $metadataFactory = new MetadataFactory($metadataDriver, null, $this->debug); |
||
489 | $metadataFactory->setIncludeInterfaces($this->includeInterfaceMetadata); |
||
490 | |||
491 | if (null !== $this->cacheDir) { |
||
492 | $this->createDir($this->cacheDir.'/metadata'); |
||
493 | $metadataFactory->setCache(new FileCache($this->cacheDir.'/metadata')); |
||
494 | } |
||
495 | |||
496 | return $metadataFactory; |
||
497 | } |
||
498 | |||
499 | /** |
||
500 | * @param string $dir |
||
501 | */ |
||
502 | private function createDir($dir) |
||
503 | { |
||
504 | if (is_dir($dir)) { |
||
505 | return; |
||
506 | } |
||
507 | |||
508 | if (false === @mkdir($dir, 0777, true)) { |
||
509 | throw new \RuntimeException(sprintf('Could not create directory "%s".', $dir)); |
||
510 | } |
||
511 | } |
||
512 | |||
513 | private function getExpressionLanguage() |
||
514 | { |
||
515 | if (null === $this->expressionLanguage) { |
||
516 | $this->expressionLanguage = new ExpressionLanguage(); |
||
517 | } |
||
518 | |||
519 | return $this->expressionLanguage; |
||
520 | } |
||
521 | } |
||
522 |
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.