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