/** * Serialize the requested resource. * * @param RequestDescription $request The description of the request submitted by the client. * @param UriProcessor $uriProcessor Reference to the uri processor. * * @return void */ protected function serializeResult(RequestDescription $request, UriProcessor $uriProcessor) { $isETagHeaderAllowed = $request->isETagHeaderAllowed(); if ($this->config->getValidateETagHeader() && !$isETagHeaderAllowed) { if (!is_null($this->_serviceHost->getRequestIfMatch()) || !is_null($this->_serviceHost->getRequestIfNoneMatch())) { throw ODataException::createBadRequestError(Messages::eTagCannotBeSpecified($this->getHost()->getAbsoluteRequestUri()->getUrlAsString())); } } $responseContentType = self::getResponseContentType($request, $uriProcessor, $this); if (is_null($responseContentType) && $request->getTargetKind() != TargetKind::MEDIA_RESOURCE()) { //the responseContentType can ONLY be null if it's a stream (media resource) and that stream is storing null as the content type throw new ODataException(Messages::unsupportedMediaType(), 415); } $odataModelInstance = null; $hasResponseBody = true; // Execution required at this point if request target to any resource other than // // (1) media resource - For Media resource 'getResponseContentType' already performed execution as it needs to know the mime type of the stream // (2) metadata - internal resource // (3) service directory - internal resource if ($request->needExecution()) { $uriProcessor->execute(); $objectModelSerializer = new ObjectModelSerializer($this, $request); if (!$request->isSingleResult()) { // Code path for collection (feed or links) $entryObjects = $request->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 ($request->isLinkUri()) { $odataModelInstance = $objectModelSerializer->writeUrlElements($entryObjects); self::assert($odataModelInstance instanceof \POData\ObjectModel\ODataURLCollection, '$odataModelInstance instanceof ODataURLCollection'); } else { $odataModelInstance = $objectModelSerializer->writeTopLevelElements($entryObjects); self::assert($odataModelInstance instanceof \POData\ObjectModel\ODataFeed, '$odataModelInstance instanceof ODataFeed'); } } else { // Code path for entry, complex, bag, resource reference link, // primitive type or primitive value $result = $request->getTargetResult(); $requestTargetKind = $request->getTargetKind(); if ($request->isLinkUri()) { // In the query 'Orders(1245)/$links/Customer', the targeted // Customer might be null if (is_null($result)) { throw ODataException::createResourceNotFoundError($request->getIdentifier()); } $odataModelInstance = $objectModelSerializer->writeUrlElement($result); } else { if ($requestTargetKind == TargetKind::RESOURCE()) { if (!is_null($this->_serviceHost->getRequestIfMatch()) && !is_null($this->_serviceHost->getRequestIfNoneMatch())) { throw ODataException::createBadRequestError(Messages::bothIfMatchAndIfNoneMatchHeaderSpecified()); } // handle entry resource $needToSerializeResponse = true; $targetResourceType = $request->getTargetResourceType(); $eTag = $this->compareETag($result, $targetResourceType, $needToSerializeResponse); if ($needToSerializeResponse) { if (is_null($result)) { // In the query 'Orders(1245)/Customer', the targeted // Customer might be null // set status code to 204 => 'No Content' $this->_serviceHost->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->_serviceHost->setResponseStatusCode(HttpStatus::CODE_NOT_MODIFIED); $hasResponseBody = false; } // if resource has eTagProperty then eTag header needs to written if (!is_null($eTag)) { $this->_serviceHost->setResponseETag($eTag); } } else { if ($requestTargetKind == TargetKind::COMPLEX_OBJECT()) { $odataModelInstance = $objectModelSerializer->writeTopLevelComplexObject($result, $request->getProjectedProperty()->getName(), $request->getTargetResourceType()); } else { if ($requestTargetKind == TargetKind::BAG()) { $odataModelInstance = $objectModelSerializer->writeTopLevelBagObject($result, $request->getProjectedProperty()->getName(), $request->getTargetResourceType(), $odataModelInstance); } else { if ($requestTargetKind == TargetKind::PRIMITIVE()) { $odataModelInstance = $objectModelSerializer->writeTopLevelPrimitive($result, $request->getProjectedProperty(), $odataModelInstance); } else { if ($requestTargetKind == TargetKind::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 ($request->getTargetKind() != TargetKind::MEDIA_RESOURCE() && $responseContentType != MimeTypes::MIME_APPLICATION_OCTETSTREAM) { //append charset for everything except: //stream resources as they have their own content type //binary properties (they content type will be App Octet for those...is this a good way? we could also decide based upon the projected property // $responseContentType .= ';charset=utf-8'; } } if ($hasResponseBody) { ResponseWriter::write($this, $request, $odataModelInstance, $responseContentType); } }
/** * 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(TargetKind::METADATA()); return $descriptor; } if ($segmentIdentifier === ODataConstants::URI_BATCH_SEGMENT) { $this->_assertion(is_null($keyPredicate)); $descriptor->setTargetKind(TargetKind::BATCH()); return $descriptor; } if ($segmentIdentifier === ODataConstants::URI_COUNT_SEGMENT) { throw ODataException::createBadRequestError(Messages::segmentParserSegmentNotAllowedOnRoot(ODataConstants::URI_COUNT_SEGMENT)); } if ($segmentIdentifier === ODataConstants::URI_LINK_SEGMENT) { throw ODataException::createBadRequestError(Messages::segmentParserSegmentNotAllowedOnRoot(ODataConstants::URI_LINK_SEGMENT)); } $resourceSetWrapper = $this->providerWrapper->resolveResourceSet($segmentIdentifier); if ($resourceSetWrapper === null) { throw ODataException::createResourceNotFoundError($segmentIdentifier); } $descriptor->setTargetResourceSetWrapper($resourceSetWrapper); $descriptor->setTargetResourceType($resourceSetWrapper->getResourceType()); $descriptor->setTargetSource(TargetSource::ENTITY_SET); $descriptor->setTargetKind(TargetKind::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; }
/** * 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(); }
/** * 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() // ) //); } } }