/** * Common function to handle exceptions in the data service. * * @param \Exception $exception exception * @param IService $service service * * @return void */ public static function handleException($exception, IService $service) { $acceptTypesText = $service->getHost()->getRequestAccept(); $responseContentType = null; try { $responseContentType = HttpProcessUtility::selectMimeType($acceptTypesText, array(MimeTypes::MIME_APPLICATION_XML, MimeTypes::MIME_APPLICATION_JSON)); } catch (HttpHeaderFailure $exception) { $exception = new ODataException($exception->getMessage(), $exception->getStatusCode()); } catch (\Exception $exception) { // Never come here } if (is_null($responseContentType)) { $responseContentType = MimeTypes::MIME_APPLICATION_XML; } if (!$exception instanceof ODataException) { $exception = new ODataException($exception->getMessage(), HttpStatus::CODE_INTERNAL_SERVER_ERROR); } $service->getHost()->setResponseVersion(ODataConstants::DATASERVICEVERSION_1_DOT_0 . ';'); // At this point all kind of exceptions will be converted //to 'ODataException' if ($exception->getStatusCode() == HttpStatus::CODE_NOT_MODIFIED) { $service->getHost()->setResponseStatusCode(HttpStatus::CODE_NOT_MODIFIED); } else { $service->getHost()->setResponseStatusCode($exception->getStatusCode()); $service->getHost()->setResponseContentType($responseContentType); $responseBody = null; if (strcasecmp($responseContentType, MimeTypes::MIME_APPLICATION_XML) == 0) { $responseBody = AtomODataWriter::serializeException($exception, true); } else { $responseBody = JsonODataV2Writer::serializeException($exception, true); } $service->getHost()->getOperationContext()->outgoingResponse()->setStream($responseBody); } }
/** * 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; }
public function handleRequest() { $this->createProviders(); $this->getHost()->validateQueryParameters(); $requestMethod = $this->getOperationContext()->incomingRequest()->getMethod(); if ($requestMethod != HTTPRequestMethod::GET()) { throw ODataException::createNotImplementedError(Messages::onlyReadSupport($requestMethod)); } return UriProcessor::process($this); }
/** * Process the request Uri and creates an instance of * RequestDescription from the processed uri. * * @param IService $service Reference to the data service instance. * * @return RequestDescription * * @throws ODataException If any exception occurs while processing the segments * or in case of any version incompatibility. */ public static function process(IService $service) { $host = $service->getHost(); $absoluteRequestUri = $host->getAbsoluteRequestUri(); $absoluteServiceUri = $host->getAbsoluteServiceUri(); $requestUriSegments = array_slice($absoluteRequestUri->getSegments(), $absoluteServiceUri->getSegmentCount()); $segments = SegmentParser::parseRequestUriSegments($requestUriSegments, $service->getProvidersWrapper(), true); $request = new RequestDescription($segments, $absoluteRequestUri, $service->getConfiguration()->getMaxDataServiceVersion(), $host->getRequestVersion(), $host->getRequestMaxVersion()); $kind = $request->getTargetKind(); if ($kind == TargetKind::METADATA() || $kind == TargetKind::BATCH() || $kind == TargetKind::SERVICE_DIRECTORY()) { return $request; } if ($kind == TargetKind::PRIMITIVE_VALUE() || $kind == TargetKind::MEDIA_RESOURCE()) { // http://odata/NW.svc/Orders/$count // http://odata/NW.svc/Orders(123)/Customer/CustomerID/$value // http://odata/NW.svc/Employees(1)/$value // http://odata/NW.svc/Employees(1)/ThumbNail_48X48/$value $request->setContainerName($segments[count($segments) - 2]->getIdentifier()); } else { $request->setContainerName($request->getIdentifier()); } if ($request->getIdentifier() === ODataConstants::URI_COUNT_SEGMENT) { if (!$service->getConfiguration()->getAcceptCountRequests()) { throw ODataException::createBadRequestError(Messages::configurationCountNotAccepted()); } $request->queryType = QueryType::COUNT(); // use of $count requires request DataServiceVersion // and MaxDataServiceVersion greater than or equal to 2.0 $request->raiseResponseVersion(2, 0); $request->raiseMinVersionRequirement(2, 0); } else { if ($request->isNamedStream()) { $request->raiseMinVersionRequirement(3, 0); } else { if ($request->getTargetKind() == TargetKind::RESOURCE()) { if (!$request->isLinkUri()) { $resourceSetWrapper = $request->getTargetResourceSetWrapper(); //assert($resourceSetWrapper != null) $hasNamedStream = $resourceSetWrapper->hasNamedStreams($service->getProvidersWrapper()); $hasBagProperty = $resourceSetWrapper->hasBagProperty($service->getProvidersWrapper()); if ($hasNamedStream || $hasBagProperty) { $request->raiseResponseVersion(3, 0); } } } else { if ($request->getTargetKind() == TargetKind::BAG()) { $request->raiseResponseVersion(3, 0); } } } } return $request; }
/** * 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++; } } }
/** * 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."); } }
/** * Get related resource for a resource * * @param ResourceSet $sourceResourceSet The source resource set * @param mixed $sourceEntityInstance The source resource * @param ResourceSet $targetResourceSet The resource set of * the navigation property * @param ResourceProperty $targetProperty The navigation property to be * retrieved * * @return object|null The related resource if exists else null */ public function getRelatedResourceReference(ResourceSet $sourceResourceSet, $sourceEntityInstance, ResourceSet $targetResourceSet, ResourceProperty $targetProperty) { $result = null; $srcClass = get_class($sourceEntityInstance); $navigationPropName = $targetProperty->getName(); if ($srcClass === 'Order') { if ($navigationPropName === 'Customer') { if (empty($sourceEntityInstance->CustomerID)) { $result = null; } else { $query = "SELECT * FROM Customers WHERE CustomerID = '{$sourceEntityInstance->CustomerID}'"; $stmt = sqlsrv_query($this->_connectionHandle, $query); if ($stmt === false) { $errorAsString = self::_getSQLSRVError(); throw ODataException::createInternalServerError($errorAsString); } if (!sqlsrv_has_rows($stmt)) { $result = null; } $result = $this->_serializeCustomer(sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)); } } else { die('Customer does not have navigation porperty with name: ' . $navigationPropName); } } else { if ($srcClass === 'Order_Details') { if ($navigationPropName === 'Order') { if (empty($sourceEntityInstance->OrderID)) { $result = null; } else { $query = "SELECT * FROM Orders WHERE OrderID = {$sourceEntityInstance->OrderID}"; $stmt = sqlsrv_query($this->_connectionHandle, $query); if ($stmt === false) { $errorAsString = self::_getSQLSRVError(); throw ODataException::createInternalServerError($errorAsString); } if (!sqlsrv_has_rows($stmt)) { $result = null; } $result = $this->_serializeOrder(sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)); } } else { die('Order_Details does not have navigation porperty with name: ' . $navigationPropName); } } } return $result; }
/** * Validates the given version in string format and returns the version as instance of Version * * @param string $versionHeader The DataServiceVersion or MaxDataServiceVersion header value * @param string $headerName The name of the header * * @return Version * * @throws ODataException If the version is malformed or not supported */ private static function parseVersionHeader($versionHeader, $headerName) { $libName = null; $versionHeader = trim($versionHeader); $libNameIndex = strpos($versionHeader, ';'); if ($libNameIndex !== false) { $libName = substr($versionHeader, $libNameIndex); } else { $libNameIndex = strlen($versionHeader); } $dotIndex = -1; for ($i = 0; $i < $libNameIndex; $i++) { if ($versionHeader[$i] == '.') { //Throw an exception if we find more than 1 dot if ($dotIndex != -1) { throw ODataException::createBadRequestError(Messages::requestDescriptionInvalidVersionHeader($versionHeader, $headerName)); } $dotIndex = $i; } else { if ($versionHeader[$i] < '0' || $versionHeader[$i] > '9') { throw ODataException::createBadRequestError(Messages::requestDescriptionInvalidVersionHeader($versionHeader, $headerName)); } } } $major = intval(substr($versionHeader, 0, $dotIndex)); $minor = 0; //Apparently the . is optional if ($dotIndex != -1) { if ($dotIndex == 0) { //If it starts with a ., throw an exception throw ODataException::createBadRequestError(Messages::requestDescriptionInvalidVersionHeader($versionHeader, $headerName)); } $minor = intval(substr($versionHeader, $dotIndex + 1, $libNameIndex)); } $version = new Version($major, $minor); //TODO: move this somewhere... /* $isSupportedVersion = false; foreach (self::getKnownDataServiceVersions() as $version1) { if ($version->compare($version1) == 0) { $isSupportedVersion = true; break; } } if (!$isSupportedVersion) { $availableVersions = null; foreach (self::getKnownDataServiceVersions() as $version1) { $availableVersions .= $version1->toString() . ', '; } $availableVersions = rtrim($availableVersions, ', '); throw ODataException::createBadRequestError( Messages::requestDescriptionUnSupportedVersion( $headerName, $versionHeader, $availableVersions ) ); } */ return $version; }
/** * Write values of properties of given entry (resource) or complex object. * * @param mixed $customObject Entity or complex object * with properties * to write out. * @param ResourceType &$resourceType Resource type describing * the metadata of * the custom object. * @param string $absoluteUri Absolute uri for the given * entry object * NULL for complex object. * @param string $relativeUri Relative uri for the given * custom object. * @param ODataEntry &$odataEntry ODataEntry instance to * place links and * expansion of the * entry object, * NULL for complex object. * @param ODataPropertyContent &$odataPropertyContent ODataPropertyContent * instance in which * to place the values. * * @return void */ private function _writeObjectProperties($customObject, ResourceType &$resourceType, $absoluteUri, $relativeUri, &$odataEntry, ODataPropertyContent &$odataPropertyContent) { // lion: if ($_SERVER['REQUEST_METHOD'] == HTTPRequestMethod::DELETE()) { $odataEntry = (object) array(); return; } // --- $resourceTypeKind = $resourceType->getResourceTypeKind(); if (is_null($absoluteUri) == ($resourceTypeKind == ResourceTypeKind::ENTITY)) { throw ODataException::createInternalServerError(Messages::badProviderInconsistentEntityOrComplexTypeUsage($resourceType->getName())); } $this->assert($resourceTypeKind == ResourceTypeKind::ENTITY && $odataEntry instanceof ODataEntry || $resourceTypeKind == ResourceTypeKind::COMPLEX && is_null($odataEntry), '(($resourceTypeKind == ResourceTypeKind::ENTITY) && ($odataEntry instanceof ODataEntry)) || (($resourceTypeKind == ResourceTypeKind::COMPLEX) && is_null($odataEntry))'); $projectionNodes = null; $navigationProperties = null; if ($resourceTypeKind == ResourceTypeKind::ENTITY) { $projectionNodes = $this->getProjectionNodes(); $navigationProperties = array(); } if (is_null($projectionNodes)) { //This is the code path to handle properties of Complex type //or Entry without projection (i.e. no expansion or selection) $resourceProperties = array(); if ($resourceTypeKind == ResourceTypeKind::ENTITY) { // If custom object is an entry then it can contain navigation // properties which are invisible (because the corresponding // resource set is invisible). // IDSMP::getResourceProperties will give collection of properties // which are visible. $currentResourceSetWrapper1 = $this->getCurrentResourceSetWrapper(); $resourceProperties = $this->service->getProvidersWrapper()->getResourceProperties($currentResourceSetWrapper1, $resourceType); } else { $resourceProperties = $resourceType->getAllProperties(); } //First write out primitve types foreach ($resourceProperties as $name => $resourceProperty) { if ($resourceProperty->getKind() == ResourcePropertyKind::PRIMITIVE || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY) || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::ETAG) || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY | ResourcePropertyKind::ETAG)) { $odataProperty = new ODataProperty(); $primitiveValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty); $this->_writePrimitiveValue($primitiveValue, $resourceProperty, $odataProperty); $odataPropertyContent->properties[] = $odataProperty; } } //Write out bag and complex type $i = 0; foreach ($resourceProperties as $resourceProperty) { if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG)) { //Handle Bag Property (Bag of Primitive or complex) $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty); $resourceType2 = $resourceProperty->getResourceType(); $this->_writeBagValue($propertyValue, $resourceProperty->getName(), $resourceType2, $relativeUri . '/' . $resourceProperty->getName(), $odataPropertyContent); } else { $resourcePropertyKind = $resourceProperty->getKind(); if ($resourcePropertyKind == ResourcePropertyKind::COMPLEX_TYPE) { $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty); $resourceType1 = $resourceProperty->getResourceType(); $this->_writeComplexValue($propertyValue, $resourceProperty->getName(), $resourceType1, $relativeUri . '/' . $resourceProperty->getName(), $odataPropertyContent); } else { if ($resourceProperty->getKind() == ResourcePropertyKind::PRIMITIVE || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY) || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::ETAG) || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY | ResourcePropertyKind::ETAG)) { continue; } else { $this->assert($resourcePropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE || $resourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE, '($resourcePropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE) || ($resourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE)'); $navigationProperties[$i] = new NavigationPropertyInfo($resourceProperty, $this->shouldExpandSegment($resourceProperty->getName())); if ($navigationProperties[$i]->expanded) { $navigationProperties[$i]->value = $this->getPropertyValue($customObject, $resourceType, $resourceProperty); } $i++; } } } } } else { //This is the code path to handle projected properties of Entry $i = 0; foreach ($projectionNodes as $projectionNode) { $propertyName = $projectionNode->getPropertyName(); $resourceProperty = $resourceType->resolveProperty($propertyName); $this->assert(!is_null($resourceProperty), '!is_null($resourceProperty)'); if ($resourceProperty->getTypeKind() == ResourceTypeKind::ENTITY) { $currentResourceSetWrapper2 = $this->getCurrentResourceSetWrapper(); $resourceProperties = $this->service->getProvidersWrapper()->getResourceProperties($currentResourceSetWrapper2, $resourceType); //Check for the visibility of this navigation property if (array_key_exists($resourceProperty->getName(), $resourceProperties)) { $navigationProperties[$i] = new NavigationPropertyInfo($resourceProperty, $this->shouldExpandSegment($propertyName)); if ($navigationProperties[$i]->expanded) { $navigationProperties[$i]->value = $this->getPropertyValue($customObject, $resourceType, $resourceProperty); } $i++; continue; } } //Primitve, complex or bag property $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty); $propertyTypeKind = $resourceProperty->getKind(); $propertyResourceType = $resourceProperty->getResourceType(); $this->assert(!is_null($propertyResourceType), '!is_null($propertyResourceType)'); if (ResourceProperty::sIsKindOf($propertyTypeKind, ResourcePropertyKind::BAG)) { $bagResourceType = $resourceProperty->getResourceType(); $this->_writeBagValue($propertyValue, $propertyName, $bagResourceType, $relativeUri . '/' . $propertyName, $odataPropertyContent); } else { if (ResourceProperty::sIsKindOf($propertyTypeKind, ResourcePropertyKind::PRIMITIVE)) { $odataProperty = new ODataProperty(); $this->_writePrimitiveValue($propertyValue, $resourceProperty, $odataProperty); $odataPropertyContent->properties[] = $odataProperty; } else { if ($propertyTypeKind == ResourcePropertyKind::COMPLEX_TYPE) { $complexResourceType = $resourceProperty->getResourceType(); $this->_writeComplexValue($propertyValue, $propertyName, $complexResourceType, $relativeUri . '/' . $propertyName, $odataPropertyContent); } else { //unexpected $this->assert(false, '$propertyTypeKind = Primitive or Bag or ComplexType'); } } } } } if (!is_null($navigationProperties)) { //Write out navigation properties (deferred or inline) foreach ($navigationProperties as $navigationPropertyInfo) { $propertyName = $navigationPropertyInfo->resourceProperty->getName(); $type = $navigationPropertyInfo->resourceProperty->getKind() == ResourcePropertyKind::RESOURCE_REFERENCE ? 'application/atom+xml;type=entry' : 'application/atom+xml;type=feed'; $link = new ODataLink(); $link->name = ODataConstants::ODATA_RELATED_NAMESPACE . $propertyName; $link->title = $propertyName; $link->type = $type; $link->url = $relativeUri . '/' . $propertyName; if ($navigationPropertyInfo->expanded) { $propertyRelativeUri = $relativeUri . '/' . $propertyName; $propertyAbsoluteUri = trim($absoluteUri, '/') . '/' . $propertyName; $needPop = $this->pushSegmentForNavigationProperty($navigationPropertyInfo->resourceProperty); $navigationPropertyKind = $navigationPropertyInfo->resourceProperty->getKind(); $this->assert($navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE || $navigationPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE, '$navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE || $navigationPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE'); $currentResourceSetWrapper = $this->getCurrentResourceSetWrapper(); $this->assert(!is_null($currentResourceSetWrapper), '!is_null($currentResourceSetWrapper)'); $link->isExpanded = true; if (!is_null($navigationPropertyInfo->value)) { if ($navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE) { $inlineFeed = new ODataFeed(); $link->isCollection = true; $currentResourceType = $currentResourceSetWrapper->getResourceType(); $this->_writeFeedElements($navigationPropertyInfo->value, $currentResourceType, $propertyName, $propertyAbsoluteUri, $propertyRelativeUri, $inlineFeed); $link->expandedResult = $inlineFeed; } else { $link->isCollection = false; $currentResourceType1 = $currentResourceSetWrapper->getResourceType(); $link->expandedResult = $this->_writeEntryElement($navigationPropertyInfo->value, $currentResourceType1, $propertyAbsoluteUri, $propertyRelativeUri); } } else { $link->expandedResult = null; } $this->popSegment($needPop); } $odataEntry->links[] = $link; } } }
/** * 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(); } } } }
/** * 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; }
/** * Get the value of a given property from an instance. * * @param mixed $entity Instance of a type which contains this property. * @param ResourceType $resourceType Resource type instance containing metadata about the instance. * @param ResourceProperty $resourceProperty Resource property instance containing metadata about the property whose value to be retrieved. * * @return mixed The value of the given property. * * @throws ODataException If reflection exception occurred while trying to access the property. * */ protected function getPropertyValue($entity, ResourceType $resourceType, ResourceProperty $resourceProperty) { try { //Is this slow? See #88 $reflectionProperty = new \ReflectionProperty($entity, $resourceProperty->getName()); return $reflectionProperty->getValue($entity); } catch (\ReflectionException $reflectionException) { throw ODataException::createInternalServerError(Messages::objectModelSerializerFailedToAccessProperty($resourceProperty->getName(), $resourceType->getName())); } }
/** * Validate the given entity instance. * * @param object $entityInstance Entity instance to validate * @param ResourceSet &$resourceSet Resource set to which the entity * instance belongs to. * @param KeyDescriptor &$keyDescriptor The key descriptor. * @param string $methodName Method from which this function * invoked. * * @return void * * @throws ODataException */ private function _validateEntityInstance($entityInstance, ResourceSet &$resourceSet, KeyDescriptor &$keyDescriptor, $methodName) { if (is_null($entityInstance)) { throw ODataException::createResourceNotFoundError($resourceSet->getName()); } // lion: if ($_SERVER['REQUEST_METHOD'] == HTTPRequestMethod::DELETE()) { return; } $entityName = $resourceSet->getResourceType()->getInstanceType()->getName(); if (!is_object($entityInstance) || !$entityInstance instanceof $entityName) { throw ODataException::createInternalServerError(Messages::providersWrapperIDSQPMethodReturnsUnExpectedType($entityName, $methodName)); } foreach ($keyDescriptor->getValidatedNamedValues() as $keyName => $valueDescription) { try { $keyProperty = new \ReflectionProperty($entityInstance, $keyName); $keyValue = $keyProperty->getValue($entityInstance); if (is_null($keyValue)) { throw ODataException::createInternalServerError(Messages::providersWrapperIDSQPMethodReturnsInstanceWithNullKeyProperties($methodName)); } $convertedValue = $valueDescription[1]->convert($valueDescription[0]); if ($keyValue != $convertedValue) { throw ODataException::createInternalServerError(Messages::providersWrapperIDSQPMethodReturnsInstanceWithNonMatchingKeys($methodName)); } } catch (\ReflectionException $reflectionException) { //throw ODataException::createInternalServerError( // Messages::orderByParserFailedToAccessOrInitializeProperty( // $resourceProperty->getName(), $resourceType->getName() // ) //); } } }
/** * Assert that the given condition is true, if false throw * ODataException for unexpected state * * @param boolean $condition The condition to assert * * @return void * * @throws ODataException */ private function _assertion($condition) { if (!$condition) { throw ODataException::createInternalServerError(Messages::orderByParserUnExpectedState()); } }
/** * Build value of $skiptoken from the given object which will be the * last object in the page. * * @param mixed $lastObject entity instance from which skiptoken needs * to be built. * * @return string * * @throws ODataException If reflection exception occurs while accessing * property. */ public function buildSkipTokenValue($lastObject) { $nextPageLink = null; foreach ($this->getOrderByPathSegments() as $orderByPathSegment) { $index = 0; $currentObject = $lastObject; $subPathSegments = $orderByPathSegment->getSubPathSegments(); $subPathCount = count($subPathSegments); foreach ($subPathSegments as &$subPathSegment) { $isLastSegment = $index == $subPathCount - 1; try { $dummyProperty = new \ReflectionProperty($currentObject, $subPathSegment->getName()); $currentObject = $dummyProperty->getValue($currentObject); if (is_null($currentObject)) { $nextPageLink .= 'null, '; break; } else { if ($isLastSegment) { $type = $subPathSegment->getInstanceType(); // assert($type implements IType) // If this is a string then do utf8_encode to convert // utf8 decoded characters to // corrospoding utf8 char (e.g. � to í), then do a // urlencode to convert í to %C3%AD // urlencode is needed for datetime and guid too // if ($type instanceof String || $type instanceof DateTime // || $type instanceof Guid) { // if ($type instanceof String) { // $currentObject = utf8_encode($currentObject); // } // $currentObject = urlencode($currentObject); //} // call IType::convertToOData to attach reuqired suffix // and prepfix. // e.g. $valueM, $valueF, datetime'$value', guid'$value', // '$value' etc.. // Also we can think about moving above urlencode to this // function $value = $type->convertToOData($currentObject); $nextPageLink .= $value . ', '; } } } catch (\ReflectionException $reflectionException) { throw ODataException::createInternalServerError(Messages::internalSkipTokenInfoFailedToAccessOrInitializeProperty($subPathSegment->getName())); } $index++; } } return rtrim($nextPageLink, ", "); }
public function handlesOrderedPaging() { throw ODataException::createNotImplementedError($this->_message); }
/** * serialize exception. * * @param ODataException $exception Exception to serialize * @param Boolean $serializeInnerException if set to true * * serialize the inner exception if $exception is an ODataException. * * @return string */ public static function serializeException(ODataException $exception, $serializeInnerException) { $writer = new JsonWriter(''); // Wrapper for error. $writer->startObjectScope()->writeName(ODataConstants::JSON_ERROR)->startObjectScope(); // "code" if ($exception->getCode() != null) { $writer->writeName(ODataConstants::JSON_ERROR_CODE)->writeValue($exception->getCode()); } // "message" $writer->writeName(ODataConstants::JSON_ERROR_MESSAGE)->startObjectScope()->writeName(ODataConstants::XML_LANG_ATTRIBUTE_NAME)->writeValue('en-US')->writeName(ODataConstants::JSON_ERROR_VALUE)->writeValue($exception->getMessage())->endScope()->endScope()->endScope(); return $writer->getJsonOutput(); }
/** * Execute the client submitted request against the data source. */ public function execute() { $segments = $this->request->getSegments(); foreach ($segments as $segment) { $requestTargetKind = $segment->getTargetKind(); if ($segment->getTargetSource() == TargetSource::ENTITY_SET) { $this->handleSegmentTargetsToResourceSet($segment); } else { if ($requestTargetKind == TargetKind::RESOURCE()) { if (is_null($segment->getPrevious()->getResult())) { throw ODataException::createResourceNotFoundError($segment->getPrevious()->getIdentifier()); } $this->_handleSegmentTargetsToRelatedResource($segment); } else { if ($requestTargetKind == TargetKind::LINK()) { $segment->setResult($segment->getPrevious()->getResult()); } else { if ($segment->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) { // we are done, $count will the last segment and // taken care by _applyQueryOptions method $segment->setResult($this->request->getCountValue()); break; } else { if ($requestTargetKind == TargetKind::MEDIA_RESOURCE()) { if (is_null($segment->getPrevious()->getResult())) { throw ODataException::createResourceNotFoundError($segment->getPrevious()->getIdentifier()); } // For MLE and Named Stream the result of last segment // should be that of previous segment, this is required // while retrieving content type or stream from IDSSP $segment->setResult($segment->getPrevious()->getResult()); // we are done, as named stream property or $value on // media resource will be the last segment break; } $value = $segment->getPrevious()->getResult(); while (!is_null($segment)) { //TODO: what exactly is this doing here? Once a null's found it seems everything will be null if (!is_null($value)) { $value = null; } else { try { //see #88 $property = new \ReflectionProperty($value, $segment->getIdentifier()); $value = $property->getValue($value); } catch (\ReflectionException $reflectionException) { //throw ODataException::createInternalServerError(Messages::orderByParserFailedToAccessOrInitializeProperty($resourceProperty->getName(), $resourceType->getName())); } } $segment->setResult($value); $segment = $segment->getNext(); if (!is_null($segment) && $segment->getIdentifier() == ODataConstants::URI_VALUE_SEGMENT) { $segment->setResult($value); $segment = $segment->getNext(); } } break; } } } } if (is_null($segment->getNext()) || $segment->getNext()->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) { $this->applyQueryOptions($segment); } } // Apply $select and $expand options to result set, this function will be always applied // irrespective of return value of IDSQP2::canApplyQueryOptions which means library will // not delegate $expand/$select operation to IDSQP2 implementation $this->handleExpansion(); }
/** * To check whether the the query options $select, $expand * is applicable for the current requested resource. * * @param string $queryItem The query option to check. * * @return void * * @throws ODataException Throws bad request error if the query * options $select, $expand cannot be * applied to the requested resource. */ private function _checkExpandOrSelectApplicable($queryItem) { if (!$this->_expandSelectApplicable) { throw ODataException::createBadRequestError(Messages::queryProcessorSelectOrExpandOptionNotApplicable($queryItem)); } }
/** * Sets the value status code header on the response * * @param string $value The status code * * @return void */ public function setResponseStatusCode($value) { $floor = floor($value / 100); if ($floor >= 1 && $floor <= 5) { $statusDescription = HttpStatus::getStatusDescription($value); if (!is_null($statusDescription)) { $statusDescription = ' ' . $statusDescription; } $this->_operationContext->outgoingResponse()->setStatusCode($value . $statusDescription); } else { throw ODataException::createInternalServerError('Invalid Status Code' . $value); } }
/** * Build nextpage link from the given object which will be the last object * in the page. * * @param mixed $lastObject Entity instance to build next page link from. * * @return string * * @throws ODataException If reflection exception occurs while accessing * property. */ public function buildNextPageLink($lastObject) { $nextPageLink = null; foreach ($this->_internalOrderByInfo->getOrderByPathSegments() as $orderByPathSegment) { $index = 0; $currentObject = $lastObject; $subPathSegments = $orderByPathSegment->getSubPathSegments(); $subPathCount = count($subPathSegments); foreach ($subPathSegments as &$subPathSegment) { $isLastSegment = $index == $subPathCount - 1; try { $dummyProperty = new \ReflectionProperty($currentObject, $subPathSegment->getName()); $currentObject = $dummyProperty->getValue($currentObject); if (is_null($currentObject)) { $nextPageLink .= 'null, '; break; } else { if ($isLastSegment) { $type = $subPathSegment->getInstanceType(); $value = $type->convertToOData($currentObject); $nextPageLink .= $value . ', '; } } } catch (\ReflectionException $reflectionException) { throw ODataException::createInternalServerError(Messages::internalSkipTokenInfoFailedToAccessOrInitializeProperty($subPathSegment->getName())); } $index++; } } return rtrim($nextPageLink, ", "); }
/** * Write all resource types (entity and complex types) * * @param ResourceType[] $resourceTypes resource types array * @param array $associationTypesInResourceTypesNamespace collection of * association types for the given resource types * array(string, AssociationType) * * @return void */ private function _writeResourceTypes($resourceTypes, $associationTypesInResourceTypesNamespace) { foreach ($resourceTypes as $resourceType) { if ($resourceType->getResourceTypeKind() == ResourceTypeKind::ENTITY) { $this->_writeEntityType($resourceType, $associationTypesInResourceTypesNamespace); } else { if ($resourceType->getResourceTypeKind() == ResourceTypeKind::COMPLEX) { $this->_writeComplexType($resourceType); } else { throw ODataException::createInternalServerError(Messages::metadataWriterExpectingEntityOrComplexResourceType()); } } } }
/** * 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))); } } } } }
/** * Checks whether this request has the specified rights * * @param EntitySetRights $requiredRights The rights to check * * @throws ODataException exception if access to this resource set is forbidden */ public function checkResourceSetRights($requiredRights) { if (($this->_resourceSetRights & $requiredRights) == 0) { throw ODataException::createForbiddenError(); } }
/** * 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()); } }
/** * Returns the etag for the given resource. * Note: This function will not add W\" prefix and " suffix, its callers * repsonsability. * * @param mixed &$entryObject Resource for which etag value needs to * be returned * @param ResourceType &$resourceType Resource type of the $entryObject * * @return string|null ETag value for the given resource (with values encoded * for use in a URI) there are etag properties, NULL if * there is no etag property. */ protected function getETagForEntry(&$entryObject, ResourceType &$resourceType) { $eTag = null; $comma = null; foreach ($resourceType->getETagProperties() as $eTagProperty) { $type = $eTagProperty->getInstanceType(); self::assert(!is_null($type) && $type instanceof IType, '!is_null($type) && $type instanceof IType'); $value = null; try { //TODO #88...also this seems like dupe work $reflectionProperty = new \ReflectionProperty($entryObject, $eTagProperty->getName()); $value = $reflectionProperty->getValue($entryObject); } catch (\ReflectionException $reflectionException) { throw ODataException::createInternalServerError(Messages::failedToAccessProperty($eTagProperty->getName(), $resourceType->getName())); } if (is_null($value)) { $eTag = $eTag . $comma . 'null'; } else { $eTag = $eTag . $comma . $type->convertToOData($value); } $comma = ','; } if (!is_null($eTag)) { // If eTag is made up of datetime or string properties then the above // IType::convertToOData will perform utf8 and url encode. But we don't // want this for eTag value. $eTag = urldecode(utf8_decode($eTag)); return rtrim($eTag, ','); } return null; }
/** * Serialize the exception * * @param ODataException $exception Exception to serialize * @param boolean $serializeInnerException if set to true, * serialize the inner exception if $exception is an ODataException. * * @return string */ public static function serializeException(ODataException $exception, $serializeInnerException) { $xmlWriter = new \XMLWriter(); $xmlWriter->openMemory(); $xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); $xmlWriter->setIndent(4); $xmlWriter->startElement(ODataConstants::XML_ERROR_ELEMENT_NAME); //$xmlWriter->writeAttributeNs( // ODataConstants::XMLNS_NAMESPACE_PREFIX, // ODataConstants::XML_NAMESPACE_PREFIX, // ODataConstants::XML_NAMESPACE, // null //); $xmlWriter->writeAttribute(ODataConstants::XMLNS_NAMESPACE_PREFIX, ODataConstants::ODATA_METADATA_NAMESPACE); $xmlWriter->endAttribute(); $xmlWriter->startElement(ODataConstants::XML_ERROR_CODE_ELEMENT_NAME); if ($exception->getCode() != null) { $xmlWriter->text($exception->getCode()); } $xmlWriter->endElement(); $xmlWriter->startElement(ODataConstants::XML_ERROR_MESSAGE_ELEMENT_NAME); $xmlWriter->text($exception->getMessage()); $xmlWriter->endElement(); $xmlWriter->endElement(); $xmlWriter->endDocument(); return $xmlWriter->outputMemory(true); }
/** * Throws parser error. * * @param string $message The error message. * * @return void * * @throws ODataException */ private function _parseError($message) { throw ODataException::createSyntaxError($message); }
/** * Ask data service to load stream provider instance. * * @return void * * @throws ODataException */ private function _loadStreamProvider() { if (is_null($this->_streamProvider)) { $maxServiceVersion = $this->_service->getConfiguration()->getMaxDataServiceVersion(); if ($maxServiceVersion->compare(new Version(3, 0)) >= 0) { $this->_streamProvider = $this->_service->getService('IStreamProvider2'); if (!is_null($this->_streamProvider) && (!is_object($this->_streamProvider) || !$this->_streamProvider instanceof IStreamProvider2)) { throw ODataException::createInternalServerError(Messages::streamProviderWrapperInvalidStream2Instance()); } } if (is_null($this->_streamProvider)) { $this->_streamProvider = $this->_service->getService('IStreamProvider'); if (!is_null($this->_streamProvider) && (!is_object($this->_streamProvider) || !$this->_streamProvider instanceof IStreamProvider)) { throw ODataException::createInternalServerError(Messages::streamProviderWrapperInvalidStreamInstance()); } } } }