Test Failed
Push — master ( c9c9eb...711208 )
by Bálint
16:29 queued 13s
created
src/POData/UriProcessor/UriProcessor.php 1 patch
Indentation   +942 added lines, -942 removed lines patch added patch discarded remove patch
@@ -35,660 +35,660 @@  discard block
 block discarded – undo
35 35
  */
36 36
 class UriProcessor
37 37
 {
38
-	/**
39
-	 * Description of the OData request that a client has submitted.
40
-	 *
41
-	 * @var RequestDescription
42
-	 */
43
-	private $request;
44
-
45
-	/**
46
-	 * Holds reference to the data service instance.
47
-	 *
48
-	 * @var IService
49
-	 */
50
-	private $service;
51
-
52
-	/**
53
-	 * Holds reference to the wrapper over IDSMP and IDSQP implementation.
54
-	 *
55
-	 * @var ProvidersWrapper
56
-	 */
57
-	private $providers;
58
-
59
-	/**
60
-	 * Collection of segment names.
61
-	 *
62
-	 * @var string[]
63
-	 */
64
-	private $_segmentNames;
65
-
66
-	/**
67
-	 * Collection of segment ResourceSetWrapper instances.
68
-	 *
69
-	 * @var ResourceSetWrapper[]
70
-	 */
71
-	private $_segmentResourceSetWrappers;
72
-
73
-	/**
74
-	 * Constructs a new instance of UriProcessor
75
-	 *
76
-	 * @param IService $service Reference to the data service instance.
77
-	 */
78
-	private function __construct(IService $service)
79
-	{
80
-		$this->service = $service;
81
-		$this->providers = $service->getProvidersWrapper();
82
-		$this->_segmentNames = array();
83
-		$this->_segmentResourceSetWrappers = array();
84
-	}
85
-
86
-	/**
87
-	 * Process the resource path and query options of client's request uri.
88
-	 *
89
-	 * @param IService $service Reference to the data service instance.
90
-	 *
91
-	 * @return URIProcessor
92
-	 *
93
-	 * @throws ODataException
94
-	 */
95
-	public static function process(IService $service)
96
-	{
97
-		$absoluteRequestUri = $service->getHost()->getAbsoluteRequestUri();
98
-		$absoluteServiceUri = $service->getHost()->getAbsoluteServiceUri();
99
-
100
-		if (!$absoluteServiceUri->isBaseOf($absoluteRequestUri)) {
101
-			throw ODataException::createInternalServerError(
102
-				Messages::uriProcessorRequestUriDoesNotHaveTheRightBaseUri(
103
-					$absoluteRequestUri->getUrlAsString(),
104
-					$absoluteServiceUri->getUrlAsString()
105
-				)
106
-			);
107
-		}
108
-
109
-		$uriProcessor = new UriProcessor($service);
110
-		//Parse the resource path part of the request Uri.
111
-		$uriProcessor->request = ResourcePathProcessor::process($service);
112
-
113
-		$uriProcessor->request->setUriProcessor($uriProcessor);
114
-
115
-		//Parse the query string options of the request Uri.
116
-		QueryProcessor::process($uriProcessor->request, $service);
117
-
118
-		return $uriProcessor;
119
-	}
120
-
121
-	/**
122
-	 * Process the resource path and query options of client's request uri.
123
-	 *
124
-	 * @param IService $service Reference to the data service instance.
125
-	 *
126
-	 * @return URIProcessor
127
-	 *
128
-	 * @throws ODataException
129
-	 */
130
-	public static function processPart($service, $request)
131
-	{
132
-		$uriProcessor = new UriProcessor($service);
133
-		//Parse the resource path part of the request Uri.
134
-		$uriProcessor->request = $request;
135
-
136
-		$request->setUriProcessor($uriProcessor);
137
-
138
-
139
-		//Parse the query string options of the request Uri.
140
-		QueryProcessor::process($uriProcessor->request, $service);
141
-
142
-		return $uriProcessor;
143
-	}
144
-
145
-	/**
146
-	 * Gets reference to the request submitted by client.
147
-	 *
148
-	 * @return RequestDescription
149
-	 */
150
-	public function getRequest()
151
-	{
152
-		return $this->request;
153
-	}
154
-
155
-	/**
156
-	 * Execute the client submitted request against the data source.
157
-	 */
158
-	public function execute()
159
-	{
160
-		$operationContext = $this->service->getOperationContext();
161
-		if (!$operationContext) {
162
-			$this->executeBase($this->request);
163
-			return;
164
-		}
165
-
166
-		$requestMethod = $operationContext->incomingRequest()->getMethod();
167
-		if ($requestMethod == HTTPRequestMethod::GET) {
168
-			$this->executeGet($this->request);
169
-		} elseif ($requestMethod == HTTPRequestMethod::PUT) {
170
-			$this->executePut($this->request);
171
-		} elseif ($requestMethod == HTTPRequestMethod::POST) {
172
-			if ($this->request->getLastSegment()->getTargetKind() == TargetKind::BATCH) {
173
-				$this->executeBatch($this->request);
174
-			} else {
175
-				$this->executePost($this->request);
176
-			}
177
-		} elseif ($requestMethod == HTTPRequestMethod::DELETE) {
178
-			$this->executeDelete($this->request);
179
-		} else {
180
-			throw ODataException::createNotImplementedError(Messages::unsupportedMethod($requestMethod));
181
-		}
182
-	}
183
-
184
-	/**
185
-	 * Execute the client submitted request against the data source (GET)
186
-	 */
187
-	protected function executeGet(RequestDescription $request)
188
-	{
189
-		return $this->executeBase($request);
190
-	}
191
-
192
-	/**
193
-	 * Execute the client submitted request against the data source (PUT)
194
-	 */
195
-	protected function executePut(RequestDescription $request)
196
-	{
197
-		$callback = function($uriProcessor, $segment) use ($request) {
198
-			$requestMethod = $request->getRequestMethod();
199
-			$resourceSet = $segment->getTargetResourceSetWrapper();
200
-			$keyDescriptor = $segment->getKeyDescriptor();
201
-			$data = $uriProcessor->request->getData();
202
-
203
-			if (!$resourceSet || !$keyDescriptor) {
204
-				$url = $request->getRequestUrl()->getUrlAsString();
205
-				throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
206
-			}
207
-
208
-			if (!$data) {
209
-				throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
210
-			}
211
-
212
-			$expand = $this->_getExpandedProjectionNodes($request);
213
-			$filter = $request->getFilterInfo();
214
-			$result = $uriProcessor->providers->putResource($resourceSet, $keyDescriptor, $data, $filter, $expand);
215
-
216
-			$segment->setSingleResult(true);
217
-			$segment->setResult($result);
218
-
219
-			return $result;
220
-		};
221
-
222
-		$segments = $request->getSegments();
223
-
224
-		foreach ($segments as $segment) {
225
-			if (is_null($segment->getNext()) || $segment->getNext()->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) {
226
-				$this->applyQueryOptions($request, $segment, $callback);
227
-			}
228
-		}
229
-			//?? TODO : TEST
230
-			// Apply $select and $expand options to result set, this function will be always applied
231
-			// irrespective of return value of IDSQP2::canApplyQueryOptions which means library will
232
-			// not delegate $expand/$select operation to IDSQP2 implementation
233
-		$this->handleExpansion($request);
234
-	}
235
-
236
-	/**
237
-	 * Execute the client submitted request against the data source (POST)
238
-	 */
239
-	protected function executePost(RequestDescription $request)
240
-	{
241
-		$callback = function($uriProcessor, $segment) use ($request) {
242
-			$requestMethod = $request->getRequestMethod();
243
-			$resourceSet = $segment->getTargetResourceSetWrapper();
244
-			$data = $request->getData();
245
-
246
-			if (!$resourceSet) {
247
-				$url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
248
-				throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
249
-			}
250
-
251
-			if (!$data) {
252
-				throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
253
-			}
254
-
255
-			$result = $uriProcessor->providers->postResource($resourceSet, $data);
256
-
257
-			$segment->setSingleResult(true);
258
-			$segment->setResult($result);
259
-
260
-			return $result;
261
-		};
262
-
263
-		$segments = $request->getSegments();
264
-
265
-		foreach ($segments as $segment) {
266
-			if (is_null($segment->getNext()) || $segment->getNext()->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) {
267
-				$this->applyQueryOptions($request, $segment, $callback);
268
-			}
269
-		}
270
-			//?? TODO : TEST
271
-			// Apply $select and $expand options to result set, this function will be always applied
272
-			// irrespective of return value of IDSQP2::canApplyQueryOptions which means library will
273
-			// not delegate $expand/$select operation to IDSQP2 implementation
274
-		$this->handleExpansion($request);
275
-	}
276
-
277
-	/**
278
-	 * Execute the client submitted request against the data source (DELETE)
279
-	 */
280
-	protected function executeDelete(RequestDescription $request)
281
-	{
282
-		return $this->executeBase($request, function($uriProcessor, $segment) use ($request) {
283
-			$requestMethod = $request->getRequestMethod();
284
-			$resourceSet = $segment->getTargetResourceSetWrapper();
285
-			$keyDescriptor = $segment->getKeyDescriptor();
286
-
287
-			if (!$resourceSet || !$keyDescriptor) {
288
-				$url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
289
-				throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
290
-			}
291
-
292
-			$result = $uriProcessor->providers->deleteResource($resourceSet, $keyDescriptor);
293
-			$segment->setResult($result);
294
-			return $result;
295
-		});
296
-	}
297
-
298
-	/**
299
-	 * Execute the client submitted batch request against the data source (POST)
300
-	 */
301
-	protected function executeBatch(RequestDescription $request)
302
-	{
303
-		$callback = null;
304
-		$post_callback = function($uriProcessor, $segment) {
305
-			$requestMethod = $uriProcessor->service->getOperationContext()->incomingRequest()->getMethod();
306
-			$resourceSet = $segment->getTargetResourceSetWrapper();
307
-			$data = $uriProcessor->request->getData();
308
-
309
-			if (!$resourceSet) {
310
-				$url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
311
-				throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
312
-			}
313
-
314
-			if (!$data) {
315
-				throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
316
-			}
317
-
318
-			$entity = $uriProcessor->providers->postResource($resourceSet, $data);
319
-
320
-			$segment->setSingleResult(true);
321
-			$segment->setResult($entity);
322
-
323
-			return $entity;
324
-		};
325
-
326
-		foreach ($request->getParts() as $request) {
327
-			$this->providers->getExpressionProvider()->clear();
328
-
329
-			switch ($request->getRequestMethod()) {
330
-				case HTTPRequestMethod::GET:
331
-					$this->executeGet($request);
332
-					$request->setExecuted();
333
-					break;
334
-				case HTTPRequestMethod::PUT:
335
-					$this->executePut($request);
336
-					$request->setExecuted();
337
-				case HTTPRequestMethod::POST:
338
-					$this->executePost($request);
339
-					$request->setExecuted();
340
-					break;
341
-				case HTTPRequestMethod::DELETE:
342
-					$this->executeDelete($request);
343
-					$request->setExecuted();
344
-					break;
345
-			}
346
-		}
347
-
348
-		return;
349
-		return $this->executeBase($request, function($uriProcessor, $segment) {
350
-			$requestMethod = $uriProcessor->service->getOperationContext()->incomingRequest()->getMethod();
351
-			$resourceSet = $segment->getTargetResourceSetWrapper();
352
-			$data = $uriProcessor->request->getData();
353
-
354
-			if (!$resourceSet) {
355
-				$url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
356
-				throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
357
-			}
358
-
359
-			if (!$data) {
360
-				throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
361
-			}
362
-
363
-			$entity = $uriProcessor->providers->postResource($resourceSet, $data);
364
-
365
-			$segment->setSingleResult(true);
366
-			$segment->setResult($entity);
367
-
368
-			return $entity;
369
-		});
370
-	}
371
-
372
-	/**
373
-	 * Execute the client submitted request against the data source
374
-	 *
375
-	 * @param callable $callback Function, what must be called
376
-	 */
377
-	protected function executeBase(RequestDescription $request, $callback = null)
378
-	{
379
-		$segments = $request->getSegments();
380
-
381
-		foreach ($segments as $segment) {
382
-
383
-			$requestTargetKind = $segment->getTargetKind();
384
-
385
-			if ($segment->getTargetSource() == TargetSource::ENTITY_SET) {
386
-				$this->handleSegmentTargetsToResourceSet($segment, $request);
387
-			} else if ($requestTargetKind == TargetKind::RESOURCE) {
388
-				if (is_null($segment->getPrevious()->getResult())) {
389
-					throw ODataException::createResourceNotFoundError(
390
-						$segment->getPrevious()->getIdentifier()
391
-					);
392
-				}
393
-				$this->_handleSegmentTargetsToRelatedResource($request, $segment);
394
-			} else if ($requestTargetKind == TargetKind::LINK) {
395
-				$segment->setResult($segment->getPrevious()->getResult());
396
-			} else if ($segment->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) {
397
-				// we are done, $count will the last segment and
398
-				// taken care by _applyQueryOptions method
399
-				$segment->setResult($request->getCountValue());
400
-				break;
401
-			} else {
402
-				if ($requestTargetKind == TargetKind::MEDIA_RESOURCE) {
403
-					if (is_null($segment->getPrevious()->getResult())) {
404
-						throw ODataException::createResourceNotFoundError(
405
-							$segment->getPrevious()->getIdentifier()
406
-						);
407
-					}
408
-					// For MLE and Named Stream the result of last segment
409
-					// should be that of previous segment, this is required
410
-					// while retrieving content type or stream from IDSSP
411
-					$segment->setResult($segment->getPrevious()->getResult());
412
-					// we are done, as named stream property or $value on
413
-					// media resource will be the last segment
414
-					break;
415
-				}
416
-
417
-				$value = $segment->getPrevious()->getResult();
418
-				while (!is_null($segment)) {
419
-					//TODO: what exactly is this doing here?  Once a null's found it seems everything will be null
420
-					if (!is_null($value)) {
421
-						$value = null;
422
-					} else {
423
-						try {
424
-							//see #88
425
-							$property = new \ReflectionProperty($value, $segment->getIdentifier());
426
-							$value = $property->getValue($value);
427
-						} catch (\ReflectionException $reflectionException) {
428
-							//throw ODataException::createInternalServerError(Messages::orderByParserFailedToAccessOrInitializeProperty($resourceProperty->getName(), $resourceType->getName()));
429
-						}
430
-					}
431
-
432
-					$segment->setResult($value);
433
-					$segment = $segment->getNext();
434
-					if (!is_null($segment) && $segment->getIdentifier() == ODataConstants::URI_VALUE_SEGMENT) {
435
-						$segment->setResult($value);
436
-						$segment = $segment->getNext();
437
-					}
438
-				}
439
-
440
-				break;
441
-
442
-			}
443
-
444
-			if (is_null($segment->getNext()) || $segment->getNext()->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) {
445
-				$this->applyQueryOptions($request, $segment, $callback);
446
-			}
447
-		}
448
-
449
-			// Apply $select and $expand options to result set, this function will be always applied
450
-			// irrespective of return value of IDSQP2::canApplyQueryOptions which means library will
451
-			// not delegate $expand/$select operation to IDSQP2 implementation
452
-		$this->handleExpansion($request);
453
-	}
454
-
455
-	/**
456
-	 * Query for a resource set pointed by the given segment descriptor and update the descriptor with the result.
457
-	 *
458
-	 * @param SegmentDescriptor $segment Describes the resource set to query
459
-	 * @return void
460
-	 *
461
-	 */
462
-	private function handleSegmentTargetsToResourceSet(SegmentDescriptor $segment, $request) {
463
-		if ($segment->isSingleResult()) {
464
-			$entityInstance = $this->providers->getResourceFromResourceSet(
465
-				$segment->getTargetResourceSetWrapper(),
466
-				$segment->getKeyDescriptor(),
467
-				$this->_getExpandedProjectionNodes($request)
468
-			);
469
-
470
-			$segment->setResult($entityInstance);
471
-
472
-		} else {
473
-
474
-			$internalskiptokentinfo = $request->getInternalSkipTokenInfo();
475
-
476
-			$queryResult = $this->providers->getResourceSet(
477
-				$request->queryType,
478
-				$segment->getTargetResourceSetWrapper(),
479
-				$request->getFilterInfo(),
480
-				$request->getInternalOrderByInfo(),
481
-				$request->getTopCount(),
482
-				$request->getSkipCount(),
483
-				$internalskiptokentinfo ? $internalskiptokentinfo->getSkipTokenInfo() : null,
484
-				$this->_getExpandedProjectionNodes($request)
485
-			);
486
-			$segment->setResult($queryResult);
487
-		}
488
-	}
489
-
490
-	/**
491
-	 * Query for a related resource set or resource set reference pointed by the
492
-	 * given segment descriptor and update the descriptor with the result.
493
-	 *
494
-	 * @param SegmentDescriptor &$segment Describes the related resource
495
-	 *                                              to query.
496
-	 *
497
-	 * @return void
498
-	 */
499
-	private function _handleSegmentTargetsToRelatedResource(RequestDescription $request, SegmentDescriptor $segment) {
500
-		$projectedProperty = $segment->getProjectedProperty();
501
-		$projectedPropertyKind = $projectedProperty->getKind();
502
-
503
-		if ($projectedPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE) {
504
-			if ($segment->isSingleResult()) {
505
-				$entityInstance = $this->providers->getResourceFromRelatedResourceSet(
506
-					$segment->getPrevious()->getTargetResourceSetWrapper(),
507
-					$segment->getPrevious()->getResult(),
508
-					$segment->getTargetResourceSetWrapper(),
509
-					$projectedProperty,
510
-					$segment->getKeyDescriptor()
511
-				);
512
-
513
-				$segment->setResult($entityInstance);
514
-			} else {
515
-				$queryResult = $this->providers->getRelatedResourceSet(
516
-					$request->queryType,
517
-					$segment->getPrevious()->getTargetResourceSetWrapper(),
518
-					$segment->getPrevious()->getResult(),
519
-					$segment->getTargetResourceSetWrapper(),
520
-					$segment->getProjectedProperty(),
521
-					$request->getFilterInfo(),
522
-					//TODO: why are these null?  see #98
523
-					null, // $orderby
524
-					null, // $top
525
-					null  // $skip
526
-				);
527
-
528
-				$segment->setResult($queryResult);
529
-			}
530
-		} else if ($projectedPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE) {
531
-			$entityInstance = $this->providers->getRelatedResourceReference(
532
-				$segment->getPrevious()->getTargetResourceSetWrapper(),
533
-				$segment->getPrevious()->getResult(),
534
-				$segment->getTargetResourceSetWrapper(),
535
-				$segment->getProjectedProperty()
536
-			);
537
-
538
-			$segment->setResult($entityInstance);
539
-		} else {
540
-			//Unexpected state
541
-		}
542
-	}
543
-
544
-	/**
545
-	 * Applies the query options to the resource(s) retrieved from the data source.
546
-	 *
547
-	 * @param SegmentDescriptor $segment The descriptor which holds resource(s) on which query options to be applied.
548
-	 * @param callable $callback Function, what must be called
549
-	 *
550
-	 */
551
-	private function applyQueryOptions(RequestDescription $request, SegmentDescriptor $segment, $callback = null)
552
-	{
553
-		// For non-GET methods
554
-		if ($callback) {
555
-			$callback($this, $segment);
556
-			return;
557
-		}
558
-
559
-		//TODO: I'm not really happy with this..i think i'd rather keep the result the QueryResult
560
-		//not even bother with the setCountValue stuff (shouldn't counts be on segments?)
561
-		//and just work with the QueryResult in the object model serializer
562
-		$result = $segment->getResult();
563
-
564
-		if (!$result instanceof QueryResult) {
565
-			//If the segment isn't a query result, then there's no paging or counting to be done
566
-			return;
567
-		}
568
-
569
-
570
-		// Note $inlinecount=allpages means include the total count regardless of paging..so we set the counts first
571
-		// regardless if POData does the paging or not.
572
-		if ($request->queryType == QueryType::ENTITIES_WITH_COUNT) {
573
-			if ($this->providers->handlesOrderedPaging()) {
574
-				$request->setCountValue($result->count);
575
-			} else {
576
-				$request->setCountValue(count($result->results));
577
-			}
578
-		}
579
-
580
-		//Have POData perform paging if necessary
581
-		if (!$this->providers->handlesOrderedPaging() && !empty($result->results)) {
582
-			$result->results = $this->performPaging($request, $result->results);
583
-		}
584
-
585
-		//a bit surprising, but $skip and $top affects $count so update it here, not above
586
-		//IE  data.svc/Collection/$count?$top=10 returns 10 even if Collection has 11+ entries
587
-		if ($request->queryType == QueryType::COUNT) {
588
-			if ($this->providers->handlesOrderedPaging()) {
589
-				$request->setCountValue($result->count);
590
-			} else {
591
-				$request->setCountValue(count($result->results));
592
-			}
593
-		}
594
-
595
-		$segment->setResult($result->results);
596
-	}
597
-
598
-	/**
599
-	 * If the provider does not perform the paging (ordering, top, skip) then this method does it
600
-	 *
601
-	 * @param array $result
602
-	 * @return array
603
-	 */
604
-	private function performPaging(RequestDescription $request, array $result)
605
-	{
606
-		//Apply (implicit and explicit) $orderby option
607
-		$internalOrderByInfo = $request->getInternalOrderByInfo();
608
-		if (!is_null($internalOrderByInfo)) {
609
-			$orderByFunction = $internalOrderByInfo->getSorterFunction()->getReference();
610
-			usort($result, $orderByFunction);
611
-		}
612
-
613
-		//Apply $skiptoken option
614
-		$internalSkipTokenInfo = $request->getInternalSkipTokenInfo();
615
-		if (!is_null($internalSkipTokenInfo)) {
616
-			$matchingIndex = $internalSkipTokenInfo->getIndexOfFirstEntryInTheNextPage($result);
617
-			$result = array_slice($result, $matchingIndex);
618
-		}
619
-
620
-		//Apply $top and $skip option
621
-		if (!empty($result)) {
622
-			$top = $request->getTopCount();
623
-			$skip = $request->getSkipCount();
624
-			if (is_null($skip)) {
625
-				$skip = 0;
626
-			}
627
-
628
-			$result = array_slice($result, $skip, $top);
629
-		}
630
-
631
-		return $result;
632
-	}
633
-
634
-
635
-	/**
636
-	 * Perform expansion.
637
-	 *
638
-	 * @return void
639
-	 */
640
-	private function handleExpansion(RequestDescription $request)
641
-	{
642
-		$node = $request->getRootProjectionNode();
643
-		if (!is_null($node) && $node->isExpansionSpecified()) {
644
-			$result = $request->getTargetResult();
645
-			if (!is_null($result) || is_iterable($result) && !empty($result)) {
646
-				$needPop = $this->_pushSegmentForRoot($request);
647
-				$this->_executeExpansion($request, $result);
648
-				$this->_popSegment($needPop);
649
-			}
650
-		}
651
-	}
652
-
653
-	/**
654
-	 * Execute queries for expansion.
655
-	 *
656
-	 * @param array(mixed)/mixed $result Resource(s) whose navigation properties needs to be expanded.
657
-	 *
658
-	 *
659
-	 * @return void
660
-	 */
661
-	private function _executeExpansion(RequestDescription $request, $result)
662
-	{
663
-		$expandedProjectionNodes = $this->_getExpandedProjectionNodes($request);
664
-		foreach ($expandedProjectionNodes as $expandedProjectionNode) {
665
-			$isCollection = $expandedProjectionNode->getResourceProperty()->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE;
666
-			$projectedProperty = $expandedProjectionNode->getResourceProperty();
667
-			$expandedPropertyName = $projectedProperty->getName();
668
-			$currentResourceSet = $this->_getCurrentResourceSetWrapper($request)->getResourceSet();
669
-			$currentResourceSetType = $currentResourceSet->getResourceType()->getInstanceType();
670
-			$expandedPropertyReflection = $currentResourceSetType->getProperty($expandedPropertyName);
671
-			$expandedPropertyReflection->setAccessible(true);
672
-			$resourceSetOfProjectedProperty = $expandedProjectionNode->getResourceSetWrapper()->getResourceSet();
673
-
674
-			if (is_iterable($result)) {
675
-				foreach ($result as $entry) {
676
-					// Check for null entry
677
-					if ($isCollection) {
678
-						$result1 = $this->providers->getRelatedResourceSet(
679
-							QueryType::ENTITIES, //it's always entities for an expansion
680
-							$currentResourceSet,
681
-							$entry,
682
-							$resourceSetOfProjectedProperty,
683
-							$projectedProperty,
684
-							null, // $filter
685
-							null, // $orderby
686
-							null, // $top
687
-							null  // $skip
688
-						)->results;
689
-						if (!empty($result1)) {
690
-							$internalOrderByInfo = $expandedProjectionNode->getInternalOrderByInfo();
691
-							/*if (!is_null($internalOrderByInfo)) {
38
+    /**
39
+     * Description of the OData request that a client has submitted.
40
+     *
41
+     * @var RequestDescription
42
+     */
43
+    private $request;
44
+
45
+    /**
46
+     * Holds reference to the data service instance.
47
+     *
48
+     * @var IService
49
+     */
50
+    private $service;
51
+
52
+    /**
53
+     * Holds reference to the wrapper over IDSMP and IDSQP implementation.
54
+     *
55
+     * @var ProvidersWrapper
56
+     */
57
+    private $providers;
58
+
59
+    /**
60
+     * Collection of segment names.
61
+     *
62
+     * @var string[]
63
+     */
64
+    private $_segmentNames;
65
+
66
+    /**
67
+     * Collection of segment ResourceSetWrapper instances.
68
+     *
69
+     * @var ResourceSetWrapper[]
70
+     */
71
+    private $_segmentResourceSetWrappers;
72
+
73
+    /**
74
+     * Constructs a new instance of UriProcessor
75
+     *
76
+     * @param IService $service Reference to the data service instance.
77
+     */
78
+    private function __construct(IService $service)
79
+    {
80
+        $this->service = $service;
81
+        $this->providers = $service->getProvidersWrapper();
82
+        $this->_segmentNames = array();
83
+        $this->_segmentResourceSetWrappers = array();
84
+    }
85
+
86
+    /**
87
+     * Process the resource path and query options of client's request uri.
88
+     *
89
+     * @param IService $service Reference to the data service instance.
90
+     *
91
+     * @return URIProcessor
92
+     *
93
+     * @throws ODataException
94
+     */
95
+    public static function process(IService $service)
96
+    {
97
+        $absoluteRequestUri = $service->getHost()->getAbsoluteRequestUri();
98
+        $absoluteServiceUri = $service->getHost()->getAbsoluteServiceUri();
99
+
100
+        if (!$absoluteServiceUri->isBaseOf($absoluteRequestUri)) {
101
+            throw ODataException::createInternalServerError(
102
+                Messages::uriProcessorRequestUriDoesNotHaveTheRightBaseUri(
103
+                    $absoluteRequestUri->getUrlAsString(),
104
+                    $absoluteServiceUri->getUrlAsString()
105
+                )
106
+            );
107
+        }
108
+
109
+        $uriProcessor = new UriProcessor($service);
110
+        //Parse the resource path part of the request Uri.
111
+        $uriProcessor->request = ResourcePathProcessor::process($service);
112
+
113
+        $uriProcessor->request->setUriProcessor($uriProcessor);
114
+
115
+        //Parse the query string options of the request Uri.
116
+        QueryProcessor::process($uriProcessor->request, $service);
117
+
118
+        return $uriProcessor;
119
+    }
120
+
121
+    /**
122
+     * Process the resource path and query options of client's request uri.
123
+     *
124
+     * @param IService $service Reference to the data service instance.
125
+     *
126
+     * @return URIProcessor
127
+     *
128
+     * @throws ODataException
129
+     */
130
+    public static function processPart($service, $request)
131
+    {
132
+        $uriProcessor = new UriProcessor($service);
133
+        //Parse the resource path part of the request Uri.
134
+        $uriProcessor->request = $request;
135
+
136
+        $request->setUriProcessor($uriProcessor);
137
+
138
+
139
+        //Parse the query string options of the request Uri.
140
+        QueryProcessor::process($uriProcessor->request, $service);
141
+
142
+        return $uriProcessor;
143
+    }
144
+
145
+    /**
146
+     * Gets reference to the request submitted by client.
147
+     *
148
+     * @return RequestDescription
149
+     */
150
+    public function getRequest()
151
+    {
152
+        return $this->request;
153
+    }
154
+
155
+    /**
156
+     * Execute the client submitted request against the data source.
157
+     */
158
+    public function execute()
159
+    {
160
+        $operationContext = $this->service->getOperationContext();
161
+        if (!$operationContext) {
162
+            $this->executeBase($this->request);
163
+            return;
164
+        }
165
+
166
+        $requestMethod = $operationContext->incomingRequest()->getMethod();
167
+        if ($requestMethod == HTTPRequestMethod::GET) {
168
+            $this->executeGet($this->request);
169
+        } elseif ($requestMethod == HTTPRequestMethod::PUT) {
170
+            $this->executePut($this->request);
171
+        } elseif ($requestMethod == HTTPRequestMethod::POST) {
172
+            if ($this->request->getLastSegment()->getTargetKind() == TargetKind::BATCH) {
173
+                $this->executeBatch($this->request);
174
+            } else {
175
+                $this->executePost($this->request);
176
+            }
177
+        } elseif ($requestMethod == HTTPRequestMethod::DELETE) {
178
+            $this->executeDelete($this->request);
179
+        } else {
180
+            throw ODataException::createNotImplementedError(Messages::unsupportedMethod($requestMethod));
181
+        }
182
+    }
183
+
184
+    /**
185
+     * Execute the client submitted request against the data source (GET)
186
+     */
187
+    protected function executeGet(RequestDescription $request)
188
+    {
189
+        return $this->executeBase($request);
190
+    }
191
+
192
+    /**
193
+     * Execute the client submitted request against the data source (PUT)
194
+     */
195
+    protected function executePut(RequestDescription $request)
196
+    {
197
+        $callback = function($uriProcessor, $segment) use ($request) {
198
+            $requestMethod = $request->getRequestMethod();
199
+            $resourceSet = $segment->getTargetResourceSetWrapper();
200
+            $keyDescriptor = $segment->getKeyDescriptor();
201
+            $data = $uriProcessor->request->getData();
202
+
203
+            if (!$resourceSet || !$keyDescriptor) {
204
+                $url = $request->getRequestUrl()->getUrlAsString();
205
+                throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
206
+            }
207
+
208
+            if (!$data) {
209
+                throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
210
+            }
211
+
212
+            $expand = $this->_getExpandedProjectionNodes($request);
213
+            $filter = $request->getFilterInfo();
214
+            $result = $uriProcessor->providers->putResource($resourceSet, $keyDescriptor, $data, $filter, $expand);
215
+
216
+            $segment->setSingleResult(true);
217
+            $segment->setResult($result);
218
+
219
+            return $result;
220
+        };
221
+
222
+        $segments = $request->getSegments();
223
+
224
+        foreach ($segments as $segment) {
225
+            if (is_null($segment->getNext()) || $segment->getNext()->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) {
226
+                $this->applyQueryOptions($request, $segment, $callback);
227
+            }
228
+        }
229
+            //?? TODO : TEST
230
+            // Apply $select and $expand options to result set, this function will be always applied
231
+            // irrespective of return value of IDSQP2::canApplyQueryOptions which means library will
232
+            // not delegate $expand/$select operation to IDSQP2 implementation
233
+        $this->handleExpansion($request);
234
+    }
235
+
236
+    /**
237
+     * Execute the client submitted request against the data source (POST)
238
+     */
239
+    protected function executePost(RequestDescription $request)
240
+    {
241
+        $callback = function($uriProcessor, $segment) use ($request) {
242
+            $requestMethod = $request->getRequestMethod();
243
+            $resourceSet = $segment->getTargetResourceSetWrapper();
244
+            $data = $request->getData();
245
+
246
+            if (!$resourceSet) {
247
+                $url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
248
+                throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
249
+            }
250
+
251
+            if (!$data) {
252
+                throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
253
+            }
254
+
255
+            $result = $uriProcessor->providers->postResource($resourceSet, $data);
256
+
257
+            $segment->setSingleResult(true);
258
+            $segment->setResult($result);
259
+
260
+            return $result;
261
+        };
262
+
263
+        $segments = $request->getSegments();
264
+
265
+        foreach ($segments as $segment) {
266
+            if (is_null($segment->getNext()) || $segment->getNext()->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) {
267
+                $this->applyQueryOptions($request, $segment, $callback);
268
+            }
269
+        }
270
+            //?? TODO : TEST
271
+            // Apply $select and $expand options to result set, this function will be always applied
272
+            // irrespective of return value of IDSQP2::canApplyQueryOptions which means library will
273
+            // not delegate $expand/$select operation to IDSQP2 implementation
274
+        $this->handleExpansion($request);
275
+    }
276
+
277
+    /**
278
+     * Execute the client submitted request against the data source (DELETE)
279
+     */
280
+    protected function executeDelete(RequestDescription $request)
281
+    {
282
+        return $this->executeBase($request, function($uriProcessor, $segment) use ($request) {
283
+            $requestMethod = $request->getRequestMethod();
284
+            $resourceSet = $segment->getTargetResourceSetWrapper();
285
+            $keyDescriptor = $segment->getKeyDescriptor();
286
+
287
+            if (!$resourceSet || !$keyDescriptor) {
288
+                $url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
289
+                throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
290
+            }
291
+
292
+            $result = $uriProcessor->providers->deleteResource($resourceSet, $keyDescriptor);
293
+            $segment->setResult($result);
294
+            return $result;
295
+        });
296
+    }
297
+
298
+    /**
299
+     * Execute the client submitted batch request against the data source (POST)
300
+     */
301
+    protected function executeBatch(RequestDescription $request)
302
+    {
303
+        $callback = null;
304
+        $post_callback = function($uriProcessor, $segment) {
305
+            $requestMethod = $uriProcessor->service->getOperationContext()->incomingRequest()->getMethod();
306
+            $resourceSet = $segment->getTargetResourceSetWrapper();
307
+            $data = $uriProcessor->request->getData();
308
+
309
+            if (!$resourceSet) {
310
+                $url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
311
+                throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
312
+            }
313
+
314
+            if (!$data) {
315
+                throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
316
+            }
317
+
318
+            $entity = $uriProcessor->providers->postResource($resourceSet, $data);
319
+
320
+            $segment->setSingleResult(true);
321
+            $segment->setResult($entity);
322
+
323
+            return $entity;
324
+        };
325
+
326
+        foreach ($request->getParts() as $request) {
327
+            $this->providers->getExpressionProvider()->clear();
328
+
329
+            switch ($request->getRequestMethod()) {
330
+                case HTTPRequestMethod::GET:
331
+                    $this->executeGet($request);
332
+                    $request->setExecuted();
333
+                    break;
334
+                case HTTPRequestMethod::PUT:
335
+                    $this->executePut($request);
336
+                    $request->setExecuted();
337
+                case HTTPRequestMethod::POST:
338
+                    $this->executePost($request);
339
+                    $request->setExecuted();
340
+                    break;
341
+                case HTTPRequestMethod::DELETE:
342
+                    $this->executeDelete($request);
343
+                    $request->setExecuted();
344
+                    break;
345
+            }
346
+        }
347
+
348
+        return;
349
+        return $this->executeBase($request, function($uriProcessor, $segment) {
350
+            $requestMethod = $uriProcessor->service->getOperationContext()->incomingRequest()->getMethod();
351
+            $resourceSet = $segment->getTargetResourceSetWrapper();
352
+            $data = $uriProcessor->request->getData();
353
+
354
+            if (!$resourceSet) {
355
+                $url = $uriProcessor->service->getHost()->getAbsoluteRequestUri()->getUrlAsString();
356
+                throw ODataException::createBadRequestError(Messages::badRequestInvalidUriForThisVerb($url, $requestMethod));
357
+            }
358
+
359
+            if (!$data) {
360
+                throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
361
+            }
362
+
363
+            $entity = $uriProcessor->providers->postResource($resourceSet, $data);
364
+
365
+            $segment->setSingleResult(true);
366
+            $segment->setResult($entity);
367
+
368
+            return $entity;
369
+        });
370
+    }
371
+
372
+    /**
373
+     * Execute the client submitted request against the data source
374
+     *
375
+     * @param callable $callback Function, what must be called
376
+     */
377
+    protected function executeBase(RequestDescription $request, $callback = null)
378
+    {
379
+        $segments = $request->getSegments();
380
+
381
+        foreach ($segments as $segment) {
382
+
383
+            $requestTargetKind = $segment->getTargetKind();
384
+
385
+            if ($segment->getTargetSource() == TargetSource::ENTITY_SET) {
386
+                $this->handleSegmentTargetsToResourceSet($segment, $request);
387
+            } else if ($requestTargetKind == TargetKind::RESOURCE) {
388
+                if (is_null($segment->getPrevious()->getResult())) {
389
+                    throw ODataException::createResourceNotFoundError(
390
+                        $segment->getPrevious()->getIdentifier()
391
+                    );
392
+                }
393
+                $this->_handleSegmentTargetsToRelatedResource($request, $segment);
394
+            } else if ($requestTargetKind == TargetKind::LINK) {
395
+                $segment->setResult($segment->getPrevious()->getResult());
396
+            } else if ($segment->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) {
397
+                // we are done, $count will the last segment and
398
+                // taken care by _applyQueryOptions method
399
+                $segment->setResult($request->getCountValue());
400
+                break;
401
+            } else {
402
+                if ($requestTargetKind == TargetKind::MEDIA_RESOURCE) {
403
+                    if (is_null($segment->getPrevious()->getResult())) {
404
+                        throw ODataException::createResourceNotFoundError(
405
+                            $segment->getPrevious()->getIdentifier()
406
+                        );
407
+                    }
408
+                    // For MLE and Named Stream the result of last segment
409
+                    // should be that of previous segment, this is required
410
+                    // while retrieving content type or stream from IDSSP
411
+                    $segment->setResult($segment->getPrevious()->getResult());
412
+                    // we are done, as named stream property or $value on
413
+                    // media resource will be the last segment
414
+                    break;
415
+                }
416
+
417
+                $value = $segment->getPrevious()->getResult();
418
+                while (!is_null($segment)) {
419
+                    //TODO: what exactly is this doing here?  Once a null's found it seems everything will be null
420
+                    if (!is_null($value)) {
421
+                        $value = null;
422
+                    } else {
423
+                        try {
424
+                            //see #88
425
+                            $property = new \ReflectionProperty($value, $segment->getIdentifier());
426
+                            $value = $property->getValue($value);
427
+                        } catch (\ReflectionException $reflectionException) {
428
+                            //throw ODataException::createInternalServerError(Messages::orderByParserFailedToAccessOrInitializeProperty($resourceProperty->getName(), $resourceType->getName()));
429
+                        }
430
+                    }
431
+
432
+                    $segment->setResult($value);
433
+                    $segment = $segment->getNext();
434
+                    if (!is_null($segment) && $segment->getIdentifier() == ODataConstants::URI_VALUE_SEGMENT) {
435
+                        $segment->setResult($value);
436
+                        $segment = $segment->getNext();
437
+                    }
438
+                }
439
+
440
+                break;
441
+
442
+            }
443
+
444
+            if (is_null($segment->getNext()) || $segment->getNext()->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) {
445
+                $this->applyQueryOptions($request, $segment, $callback);
446
+            }
447
+        }
448
+
449
+            // Apply $select and $expand options to result set, this function will be always applied
450
+            // irrespective of return value of IDSQP2::canApplyQueryOptions which means library will
451
+            // not delegate $expand/$select operation to IDSQP2 implementation
452
+        $this->handleExpansion($request);
453
+    }
454
+
455
+    /**
456
+     * Query for a resource set pointed by the given segment descriptor and update the descriptor with the result.
457
+     *
458
+     * @param SegmentDescriptor $segment Describes the resource set to query
459
+     * @return void
460
+     *
461
+     */
462
+    private function handleSegmentTargetsToResourceSet(SegmentDescriptor $segment, $request) {
463
+        if ($segment->isSingleResult()) {
464
+            $entityInstance = $this->providers->getResourceFromResourceSet(
465
+                $segment->getTargetResourceSetWrapper(),
466
+                $segment->getKeyDescriptor(),
467
+                $this->_getExpandedProjectionNodes($request)
468
+            );
469
+
470
+            $segment->setResult($entityInstance);
471
+
472
+        } else {
473
+
474
+            $internalskiptokentinfo = $request->getInternalSkipTokenInfo();
475
+
476
+            $queryResult = $this->providers->getResourceSet(
477
+                $request->queryType,
478
+                $segment->getTargetResourceSetWrapper(),
479
+                $request->getFilterInfo(),
480
+                $request->getInternalOrderByInfo(),
481
+                $request->getTopCount(),
482
+                $request->getSkipCount(),
483
+                $internalskiptokentinfo ? $internalskiptokentinfo->getSkipTokenInfo() : null,
484
+                $this->_getExpandedProjectionNodes($request)
485
+            );
486
+            $segment->setResult($queryResult);
487
+        }
488
+    }
489
+
490
+    /**
491
+     * Query for a related resource set or resource set reference pointed by the
492
+     * given segment descriptor and update the descriptor with the result.
493
+     *
494
+     * @param SegmentDescriptor &$segment Describes the related resource
495
+     *                                              to query.
496
+     *
497
+     * @return void
498
+     */
499
+    private function _handleSegmentTargetsToRelatedResource(RequestDescription $request, SegmentDescriptor $segment) {
500
+        $projectedProperty = $segment->getProjectedProperty();
501
+        $projectedPropertyKind = $projectedProperty->getKind();
502
+
503
+        if ($projectedPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE) {
504
+            if ($segment->isSingleResult()) {
505
+                $entityInstance = $this->providers->getResourceFromRelatedResourceSet(
506
+                    $segment->getPrevious()->getTargetResourceSetWrapper(),
507
+                    $segment->getPrevious()->getResult(),
508
+                    $segment->getTargetResourceSetWrapper(),
509
+                    $projectedProperty,
510
+                    $segment->getKeyDescriptor()
511
+                );
512
+
513
+                $segment->setResult($entityInstance);
514
+            } else {
515
+                $queryResult = $this->providers->getRelatedResourceSet(
516
+                    $request->queryType,
517
+                    $segment->getPrevious()->getTargetResourceSetWrapper(),
518
+                    $segment->getPrevious()->getResult(),
519
+                    $segment->getTargetResourceSetWrapper(),
520
+                    $segment->getProjectedProperty(),
521
+                    $request->getFilterInfo(),
522
+                    //TODO: why are these null?  see #98
523
+                    null, // $orderby
524
+                    null, // $top
525
+                    null  // $skip
526
+                );
527
+
528
+                $segment->setResult($queryResult);
529
+            }
530
+        } else if ($projectedPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE) {
531
+            $entityInstance = $this->providers->getRelatedResourceReference(
532
+                $segment->getPrevious()->getTargetResourceSetWrapper(),
533
+                $segment->getPrevious()->getResult(),
534
+                $segment->getTargetResourceSetWrapper(),
535
+                $segment->getProjectedProperty()
536
+            );
537
+
538
+            $segment->setResult($entityInstance);
539
+        } else {
540
+            //Unexpected state
541
+        }
542
+    }
543
+
544
+    /**
545
+     * Applies the query options to the resource(s) retrieved from the data source.
546
+     *
547
+     * @param SegmentDescriptor $segment The descriptor which holds resource(s) on which query options to be applied.
548
+     * @param callable $callback Function, what must be called
549
+     *
550
+     */
551
+    private function applyQueryOptions(RequestDescription $request, SegmentDescriptor $segment, $callback = null)
552
+    {
553
+        // For non-GET methods
554
+        if ($callback) {
555
+            $callback($this, $segment);
556
+            return;
557
+        }
558
+
559
+        //TODO: I'm not really happy with this..i think i'd rather keep the result the QueryResult
560
+        //not even bother with the setCountValue stuff (shouldn't counts be on segments?)
561
+        //and just work with the QueryResult in the object model serializer
562
+        $result = $segment->getResult();
563
+
564
+        if (!$result instanceof QueryResult) {
565
+            //If the segment isn't a query result, then there's no paging or counting to be done
566
+            return;
567
+        }
568
+
569
+
570
+        // Note $inlinecount=allpages means include the total count regardless of paging..so we set the counts first
571
+        // regardless if POData does the paging or not.
572
+        if ($request->queryType == QueryType::ENTITIES_WITH_COUNT) {
573
+            if ($this->providers->handlesOrderedPaging()) {
574
+                $request->setCountValue($result->count);
575
+            } else {
576
+                $request->setCountValue(count($result->results));
577
+            }
578
+        }
579
+
580
+        //Have POData perform paging if necessary
581
+        if (!$this->providers->handlesOrderedPaging() && !empty($result->results)) {
582
+            $result->results = $this->performPaging($request, $result->results);
583
+        }
584
+
585
+        //a bit surprising, but $skip and $top affects $count so update it here, not above
586
+        //IE  data.svc/Collection/$count?$top=10 returns 10 even if Collection has 11+ entries
587
+        if ($request->queryType == QueryType::COUNT) {
588
+            if ($this->providers->handlesOrderedPaging()) {
589
+                $request->setCountValue($result->count);
590
+            } else {
591
+                $request->setCountValue(count($result->results));
592
+            }
593
+        }
594
+
595
+        $segment->setResult($result->results);
596
+    }
597
+
598
+    /**
599
+     * If the provider does not perform the paging (ordering, top, skip) then this method does it
600
+     *
601
+     * @param array $result
602
+     * @return array
603
+     */
604
+    private function performPaging(RequestDescription $request, array $result)
605
+    {
606
+        //Apply (implicit and explicit) $orderby option
607
+        $internalOrderByInfo = $request->getInternalOrderByInfo();
608
+        if (!is_null($internalOrderByInfo)) {
609
+            $orderByFunction = $internalOrderByInfo->getSorterFunction()->getReference();
610
+            usort($result, $orderByFunction);
611
+        }
612
+
613
+        //Apply $skiptoken option
614
+        $internalSkipTokenInfo = $request->getInternalSkipTokenInfo();
615
+        if (!is_null($internalSkipTokenInfo)) {
616
+            $matchingIndex = $internalSkipTokenInfo->getIndexOfFirstEntryInTheNextPage($result);
617
+            $result = array_slice($result, $matchingIndex);
618
+        }
619
+
620
+        //Apply $top and $skip option
621
+        if (!empty($result)) {
622
+            $top = $request->getTopCount();
623
+            $skip = $request->getSkipCount();
624
+            if (is_null($skip)) {
625
+                $skip = 0;
626
+            }
627
+
628
+            $result = array_slice($result, $skip, $top);
629
+        }
630
+
631
+        return $result;
632
+    }
633
+
634
+
635
+    /**
636
+     * Perform expansion.
637
+     *
638
+     * @return void
639
+     */
640
+    private function handleExpansion(RequestDescription $request)
641
+    {
642
+        $node = $request->getRootProjectionNode();
643
+        if (!is_null($node) && $node->isExpansionSpecified()) {
644
+            $result = $request->getTargetResult();
645
+            if (!is_null($result) || is_iterable($result) && !empty($result)) {
646
+                $needPop = $this->_pushSegmentForRoot($request);
647
+                $this->_executeExpansion($request, $result);
648
+                $this->_popSegment($needPop);
649
+            }
650
+        }
651
+    }
652
+
653
+    /**
654
+     * Execute queries for expansion.
655
+     *
656
+     * @param array(mixed)/mixed $result Resource(s) whose navigation properties needs to be expanded.
657
+     *
658
+     *
659
+     * @return void
660
+     */
661
+    private function _executeExpansion(RequestDescription $request, $result)
662
+    {
663
+        $expandedProjectionNodes = $this->_getExpandedProjectionNodes($request);
664
+        foreach ($expandedProjectionNodes as $expandedProjectionNode) {
665
+            $isCollection = $expandedProjectionNode->getResourceProperty()->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE;
666
+            $projectedProperty = $expandedProjectionNode->getResourceProperty();
667
+            $expandedPropertyName = $projectedProperty->getName();
668
+            $currentResourceSet = $this->_getCurrentResourceSetWrapper($request)->getResourceSet();
669
+            $currentResourceSetType = $currentResourceSet->getResourceType()->getInstanceType();
670
+            $expandedPropertyReflection = $currentResourceSetType->getProperty($expandedPropertyName);
671
+            $expandedPropertyReflection->setAccessible(true);
672
+            $resourceSetOfProjectedProperty = $expandedProjectionNode->getResourceSetWrapper()->getResourceSet();
673
+
674
+            if (is_iterable($result)) {
675
+                foreach ($result as $entry) {
676
+                    // Check for null entry
677
+                    if ($isCollection) {
678
+                        $result1 = $this->providers->getRelatedResourceSet(
679
+                            QueryType::ENTITIES, //it's always entities for an expansion
680
+                            $currentResourceSet,
681
+                            $entry,
682
+                            $resourceSetOfProjectedProperty,
683
+                            $projectedProperty,
684
+                            null, // $filter
685
+                            null, // $orderby
686
+                            null, // $top
687
+                            null  // $skip
688
+                        )->results;
689
+                        if (!empty($result1)) {
690
+                            $internalOrderByInfo = $expandedProjectionNode->getInternalOrderByInfo();
691
+                            /*if (!is_null($internalOrderByInfo)) {
692 692
 								$orderByFunction = $internalOrderByInfo->getSorterFunction()->getReference();
693 693
 								usort($result1, $orderByFunction);
694 694
 								unset($internalOrderByInfo);
@@ -698,60 +698,60 @@  discard block
 block discarded – undo
698 698
 								}
699 699
 							}*/
700 700
 
701
-							$expandedPropertyReflection->setValue($entry, $result1);
702
-							// $entry->$expandedPropertyName = $result1;
703
-
704
-							$projectedProperty = $expandedProjectionNode->getResourceProperty();
705
-							$needPop = $this->_pushSegmentForNavigationProperty(
706
-								$request,
707
-								$projectedProperty
708
-							);
709
-							$this->_executeExpansion($request, $result1);
710
-							$this->_popSegment($needPop);
711
-						} else {
712
-							$expandedPropertyReflection->setValue($entry, array());
713
-						}
714
-					} else {
715
-						$result1 = $this->providers->getRelatedResourceReference(
716
-							$currentResourceSet,
717
-							$entry,
718
-							$resourceSetOfProjectedProperty,
719
-							$projectedProperty
720
-						);
721
-
722
-						$expandedPropertyReflection->setValue($entry, $result1);
723
-						// $entry->$expandedPropertyName = $result1;
724
-
725
-						if (!is_null($result1)) {
726
-							$projectedProperty3 = $expandedProjectionNode->getResourceProperty();
727
-							$needPop = $this->_pushSegmentForNavigationProperty(
728
-								$request,
729
-								$projectedProperty3
730
-							);
731
-							$this->_executeExpansion($request, $result1);
732
-							$this->_popSegment($needPop);
733
-						}
734
-					}
735
-				}
736
-			} else {
737
-				if ($isCollection) {
738
-					$currentResourceSet2 = $this->_getCurrentResourceSetWrapper($request)->getResourceSet();
739
-					$resourceSetOfProjectedProperty2 = $expandedProjectionNode->getResourceSetWrapper()->getResourceSet();
740
-					$projectedProperty4 = $expandedProjectionNode->getResourceProperty();
741
-					$result1 = $this->providers->getRelatedResourceSet(
742
-						QueryType::ENTITIES, //it's always entities for an expansion
743
-						$currentResourceSet2,
744
-						$result,
745
-						$resourceSetOfProjectedProperty2,
746
-						$projectedProperty4,
747
-						null, // $filter
748
-						null, // $orderby
749
-						null, // $top
750
-						null  // $skip
751
-					)->results;
752
-					if (!empty($result1)) {
753
-						$internalOrderByInfo = $expandedProjectionNode->getInternalOrderByInfo();
754
-						/*
701
+                            $expandedPropertyReflection->setValue($entry, $result1);
702
+                            // $entry->$expandedPropertyName = $result1;
703
+
704
+                            $projectedProperty = $expandedProjectionNode->getResourceProperty();
705
+                            $needPop = $this->_pushSegmentForNavigationProperty(
706
+                                $request,
707
+                                $projectedProperty
708
+                            );
709
+                            $this->_executeExpansion($request, $result1);
710
+                            $this->_popSegment($needPop);
711
+                        } else {
712
+                            $expandedPropertyReflection->setValue($entry, array());
713
+                        }
714
+                    } else {
715
+                        $result1 = $this->providers->getRelatedResourceReference(
716
+                            $currentResourceSet,
717
+                            $entry,
718
+                            $resourceSetOfProjectedProperty,
719
+                            $projectedProperty
720
+                        );
721
+
722
+                        $expandedPropertyReflection->setValue($entry, $result1);
723
+                        // $entry->$expandedPropertyName = $result1;
724
+
725
+                        if (!is_null($result1)) {
726
+                            $projectedProperty3 = $expandedProjectionNode->getResourceProperty();
727
+                            $needPop = $this->_pushSegmentForNavigationProperty(
728
+                                $request,
729
+                                $projectedProperty3
730
+                            );
731
+                            $this->_executeExpansion($request, $result1);
732
+                            $this->_popSegment($needPop);
733
+                        }
734
+                    }
735
+                }
736
+            } else {
737
+                if ($isCollection) {
738
+                    $currentResourceSet2 = $this->_getCurrentResourceSetWrapper($request)->getResourceSet();
739
+                    $resourceSetOfProjectedProperty2 = $expandedProjectionNode->getResourceSetWrapper()->getResourceSet();
740
+                    $projectedProperty4 = $expandedProjectionNode->getResourceProperty();
741
+                    $result1 = $this->providers->getRelatedResourceSet(
742
+                        QueryType::ENTITIES, //it's always entities for an expansion
743
+                        $currentResourceSet2,
744
+                        $result,
745
+                        $resourceSetOfProjectedProperty2,
746
+                        $projectedProperty4,
747
+                        null, // $filter
748
+                        null, // $orderby
749
+                        null, // $top
750
+                        null  // $skip
751
+                    )->results;
752
+                    if (!empty($result1)) {
753
+                        $internalOrderByInfo = $expandedProjectionNode->getInternalOrderByInfo();
754
+                        /*
755 755
 						if (!is_null($internalOrderByInfo)) {
756 756
 							$orderByFunction = $internalOrderByInfo->getSorterFunction()->getReference();
757 757
 							usort($result1, $orderByFunction);
@@ -763,238 +763,238 @@  discard block
 block discarded – undo
763 763
 						}
764 764
 						*/
765 765
 
766
-						$result->$expandedPropertyName = $result1;
767
-						$projectedProperty7 = $expandedProjectionNode->getResourceProperty();
768
-						$needPop = $this->_pushSegmentForNavigationProperty(
769
-							$request,
770
-							$projectedProperty7
771
-						);
772
-						$this->_executeExpansion($request, $result1);
773
-						$this->_popSegment($needPop);
774
-					} else {
775
-						$result->$expandedPropertyName = array();
776
-					}
777
-				} else {
778
-					$currentResourceSet3 = $this->_getCurrentResourceSetWrapper($request)->getResourceSet();
779
-					$resourceSetOfProjectedProperty3 = $expandedProjectionNode->getResourceSetWrapper()->getResourceSet();
780
-					$projectedProperty5 = $expandedProjectionNode->getResourceProperty();
781
-					$result1 = $this->providers->getRelatedResourceReference(
782
-						$currentResourceSet3,
783
-						$result,
784
-						$resourceSetOfProjectedProperty3,
785
-						$projectedProperty5
786
-					);
787
-					$result->$expandedPropertyName = $result1;
788
-					if (!is_null($result1)) {
789
-						$projectedProperty6 = $expandedProjectionNode->getResourceProperty();
790
-						$needPop = $this->_pushSegmentForNavigationProperty(
791
-							$request,
792
-							$projectedProperty6
793
-						);
794
-						$this->_executeExpansion($request, $result1);
795
-						$this->_popSegment($needPop);
796
-					}
797
-				}
798
-			}
799
-		}
800
-	}
801
-
802
-	/**
803
-	 * Resource set wrapper for the resource being retireved.
804
-	 *
805
-	 * @return ResourceSetWrapper
806
-	 */
807
-	private function _getCurrentResourceSetWrapper(RequestDescription $request)
808
-	{
809
-		$count = count($this->_segmentResourceSetWrappers);
810
-		if ($count == 0) {
811
-			return $request->getTargetResourceSetWrapper();
812
-		} else {
813
-			return $this->_segmentResourceSetWrappers[$count - 1];
814
-		}
815
-	}
816
-
817
-	/**
818
-	 * Pushes a segment for the root of the tree
819
-	 * Note: Calls to this method should be balanced with calls to popSegment.
820
-	 *
821
-	 * @return bool true if the segment was pushed, false otherwise.
822
-	 */
823
-	private function _pushSegmentForRoot($request)
824
-	{
825
-		$segmentName = $request->getContainerName();
826
-		$segmentResourceSetWrapper
827
-			= $request->getTargetResourceSetWrapper();
828
-		return $this->_pushSegment($request, $segmentName, $segmentResourceSetWrapper);
829
-	}
830
-
831
-	/**
832
-	 * Pushes a segment for the current navigation property being written out.
833
-	 * Note: Refer 'ObjectModelSerializerNotes.txt' for more details about
834
-	 * 'Segment Stack' and this method.
835
-	 * Note: Calls to this method should be balanced with calls to popSegment.
836
-	 *
837
-	 * @param ResourceProperty &$resourceProperty Current navigation property
838
-	 *                                            being written out
839
-	 *
840
-	 * @return bool true if a segment was pushed, false otherwise
841
-	 *
842
-	 * @throws InvalidOperationException If this function invoked with non-navigation
843
-	 *                                   property instance.
844
-	 */
845
-	private function _pushSegmentForNavigationProperty(RequestDescription $request, ResourceProperty &$resourceProperty)
846
-	{
847
-		if ($resourceProperty->getTypeKind() == ResourceTypeKind::ENTITY) {
848
-			$this->assert(
849
-				!empty($this->_segmentNames),
850
-				'!is_empty($this->_segmentNames'
851
-			);
852
-			$currentResourceSetWrapper = $this->_getCurrentResourceSetWrapper($request);
853
-			$currentResourceType = $currentResourceSetWrapper->getResourceType();
854
-			$currentResourceSetWrapper = $this->service
855
-				->getProvidersWrapper()
856
-				->getResourceSetWrapperForNavigationProperty(
857
-					$currentResourceSetWrapper,
858
-					$currentResourceType,
859
-					$resourceProperty
860
-				);
861
-
862
-			$this->assert(
863
-				!is_null($currentResourceSetWrapper),
864
-				'!null($currentResourceSetWrapper)'
865
-			);
866
-			return $this->_pushSegment(
867
-				$request,
868
-				$resourceProperty->getName(),
869
-				$currentResourceSetWrapper
870
-			);
871
-		} else {
872
-			throw new InvalidOperationException(
873
-				'pushSegmentForNavigationProperty should not be called with non-entity type'
874
-			);
875
-		}
876
-	}
877
-
878
-	/**
879
-	 * Gets collection of expanded projection nodes under the current node.
880
-	 *
881
-	 * @return ExpandedProjectionNode[] List of nodes describing expansions for the current segment
882
-	 *
883
-	 */
884
-	private function _getExpandedProjectionNodes($request)
885
-	{
886
-		$expandedProjectionNode = $this->_getCurrentExpandedProjectionNode($request);
887
-		$expandedProjectionNodes = array();
888
-		if (!is_null($expandedProjectionNode)) {
889
-			foreach ($expandedProjectionNode->getChildNodes() as $node) {
890
-				if ($node instanceof ExpandedProjectionNode) {
891
-					$expandedProjectionNodes[] = $node;
892
-				}
893
-			}
894
-		}
895
-
896
-		return $expandedProjectionNodes;
897
-	}
898
-
899
-	/**
900
-	 * Find a 'ExpandedProjectionNode' instance in the projection tree
901
-	 * which describes the current segment.
902
-	 *
903
-	 * @return ExpandedProjectionNode|null
904
-	 */
905
-	private function _getCurrentExpandedProjectionNode($request)
906
-	{
907
-		$expandedProjectionNode
908
-			= $request->getRootProjectionNode();
909
-		if (!is_null($expandedProjectionNode)) {
910
-			$depth = count($this->_segmentNames);
911
-			if ($depth != 0) {
912
-				for ($i = 1; $i < $depth; $i++) {
913
-					$expandedProjectionNode
914
-						= $expandedProjectionNode->findNode($this->_segmentNames[$i]);
915
-						$this->assert(
916
-							!is_null($expandedProjectionNode),
917
-							'!is_null($expandedProjectionNode)'
918
-						);
919
-						$this->assert(
920
-							$expandedProjectionNode instanceof ExpandedProjectionNode,
921
-							'$expandedProjectionNode instanceof ExpandedProjectionNode'
922
-						);
923
-				}
924
-			}
925
-		}
926
-
927
-		return $expandedProjectionNode;
928
-	}
929
-
930
-	/**
931
-	 * Pushes information about the segment whose instance is going to be
932
-	 * retrieved from the IDSQP implementation
933
-	 * Note: Calls to this method should be balanced with calls to popSegment.
934
-	 *
935
-	 * @param string             $segmentName         Name of segment to push.
936
-	 * @param ResourceSetWrapper &$resourceSetWrapper The resource set wrapper
937
-	 *                                                to push.
938
-	 *
939
-	 * @return bool true if the segment was push, false otherwise
940
-	 */
941
-	private function _pushSegment($request, $segmentName, ResourceSetWrapper &$resourceSetWrapper)
942
-	{
943
-		$rootProjectionNode = $request->getRootProjectionNode();
944
-		if (!is_null($rootProjectionNode)
945
-			&& $rootProjectionNode->isExpansionSpecified()
946
-		) {
947
-			array_push($this->_segmentNames, $segmentName);
948
-			array_push($this->_segmentResourceSetWrappers, $resourceSetWrapper);
949
-			return true;
950
-		}
951
-
952
-		return false;
953
-	}
954
-
955
-	/**
956
-	 * Pops segment information from the 'Segment Stack'
957
-	 * Note: Calls to this method should be balanced with previous calls
958
-	 * to _pushSegment.
959
-	 *
960
-	 * @param boolean $needPop Is a pop required. Only true if last push
961
-	 *                         was successful.
962
-	 *
963
-	 * @return void
964
-	 *
965
-	 * @throws InvalidOperationException If found un-balanced call
966
-	 *                                   with _pushSegment
967
-	 */
968
-	private function _popSegment($needPop)
969
-	{
970
-		if ($needPop) {
971
-			if (!empty($this->_segmentNames)) {
972
-				array_pop($this->_segmentNames);
973
-				array_pop($this->_segmentResourceSetWrappers);
974
-			} else {
975
-				throw new InvalidOperationException(
976
-					'Found non-balanced call to _pushSegment and popSegment'
977
-				);
978
-			}
979
-		}
980
-	}
981
-
982
-	/**
983
-	 * Assert that the given condition is true.
984
-	 *
985
-	 * @param boolean $condition         Constion to assert.
986
-	 * @param string  $conditionAsString Message to show incase assertion fails.
987
-	 *
988
-	 * @return void
989
-	 *
990
-	 * @throws InvalidOperationException
991
-	 */
992
-	protected function assert($condition, $conditionAsString)
993
-	{
994
-		if (!$condition) {
995
-			throw new InvalidOperationException(
996
-				"Unexpected state, expecting $conditionAsString"
997
-			);
998
-		}
999
-	}
766
+                        $result->$expandedPropertyName = $result1;
767
+                        $projectedProperty7 = $expandedProjectionNode->getResourceProperty();
768
+                        $needPop = $this->_pushSegmentForNavigationProperty(
769
+                            $request,
770
+                            $projectedProperty7
771
+                        );
772
+                        $this->_executeExpansion($request, $result1);
773
+                        $this->_popSegment($needPop);
774
+                    } else {
775
+                        $result->$expandedPropertyName = array();
776
+                    }
777
+                } else {
778
+                    $currentResourceSet3 = $this->_getCurrentResourceSetWrapper($request)->getResourceSet();
779
+                    $resourceSetOfProjectedProperty3 = $expandedProjectionNode->getResourceSetWrapper()->getResourceSet();
780
+                    $projectedProperty5 = $expandedProjectionNode->getResourceProperty();
781
+                    $result1 = $this->providers->getRelatedResourceReference(
782
+                        $currentResourceSet3,
783
+                        $result,
784
+                        $resourceSetOfProjectedProperty3,
785
+                        $projectedProperty5
786
+                    );
787
+                    $result->$expandedPropertyName = $result1;
788
+                    if (!is_null($result1)) {
789
+                        $projectedProperty6 = $expandedProjectionNode->getResourceProperty();
790
+                        $needPop = $this->_pushSegmentForNavigationProperty(
791
+                            $request,
792
+                            $projectedProperty6
793
+                        );
794
+                        $this->_executeExpansion($request, $result1);
795
+                        $this->_popSegment($needPop);
796
+                    }
797
+                }
798
+            }
799
+        }
800
+    }
801
+
802
+    /**
803
+     * Resource set wrapper for the resource being retireved.
804
+     *
805
+     * @return ResourceSetWrapper
806
+     */
807
+    private function _getCurrentResourceSetWrapper(RequestDescription $request)
808
+    {
809
+        $count = count($this->_segmentResourceSetWrappers);
810
+        if ($count == 0) {
811
+            return $request->getTargetResourceSetWrapper();
812
+        } else {
813
+            return $this->_segmentResourceSetWrappers[$count - 1];
814
+        }
815
+    }
816
+
817
+    /**
818
+     * Pushes a segment for the root of the tree
819
+     * Note: Calls to this method should be balanced with calls to popSegment.
820
+     *
821
+     * @return bool true if the segment was pushed, false otherwise.
822
+     */
823
+    private function _pushSegmentForRoot($request)
824
+    {
825
+        $segmentName = $request->getContainerName();
826
+        $segmentResourceSetWrapper
827
+            = $request->getTargetResourceSetWrapper();
828
+        return $this->_pushSegment($request, $segmentName, $segmentResourceSetWrapper);
829
+    }
830
+
831
+    /**
832
+     * Pushes a segment for the current navigation property being written out.
833
+     * Note: Refer 'ObjectModelSerializerNotes.txt' for more details about
834
+     * 'Segment Stack' and this method.
835
+     * Note: Calls to this method should be balanced with calls to popSegment.
836
+     *
837
+     * @param ResourceProperty &$resourceProperty Current navigation property
838
+     *                                            being written out
839
+     *
840
+     * @return bool true if a segment was pushed, false otherwise
841
+     *
842
+     * @throws InvalidOperationException If this function invoked with non-navigation
843
+     *                                   property instance.
844
+     */
845
+    private function _pushSegmentForNavigationProperty(RequestDescription $request, ResourceProperty &$resourceProperty)
846
+    {
847
+        if ($resourceProperty->getTypeKind() == ResourceTypeKind::ENTITY) {
848
+            $this->assert(
849
+                !empty($this->_segmentNames),
850
+                '!is_empty($this->_segmentNames'
851
+            );
852
+            $currentResourceSetWrapper = $this->_getCurrentResourceSetWrapper($request);
853
+            $currentResourceType = $currentResourceSetWrapper->getResourceType();
854
+            $currentResourceSetWrapper = $this->service
855
+                ->getProvidersWrapper()
856
+                ->getResourceSetWrapperForNavigationProperty(
857
+                    $currentResourceSetWrapper,
858
+                    $currentResourceType,
859
+                    $resourceProperty
860
+                );
861
+
862
+            $this->assert(
863
+                !is_null($currentResourceSetWrapper),
864
+                '!null($currentResourceSetWrapper)'
865
+            );
866
+            return $this->_pushSegment(
867
+                $request,
868
+                $resourceProperty->getName(),
869
+                $currentResourceSetWrapper
870
+            );
871
+        } else {
872
+            throw new InvalidOperationException(
873
+                'pushSegmentForNavigationProperty should not be called with non-entity type'
874
+            );
875
+        }
876
+    }
877
+
878
+    /**
879
+     * Gets collection of expanded projection nodes under the current node.
880
+     *
881
+     * @return ExpandedProjectionNode[] List of nodes describing expansions for the current segment
882
+     *
883
+     */
884
+    private function _getExpandedProjectionNodes($request)
885
+    {
886
+        $expandedProjectionNode = $this->_getCurrentExpandedProjectionNode($request);
887
+        $expandedProjectionNodes = array();
888
+        if (!is_null($expandedProjectionNode)) {
889
+            foreach ($expandedProjectionNode->getChildNodes() as $node) {
890
+                if ($node instanceof ExpandedProjectionNode) {
891
+                    $expandedProjectionNodes[] = $node;
892
+                }
893
+            }
894
+        }
895
+
896
+        return $expandedProjectionNodes;
897
+    }
898
+
899
+    /**
900
+     * Find a 'ExpandedProjectionNode' instance in the projection tree
901
+     * which describes the current segment.
902
+     *
903
+     * @return ExpandedProjectionNode|null
904
+     */
905
+    private function _getCurrentExpandedProjectionNode($request)
906
+    {
907
+        $expandedProjectionNode
908
+            = $request->getRootProjectionNode();
909
+        if (!is_null($expandedProjectionNode)) {
910
+            $depth = count($this->_segmentNames);
911
+            if ($depth != 0) {
912
+                for ($i = 1; $i < $depth; $i++) {
913
+                    $expandedProjectionNode
914
+                        = $expandedProjectionNode->findNode($this->_segmentNames[$i]);
915
+                        $this->assert(
916
+                            !is_null($expandedProjectionNode),
917
+                            '!is_null($expandedProjectionNode)'
918
+                        );
919
+                        $this->assert(
920
+                            $expandedProjectionNode instanceof ExpandedProjectionNode,
921
+                            '$expandedProjectionNode instanceof ExpandedProjectionNode'
922
+                        );
923
+                }
924
+            }
925
+        }
926
+
927
+        return $expandedProjectionNode;
928
+    }
929
+
930
+    /**
931
+     * Pushes information about the segment whose instance is going to be
932
+     * retrieved from the IDSQP implementation
933
+     * Note: Calls to this method should be balanced with calls to popSegment.
934
+     *
935
+     * @param string             $segmentName         Name of segment to push.
936
+     * @param ResourceSetWrapper &$resourceSetWrapper The resource set wrapper
937
+     *                                                to push.
938
+     *
939
+     * @return bool true if the segment was push, false otherwise
940
+     */
941
+    private function _pushSegment($request, $segmentName, ResourceSetWrapper &$resourceSetWrapper)
942
+    {
943
+        $rootProjectionNode = $request->getRootProjectionNode();
944
+        if (!is_null($rootProjectionNode)
945
+            && $rootProjectionNode->isExpansionSpecified()
946
+        ) {
947
+            array_push($this->_segmentNames, $segmentName);
948
+            array_push($this->_segmentResourceSetWrappers, $resourceSetWrapper);
949
+            return true;
950
+        }
951
+
952
+        return false;
953
+    }
954
+
955
+    /**
956
+     * Pops segment information from the 'Segment Stack'
957
+     * Note: Calls to this method should be balanced with previous calls
958
+     * to _pushSegment.
959
+     *
960
+     * @param boolean $needPop Is a pop required. Only true if last push
961
+     *                         was successful.
962
+     *
963
+     * @return void
964
+     *
965
+     * @throws InvalidOperationException If found un-balanced call
966
+     *                                   with _pushSegment
967
+     */
968
+    private function _popSegment($needPop)
969
+    {
970
+        if ($needPop) {
971
+            if (!empty($this->_segmentNames)) {
972
+                array_pop($this->_segmentNames);
973
+                array_pop($this->_segmentResourceSetWrappers);
974
+            } else {
975
+                throw new InvalidOperationException(
976
+                    'Found non-balanced call to _pushSegment and popSegment'
977
+                );
978
+            }
979
+        }
980
+    }
981
+
982
+    /**
983
+     * Assert that the given condition is true.
984
+     *
985
+     * @param boolean $condition         Constion to assert.
986
+     * @param string  $conditionAsString Message to show incase assertion fails.
987
+     *
988
+     * @return void
989
+     *
990
+     * @throws InvalidOperationException
991
+     */
992
+    protected function assert($condition, $conditionAsString)
993
+    {
994
+        if (!$condition) {
995
+            throw new InvalidOperationException(
996
+                "Unexpected state, expecting $conditionAsString"
997
+            );
998
+        }
999
+    }
1000 1000
 }
Please login to merge, or discard this patch.