Completed
Push — master ( 192295...bd9b3e )
by Artem
22s queued 10s
created

JmsSerializerSubscriber   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 163
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 11
dl 0
loc 163
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A getSubscribedEvents() 0 7 1
C onPreSerialize() 0 62 12
B onPostSerialize() 0 21 6
B getHostUrl() 0 17 7
1
<?php
2
/*
3
 * This file is part of the FreshVichUploaderSerializationBundle
4
 *
5
 * (c) Artem Henvald <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace Fresh\VichUploaderSerializationBundle\EventListener;
14
15
use Doctrine\Common\Annotations\Reader;
16
use Doctrine\Common\Persistence\Proxy;
17
use Doctrine\Common\Util\ClassUtils;
18
use Fresh\VichUploaderSerializationBundle\Annotation\VichSerializableClass;
19
use Fresh\VichUploaderSerializationBundle\Annotation\VichSerializableField;
20
use Fresh\VichUploaderSerializationBundle\Exception\IncompatibleUploadableAndSerializableFieldAnnotationException;
21
use JMS\Serializer\EventDispatcher\Events;
22
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
23
use JMS\Serializer\EventDispatcher\ObjectEvent;
24
use JMS\Serializer\EventDispatcher\PreSerializeEvent;
25
use Psr\Log\LoggerInterface;
26
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
27
use Symfony\Component\Routing\RequestContext;
28
use Vich\UploaderBundle\Mapping\Annotation\UploadableField;
29
use Vich\UploaderBundle\Storage\StorageInterface;
30
31
/**
32
 * JmsPreSerializeListener Class.
33
 *
34
 * @author Artem Henvald <[email protected]>
35
 */
36
class JmsSerializerSubscriber implements EventSubscriberInterface
37
{
38
    /** @var StorageInterface */
39
    private $storage;
40
41
    /** @var RequestContext */
42
    private $requestContext;
43
44
    /** @var Reader */
45
    private $annotationReader;
46
47
    /** @var PropertyAccessorInterface */
48
    private $propertyAccessor;
49
50
    /** @var LoggerInterface */
51
    private $logger;
52
53
    /** @var array */
54
    private $serializedObjects = [];
55
56
    /**
57
     * @param StorageInterface          $storage
58
     * @param RequestContext            $requestContext
59
     * @param Reader                    $annotationReader
60
     * @param PropertyAccessorInterface $propertyAccessor
61
     * @param LoggerInterface           $logger
62
     */
63
    public function __construct(StorageInterface $storage, RequestContext $requestContext, Reader $annotationReader, PropertyAccessorInterface $propertyAccessor, LoggerInterface $logger)
64
    {
65
        $this->storage = $storage;
66
        $this->requestContext = $requestContext;
67
        $this->annotationReader = $annotationReader;
68
        $this->propertyAccessor = $propertyAccessor;
69
        $this->logger = $logger;
70
    }
71
72
    /**
73
     * @return array
74
     */
75
    public static function getSubscribedEvents(): array
76
    {
77
        return [
78
            ['event' => Events::PRE_SERIALIZE, 'method' => 'onPreSerialize'],
79
            ['event' => Events::POST_SERIALIZE, 'method' => 'onPostSerialize'],
80
        ];
81
    }
82
83
    /**
84
     * @param PreSerializeEvent $event
85
     *
86
     * @throws IncompatibleUploadableAndSerializableFieldAnnotationException
87
     */
88
    public function onPreSerialize(PreSerializeEvent $event): void
89
    {
90
        $object = $event->getObject();
91
        if (!is_object($object)) {
92
            return;
93
        }
94
95
        if ($object instanceof Proxy && !$object->__isInitialized()) {
96
            $object->__load();
97
        }
98
99
        $objectUid = \spl_object_hash($object);
100
        if (\array_key_exists($objectUid, $this->serializedObjects)) {
101
            return;
102
        }
103
104
        $classAnnotation = $this->annotationReader->getClassAnnotation(
105
            new \ReflectionClass(ClassUtils::getClass($object)),
106
            VichSerializableClass::class
107
        );
108
109
        if ($classAnnotation instanceof VichSerializableClass) {
110
            $reflectionClass = ClassUtils::newReflectionClass(\get_class($object));
111
            $this->logger->debug(\sprintf(
112
                'Found @VichSerializableClass annotation for the class "%s"',
113
                $reflectionClass->getName()
0 ignored issues
show
Bug introduced by
Consider using $reflectionClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
114
            ));
115
116
            foreach ($reflectionClass->getProperties() as $property) {
117
                $vichSerializableAnnotation = $this->annotationReader->getPropertyAnnotation($property, VichSerializableField::class);
118
119
                if ($vichSerializableAnnotation instanceof VichSerializableField) {
120
                    $vichUploadableFileAnnotation = $this->annotationReader->getPropertyAnnotation($property, UploadableField::class);
121
122
                    if ($vichUploadableFileAnnotation instanceof UploadableField) {
123
                        throw new IncompatibleUploadableAndSerializableFieldAnnotationException(\sprintf(
124
                            'The field "%s" in the class "%s" cannot have @UploadableField and @VichSerializableField annotations at the same moment.',
125
                            $property->getName(),
126
                            $reflectionClass->getName()
0 ignored issues
show
Bug introduced by
Consider using $reflectionClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
127
                        ));
128
                    }
129
                    $this->logger->debug(\sprintf(
130
                        'Found @VichSerializableField annotation for the field "%s" in the class "%s"',
131
                        $property->getName(),
132
                        $reflectionClass->getName()
0 ignored issues
show
Bug introduced by
Consider using $reflectionClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
133
                    ));
134
135
                    $uri = null;
136
                    $property->setAccessible(true);
137
138
                    if ($property->getValue($event->getObject())) {
139
                        $uri = $this->storage->resolveUri($object, $vichSerializableAnnotation->getField());
140
                        if ($vichSerializableAnnotation->isIncludeHost() && false === \filter_var($uri, FILTER_VALIDATE_URL)) {
141
                            $uri = $this->getHostUrl().$uri;
142
                        }
143
                    }
144
                    $this->serializedObjects[$objectUid][$property->getName()] = $property->getValue($event->getObject());
145
                    $property->setValue($object, $uri);
146
                }
147
            }
148
        }
149
    }
150
151
    /**
152
     * @param ObjectEvent $event
153
     */
154
    public function onPostSerialize(ObjectEvent $event): void
155
    {
156
        $object = $event->getObject();
157
        if (!is_object($object)) {
158
            return;
159
        }
160
161
        if ($object instanceof Proxy && !$object->__isInitialized()) {
162
            $object->__load();
163
        }
164
165
        $objectUid = \spl_object_hash($object);
166
        if (!\array_key_exists($objectUid, $this->serializedObjects)) {
167
            return;
168
        }
169
170
        foreach ($this->serializedObjects[$objectUid] as $propertyName => $propertyValue) {
171
            $this->propertyAccessor->setValue($object, $propertyName, $propertyValue);
172
        }
173
        unset($this->serializedObjects[$objectUid]);
174
    }
175
176
    /**
177
     * Get host url (scheme://host:port).
178
     *
179
     * @return string
180
     */
181
    private function getHostUrl(): string
182
    {
183
        $scheme = $this->requestContext->getScheme();
184
        $url = $scheme.'://'.$this->requestContext->getHost();
185
186
        $httpPort = $this->requestContext->getHttpPort();
187
        if ('http' === $scheme && $httpPort && 80 !== $httpPort) {
188
            return $url.':'.$httpPort;
189
        }
190
191
        $httpsPort = $this->requestContext->getHttpsPort();
192
        if ('https' === $scheme && $httpsPort && 443 !== $httpsPort) {
193
            return $url.':'.$httpsPort;
194
        }
195
196
        return $url;
197
    }
198
}
199