Completed
Push — master ( 6d34bd...c222a2 )
by Karol
03:16
created

Serializer/Handler/FileReferenceHandler.php (1 issue)

Labels
Severity
1
<?php
2
declare(strict_types=1);
3
4
namespace SourceBroker\T3api\Serializer\Handler;
5
6
use JMS\Serializer\DeserializationContext;
7
use JMS\Serializer\JsonDeserializationVisitor;
8
use JMS\Serializer\SerializationContext;
9
use JMS\Serializer\Visitor\DeserializationVisitorInterface;
10
use JMS\Serializer\Visitor\SerializationVisitorInterface;
11
use RuntimeException;
12
use SourceBroker\T3api\Exception\ValidationException;
13
use SourceBroker\T3api\Service\SerializerService;
14
use TYPO3\CMS\Core\Resource\FileReference as Typo3FileReference;
15
use TYPO3\CMS\Core\Resource\Rendering\RendererRegistry;
16
use TYPO3\CMS\Core\Resource\ResourceFactory;
17
use TYPO3\CMS\Extbase\Domain\Model\AbstractFileFolder;
18
use TYPO3\CMS\Extbase\Domain\Model\FileReference as ExtbaseFileReference;
19
use TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject;
20
use TYPO3\CMS\Extbase\Error\Error;
21
use TYPO3\CMS\Extbase\Error\Result;
22
use TYPO3\CMS\Extbase\Object\ObjectManager;
23
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
24
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
25
26
/**
27
 * Class FileReferenceHandler
28
 */
29
class FileReferenceHandler extends AbstractHandler implements SerializeHandlerInterface, DeserializeHandlerInterface
30
{
31
    public const TYPE = 'FileReferenceTransport';
32
33
    /**
34
     * @var string[]
35
     */
36
    protected static $supportedTypes = [self::TYPE];
37
38
    /**
39
     * @var ResourceFactory
40
     */
41
    protected $resourceFactory;
42
43
    /**
44
     * @var ObjectManager
45
     */
46
    protected $objectManager;
47
48
    /**
49
     * @var PersistenceManager
50
     */
51
    protected $persistenceManager;
52
53
    /**
54
     * @var SerializerService
55
     */
56
    protected $serializerService;
57
58
    /**
59
     * @param ResourceFactory $resourceFactory
60
     */
61
    public function injectResourceFactory(ResourceFactory $resourceFactory): void
62
    {
63
        $this->resourceFactory = $resourceFactory;
64
    }
65
66
    /**
67
     * @param ObjectManager $objectManager
68
     */
69
    public function injectObjectManager(ObjectManager $objectManager): void
70
    {
71
        $this->objectManager = $objectManager;
72
    }
73
74
    /**
75
     * @param PersistenceManager $persistenceManager
76
     */
77
    public function injectPersistenceManager(PersistenceManager $persistenceManager): void
78
    {
79
        $this->persistenceManager = $persistenceManager;
80
    }
81
82
    /**
83
     * @param SerializerService $serializerService
84
     */
85
    public function injectSerializerService(SerializerService $serializerService): void
86
    {
87
        $this->serializerService = $serializerService;
88
    }
89
90
    /**
91
     * @param SerializationVisitorInterface $visitor
92
     * @param ExtbaseFileReference|Typo3FileReference $fileReference
93
     * @param array $type
94
     * @param SerializationContext $context
95
     *
96
     * @return array
97
     *
98
     * @todo Try to implement it with default JMS serialization functionality instead of using this handler
99
     */
100
    public function serialize(
101
        SerializationVisitorInterface $visitor,
102
        $fileReference,
103
        array $type,
104
        SerializationContext $context
105
    ): array {
106
        /** @var Typo3FileReference $originalResource */
107
        $originalResource = $fileReference instanceof ExtbaseFileReference
108
            ? $fileReference->getOriginalResource()
109
            : $fileReference;
110
        $originalFile = $originalResource->getOriginalFile();
111
112
        $url = $originalResource->getPublicUrl();
113
        if (parse_url($url, PHP_URL_SCHEME) === null) {
114
            $url = $context->getAttribute('TYPO3_SITE_URL') . $url;
115
        }
116
117
        $out = [
118
            'uid' => $fileReference->getUid(),
119
            'url' => $url,
120
            'file' => [
121
                'uid' => $originalFile->getUid(),
122
                'name' => $originalFile->getName(),
123
                'mimeType' => $originalFile->getMimeType(),
124
                'size' => $originalFile->getSize(),
125
            ],
126
        ];
127
128
        // TODO: move to some signal/slot
129
        if (preg_match('#video/.*#', $originalFile->getMimeType())) {
130
            $fileRenderer = RendererRegistry::getInstance()->getRenderer($originalFile);
131
            if ($fileRenderer !== null && preg_match(
132
                '/src="([^"]+)"/',
133
                $fileRenderer->render($originalFile, 1, 1),
134
                $match
135
            )) {
136
                $urlEmbed = $match[1];
137
                if (parse_url($urlEmbed, PHP_URL_SCHEME) === null) {
138
                    $urlEmbed = $context->getAttribute('TYPO3_SITE_URL') . $urlEmbed;
139
                }
140
                $out['urlEmbed'] = $urlEmbed;
141
            }
142
        }
143
144
        return $out;
145
    }
146
147
    /**
148
     * @param DeserializationVisitorInterface $visitor
149
     * @param mixed $data
150
     * @param array $type
151
     * @param DeserializationContext $context
152
     * @throws ValidationException
153
     * @return mixed|void
154
     */
155
    public function deserialize(
156
        DeserializationVisitorInterface $visitor,
157
        $data,
158
        array $type,
159
        DeserializationContext $context
160
    ) {
161
        if ($type['name'] !== self::TYPE) {
162
            throw new RuntimeException(sprintf('`%s` is unknown type.', $type['name']), 1577534783745);
163
        }
164
165
        if (empty($type['params']['targetType'])) {
166
            throw new RuntimeException('`targetType` is required parameter.', 1577534803669);
167
        }
168
169
        if (!is_subclass_of($type['params']['targetType'], AbstractFileFolder::class)) {
170
            throw new RuntimeException(
171
                sprintf('Has to be an instance of `%s` to be processed', AbstractFileFolder::class),
172
                1577534838461
173
            );
174
        }
175
176
        if ($data === 0) {
177
            $this->removeExistingFileReference($context);
178
179
            return null;
180
        }
181
182
        $isNew = is_array($data) && empty($data['uid']);
183
184
        if ($isNew) {
185
            return $this->createSysFileReference($data, $type['params']['targetType'], $context);
186
        }
187
188
        $uid = (int)(is_numeric($data) ? $data : $data['uid']);
189
190
        if ($uid) {
191
            return $this->persistenceManager->getObjectByIdentifier(
192
                $uid,
193
                ExtbaseFileReference::class,
194
                false
195
            );
196
        }
197
    }
198
199
    /**
200
     * @param array $data
201
     * @param string $type
202
     * @param DeserializationContext $context
203
     * @throws ValidationException
204
     * @return ExtbaseFileReference
205
     */
206
    protected function createSysFileReference(
207
        array $data,
208
        string $type,
209
        DeserializationContext $context
210
    ): ExtbaseFileReference {
211
        /** @var JsonDeserializationVisitor $visitor */
212
        $visitor = $context->getVisitor();
213
214
        if (empty($data['uidLocal'])) {
215
            $result = new Result();
216
            $result->forProperty('uidLocal')->addError(
217
                new Error('Property `uidLocal` is required to create sys file reference', 1577083636258)
218
            );
219
220
            throw new ValidationException($result, 1581461062805);
221
        }
222
223
        $this->removeExistingFileReference($context);
224
225
        /** @var ExtbaseFileReference $fileReference */
226
        $fileReference = $this->serializerService->deserialize(
227
            json_encode($data),
228
            $type,
229
            $this->cloneDeserializationContext($context, ['target' => null])
230
        );
231
232
        $fileReference->setOriginalResource(
233
            $this->resourceFactory->createFileReferenceObject(
234
                [
235
                    'uid_local' => $data['uidLocal'],
236
                    'uid' => uniqid('NEW_', true),
237
                ]
238
            )
239
        );
240
241
        if ($visitor->getCurrentObject() instanceof AbstractDomainObject) {
242
            /** @var AbstractDomainObject $currentObject */
243
            $currentObject = $visitor->getCurrentObject();
244
            $fileReference->setPid($currentObject->getPid());
0 ignored issues
show
It seems like $currentObject->getPid() can also be of type null; however, parameter $pid of TYPO3\CMS\Extbase\Domain...tDomainObject::setPid() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

244
            $fileReference->setPid(/** @scrutinizer ignore-type */ $currentObject->getPid());
Loading history...
245
            $fileReference->_setProperty('_languageUid', $currentObject->_getProperty('_languageUid'));
246
        }
247
248
        return $fileReference;
249
    }
250
251
    /**
252
     * Removes already existing file reference if property is not a collection but relation to single file
253
     *
254
     * @param DeserializationContext $context
255
     */
256
    protected function removeExistingFileReference(DeserializationContext $context): void
257
    {
258
        /** @var JsonDeserializationVisitor $visitor */
259
        $visitor = $context->getVisitor();
260
        $propertyName = $context->getCurrentPath()[count($context->getCurrentPath()) - 1];
261
        $propertyValue = ObjectAccess::getProperty($visitor->getCurrentObject(), $propertyName);
262
        if ($propertyValue instanceof ExtbaseFileReference || $propertyValue instanceof Typo3FileReference) {
263
            $this->persistenceManager->remove($propertyValue);
264
        }
265
    }
266
}
267