Passed
Push — master ( efaea1...0ff05c )
by Alex
15:10
created

UriProcessorNew::executeGetLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace POData\UriProcessor;
4
5
use POData\Common\HttpStatus;
6
use POData\Common\Messages;
7
use POData\Common\ODataConstants;
8
use POData\Common\ODataException;
9
use POData\IService;
10
use POData\ObjectModel\CynicDeserialiser;
11
use POData\ObjectModel\ModelDeserialiser;
12
use POData\ObjectModel\ODataEntry;
13
use POData\ObjectModel\ODataURL;
14
use POData\OperationContext\HTTPRequestMethod;
15
use POData\Providers\Metadata\ResourcePropertyKind;
16
use POData\Providers\Metadata\ResourceSet;
17
use POData\Providers\Metadata\ResourceSetWrapper;
18
use POData\Providers\ProvidersWrapper;
19
use POData\Providers\Query\QueryResult;
20
use POData\Providers\Query\QueryType;
21
use POData\UriProcessor\Interfaces\IUriProcessor;
22
use POData\UriProcessor\QueryProcessor\QueryProcessor;
23
use POData\UriProcessor\ResourcePathProcessor\ResourcePathProcessor;
24
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor;
25
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\SegmentDescriptor;
26
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\SegmentParser;
27
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetKind;
28
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetSource;
29
30
/**
31
 * Class UriProcessorNew.
32
 *
33
 * A type to process client's requested URI
34
 * The syntax of request URI is:
35
 *  Scheme Host Port ServiceRoot ResourcePath ? QueryOption
36
 * For more details refer:
37
 * http://www.odata.org/developers/protocols/uri-conventions#UriComponents
38
 */
39
class UriProcessorNew implements IUriProcessor
40
{
41
    /**
42
     * Description of the OData request that a client has submitted.
43
     *
44
     * @var RequestDescription
45
     */
46
    private $request;
47
48
    /**
49
     * Holds reference to the data service instance.
50
     *
51
     * @var IService
52
     */
53
    private $service;
54
55
    /**
56
     * Holds reference to the wrapper over IDSMP and IDSQP implementation.
57
     *
58
     * @var ProvidersWrapper
59
     */
60
    private $providers;
61
62
    /**
63
     * Holds reference to request expander.
64
     *
65
     * @var RequestExpander
66
     */
67
    private $expander;
68
69
    /**
70
     * @var ModelDeserialiser
71
     */
72
    private $cereal;
73
74
    /**
75
     * @var CynicDeserialiser
76
     */
77
    private $cynicDeserialiser;
78
79
    /**
80
     * Constructs a new instance of UriProcessor.
81
     *
82
     * @param IService $service Reference to the data service instance
83
     */
84
    private function __construct(IService $service)
85
    {
86
        $this->service = $service;
87
        $this->providers = $service->getProvidersWrapper();
88
        $this->request = ResourcePathProcessor::process($service);
89
        $this->expander = new RequestExpander(
90
            $this->getRequest(),
91
            $this->getService(),
92
            $this->getProviders()
93
        );
94
        $this->getRequest()->setUriProcessor($this);
95
        $this->cereal = new ModelDeserialiser();
96
        $this->cynicDeserialiser = new CynicDeserialiser(
97
            $service->getMetadataProvider(),
98
            $service->getProvidersWrapper()
99
        );
100
    }
101
102
    /**
103
     * Process the resource path and query options of client's request uri.
104
     *
105
     * @param IService $service Reference to the data service instance
106
     *
107
     * @throws ODataException
108
     *
109
     * @return IUriProcessor
110
     */
111
    public static function process(IService $service)
112
    {
113
        $absRequestUri = $service->getHost()->getAbsoluteRequestUri();
114
        $absServiceUri = $service->getHost()->getAbsoluteServiceUri();
115
116
        if (!$absServiceUri->isBaseOf($absRequestUri)) {
117
            throw ODataException::createInternalServerError(
118
                Messages::uriProcessorRequestUriDoesNotHaveTheRightBaseUri(
119
                    $absRequestUri->getUrlAsString(),
120
                    $absServiceUri->getUrlAsString()
121
                )
122
            );
123
        }
124
125
        $processor = new self($service);
126
        //Parse the query string options of the request Uri.
127
        QueryProcessor::process($processor->request, $service);
128
        return $processor;
129
    }
130
131
    /**
132
     * Gets reference to the request submitted by client.
133
     *
134
     * @return RequestDescription
135
     */
136
    public function getRequest()
137
    {
138
        return $this->request;
139
    }
140
141
    /**
142
     * Gets reference to the request submitted by client.
143
     *
144
     * @return ProvidersWrapper
145
     */
146
    public function getProviders()
147
    {
148
        return $this->providers;
149
    }
150
151
    /**
152
     * Gets the data service instance.
153
     *
154
     * @return IService
155
     */
156
    public function getService()
157
    {
158
        return $this->service;
159
    }
160
161
    /**
162
     * Gets the request expander instance.
163
     *
164
     * @return RequestExpander
165
     */
166
    public function getExpander()
167
    {
168
        return $this->expander;
169
    }
170
171
    /**
172
     * @return ModelDeserialiser
173
     */
174
    public function getModelDeserialiser()
175
    {
176
        return $this->cereal;
177
    }
178
179
    public function getCynicDeserialiser()
180
    {
181
        return $this->cynicDeserialiser;
182
    }
183
184
    /**
185
     * Execute the client submitted request against the data source.
186
     */
187
    public function execute()
188
    {
189
        $service = $this->getService();
190
        assert($service instanceof IService, '!($service instanceof IService)');
191
        $context = $service->getOperationContext();
192
        $method = $context->incomingRequest()->getMethod();
193
194
        switch ($method) {
195
            case HTTPRequestMethod::GET():
196
                $this->executeGet();
197
                break;
198
            case HTTPRequestMethod::DELETE():
199
                $this->executeGet();
200
                $this->executeDelete();
201
                break;
202
            case HTTPRequestMethod::PUT():
203
                $this->executeGet();
204
                $this->executePut();
205
                break;
206
            case HTTPRequestMethod::POST():
207
                $this->executePost();
208
                break;
209
            default:
210
                throw ODataException::createNotImplementedError(Messages::onlyReadSupport($method));
211
        }
212
213
        // Apply $select and $expand options to result set, this function will be always applied
214
        // irrespective of return value of IDSQP2::canApplyQueryOptions which means library will
215
        // not delegate $expand/$select operation to IDSQP2 implementation
216
        $this->getExpander()->handleExpansion();
217
    }
218
219
    /**
220
     * Execute the client submitted request against the data source (GET).
221
     */
222
    protected function executeGet()
223
    {
224
        $segments = $this->getRequest()->getSegments();
225
        $root = $this->getRequest()->getRootProjectionNode();
226
        $eagerLoad = (null === $root) ? [] : $root->getEagerLoadList();
227
228
        foreach ($segments as $segment) {
229
            $requestTargetKind = $segment->getTargetKind();
230
231
            switch ($requestTargetKind) {
232
                case TargetKind::SINGLETON():
233
                    $this->executeGetSingleton($segment);
234
                    break;
235
                case TargetKind::RESOURCE():
236
                    if (TargetSource::ENTITY_SET == $segment->getTargetSource()) {
237
                        $this->handleSegmentTargetsToResourceSet($segment);
238
                    } else {
239
                        $this->executeGetResource($segment, $eagerLoad);
240
                    }
241
                    break;
242
                case TargetKind::MEDIA_RESOURCE():
243
                    $this->checkResourceExistsByIdentifier($segment);
244
                    $segment->setResult($segment->getPrevious()->getResult());
245
                    // a media resource means we're done - bail out of segment processing
246
                    break 2;
247
                case TargetKind::LINK():
248
                    $this->executeGetLink($segment);
249
                    break;
250
                case TargetKind::PRIMITIVE_VALUE():
251
                    $previous = $segment->getPrevious();
252
                    if (null !== $previous && TargetKind::RESOURCE() == $previous->getTargetKind()) {
253
                        $result = $previous->getResult();
254
                        if ($result instanceof QueryResult) {
255
                            $raw = null !== $result->count ? $result->count : count($result->results);
256
                            $segment->setResult($raw);
257
                        }
258
                    }
259
                    break;
260
                case TargetKind::PRIMITIVE():
261
                case TargetKind::COMPLEX_OBJECT():
262
                case TargetKind::BAG():
263
                    break;
264
                default:
265
                    assert(false, 'Not implemented yet');
266
            }
267
268
            if (null === $segment->getNext()
269
                || ODataConstants::URI_COUNT_SEGMENT == $segment->getNext()->getIdentifier()
270
            ) {
271
                $this->applyQueryOptions($segment);
272
            }
273
        }
274
    }
275
276
    /**
277
     * Execute the client submitted request against the data source (DELETE).
278
     */
279
    protected function executeDelete()
280
    {
281
        $segment = $this->getFinalEffectiveSegment();
282
        $requestMethod = $this->getService()->getOperationContext()->incomingRequest()->getMethod();
283
        $resourceSet = $segment->getTargetResourceSetWrapper();
284
        $keyDescriptor = $segment->getKeyDescriptor();
285
286
        $this->checkUriValidForSuppliedVerb($resourceSet, $keyDescriptor, $requestMethod);
287
        assert($resourceSet instanceof ResourceSet);
0 ignored issues
show
Bug introduced by
The call to assert() has too few arguments starting with description. ( Ignorable by Annotation )

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

287
        /** @scrutinizer ignore-call */ 
288
        assert($resourceSet instanceof ResourceSet);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
288
        $this->getProviders()->deleteResource($resourceSet, $segment->getResult());
289
        $this->getService()->getHost()->setResponseStatusCode(HttpStatus::CODE_NOCONTENT);
290
    }
291
292
    /**
293
     * Execute the client submitted request against the data source (PUT).
294
     */
295
    protected function executePut()
296
    {
297
        $segment = $this->getFinalEffectiveSegment();
298
        $requestMethod = $this->getService()->getOperationContext()->incomingRequest()->getMethod();
299
        $resourceSet = null !== $segment ? $segment->getTargetResourceSetWrapper() : null;
300
        $keyDescriptor = null !== $segment ? $segment->getKeyDescriptor() : null;
301
302
        $this->checkUriValidForSuppliedVerb($resourceSet, $keyDescriptor, $requestMethod);
303
        assert($resourceSet instanceof ResourceSet);
0 ignored issues
show
Bug introduced by
The call to assert() has too few arguments starting with description. ( Ignorable by Annotation )

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

303
        /** @scrutinizer ignore-call */ 
304
        assert($resourceSet instanceof ResourceSet);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
304
        assert($keyDescriptor instanceof KeyDescriptor);
305
306
        $payload = $this->getRequest()->getData();
307
        assert($payload instanceof ODataEntry, get_class($payload));
0 ignored issues
show
Bug introduced by
It seems like $payload can also be of type array; however, parameter $object of get_class() does only seem to accept object, 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

307
        assert($payload instanceof ODataEntry, get_class(/** @scrutinizer ignore-type */ $payload));
Loading history...
308
        assert(!empty($payload->id), 'Payload ID must not be empty for PUT request');
309
        $data = $this->getModelDeserialiser()->bulkDeserialise($resourceSet->getResourceType(), $payload);
310
311
        if (empty($data)) {
312
            throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
313
        }
314
315
        $queryResult = $this->getCynicDeserialiser()->processPayload($payload);
316
        $segment->setResult($queryResult);
317
    }
318
319
    /**
320
     * Execute the client submitted request against the data source (POST).
321
     */
322
    protected function executePost()
323
    {
324
        $segments = $this->getRequest()->getSegments();
325
        $requestMethod = $this->getService()->getOperationContext()->incomingRequest()->getMethod();
326
327
        foreach ($segments as $segment) {
328
            $requestTargetKind = $segment->getTargetKind();
329
            if ($requestTargetKind == TargetKind::RESOURCE()) {
330
                $resourceSet = $segment->getTargetResourceSetWrapper();
331
                if (!$resourceSet) {
332
                    $url = $this->getService()->getHost()->getAbsoluteRequestUri()->getUrlAsString();
333
                    $msg = Messages::badRequestInvalidUriForThisVerb($url, $requestMethod);
334
                    throw ODataException::createBadRequestError($msg);
335
                }
336
337
                $payload = $this->getRequest()->getData();
338
                if ($payload instanceof ODataURL) {
339
                    $this->executeGet();
340
                    $masterModel = $this->getRequest()->getSegments()[0]->getResult();
341
                    $masterResourceSet = $this->getRequest()->getSegments()[0]->getTargetResourceSetWrapper();
342
                    $masterNavProperty = $this->getRequest()->getLastSegment()->getIdentifier();
343
                    $slaveModelUri = new \POData\Common\Url($payload->url);
344
                    $host = $this->service->getHost();
345
                    $absoluteServiceUri = $host->getAbsoluteServiceUri();
346
                    $requestUriSegments = array_slice(
347
                        $slaveModelUri->getSegments(),
348
                        $absoluteServiceUri->getSegmentCount()
349
                    );
350
                    $newSegments = SegmentParser::parseRequestUriSegments(
351
                        $requestUriSegments,
352
                        $this->service->getProvidersWrapper(),
353
                        true
354
                    );
355
                    $this->executeGetResource($newSegments[0]);
356
                    $slaveModel = $newSegments[0]->getResult();
357
                    $slaveResourceSet = $newSegments[0]->getTargetResourceSetWrapper();
358
                    $linkAdded = $this->getProviders()
359
                        ->hookSingleModel(
360
                            $masterResourceSet,
361
                            $masterModel,
362
                            $slaveResourceSet,
363
                            $slaveModel,
364
                            $masterNavProperty
365
                        );
366
                    if ($linkAdded) {
367
                        $this->getService()->getHost()->setResponseStatusCode(HttpStatus::CODE_NOCONTENT);
368
                    } else {
369
                        throw ODataException::createInternalServerError('AdapterIndicatedLinkNotAttached');
370
                    }
371
                    foreach ($segments as $segment) {
372
                        $segment->setResult(null);
373
                    }
374
                    return;
375
                }
376
                assert($payload instanceof ODataEntry, get_class($payload));
377
                assert(empty($payload->id), 'Payload ID must be empty for POST request');
378
                $data = $this->getModelDeserialiser()->bulkDeserialise($resourceSet->getResourceType(), $payload);
379
380
                if (empty($data)) {
381
                    throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
382
                }
383
                $this->getService()->getHost()->setResponseStatusCode(HttpStatus::CODE_CREATED);
384
                $queryResult = $this->getCynicDeserialiser()->processPayload($payload);
385
                $keyID = $payload->id;
386
                assert($keyID instanceof KeyDescriptor, get_class($keyID));
387
                $locationUrl = $keyID->generateRelativeUri($resourceSet->getResourceSet());
388
                $absoluteServiceUri = $this->getService()->getHost()->getAbsoluteServiceUri()->getUrlAsString();
389
                $location = rtrim($absoluteServiceUri, '/') . '/' . $locationUrl;
390
                $this->getService()->getHost()->setResponseLocation($location);
391
                $segment->setResult($queryResult);
392
            }
393
        }
394
    }
395
396
    /**
397
     * @return null|SegmentDescriptor
398
     */
399
    protected function getFinalEffectiveSegment()
400
    {
401
        $segment = $this->getRequest()->getLastSegment();
402
        // if last segment is $count, back up one
403
        if (null !== $segment && ODataConstants::URI_COUNT_SEGMENT == $segment->getIdentifier()) {
404
            $segment = $segment->getPrevious();
405
            return $segment;
406
        }
407
        return $segment;
408
    }
409
410
    /**
411
     * @param $resourceSet
412
     * @param $keyDescriptor
413
     * @param $requestMethod
414
     * @throws ODataException
415
     */
416
    protected function checkUriValidForSuppliedVerb($resourceSet, $keyDescriptor, $requestMethod)
417
    {
418
        if (!$resourceSet || !$keyDescriptor) {
419
            $url = $this->getService()->getHost()->getAbsoluteRequestUri()->getUrlAsString();
420
            throw ODataException::createBadRequestError(
421
                Messages::badRequestInvalidUriForThisVerb($url, $requestMethod)
422
            );
423
        }
424
    }
425
426
    /**
427
     * @param $segment
428
     */
429
    private function executeGetSingleton($segment)
430
    {
431
        $segmentId = $segment->getIdentifier();
432
        $singleton = $this->getService()->getProvidersWrapper()->resolveSingleton($segmentId);
433
        $segment->setResult($singleton->get());
434
    }
435
436
    /**
437
     * @param $segment
438
     */
439
    private function executeGetResource($segment, array $eagerList = [])
440
    {
441
        foreach ($eagerList as $eager) {
442
            assert(is_string($eager) && 0 < strlen($eager), 'Eager-load list elements must be non-empty strings');
443
        }
444
        $isRelated = TargetSource::ENTITY_SET == $segment->getTargetSource();
445
        if ($isRelated) {
446
            $queryResult = $this->executeGetResourceDirect($segment, $eagerList);
447
        } else {
448
            $queryResult = $this->executeGetResourceRelated($segment, $eagerList);
449
        }
450
        $segment->setResult($queryResult);
451
    }
452
453
    /**
454
     * @param $segment
455
     */
456
    private function executeGetLink($segment)
457
    {
458
        $previous = $segment->getPrevious();
459
        assert(isset($previous));
0 ignored issues
show
Bug introduced by
The call to assert() has too few arguments starting with description. ( Ignorable by Annotation )

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

459
        /** @scrutinizer ignore-call */ 
460
        assert(isset($previous));

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
460
        $segment->setResult($previous->getResult());
461
    }
462
463
    /**
464
     * @param $segment
465
     * @return null|object|QueryResult
466
     */
467
    private function executeGetResourceDirect($segment, array $eagerList)
468
    {
469
        if ($segment->isSingleResult()) {
470
            $queryResult = $this->getProviders()->getResourceFromResourceSet(
471
                $segment->getTargetResourceSetWrapper(),
472
                $segment->getKeyDescriptor(),
473
                $eagerList
474
            );
475
        } else {
476
            $skip = $this->getRequest()->getSkipCount();
477
            $skip = (null === $skip) ? 0 : $skip;
478
            $skipToken = $this->getRequest()->getInternalSkipTokenInfo();
479
            $skipToken = (null != $skipToken) ? $skipToken->getSkipTokenInfo() : null;
480
            $queryResult = $this->getProviders()->getResourceSet(
481
                $this->getRequest()->queryType,
482
                $segment->getTargetResourceSetWrapper(),
483
                $this->getRequest()->getFilterInfo(),
484
                $this->getRequest()->getInternalOrderByInfo(),
485
                $this->getRequest()->getTopCount(),
486
                $skip,
487
                $skipToken,
488
                $eagerList
489
            );
490
        }
491
        return $queryResult;
492
    }
493
494
    /**
495
     * @param $segment
496
     * @return null|object|QueryResult
497
     */
498
    private function executeGetResourceRelated($segment, $eagerList)
0 ignored issues
show
Unused Code introduced by
The parameter $eagerList is not used and could be removed. ( Ignorable by Annotation )

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

498
    private function executeGetResourceRelated($segment, /** @scrutinizer ignore-unused */ $eagerList)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
499
    {
500
        $projectedProperty = $segment->getProjectedProperty();
501
        $projectedPropertyKind = null !== $projectedProperty ? $projectedProperty->getKind() : 0;
502
        $queryResult = null;
503
        switch ($projectedPropertyKind) {
504
            case ResourcePropertyKind::RESOURCE_REFERENCE:
505
                $queryResult = $this->getProviders()->getRelatedResourceReference(
506
                    $segment->getPrevious()->getTargetResourceSetWrapper(),
507
                    $segment->getPrevious()->getResult(),
508
                    $segment->getTargetResourceSetWrapper(),
509
                    $projectedProperty
510
                );
511
                break;
512
            case ResourcePropertyKind::RESOURCESET_REFERENCE:
513
                if ($segment->isSingleResult()) {
514
                    $queryResult = $this->getProviders()->getResourceFromRelatedResourceSet(
515
                        $segment->getPrevious()->getTargetResourceSetWrapper(),
516
                        $segment->getPrevious()->getResult(),
517
                        $segment->getTargetResourceSetWrapper(),
518
                        $projectedProperty,
519
                        $segment->getKeyDescriptor()
520
                    );
521
                } else {
522
                    $skipToken = $this->getRequest()->getInternalSkipTokenInfo();
523
                    $skipToken = (null !== $skipToken) ? $skipToken->getSkipTokenInfo() : null;
524
                    $queryResult = $this->getProviders()->getRelatedResourceSet(
525
                        $this->getRequest()->queryType,
526
                        $segment->getPrevious()->getTargetResourceSetWrapper(),
527
                        $segment->getPrevious()->getResult(),
528
                        $segment->getTargetResourceSetWrapper(),
529
                        $projectedProperty,
530
                        $this->getRequest()->getFilterInfo(),
531
                        null, // $orderby
532
                        null, // $top
533
                        null, // $skip
534
                        $skipToken
535
                    );
536
                }
537
                break;
538
            default:
539
                $this->checkResourceExistsByIdentifier($segment);
540
                assert(false, 'Invalid property kind type for resource retrieval');
541
        }
542
        return $queryResult;
543
    }
544
545
    /**
546
     * Query for a resource set pointed by the given segment descriptor and update the descriptor with the result.
547
     *
548
     * @param SegmentDescriptor $segment Describes the resource set to query
549
     */
550
    private function handleSegmentTargetsToResourceSet(SegmentDescriptor $segment)
551
    {
552
        if ($segment->isSingleResult()) {
553
            $entityInstance = $this->getProviders()->getResourceFromResourceSet(
554
                $segment->getTargetResourceSetWrapper(),
555
                $segment->getKeyDescriptor()
556
            );
557
558
            $segment->setResult($entityInstance);
559
        } else {
560
            $skip = (null == $this->getRequest()) ? 0 : $this->getRequest()->getSkipCount();
561
            $skip = (null === $skip) ? 0 : $skip;
562
            $skipToken = $this->getRequest()->getInternalSkipTokenInfo();
563
            $skipToken = (null != $skipToken) ? $skipToken->getSkipTokenInfo() : null;
564
            $queryResult = $this->getProviders()->getResourceSet(
565
                $this->getRequest()->queryType,
566
                $segment->getTargetResourceSetWrapper(),
567
                $this->getRequest()->getFilterInfo(),
568
                $this->getRequest()->getInternalOrderByInfo(),
569
                $this->getRequest()->getTopCount(),
570
                $skip,
571
                $skipToken
572
            );
573
            $segment->setResult($queryResult);
574
        }
575
    }
576
577
    /**
578
     * @param $segment
579
     * @throws ODataException
580
     */
581
    private function checkResourceExistsByIdentifier($segment)
582
    {
583
        if (null === $segment->getPrevious()->getResult()) {
584
            throw ODataException::createResourceNotFoundError(
585
                $segment->getPrevious()->getIdentifier()
586
            );
587
        }
588
    }
589
590
    /**
591
     * Applies the query options to the resource(s) retrieved from the data source.
592
     *
593
     * @param SegmentDescriptor $segment The descriptor which holds resource(s) on which query options to be applied
594
     */
595
    private function applyQueryOptions(SegmentDescriptor $segment)
596
    {
597
        $result = $segment->getResult();
598
        if (!$result instanceof QueryResult) {
599
            //If the segment isn't a query result, then there's no paging or counting to be done
600
            return;
601
        }
602
        // Note $inlinecount=allpages means include the total count regardless of paging..so we set the counts first
603
        // regardless if POData does the paging or not.
604
        if ($this->getRequest()->queryType == QueryType::ENTITIES_WITH_COUNT()) {
605
            if ($this->getProviders()->handlesOrderedPaging()) {
606
                $this->getRequest()->setCountValue($result->count);
607
            } else {
608
                $this->getRequest()->setCountValue(count($result->results));
609
            }
610
        }
611
        //Have POData perform paging if necessary
612
        if (!$this->getProviders()->handlesOrderedPaging() && !empty($result->results)) {
613
            $result->results = $this->performPaging($result->results);
0 ignored issues
show
Bug introduced by
It seems like $result->results can also be of type object; however, parameter $result of POData\UriProcessor\UriP...sorNew::performPaging() does only seem to accept array, 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

613
            $result->results = $this->performPaging(/** @scrutinizer ignore-type */ $result->results);
Loading history...
614
        }
615
        //a bit surprising, but $skip and $top affects $count so update it here, not above
616
        //IE  data.svc/Collection/$count?$top=10 returns 10 even if Collection has 11+ entries
617
        if ($this->getRequest()->queryType == QueryType::COUNT()) {
618
            if ($this->getProviders()->handlesOrderedPaging()) {
619
                $this->getRequest()->setCountValue($result->count);
620
            } else {
621
                $this->getRequest()->setCountValue(count($result->results));
622
            }
623
        }
624
        $segment->setResult($result);
625
    }
626
627
    /**
628
     * If the provider does not perform the paging (ordering, top, skip) then this method does it.
629
     *
630
     * @param array $result
631
     *
632
     * @return array
633
     */
634
    private function performPaging(array $result)
635
    {
636
        //Apply (implicit and explicit) $orderby option
637
        $internalOrderByInfo = $this->getRequest()->getInternalOrderByInfo();
638
        if (null !== $internalOrderByInfo) {
639
            $orderByFunction = $internalOrderByInfo->getSorterFunction();
640
            usort($result, $orderByFunction);
641
        }
642
        //Apply $skiptoken option
643
        $internalSkipTokenInfo = $this->getRequest()->getInternalSkipTokenInfo();
644
        if (null !== $internalSkipTokenInfo) {
645
            $matchingIndex = $internalSkipTokenInfo->getIndexOfFirstEntryInTheNextPage($result);
646
            $result = array_slice($result, $matchingIndex);
647
        }
648
        //Apply $top and $skip option
649
        if (!empty($result)) {
650
            $top = $this->getRequest()->getTopCount();
651
            $skip = $this->getRequest()->getSkipCount();
652
            if (null === $skip) {
653
                $skip = 0;
654
            }
655
            $result = array_slice($result, $skip, $top);
656
        }
657
        return $result;
658
    }
659
}
660