Passed
Pull Request — master (#120)
by Alex
03:44
created

RequestDescription::readData()   D

Complexity

Conditions 10
Paths 9

Size

Total Lines 27
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 19
nc 9
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace POData\UriProcessor;
4
5
use POData\Common\Messages;
6
use POData\Common\MimeTypes;
7
use POData\Common\ODataConstants;
8
use POData\Common\ODataException;
9
use POData\Common\Url;
10
use POData\Common\Version;
11
use POData\OperationContext\IHTTPRequest;
12
use POData\Providers\Metadata\ResourceProperty;
13
use POData\Providers\Metadata\ResourceSetWrapper;
14
use POData\Providers\Metadata\ResourceStreamInfo;
15
use POData\Providers\Metadata\ResourceType;
16
use POData\Providers\Query\QueryType;
17
use POData\UriProcessor\Interfaces\IUriProcessor;
18
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\RootProjectionNode;
19
use POData\UriProcessor\QueryProcessor\ExpressionParser\FilterInfo;
20
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
21
use POData\UriProcessor\QueryProcessor\SkipTokenParser\InternalSkipTokenInfo;
22
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\SegmentDescriptor;
23
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetKind;
24
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetSource;
25
26
/**
27
 * Class RequestDescription.
28
 */
29
class RequestDescription
30
{
31
    /**
32
     * Holds the value of HTTP 'DataServiceVersion' header in the request,
33
     * DataServiceVersion header value states the version of the
34
     * Open Data Protocol used by the client to generate the request.
35
     * Refer http://www.odata.org/developers/protocols/overview#ProtocolVersioning.
36
     *
37
     * @var Version
38
     */
39
    private $requestVersion = null;
40
41
    /**
42
     * Holds the value of HTTP 'MaxDataServiceVersion' header in the request,
43
     * MaxDataServiceVersion header value specifies the maximum version number
44
     * the client can accept in a response.
45
     * Refer http://www.odata.org/developers/protocols/overview#ProtocolVersioning.
46
     *
47
     * @var Version
48
     */
49
    private $requestMaxVersion = null;
50
51
    /**
52
     * This is the value of 'DataServiceVersion' header to be output in the response. this header
53
     * value states the OData version the server used to generate the response.
54
     * While processing the query and result set this value will be keeps on
55
     * updating, after every update this is compared against the
56
     * 'MaxDataServiceVersion' header in the client request to see whether the
57
     * client can interpret the response or not. The client should use this
58
     * value to determine whether it can correctly interpret the response or not.
59
     * Refer http://www.odata.org/developers/protocols/overview#ProtocolVersioning.
60
     *
61
     * @var Version
62
     */
63
    private $requiredMinResponseVersion;
64
65
    /**
66
     * The minimum client version requirement, This value keeps getting updated
67
     * during processing of query, this is compared against the
68
     * DataServiceVersion header in the client request and if the client request
69
     * is less than this value then we fail the request (e.g. $count request
70
     * was sent but client said it was Version 1.0).
71
     *
72
     * @var Version
73
     */
74
    private $requiredMinRequestVersion;
75
76
    /** @var Version */
77
    private $maxServiceVersion;
78
79
    /**
80
     * Collection of known data service versions.
81
     *
82
     * @var Version[]
83
     */
84
    private static $knownDataServiceVersions = null;
85
86
    /**
87
     * @var Url
88
     */
89
    private $requestUrl;
90
91
    /**
92
     * Collection of SegmentDescriptor containing information about
93
     * each segment in the resource path part of the request uri.
94
     *
95
     * @var SegmentDescriptor[]
96
     */
97
    private $segments;
98
99
    /**
100
     * Holds reference to the last segment descriptor.
101
     *
102
     * @var SegmentDescriptor
103
     */
104
    private $lastSegment;
105
106
    /**
107
     * The name of the container for results.
108
     *
109
     * @var string|null
110
     */
111
    private $containerName;
112
113
    /**
114
     * The count option specified in the request.
115
     *
116
     * @var QueryType
117
     */
118
    public $queryType;
119
120
    /**
121
     * Number of segments.
122
     *
123
     * @var int
124
     */
125
    private $segmentCount;
126
127
    /**
128
     * Holds the value of $skip query option, if no $skip option
129
     * found then this parameter will be NULL.
130
     *
131
     * @var int|null
132
     */
133
    private $skipCount;
134
135
    /**
136
     * Holds the value of take count, this value is depends on
137
     * presence of $top option and configured page size.
138
     *
139
     * @var int|null
140
     */
141
    private $topCount;
142
143
    /**
144
     * Holds the value of $top query option, if no $top option
145
     * found then this parameter will be NULL.
146
     *
147
     * @var int|null
148
     */
149
    private $topOptionCount;
150
151
    /**
152
     * Holds the parsed details for sorting, this will
153
     * be set in 3 cases
154
     * (1) if $orderby option is specified in the request uri
155
     * (2) if $skip or $top option is specified in the request uri
156
     * (3) if server side paging is enabled for the resource
157
     *     targeted by the request uri.
158
     *
159
     * @var InternalOrderByInfo|null
160
     */
161
    private $internalOrderByInfo;
162
163
    /**
164
     * Holds the parsed details for $skiptoken option, this will
165
     * be NULL if $skiptoken option is absent.
166
     *
167
     * @var InternalSkipTokenInfo|null
168
     */
169
    private $internalSkipTokenInfo;
170
171
    /**
172
     * Holds the parsed details for $filter option, this will be NULL if $filter option is absent.
173
     *
174
     * @var FilterInfo|null
175
     */
176
    private $filterInfo;
177
178
    /**
179
     * Holds reference to the root of the tree describing expand
180
     * and select information, this field will be NULL if no
181
     * $expand or $select specified in the request uri.
182
     *
183
     * @var RootProjectionNode|null
184
     */
185
    private $rootProjectionNode;
186
187
    /**
188
     * Holds number of entities in the result set, if either $count or
189
     * $inlinecount=allpages is specified, otherwise NULL.
190
     *
191
     *
192
     * @var int|null
193
     */
194
    private $countValue;
195
196
    /**
197
     * Data of request from request body.
198
     *
199
     * @var array|null
200
     */
201
    private $data;
202
203
    /**
204
     * Flag indicating status of query execution.
205
     *
206
     * @var bool
207
     */
208
    private $isExecuted;
209
210
    /**
211
     * Reference to Uri processor.
212
     *
213
     * @var IUriProcessor
214
     */
215
    private $uriProcessor;
216
217
    /**
218
     * @param SegmentDescriptor[] $segmentDescriptors Description of segments in the resource path
219
     * @param Url                 $requestUri
220
     * @param Version             $serviceMaxVersion
221
     * @param string|null         $requestVersion
222
     * @param string|null         $maxRequestVersion
223
     * @param string              $dataType
0 ignored issues
show
Documentation introduced by
Should the type for parameter $dataType not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
224
     */
225
    public function __construct(
226
        $segmentDescriptors,
227
        Url $requestUri,
228
        Version $serviceMaxVersion,
229
        $requestVersion,
230
        $maxRequestVersion,
231
        $dataType = null,
232
        IHTTPRequest $payload = null
233
    ) {
234
        $this->segments = $segmentDescriptors;
235
        $this->segmentCount = count($this->segments);
236
        $this->requestUrl = $requestUri;
237
        $this->lastSegment = $segmentDescriptors[$this->segmentCount - 1];
238
        $this->queryType = QueryType::ENTITIES();
239
        //we use this for validation checks down in validateVersions...
240
        //but maybe we should check that outside of this object...
241
        $this->maxServiceVersion = $serviceMaxVersion;
242
243
        //Per OData 1 & 2 spec we must return the smallest size
244
        //We start at 1.0 and move it up as features are requested
245
        $this->requiredMinResponseVersion = clone Version::v1();
246
        $this->requiredMinRequestVersion = clone Version::v1();
247
248
        //see http://www.odata.org/documentation/odata-v2-documentation/overview/#ProtocolVersioning
249
        //if requestVersion isn't there, use Service Max Version
250
        $this->requestVersion = is_null($requestVersion)
251
            ? $serviceMaxVersion
252
            : self::parseVersionHeader($requestVersion, ODataConstants::ODATAVERSIONHEADER);
253
254
        //if max version isn't there, use the request version
255
        $this->requestMaxVersion = is_null($maxRequestVersion)
256
            ? $this->requestVersion
257
            : self::parseVersionHeader($maxRequestVersion, ODataConstants::ODATAMAXVERSIONHEADER);
258
259
        //if it's OData v3..things change a bit
260
        if ($this->maxServiceVersion == Version::v3()) {
261
            if (is_null($maxRequestVersion)) {
262
                //if max request version isn't specified we use the service max version instead of the request version
263
                //thus we favour newer versions
264
                $this->requestMaxVersion = $this->maxServiceVersion;
265
            }
266
267
            //also we change min response version to be the max version, again favoring later things
268
            //note that if the request max version is specified, it is still respected
269
            $this->requiredMinResponseVersion = clone $this->requestMaxVersion;
270
        }
271
272
        $this->containerName = null;
273
        $this->skipCount = null;
274
        $this->topCount = null;
275
        $this->topOptionCount = null;
276
        $this->internalOrderByInfo = null;
277
        $this->internalSkipTokenInfo = null;
278
279
        $this->filterInfo = null;
280
        $this->countValue = null;
281
        $this->isExecuted = false;
282
        $this->data = isset($payload) ? $payload->getAllInput() : null;
283
284
        // Define data from request body
285
        if ($dataType) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $dataType of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
286
            $this->readData($dataType);
287
        }
288
    }
289
290
    /**
291
     * Define request data from body.
292
     *
293
     * @param string $dataType
294
     */
295
    private function readData($dataType)
296
    {
297
        $string = $this->data;
298
        $dataArray = [];
299
        if ($dataType === MimeTypes::MIME_APPLICATION_ATOM) {
300
            if (is_array($string) && 1 == count($string)) {
301
                $string = $string[0];
302
            }
303
            $data = XML2Array::createArray($string);
304
            if (!empty($data['entry']['content']['m:properties'])) {
305
                $clearData = $data['entry']['content']['m:properties'];
306
                if (is_array($clearData)) {
307
                    foreach ($clearData as $key => $value) {
308
                        if (is_array($value)) {
309
                            $dataArray[substr($key, 2)] = $value['@value'];
310
                        } else {
311
                            $dataArray[substr($key, 2)] = $value;
312
                        }
313
                    }
314
                }
315
            }
316
            $this->data = $dataArray;
317
        } elseif ($dataType === MimeTypes::MIME_APPLICATION_JSON) {
318
            $data = !is_array($string) ? json_decode($string, true) : $string;
319
            $this->data = $data;
320
        }
321
    }
322
323
    /**
324
     * Get request data from body.
325
     */
326
    public function getData()
327
    {
328
        return $this->data;
329
    }
330
331
    /**
332
     * Raise the minimum client version requirement for this request and
333
     * perform capability negotiation.
334
     *
335
     * @param int $major The major segment of the version
336
     * @param int $minor The minor segment of the version
337
     *
338
     * @throws ODataException If capability negotiation fails
339
     */
340
    public function raiseMinVersionRequirement($major, $minor)
341
    {
342
        if ($this->requiredMinRequestVersion->raiseVersion($major, $minor)) {
343
            $this->validateVersions();
344
        }
345
    }
346
347
    /**
348
     * Raise the response version for this request and perform capability negotiation.
349
     *
350
     *
351
     * @param int $major The major segment of the version
352
     * @param int $minor The minor segment of the version
353
     *
354
     * @throws ODataException If capability negotiation fails
355
     */
356
    public function raiseResponseVersion($major, $minor)
357
    {
358
        if ($this->requiredMinResponseVersion->raiseVersion($major, $minor)) {
359
            $this->validateVersions();
360
        }
361
    }
362
363
    /**
364
     * Gets collection of segment descriptors containing information about
365
     * each segment in the resource path part of the request uri.
366
     *
367
     * @return SegmentDescriptor[]
368
     */
369
    public function getSegments()
370
    {
371
        return $this->segments;
372
    }
373
374
    /**
375
     * Gets reference to the descriptor of last segment.
376
     *
377
     * @return SegmentDescriptor
378
     */
379
    public function getLastSegment()
380
    {
381
        return $this->lastSegment;
382
    }
383
384
    /**
385
     * Gets kind of resource targeted by the resource path.
386
     *
387
     * @return TargetKind
388
     */
389
    public function getTargetKind()
390
    {
391
        return $this->lastSegment->getTargetKind();
392
    }
393
394
    /**
395
     * Gets kind of 'source of data' targeted by the resource path.
396
     *
397
     * @return TargetSource
398
     */
399
    public function getTargetSource()
400
    {
401
        return $this->lastSegment->getTargetSource();
402
    }
403
404
    /**
405
     * Gets reference to the ResourceSetWrapper instance targeted by
406
     * the resource path, ResourceSetWrapper will present in the
407
     * following cases:
408
     * if the last segment descriptor describes
409
     *      (a) resource set
410
     *          http://server/NW.svc/Customers
411
     *          http://server/NW.svc/Customers('ALFKI')
412
     *          http://server/NW.svc/Customers('ALFKI')/Orders
413
     *          http://server/NW.svc/Customers('ALFKI')/Orders(123)
414
     *          http://server/NW.svc/Customers('ALFKI')/$links/Orders
415
     *      (b) resource set reference
416
     *          http://server/NW.svc/Orders(123)/Customer
417
     *          http://server/NW.svc/Orders(123)/$links/Customer
418
     *      (c) $count
419
     *          http://server/NW.svc/Customers/$count
420
     * ResourceSet wrapper will be absent (NULL) in the following cases:
421
     * if the last segment descriptor describes
422
     *      (a) Primitive
423
     *          http://server/NW.svc/Customers('ALFKI')/Country
424
     *      (b) $value on primitive type
425
     *          http://server/NW.svc/Customers('ALFKI')/Country/$value
426
     *      (c) Complex
427
     *          http://server/NW.svc/Customers('ALFKI')/Address
428
     *      (d) Bag
429
     *          http://server/NW.svc/Employees(123)/Emails
430
     *      (e) MLE
431
     *          http://server/NW.svc/Employees(123)/$value
432
     *      (f) Named Stream
433
     *          http://server/NW.svc/Employees(123)/Thumnail48_48
434
     *      (g) metadata
435
     *          http://server/NW.svc/$metadata
436
     *      (h) service directory
437
     *          http://server/NW.svc
438
     *      (i) $bath
439
     *          http://server/NW.svc/$batch.
440
     *
441
     * @return ResourceSetWrapper|null
442
     */
443
    public function getTargetResourceSetWrapper()
444
    {
445
        return $this->lastSegment->getTargetResourceSetWrapper();
446
    }
447
448
    /**
449
     * Gets reference to the ResourceType instance targeted by
450
     * the resource path, ResourceType will present in the
451
     * following cases:
452
     * if the last segment descriptor describes
453
     *      (a) resource set
454
     *          http://server/NW.svc/Customers
455
     *          http://server/NW.svc/Customers('ALFKI')
456
     *          http://server/NW.svc/Customers('ALFKI')/Orders
457
     *          http://server/NW.svc/Customers('ALFKI')/Orders(123)
458
     *          http://server/NW.svc/Customers('ALFKI')/$links/Orders
459
     *      (b) resource set reference
460
     *          http://server/NW.svc/Orders(123)/Customer
461
     *          http://server/NW.svc/Orders(123)/$links/Customer
462
     *      (c) $count
463
     *          http://server/NW.svc/Customers/$count
464
     *      (d) Primitive
465
     *          http://server/NW.svc/Customers('ALFKI')/Country
466
     *      (e) $value on primitive type
467
     *          http://server/NW.svc/Customers('ALFKI')/Country/$value
468
     *      (f) Complex
469
     *          http://server/NW.svc/Customers('ALFKI')/Address
470
     *      (g) Bag
471
     *          http://server/NW.svc/Employees(123)/Emails
472
     *      (h) MLE
473
     *          http://server/NW.svc/Employees(123)/$value
474
     *      (i) Named Stream
475
     *          http://server/NW.svc/Employees(123)/Thumnail48_48
476
     * ResourceType will be absent (NULL) in the following cases:
477
     * if the last segment descriptor describes
478
     *      (a) metadata
479
     *          http://server/NW.svc/$metadata
480
     *      (b) service directory
481
     *          http://server/NW.svc
482
     *      (c) $bath
483
     *          http://server/NW.svc/$batch.
484
     *
485
     * @return ResourceType|null
486
     */
487
    public function getTargetResourceType()
488
    {
489
        return $this->lastSegment->getTargetResourceType();
490
    }
491
492
    /**
493
     * Gets reference to the ResourceProperty instance targeted by
494
     * the resource path, ResourceProperty will present in the
495
     * following cases:
496
     * if the last segment descriptor describes
497
     *      (a) resource set (after 1 level)
498
     *          http://server/NW.svc/Customers('ALFKI')/Orders
499
     *          http://server/NW.svc/Customers('ALFKI')/Orders(123)
500
     *          http://server/NW.svc/Customers('ALFKI')/$links/Orders
501
     *      (b) resource set reference
502
     *          http://server/NW.svc/Orders(123)/Customer
503
     *          http://server/NW.svc/Orders(123)/$links/Customer
504
     *      (c) $count
505
     *          http://server/NW.svc/Customers/$count
506
     *      (d) Primitive
507
     *          http://server/NW.svc/Customers('ALFKI')/Country
508
     *      (e) $value on primitive type
509
     *          http://server/NW.svc/Customers('ALFKI')/Country/$value
510
     *      (f) Complex
511
     *          http://server/NW.svc/Customers('ALFKI')/Address
512
     *      (g) Bag
513
     *          http://server/NW.svc/Employees(123)/Emails
514
     *      (h) MLE
515
     *          http://server/NW.svc/Employees(123)/$value.
516
     *
517
     * ResourceType will be absent (NULL) in the following cases:
518
     * if the last segment descriptor describes
519
     *      (a) If last segment is the only segment pointing to
520
     *          ResourceSet (single or multiple)
521
     *          http://server/NW.svc/Customers
522
     *          http://server/NW.svc/Customers('ALFKI')
523
     *      (b) Named Stream
524
     *          http://server/NW.svc/Employees(123)/Thumnail48_48
525
     *      (c) metadata
526
     *          http://server/NW.svc/$metadata
527
     *      (d) service directory
528
     *          http://server/NW.svc
529
     *      (e) $bath
530
     *          http://server/NW.svc/$batch
531
     *
532
     * @return ResourceProperty|null
533
     */
534
    public function getProjectedProperty()
535
    {
536
        return  $this->lastSegment->getProjectedProperty();
537
    }
538
539
    /**
540
     * Gets the name of the container for results.
541
     *
542
     * @return string|null
543
     */
544
    public function getContainerName()
545
    {
546
        return $this->containerName;
547
    }
548
549
    /**
550
     * Sets the name of the container for results.
551
     *
552
     * @param string $containerName The container name
553
     */
554
    public function setContainerName($containerName)
555
    {
556
        $this->containerName = $containerName;
557
    }
558
559
    /**
560
     * Whether thr request targets a single result or not.
561
     *
562
     * @return bool
563
     */
564
    public function isSingleResult()
565
    {
566
        return $this->lastSegment->isSingleResult();
567
    }
568
569
    /**
570
     * Gets the identifier associated with the the resource path.
571
     *
572
     * @return string
573
     */
574
    public function getIdentifier()
575
    {
576
        return $this->lastSegment->getIdentifier();
577
    }
578
579
    /**
580
     * Gets the request uri.
581
     *
582
     * @return Url
583
     */
584
    public function getRequestUrl()
585
    {
586
        return $this->requestUrl;
587
    }
588
589
    /**
590
     * Gets the value of $skip query option.
591
     *
592
     * @return int|null The value of $skip query option, NULL if $skip is absent
593
     */
594
    public function getSkipCount()
595
    {
596
        return $this->skipCount;
597
    }
598
599
    /**
600
     * Sets skip value.
601
     *
602
     * @param int $skipCount The value of $skip query option
603
     */
604
    public function setSkipCount($skipCount)
605
    {
606
        $this->skipCount = $skipCount;
607
    }
608
609
    /**
610
     * Gets the value of take count.
611
     *
612
     * @return int|null The value of take, NULL if no take to be applied
613
     */
614
    public function getTopCount()
615
    {
616
        return $this->topCount;
617
    }
618
619
    /**
620
     * Sets the value of take count.
621
     *
622
     * @param int $topCount The value of take query option
623
     */
624
    public function setTopCount($topCount)
625
    {
626
        $this->topCount = $topCount;
627
    }
628
629
    /**
630
     * Gets the value of $top query option.
631
     *
632
     * @return int|null The value of $top query option, NULL if $top is absent
633
     */
634
    public function getTopOptionCount()
635
    {
636
        return $this->topOptionCount;
637
    }
638
639
    /**
640
     * Sets top value.
641
     *
642
     * @param int $topOptionCount The value of $top query option
643
     */
644
    public function setTopOptionCount($topOptionCount)
645
    {
646
        $this->topOptionCount = $topOptionCount;
647
    }
648
649
    /**
650
     * Gets sorting (orderby) information, this function return
651
     * sorting information in 3 cases:
652
     * (1) if $orderby option is specified in the request uri
653
     * (2) if $skip or $top option is specified in the request uri
654
     * (3) if server side paging is enabled for the resource targeted
655
     *     by the request uri.
656
     *
657
     * @return InternalOrderByInfo|null
658
     */
659
    public function getInternalOrderByInfo()
660
    {
661
        return $this->internalOrderByInfo;
662
    }
663
664
    /**
665
     * Sets sorting (orderby) information.
666
     *
667
     * @param InternalOrderByInfo &$internalOrderByInfo The sorting information
668
     */
669
    public function setInternalOrderByInfo(InternalOrderByInfo & $internalOrderByInfo)
670
    {
671
        $this->internalOrderByInfo = $internalOrderByInfo;
672
    }
673
674
    /**
675
     * Gets the parsed details for $skiptoken option.
676
     *
677
     * @return InternalSkipTokenInfo|null Returns parsed details of $skiptoken option, NULL if $skiptoken is absent
678
     */
679
    public function getInternalSkipTokenInfo()
680
    {
681
        return $this->internalSkipTokenInfo;
682
    }
683
684
    /**
685
     * Sets $skiptoken information.
686
     *
687
     * @param InternalSkipTokenInfo &$internalSkipTokenInfo The paging information
688
     */
689
    public function setInternalSkipTokenInfo(
690
        InternalSkipTokenInfo & $internalSkipTokenInfo
0 ignored issues
show
Comprehensibility Naming introduced by
The variable name $internalSkipTokenInfo exceeds the maximum configured length of 20.

Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.

Loading history...
691
    ) {
692
        $this->internalSkipTokenInfo = $internalSkipTokenInfo;
693
    }
694
695
    /**
696
     * @return FilterInfo|null Returns parsed details of $filter option, NULL if $filter is absent
697
     */
698
    public function getFilterInfo()
699
    {
700
        return $this->filterInfo;
701
    }
702
703
    /**
704
     * @param FilterInfo $filterInfo The filter information
705
     */
706
    public function setFilterInfo(FilterInfo $filterInfo)
707
    {
708
        $this->filterInfo = $filterInfo;
709
    }
710
711
    /**
712
     * Sets $expand and $select information.
713
     *
714
     * @param RootProjectionNode &$rootProjectionNode Root of the projection tree
715
     */
716
    public function setRootProjectionNode(RootProjectionNode & $rootProjectionNode)
717
    {
718
        $this->rootProjectionNode = $rootProjectionNode;
719
    }
720
721
    /**
722
     * Gets the root of the tree describing expand and select options,.
723
     *
724
     * @return RootProjectionNode|null Returns parsed details of $expand
725
     *                                 and $select options, NULL if
726
     *                                 $both options are absent
727
     */
728
    public function getRootProjectionNode()
729
    {
730
        return $this->rootProjectionNode;
731
    }
732
733
    /**
734
     * Gets the count of result set if $count or $inlinecount=allpages
735
     * has been applied otherwise NULL.
736
     *
737
     * @return int|null
738
     */
739
    public function getCountValue()
740
    {
741
        return $this->countValue;
742
    }
743
744
    /**
745
     * Sets the count of result set.
746
     *
747
     * @param int $countValue The count value
748
     */
749
    public function setCountValue($countValue)
750
    {
751
        $this->countValue = $countValue;
752
    }
753
754
    /**
755
     * To set the flag indicating the execution status as true.
756
     */
757
    public function setExecuted()
758
    {
759
        $this->isExecuted = true;
760
    }
761
762
    /**
763
     * To check whether to execute the query using IDSQP.
764
     *
765
     * @return bool True if query need to be executed, False otherwise
766
     */
767
    public function needExecution()
0 ignored issues
show
Coding Style introduced by
function needExecution() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
768
    {
769
        return !$this->isExecuted
770
            && ($this->lastSegment->getTargetKind() != TargetKind::METADATA())
771
            && ($this->lastSegment->getTargetKind() != TargetKind::SERVICE_DIRECTORY());
772
    }
773
774
    /**
775
     * To check if the resource path is a request for link uri.
776
     *
777
     * @return bool True if request is for link uri else false
778
     */
779
    public function isLinkUri()
780
    {
781
        return ($this->segmentCount > 2) && ($this->segments[$this->segmentCount - 2]->getTargetKind() == TargetKind::LINK());
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 126 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
782
    }
783
784
    /**
785
     * To check if the resource path is a request for meida resource.
786
     *
787
     * @return bool True if request is for media resource else false
788
     */
789
    public function isMediaResource()
790
    {
791
        return $this->lastSegment->getTargetKind() == TargetKind::MEDIA_RESOURCE();
792
    }
793
794
    /**
795
     * To check if the resource path is a request for named stream.
796
     *
797
     * @return bool True if request is for named stream else false
798
     */
799
    public function isNamedStream()
800
    {
801
        return $this->isMediaResource() && !($this->lastSegment->getIdentifier() === ODataConstants::URI_VALUE_SEGMENT);
802
    }
803
804
    /**
805
     * Get ResourceStreamInfo for the media link entry or named stream request.
806
     *
807
     * @return ResourceStreamInfo|null Instance of ResourceStreamInfo if the
808
     *                                 current request targets named stream, NULL for MLE
809
     */
810
    public function getResourceStreamInfo()
811
    {
812
        //assert($this->isMediaResource)
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
813
        if ($this->isNamedStream()) {
814
            return $this->getTargetResourceType()
815
                ->tryResolveNamedStreamByName(
816
                    $this->lastSegment->getIdentifier()
817
                );
818
        }
819
    }
820
821
    /**
822
     * Gets the resource instance targeted by the request uri.
823
     * Note: This value will be populated after query execution only.
824
     *
825
     * @return mixed
826
     */
827
    public function getTargetResult()
828
    {
829
        return $this->lastSegment->getResult();
830
    }
831
832
    /**
833
     * Gets the OData version the server used to generate the response.
834
     *
835
     * @return Version
836
     */
837
    public function getResponseVersion()
838
    {
839
        return $this->requiredMinResponseVersion;
840
    }
841
842
    /**
843
     * Checks whether etag headers are allowed for this request.
844
     *
845
     * @return bool True if ETag header (If-Match or If-NoneMatch)
846
     *              is allowed for the request, False otherwise
847
     */
848
    public function isETagHeaderAllowed()
849
    {
850
        return $this->lastSegment->isSingleResult()
851
            && ($this->queryType != QueryType::COUNT())
852
            && !$this->isLinkUri()
853
            && (is_null($this->rootProjectionNode)
854
                || !($this->rootProjectionNode->isExpansionSpecified())
855
                );
856
    }
857
858
    /**
859
     * Gets collection of known data service versions, currently 1.0, 2.0 and 3.0.
860
     *
861
     * @return Version[]
862
     */
863
    public static function getKnownDataServiceVersions()
864
    {
865
        if (is_null(self::$knownDataServiceVersions)) {
866
            self::$knownDataServiceVersions = [
867
                new Version(1, 0),
868
                new Version(2, 0),
869
                new Version(3, 0),
870
            ];
871
        }
872
873
        return self::$knownDataServiceVersions;
874
    }
875
876
    /**
877
     * This function is used to perform following checking (validation)
878
     * for capability negotiation.
879
     *  (1) Check client request's 'DataServiceVersion' header value is
880
     *      less than or equal to the minimum version required to intercept
881
     *      the response
882
     *  (2) Check client request's 'MaxDataServiceVersion' header value is
883
     *      less than or equal to the version of protocol required to generate
884
     *      the response
885
     *  (3) Check the configured maximum protocol version is less than or equal
886
     *      to the version of protocol required to generate the response
887
     *  In addition to these checking, this function is also responsible for
888
     *  initializing the properties representing 'DataServiceVersion' and
889
     *  'MaxDataServiceVersion'.
890
     *
891
     *
892
     * @throws ODataException If any of the above 3 check fails
893
     */
894
    public function validateVersions()
895
    {
896
897
        //If the request version is below the minimum version required by supplied request arguments..throw an exception
898
        if ($this->requestVersion->compare($this->requiredMinRequestVersion) < 0) {
899
            throw ODataException::createBadRequestError(
900
                Messages::requestVersionTooLow(
901
                    $this->requestVersion->toString(),
902
                    $this->requiredMinRequestVersion->toString()
903
                )
904
            );
905
        }
906
907
        //If the requested max version is below the version required to fulfill the response...throw an exception
908
        if ($this->requestMaxVersion->compare($this->requiredMinResponseVersion) < 0) {
909
            throw ODataException::createBadRequestError(
910
                Messages::requestVersionTooLow(
911
                    $this->requestMaxVersion->toString(),
912
                    $this->requiredMinResponseVersion->toString()
913
                )
914
            );
915
        }
916
917
        //If the max version supported by the service is below the version required to fulfill the response..throw an exception
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 127 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
918
        if ($this->maxServiceVersion->compare($this->requiredMinResponseVersion) < 0) {
919
            throw ODataException::createBadRequestError(
920
                Messages::requestVersionIsBiggerThanProtocolVersion(
921
                    $this->requiredMinResponseVersion->toString(),
922
                    $this->maxServiceVersion->toString()
923
                )
924
            );
925
        }
926
    }
927
928
    /**
929
     * Validates the given version in string format and returns the version as instance of Version.
930
     *
931
     * @param string $versionHeader The DataServiceVersion or MaxDataServiceVersion header value
932
     * @param string $headerName    The name of the header
933
     *
934
     * @throws ODataException If the version is malformed or not supported
935
     *
936
     * @return Version
937
     */
938
    private static function parseVersionHeader($versionHeader, $headerName)
939
    {
940
        $libName = null;
0 ignored issues
show
Unused Code introduced by
$libName 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...
941
        $versionHeader = trim($versionHeader);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $versionHeader. This often makes code more readable.
Loading history...
942
        $libNameIndex = strpos($versionHeader, ';');
943
        if ($libNameIndex !== false) {
944
            $libName = substr($versionHeader, $libNameIndex);
0 ignored issues
show
Unused Code introduced by
$libName 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...
945
        } else {
946
            $libNameIndex = strlen($versionHeader);
947
        }
948
949
        $dotIndex = -1;
950
        for ($i = 0; $i < $libNameIndex; ++$i) {
951
            if ($versionHeader[$i] == '.') {
952
                //Throw an exception if we find more than 1 dot
953
                if ($dotIndex != -1) {
954
                    throw ODataException::createBadRequestError(
955
                        Messages::requestDescriptionInvalidVersionHeader(
956
                            $versionHeader,
957
                            $headerName
958
                        )
959
                    );
960
                }
961
962
                $dotIndex = $i;
963
            } elseif ($versionHeader[$i] < '0' || $versionHeader[$i] > '9') {
964
                throw ODataException::createBadRequestError(
965
                    Messages::requestDescriptionInvalidVersionHeader(
966
                        $versionHeader,
967
                        $headerName
968
                    )
969
                );
970
            }
971
        }
972
973
        $major = intval(substr($versionHeader, 0, $dotIndex));
974
        $minor = 0;
975
976
        //Apparently the . is optional
977
        if ($dotIndex != -1) {
978
            if ($dotIndex == 0) {
979
                //If it starts with a ., throw an exception
980
                throw ODataException::createBadRequestError(
981
                    Messages::requestDescriptionInvalidVersionHeader(
982
                        $versionHeader,
983
                        $headerName
984
                    )
985
                );
986
            }
987
            $minor = intval(substr($versionHeader, $dotIndex + 1, $libNameIndex));
988
        }
989
990
        $version = new Version($major, $minor);
991
992
        //TODO: move this somewhere...
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
993
        //$this->validateVersions();
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
994
        $isSupportedVersion = false;
995
        foreach (self::getKnownDataServiceVersions() as $version1) {
996
            if ($version->compare($version1) == 0) {
997
                $isSupportedVersion = true;
998
                break;
999
            }
1000
        }
1001
1002
        if (!$isSupportedVersion) {
1003
            $availableVersions = null;
1004
            foreach (self::getKnownDataServiceVersions() as $version1) {
1005
                $availableVersions .= $version1->toString() . ', ';
1006
            }
1007
1008
            $availableVersions = rtrim($availableVersions, ', ');
1009
            throw ODataException::createBadRequestError(
1010
                Messages::requestDescriptionUnSupportedVersion(
1011
                    $headerName,
1012
                    $versionHeader,
1013
                    $availableVersions
1014
                )
1015
            );
1016
        }
1017
1018
        return $version;
1019
    }
1020
1021
    /**
1022
     * Gets reference to the IUriProcessor instance.
1023
     *
1024
     * @return IUriProcessor
1025
     */
1026
    public function getUriProcessor()
1027
    {
1028
        return $this->uriProcessor;
1029
    }
1030
1031
    /**
1032
     * Set reference to IUriProcessor instance.
1033
     *
1034
     * @param IUriProcessor $uriProcessor Reference to the UriProcessor
1035
     */
1036
    public function setUriProcessor(IUriProcessor $uriProcessor)
1037
    {
1038
        $this->uriProcessor = $uriProcessor;
1039
    }
1040
}
1041