Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like ObjectModelSerializerBase often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ObjectModelSerializerBase, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
22 | class ObjectModelSerializerBase |
||
23 | { |
||
24 | /** |
||
25 | * The service implementation. |
||
26 | * |
||
27 | * @var IService |
||
28 | */ |
||
29 | protected $service; |
||
30 | |||
31 | /** |
||
32 | * Request description instance describes OData request the |
||
33 | * the client has submitted and result of the request. |
||
34 | * |
||
35 | * @var RequestDescription |
||
36 | */ |
||
37 | protected $request; |
||
38 | |||
39 | /** |
||
40 | * Collection of complex type instances used for cycle detection. |
||
41 | * |
||
42 | * @var array |
||
43 | */ |
||
44 | protected $complexTypeInstanceCollection; |
||
45 | |||
46 | /** |
||
47 | * Absolute service Uri. |
||
48 | * |
||
49 | * @var string |
||
50 | */ |
||
51 | protected $absoluteServiceUri; |
||
52 | |||
53 | /** |
||
54 | * Absolute service Uri with slash. |
||
55 | * |
||
56 | * @var string |
||
57 | */ |
||
58 | protected $absoluteServiceUriWithSlash; |
||
59 | |||
60 | /** |
||
61 | * Holds reference to segment stack being processed |
||
62 | * |
||
63 | * @var SegmentStack |
||
64 | */ |
||
65 | protected $stack; |
||
66 | |||
67 | /** |
||
68 | * @param IService $service Reference to the data service instance |
||
69 | * @param RequestDescription $request Type instance describing the client submitted request |
||
70 | */ |
||
71 | protected function __construct(IService $service, RequestDescription $request = null) |
||
72 | { |
||
73 | $this->service = $service; |
||
74 | $this->request = $request; |
||
75 | $this->absoluteServiceUri = $service->getHost()->getAbsoluteServiceUri()->getUrlAsString(); |
||
76 | $this->absoluteServiceUriWithSlash = rtrim($this->absoluteServiceUri, '/') . '/'; |
||
77 | $this->stack = new SegmentStack($request); |
||
78 | $this->complexTypeInstanceCollection = array(); |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * Gets reference to the request submitted by client. |
||
83 | * |
||
84 | * @return RequestDescription |
||
85 | */ |
||
86 | public function getRequest() |
||
87 | { |
||
88 | assert(null != $this->request, "Request not yet set"); |
||
89 | return $this->request; |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * Sets reference to the request submitted by client. |
||
94 | * @param RequestDescription $request |
||
95 | * |
||
96 | */ |
||
97 | public function setRequest(RequestDescription $request) |
||
98 | { |
||
99 | $this->request = $request; |
||
100 | $this->stack->setRequest($request); |
||
101 | } |
||
102 | |||
103 | /** |
||
104 | * Gets the data service instance |
||
105 | * |
||
106 | * @return IService |
||
107 | */ |
||
108 | public function getService() |
||
109 | { |
||
110 | return $this->service; |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * Gets the segment stack instance |
||
115 | * |
||
116 | * @return SegmentStack |
||
117 | */ |
||
118 | public function getStack() |
||
119 | { |
||
120 | return $this->stack; |
||
121 | } |
||
122 | |||
123 | /** |
||
124 | * Builds the key for the given entity instance. |
||
125 | * Note: The generated key can be directly used in the uri, |
||
126 | * this function will perform |
||
127 | * required escaping of characters, for example: |
||
128 | * Ships(ShipName='Antonio%20Moreno%20Taquer%C3%ADa',ShipID=123), |
||
129 | * Note to method caller: Don't do urlencoding on |
||
130 | * return value of this method as it already encoded. |
||
131 | * |
||
132 | * @param mixed $entityInstance Entity instance for which key value needs to be prepared |
||
133 | * @param ResourceType $resourceType Resource type instance containing metadata about the instance |
||
134 | * @param string $containerName Name of the entity set that the entity instance belongs to |
||
135 | * |
||
136 | * @return string Key for the given resource, with values encoded for use in a URI |
||
137 | */ |
||
138 | protected function getEntryInstanceKey($entityInstance, ResourceType $resourceType, $containerName) |
||
139 | { |
||
140 | $keyProperties = $resourceType->getKeyProperties(); |
||
141 | assert(count($keyProperties) != 0, 'count($keyProperties) == 0'); |
||
142 | $keyString = $containerName . '('; |
||
143 | $comma = null; |
||
144 | foreach ($keyProperties as $keyName => $resourceProperty) { |
||
145 | $keyType = $resourceProperty->getInstanceType(); |
||
146 | assert($keyType instanceof IType, '$keyType not instanceof IType'); |
||
147 | $keyValue = $this->getPropertyValue($entityInstance, $resourceType, $resourceProperty); |
||
148 | if (is_null($keyValue)) { |
||
149 | throw ODataException::createInternalServerError( |
||
150 | Messages::badQueryNullKeysAreNotSupported($resourceType->getName(), $keyName) |
||
151 | ); |
||
152 | } |
||
153 | |||
154 | $keyValue = $keyType->convertToOData($keyValue); |
||
155 | $keyString .= $comma . $keyName . '=' . $keyValue; |
||
156 | $comma = ','; |
||
157 | } |
||
158 | |||
159 | $keyString .= ')'; |
||
160 | |||
161 | return $keyString; |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Get the value of a given property from an instance. |
||
166 | * |
||
167 | * @param mixed $entity Instance of a type which contains this property |
||
168 | * @param ResourceType $resourceType Resource type instance containing metadata about the instance |
||
169 | * @param ResourceProperty $resourceProperty Resource property instance containing metadata about the property whose value to be retrieved |
||
170 | * |
||
171 | * @return mixed The value of the given property |
||
172 | * |
||
173 | * @throws ODataException If reflection exception occurred while trying to access the property |
||
174 | */ |
||
175 | protected function getPropertyValue($entity, ResourceType $resourceType, ResourceProperty $resourceProperty) |
||
176 | { |
||
177 | try { |
||
178 | return \POData\Common\ReflectionHandler::getProperty($entity, $resourceProperty->getName()); |
||
179 | } catch (\ReflectionException $reflectionException) { |
||
180 | throw ODataException::createInternalServerError( |
||
181 | Messages::objectModelSerializerFailedToAccessProperty( |
||
182 | $resourceProperty->getName(), |
||
183 | $resourceType->getName() |
||
184 | ) |
||
185 | ); |
||
186 | } |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * Resource set wrapper for the resource being serialized. |
||
191 | * |
||
192 | * @return ResourceSetWrapper |
||
193 | */ |
||
194 | protected function getCurrentResourceSetWrapper() |
||
195 | { |
||
196 | $segmentWrappers = $this->getStack()->getSegmentWrappers(); |
||
197 | $count = count($segmentWrappers); |
||
198 | return 0 == $count ? $this->getRequest()->getTargetResourceSetWrapper() : $segmentWrappers[$count - 1]; |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * Whether the current resource set is root resource set. |
||
203 | * |
||
204 | * @return bool true if the current resource set root container else |
||
205 | * false |
||
206 | */ |
||
207 | protected function isRootResourceSet() |
||
208 | { |
||
209 | $segmentWrappers = $this->getStack()->getSegmentWrappers(); |
||
210 | return empty($segmentWrappers) || 1 == count($segmentWrappers); |
||
211 | } |
||
212 | |||
213 | /** |
||
214 | * Returns the etag for the given resource. |
||
215 | * |
||
216 | * @param mixed $entryObject Resource for which etag value |
||
217 | * needs to be returned |
||
218 | * @param ResourceType $resourceType Resource type of the $entryObject |
||
219 | * |
||
220 | * @return string|null ETag value for the given resource |
||
221 | * (with values encoded for use in a URI) |
||
222 | * if there are etag properties, NULL if there is no etag property |
||
223 | */ |
||
224 | protected function getETagForEntry($entryObject, ResourceType $resourceType) |
||
225 | { |
||
226 | $eTag = null; |
||
227 | $comma = null; |
||
228 | foreach ($resourceType->getETagProperties() as $eTagProperty) { |
||
229 | $type = $eTagProperty->getInstanceType(); |
||
230 | assert(!is_null($type) && $type instanceof IType, 'is_null($type) || $type not instanceof IType'); |
||
231 | $value = $this->getPropertyValue($entryObject, $resourceType, $eTagProperty); |
||
232 | if (is_null($value)) { |
||
233 | $eTag = $eTag . $comma . 'null'; |
||
234 | } else { |
||
235 | $eTag = $eTag . $comma . $type->convertToOData($value); |
||
236 | } |
||
237 | |||
238 | $comma = ','; |
||
239 | } |
||
240 | |||
241 | View Code Duplication | if (!is_null($eTag)) { |
|
|
|||
242 | // If eTag is made up of datetime or string properties then the above |
||
243 | // IType::converToOData will perform utf8 and url encode. But we don't |
||
244 | // want this for eTag value. |
||
245 | $eTag = urldecode(utf8_decode($eTag)); |
||
246 | |||
247 | return ODataConstants::HTTP_WEAK_ETAG_PREFIX . rtrim($eTag, ',') . '"'; |
||
248 | } |
||
249 | |||
250 | return null; |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * Pushes a segment for the root of the tree being written out |
||
255 | * Note: Refer 'ObjectModelSerializerNotes.txt' for more details about |
||
256 | * 'Segment Stack' and this method. |
||
257 | * Note: Calls to this method should be balanced with calls to popSegment. |
||
258 | * |
||
259 | * @return bool true if the segment was pushed, false otherwise |
||
260 | */ |
||
261 | protected function pushSegmentForRoot() |
||
262 | { |
||
263 | $segmentName = $this->getRequest()->getContainerName(); |
||
264 | $segmentResourceSetWrapper = $this->getRequest()->getTargetResourceSetWrapper(); |
||
265 | assert(null != $segmentResourceSetWrapper, "Segment resource set wrapper must not be null"); |
||
266 | |||
267 | return $this->_pushSegment($segmentName, $segmentResourceSetWrapper); |
||
268 | } |
||
269 | |||
270 | /** |
||
271 | * Pushes a segment for the current navigation property being written out. |
||
272 | * Note: Refer 'ObjectModelSerializerNotes.txt' for more details about |
||
273 | * 'Segment Stack' and this method. |
||
274 | * Note: Calls to this method should be balanced with calls to popSegment. |
||
275 | * |
||
276 | * @param ResourceProperty &$resourceProperty The current navigation property |
||
277 | * being written out |
||
278 | * |
||
279 | * @return bool true if a segment was pushed, false otherwise |
||
280 | * |
||
281 | * @throws InvalidOperationException If this function invoked with non-navigation |
||
282 | * property instance |
||
283 | */ |
||
284 | View Code Duplication | protected function pushSegmentForNavigationProperty(ResourceProperty & $resourceProperty) |
|
285 | { |
||
286 | if (ResourceTypeKind::ENTITY == $resourceProperty->getTypeKind()) { |
||
287 | assert(!empty($this->getStack()->getSegmentNames()), 'Segment names should not be empty'); |
||
288 | $currentResourceSetWrapper = $this->getCurrentResourceSetWrapper(); |
||
289 | $currentResourceType = $currentResourceSetWrapper->getResourceType(); |
||
290 | $currentResourceSetWrapper = $this->getService() |
||
291 | ->getProvidersWrapper() |
||
292 | ->getResourceSetWrapperForNavigationProperty( |
||
293 | $currentResourceSetWrapper, |
||
294 | $currentResourceType, |
||
295 | $resourceProperty |
||
296 | ); |
||
297 | |||
298 | assert(!is_null($currentResourceSetWrapper), 'is_null($currentResourceSetWrapper)'); |
||
299 | |||
300 | return $this->_pushSegment($resourceProperty->getName(), $currentResourceSetWrapper); |
||
301 | } |
||
302 | throw new InvalidOperationException('pushSegmentForNavigationProperty should not be called with non-entity type'); |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * Gets collection of projection nodes under the current node. |
||
307 | * |
||
308 | * @return ProjectionNode[]|ExpandedProjectionNode[]|null List of nodes |
||
309 | * describing projections for the current segment, If this method returns |
||
310 | * null it means no projections are to be applied and the entire resource |
||
311 | * for the current segment should be serialized, If it returns non-null |
||
312 | * only the properties described by the returned projection segments should |
||
313 | * be serialized |
||
314 | */ |
||
315 | protected function getProjectionNodes() |
||
324 | |||
325 | /** |
||
326 | * Find a 'ExpandedProjectionNode' instance in the projection tree |
||
327 | * which describes the current segment. |
||
328 | * |
||
329 | * @return ExpandedProjectionNode|null |
||
330 | */ |
||
331 | View Code Duplication | protected function getCurrentExpandedProjectionNode() |
|
332 | { |
||
333 | $expandedProjectionNode = $this->getRequest()->getRootProjectionNode(); |
||
334 | if (is_null($expandedProjectionNode)) { |
||
335 | return null; |
||
336 | } else { |
||
337 | $segmentNames = $this->getStack()->getSegmentNames(); |
||
338 | $depth = count($segmentNames); |
||
339 | // $depth == 1 means serialization of root entry |
||
340 | //(the resource identified by resource path) is going on, |
||
341 | //so control won't get into the below for loop. |
||
342 | //we will directly return the root node, |
||
343 | //which is 'ExpandedProjectionNode' |
||
344 | // for resource identified by resource path. |
||
345 | if ($depth != 0) { |
||
346 | for ($i = 1; $i < $depth; ++$i) { |
||
347 | $expandedProjectionNode |
||
348 | = $expandedProjectionNode->findNode($segmentNames[$i]); |
||
349 | assert(!is_null($expandedProjectionNode), 'is_null($expandedProjectionNode)'); |
||
350 | assert( |
||
351 | $expandedProjectionNode instanceof ExpandedProjectionNode, |
||
352 | '$expandedProjectionNode not instanceof ExpandedProjectionNode' |
||
353 | ); |
||
354 | } |
||
355 | } |
||
356 | } |
||
357 | |||
358 | return $expandedProjectionNode; |
||
359 | } |
||
360 | |||
361 | /** |
||
362 | * Check whether to expand a navigation property or not. |
||
363 | * |
||
364 | * @param string $navigationPropertyName Name of naviagtion property in question |
||
365 | * |
||
366 | * @return bool True if the given navigation should be |
||
367 | * explanded otherwise false |
||
368 | */ |
||
369 | protected function shouldExpandSegment($navigationPropertyName) |
||
370 | { |
||
371 | $expandedProjectionNode = $this->getCurrentExpandedProjectionNode(); |
||
372 | if (is_null($expandedProjectionNode)) { |
||
373 | return false; |
||
374 | } |
||
375 | |||
376 | $expandedProjectionNode = $expandedProjectionNode->findNode($navigationPropertyName); |
||
377 | |||
378 | // null is a valid input to an instanceof call as of PHP 5.6 - will always return false |
||
379 | return $expandedProjectionNode instanceof ExpandedProjectionNode; |
||
380 | } |
||
381 | |||
382 | /** |
||
383 | * Pushes information about the segment that is going to be serialized |
||
384 | * to the 'Segment Stack'. |
||
385 | * Note: Refer 'ObjectModelSerializerNotes.txt' for more details about |
||
386 | * 'Segment Stack' and this method. |
||
387 | * Note: Calls to this method should be balanced with calls to popSegment. |
||
388 | * |
||
389 | * @param string $segmentName Name of segment to push |
||
390 | * @param ResourceSetWrapper &$resourceSetWrapper The resource set |
||
391 | * wrapper to push |
||
392 | * |
||
393 | * @return bool true if the segment was push, false otherwise |
||
394 | */ |
||
395 | private function _pushSegment($segmentName, ResourceSetWrapper & $resourceSetWrapper) |
||
396 | { |
||
397 | // Even though there is no expand in the request URI, still we need to push |
||
398 | // the segment information if we need to count |
||
399 | //the number of entities written. |
||
400 | // After serializing each entity we should check the count to see whether |
||
401 | // we serialized more entities than configured |
||
402 | //(page size, maxResultPerCollection). |
||
403 | // But we will not do this check since library is doing paging and never |
||
404 | // accumulate entities more than configured. |
||
405 | |||
406 | return $this->getStack()->pushSegment($segmentName, $resourceSetWrapper); |
||
407 | } |
||
408 | |||
409 | /** |
||
410 | * Get next page link from the given entity instance. |
||
411 | * |
||
412 | * @param mixed &$lastObject Last object serialized to be |
||
413 | * used for generating $skiptoken |
||
414 | * @param string $absoluteUri Absolute response URI |
||
415 | * |
||
416 | * @return ODataLink for the link for next page |
||
417 | */ |
||
418 | protected function getNextLinkUri(&$lastObject, $absoluteUri) |
||
419 | { |
||
420 | $currentExpandedProjectionNode = $this->getCurrentExpandedProjectionNode(); |
||
421 | $internalOrderByInfo = $currentExpandedProjectionNode->getInternalOrderByInfo(); |
||
422 | $skipToken = $internalOrderByInfo->buildSkipTokenValue($lastObject); |
||
423 | assert(!is_null($skipToken), '!is_null($skipToken)'); |
||
424 | $queryParameterString = null; |
||
425 | if ($this->isRootResourceSet()) { |
||
426 | $queryParameterString = $this->getNextPageLinkQueryParametersForRootResourceSet(); |
||
427 | } else { |
||
428 | $queryParameterString = $this->getNextPageLinkQueryParametersForExpandedResourceSet(); |
||
429 | } |
||
430 | |||
431 | $queryParameterString .= '$skip=' . $skipToken; |
||
432 | $odataLink = new ODataLink(); |
||
433 | $odataLink->name = ODataConstants::ATOM_LINK_NEXT_ATTRIBUTE_STRING; |
||
434 | $odataLink->url = rtrim($absoluteUri, '/') . '?' . $queryParameterString; |
||
435 | |||
436 | return $odataLink; |
||
437 | } |
||
438 | |||
439 | /** |
||
440 | * Builds the string corresponding to query parameters for top level results |
||
441 | * (result set identified by the resource path) to be put in next page link. |
||
442 | * |
||
443 | * @return string|null string representing the query parameters in the URI |
||
444 | * query parameter format, NULL if there |
||
445 | * is no query parameters |
||
446 | * required for the next link of top level result set |
||
447 | */ |
||
448 | protected function getNextPageLinkQueryParametersForRootResourceSet() |
||
449 | { |
||
450 | $queryParameterString = null; |
||
451 | foreach ([ODataConstants::HTTPQUERY_STRING_FILTER, |
||
452 | ODataConstants::HTTPQUERY_STRING_EXPAND, |
||
453 | ODataConstants::HTTPQUERY_STRING_ORDERBY, |
||
454 | ODataConstants::HTTPQUERY_STRING_INLINECOUNT, |
||
455 | ODataConstants::HTTPQUERY_STRING_SELECT] as $queryOption) { |
||
456 | $value = $this->getService()->getHost()->getQueryStringItem($queryOption); |
||
457 | if (!is_null($value)) { |
||
458 | if (!is_null($queryParameterString)) { |
||
459 | $queryParameterString = $queryParameterString . '&'; |
||
460 | } |
||
461 | |||
462 | $queryParameterString .= $queryOption . '=' . $value; |
||
463 | } |
||
464 | } |
||
465 | |||
466 | $topCountValue = $this->getRequest()->getTopOptionCount(); |
||
467 | if (!is_null($topCountValue)) { |
||
468 | $remainingCount = $topCountValue - $this->getRequest()->getTopCount(); |
||
469 | if (!is_null($queryParameterString)) { |
||
470 | $queryParameterString .= '&'; |
||
471 | } |
||
472 | |||
473 | $queryParameterString .= ODataConstants::HTTPQUERY_STRING_TOP . '=' . $remainingCount; |
||
474 | } |
||
475 | |||
476 | if (!is_null($queryParameterString)) { |
||
477 | $queryParameterString .= '&'; |
||
478 | } |
||
479 | |||
480 | return $queryParameterString; |
||
481 | } |
||
482 | |||
483 | /** |
||
484 | * Builds the string corresponding to query parameters for current expanded |
||
485 | * results to be put in next page link. |
||
486 | * |
||
487 | * @return string|null string representing the $select and $expand parameters |
||
488 | * in the URI query parameter format, NULL if there is no |
||
489 | * query parameters ($expand and/select) required for the |
||
490 | * next link of expanded result set |
||
491 | */ |
||
492 | protected function getNextPageLinkQueryParametersForExpandedResourceSet() |
||
493 | { |
||
494 | $queryParameterString = null; |
||
495 | $expandedProjectionNode = $this->getCurrentExpandedProjectionNode(); |
||
496 | if (!is_null($expandedProjectionNode)) { |
||
497 | $pathSegments = array(); |
||
498 | $selectionPaths = null; |
||
499 | $expansionPaths = null; |
||
500 | $foundSelections = false; |
||
501 | $foundExpansions = false; |
||
502 | $this->_buildSelectionAndExpansionPathsForNode( |
||
503 | $pathSegments, |
||
504 | $selectionPaths, |
||
505 | $expansionPaths, |
||
506 | $expandedProjectionNode, |
||
507 | $foundSelections, |
||
508 | $foundExpansions |
||
509 | ); |
||
510 | |||
511 | if ($foundSelections && $expandedProjectionNode->canSelectAllProperties()) { |
||
512 | $this->_appendSelectionOrExpandPath($selectionPaths, $pathSegments, '*'); |
||
513 | } |
||
514 | |||
515 | if (!is_null($selectionPaths)) { |
||
516 | $queryParameterString = '$select=' . $selectionPaths; |
||
517 | } |
||
518 | |||
519 | if (!is_null($expansionPaths)) { |
||
520 | if (!is_null($queryParameterString)) { |
||
521 | $queryParameterString .= '&'; |
||
522 | } |
||
523 | |||
524 | $queryParameterString = '$expand=' . $expansionPaths; |
||
525 | } |
||
526 | |||
527 | if (!is_null($queryParameterString)) { |
||
528 | $queryParameterString .= '&'; |
||
529 | } |
||
530 | } |
||
531 | |||
532 | return $queryParameterString; |
||
533 | } |
||
534 | |||
535 | /** |
||
536 | * Wheter next link is needed for the current resource set (feed) |
||
537 | * being serialized. |
||
538 | * |
||
539 | * @param int $resultSetCount Number of entries in the current |
||
540 | * resource set |
||
541 | * |
||
542 | * @return bool true if the feed must have a next page link |
||
543 | */ |
||
544 | protected function needNextPageLink($resultSetCount) |
||
545 | { |
||
546 | $currentResourceSet = $this->getCurrentResourceSetWrapper(); |
||
547 | $recursionLevel = count($this->getStack()->getSegmentNames()); |
||
548 | //$this->assert($recursionLevel != 0, '$recursionLevel != 0'); |
||
549 | $pageSize = $currentResourceSet->getResourceSetPageSize(); |
||
550 | |||
551 | if ($recursionLevel == 1) { |
||
552 | //presence of $top option affect next link for root container |
||
553 | $topValueCount = $this->getRequest()->getTopOptionCount(); |
||
554 | if (!is_null($topValueCount) && ($topValueCount <= $pageSize)) { |
||
555 | return false; |
||
556 | } |
||
557 | } |
||
558 | |||
559 | return $resultSetCount == $pageSize; |
||
560 | } |
||
561 | |||
562 | /** |
||
563 | * Pops segment information from the 'Segment Stack' |
||
564 | * Note: Refer 'ObjectModelSerializerNotes.txt' for more details about |
||
565 | * 'Segment Stack' and this method. |
||
566 | * Note: Calls to this method should be balanced with previous |
||
567 | * calls to _pushSegment. |
||
568 | * |
||
569 | * @param bool $needPop Is a pop required. Only true if last |
||
570 | * push was successful |
||
571 | * |
||
572 | * @throws InvalidOperationException If found un-balanced call with _pushSegment |
||
573 | */ |
||
574 | protected function popSegment($needPop) |
||
575 | { |
||
576 | $this->getStack()->popSegment($needPop); |
||
577 | } |
||
578 | |||
579 | /** |
||
580 | * Recursive metod to build $expand and $select paths for a specified node. |
||
581 | * |
||
582 | * @param string[] &$parentPathSegments Array of path |
||
583 | * segments which leads |
||
584 | * up to (including) |
||
585 | * the segment |
||
586 | * represented by |
||
587 | * $expandedProjectionNode |
||
588 | * @param string[] &$selectionPaths The string which |
||
589 | * holds projection |
||
590 | * path segment |
||
591 | * seperated by comma, |
||
592 | * On return this argument |
||
593 | * will be updated with |
||
594 | * the selection path |
||
595 | * segments under |
||
596 | * this node |
||
597 | * @param string[] &$expansionPaths The string which holds |
||
598 | * expansion path segment |
||
599 | * seperated by comma. |
||
600 | * On return this argument |
||
601 | * will be updated with |
||
602 | * the expand path |
||
603 | * segments under |
||
604 | * this node |
||
605 | * @param ExpandedProjectionNode &$expandedProjectionNode The expanded node for |
||
606 | * which expansion |
||
607 | * and selection path |
||
608 | * to be build |
||
609 | * @param bool &$foundSelections On return, this |
||
610 | * argument will hold |
||
611 | * true if any selection |
||
612 | * defined under this node |
||
613 | * false otherwise |
||
614 | * @param bool &$foundExpansions On return, this |
||
615 | * argument will hold |
||
616 | * true if any expansion |
||
617 | * defined under this node |
||
618 | * false otherwise |
||
619 | * @param bool $foundSelections |
||
620 | * @param bool $foundExpansions |
||
621 | */ |
||
622 | private function _buildSelectionAndExpansionPathsForNode( |
||
689 | |||
690 | /** |
||
691 | * Append the given path to $expand or $select path list. |
||
692 | * |
||
693 | * @param string &$path The $expand or $select path list to which to append the given path |
||
694 | * @param string[] &$parentPathSegments The list of path up to the $segmentToAppend |
||
695 | * @param string $segmentToAppend The last segment of the path |
||
696 | */ |
||
697 | private function _appendSelectionOrExpandPath(&$path, &$parentPathSegments, $segmentToAppend) |
||
709 | } |
||
710 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.