/** * Build 'Projection Tree' from the given expand path segments * * @param array(array(string)) $expandPathSegments Collection of expand * paths. * * @return void * * @throws ODataException If any error occurs while processing the expand * path segments. */ private function _buildProjectionTree($expandPathSegments) { foreach ($expandPathSegments as $expandSubPathSegments) { $currentNode = $this->_rootProjectionNode; foreach ($expandSubPathSegments as $expandSubPathSegment) { $resourceSetWrapper = $currentNode->getResourceSetWrapper(); $resourceType = $currentNode->getResourceType(); $resourceProperty = $resourceType->tryResolvePropertyTypeByName($expandSubPathSegment); if (is_null($resourceProperty)) { ODataException::createSyntaxError(Messages::expandProjectionParserPropertyNotFound($resourceType->getFullName(), $expandSubPathSegment, false)); } else { if ($resourceProperty->getTypeKind() != ResourceTypeKind::ENTITY) { ODataException::createBadRequestError(Messages::expandProjectionParserExpandCanOnlyAppliedToEntity($resourceType->getFullName(), $expandSubPathSegment)); } } $resourceSetWrapper = $this->_providerWrapper->getResourceSetWrapperForNavigationProperty($resourceSetWrapper, $resourceType, $resourceProperty); if (is_null($resourceSetWrapper)) { ODataException::createBadRequestError(Messages::badRequestInvalidPropertyNameSpecified($resourceType->getFullName(), $expandSubPathSegment)); } $singleResult = $resourceProperty->isKindOf(ResourcePropertyKind::RESOURCE_REFERENCE); $resourceSetWrapper->checkResourceSetRightsForRead($singleResult); $pageSize = $resourceSetWrapper->getResourceSetPageSize(); $internalOrderByInfo = null; if ($pageSize != 0 && !$singleResult) { $this->_rootProjectionNode->setPagedExpandedResult(true); $rt = $resourceSetWrapper->getResourceType(); //assert($rt != null) $keys = array_keys($rt->getKeyProperties()); //assert(!empty($keys)) $orderBy = null; foreach ($keys as $key) { $orderBy = $orderBy . $key . ', '; } $orderBy = rtrim($orderBy, ', '); try { $internalOrderByInfo = OrderByParser::parseOrderByClause($resourceSetWrapper, $rt, $orderBy, $this->_providerWrapper); } catch (ODataException $odataException) { throw $odataException; } } $node = $currentNode->findNode($expandSubPathSegment); if (is_null($node)) { $maxResultCount = $this->_providerWrapper->getConfiguration()->getMaxResultsPerCollection(); $node = new ExpandedProjectionNode($expandSubPathSegment, $resourceProperty, $resourceSetWrapper, $internalOrderByInfo, null, $pageSize == 0 ? null : $pageSize, $maxResultCount == PHP_INT_MAX ? null : $maxResultCount); $currentNode->addNode($node); } $currentNode = $node; } } }
/** * Create SegmentDescriptors for a set of given segments, optionally * check for rights. * * @param array(string) $segments String array of segments to parse * @param boolean $checkRights Whether to check for rights or not * * @return void * * @throws ODataException Exception incase of any error found while * precessing segments */ private function _createSegmentDescriptors($segments, $checkRights) { if (empty($segments)) { $this->_segmentDescriptors[] = new SegmentDescriptor(); $this->_segmentDescriptors[0]->setTargetKind(RequestTargetKind::SERVICE_DIRECTORY); return; } $segmentCount = count($segments); $identifier = $keyPredicate = null; $this->_extractSegmentIdentifierAndKeyPredicate($segments[0], $identifier, $keyPredicate); $this->_segmentDescriptors[] = $this->_createFirstSegmentDescriptor($identifier, $keyPredicate, $checkRights); $previous = $this->_segmentDescriptors[0]; for ($i = 1; $i < $segmentCount; $i++) { if ($previous->getTargetKind() == RequestTargetKind::METADATA || $previous->getTargetKind() == RequestTargetKind::BATCH || $previous->getTargetKind() == RequestTargetKind::PRIMITIVE_VALUE || $previous->getTargetKind() == RequestTargetKind::BAG || $previous->getTargetKind() == RequestTargetKind::MEDIA_RESOURCE) { ODataException::resourceNotFoundError(Messages::segmentParserMustBeLeafSegment($previous->getIdentifier())); } $identifier = $keyPredicate = null; $this->_extractSegmentIdentifierAndKeyPredicate($segments[$i], $identifier, $keyPredicate); $hasPredicate = !is_null($keyPredicate); $descriptor = null; if ($previous->getTargetKind() == RequestTargetKind::PRIMITIVE) { if ($identifier !== ODataConstants::URI_VALUE_SEGMENT) { ODataException::resourceNotFoundError(Messages::segmentParserOnlyValueSegmentAllowedAfterPrimitivePropertySegment($identifier, $previous->getIdentifier())); } $this->_assertion(!$hasPredicate); $descriptor = SegmentDescriptor::createFrom($previous); $descriptor->setIdentifier(ODataConstants::URI_VALUE_SEGMENT); $descriptor->setTargetKind(RequestTargetKind::PRIMITIVE_VALUE); $descriptor->setSingleResult(true); } else { if (!is_null($previous->getPrevious()) && $previous->getPrevious()->getIdentifier() === ODataConstants::URI_LINK_SEGMENT && $identifier !== ODataConstants::URI_COUNT_SEGMENT) { ODataException::createBadRequestError(Messages::segmentParserNoSegmentAllowedAfterPostLinkSegment($identifier)); } else { if ($previous->getTargetKind() == RequestTargetKind::RESOURCE && $previous->isSingleResult() && $identifier === ODataConstants::URI_LINK_SEGMENT) { $this->_assertion(!$hasPredicate); $descriptor = SegmentDescriptor::createFrom($previous); $descriptor->setIdentifier(ODataConstants::URI_LINK_SEGMENT); $descriptor->setTargetKind(RequestTargetKind::LINK); } else { //Do a sanity check here if ($previous->getTargetKind() != RequestTargetKind::COMPLEX_OBJECT && $previous->getTargetKind() != RequestTargetKind::RESOURCE && $previous->getTargetKind() != RequestTargetKind::LINK) { ODataException::createInternalServerError(Messages::segmentParserInconsistentTargetKindState()); } if (!$previous->isSingleResult() && $identifier !== ODataConstants::URI_COUNT_SEGMENT) { ODataException::createBadRequestError(Messages::segmentParserCannotQueryCollection($previous->getIdentifier())); } $descriptor = new SegmentDescriptor(); $descriptor->setIdentifier($identifier); $descriptor->setTargetSource(RequestTargetSource::PROPERTY); $projectedProperty = $previous->getTargetResourceType()->tryResolvePropertyTypeByName($identifier); $descriptor->setProjectedProperty($projectedProperty); if ($identifier === ODataConstants::URI_COUNT_SEGMENT) { if ($previous->getTargetKind() != RequestTargetKind::RESOURCE) { ODataException::createBadRequestError(Messages::segmentParserCountCannotBeApplied($previous->getIdentifier())); } if ($previous->isSingleResult()) { ODataException::createBadRequestError(Messages::segmentParserCountCannotFollowSingleton($previous->getIdentifier())); } $descriptor->setTargetKind(RequestTargetKind::PRIMITIVE_VALUE); $descriptor->setSingleResult(true); $descriptor->setTargetResourceSetWrapper($previous->getTargetResourceSetWrapper()); $descriptor->setTargetResourceType($previous->getTargetResourceType()); } else { if ($identifier === ODataConstants::URI_VALUE_SEGMENT && $previous->getTargetKind() == RequestTargetKind::RESOURCE) { $descriptor->setSingleResult(true); $descriptor->setTargetResourceType($previous->getTargetResourceType()); $descriptor->setTargetKind(RequestTargetKind::MEDIA_RESOURCE); } else { if (is_null($projectedProperty)) { if (!is_null($previous->getTargetResourceType()) && !is_null($previous->getTargetResourceType()->tryResolveNamedStreamByName($identifier))) { $descriptor->setTargetKind(RequestTargetKind::MEDIA_RESOURCE); $descriptor->setSingleResult(true); $descriptor->setTargetResourceType($previous->getTargetResourceType()); } else { ODataException::createResourceNotFoundError($identifier); } } else { $descriptor->setTargetResourceType($projectedProperty->getResourceType()); $descriptor->setSingleResult($projectedProperty->getKind() != ResourcePropertyKind::RESOURCESET_REFERENCE); if ($previous->getTargetKind() == RequestTargetKind::LINK && $projectedProperty->getTypeKind() != ResourceTypeKind::ENTITY) { ODataException::createBadRequestError(Messages::segmentParserLinkSegmentMustBeFollowedByEntitySegment($identifier)); } switch ($projectedProperty->getKind()) { case ResourcePropertyKind::COMPLEX_TYPE: $descriptor->setTargetKind(RequestTargetKind::COMPLEX_OBJECT); break; case ResourcePropertyKind::BAG | ResourcePropertyKind::PRIMITIVE: case ResourcePropertyKind::BAG | ResourcePropertyKind::COMPLEX_TYPE: $descriptor->setTargetKind(RequestTargetKind::BAG); break; case ResourcePropertyKind::RESOURCE_REFERENCE: case ResourcePropertyKind::RESOURCESET_REFERENCE: $descriptor->setTargetKind(RequestTargetKind::RESOURCE); $resourceSetWrapper = $this->_providerWrapper->getResourceSetWrapperForNavigationProperty($previous->getTargetResourceSetWrapper(), $previous->getTargetResourceType(), $projectedProperty); if (is_null($resourceSetWrapper)) { ODataException::createResourceNotFoundError($projectedProperty->getName()); } $descriptor->setTargetResourceSetWrapper($resourceSetWrapper); break; default: if (!$projectedProperty->isKindOf(ResourcePropertyKind::PRIMITIVE)) { ODataException::createInternalServerError(Messages::segmentParserUnExpectedPropertyKind('Primitive')); } $descriptor->setTargetKind(RequestTargetKind::PRIMITIVE); break; } if ($hasPredicate) { $this->_assertion(!$descriptor->isSingleResult()); $keyDescriptor = $this->_createKeyDescriptor($identifier . '(' . $keyPredicate . ')', $projectedProperty->getResourceType(), $keyPredicate); $descriptor->setKeyDescriptor($keyDescriptor); if (!$keyDescriptor->isEmpty()) { $descriptor->setSingleResult(true); } } if ($checkRights && !is_null($descriptor->getTargetResourceSetWrapper())) { $descriptor->getTargetResourceSetWrapper()->checkResourceSetRightsForRead($descriptor->isSingleResult()); } } } } } } } $descriptor->setPrevious($previous); $previous->setNext($descriptor); $this->_segmentDescriptors[] = $descriptor; $previous = $descriptor; } if ($previous->getTargetKind() == RequestTargetKind::LINK) { ODataException::createBadRequestError(Messages::segmentParserMissingSegmentAfterLink()); } }
/** * Build 'OrderBy Tree' from the given orderby path segments, also build * comparsion function for each path segment. * * @param array(array) &$ordeyByPathSegments Collection of orderby path segments, * this is passed by reference * since we need this function to * modify this array in two cases: * 1. if asc or desc present, then the * corrosponding sub path segment * should be removed * 2. remove duplicate orderby path * segment * * @return void * * @throws ODataException If any error occurs while processing the orderby path * segments */ private function _buildOrderByTree(&$ordeyByPathSegments) { foreach ($ordeyByPathSegments as $index1 => &$ordeyBySubPathSegments) { $currentNode = $this->_rootOrderByNode; $currentObject = $this->_dummyObject; $ascending = true; $subPathCount = count($ordeyBySubPathSegments); // Check sort order is specified in the path, if so set a // flag and remove that segment if ($subPathCount > 1) { if ($ordeyBySubPathSegments[$subPathCount - 1] === '*desc') { $ascending = false; unset($ordeyBySubPathSegments[$subPathCount - 1]); $subPathCount--; } else { if ($ordeyBySubPathSegments[$subPathCount - 1] === '*asc') { unset($ordeyBySubPathSegments[$subPathCount - 1]); $subPathCount--; } } } $ancestors = array($this->_rootOrderByNode->getResourceSetWrapper()->getName()); foreach ($ordeyBySubPathSegments as $index2 => $orderBySubPathSegment) { $isLastSegment = $index2 == $subPathCount - 1; $resourceSetWrapper = null; $resourceType = $currentNode->getResourceType(); $resourceProperty = $resourceType->tryResolvePropertyTypeByName($orderBySubPathSegment); if (is_null($resourceProperty)) { ODataException::createSyntaxError(Messages::orderByParserPropertyNotFound($resourceType->getFullName(), $orderBySubPathSegment)); } if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG)) { ODataException::createBadRequestError(Messages::orderByParserBagPropertyNotAllowed($resourceProperty->getName())); } else { if ($resourceProperty->isKindOf(ResourcePropertyKind::PRIMITIVE)) { if (!$isLastSegment) { ODataException::createBadRequestError(Messages::orderByParserPrimitiveAsIntermediateSegment($resourceProperty->getName())); } $type = $resourceProperty->getInstanceType(); if ($type instanceof Binary) { ODataException::createBadRequestError(Messages::orderbyParserSortByBinaryPropertyNotAllowed($resourceProperty->getName())); } } else { if ($resourceProperty->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE || $resourceProperty->getKind() == ResourcePropertyKind::RESOURCE_REFERENCE) { $this->_assertion($currentNode instanceof OrderByRootNode || $currentNode instanceof OrderByNode); $resourceSetWrapper = $currentNode->getResourceSetWrapper(); $this->_assertion(!is_null($resourceSetWrapper)); $resourceSetWrapper = $this->_providerWrapper->getResourceSetWrapperForNavigationProperty($resourceSetWrapper, $resourceType, $resourceProperty); if (is_null($resourceSetWrapper)) { ODataException::createBadRequestError(Messages::badRequestInvalidPropertyNameSpecified($resourceType->getFullName(), $orderBySubPathSegment)); } if ($resourceProperty->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE) { ODataException::createBadRequestError(Messages::orderbyParserResourceSetReferenceNotAllowed($resourceProperty->getName(), $resourceType->getFullName())); } $resourceSetWrapper->checkResourceSetRightsForRead(true); if ($isLastSegment) { ODataException::createBadRequestError(Messages::orderByParserSortByNavigationPropertyIsNotAllowed($resourceProperty->getName())); } $ancestors[] = $orderBySubPathSegment; } else { if ($resourceProperty->isKindOf(ResourcePropertyKind::COMPLEX_TYPE)) { if ($isLastSegment) { ODataException::createBadRequestError(Messages::orderByParserSortByComplexPropertyIsNotAllowed($resourceProperty->getName())); } $ancestors[] = $orderBySubPathSegment; } else { ODataException::createInternalServerError(Messages::orderByParserUnexpectedPropertyType()); } } } } $node = $currentNode->findNode($orderBySubPathSegment); if (is_null($node)) { if ($resourceProperty->isKindOf(ResourcePropertyKind::PRIMITIVE)) { $node = new OrderByLeafNode($orderBySubPathSegment, $resourceProperty, $ascending); $this->_comparisonFunctions[] = $node->buildComparisonFunction($ancestors); } else { if ($resourceProperty->getKind() == ResourcePropertyKind::RESOURCE_REFERENCE) { $node = new OrderByNode($orderBySubPathSegment, $resourceProperty, $resourceSetWrapper); // Initialize this member variable (identified by // $resourceProperty) of parent object. try { $dummyProperty = new \ReflectionProperty($currentObject, $resourceProperty->getName()); $object = $resourceProperty->getInstanceType()->newInstance(); $dummyProperty->setValue($currentObject, $object); $currentObject = $object; } catch (\ReflectionException $reflectionException) { throw ODataException::createInternalServerError(Messages::orderByParserFailedToAccessOrInitializeProperty($resourceProperty->getName(), $resourceType->getName())); } } else { if ($resourceProperty->getKind() == ResourcePropertyKind::COMPLEX_TYPE) { $node = new OrderByNode($orderBySubPathSegment, $resourceProperty, null); // Initialize this member variable // (identified by $resourceProperty)of parent object. try { $dummyProperty = new \ReflectionProperty($currentObject, $resourceProperty->getName()); $object = $resourceProperty->getInstanceType()->newInstance(); $dummyProperty->setValue($currentObject, $object); $currentObject = $object; } catch (\ReflectionException $reflectionException) { throw ODataException::createInternalServerError(Messages::orderByParserFailedToAccessOrInitializeProperty($resourceProperty->getName(), $resourceType->getName())); } } } } $currentNode->addNode($node); } else { try { $dummyProperty = new \ReflectionProperty($currentObject, $resourceProperty->getName()); $currentObject = $dummyProperty->getValue($currentObject); } catch (\ReflectionException $reflectionException) { throw ODataException::createInternalServerError(Messages::orderByParserFailedToAccessOrInitializeProperty($resourceProperty->getName(), $resourceType->getName())); } if ($node instanceof OrderByLeafNode) { //remove duplicate orderby path unset($ordeyByPathSegments[$index1]); } } $currentNode = $node; } } }