Completed
Push — master ( 1319dc...6e6413 )
by Lukas Kahwe
05:12
created

RequestBodyParamConverter   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 143
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 96.88%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
wmc 21
lcom 1
cbo 9
dl 0
loc 143
ccs 62
cts 64
cp 0.9688
rs 10
c 3
b 1
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A supports() 0 4 1
B __construct() 0 24 5
C apply() 0 41 7
B configureContext() 0 16 6
A getValidatorOptions() 0 11 2
1
<?php
2
3
/*
4
 * This file is part of the FOSRestBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FOS\RestBundle\Request;
13
14
use FOS\RestBundle\Context\Adapter\DeserializationContextAdapterInterface;
15
use FOS\RestBundle\Context\Context;
16
use FOS\RestBundle\Serializer\Serializer;
17
use JMS\Serializer\Exception\Exception as JMSSerializerException;
18
use JMS\Serializer\Exception\UnsupportedFormatException;
19
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
20
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface;
21
use Symfony\Component\HttpFoundation\Request;
22
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
23
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
24
use Symfony\Component\OptionsResolver\OptionsResolver;
25
use Symfony\Component\Serializer\Exception\ExceptionInterface as SymfonySerializerException;
26
use Symfony\Component\Validator\Validator\ValidatorInterface;
27
28
/**
29
 * @author Tyler Stroud <[email protected]>
30
 */
31
class RequestBodyParamConverter implements ParamConverterInterface
32
{
33
    private $serializer;
34
    private $context = [];
35
    private $validator;
36
37
    /**
38
     * The name of the argument on which the ConstraintViolationList will be set.
39
     *
40
     * @var null|string
41
     */
42
    private $validationErrorsArgument;
43
44
    /**
45
     * @var DeserializationContextAdapterInterface
46
     */
47
    private $contextAdapter;
48
49
    /**
50
     * @param Serializer         $serializer
51
     * @param array|null         $groups                   An array of groups to be used in the serialization context
52
     * @param string|null        $version                  A version string to be used in the serialization context
53
     * @param ValidatorInterface $validator
54
     * @param string|null        $validationErrorsArgument
55
     *
56
     * @throws \InvalidArgumentException
57
     */
58 14
    public function __construct(
59
        Serializer $serializer,
60
        $groups = null,
61
        $version = null,
62
        ValidatorInterface $validator = null,
63
        $validationErrorsArgument = null
64
    ) {
65 14
        $this->serializer = $serializer;
66
67 14
        if (!empty($groups)) {
68 1
            $this->context['groups'] = (array) $groups;
69 1
        }
70
71 14
        if (!empty($version)) {
72 1
            $this->context['version'] = $version;
73 1
        }
74
75 14
        if (null !== $validator && null === $validationErrorsArgument) {
76
            throw new \InvalidArgumentException('"$validationErrorsArgument" cannot be null when using the validator');
77
        }
78
79 14
        $this->validator = $validator;
80 14
        $this->validationErrorsArgument = $validationErrorsArgument;
81 14
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86 8
    public function apply(Request $request, ParamConverter $configuration)
87
    {
88 8
        $options = (array) $configuration->getOptions();
89
90 8
        if (isset($options['deserializationContext']) && is_array($options['deserializationContext'])) {
91 1
            $arrayContext = array_merge($this->context, $options['deserializationContext']);
92 1
        } else {
93 7
            $arrayContext = $this->context;
94
        }
95 8
        $this->configureContext($context = new Context(), $arrayContext);
96
97
        try {
98 8
            $object = $this->serializer->deserialize(
99 8
                $request->getContent(),
0 ignored issues
show
Bug introduced by
It seems like $request->getContent() targeting Symfony\Component\HttpFo...n\Request::getContent() can also be of type resource; however, FOS\RestBundle\Serialize...rializer::deserialize() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
100 8
                $configuration->getClass(),
101 8
                $request->getContentType(),
102
                $context
103 8
            );
104 8
        } catch (UnsupportedFormatException $e) {
105
            throw new UnsupportedMediaTypeHttpException($e->getMessage(), $e);
106 3
        } catch (JMSSerializerException $e) {
107 1
            throw new BadRequestHttpException($e->getMessage(), $e);
0 ignored issues
show
Documentation introduced by
$e is of type object<JMS\Serializer\Exception\Exception>, but the function expects a null|object<Exception>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
108 2
        } catch (SymfonySerializerException $e) {
109 1
            throw new BadRequestHttpException($e->getMessage(), $e);
0 ignored issues
show
Documentation introduced by
$e is of type object<Symfony\Component...ion\ExceptionInterface>, but the function expects a null|object<Exception>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
110
        }
111
112 5
        $request->attributes->set($configuration->getName(), $object);
113
114 5
        if (null !== $this->validator) {
115 1
            $validatorOptions = $this->getValidatorOptions($options);
116
117 1
            $errors = $this->validator->validate($object, null, $validatorOptions['groups']);
118
119 1
            $request->attributes->set(
120 1
                $this->validationErrorsArgument,
121
                $errors
122 1
            );
123 1
        }
124
125 5
        return true;
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131 3
    public function supports(ParamConverter $configuration)
132
    {
133 3
        return null !== $configuration->getClass();
134
    }
135
136
    /**
137
     * @param Context $context
138
     * @param array   $options
139
     */
140 8
    protected function configureContext(Context $context, array $options)
141
    {
142 8
        foreach ($options as $key => $value) {
143 1
            if ($key == 'groups') {
144 1
                $context->addGroups($options['groups']);
145 1
            } elseif ($key == 'version') {
146 1
                $context->setVersion($options['version']);
147 1
            } elseif ($key == 'maxDepth') {
148 1
                $context->setMaxDepth($options['maxDepth']);
149 1
            } elseif ($key == 'serializeNull') {
150 1
                $context->setSerializeNull($options['serializeNull']);
151 1
            } else {
152 1
                $context->setAttribute($key, $value);
153
            }
154 8
        }
155 8
    }
156
157
    /**
158
     * @param array $options
159
     *
160
     * @return array
161
     */
162 2
    private function getValidatorOptions(array $options)
163
    {
164 2
        $resolver = new OptionsResolver();
165 2
        $resolver->setDefaults([
166 2
            'groups' => null,
167 2
            'traverse' => false,
168 2
            'deep' => false,
169 2
        ]);
170
171 2
        return $resolver->resolve(isset($options['validator']) ? $options['validator'] : []);
172
    }
173
}
174