Completed
Push — master ( c7e8d1...934444 )
by Bálint
02:50
created

UriProcessor::execute()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 19
nc 7
nop 0
1
<?php
2
3
namespace POData\UriProcessor;
4
5
use POData\Providers\ProvidersWrapper;
6
use POData\Providers\Metadata\ResourcePropertyKind;
7
use POData\Providers\Metadata\ResourceTypeKind;
8
use POData\Providers\Metadata\ResourceSetWrapper;
9
use POData\Providers\Metadata\ResourceProperty;
10
use POData\Providers\Query\QueryType;
11
use POData\UriProcessor\QueryProcessor\QueryProcessor;
12
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\ExpandedProjectionNode;
13
use POData\UriProcessor\ResourcePathProcessor\ResourcePathProcessor;
14
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\SegmentDescriptor;
15
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetKind;
16
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetSource;
17
use POData\IService;
18
use POData\Common\Url;
19
use POData\Common\Messages;
20
use POData\Common\ODataException;
21
use POData\Common\InvalidOperationException;
22
use POData\Common\ODataConstants;
23
use POData\Providers\Query\QueryResult;
24
use POData\OperationContext\HTTPRequestMethod;
25
26
/**
27
 * Class UriProcessor
28
 *
29
 * A type to process client's requets URI
30
 * The syntax of request URI is:
31
 *  Scheme Host Port ServiceRoot ResourcePath ? QueryOption
32
 * For more details refer:
33
 * http://www.odata.org/developers/protocols/uri-conventions#UriComponents
34
 *
35
 * @package POData\UriProcessor
36
 */
37
class UriProcessor
38
{
39
    /**
40
     * Description of the OData request that a client has submitted.
41
     *
42
     * @var RequestDescription
43
     */
44
    private $request;
45
46
    /**
47
     * Holds reference to the data service instance.
48
     *
49
     * @var IService
50
     */
51
    private $service;
52
53
    /**
54
     * Holds reference to the wrapper over IDSMP and IDSQP implementation.
55
     *
56
     * @var ProvidersWrapper
57
     */
58
    private $providers;
59
60
    /**
61
     * Collection of segment names.
62
     *
63
     * @var string[]
64
     */
65
    private $_segmentNames;
66
67
    /**
68
     * Collection of segment ResourceSetWrapper instances.
69
     *
70
     * @var ResourceSetWrapper[]
71
     */
72
    private $_segmentResourceSetWrappers;
73
74
    /**
75
     * Constructs a new instance of UriProcessor
76
     *
77
     * @param IService $service Reference to the data service instance.
78
     */
79
    private function __construct(IService $service)
80
    {
81
        $this->service = $service;
82
        $this->providers = $service->getProvidersWrapper();
83
        $this->_segmentNames = array();
84
        $this->_segmentResourceSetWrappers = array();
85
    }
86
87
    /**
88
     * Process the resource path and query options of client's request uri.
89
     *
90
     * @param IService $service Reference to the data service instance.
91
     *
92
     * @return URIProcessor
93
     *
94
     * @throws ODataException
95
     */
96
    public static function process(IService $service)
97
    {
98
        $absoluteRequestUri = $service->getHost()->getAbsoluteRequestUri();
99
        $absoluteServiceUri = $service->getHost()->getAbsoluteServiceUri();
100
101
        if (!$absoluteServiceUri->isBaseOf($absoluteRequestUri)) {
102
			throw ODataException::createInternalServerError(
103
                Messages::uriProcessorRequestUriDoesNotHaveTheRightBaseUri(
104
                    $absoluteRequestUri->getUrlAsString(),
105
                    $absoluteServiceUri->getUrlAsString()
106
                )
107
            );
108
        }
109
110
        $uriProcessor = new UriProcessor($service);
111
        //Parse the resource path part of the request Uri.
112
		$uriProcessor->request = ResourcePathProcessor::process($service);
113
114
	    $uriProcessor->request->setUriProcessor($uriProcessor);
115
116
        //Parse the query string options of the request Uri.
117
        QueryProcessor::process( $uriProcessor->request, $service );
118
119
        return $uriProcessor;
120
    }
121
122
    /**
123
     * Gets reference to the request submitted by client.
124
     *
125
     * @return RequestDescription
126
     */
127
    public function getRequest()
128
    {
129
        return $this->request;
130
    }
131
132
    /**
133
     * Execute the client submitted request against the data source.
134
     */
135
    public function execute()
136
    {
137
        $operationContext = $this->service->getOperationContext();
138
        if (!$operationContext) {
139
            $this->executeBase();
140
            return;
141
        }
142
143
        $requestMethod = $operationContext->incomingRequest()->getMethod();
144
        if ($requestMethod == HTTPRequestMethod::GET) {
145
            $this->executeGet();
146
        }
147
        elseif ($requestMethod == HTTPRequestMethod::PUT) {
148
            $this->executePut();
149
        }
150
        elseif ($requestMethod == HTTPRequestMethod::POST) {
151
            if ($this->request->getLastSegment()->getTargetKind() == TargetKind::BATCH()) {
152
                $this->executeBatch();
153
            } else {
154
                $this->executePost();
155
            }
156
        }
157
        elseif ($requestMethod == HTTPRequestMethod::DELETE) {
158
            $this->executeDelete();
159
        }
160
        else {
161
            throw ODataException::createNotImplementedError(Messages::unsupportedMethod($requestMethod));
162
        }
163
    }
164
165
    /**
166
     * Execute the client submitted request against the data source (GET)
167
     */
168
    protected function executeGet()
169
    {
170
        return $this->executeBase();
171
    }
172
173
    /**
174
     * Execute the client submitted request against the data source (PUT)
175
     */
176 View Code Duplication
    protected function executePut()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
177
    {
178
        return $this->executeBase(function ($uriProcessor, $segment) {
179
            $requestMethod = $uriProcessor->service->getOperationContext()->incomingRequest()->getMethod();
180
            $resourceSet = $segment->getTargetResourceSetWrapper();
181
            $keyDescriptor = $segment->getKeyDescriptor();
182
            $data = $uriProcessor->request->getData();
183
184
            if (!$resourceSet || !$keyDescriptor) {
185
                $url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
186
                throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
187
            }
188
189
            if (!$data) {
190
                throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
191
            }
192
193
            return $uriProcessor->providers->putResource($resourceSet, $keyDescriptor, $data);
194
        });
195
    }
196
197
    /**
198
     * Execute the client submitted request against the data source (POST)
199
     */
200 View Code Duplication
    protected function executePost()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201
    {
202
        return $this->executeBase(function ($uriProcessor, $segment) {
203
            $requestMethod = $uriProcessor->service->getOperationContext()->incomingRequest()->getMethod();
204
            $resourceSet = $segment->getTargetResourceSetWrapper();
205
            $data = $uriProcessor->request->getData();
206
207
            if (!$resourceSet) {
208
                $url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
209
                throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
210
            }
211
212
            if (!$data) {
213
                throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
214
            }
215
216
            $entity = $uriProcessor->providers->postResource($resourceSet, $data);
217
218
            $segment->setSingleResult(true);
219
            $segment->setResult($entity);
220
221
            return $entity;
222
        });
223
    }
224
225
    /**
226
     * Execute the client submitted request against the data source (DELETE)
227
     */
228
    protected function executeDelete()
229
    {
230
        return $this->executeBase(function ($uriProcessor, $segment) {
231
            $requestMethod = $uriProcessor->service->getOperationContext()->incomingRequest()->getMethod();
232
            $resourceSet = $segment->getTargetResourceSetWrapper();
233
            $keyDescriptor = $segment->getKeyDescriptor();
234
235
            if (!$resourceSet || !$keyDescriptor) {
236
                $url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
237
                throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
238
            }
239
240
            return $uriProcessor->providers->deleteResource($resourceSet, $keyDescriptor);
241
        });
242
    }
243
244
    /**
245
     * Execute the client submitted batch request against the data source (POST)
246
     */
247
    protected function executeBatch()
248
    {
249
        $callback = null;
250 View Code Duplication
        $post_callback = function ($uriProcessor, $segment) {
0 ignored issues
show
Unused Code introduced by
$post_callback is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
251
            $requestMethod = $uriProcessor->service->getOperationContext()->incomingRequest()->getMethod();
252
            $resourceSet = $segment->getTargetResourceSetWrapper();
253
            $data = $uriProcessor->request->getData();
254
255
            if (!$resourceSet) {
256
                $url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
257
                throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
258
            }
259
260
            if (!$data) {
261
                throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
262
            }
263
264
            $entity = $uriProcessor->providers->postResource($resourceSet, $data);
265
266
            $segment->setSingleResult(true);
267
            $segment->setResult($entity);
268
269
            return $entity;
270
        };
271
272
        foreach ($this->request->getParts() as $request) {
273
274
            $segments = $request->getSegments();
275
276 View Code Duplication
            foreach ($segments as $segment) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
277
278
                $requestTargetKind = $segment->getTargetKind();
279
280
                if ($segment->getTargetSource() == TargetSource::ENTITY_SET) {
281
                    $this->handleSegmentTargetsToResourceSet($segment);
282
                } else if ($requestTargetKind == TargetKind::RESOURCE()) {
283
                    if (is_null($segment->getPrevious()->getResult())) {
284
                        throw ODataException::createResourceNotFoundError(
285
                            $segment->getPrevious()->getIdentifier()
286
                        );
287
                    }
288
                    $this->_handleSegmentTargetsToRelatedResource($segment);
289
                } else if ($requestTargetKind == TargetKind::LINK()) {
290
                    $segment->setResult($segment->getPrevious()->getResult());
291
                } else if ($segment->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) {
292
                    // we are done, $count will the last segment and
293
                    // taken care by _applyQueryOptions method
294
                    $segment->setResult($this->request->getCountValue());
295
                    break;
296
                } else {
297
                    if ($requestTargetKind == TargetKind::MEDIA_RESOURCE()) {
298
                        if (is_null($segment->getPrevious()->getResult())) {
299
                            throw ODataException::createResourceNotFoundError(
300
                                $segment->getPrevious()->getIdentifier()
301
                            );
302
                        }
303
                        // For MLE and Named Stream the result of last segment
304
                        // should be that of previous segment, this is required
305
                        // while retrieving content type or stream from IDSSP
306
                        $segment->setResult($segment->getPrevious()->getResult());
307
                        // we are done, as named stream property or $value on
308
                        // media resource will be the last segment
309
                        break;
310
                    }
311
312
                    $value = $segment->getPrevious()->getResult();
313
                    while (!is_null($segment)) {
314
                        //TODO: what exactly is this doing here?  Once a null's found it seems everything will be null
315
                        if (!is_null($value)) {
316
                            $value = null;
317
                        } else {
318
                            try {
319
                                //see #88
320
                                $property = new \ReflectionProperty($value, $segment->getIdentifier());
321
                                $value = $property->getValue($value);
322
                            } catch (\ReflectionException $reflectionException) {
323
                                //throw ODataException::createInternalServerError(Messages::orderByParserFailedToAccessOrInitializeProperty($resourceProperty->getName(), $resourceType->getName()));
0 ignored issues
show
Unused Code Comprehensibility introduced by
68% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
324
                            }
325
                        }
326
327
                        $segment->setResult($value);
328
                        $segment = $segment->getNext();
329
                        if (!is_null($segment) && $segment->getIdentifier() == ODataConstants::URI_VALUE_SEGMENT) {
330
                            $segment->setResult($value);
331
                            $segment = $segment->getNext();
332
                        }
333
                    }
334
335
                    break;
336
337
                }
338
339
                if (is_null($segment->getNext()) || $segment->getNext()->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) {
340
                    $this->applyQueryOptions($segment, $callback);
341
                }
342
            }
343
344
            // Apply $select and $expand options to result set, this function will be always applied
345
            // irrespective of return value of IDSQP2::canApplyQueryOptions which means library will
346
            // not delegate $expand/$select operation to IDSQP2 implementation
347
            $this->handleExpansion();
348
        }
349
350
        return;
351 View Code Duplication
        return $this->executeBase(function ($uriProcessor, $segment) {
0 ignored issues
show
Unused Code introduced by
return $this->executeBas... return $entity; }); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
352
            $requestMethod = $uriProcessor->service->getOperationContext()->incomingRequest()->getMethod();
353
            $resourceSet = $segment->getTargetResourceSetWrapper();
354
            $data = $uriProcessor->request->getData();
355
356
            if (!$resourceSet) {
357
                $url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
358
                throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
359
            }
360
361
            if (!$data) {
362
                throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
363
            }
364
365
            $entity = $uriProcessor->providers->postResource($resourceSet, $data);
366
367
            $segment->setSingleResult(true);
368
            $segment->setResult($entity);
369
370
            return $entity;
371
        });
372
    }
373
374
    /**
375
     * Execute the client submitted request against the data source
376
     *
377
     * @param callable $callback Function, what must be called
378
     */
379
    protected function executeBase($callback = null)
380
    {
381
        $segments = $this->request->getSegments();
382
383 View Code Duplication
        foreach ($segments as $segment) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
384
385
            $requestTargetKind = $segment->getTargetKind();
386
387
	        if ($segment->getTargetSource() == TargetSource::ENTITY_SET) {
388
                $this->handleSegmentTargetsToResourceSet($segment);
389
            } else if ($requestTargetKind == TargetKind::RESOURCE()) {
390
                if (is_null($segment->getPrevious()->getResult())) {
391
					throw ODataException::createResourceNotFoundError(
392
                        $segment->getPrevious()->getIdentifier()
393
                    );
394
                }
395
                $this->_handleSegmentTargetsToRelatedResource($segment);
396
            } else if ($requestTargetKind == TargetKind::LINK()) {
397
                $segment->setResult($segment->getPrevious()->getResult());
398
            } else if ($segment->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) {
399
                // we are done, $count will the last segment and
400
                // taken care by _applyQueryOptions method
401
                $segment->setResult($this->request->getCountValue());
402
                break;
403
            } else {
404
                if ($requestTargetKind == TargetKind::MEDIA_RESOURCE()) {
405
                    if (is_null($segment->getPrevious()->getResult())) {
406
						throw ODataException::createResourceNotFoundError(
407
                            $segment->getPrevious()->getIdentifier()
408
                        );
409
                    }
410
                    // For MLE and Named Stream the result of last segment
411
                    // should be that of previous segment, this is required
412
                    // while retrieving content type or stream from IDSSP
413
                    $segment->setResult($segment->getPrevious()->getResult());
414
                    // we are done, as named stream property or $value on
415
                    // media resource will be the last segment
416
                    break;
417
                }
418
419
	            $value = $segment->getPrevious()->getResult();
420
                while (!is_null($segment)) {
421
	                //TODO: what exactly is this doing here?  Once a null's found it seems everything will be null
422
                    if (!is_null($value)) {
423
                        $value = null;
424
                    } else {
425
                        try {
426
	                        //see #88
427
                            $property = new \ReflectionProperty($value, $segment->getIdentifier());
428
                            $value = $property->getValue($value);
429
                        } catch (\ReflectionException $reflectionException) {
430
                            //throw ODataException::createInternalServerError(Messages::orderByParserFailedToAccessOrInitializeProperty($resourceProperty->getName(), $resourceType->getName()));
0 ignored issues
show
Unused Code Comprehensibility introduced by
68% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
431
                        }
432
                    }
433
434
                    $segment->setResult($value);
435
                    $segment = $segment->getNext();
436
                    if (!is_null($segment) && $segment->getIdentifier() == ODataConstants::URI_VALUE_SEGMENT) {
437
                        $segment->setResult($value);
438
                        $segment = $segment->getNext();
439
                    }
440
                }
441
442
                break;
443
444
            }
445
446
            if (is_null($segment->getNext()) || $segment->getNext()->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) {
447
                $this->applyQueryOptions($segment, $callback);
448
            }
449
        }
450
451
         // Apply $select and $expand options to result set, this function will be always applied
452
         // irrespective of return value of IDSQP2::canApplyQueryOptions which means library will
453
         // not delegate $expand/$select operation to IDSQP2 implementation
454
        $this->handleExpansion();
455
    }
456
457
    /**
458
     * Query for a resource set pointed by the given segment descriptor and update the descriptor with the result.
459
     *
460
     * @param SegmentDescriptor $segment Describes the resource set to query
461
     * @return void
462
     *
463
     */
464
    private function handleSegmentTargetsToResourceSet( SegmentDescriptor $segment ) {
465
        if ($segment->isSingleResult()) {
466
            $entityInstance = $this->providers->getResourceFromResourceSet(
467
                $segment->getTargetResourceSetWrapper(),
468
                $segment->getKeyDescriptor()
469
            );
470
471
            $segment->setResult($entityInstance);
472
473
        } else {
474
475
            $internalskiptokentinfo = $this->request->getInternalSkipTokenInfo();
476
477
            $queryResult = $this->providers->getResourceSet(
478
	            $this->request->queryType,
479
                $segment->getTargetResourceSetWrapper(),
480
                $this->request->getFilterInfo(),
0 ignored issues
show
Bug introduced by
It seems like $this->request->getFilterInfo() can be null; however, getResourceSet() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
481
                $this->request->getInternalOrderByInfo(),
0 ignored issues
show
Bug introduced by
It seems like $this->request->getInternalOrderByInfo() can be null; however, getResourceSet() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
482
                $this->request->getTopCount(),
483
                $this->request->getSkipCount(),
484
                $internalskiptokentinfo ? $internalskiptokentinfo->getSkipTokenInfo() : null,
485
                $this->_getExpandedProjectionNodes()
486
            );
487
            $segment->setResult($queryResult);
488
        }
489
    }
490
491
    /**
492
     * Query for a related resource set or resource set reference pointed by the
493
     * given segment descriptor and update the descriptor with the result.
494
     *
495
     * @param SegmentDescriptor &$segment Describes the related resource
496
     *                                              to query.
497
     *
498
     * @return void
499
     */
500
    private function _handleSegmentTargetsToRelatedResource(SegmentDescriptor $segment) {
501
        $projectedProperty = $segment->getProjectedProperty();
502
        $projectedPropertyKind = $projectedProperty->getKind();
503
504
        if ($projectedPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE) {
505
            if ($segment->isSingleResult()) {
506
                $entityInstance = $this->providers->getResourceFromRelatedResourceSet(
507
                    $segment->getPrevious()->getTargetResourceSetWrapper(),
508
                    $segment->getPrevious()->getResult(),
509
                    $segment->getTargetResourceSetWrapper(),
510
                    $projectedProperty,
511
                    $segment->getKeyDescriptor()
512
                );
513
514
                $segment->setResult($entityInstance);
515
            } else {
516
                $queryResult = $this->providers->getRelatedResourceSet(
517
	                $this->request->queryType,
518
                    $segment->getPrevious()->getTargetResourceSetWrapper(),
519
                    $segment->getPrevious()->getResult(),
520
                    $segment->getTargetResourceSetWrapper(),
521
                    $segment->getProjectedProperty(),
522
                    $this->request->getFilterInfo(),
0 ignored issues
show
Bug introduced by
It seems like $this->request->getFilterInfo() can be null; however, getRelatedResourceSet() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
523
	                //TODO: why are these null?  see #98
524
                    null, // $orderby
525
                    null, // $top
526
                    null  // $skip
527
                );
528
529
                $segment->setResult($queryResult);
530
            }
531
        } else if ($projectedPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE) {
532
            $entityInstance = $this->providers->getRelatedResourceReference(
533
                $segment->getPrevious()->getTargetResourceSetWrapper(),
534
                $segment->getPrevious()->getResult(),
535
                $segment->getTargetResourceSetWrapper(),
536
                $segment->getProjectedProperty()
537
            );
538
539
            $segment->setResult($entityInstance);
540
        } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
541
            //Unexpected state
542
        }
543
    }
544
545
    /**
546
     * Applies the query options to the resource(s) retrieved from the data source.
547
     *
548
     * @param SegmentDescriptor $segment The descriptor which holds resource(s) on which query options to be applied.
549
     * @param callable $callback Function, what must be called
550
     *
551
     */
552
    private function applyQueryOptions(SegmentDescriptor $segment, $callback = null)
553
    {
554
        // For non-GET methods
555
        if ($callback) {
556
            $callback($this, $segment);
557
            return;
558
        }
559
560
	    //TODO: I'm not really happy with this..i think i'd rather keep the result the QueryResult
561
	    //not even bother with the setCountValue stuff (shouldn't counts be on segments?)
562
	    //and just work with the QueryResult in the object model serializer
563
	    $result = $segment->getResult();
564
565
	    if(!$result instanceof QueryResult){
566
		    //If the segment isn't a query result, then there's no paging or counting to be done
567
		    return;
568
        }
569
570
571
        // Note $inlinecount=allpages means include the total count regardless of paging..so we set the counts first
572
	    // regardless if POData does the paging or not.
573 View Code Duplication
        if ($this->request->queryType == QueryType::ENTITIES_WITH_COUNT()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
574
            if ($this->providers->handlesOrderedPaging()) {
575
                $this->request->setCountValue($result->count);
576
            } else {
577
                $this->request->setCountValue(count($result->results));
578
            }
579
        }
580
581
	    //Have POData perform paging if necessary
582
	    if(!$this->providers->handlesOrderedPaging() && !empty($result->results)){
583
			$result->results = $this->performPaging($result->results);
584
	    }
585
586
	    //a bit surprising, but $skip and $top affects $count so update it here, not above
587
	    //IE  data.svc/Collection/$count?$top=10 returns 10 even if Collection has 11+ entries
588 View Code Duplication
	    if ($this->request->queryType == QueryType::COUNT()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
589
		    if ($this->providers->handlesOrderedPaging()) {
590
			    $this->request->setCountValue($result->count);
591
		    } else {
592
			    $this->request->setCountValue(count($result->results));
593
		    }
594
	    }
595
596
        $segment->setResult($result->results);
597
    }
598
599
	/**
600
	 * If the provider does not perform the paging (ordering, top, skip) then this method does it
601
	 *
602
	 * @param array $result
603
	 * @return array
604
	 */
605
	private function performPaging(array $result)
606
	{
607
		//Apply (implicit and explicit) $orderby option
608
		$internalOrderByInfo = $this->request->getInternalOrderByInfo();
609
		if (!is_null($internalOrderByInfo)) {
610
			$orderByFunction = $internalOrderByInfo->getSorterFunction()->getReference();
611
			usort($result, $orderByFunction);
612
		}
613
614
		//Apply $skiptoken option
615
		$internalSkipTokenInfo = $this->request->getInternalSkipTokenInfo();
616
		if (!is_null($internalSkipTokenInfo)) {
617
			$matchingIndex = $internalSkipTokenInfo->getIndexOfFirstEntryInTheNextPage($result);
618
			$result = array_slice($result, $matchingIndex);
619
		}
620
621
		//Apply $top and $skip option
622
		if (!empty($result)) {
623
			$top  = $this->request->getTopCount();
624
			$skip = $this->request->getSkipCount();
625
			if(is_null($skip)) $skip = 0;
626
627
			$result = array_slice($result, $skip, $top);
628
		}
629
630
		return $result;
631
	}
632
633
634
    /**
635
     * Perform expansion.
636
     *
637
     * @return void
638
     */
639
    private function handleExpansion()
640
    {
641
        $node = $this->request->getRootProjectionNode();
642
        if (!is_null($node) && $node->isExpansionSpecified()) {
643
            $result = $this->request->getTargetResult();
644
            if (!is_null($result) || is_array($result) && !empty($result)) {
645
                $needPop = $this->_pushSegmentForRoot();
646
                $this->_executeExpansion($result);
647
                $this->_popSegment($needPop);
648
            }
649
        }
650
    }
651
652
    /**
653
     * Execute queries for expansion.
654
     *
655
     * @param array(mixed)/mixed $result Resource(s) whose navigation properties needs to be expanded.
0 ignored issues
show
Documentation introduced by
The doc-type array(mixed)/mixed could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
656
     *
657
     *
658
     * @return void
659
     */
660
    private function _executeExpansion($result)
661
    {
662
        $expandedProjectionNodes = $this->_getExpandedProjectionNodes();
663
        foreach ($expandedProjectionNodes as $expandedProjectionNode) {
664
            $isCollection = $expandedProjectionNode->getResourceProperty()->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE;
665
            $expandedPropertyName = $expandedProjectionNode->getResourceProperty()->getName();
666
            if (is_array($result)) {
667
                foreach ($result as $entry) {
668
                    // Check for null entry
669 View Code Duplication
                    if ($isCollection) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
670
                        $currentResourceSet = $this->_getCurrentResourceSetWrapper()->getResourceSet();
671
                        $resourceSetOfProjectedProperty = $expandedProjectionNode->getResourceSetWrapper()->getResourceSet();
672
                        $projectedProperty1 = $expandedProjectionNode->getResourceProperty();
673
                        $result1 = $this->providers->getRelatedResourceSet(
674
	                        QueryType::ENTITIES(), //it's always entities for an expansion
675
                            $currentResourceSet,
676
                            $entry,
677
                            $resourceSetOfProjectedProperty,
678
                            $projectedProperty1,
679
                            null, // $filter
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object<POData\UriProcess...ssionParser\FilterInfo>.

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...
680
                            null, // $orderby
681
                            null, // $top
682
                            null  // $skip
683
                        )->results;
684
                        if (!empty($result1)) {
685
                            $internalOrderByInfo = $expandedProjectionNode->getInternalOrderByInfo();
686
                            if (!is_null($internalOrderByInfo)) {
687
                                $orderByFunction = $internalOrderByInfo->getSorterFunction()->getReference();
688
                                usort($result1, $orderByFunction);
689
                                unset($internalOrderByInfo);
690
                                $takeCount = $expandedProjectionNode->getTakeCount();
691
                                if (!is_null($takeCount)) {
692
                                    $result1 = array_slice($result1, 0, $takeCount);
693
                                }
694
                            }
695
696
                            $entry->$expandedPropertyName = $result1;
697
                            $projectedProperty = $expandedProjectionNode->getResourceProperty();
698
                            $needPop = $this->_pushSegmentForNavigationProperty(
699
                                $projectedProperty
700
                            );
701
                            $this->_executeExpansion($result1);
702
                            $this->_popSegment($needPop);
703
                        } else {
704
                            $entry->$expandedPropertyName = array();
705
                        }
706
                    } else {
707
                        $currentResourceSet1 = $this->_getCurrentResourceSetWrapper()->getResourceSet();
708
                        $resourceSetOfProjectedProperty1 = $expandedProjectionNode->getResourceSetWrapper()->getResourceSet();
709
                        $projectedProperty2 = $expandedProjectionNode->getResourceProperty();
710
                        $result1 = $this->providers->getRelatedResourceReference(
711
                            $currentResourceSet1,
712
                            $entry,
713
                            $resourceSetOfProjectedProperty1,
714
                            $projectedProperty2
715
                        );
716
                        $entry->$expandedPropertyName = $result1;
717
                        if (!is_null($result1)) {
718
                            $projectedProperty3 = $expandedProjectionNode->getResourceProperty();
719
                            $needPop = $this->_pushSegmentForNavigationProperty(
720
                                $projectedProperty3
721
                            );
722
                            $this->_executeExpansion($result1);
723
                            $this->_popSegment($needPop);
724
                        }
725
                    }
726
                }
727 View Code Duplication
            } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
728
                if ($isCollection) {
729
                    $currentResourceSet2 = $this->_getCurrentResourceSetWrapper()->getResourceSet();
730
                    $resourceSetOfProjectedProperty2 = $expandedProjectionNode->getResourceSetWrapper()->getResourceSet();
731
                    $projectedProperty4 = $expandedProjectionNode->getResourceProperty();
732
                    $result1 = $this->providers->getRelatedResourceSet(
733
	                    QueryType::ENTITIES(), //it's always entities for an expansion
734
                        $currentResourceSet2,
735
                        $result,
736
                        $resourceSetOfProjectedProperty2,
737
                        $projectedProperty4,
738
                        null, // $filter
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object<POData\UriProcess...ssionParser\FilterInfo>.

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...
739
                        null, // $orderby
740
                        null, // $top
741
                        null  // $skip
742
                    )->results;
743
                    if (!empty($result1)) {
744
                        $internalOrderByInfo = $expandedProjectionNode->getInternalOrderByInfo();
745
                        if (!is_null($internalOrderByInfo)) {
746
                            $orderByFunction = $internalOrderByInfo->getSorterFunction()->getReference();
747
                            usort($result1, $orderByFunction);
748
                            unset($internalOrderByInfo);
749
                            $takeCount = $expandedProjectionNode->getTakeCount();
750
                            if (!is_null($takeCount)) {
751
                                $result1 = array_slice($result1, 0, $takeCount);
752
                            }
753
                        }
754
755
                        $result->$expandedPropertyName = $result1;
756
                        $projectedProperty7 = $expandedProjectionNode->getResourceProperty();
757
                        $needPop = $this->_pushSegmentForNavigationProperty(
758
                            $projectedProperty7
759
                        );
760
                        $this->_executeExpansion($result1);
761
                        $this->_popSegment($needPop);
762
                    } else {
763
                        $result->$expandedPropertyName = array();
764
                    }
765
                } else {
766
                    $currentResourceSet3 = $this->_getCurrentResourceSetWrapper()->getResourceSet();
767
                    $resourceSetOfProjectedProperty3 = $expandedProjectionNode->getResourceSetWrapper()->getResourceSet();
768
                    $projectedProperty5 = $expandedProjectionNode->getResourceProperty();
769
                    $result1 = $this->providers->getRelatedResourceReference(
770
                        $currentResourceSet3,
771
                        $result,
772
                        $resourceSetOfProjectedProperty3,
773
                        $projectedProperty5
774
                    );
775
                    $result->$expandedPropertyName = $result1;
776
                    if (!is_null($result1)) {
777
                        $projectedProperty6 = $expandedProjectionNode->getResourceProperty();
778
                        $needPop = $this->_pushSegmentForNavigationProperty(
779
                            $projectedProperty6
780
                        );
781
                        $this->_executeExpansion($result1);
782
                        $this->_popSegment($needPop);
783
                    }
784
                }
785
            }
786
        }
787
    }
788
789
    /**
790
     * Resource set wrapper for the resource being retireved.
791
     *
792
     * @return ResourceSetWrapper
793
     */
794 View Code Duplication
    private function _getCurrentResourceSetWrapper()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
795
    {
796
        $count = count($this->_segmentResourceSetWrappers);
797
        if ($count == 0) {
798
            return $this->request->getTargetResourceSetWrapper();
799
        } else {
800
            return $this->_segmentResourceSetWrappers[$count - 1];
801
        }
802
    }
803
804
    /**
805
     * Pushes a segment for the root of the tree
806
     * Note: Calls to this method should be balanced with calls to popSegment.
807
     *
808
     * @return bool true if the segment was pushed, false otherwise.
809
     */
810
    private function _pushSegmentForRoot()
811
    {
812
        $segmentName = $this->request->getContainerName();
813
        $segmentResourceSetWrapper
814
            = $this->request->getTargetResourceSetWrapper();
815
        return $this->_pushSegment($segmentName, $segmentResourceSetWrapper);
0 ignored issues
show
Bug introduced by
It seems like $segmentResourceSetWrapper defined by $this->request->getTargetResourceSetWrapper() on line 814 can be null; however, POData\UriProcessor\UriProcessor::_pushSegment() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
816
    }
817
818
    /**
819
     * Pushes a segment for the current navigation property being written out.
820
     * Note: Refer 'ObjectModelSerializerNotes.txt' for more details about
821
     * 'Segment Stack' and this method.
822
     * Note: Calls to this method should be balanced with calls to popSegment.
823
     *
824
     * @param ResourceProperty &$resourceProperty Current navigation property
825
     *                                            being written out
826
     *
827
     * @return bool true if a segment was pushed, false otherwise
828
     *
829
     * @throws InvalidOperationException If this function invoked with non-navigation
830
     *                                   property instance.
831
     */
832 View Code Duplication
    private function _pushSegmentForNavigationProperty(ResourceProperty &$resourceProperty)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
833
    {
834
        if ($resourceProperty->getTypeKind() == ResourceTypeKind::ENTITY) {
835
            $this->assert(
836
                !empty($this->_segmentNames),
837
                '!is_empty($this->_segmentNames'
838
            );
839
            $currentResourceSetWrapper = $this->_getCurrentResourceSetWrapper();
840
            $currentResourceType = $currentResourceSetWrapper->getResourceType();
841
            $currentResourceSetWrapper = $this->service
842
                ->getProvidersWrapper()
843
                ->getResourceSetWrapperForNavigationProperty(
844
                    $currentResourceSetWrapper,
0 ignored issues
show
Bug introduced by
It seems like $currentResourceSetWrapper defined by $this->service->getProvi...ype, $resourceProperty) on line 841 can be null; however, POData\Providers\Provide...ForNavigationProperty() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
845
                    $currentResourceType,
846
                    $resourceProperty
847
                );
848
849
            $this->assert(
850
                !is_null($currentResourceSetWrapper),
851
                '!null($currentResourceSetWrapper)'
852
            );
853
            return $this->_pushSegment(
854
                $resourceProperty->getName(),
855
                $currentResourceSetWrapper
0 ignored issues
show
Bug introduced by
It seems like $currentResourceSetWrapper defined by $this->service->getProvi...ype, $resourceProperty) on line 841 can be null; however, POData\UriProcessor\UriProcessor::_pushSegment() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
856
            );
857
        } else {
858
            throw new InvalidOperationException(
859
                'pushSegmentForNavigationProperty should not be called with non-entity type'
860
            );
861
        }
862
    }
863
864
    /**
865
     * Gets collection of expanded projection nodes under the current node.
866
     *
867
     * @return ExpandedProjectionNode[] List of nodes describing expansions for the current segment
868
     *
869
     */
870
    private function _getExpandedProjectionNodes()
871
    {
872
        $expandedProjectionNode = $this->_getCurrentExpandedProjectionNode();
873
        $expandedProjectionNodes = array();
874
        if (!is_null($expandedProjectionNode)) {
875
            foreach ($expandedProjectionNode->getChildNodes() as $node) {
876
                if ($node instanceof ExpandedProjectionNode) {
877
                    $expandedProjectionNodes[] = $node;
878
                }
879
            }
880
        }
881
882
        return $expandedProjectionNodes;
883
    }
884
885
    /**
886
     * Find a 'ExpandedProjectionNode' instance in the projection tree
887
     * which describes the current segment.
888
     *
889
     * @return ExpandedProjectionNode|null
890
     */
891 View Code Duplication
    private function _getCurrentExpandedProjectionNode()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
892
    {
893
        $expandedProjectionNode
894
            = $this->request->getRootProjectionNode();
895
        if (!is_null($expandedProjectionNode)) {
896
            $depth = count($this->_segmentNames);
897
            if ($depth != 0) {
898
                for ($i = 1; $i < $depth; $i++) {
899
                    $expandedProjectionNode
900
                        = $expandedProjectionNode->findNode($this->_segmentNames[$i]);
901
                        $this->assert(
902
                            !is_null($expandedProjectionNode),
903
                            '!is_null($expandedProjectionNode)'
904
                        );
905
                        $this->assert(
906
                            $expandedProjectionNode instanceof ExpandedProjectionNode,
907
                            '$expandedProjectionNode instanceof ExpandedProjectionNode'
908
                        );
909
                }
910
            }
911
        }
912
913
        return $expandedProjectionNode;
914
    }
915
916
    /**
917
     * Pushes information about the segment whose instance is going to be
918
     * retrieved from the IDSQP implementation
919
     * Note: Calls to this method should be balanced with calls to popSegment.
920
     *
921
     * @param string             $segmentName         Name of segment to push.
922
     * @param ResourceSetWrapper &$resourceSetWrapper The resource set wrapper
923
     *                                                to push.
924
     *
925
     * @return bool true if the segment was push, false otherwise
926
     */
927 View Code Duplication
    private function _pushSegment($segmentName, ResourceSetWrapper &$resourceSetWrapper)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
928
    {
929
        $rootProjectionNode = $this->request->getRootProjectionNode();
930
        if (!is_null($rootProjectionNode)
931
            && $rootProjectionNode->isExpansionSpecified()
932
        ) {
933
            array_push($this->_segmentNames, $segmentName);
934
            array_push($this->_segmentResourceSetWrappers, $resourceSetWrapper);
935
            return true;
936
        }
937
938
        return false;
939
    }
940
941
    /**
942
     * Pops segment information from the 'Segment Stack'
943
     * Note: Calls to this method should be balanced with previous calls
944
     * to _pushSegment.
945
     *
946
     * @param boolean $needPop Is a pop required. Only true if last push
947
     *                         was successful.
948
     *
949
     * @return void
950
     *
951
     * @throws InvalidOperationException If found un-balanced call
952
     *                                   with _pushSegment
953
     */
954 View Code Duplication
    private function _popSegment($needPop)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
955
    {
956
        if ($needPop) {
957
            if (!empty($this->_segmentNames)) {
958
                array_pop($this->_segmentNames);
959
                array_pop($this->_segmentResourceSetWrappers);
960
            } else {
961
                throw new InvalidOperationException(
962
                    'Found non-balanced call to _pushSegment and popSegment'
963
                );
964
            }
965
        }
966
    }
967
968
    /**
969
     * Assert that the given condition is true.
970
     *
971
     * @param boolean $condition         Constion to assert.
972
     * @param string  $conditionAsString Message to show incase assertion fails.
973
     *
974
     * @return void
975
     *
976
     * @throws InvalidOperationException
977
     */
978
    protected function assert($condition, $conditionAsString)
979
    {
980
        if (!$condition) {
981
            throw new InvalidOperationException(
982
                "Unexpected state, expecting $conditionAsString"
983
            );
984
        }
985
    }
986
}
987