Completed
Push — develop ( d5109f...d13d89 )
by Daniel
06:42
created

ApiNormalizer::isImagineSupportedFile()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 1
dl 0
loc 11
ccs 6
cts 7
cp 0.8571
crap 3.0261
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Silverback\ApiComponentBundle\Serializer;
4
5
use ApiPlatform\Core\DataProvider\ContextAwareCollectionDataProviderInterface;
6
use Doctrine\ORM\EntityManagerInterface;
7
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
8
use Silverback\ApiComponentBundle\Entity\Content\Component\Collection\Collection;
9
use Silverback\ApiComponentBundle\Entity\Content\Component\ComponentLocation;
10
use Silverback\ApiComponentBundle\Entity\Content\Component\Form\Form;
11
use Silverback\ApiComponentBundle\Entity\Content\Dynamic\AbstractDynamicPage;
12
use Silverback\ApiComponentBundle\Entity\Content\FileInterface;
13
use Silverback\ApiComponentBundle\Entity\Content\Page;
14
use Silverback\ApiComponentBundle\Entity\Layout\Layout;
15
use Silverback\ApiComponentBundle\Factory\Entity\Content\Component\Form\FormViewFactory;
16
use Silverback\ApiComponentBundle\Imagine\PathResolver;
17
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
18
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
19
use Symfony\Component\Serializer\SerializerAwareInterface;
20
use Symfony\Component\Serializer\SerializerInterface;
21
22
final class ApiNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface
23
{
24
    private $decorated;
25
    private $imagineCacheManager;
26
    private $formViewFactory;
27
    private $pathResolver;
28
    private $em;
29
    private $projectDir;
30
    private $collectionDataProvider;
31
32
    /**
33
     * FileNormalizer constructor.
34
     * @param NormalizerInterface $decorated
35
     * @param CacheManager $imagineCacheManager
36
     * @param FormViewFactory $formViewFactory
37
     * @param PathResolver $pathResolver
38
     * @param EntityManagerInterface $entityManager
39
     * @param ContextAwareCollectionDataProviderInterface $collectionDataProvider
40
     * @param string $projectDir
41
     */
42 10
    public function __construct(
43
        NormalizerInterface $decorated,
44
        CacheManager $imagineCacheManager,
45
        FormViewFactory $formViewFactory,
46
        PathResolver $pathResolver,
47
        EntityManagerInterface $entityManager,
48
        ContextAwareCollectionDataProviderInterface $collectionDataProvider,
49
        string $projectDir = '/'
50
    ) {
51 10
        if (!$decorated instanceof DenormalizerInterface) {
52
            throw new \InvalidArgumentException(sprintf('The decorated normalizer must implement the %s.', DenormalizerInterface::class));
53
        }
54 10
        $this->decorated = $decorated;
55 10
        $this->imagineCacheManager = $imagineCacheManager;
56 10
        $this->formViewFactory = $formViewFactory;
57 10
        $this->pathResolver = $pathResolver;
58 10
        $this->em = $entityManager;
59 10
        $this->collectionDataProvider = $collectionDataProvider;
60 10
        $this->projectDir = $projectDir;
61 10
    }
62
63
    /**
64
     * @param mixed $data
65
     * @param string|null $format
66
     * @return bool
67
     */
68 5
    public function supportsNormalization($data, $format = null): bool
69
    {
70 5
        return $this->decorated->supportsNormalization($data, $format);
71
    }
72
73
    /**
74
     * @param $object
75
     * @param string|null $format
76
     * @param array $context
77
     * @return array|bool|float|int|string
78
     * @throws \Symfony\Component\Serializer\Exception\LogicException
79
     * @throws \Symfony\Component\Serializer\Exception\InvalidArgumentException
80
     * @throws \Symfony\Component\Serializer\Exception\CircularReferenceException
81
     * @throws \ApiPlatform\Core\Exception\ResourceClassNotSupportedException
82
     */
83 6
    public function normalize($object, $format = null, array $context = [])
84
    {
85 6
        if (($object instanceof Page || $object instanceof AbstractDynamicPage) && !$object->getLayout()) {
86
            // Should we be using the ItemDataProvider (or detect data provider and use that, we already use a custom data provider for layouts)
87
            $object->setLayout($this->em->getRepository(Layout::class)->findOneBy(['default' => true]));
88
        }
89 6
        if ($object instanceof AbstractDynamicPage) {
90
            $object = $this->populateDynamicComponents($object);
91
        }
92 6
        if ($object instanceof Collection) {
93
            // We should really find whatever the data provider is currently for the resource instead of just using the default
94
            $object->setCollection($this->collectionDataProvider->getCollection($object->getResource(), 'GET', $context));
95
        }
96 6
        if ($object instanceof Form && !$object->getForm()) {
97 1
            $object->setForm($this->formViewFactory->create($object));
98
        }
99 6
        $data = $this->decorated->normalize($object, $format, $context);
100
101 6
        if ($object instanceof FileInterface) {
102 1
            $data = array_merge($data, $this->getFileData($object));
103
        }
104 6
        return $data;
105
    }
106
107
    /**
108
     * @param \Silverback\ApiComponentBundle\Entity\Content\FileInterface $object
109
     * @return array
110
     */
111 1
    private function getFileData(FileInterface $object): array
112
    {
113 1
        $data = [];
114 1
        $filePath = $object->getFilePath();
115 1
        if ($filePath) {
116 1
            if (false !== \exif_imagetype($filePath)) {
117 1
                [$width, $height] = getimagesize($filePath);
118
            } else {
119
                $width = $height = 0;
120
            }
121 1
            $data['width'] = $width;
122 1
            $data['height'] = $height;
123
124 1
            $supported = $this->isImagineSupportedFile($filePath);
125 1
            foreach ($object::getImagineFilters() as $returnKey => $filter) {
126 1
                $data[$returnKey] = $supported ? parse_url(
127 1
                    $this->imagineCacheManager->getBrowserPath($this->pathResolver->resolve($filePath), $filter),
128 1
                    PHP_URL_PATH
129 1
                ) : null;
130
            }
131
132 1
            $data['filePath'] = $this->getPublicPath($filePath);
133
        }
134 1
        return $data;
135
    }
136
137 1
    private function getPublicPath(string $filePath) {
138 1
        $publicPaths = [$this->projectDir, '/public/', '/web/'];
139 1
        foreach ($publicPaths as $path) {
140 1
            if (mb_strpos($filePath, $path) === 0 && $start = \strlen($path)) {
141 1
                $filePath = mb_substr($filePath, $start);
142
            }
143
        }
144 1
        return $filePath;
145
    }
146
147
    /**
148
     * @param mixed $data
149
     * @param string $type
150
     * @param string|null $format
151
     * @return bool
152
     */
153 5
    public function supportsDenormalization($data, $type, $format = null): bool
154
    {
155 5
        return $this->decorated->supportsDenormalization($data, $type, $format);
156
    }
157
158
    /**
159
     * @param mixed $data
160
     * @param string $class
161
     * @param string|null $format
162
     * @param array $context
163
     * @return object
164
     * @throws \Symfony\Component\Serializer\Exception\UnexpectedValueException
165
     * @throws \Symfony\Component\Serializer\Exception\RuntimeException
166
     * @throws \Symfony\Component\Serializer\Exception\LogicException
167
     * @throws \Symfony\Component\Serializer\Exception\InvalidArgumentException
168
     * @throws \Symfony\Component\Serializer\Exception\ExtraAttributesException
169
     * @throws \Symfony\Component\Serializer\Exception\BadMethodCallException
170
     * @throws \InvalidArgumentException
171
     */
172 1
    public function denormalize($data, $class, $format = null, array $context = [])
173
    {
174 1
        $context['allow_extra_attributes'] = $class === Form::class;
175 1
        return $this->decorated->denormalize($data, $class, $format, $context);
176
    }
177
178
    /**
179
     * @param SerializerInterface $serializer
180
     */
181 5
    public function setSerializer(SerializerInterface $serializer)
182
    {
183 5
        if ($this->decorated instanceof SerializerAwareInterface) {
184 5
            $this->decorated->setSerializer($serializer);
185
        }
186 5
    }
187
188
    /**
189
     * @param string $filePath
190
     * @return bool
191
     */
192 3
    public function isImagineSupportedFile(?string $filePath): bool
193
    {
194 3
        if (!$filePath) {
195
            return false;
196
        }
197
        try {
198 3
            $imageType = \exif_imagetype($filePath);
199 1
        } catch (\Exception $e) {
200 1
            return false;
201
        }
202 3
        return \in_array($imageType, [IMAGETYPE_JPEG, IMAGETYPE_JPEG2000, IMAGETYPE_PNG, IMAGETYPE_GIF], true);
203
    }
204
205
    /**
206
     * @param AbstractDynamicPage $page
207
     * @return AbstractDynamicPage
208
     */
209
    public function populateDynamicComponents(AbstractDynamicPage $page): AbstractDynamicPage
210
    {
211
        $locations = $this->em->getRepository(ComponentLocation::class)->findByDynamicPage($page);
212
        if ($locations) {
213
            $page->setComponentLocations($locations);
214
        }
215
        return $page;
216
    }
217
}
218