| Total Complexity | 114 | 
| Total Lines | 856 | 
| Duplicated Lines | 0 % | 
| Changes | 9 | ||
| Bugs | 2 | Features | 0 | 
Complex classes like BaseService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use BaseService, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 63 | abstract class BaseService implements IRequestHandler, IService  | 
            ||
| 64 | { | 
            ||
| 65 | /**  | 
            ||
| 66 | * The wrapper over IQueryProvider and IMetadataProvider implementations.  | 
            ||
| 67 | *  | 
            ||
| 68 | * @var ProvidersWrapper  | 
            ||
| 69 | */  | 
            ||
| 70 | private $providersWrapper;  | 
            ||
| 71 | |||
| 72 | /**  | 
            ||
| 73 | * The wrapper over IStreamProvider implementation.  | 
            ||
| 74 | *  | 
            ||
| 75 | * @var StreamProviderWrapper  | 
            ||
| 76 | */  | 
            ||
| 77 | protected $streamProvider;  | 
            ||
| 78 | |||
| 79 | /**  | 
            ||
| 80 | * Hold reference to the ServiceHost instance created by dispatcher,  | 
            ||
| 81 | * using this library can access headers and body of Http Request  | 
            ||
| 82 | * dispatcher received and the Http Response Dispatcher is going to send.  | 
            ||
| 83 | *  | 
            ||
| 84 | * @var ServiceHost  | 
            ||
| 85 | */  | 
            ||
| 86 | private $serviceHost;  | 
            ||
| 87 | |||
| 88 | /**  | 
            ||
| 89 | * To hold reference to ServiceConfiguration instance where the  | 
            ||
| 90 | * service specific rules (page limit, resource set access rights  | 
            ||
| 91 | * etc...) are defined.  | 
            ||
| 92 | *  | 
            ||
| 93 | * @var IServiceConfiguration  | 
            ||
| 94 | */  | 
            ||
| 95 | protected $config;  | 
            ||
| 96 | |||
| 97 | /**  | 
            ||
| 98 | * Hold reference to object serialiser - bit wot turns PHP objects  | 
            ||
| 99 | * into message traffic on wire.  | 
            ||
| 100 | *  | 
            ||
| 101 | * @var IObjectSerialiser  | 
            ||
| 102 | */  | 
            ||
| 103 | protected $objectSerialiser;  | 
            ||
| 104 | |||
| 105 | /**  | 
            ||
| 106 | * Get reference to object serialiser - bit wot turns PHP objects  | 
            ||
| 107 | * into message traffic on wire.  | 
            ||
| 108 | *  | 
            ||
| 109 | * @return IObjectSerialiser  | 
            ||
| 110 | */  | 
            ||
| 111 | public function getObjectSerialiser(): IObjectSerialiser  | 
            ||
| 116 | }  | 
            ||
| 117 | |||
| 118 | /**  | 
            ||
| 119 | * BaseService constructor.  | 
            ||
| 120 | * @param IObjectSerialiser|null $serialiser  | 
            ||
| 121 | * @param IMetadataProvider|null $metaProvider  | 
            ||
| 122 | * @param IServiceConfiguration|null $config  | 
            ||
| 123 | * @throws \Exception  | 
            ||
| 124 | */  | 
            ||
| 125 | protected function __construct(  | 
            ||
| 126 | IObjectSerialiser $serialiser = null,  | 
            ||
| 127 | IMetadataProvider $metaProvider = null,  | 
            ||
| 128 | IServiceConfiguration $config = null  | 
            ||
| 129 |     ) { | 
            ||
| 130 |         if (null != $serialiser) { | 
            ||
| 131 | $serialiser->setService($this);  | 
            ||
| 132 |         } else { | 
            ||
| 133 | $serialiser = new ObjectModelSerializer($this, null);  | 
            ||
| 134 | }  | 
            ||
| 135 | $this->config = $config ?? $this->initializeDefaultConfig(new ServiceConfiguration($metaProvider));  | 
            ||
| 136 | $this->objectSerialiser = $serialiser;  | 
            ||
| 137 | }  | 
            ||
| 138 | |||
| 139 | /**  | 
            ||
| 140 | * Gets reference to ServiceConfiguration instance so that  | 
            ||
| 141 | * service specific rules defined by the developer can be  | 
            ||
| 142 | * accessed.  | 
            ||
| 143 | *  | 
            ||
| 144 | * @return IServiceConfiguration  | 
            ||
| 145 | */  | 
            ||
| 146 | public function getConfiguration(): IServiceConfiguration  | 
            ||
| 147 |     { | 
            ||
| 148 | assert(null != $this->config);  | 
            ||
| 149 | |||
| 150 | return $this->config;  | 
            ||
| 151 | }  | 
            ||
| 152 | |||
| 153 | //TODO: shouldn't we hide this from the interface..if we need it at all.  | 
            ||
| 154 | |||
| 155 | /**  | 
            ||
| 156 | * Get the wrapper over developer's IQueryProvider and IMetadataProvider implementation.  | 
            ||
| 157 | *  | 
            ||
| 158 | * @return ProvidersWrapper  | 
            ||
| 159 | */  | 
            ||
| 160 | public function getProvidersWrapper(): ProvidersWrapper  | 
            ||
| 161 |     { | 
            ||
| 162 | return $this->providersWrapper;  | 
            ||
| 163 | }  | 
            ||
| 164 | |||
| 165 | /**  | 
            ||
| 166 | * Gets reference to wrapper class instance over IDSSP implementation.  | 
            ||
| 167 | *  | 
            ||
| 168 | * @return StreamProviderWrapper  | 
            ||
| 169 | */  | 
            ||
| 170 | public function getStreamProviderWrapper()  | 
            ||
| 171 |     { | 
            ||
| 172 | return $this->streamProvider;  | 
            ||
| 173 | }  | 
            ||
| 174 | |||
| 175 | /**  | 
            ||
| 176 | * Get reference to the data service host instance.  | 
            ||
| 177 | *  | 
            ||
| 178 | * @return ServiceHost  | 
            ||
| 179 | */  | 
            ||
| 180 | public function getHost(): ServiceHost  | 
            ||
| 181 |     { | 
            ||
| 182 | assert(null != $this->serviceHost);  | 
            ||
| 183 | |||
| 184 | return $this->serviceHost;  | 
            ||
| 185 | }  | 
            ||
| 186 | |||
| 187 | /**  | 
            ||
| 188 | * Sets the data service host instance.  | 
            ||
| 189 | *  | 
            ||
| 190 | * @param ServiceHost $serviceHost The data service host instance  | 
            ||
| 191 | */  | 
            ||
| 192 | public function setHost(ServiceHost $serviceHost): void  | 
            ||
| 193 |     { | 
            ||
| 194 | $this->serviceHost = $serviceHost;  | 
            ||
| 195 | }  | 
            ||
| 196 | |||
| 197 | /**  | 
            ||
| 198 | * To get reference to operation context where we have direct access to  | 
            ||
| 199 | * headers and body of Http Request, we have received and the Http Response  | 
            ||
| 200 | * We are going to send.  | 
            ||
| 201 | *  | 
            ||
| 202 | * @return IOperationContext  | 
            ||
| 203 | */  | 
            ||
| 204 | public function getOperationContext(): IOperationContext  | 
            ||
| 205 |     { | 
            ||
| 206 | return $this->getHost()->getOperationContext();  | 
            ||
| 207 | }  | 
            ||
| 208 | |||
| 209 | /**  | 
            ||
| 210 | * Get reference to the wrapper over IStreamProvider or  | 
            ||
| 211 | * IStreamProvider2 implementations.  | 
            ||
| 212 | *  | 
            ||
| 213 | * @return StreamProviderWrapper  | 
            ||
| 214 | */  | 
            ||
| 215 | public function getStreamProvider(): StreamProviderWrapper  | 
            ||
| 216 |     { | 
            ||
| 217 |         if (null === $this->streamProvider) { | 
            ||
| 218 | $this->streamProvider = new StreamProviderWrapper();  | 
            ||
| 219 | $this->streamProvider->setService($this);  | 
            ||
| 220 | }  | 
            ||
| 221 | |||
| 222 | return $this->streamProvider;  | 
            ||
| 223 | }  | 
            ||
| 224 | |||
| 225 | /**  | 
            ||
| 226 | * Top-level handler invoked by Dispatcher against any request to this  | 
            ||
| 227 | * service. This method will hand over request processing task to other  | 
            ||
| 228 | * functions which process the request, set required headers and Response  | 
            ||
| 229 | * stream (if any in Atom/Json format) in  | 
            ||
| 230 | * WebOperationContext::Current()::OutgoingWebResponseContext.  | 
            ||
| 231 | * Once this function returns, dispatcher uses global WebOperationContext  | 
            ||
| 232 | * to write out the request response to client.  | 
            ||
| 233 | * This function will perform the following operations:  | 
            ||
| 234 | * (1) Check whether the top level service class implements  | 
            ||
| 235 | * IServiceProvider which means the service is a custom service, in  | 
            ||
| 236 | * this case make sure the top level service class implements  | 
            ||
| 237 | * IMetaDataProvider and IQueryProvider.  | 
            ||
| 238 | * These are the minimal interfaces that a custom service to be  | 
            ||
| 239 | * implemented in order to expose its data as OData. Save reference to  | 
            ||
| 240 | * These interface implementations.  | 
            ||
| 241 | * NOTE: Here we will ensure only providers for IDSQP and IDSMP. The  | 
            ||
| 242 | * IDSSP will be ensured only when there is an GET request on MLE/Named  | 
            ||
| 243 | * stream.  | 
            ||
| 244 | *  | 
            ||
| 245 | * (2). Invoke 'Initialize' method of top level service for  | 
            ||
| 246 | * collecting the configuration rules set by the developer for this  | 
            ||
| 247 | * service.  | 
            ||
| 248 | *  | 
            ||
| 249 | * (3). Invoke the Uri processor to process the request URI. The uri  | 
            ||
| 250 | * processor will do the following:  | 
            ||
| 251 | * (a). Validate the request uri syntax using OData uri rules  | 
            ||
| 252 | * (b). Validate the request using metadata of this service  | 
            ||
| 253 | * (c). Parse the request uri and using, IQueryProvider  | 
            ||
| 254 | * implementation, fetches the resources pointed by the uri  | 
            ||
| 255 | * if required  | 
            ||
| 256 | * (d). Build a RequestDescription which encapsulate everything  | 
            ||
| 257 | * related to request uri (e.g. type of resource, result  | 
            ||
| 258 | * etc...)  | 
            ||
| 259 | * (3). Invoke handleRequest2 for further processing  | 
            ||
| 260 | * @throws ODataException  | 
            ||
| 261 | */  | 
            ||
| 262 | public function handleRequest()  | 
            ||
| 263 |     { | 
            ||
| 264 |         try { | 
            ||
| 265 | $this->createProviders();  | 
            ||
| 266 | $this->getHost()->validateQueryParameters();  | 
            ||
| 267 | $uriProcessor = UriProcessorNew::process($this);  | 
            ||
| 268 | $request = $uriProcessor->getRequest();  | 
            ||
| 269 |             if (TargetKind::BATCH() == $request->getTargetKind()) { | 
            ||
| 270 | //dd($request);  | 
            ||
| 271 | $this->getProvidersWrapper()->startTransaction(true);  | 
            ||
| 272 |                 try { | 
            ||
| 273 | $this->handleBatchRequest($request);  | 
            ||
| 274 |                 } catch (\Exception $ex) { | 
            ||
| 275 | $this->getProvidersWrapper()->rollBackTransaction();  | 
            ||
| 276 | throw $ex;  | 
            ||
| 277 | }  | 
            ||
| 278 | $this->getProvidersWrapper()->commitTransaction();  | 
            ||
| 279 |             } else { | 
            ||
| 280 | $this->serializeResult($request, $uriProcessor);  | 
            ||
| 281 | }  | 
            ||
| 282 |         } catch (\Exception $exception) { | 
            ||
| 283 | ErrorHandler::handleException($exception, $this);  | 
            ||
| 284 | // Return to dispatcher for writing serialized exception  | 
            ||
| 285 | return;  | 
            ||
| 286 | }  | 
            ||
| 287 | }  | 
            ||
| 288 | |||
| 289 | /**  | 
            ||
| 290 | * @param $request  | 
            ||
| 291 | * @throws ODataException  | 
            ||
| 292 | */  | 
            ||
| 293 | private function handleBatchRequest($request)  | 
            ||
| 305 | }  | 
            ||
| 306 | |||
| 307 | /**  | 
            ||
| 308 | * @return IQueryProvider|null  | 
            ||
| 309 | */  | 
            ||
| 310 | abstract public function getQueryProvider(): ?IQueryProvider;  | 
            ||
| 311 | |||
| 312 | /**  | 
            ||
| 313 | * @return IMetadataProvider  | 
            ||
| 314 | */  | 
            ||
| 315 | abstract public function getMetadataProvider();  | 
            ||
| 316 | |||
| 317 | /**  | 
            ||
| 318 | * @return \POData\Providers\Stream\IStreamProvider2  | 
            ||
| 319 | */  | 
            ||
| 320 | abstract public function getStreamProviderX();  | 
            ||
| 321 | |||
| 322 | /** @var ODataWriterRegistry */  | 
            ||
| 323 | protected $writerRegistry;  | 
            ||
| 324 | |||
| 325 | /** @var ODataReaderRegistry */  | 
            ||
| 326 | protected $readerRegistry;  | 
            ||
| 327 | |||
| 328 | /**  | 
            ||
| 329 | * Returns the ODataWriterRegistry to use when writing the response to a service document or resource request.  | 
            ||
| 330 | *  | 
            ||
| 331 | * @return ODataWriterRegistry  | 
            ||
| 332 | */  | 
            ||
| 333 | public function getODataWriterRegistry(): ODataWriterRegistry  | 
            ||
| 334 |     { | 
            ||
| 335 | assert(null != $this->writerRegistry);  | 
            ||
| 336 | |||
| 337 | return $this->writerRegistry;  | 
            ||
| 338 | }  | 
            ||
| 339 | |||
| 340 | /**  | 
            ||
| 341 | * Returns the ODataReaderRegistry to use when writing the response to a service document or resource request.  | 
            ||
| 342 | *  | 
            ||
| 343 | * @return ODataReaderRegistry  | 
            ||
| 344 | */  | 
            ||
| 345 | public function getODataReaderRegistry(): ODataReaderRegistry  | 
            ||
| 350 | }  | 
            ||
| 351 | /**  | 
            ||
| 352 | * This method will query and validates for IMetadataProvider and IQueryProvider implementations, invokes  | 
            ||
| 353 | * BaseService::Initialize to initialize service specific policies.  | 
            ||
| 354 | *  | 
            ||
| 355 | * @throws ODataException  | 
            ||
| 356 | * @throws \Exception  | 
            ||
| 357 | */  | 
            ||
| 358 | protected function createProviders()  | 
            ||
| 388 | }  | 
            ||
| 389 | |||
| 390 | //TODO: i don't want this to be public..but it's the only way to test it right now...  | 
            ||
| 391 | |||
| 392 | /**  | 
            ||
| 393 | * @throws \Exception  | 
            ||
| 394 | */  | 
            ||
| 395 | public function registerWriters()  | 
            ||
| 396 |     { | 
            ||
| 397 | $registry = $this->getODataWriterRegistry();  | 
            ||
| 398 | $serviceVersion = $this->getConfiguration()->getMaxDataServiceVersion();  | 
            ||
| 399 | $serviceURI = $this->getHost()->getAbsoluteServiceUri()->getUrlAsString();  | 
            ||
| 400 | |||
| 401 | //We always register the v1 stuff  | 
            ||
| 402 | $registry->register(new JsonODataV1Writer($this->getConfiguration()->getLineEndings(), $this->getConfiguration()->getPrettyOutput()));  | 
            ||
| 403 | $registry->register(new AtomODataWriter($this->getConfiguration()->getLineEndings(), $this->getConfiguration()->getPrettyOutput(),$serviceURI));  | 
            ||
| 404 | |||
| 405 |         if (-1 < $serviceVersion->compare(Version::v2())) { | 
            ||
| 406 | $registry->register(new JsonODataV2Writer($this->getConfiguration()->getLineEndings(), $this->getConfiguration()->getPrettyOutput()));  | 
            ||
| 407 | }  | 
            ||
| 408 | |||
| 409 |         if (-1 < $serviceVersion->compare(Version::v3())) { | 
            ||
| 410 | $registry->register(new JsonLightODataWriter($this->getConfiguration()->getLineEndings(), $this->getConfiguration()->getPrettyOutput(),JsonLightMetadataLevel::NONE(), $serviceURI));  | 
            ||
| 411 | $registry->register(new JsonLightODataWriter($this->getConfiguration()->getLineEndings(), $this->getConfiguration()->getPrettyOutput(),JsonLightMetadataLevel::MINIMAL(), $serviceURI));  | 
            ||
| 412 | $registry->register(new JsonLightODataWriter($this->getConfiguration()->getLineEndings(), $this->getConfiguration()->getPrettyOutput(),JsonLightMetadataLevel::FULL(), $serviceURI));  | 
            ||
| 413 | }  | 
            ||
| 414 | }  | 
            ||
| 415 | |||
| 416 | public function registerReaders()  | 
            ||
| 417 |     { | 
            ||
| 418 | $registry = $this->getODataReaderRegistry();  | 
            ||
| 419 | //We always register the v1 stuff  | 
            ||
| 420 | $registry->register(new AtomODataReader());  | 
            ||
| 421 | }  | 
            ||
| 422 | |||
| 423 | /**  | 
            ||
| 424 | * Serialize the requested resource.  | 
            ||
| 425 | *  | 
            ||
| 426 | * @param RequestDescription $request The description of the request submitted by the client  | 
            ||
| 427 | * @param IUriProcessor $uriProcessor Reference to the uri processor  | 
            ||
| 428 | *  | 
            ||
| 429 | * @throws Common\HttpHeaderFailure  | 
            ||
| 430 | * @throws Common\UrlFormatException  | 
            ||
| 431 | * @throws InvalidOperationException  | 
            ||
| 432 | * @throws ODataException  | 
            ||
| 433 | * @throws \ReflectionException  | 
            ||
| 434 | * @throws \Exception  | 
            ||
| 435 | */  | 
            ||
| 436 | protected function serializeResult(RequestDescription $request, IUriProcessor $uriProcessor)  | 
            ||
| 613 | }  | 
            ||
| 614 | }  | 
            ||
| 615 | |||
| 616 | /**  | 
            ||
| 617 | * Gets the response format for the requested resource.  | 
            ||
| 618 | *  | 
            ||
| 619 | * @param RequestDescription $request The request submitted by client and it's execution result  | 
            ||
| 620 | * @param IUriProcessor $uriProcessor The reference to the IUriProcessor  | 
            ||
| 621 | *  | 
            ||
| 622 | * @throws Common\HttpHeaderFailure  | 
            ||
| 623 | * @throws InvalidOperationException  | 
            ||
| 624 | * @throws ODataException , HttpHeaderFailure  | 
            ||
| 625 | * @throws \ReflectionException  | 
            ||
| 626 | * @throws Common\UrlFormatException  | 
            ||
| 627 | * @return string|null the response content-type, a null value means the requested resource  | 
            ||
| 628 | * is named stream and IDSSP2::getStreamContentType returned null  | 
            ||
| 629 | */  | 
            ||
| 630 | public function getResponseContentType(  | 
            ||
| 760 | }  | 
            ||
| 761 | |||
| 762 | /**  | 
            ||
| 763 | * For the given entry object compare its eTag (if it has eTag properties)  | 
            ||
| 764 | * with current eTag request headers (if present).  | 
            ||
| 765 | *  | 
            ||
| 766 | * @param mixed &$entryObject entity resource for which etag  | 
            ||
| 767 | * needs to be checked  | 
            ||
| 768 | * @param ResourceType &$resourceType Resource type of the entry  | 
            ||
| 769 | * object  | 
            ||
| 770 | * @param bool &$needToSerializeResponse On return, this will contain  | 
            ||
| 771 | * True if response needs to be  | 
            ||
| 772 | * serialized, False otherwise  | 
            ||
| 773 | *  | 
            ||
| 774 | * @throws ODataException  | 
            ||
| 775 | * @throws InvalidOperationException  | 
            ||
| 776 | * @throws \ReflectionException  | 
            ||
| 777 | * @return string|null The ETag for the entry object if it has eTag properties  | 
            ||
| 778 | * NULL otherwise  | 
            ||
| 779 | */  | 
            ||
| 780 | protected function compareETag(  | 
            ||
| 860 | }  | 
            ||
| 861 | |||
| 862 | /**  | 
            ||
| 863 | * Returns the etag for the given resource.  | 
            ||
| 864 | * Note: This function will not add W\" prefix and " suffix, that is caller's  | 
            ||
| 865 | * responsibility.  | 
            ||
| 866 | *  | 
            ||
| 867 | * @param mixed &$entryObject Resource for which etag value needs to  | 
            ||
| 868 | * be returned  | 
            ||
| 869 | * @param ResourceType &$resourceType Resource type of the $entryObject  | 
            ||
| 870 | *  | 
            ||
| 871 | * @throws ODataException  | 
            ||
| 872 | * @throws InvalidOperationException  | 
            ||
| 873 | * @throws \ReflectionException  | 
            ||
| 874 | * @return string|null ETag value for the given resource (with values encoded  | 
            ||
| 875 | * for use in a URI) there are etag properties, NULL if  | 
            ||
| 876 | * there is no etag property  | 
            ||
| 877 | */  | 
            ||
| 878 | protected function getETagForEntry(&$entryObject, ResourceType &$resourceType): ?string  | 
            ||
| 914 | }  | 
            ||
| 915 | |||
| 916 | protected function initializeDefaultConfig(IServiceConfiguration $config)  | 
            ||
| 919 | }  | 
            ||
| 920 | }  | 
            ||
| 921 |