/** * Parse the given skiptoken, validate it using the given InternalOrderByInfo * and generates instance of InternalSkipTokenInfo. * * @param ResourceType &$resourceType The resource type of the * resource targeted by the * resource path. * @param InternalOrderByInfo &$internalOrderByInfo The $orderby details. * @param string $skipToken The $skiptoken value. * * @return InternalSkipTokenInfo * * @throws ODataException */ public static function parseSkipTokenClause(ResourceType &$resourceType, InternalOrderByInfo &$internalOrderByInfo, $skipToken) { $tokenValueDescriptor = null; if (!KeyDescriptor::tryParseValuesFromSkipToken($skipToken, $tokenValueDescriptor)) { throw ODataException::createSyntaxError(Messages::skipTokenParserSyntaxError($skipToken)); } $orderByPathSegments = null; //$positionalValues are of type array(int, array(string, IType)) $positionalValues =& $tokenValueDescriptor->getPositionalValuesByRef(); $count = count($positionalValues); $orderByPathSegments = $internalOrderByInfo->getOrderByPathSegments(); $orderByPathCount = count($orderByPathSegments); if ($count != $orderByPathCount) { throw ODataException::createBadRequestError(Messages::skipTokenParserSkipTokenNotMatchingOrdering($count, $skipToken, $orderByPathCount)); } $i = 0; foreach ($orderByPathSegments as $orderByPathSegment) { $typeProvidedInSkipToken = $positionalValues[$i][1]; if (!$typeProvidedInSkipToken instanceof Null1) { $orderBySubPathSegments = $orderByPathSegment->getSubPathSegments(); $j = count($orderBySubPathSegments) - 1; $expectedType = $orderBySubPathSegments[$j]->getInstanceType(); if (!$expectedType->isCompatibleWith($typeProvidedInSkipToken)) { throw ODataException::createSyntaxError(Messages::skipTokenParserInCompatibleTypeAtPosition($skipToken, $expectedType->getFullTypeName(), $i, $typeProvidedInSkipToken->getFullTypeName())); } } $i++; } return new InternalSkipTokenInfo($internalOrderByInfo, $positionalValues, $resourceType); }
/** * Gets the current identifier text * * @return string */ public function getIdentifier() { if ($this->Id != ExpressionTokenId::IDENTIFIER) { throw ODataException::createSyntaxError('Identifier expected at position ' . $this->Position); } return $this->Text; }
/** * Throws parser error. * * @param string $message The error message. * * @return void * * @throws ODataException */ private function _parseError($message) { throw ODataException::createSyntaxError($message); }
/** * Increment recursion count and throw error if beyond limit * * @return void * * @throws ODataException If max recursion limit hits. */ private function _recurseEnter() { $this->_recursionDepth++; if ($this->_recursionDepth == self::RECURSION_LIMIT) { throw ODataException::createSyntaxError("Recursion limit reached."); } }
/** * Modify the 'Projection Tree' to include selection details * * @param array(array(string)) $selectPathSegments Collection of select * paths. * * @return void * * @throws ODataException If any error occurs while processing select * path segments */ private function _applySelectionToProjectionTree($selectPathSegments) { foreach ($selectPathSegments as $selectSubPathSegments) { $currentNode = $this->_rootProjectionNode; $subPathCount = count($selectSubPathSegments); foreach ($selectSubPathSegments as $index => $selectSubPathSegment) { if (!$currentNode instanceof RootProjectionNode && !$currentNode instanceof ExpandedProjectionNode) { throw ODataException::createBadRequestError(Messages::expandProjectionParserPropertyWithoutMatchingExpand($currentNode->getPropertyName())); } $currentNode->setSelectionFound(); $isLastSegment = $index == $subPathCount - 1; if ($selectSubPathSegment === '*') { $currentNode->setSelectAllImmediateProperties(); break; } $currentResourceType = $currentNode->getResourceType(); $resourceProperty = $currentResourceType->resolveProperty($selectSubPathSegment); if (is_null($resourceProperty)) { throw ODataException::createSyntaxError(Messages::expandProjectionParserPropertyNotFound($currentResourceType->getFullName(), $selectSubPathSegment, true)); } if (!$isLastSegment) { if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG)) { throw ODataException::createBadRequestError(Messages::expandProjectionParserBagPropertyAsInnerSelectSegment($currentResourceType->getFullName(), $selectSubPathSegment)); } else { if ($resourceProperty->isKindOf(ResourcePropertyKind::PRIMITIVE)) { throw ODataException::createBadRequestError(Messages::expandProjectionParserPrimitivePropertyUsedAsNavigationProperty($currentResourceType->getFullName(), $selectSubPathSegment)); } else { if ($resourceProperty->isKindOf(ResourcePropertyKind::COMPLEX_TYPE)) { throw ODataException::createBadRequestError(Messages::expandProjectionParserComplexPropertyAsInnerSelectSegment($currentResourceType->getFullName(), $selectSubPathSegment)); } else { if ($resourceProperty->getKind() != ResourcePropertyKind::RESOURCE_REFERENCE && $resourceProperty->getKind() != ResourcePropertyKind::RESOURCESET_REFERENCE) { throw ODataException::createInternalServerError(Messages::expandProjectionParserUnexpectedPropertyType()); } } } } } $node = $currentNode->findNode($selectSubPathSegment); if (is_null($node)) { if (!$isLastSegment) { throw ODataException::createBadRequestError(Messages::expandProjectionParserPropertyWithoutMatchingExpand($selectSubPathSegment)); } $node = new ProjectionNode($selectSubPathSegment, $resourceProperty); $currentNode->addNode($node); } $currentNode = $node; if ($currentNode instanceof ExpandedProjectionNode && $isLastSegment) { $currentNode->setSelectionFound(); $currentNode->markSubtreeAsSelected(); } } } }
/** * Assert that the given condition is true, if false throw * ODataException for syntax error * * @param boolean $condition The condition to assert * * @return void * * @throws ODataException */ private function _assertion($condition) { if (!$condition) { throw ODataException::createSyntaxError(Messages::syntaxError()); } }
/** * Populate map table * * @param AbstractExpression $nullCheckExpTree The expression to verfiy. * * @return void * * @throws ODataException */ private function _map($nullCheckExpTree) { if ($nullCheckExpTree instanceof LogicalExpression) { $this->_map($nullCheckExpTree->getLeft()); $this->_map($nullCheckExpTree->getRight()); } else { if ($nullCheckExpTree instanceof UnaryExpression) { $this->_map($nullCheckExpTree->getChild()); } else { if ($nullCheckExpTree instanceof FunctionCallExpression) { $param = $nullCheckExpTree->getParamExpressions(); $this->_map($param[0]); } else { if ($nullCheckExpTree instanceof PropertyAccessExpression) { $parent = $nullCheckExpTree; $key = null; do { $key = $parent->getResourceProperty()->getName() . '_' . $key; $parent = $parent->getParent(); } while ($parent != null); $this->_mapTable[$key] = $nullCheckExpTree; } else { throw ODataException::createSyntaxError(Messages::expressionParser2UnexpectedExpression(get_class($nullCheckExpTree))); } } } } }
/** * Read skip or top query option value which is expected to be positive * integer. * * @param string $queryItem The name of the query item to read from request * uri ($skip or $top). * @param int &$value On return, If the requested query item is * present with a valid integer value then this * argument will holds that integer value * otherwise holds zero. * * @return boolean True If the requested query item with valid integer * value is present in the request, false query * item is absent in the request uri. * * @throws ODataException Throws syntax error if the requested argument * is present and it is not an integer. */ private function _readSkipOrTopOption($queryItem, &$value) { $value = $this->service->getHost()->getQueryStringItem($queryItem); if (!is_null($value)) { $int = new Int32(); if (!$int->validate($value, $outValue)) { throw ODataException::createSyntaxError(Messages::queryProcessorIncorrectArgumentFormat($queryItem, $value)); } $value = intval($value); if ($value < 0) { throw ODataException::createSyntaxError(Messages::queryProcessorIncorrectArgumentFormat($queryItem, $value)); } return true; } $value = 0; return false; }
/** * Validate operands (arguments) of a function call operation and return * matching function * * @param \POData\UriProcessor\QueryProcessor\FunctionDescription[] $functions List of functions to be checked * @param AbstractExpression[] $argExpressions Function argument expressions * @param ExpressionToken $expressionToken Expression token * * @throws ODataException * * @return \POData\UriProcessor\QueryProcessor\FunctionDescription */ public static function verifyFunctionCallOpArguments($functions, $argExpressions, $expressionToken) { $function = self::findFunctionWithPromotion($functions, $argExpressions, false); if ($function == null) { $protoTypes = null; foreach ($functions as $function) { $protoTypes .= $function->getPrototypeAsString() . '; '; } throw ODataException::createSyntaxError(Messages::expressionLexerNoApplicableFunctionsFound($expressionToken->Text, $protoTypes, $expressionToken->Position)); } return $function; }
/** * Build 'OrderBy Tree' from the given orderby path segments, also build * comparsion function for each path segment. * * @param array(array) &$orderByPathSegments 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 * corresponding 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(&$orderByPathSegments) { foreach ($orderByPathSegments as $index1 => &$orderBySubPathSegments) { $currentNode = $this->_rootOrderByNode; $currentObject = $this->_dummyObject; $ascending = true; $subPathCount = count($orderBySubPathSegments); // Check sort order is specified in the path, if so set a // flag and remove that segment if ($subPathCount > 1) { if ($orderBySubPathSegments[$subPathCount - 1] === '*desc') { $ascending = false; unset($orderBySubPathSegments[$subPathCount - 1]); $subPathCount--; } else { if ($orderBySubPathSegments[$subPathCount - 1] === '*asc') { unset($orderBySubPathSegments[$subPathCount - 1]); $subPathCount--; } } } $ancestors = array($this->_rootOrderByNode->getResourceSetWrapper()->getName()); foreach ($orderBySubPathSegments as $index2 => $orderBySubPathSegment) { $isLastSegment = $index2 == $subPathCount - 1; $resourceSetWrapper = null; $resourceType = $currentNode->getResourceType(); $resourceProperty = $resourceType->resolveProperty($orderBySubPathSegment); if (is_null($resourceProperty)) { throw ODataException::createSyntaxError(Messages::orderByParserPropertyNotFound($resourceType->getFullName(), $orderBySubPathSegment)); } if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG)) { throw ODataException::createBadRequestError(Messages::orderByParserBagPropertyNotAllowed($resourceProperty->getName())); } else { if ($resourceProperty->isKindOf(ResourcePropertyKind::PRIMITIVE)) { if (!$isLastSegment) { throw ODataException::createBadRequestError(Messages::orderByParserPrimitiveAsIntermediateSegment($resourceProperty->getName())); } $type = $resourceProperty->getInstanceType(); if ($type instanceof Binary) { throw 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)) { throw ODataException::createBadRequestError(Messages::badRequestInvalidPropertyNameSpecified($resourceType->getFullName(), $orderBySubPathSegment)); } if ($resourceProperty->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE) { throw ODataException::createBadRequestError(Messages::orderByParserResourceSetReferenceNotAllowed($resourceProperty->getName(), $resourceType->getFullName())); } $resourceSetWrapper->checkResourceSetRightsForRead(true); if ($isLastSegment) { throw ODataException::createBadRequestError(Messages::orderByParserSortByNavigationPropertyIsNotAllowed($resourceProperty->getName())); } $ancestors[] = $orderBySubPathSegment; } else { if ($resourceProperty->isKindOf(ResourcePropertyKind::COMPLEX_TYPE)) { if ($isLastSegment) { throw ODataException::createBadRequestError(Messages::orderByParserSortByComplexPropertyIsNotAllowed($resourceProperty->getName())); } $ancestors[] = $orderBySubPathSegment; } else { throw 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 { $reflectionClass = new \ReflectionClass(get_class($currentObject)); $reflectionProperty = $reflectionClass->getProperty($resourceProperty->getName()); $reflectionProperty->setAccessible(true); $currentObject = $reflectionProperty->getValue($currentObject); //$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($orderByPathSegments[$index1]); } } $currentNode = $node; } } }
/** * Validate this KeyDescriptor, If valid, this function populates * _validatedNamedValues array with key as keyName and value as an array of * key value and key type. * * @param string $segmentAsString The segment in the form identifer * (keyPredicate) which this descriptor * represents * @param ResourceType $resourceType The type of the idenfier in the segment * * @return void * * @throws ODataException If validation fails. */ public function validate($segmentAsString, ResourceType $resourceType) { if ($this->isEmpty()) { $this->_validatedNamedValues = array(); return; } $keyProperties = $resourceType->getKeyProperties(); $keyPropertiesCount = count($keyProperties); if (!empty($this->_namedValues)) { if (count($this->_namedValues) != $keyPropertiesCount) { throw ODataException::createSyntaxError(Messages::keyDescriptorKeyCountNotMatching($segmentAsString, $keyPropertiesCount, count($this->_namedValues))); } foreach ($keyProperties as $keyName => $keyResourceProperty) { if (!array_key_exists($keyName, $this->_namedValues)) { $keysAsString = null; foreach (array_keys($keyProperties) as $key) { $keysAsString .= $key . ', '; } $keysAsString = rtrim($keysAsString, ' ,'); throw ODataException::createSyntaxError(Messages::keyDescriptorMissingKeys($segmentAsString, $keysAsString)); } $typeProvided = $this->_namedValues[$keyName][1]; $expectedType = $keyResourceProperty->getInstanceType(); if (!$expectedType->isCompatibleWith($typeProvided)) { throw ODataException::createSyntaxError(Messages::keyDescriptorInCompatibleKeyType($segmentAsString, $keyName, $expectedType->getFullTypeName(), $typeProvided->getFullTypeName())); } $this->_validatedNamedValues[$keyName] = $this->_namedValues[$keyName]; } } else { if (count($this->_positionalValues) != $keyPropertiesCount) { throw ODataException::createSyntaxError(Messages::keyDescriptorKeyCountNotMatching($segmentAsString, $keyPropertiesCount, count($this->_positionalValues))); } $i = 0; foreach ($keyProperties as $keyName => $keyResourceProperty) { $typeProvided = $this->_positionalValues[$i][1]; $expectedType = $keyResourceProperty->getInstanceType(); if (!$expectedType->isCompatibleWith($typeProvided)) { throw ODataException::createSyntaxError(Messages::keyDescriptorInCompatibleKeyTypeAtPosition($segmentAsString, $keyResourceProperty->getName(), $i, $expectedType->getFullTypeName(), $typeProvided->getFullTypeName())); } $this->_validatedNamedValues[$keyName] = $this->_positionalValues[$i]; $i++; } } }