GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 98ac49...dad22a )
by Sergey
03:42
created

JsonApiService::validateRequest()   C

Complexity

Conditions 7
Paths 11

Size

Total Lines 31
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 3
Bugs 1 Features 1
Metric Value
c 3
b 1
f 1
dl 0
loc 31
ccs 0
cts 18
cp 0
rs 6.7272
cc 7
eloc 19
nc 11
nop 1
crap 56
1
<?php
2
/*
3
 * This file is part of the reva2/jsonapi.
4
 *
5
 * (c) Sergey Revenko <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Reva2\JsonApi\Services;
12
13
use Doctrine\Common\Proxy\Exception\InvalidArgumentException;
14
use Neomerx\JsonApi\Contracts\Codec\CodecMatcherInterface;
15
use Neomerx\JsonApi\Contracts\Schema\ContainerInterface;
16
use Neomerx\JsonApi\Document\Error;
17
use Neomerx\JsonApi\Exceptions\JsonApiException;
18
use Reva2\JsonApi\Contracts\Decoders\DataParserInterface;
19
use Reva2\JsonApi\Contracts\Decoders\DecoderInterface;
20
use Reva2\JsonApi\Contracts\Factories\FactoryInterface;
21
use Reva2\JsonApi\Contracts\Http\Query\QueryParametersParserInterface;
22
use Reva2\JsonApi\Contracts\Http\RequestInterface;
23
use Reva2\JsonApi\Contracts\Services\EnvironmentInterface;
24
use Reva2\JsonApi\Contracts\Services\JsonApiRegistryInterface;
25
use Reva2\JsonApi\Contracts\Services\JsonApiServiceInterface;
26
use Reva2\JsonApi\Contracts\Services\ValidationServiceInterface;
27
use Reva2\JsonApi\Http\ResponseFactory;
28
use Symfony\Component\HttpFoundation\Request;
29
use Neomerx\JsonApi\Http\Request as Psr7Request;
30
31
/**
32
 * Service for JSON API requests processing
33
 *
34
 * @package Reva2\JsonApi\Services
35
 * @author Sergey Revenko <[email protected]>
36
 */
37
class JsonApiService implements JsonApiServiceInterface
38
{
39
    /**
40
     * @var FactoryInterface
41
     */
42
    protected $factory;
43
44
    /**
45
     * @var JsonApiRegistryInterface
46
     */
47
    protected $registry;
48
49
    /**
50
     * @var ContainerInterface
51
     */
52
    protected $schemas;
53
54
    /**
55
     * @var ValidationServiceInterface
56
     */
57
    protected $validator;
58
59
    /**
60
     * @var DataParserInterface
61
     */
62
    protected $parser;
63
64
    /**
65
     * Constructor
66
     *
67
     * @param FactoryInterface $factory
68
     * @param JsonApiRegistryInterface $registry
69
     * @param ContainerInterface $schemas
70
     * @param DataParserInterface $parser
71
     * @param ValidationServiceInterface $validator
72
     */
73
    public function __construct(
74
        FactoryInterface $factory,
75
        JsonApiRegistryInterface $registry,
76
        ContainerInterface $schemas,
77
        DataParserInterface $parser,
78
        ValidationServiceInterface $validator
79
    ) {
80
        $this->factory = $factory;
81
        $this->registry = $registry;
82
        $this->parser = $parser;
83
        $this->schemas = $schemas;
84
        $this->validator = $validator;
85
    }
86
87
    /**
88
     * @inheritdoc
89
     */
90
    public function getFactory()
91
    {
92
        return $this->factory;
93
    }
94
95
    /**
96
     * @inheritdoc
97
     */
98
    public function parseRequest(Request $request, EnvironmentInterface $environment = null)
99
    {
100
        if (null === $environment) {
101
            $environment = $this->getRequestEnvironment($request);
102
        }
103
104
        $this->initializeEnvironment($environment, $request);
105
106
        $apiRequest = $this->factory->createRequest($environment);
107
        $apiRequest
108
            ->setQuery($this->parseQuery($request, $environment))
109
            ->setBody($this->parseBody($request, $environment));
110
111
        if (null !== $environment->getValidationGroups()) {
112
            $this->validateRequest($apiRequest);
113
        }
114
115
        return $apiRequest;
116
    }
117
118
    /**
119
     * @inheritdoc
120
     */
121
    public function validateRequest(RequestInterface $request)
122
    {
123
        $validationGroups = $request->getEnvironment()->getValidationGroups();
124
        if (is_bool($validationGroups)) {
125
            if (false === $validationGroups) {
126
                return;
127
            } else {
128
                $validationGroups = null;
129
            }
130
        }
131
132
        $errors = $this->validateData($request->getQuery(), $validationGroups);
0 ignored issues
show
Bug introduced by
It seems like $validationGroups can also be of type null; however, Reva2\JsonApi\Services\J...Service::validateData() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
133
        $errors = array_merge($errors, $this->validateData($request->getBody(), $validationGroups));
0 ignored issues
show
Bug introduced by
It seems like $validationGroups can also be of type null; however, Reva2\JsonApi\Services\J...Service::validateData() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
134
135
        if (0 === count($errors)) {
136
            return;
137
        }
138
139
        $code = null;
140
        foreach ($errors as $error) {
141
            /* @var $error Error */
142
            if (null === $code) {
143
                $code = $error->getStatus();
144
            } elseif ($code !== $error->getStatus()) {
145
                $code = 400;
146
                break;
147
            }
148
        }
149
150
        throw new JsonApiException($errors, $code);
151
    }
152
153
    /**
154
     * @inheritdoc
155
     */
156
    public function getResponseFactory(RequestInterface $request)
157
    {
158
        return new ResponseFactory($this->schemas, $request->getEnvironment(), $request->getQuery());
159
    }
160
161
    /**
162
     * Returns JSON API environment configured in request
163
     *
164
     * @param Request $request
165
     * @return EnvironmentInterface
166
     */
167
    protected function getRequestEnvironment(Request $request)
168
    {
169
        if (false === $request->attributes->has('_jsonapi')) {
170
            throw new \RuntimeException('JSON API environment is not provided');
171
        }
172
173
        $environment = $request->attributes->get('_jsonapi');
174
        if (!$environment instanceof EnvironmentInterface) {
175
            throw new \InvalidArgumentException(sprintf(
176
                "JSON API environment should implement %s interface",
177
                EnvironmentInterface::class
178
            ));
179
        }
180
181
        return $environment;
182
    }
183
184
    /**
185
     * Initialize JSON API environment for specified request
186
     *
187
     * @param EnvironmentInterface $environment
188
     * @param Request $request
189
     */
190
    private function initializeEnvironment(EnvironmentInterface $environment, Request $request)
191
    {
192
        $matcher = $this->createMatcher($environment);
193
194
        $this->parseRequestHeaders($request, $matcher);
195
196
        $environment
197
            ->setDecoder($matcher->getDecoder())
198
            ->setEncoder($matcher->getEncoder())
199
            ->setEncoderMediaType($matcher->getEncoderRegisteredMatchedType());
200
    }
201
202
    /**
203
     * Create codec matcher for specified environment
204
     *
205
     * @param EnvironmentInterface $environment
206
     * @return \Neomerx\JsonApi\Contracts\Codec\CodecMatcherInterface
207
     */
208
    private function createMatcher(EnvironmentInterface $environment)
209
    {
210
        $matcher = $this->factory->createCodecMatcher();
211
212
        $config = $environment->getMatcherConfiguration();
213
        if ((array_key_exists('decoders', $config)) && (is_array($config['decoders']))) {
214
            $this->registerDecoders($config['decoders'], $matcher);
215
        }
216
217
        if ((array_key_exists('encoders', $config)) && (is_array($config['encoders']))) {
218
            $this->registerEncoders($config['encoders'], $matcher);
219
        }
220
221
        return $matcher;
222
    }
223
224
    /**
225
     * Convert media type string to media type object
226
     *
227
     * @param string $type
228
     * @return \Neomerx\JsonApi\Contracts\Http\Headers\MediaTypeInterface
229
     */
230
    private function parseMediaTypeString($type)
231
    {
232
        $parts = explode('/', $type);
233
        if (2 !== $parts) {
234
            throw new InvalidArgumentException(sprintf("Invalid media type '%s' specified", $type));
235
        }
236
237
        return $this->factory->createMediaType($parts[0], $parts[1]);
238
    }
239
240
    /**
241
     * Parse request headers and detect appropriate decoder and encoder
242
     *
243
     * @param Request $request
244
     * @param CodecMatcherInterface $matcher
245
     */
246
    private function parseRequestHeaders(Request $request, CodecMatcherInterface $matcher)
247
    {
248
        $psr7Request = $this->createPsr7Request($request);
249
        $headers = $this->factory->createHeaderParametersParser()->parse($psr7Request);
250
        $checker = $this->factory->createHeadersChecker($matcher);
251
252
        $checker->checkHeaders($headers);
253
    }
254
255
    /**
256
     * Create PSR7 request from symfony http foundation request
257
     *
258
     * @param Request $request
259
     * @return Psr7Request
260
     */
261
    private function createPsr7Request(Request $request)
262
    {
263
        return new Psr7Request(
264
            function () use ($request) {
265
                return $request->getMethod();
266
            },
267
            function ($name) use ($request) {
268
                return $request->headers->get($name);
269
            },
270
            function () use ($request) {
271
                return $request->query->all();
272
            }
273
        );
274
    }
275
276
    /**
277
     * Parse request query parameters
278
     *
279
     * @param Request $request
280
     * @param EnvironmentInterface $environment
281
     * @return \Neomerx\JsonApi\Contracts\Encoder\Parameters\EncodingParametersInterface|null
282
     */
283
    private function parseQuery(Request $request, EnvironmentInterface $environment)
284
    {
285
        if (null === $environment->getQueryType()) {
286
            return null;
287
        }
288
289
        $queryParser = $this->factory->createQueryParametersParser();
290
        if ($queryParser instanceof QueryParametersParserInterface) {
291
            $queryParser
292
                ->setDataParser($this->parser)
293
                ->setQueryType($environment->getQueryType());
294
        }
295
296
        return $queryParser->parse($this->createPsr7Request($request));
297
    }
298
299
    /**
300
     * Parse request body
301
     *
302
     * @param Request $request
303
     * @param EnvironmentInterface $environment
304
     * @return mixed|null
305
     */
306
    private function parseBody(Request $request, EnvironmentInterface $environment)
307
    {
308
        if (null === $environment->getBodyType()) {
309
            return null;
310
        }
311
312
        $decoder = $environment->getDecoder();
313
        if ($decoder instanceof DecoderInterface) {
314
            $decoder->setContentType($environment->getBodyType());
315
        }
316
317
        return $decoder->decode($request->getContent());
318
    }
319
320
    /**
321
     * Validate specified data
322
     *
323
     * @param mixed $data
324
     * @param array $validationGroups
325
     * @return Error[]
326
     */
327
    private function validateData($data = null, array $validationGroups = [])
328
    {
329
        return (null !== $data) ? $this->validator->validate($data, $validationGroups) : [];
330
    }
331
332
    /**
333
     * Register specified decoders
334
     *
335
     * @param array $decoders
336
     * @param CodecMatcherInterface $matcher
337
     */
338
    private function registerDecoders(array $decoders, CodecMatcherInterface $matcher)
339
    {
340
        foreach ($decoders as $mediaType => $decoderType) {
341
            $matcher->registerDecoder(
342
                $this->parseMediaTypeString($mediaType),
343
                $this->registry->getDecoder($decoderType)
344
            );
345
        }
346
    }
347
348
    /**
349
     * Register specified encoders
350
     *
351
     * @param array $encoders
352
     * @param CodecMatcherInterface $matcher
353
     */
354
    private function registerEncoders(array $encoders, CodecMatcherInterface $matcher)
355
    {
356
        foreach ($encoders as $mediaType => $encoderType) {
357
            $matcher->registerEncoder(
358
                $this->parseMediaTypeString($mediaType),
359
                $this->registry->getEncoder($encoderType)
360
            );
361
        }
362
    }
363
}
364