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); }
/** * Top-level handler invoked by Dispatcher against any request to this * service. This method will hand over request processing task to other * functions which process the request, set required headers and Response * stream (if any in Atom/Json format) in * WebOperationContext::Current()::OutgoingWebResponseContext. * Once this function returns, dispatcher uses global WebOperationContext * to write out the request response to client. * This function will perform the following operations: * (1) Check whether the top level service class implements * IServiceProvider which means the service is a custom service, in * this case make sure the top level service class implements * IMetaDataProvider and IQueryProvider. * These are the minimal interfaces that a custom service to be * implemented in order to expose its data as OData. Save reference to * These interface implementations. * NOTE: Here we will ensure only providers for IDSQP and IDSMP. The * IDSSP will be ensured only when there is an GET request on MLE/Named * stream. * * (2). Invoke 'Initialize' method of top level service for * collecting the configuration rules set by the developer for this * service. * * (3). Invoke the Uri processor to process the request URI. The uri * processor will do the following: * (a). Validate the request uri syntax using OData uri rules * (b). Validate the request using metadata of this service * (c). Parse the request uri and using, IQueryProvider * implementation, fetches the resources pointed by the uri * if required * (d). Build a RequestDescription which encapsulate everything * related to request uri (e.g. type of resource, result * etc...) * (3). Invoke handleRequest2 for further processing * * @return void */ public function handleRequest() { try { $this->createProviders(); $this->_serviceHost->validateQueryParameters(); $requestMethod = $this->getOperationContext()->incomingRequest()->getMethod(); if ($requestMethod != HTTPRequestMethod::GET()) { throw ODataException::createNotImplementedError(Messages::onlyReadSupport($requestMethod)); } $uriProcessor = UriProcessor::process($this); $request = $uriProcessor->getRequest(); $this->serializeResult($request, $uriProcessor); } catch (\Exception $exception) { ErrorHandler::handleException($exception, $this); // Return to dispatcher for writing serialized exception return; } }
/** * 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; } } }
/** * For queries like http://localhost/NorthWind.svc/Customers(‘ALFKI’) */ public function getResourceFromResourceSet(ResourceSet $resourceSet, KeyDescriptor $keyDescriptor) { // lion: if ($_SERVER['REQUEST_METHOD'] == HTTPRequestMethod::DELETE()) { $res = $this->getResource($resourceSet, $keyDescriptor); if ($res != null) { // Delete object if found } return $res != null; } // -- return $this->getResource($resourceSet, $keyDescriptor); }
/** * Builds the key for the given entity instance. * Note: The generated key can be directly used in the uri, * this function will perform * required escaping of characters, for example: * Ships(ShipName='Antonio%20Moreno%20Taquer%C3%ADa',ShipID=123), * Note to method caller: Don't do urlencoding on * return value of this method as it already encoded. * * @param mixed $entityInstance Entity instance for which key value needs to be prepared. * @param ResourceType $resourceType Resource type instance containing metadata about the instance. * @param string $containerName Name of the entity set that the entity instance belongs to * . * * @return string Key for the given resource, with values encoded for use in a URI * . */ protected function getEntryInstanceKey($entityInstance, ResourceType $resourceType, $containerName) { // lion: if ($_SERVER['REQUEST_METHOD'] == HTTPRequestMethod::DELETE()) { return null; } // --- $keyProperties = $resourceType->getKeyProperties(); $this->assert(count($keyProperties) != 0, 'count($keyProperties) != 0'); $keyString = $containerName . '('; $comma = null; foreach ($keyProperties as $keyName => $resourceProperty) { $keyType = $resourceProperty->getInstanceType(); $this->assert($keyType instanceof IType, '$keyType instanceof IType'); $keyValue = $this->getPropertyValue($entityInstance, $resourceType, $resourceProperty); if (is_null($keyValue)) { throw ODataException::createInternalServerError(Messages::badQueryNullKeysAreNotSupported($resourceType->getName(), $keyName)); } $keyValue = $keyType->convertToOData($keyValue); $keyString .= $comma . $keyName . '=' . $keyValue; $comma = ','; } $keyString .= ')'; return $keyString; }
/** * Top-level handler invoked by Dispatcher against any request to this * service. This method will hand over request processing task to other * functions which process the request, set required headers and Response * stream (if any in Atom/Json format) in * WebOperationContext::Current()::OutgoingWebResponseContext. * Once this function returns, dispatcher uses global WebOperationContext * to write out the request response to client. * This function will perform the following operations: * (1) Check whether the top level service class implements * IServiceProvider which means the service is a custom service, in * this case make sure the top level service class implements * IMetaDataProvider and IQueryProvider. * These are the minimal interfaces that a custom service to be * implemented in order to expose its data as OData. Save reference to * These interface implementations. * NOTE: Here we will ensure only providers for IDSQP and IDSMP. The * IDSSP will be ensured only when there is an GET request on MLE/Named * stream. * * (2). Invoke 'Initialize' method of top level service for * collecting the configuration rules set by the developer for this * service. * * (3). Invoke the Uri processor to process the request URI. The uri * processor will do the following: * (a). Validate the request uri syntax using OData uri rules * (b). Validate the request using metadata of this service * (c). Parse the request uri and using, IQueryProvider * implementation, fetches the resources pointed by the uri * if required * (d). Build a RequestDescription which encapsulate everything * related to request uri (e.g. type of resource, result * etc...) * (3). Invoke handleRequest2 for further processing * * @return void */ public function handleRequest() { try { $this->createProviders(); $this->_serviceHost->validateQueryParameters(); $requestMethod = $this->getOperationContext()->incomingRequest()->getMethod(); // Lion: accept OPTIONS request if ($requestMethod == HTTPRequestMethod::OPTIONS()) { header('Allow: GET,PATCH,POST,PUT'); http_response_code(200); return; } // ---- // if ($requestMethod != HTTPRequestMethod::GET()) { // throw ODataException::createNotImplementedError(Messages::onlyReadSupport($requestMethod)); // } $uriProcessor = UriProcessor::process($this); $request = $uriProcessor->getRequest(); $this->serializeResult($request, $uriProcessor); } catch (\Exception $exception) { ErrorHandler::handleException($exception, $this); // Return to dispatcher for writing serialized exception return; } }
/** * 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() // ) //); } } }