Passed
Push — master ( 964ebf...626fc8 )
by Christopher
01:03
created

UriProcessorNew::executeGetResourceDirect()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 25
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 25
rs 8.5806
cc 4
eloc 20
nc 5
nop 1
1
<?php
2
3
namespace POData\UriProcessor;
4
5
use POData\Common\Messages;
6
use POData\Common\ODataConstants;
7
use POData\Common\ODataException;
8
use POData\IService;
9
use POData\OperationContext\HTTPRequestMethod;
10
use POData\Providers\Metadata\ResourcePropertyKind;
11
use POData\Providers\ProvidersWrapper;
12
use POData\Providers\Query\QueryResult;
13
use POData\Providers\Query\QueryType;
14
use POData\UriProcessor\Interfaces\IUriProcessor;
15
use POData\UriProcessor\QueryProcessor\QueryProcessor;
16
use POData\UriProcessor\ResourcePathProcessor\ResourcePathProcessor;
17
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\SegmentDescriptor;
18
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetKind;
19
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetSource;
20
21
/**
22
 * Class UriProcessorNew.
23
 *
24
 * A type to process client's requested URI
25
 * The syntax of request URI is:
26
 *  Scheme Host Port ServiceRoot ResourcePath ? QueryOption
27
 * For more details refer:
28
 * http://www.odata.org/developers/protocols/uri-conventions#UriComponents
29
 */
30
class UriProcessorNew implements IUriProcessor
31
{
32
    /**
33
     * Description of the OData request that a client has submitted.
34
     *
35
     * @var RequestDescription
36
     */
37
    private $request;
38
39
    /**
40
     * Holds reference to the data service instance.
41
     *
42
     * @var IService
43
     */
44
    private $service;
45
46
    /**
47
     * Holds reference to the wrapper over IDSMP and IDSQP implementation.
48
     *
49
     * @var ProvidersWrapper
50
     */
51
    private $providers;
52
53
    /**
54
     * Holds reference to request expander.
55
     *
56
     * @var RequestExpander
57
     */
58
    private $expander;
59
60
    /**
61
     * Constructs a new instance of UriProcessor.
62
     *
63
     * @param IService $service Reference to the data service instance
64
     */
65
    private function __construct(IService $service)
66
    {
67
        $this->service = $service;
68
        $this->providers = $service->getProvidersWrapper();
69
        $this->request = ResourcePathProcessor::process($service);
70
        $this->expander = new RequestExpander(
71
            $this->getRequest(),
72
            $this->getService(),
73
            $this->getProviders()
74
        );
75
        $this->getRequest()->setUriProcessor($this);
76
    }
77
78
    /**
79
     * Process the resource path and query options of client's request uri.
80
     *
81
     * @param IService $service Reference to the data service instance
82
     *
83
     * @throws ODataException
84
     *
85
     * @return IUriProcessor
86
     */
87
    public static function process(IService $service)
88
    {
89
        $absRequestUri = $service->getHost()->getAbsoluteRequestUri();
90
        $absServiceUri = $service->getHost()->getAbsoluteServiceUri();
91
92
        if (!$absServiceUri->isBaseOf($absRequestUri)) {
93
            throw ODataException::createInternalServerError(
94
                Messages::uriProcessorRequestUriDoesNotHaveTheRightBaseUri(
95
                    $absRequestUri->getUrlAsString(),
96
                    $absServiceUri->getUrlAsString()
97
                )
98
            );
99
        }
100
101
        $processor = new self($service);
102
103
        return $processor;
104
    }
105
106
    /**
107
     * Gets reference to the request submitted by client.
108
     *
109
     * @return RequestDescription
110
     */
111
    public function getRequest()
112
    {
113
        return $this->request;
114
    }
115
116
    /**
117
     * Gets reference to the request submitted by client.
118
     *
119
     * @return ProvidersWrapper
120
     */
121
    public function getProviders()
122
    {
123
        return $this->providers;
124
    }
125
126
    /**
127
     * Gets the data service instance.
128
     *
129
     * @return IService
130
     */
131
    public function getService()
132
    {
133
        return $this->service;
134
    }
135
136
    /**
137
     * Gets the request expander instance.
138
     *
139
     * @return RequestExpander
140
     */
141
    public function getExpander()
142
    {
143
        return $this->expander;
144
    }
145
146
    /**
147
     * Execute the client submitted request against the data source.
148
     */
149
    public function execute()
150
    {
151
        $service = $this->getService();
152
        $context = $service->getOperationContext();
153
        $method = $context->incomingRequest()->getMethod();
154
155
        switch ($method) {
156
            case HTTPRequestMethod::GET():
157
                $this->executeGet();
158
                break;
159
            case HTTPRequestMethod::DELETE():
160
                $this->executeGet();
161
                $this->executeDelete();
162
                break;
163
            case HTTPRequestMethod::PUT():
164
                $this->executeGet();
165
                $this->executePut();
166
                break;
167
            case HTTPRequestMethod::POST():
168
                $this->executePost();
169
                break;
170
            default:
171
                throw ODataException::createNotImplementedError(Messages::onlyReadSupport($method));
172
        }
173
174
        // Apply $select and $expand options to result set, this function will be always applied
175
        // irrespective of return value of IDSQP2::canApplyQueryOptions which means library will
176
        // not delegate $expand/$select operation to IDSQP2 implementation
177
        $this->getExpander()->handleExpansion();
178
    }
179
180
    /**
181
     * Execute the client submitted request against the data source (GET).
182
     */
183
    protected function executeGet()
184
    {
185
        $segments = $this->getRequest()->getSegments();
186
187
        foreach ($segments as $segment) {
188
            $requestTargetKind = $segment->getTargetKind();
189
190
            switch ($requestTargetKind) {
191
                case TargetKind::SINGLETON():
192
                    $this->executeGetSingleton($segment);
193
                    break;
194
                case TargetKind::RESOURCE():
195
                    $this->executeGetResource($segment);
196
                    break;
197
                case TargetKind::MEDIA_RESOURCE():
198
                    $segment->setResult($segment->getPrevious()->getResult());
199
                    // a media resource means we're done - bail out of segment processing
200
                    break 2;
201
                case TargetKind::LINK():
202
                    $this->executeGetLink($segment);
203
                    break;
204
                case TargetKind::PRIMITIVE():
205
                case TargetKind::PRIMITIVE_VALUE():
206
                case TargetKind::COMPLEX_OBJECT():
207
                case TargetKind::BAG():
208
                    break;
209
                default:
210
                    assert(false, "Not implemented yet");
211
            }
212
        }
213
    }
214
215
    /**
216
     * Execute the client submitted request against the data source (DELETE).
217
     */
218
    protected function executeDelete()
219
    {
220
        $segment = $this->getFinalEffectiveSegment();
221
        $requestMethod = $this->getService()->getOperationContext()->incomingRequest()->getMethod();
222
        $resourceSet = $segment->getTargetResourceSetWrapper();
223
        $keyDescriptor = $segment->getKeyDescriptor();
224
225
        $this->checkUriValidForSuppliedVerb($resourceSet, $keyDescriptor, $requestMethod);
226
        $this->getProviders()->deleteResource($resourceSet, $segment->getResult());
227
    }
228
229
    /**
230
     * Execute the client submitted request against the data source (PUT).
231
     */
232
    protected function executePut()
233
    {
234
        $segment = $this->getFinalEffectiveSegment();
235
        $requestMethod = $this->getService()->getOperationContext()->incomingRequest()->getMethod();
236
        $resourceSet = $segment->getTargetResourceSetWrapper();
237
        $keyDescriptor = $segment->getKeyDescriptor();
238
239
        $this->checkUriValidForSuppliedVerb($resourceSet, $keyDescriptor, $requestMethod);
240
241
        $data = $this->getRequest()->getData();
242
        if (!$data) {
243
            throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
244
        }
245
246
        $queryResult = $this->getProviders()->updateResource(
247
            $resourceSet,
248
            $segment->getResult(),
249
            $keyDescriptor,
250
            $data,
1 ignored issue
show
Documentation introduced by
$data is of type array, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
251
            false
252
        );
253
        $segment->setResult($queryResult);
254
    }
255
256
    /**
257
     * Execute the client submitted request against the data source (POST).
258
     */
259
    protected function executePost()
260
    {
261
        $segments = $this->getRequest()->getSegments();
262
        $requestMethod = $this->getService()->getOperationContext()->incomingRequest()->getMethod();
263
264
        foreach ($segments as $segment) {
265
            $requestTargetKind = $segment->getTargetKind();
266
            if ($requestTargetKind == TargetKind::RESOURCE()) {
267
                $resourceSet = $segment->getTargetResourceSetWrapper();
268
                $keyDescriptor = $segment->getKeyDescriptor();
269
270
                $data = $this->getRequest()->getData();
271
                if (empty($data)) {
272
                    throw ODataException::createBadRequestError(Messages::noDataForThisVerb($requestMethod));
273
                }
274
                $queryResult = $this->getProviders()->createResourceforResourceSet($resourceSet, $keyDescriptor, $data);
1 ignored issue
show
Documentation introduced by
$data is of type array, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
275
                $segment->setResult($queryResult);
276
            }
277
        }
278
    }
279
280
    /**
281
     * @return null|SegmentDescriptor
282
     */
283
    protected function getFinalEffectiveSegment()
284
    {
285
        $segment = $this->getRequest()->getLastSegment();
286
        // if last segment is $count, back up one
287
        if (ODataConstants::URI_COUNT_SEGMENT == $segment->getIdentifier()) {
288
            $segment = $segment->getPrevious();
289
            return $segment;
290
        }
291
        return $segment;
292
    }
293
294
    /**
295
     * @param $resourceSet
296
     * @param $keyDescriptor
297
     * @param $requestMethod
298
     * @throws ODataException
299
     */
300 View Code Duplication
    protected function checkUriValidForSuppliedVerb($resourceSet, $keyDescriptor, $requestMethod)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
301
    {
302
        if (!$resourceSet || !$keyDescriptor) {
303
            $url = $this->getService()->getHost()->getAbsoluteRequestUri()->getUrlAsString();
304
            throw ODataException::createBadRequestError(
305
                Messages::badRequestInvalidUriForThisVerb($url, $requestMethod)
306
            );
307
        }
308
    }
309
310
    /**
311
     * @param $segment
312
     */
313
    private function executeGetSingleton($segment)
314
    {
315
        $segmentId = $segment->getIdentifier();
316
        $singleton = $this->getService()->getProvidersWrapper()->resolveSingleton($segmentId);
317
        $segment->setResult($singleton->get());
318
    }
319
320
    /**
321
     * @param $segment
322
     */
323
    private function executeGetResource($segment)
324
    {
325
        $isRelated = $segment->getTargetSource() != TargetSource::ENTITY_SET;
326
        $queryResult = null;
0 ignored issues
show
Unused Code introduced by
$queryResult is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
327
        if (!$isRelated) {
328
            $queryResult = $this->executeGetResourceDirect($segment);
329
        } else {
330
            $queryResult = $this->executeGetResourceRelated($segment);
331
        }
332
        $segment->setResult($queryResult);
333
    }
334
335
    /**
336
     * @param $segment
337
     */
338
    private function executeGetLink($segment)
339
    {
340
        $previous = $segment->getPrevious();
341
        assert(isset($previous));
342
        $segment->setResult($previous->getResult());
343
    }
344
345
    /**
346
     * @param $segment
347
     * @return null|object|QueryResult
348
     */
349
    private function executeGetResourceDirect($segment)
350
    {
351
        $queryResult = null;
0 ignored issues
show
Unused Code introduced by
$queryResult is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
352
        if ($segment->isSingleResult()) {
353
            $queryResult = $this->getProviders()->getResourceFromResourceSet(
354
                $segment->getTargetResourceSetWrapper(),
355
                $segment->getKeyDescriptor()
356
            );
357
        } else {
358
            $skip = $this->getRequest()->getSkipCount();
359
            $skip = (null === $skip) ? 0 : $skip;
360
            $skipToken = $this->getRequest()->getInternalSkipTokenInfo();
361
            $skipToken = (null != $skipToken) ? $skipToken->getSkipTokenInfo() : null;
362
            $queryResult = $this->getProviders()->getResourceSet(
363
                $this->getRequest()->queryType,
364
                $segment->getTargetResourceSetWrapper(),
365
                $this->getRequest()->getFilterInfo(),
366
                $this->getRequest()->getInternalOrderByInfo(),
367
                $this->getRequest()->getTopCount(),
368
                $skip,
369
                $skipToken
370
            );
371
        }
372
        return $queryResult;
373
    }
374
375
    /**
376
     * @param $segment
377
     * @return null|object|QueryResult
378
     */
379
    private function executeGetResourceRelated($segment)
380
    {
381
        $projectedProperty = $segment->getProjectedProperty();
382
        $projectedPropertyKind = $projectedProperty->getKind();
383
        $queryResult = null;
384
        switch ($projectedPropertyKind) {
385
            case ResourcePropertyKind::RESOURCE_REFERENCE:
386
                $queryResult = $this->getProviders()->getRelatedResourceReference(
387
                    $segment->getPrevious()->getTargetResourceSetWrapper(),
388
                    $segment->getPrevious()->getResult(),
389
                    $segment->getTargetResourceSetWrapper(),
390
                    $projectedProperty
391
                );
392
                break;
393
            case ResourcePropertyKind::RESOURCESET_REFERENCE:
394
                if ($segment->isSingleResult()) {
395
                    $queryResult = $this->getProviders()->getResourceFromRelatedResourceSet(
396
                        $segment->getPrevious()->getTargetResourceSetWrapper(),
397
                        $segment->getPrevious()->getResult(),
398
                        $segment->getTargetResourceSetWrapper(),
399
                        $projectedProperty,
400
                        $segment->getKeyDescriptor()
401
                    );
402
                } else {
403
                    $skipToken = $this->getRequest()->getInternalSkipTokenInfo();
404
                    $skipToken = (null !== $skipToken) ? $skipToken->getSkipTokenInfo() : null;
405
                    $queryResult = $this->getProviders()->getRelatedResourceSet(
406
                        $this->getRequest()->queryType,
407
                        $segment->getPrevious()->getTargetResourceSetWrapper(),
408
                        $segment->getPrevious()->getResult(),
409
                        $segment->getTargetResourceSetWrapper(),
410
                        $projectedProperty,
411
                        $this->getRequest()->getFilterInfo(),
412
                        null, // $orderby
413
                        null, // $top
414
                        null,  // $skip
415
                        $skipToken
416
                    );
417
                }
418
                break;
419
            default:
420
                assert(false, "Invalid property kind type for resource retrieval");
421
        }
422
        return $queryResult;
423
    }
424
}
425