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

RequestBodyParamConverter::configureDeserializationContext()   B

Complexity

Conditions 10
Paths 6

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 10

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 18
ccs 14
cts 14
cp 1
rs 7.2765
cc 10
eloc 13
nc 6
nop 2
crap 10

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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