/** * Serialize the requested resource. * * @param RequestDescription &$requestDescription The description of the request * submitted by the client. * @param UriProcessor &$uriProcessor Reference to the uri processor. * * @return void */ protected function serializeResult(RequestDescription &$requestDescription, UriProcessor &$uriProcessor) { $isETagHeaderAllowed = $requestDescription->isETagHeaderAllowed(); if ($this->_dataServiceConfiguration->getValidateETagHeader() && !$isETagHeaderAllowed) { if (!is_null($this->_dataServiceHost->getRequestIfMatch()) || !is_null($this->_dataServiceHost->getRequestIfNoneMatch())) { ODataException::createBadRequestError(Messages::dataServiceETagCannotBeSpecified($this->getHost()->getAbsoluteRequestUri()->getUrlAsString())); } } $responseContentType = null; $responseFormat = self::getResponseFormat($requestDescription, $uriProcessor, $this, $responseContentType); $odataModelInstance = null; $hasResponseBody = true; // Execution required at this point if request target to any resource // other than // (1) media resource - For Media resource 'getResponseFormat' already // performed execution // (2) metadata - internal resource // (3) service directory - internal resource if ($requestDescription->needExecution()) { $uriProcessor->execute(); $objectModelSerializer = new ObjectModelSerializer($this, $requestDescription); if (!$requestDescription->isSingleResult()) { // Code path for collection (feed or links) $entryObjects = $requestDescription->getTargetResult(); self::assert(!is_null($entryObjects) && is_array($entryObjects), '!is_null($entryObjects) && is_array($entryObjects)'); // If related resource set is empty for an entry then we should // not throw error instead response must be empty feed or empty links if ($requestDescription->isLinkUri()) { $odataModelInstance = $objectModelSerializer->writeUrlElements($entryObjects); self::assert($odataModelInstance instanceof \ODataProducer\ObjectModel\ODataURLCollection, '$odataModelInstance instanceof ODataURLCollection'); } else { $odataModelInstance = $objectModelSerializer->writeTopLevelElements($entryObjects); self::assert($odataModelInstance instanceof \ODataProducer\ObjectModel\ODataFeed, '$odataModelInstance instanceof ODataFeed'); } } else { // Code path for entry, complex, bag, resource reference link, // primitive type or primitive value $result = $requestDescription->getTargetResult(); $requestTargetKind = $requestDescription->getTargetKind(); if ($requestDescription->isLinkUri()) { // In the query 'Orders(1245)/$links/Customer', the targetted // Customer might be null if (is_null($result)) { ODataException::createResourceNotFoundError($requestDescription->getIdentifier()); } $odataModelInstance = $objectModelSerializer->writeUrlElement($result); } else { if ($requestTargetKind == RequestTargetKind::RESOURCE) { if (!is_null($this->_dataServiceHost->getRequestIfMatch()) && !is_null($this->_dataServiceHost->getRequestIfNoneMatch())) { ODataException::createBadRequestError(Messages::dataServiceBothIfMatchAndIfNoneMatchHeaderSpecified()); } // handle entry resource $needToSerializeResponse = true; $targetResourceType = $requestDescription->getTargetResourceType(); $eTag = $this->compareETag($result, $targetResourceType, $needToSerializeResponse); if ($needToSerializeResponse) { if (is_null($result)) { // In the query 'Orders(1245)/Customer', the targetted // Customer might be null // set status code to 204 => 'No Content' $this->_dataServiceHost->setResponseStatusCode(HttpStatus::CODE_NOCONTENT); $hasResponseBody = false; } else { $odataModelInstance = $objectModelSerializer->writeTopLevelElement($result); } } else { // Resource is not modified so set status code // to 304 => 'Not Modified' $this->_dataServiceHost->setResponseStatusCode(HttpStatus::CODE_NOT_MODIFIED); $hasResponseBody = false; } // if resource has eTagProperty then eTag header needs to written if (!is_null($eTag)) { $this->_dataServiceHost->setResponseETag($eTag); } } else { if ($requestTargetKind == RequestTargetKind::COMPLEX_OBJECT) { $odataModelInstance = new ODataPropertyContent(); $targetResourceTypeComplex = $requestDescription->getTargetResourceType(); $objectModelSerializer->writeTopLevelComplexObject($result, $requestDescription->getProjectedProperty()->getName(), $targetResourceTypeComplex, $odataModelInstance); } else { if ($requestTargetKind == RequestTargetKind::BAG) { $odataModelInstance = new ODataPropertyContent(); $targetResourceTypeBag = $requestDescription->getTargetResourceType(); $objectModelSerializer->writeTopLevelBagObject($result, $requestDescription->getProjectedProperty()->getName(), $targetResourceTypeBag, $odataModelInstance); } else { if ($requestTargetKind == RequestTargetKind::PRIMITIVE) { $odataModelInstance = new ODataPropertyContent(); $projectedProperty = $requestDescription->getProjectedProperty(); $objectModelSerializer->writeTopLevelPrimitive($result, $projectedProperty, $odataModelInstance); } else { if ($requestTargetKind == RequestTargetKind::PRIMITIVE_VALUE) { // Code path for primitive value (Since its primitve no need for // object model serialization) // Customers('ANU')/CompanyName/$value => string // Employees(1)/Photo/$value => binary stream // Customers/$count => string } else { self::assert(false, 'Unexpected resource target kind'); } } } } } } } } //Note: Response content type can be null for named stream if ($hasResponseBody && !is_null($responseContentType)) { if ($responseFormat != ResponseFormat::BINARY) { $responseContentType .= ';charset=utf-8'; } } if ($hasResponseBody) { ResponseWriter::write($this, $requestDescription, $odataModelInstance, $responseContentType, $responseFormat); } }
/** * Create SegmentDescriptor for the first segment * * @param string $segmentIdentifier The identifier part of the * first segment * @param string $keyPredicate The predicate part of the first * segment if any else NULL * @param boolean $checkRights Whether to check the rights on * this segment * * @return SegmentDescriptor Descriptor for the first segment * * @throws ODataException Exception if any validation fails */ private function _createFirstSegmentDescriptor($segmentIdentifier, $keyPredicate, $checkRights) { $descriptor = new SegmentDescriptor(); $descriptor->setIdentifier($segmentIdentifier); if ($segmentIdentifier === ODataConstants::URI_METADATA_SEGMENT) { $this->_assertion(is_null($keyPredicate)); $descriptor->setTargetKind(RequestTargetKind::METADATA); return $descriptor; } if ($segmentIdentifier === ODataConstants::URI_BATCH_SEGMENT) { $this->_assertion(is_null($keyPredicate)); $descriptor->setTargetKind(RequestTargetKind::BATCH); return $descriptor; } if ($segmentIdentifier === ODataConstants::URI_COUNT_SEGMENT) { ODataException::createBadRequestError(Messages::segmentParserSegmentNotAllowedOnRoot(ODataConstants::URI_COUNT_SEGMENT)); } if ($segmentIdentifier === ODataConstants::URI_LINK_SEGMENT) { ODataException::createBadRequestError(Messages::segmentParserSegmentNotAllowedOnRoot(ODataConstants::URI_LINK_SEGMENT)); } $resourceSetWrapper = $this->_providerWrapper->resolveResourceSet($segmentIdentifier); if ($resourceSetWrapper === null) { ODataException::createResourceNotFoundError($segmentIdentifier); } $descriptor->setTargetResourceSetWrapper($resourceSetWrapper); $descriptor->setTargetResourceType($resourceSetWrapper->getResourceType()); $descriptor->setTargetSource(RequestTargetSource::ENTITY_SET); $descriptor->setTargetKind(RequestTargetKind::RESOURCE); if ($keyPredicate !== null) { $keyDescriptor = $this->_createKeyDescriptor($segmentIdentifier . '(' . $keyPredicate . ')', $resourceSetWrapper->getResourceType(), $keyPredicate); $descriptor->setKeyDescriptor($keyDescriptor); if (!$keyDescriptor->isEmpty()) { $descriptor->setSingleResult(true); } } if ($checkRights) { $resourceSetWrapper->checkResourceSetRightsForRead($descriptor->isSingleResult()); } return $descriptor; }
/** * 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)) { ODataException::createResourceNotFoundError($resourceSet->getName()); } $entityName = $resourceSet->getResourceType()->getInstanceType()->getName(); if (!is_object($entityInstance) || !$entityInstance instanceof $entityName) { ODataException::createInternalServerError(Messages::metadataQueryProviderWrapperIDSQPMethodReturnsUnExpectedType($entityName, $methodName)); } foreach ($keyDescriptor->getValidatedNamedValues() as $keyName => $valueDescription) { try { $keyProperty = new \ReflectionProperty($entityInstance, $keyName); $keyValue = $keyProperty->getValue($entityInstance); if (is_null($keyValue)) { ODataException::createInternalServerError(Messages::metadataQueryProviderWrapperIDSQPMethodReturnsInstanceWithNullKeyProperties($methodName)); } $convertedValue = $valueDescription[1]->convert($valueDescription[0]); if ($keyValue != $convertedValue) { ODataException::createInternalServerError(Messages::metadataQueryProviderWrapperIDSQPMethodReturnsInstanceWithNonMatchingKeys($methodName)); } } catch (\ReflectionException $reflectionException) { //throw ODataException::createInternalServerError( // Messages::orderByParserFailedToAccessOrInitializeProperty( // $resourceProperty->getName(), $resourceType->getName() // ) //); } } }
/** * Execute the client submitted request aganist the data source. * * @return void */ public function execute() { $segmentDescriptors =& $this->_requestDescription->getSegmentDescriptors(); foreach ($segmentDescriptors as $segmentDescriptor) { $requestTargetKind = $segmentDescriptor->getTargetKind(); if ($segmentDescriptor->getTargetSource() == RequestTargetSource::ENTITY_SET) { $this->_handleSegmentTargetsToResourceSet($segmentDescriptor); } else { if ($requestTargetKind == RequestTargetKind::RESOURCE) { if (is_null($segmentDescriptor->getPrevious()->getResult())) { ODataException::createResourceNotFoundError($segmentDescriptor->getPrevious()->getIdentifier()); } $this->_handleSegmentTargetsToRelatedResource($segmentDescriptor); } else { if ($requestTargetKind == RequestTargetKind::LINK) { $segmentDescriptor->setResult($segmentDescriptor->getPrevious()->getResult()); } else { if ($segmentDescriptor->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) { // we are done, $count will the last segment and // taken care by _applyQueryOptions method $segmentDescriptor->setResult($this->_requestDescription->getCountValue()); break; } else { if ($requestTargetKind == RequestTargetKind::MEDIA_RESOURCE) { if (is_null($segmentDescriptor->getPrevious()->getResult())) { ODataException::createResourceNotFoundError($segmentDescriptor->getPrevious()->getIdentifier()); } // For MLE and Named Stream the result of last segment // should be that of previous segment, this is required // while retriving content type or stream from IDSSP $segmentDescriptor->setResult($segmentDescriptor->getPrevious()->getResult()); // we are done, as named stream property or $value on // media resource will be the last segment break; } else { $value = $segmentDescriptor->getPrevious()->getResult(); while (!is_null($segmentDescriptor)) { if (is_null($value)) { $value = null; } else { try { $property = new \ReflectionProperty($value, $segmentDescriptor->getIdentifier()); $value = $property->getValue($value); } catch (\ReflectionException $reflectionException) { //throw ODataException::createInternalServerError(Messages::orderByParserFailedToAccessOrInitializeProperty($resourceProperty->getName(), $resourceType->getName())); } } $segmentDescriptor->setResult($value); $segmentDescriptor = $segmentDescriptor->getNext(); if (!is_null($segmentDescriptor) && $segmentDescriptor->getIdentifier() == ODataConstants::URI_VALUE_SEGMENT) { $segmentDescriptor->setResult($value); $segmentDescriptor = $segmentDescriptor->getNext(); } } //done, exit from outer loop as inner while complete traversal. break; } } } } } if (is_null($segmentDescriptor->getNext()) || $segmentDescriptor->getNext()->getIdentifier() == ODataConstants::URI_COUNT_SEGMENT) { $this->_applyQueryOptions($segmentDescriptor); } } // 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(); }