RequestDeserializationHandler::pack()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Realshadow\RequestDeserializer\Http\Request;
4
5
use JMS\Serializer\EventDispatcher\Events;
6
use Mews\Purifier\Purifier;
7
use Realshadow\RequestDeserializer\Contracts\RequestInterface;
8
use Dingo\Api\Exception\ValidationHttpException;
9
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
10
use JMS\Serializer\EventDispatcher\PreDeserializeEvent;
11
use JsonSchema\Uri\UriRetriever;
12
use JsonSchema\Validator;
13
14
15
/**
16
 * Handles request deserialization
17
 *
18
 * @package Realshadow\RequestDeserializer\Http\Request
19
 * @author Lukáš Homza <[email protected]>
20
 */
21
class RequestDeserializationHandler implements EventSubscriberInterface
22
{
23
24
    const NO_VALIDATE = 'no_validate';
25
26
    /**
27
     * @var Validator $validator
28
     */
29
    private $validator;
30
31
    /**
32
     * @var Purifier $purifier
33
     */
34
    private $purifier;
35
36
    /**
37
     * Recursively converts object to array
38
     *
39
     * @param \stdClass $data
40
     *
41
     * @return array
42
     */
43
    private function pack(\stdClass $data)
44
    {
45
        return json_decode(json_encode($data), true);
46
    }
47
48
    /**
49
     * Recursively converts array to object
50
     *
51
     * @param array|\stdClass $data
52
     *
53
     * @return \stdClass
54
     */
55
    private function unpack($data)
56
    {
57
        $data = json_decode(json_encode($data));
58
59
        return is_array($data) ? (object) $data : $data;
60
    }
61
62
    /**
63
     * RequestDeserializationHandler constructor.
64
     *
65
     * @param Validator $validator
66
     * @param Purifier $purifier
67
     */
68
    public function __construct(Validator $validator, Purifier $purifier)
69
    {
70
        $this->validator = $validator;
71
        $this->purifier = $purifier;
72
    }
73
74
    /**
75
     * @inheritdoc
76
     */
77
    public static function getSubscribedEvents()
78
    {
79
        return [
80
            ['event' => Events::PRE_DESERIALIZE, 'method' => 'sanitize'],
81
            ['event' => Events::PRE_DESERIALIZE, 'method' => 'validate'],
82
        ];
83
    }
84
85
    /**
86
     * @param PreDeserializeEvent $event
87
     *
88
     * @return PreDeserializeEvent
89
     */
90
    public function sanitize(PreDeserializeEvent $event)
91
    {
92
        $data = $this->purifier->clean($event->getData());
93
94
        if (is_array($data)) {
95
            array_walk_recursive($data, function (&$value) {
96
                $value = is_string($value) ? htmlspecialchars($value) : $value;
97
            });
98
        } else {
99
            $data = htmlspecialchars($data);
100
        }
101
102
        $event->setData($data);
103
104
        return $event;
105
    }
106
107
    /**
108
     * @param PreDeserializeEvent $event
109
     *
110
     * @return PreDeserializeEvent
111
     * @throws \ReflectionException
112
     *
113
     * @throws \JsonSchema\Exception\ExceptionInterface
114
     * @throws \Dingo\Api\Exception\ValidationHttpException
115
     */
116
    public function validate(PreDeserializeEvent $event)
117
    {
118
        $shouldSkipValidation = $event->getContext()
119
            ->attributes
120
            ->get(static::NO_VALIDATE)
121
            ->getOrElse(false);
122
123
        if ( ! $shouldSkipValidation) {
124
            $entityClass = $event->getType()['name'];
125
126
            /** @var RequestInterface $entity */
127
128
            $reflectionClass = new \ReflectionClass($entityClass);
129
            if ( ! $reflectionClass->implementsInterface(RequestInterface::class)) {
130
                return $event;
131
            }
132
133
            unset($reflectionClass);
134
135
            $entity = app($entityClass);
136
137
            $retriever = new UriRetriever();
138
            $schema = $retriever->retrieve('file://' . $entity->getSchema());
139
140
            $data = $event->getData();
141
            if ( ! $data) {
142
                $data = new \stdClass;
143
            }
144
145
            $data = $this->unpack($data);
146
147
            $this->validator->validate($data, $schema);
148
149
            if ( ! $this->validator->isValid()) {
150
                throw new ValidationHttpException($this->validator->getErrors());
151
            }
152
153
            $event->setData(
154
                $this->pack($data)
155
            );
156
        }
157
158
        return $event;
159
    }
160
161
}
162