Test Setup Failed
Pull Request — master (#90)
by Alex
04:26
created

RequestDescription::_readData()   D

Complexity

Conditions 10
Paths 9

Size

Total Lines 27
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 27
rs 4.8196
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\QueryProcessor\ExpandProjectionParser\RootProjectionNode;
18
use POData\UriProcessor\QueryProcessor\ExpressionParser\FilterInfo;
19
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
20
use POData\UriProcessor\QueryProcessor\SkipTokenParser\InternalSkipTokenInfo;
21
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\SegmentDescriptor;
22
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetKind;
23
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetSource;
24
25
/**
26
 * Class RequestDescription.
27
 */
28
class RequestDescription
29
{
30
    /**
31
     * Holds the value of HTTP 'DataServiceVersion' header in the request,
32
     * DataServiceVersion header value states the version of the
33
     * Open Data Protocol used by the client to generate the request.
34
     * Refer http://www.odata.org/developers/protocols/overview#ProtocolVersioning.
35
     *
36
     * @var Version
37
     */
38
    private $requestVersion = null;
39
40
    /**
41
     * Holds the value of HTTP 'MaxDataServiceVersion' header in the request,
42
     * MaxDataServiceVersion header value specifies the maximum version number
43
     * the client can accept in a response.
44
     * Refer http://www.odata.org/developers/protocols/overview#ProtocolVersioning.
45
     *
46
     * @var Version
47
     */
48
    private $requestMaxVersion = null;
49
50
    /**
51
     * This is the value of 'DataServiceVersion' header to be output in the response. this header
52
     * value states the OData version the server used to generate the response.
53
     * While processing the query and result set this value will be keeps on
54
     * updating, after every update this is compared against the
55
     * 'MaxDataServiceVersion' header in the client request to see whether the
56
     * client can interpret the response or not. The client should use this
57
     * value to determine whether it can correctly interpret the response or not.
58
     * Refer http://www.odata.org/developers/protocols/overview#ProtocolVersioning.
59
     *
60
     * @var Version
61
     */
62
    private $requiredMinResponseVersion;
63
64
    /**
65
     * The minimum client version requirement, This value keeps getting updated
66
     * during processing of query, this is compared against the
67
     * DataServiceVersion header in the client request and if the client request
68
     * is less than this value then we fail the request (e.g. $count request
69
     * was sent but client said it was Version 1.0).
70
     *
71
     * @var Version
72
     */
73
    private $requiredMinRequestVersion;
74
75
    /** @var Version */
76
    private $maxServiceVersion;
77
78
    /**
79
     * Collection of known data service versions.
80
     *
81
     * @var Version[]
82
     */
83
    private static $_knownDataServiceVersions = null;
84
85
    /**
86
     * @var Url
87
     */
88
    private $requestUrl;
89
90
    /**
91
     * Collection of SegmentDescriptor containing information about
92
     * each segment in the resource path part of the request uri.
93
     *
94
     * @var SegmentDescriptor[]
95
     */
96
    private $segments;
97
98
    /**
99
     * Holds reference to the last segment descriptor.
100
     *
101
     * @var SegmentDescriptor
102
     */
103
    private $lastSegment;
104
105
    /**
106
     * The name of the container for results.
107
     *
108
     * @var string|null
109
     */
110
    private $_containerName;
111
112
    /**
113
     * The count option specified in the request.
114
     *
115
     * @var QueryType
116
     */
117
    public $queryType;
118
119
    /**
120
     * Number of segments.
121
     *
122
     * @var int
123
     */
124
    private $_segmentCount;
125
126
    /**
127
     * Holds the value of $skip query option, if no $skip option
128
     * found then this parameter will be NULL.
129
     *
130
     * @var int|null
131
     */
132
    private $_skipCount;
133
134
    /**
135
     * Holds the value of take count, this value is depends on
136
     * presence of $top option and configured page size.
137
     *
138
     * @var int|null
139
     */
140
    private $_topCount;
141
142
    /**
143
     * Holds the value of $top query option, if no $top option
144
     * found then this parameter will be NULL.
145
     *
146
     * @var int|null
147
     */
148
    private $_topOptionCount;
149
150
    /**
151
     * Holds the parsed details for sorting, this will
152
     * be set in 3 cases
153
     * (1) if $orderby option is specified in the request uri
154
     * (2) if $skip or $top option is specified in the request uri
155
     * (3) if server side paging is enabled for the resource
156
     *     targeted by the request uri.
157
     *
158
     * @var InternalOrderByInfo|null
159
     */
160
    private $internalOrderByInfo;
161
162
    /**
163
     * Holds the parsed details for $skiptoken option, this will
164
     * be NULL if $skiptoken option is absent.
165
     *
166
     * @var InternalSkipTokenInfo|null
167
     */
168
    private $_internalSkipTokenInfo;
169
170
    /**
171
     * Holds the parsed details for $filter option, this will be NULL if $filter option is absent.
172
     *
173
     * @var FilterInfo|null
174
     */
175
    private $_filterInfo;
176
177
    /**
178
     * Holds reference to the root of the tree describing expand
179
     * and select information, this field will be NULL if no
180
     * $expand or $select specified in the request uri.
181
     *
182
     * @var RootProjectionNode|null
183
     */
184
    private $_rootProjectionNode;
185
186
    /**
187
     * Holds number of entities in the result set, if either $count or
188
     * $inlinecount=allpages is specified, otherwise NULL.
189
     *
190
     *
191
     * @var int|null
192
     */
193
    private $_countValue;
194
195
    /**
196
     * Data of request from request body.
197
     *
198
     * @var array|null
199
     */
200
    private $_data;
201
202
    /**
203
     * Flag indicating status of query execution.
204
     *
205
     * @var bool
206
     */
207
    private $_isExecuted;
208
209
    /**
210
     * Reference to Uri processor.
211
     *
212
     * @var UriProcessor
213
     */
214
    private $_uriProcessor;
215
216
    /**
217
     * @param SegmentDescriptor[] $segmentDescriptors Description of segments in the resource path
218
     * @param Url                 $requestUri
219
     * @param Version             $serviceMaxVersion
220
     * @param string|null         $requestVersion
221
     * @param string|null         $maxRequestVersion
222
     * @param string              $dataType
223
     */
224
    public function __construct(
225
        $segmentDescriptors,
226
        Url $requestUri,
227
        Version $serviceMaxVersion,
228
        $requestVersion,
229
        $maxRequestVersion,
230
        $dataType = null,
231
        IHTTPRequest $payload = null
232
    ) {
233
        $this->segments = $segmentDescriptors;
234
        $this->_segmentCount = count($this->segments);
235
        $this->requestUrl = $requestUri;
236
        $this->lastSegment = $segmentDescriptors[$this->_segmentCount - 1];
237
        $this->queryType = QueryType::ENTITIES();
238
        //we use this for validation checks down in validateVersions...
239
        //but maybe we should check that outside of this object...
240
        $this->maxServiceVersion = $serviceMaxVersion;
241
242
        //Per OData 1 & 2 spec we must return the smallest size
243
        //We start at 1.0 and move it up as features are requested
244
        $this->requiredMinResponseVersion = clone Version::v1();
245
        $this->requiredMinRequestVersion = clone Version::v1();
246
247
        //see http://www.odata.org/documentation/odata-v2-documentation/overview/#ProtocolVersioning
248
        //if requestVersion isn't there, use Service Max Version
249
        $this->requestVersion = is_null($requestVersion)
250
            ? $serviceMaxVersion
251
            : self::parseVersionHeader($requestVersion, ODataConstants::ODATAVERSIONHEADER);
252
253
        //if max version isn't there, use the request version
254
        $this->requestMaxVersion = is_null($maxRequestVersion)
255
            ? $this->requestVersion
256
            : self::parseVersionHeader($maxRequestVersion, ODataConstants::ODATAMAXVERSIONHEADER);
257
258
        //if it's OData v3..things change a bit
259
        if ($this->maxServiceVersion == Version::v3()) {
260
            if (is_null($maxRequestVersion)) {
261
                //if max request version isn't specified we use the service max version instead of the request version
262
                //thus we favour newer versions
263
                $this->requestMaxVersion = $this->maxServiceVersion;
264
            }
265
266
            //also we change min response version to be the max version, again favoring later things
267
            //note that if the request max version is specified, it is still respected
268
            $this->requiredMinResponseVersion = clone $this->requestMaxVersion;
269
        }
270
271
        $this->_containerName = null;
272
        $this->_skipCount = null;
273
        $this->_topCount = null;
274
        $this->_topOptionCount = null;
275
        $this->internalOrderByInfo = null;
276
        $this->_internalSkipTokenInfo = null;
277
278
        $this->_filterInfo = null;
279
        $this->_countValue = null;
280
        $this->_isExecuted = false;
281
        $this->_data = isset($payload) ? $payload->getAllInput() : null;
282
283
        // Define data from request body
284
        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...
285
            $this->_readData($dataType);
286
        }
287
    }
288
289
    /**
290
     * Define request data from body.
291
     *
292
     * @param string $dataType
293
     */
294
    private function _readData($dataType)
295
    {
296
        $string = $this->_data;
297
        $dataArray = [];
298
        if ($dataType === MimeTypes::MIME_APPLICATION_ATOM) {
299
            if (is_array($string) && 1 == count($string)) {
300
                $string = $string[0];
301
            }
302
            $data = XML2Array::createArray($string);
303
            if (!empty($data['entry']['content']['m:properties'])) {
304
                $clearData = $data['entry']['content']['m:properties'];
305
                if (is_array($clearData)) {
306
                    foreach ($clearData as $key => $value) {
307
                        if (is_array($value)) {
308
                            $dataArray[substr($key, 2)] = $value['@value'];
309
                        } else {
310
                            $dataArray[substr($key, 2)] = $value;
311
                        }
312
                    }
313
                }
314
            }
315
            $this->_data = $dataArray;
316
        } elseif ($dataType === MimeTypes::MIME_APPLICATION_JSON) {
317
            $data = !is_array($string) ? json_decode($string, true) : $string;
318
            $this->_data = $data;
319
        }
320
    }
321
322
    /**
323
     * Get request data from body.
324
     */
325
    public function getData()
326
    {
327
        return $this->_data;
328
    }
329
330
    /**
331
     * Raise the minimum client version requirement for this request and
332
     * perform capability negotiation.
333
     *
334
     * @param int $major The major segment of the version
335
     * @param int $minor The minor segment of the version
336
     *
337
     * @throws ODataException If capability negotiation fails
338
     */
339
    public function raiseMinVersionRequirement($major, $minor)
340
    {
341
        if ($this->requiredMinRequestVersion->raiseVersion($major, $minor)) {
342
            $this->validateVersions();
343
        }
344
    }
345
346
    /**
347
     * Raise the response version for this request and perform capability negotiation.
348
     *
349
     *
350
     * @param int $major The major segment of the version
351
     * @param int $minor The minor segment of the version
352
     *
353
     * @throws ODataException If capability negotiation fails
354
     */
355
    public function raiseResponseVersion($major, $minor)
356
    {
357
        if ($this->requiredMinResponseVersion->raiseVersion($major, $minor)) {
358
            $this->validateVersions();
359
        }
360
    }
361
362
    /**
363
     * Gets collection of segment descriptors containing information about
364
     * each segment in the resource path part of the request uri.
365
     *
366
     * @return SegmentDescriptor[]
367
     */
368
    public function getSegments()
369
    {
370
        return $this->segments;
371
    }
372
373
    /**
374
     * Gets reference to the descriptor of last segment.
375
     *
376
     * @return SegmentDescriptor
377
     */
378
    public function getLastSegment()
379
    {
380
        return $this->lastSegment;
381
    }
382
383
    /**
384
     * Gets kind of resource targeted by the resource path.
385
     *
386
     * @return TargetKind
387
     */
388
    public function getTargetKind()
389
    {
390
        return $this->lastSegment->getTargetKind();
391
    }
392
393
    /**
394
     * Gets kind of 'source of data' targeted by the resource path.
395
     *
396
     * @return TargetSource
397
     */
398
    public function getTargetSource()
399
    {
400
        return $this->lastSegment->getTargetSource();
401
    }
402
403
    /**
404
     * Gets reference to the ResourceSetWrapper instance targeted by
405
     * the resource path, ResourceSetWrapper will present in the
406
     * following cases:
407
     * if the last segment descriptor describes
408
     *      (a) resource set
409
     *          http://server/NW.svc/Customers
410
     *          http://server/NW.svc/Customers('ALFKI')
411
     *          http://server/NW.svc/Customers('ALFKI')/Orders
412
     *          http://server/NW.svc/Customers('ALFKI')/Orders(123)
413
     *          http://server/NW.svc/Customers('ALFKI')/$links/Orders
414
     *      (b) resource set reference
415
     *          http://server/NW.svc/Orders(123)/Customer
416
     *          http://server/NW.svc/Orders(123)/$links/Customer
417
     *      (c) $count
418
     *          http://server/NW.svc/Customers/$count
419
     * ResourceSet wrapper will be absent (NULL) in the following cases:
420
     * if the last segment descriptor describes
421
     *      (a) Primitive
422
     *          http://server/NW.svc/Customers('ALFKI')/Country
423
     *      (b) $value on primitive type
424
     *          http://server/NW.svc/Customers('ALFKI')/Country/$value
425
     *      (c) Complex
426
     *          http://server/NW.svc/Customers('ALFKI')/Address
427
     *      (d) Bag
428
     *          http://server/NW.svc/Employees(123)/Emails
429
     *      (e) MLE
430
     *          http://server/NW.svc/Employees(123)/$value
431
     *      (f) Named Stream
432
     *          http://server/NW.svc/Employees(123)/Thumnail48_48
433
     *      (g) metadata
434
     *          http://server/NW.svc/$metadata
435
     *      (h) service directory
436
     *          http://server/NW.svc
437
     *      (i) $bath
438
     *          http://server/NW.svc/$batch.
439
     *
440
     * @return ResourceSetWrapper|null
441
     */
442
    public function getTargetResourceSetWrapper()
443
    {
444
        return $this->lastSegment->getTargetResourceSetWrapper();
445
    }
446
447
    /**
448
     * Gets reference to the ResourceType instance targeted by
449
     * the resource path, ResourceType will present in the
450
     * following cases:
451
     * if the last segment descriptor describes
452
     *      (a) resource set
453
     *          http://server/NW.svc/Customers
454
     *          http://server/NW.svc/Customers('ALFKI')
455
     *          http://server/NW.svc/Customers('ALFKI')/Orders
456
     *          http://server/NW.svc/Customers('ALFKI')/Orders(123)
457
     *          http://server/NW.svc/Customers('ALFKI')/$links/Orders
458
     *      (b) resource set reference
459
     *          http://server/NW.svc/Orders(123)/Customer
460
     *          http://server/NW.svc/Orders(123)/$links/Customer
461
     *      (c) $count
462
     *          http://server/NW.svc/Customers/$count
463
     *      (d) Primitive
464
     *          http://server/NW.svc/Customers('ALFKI')/Country
465
     *      (e) $value on primitive type
466
     *          http://server/NW.svc/Customers('ALFKI')/Country/$value
467
     *      (f) Complex
468
     *          http://server/NW.svc/Customers('ALFKI')/Address
469
     *      (g) Bag
470
     *          http://server/NW.svc/Employees(123)/Emails
471
     *      (h) MLE
472
     *          http://server/NW.svc/Employees(123)/$value
473
     *      (i) Named Stream
474
     *          http://server/NW.svc/Employees(123)/Thumnail48_48
475
     * ResourceType will be absent (NULL) in the following cases:
476
     * if the last segment descriptor describes
477
     *      (a) metadata
478
     *          http://server/NW.svc/$metadata
479
     *      (b) service directory
480
     *          http://server/NW.svc
481
     *      (c) $bath
482
     *          http://server/NW.svc/$batch.
483
     *
484
     * @return ResourceType|null
485
     */
486
    public function getTargetResourceType()
487
    {
488
        return $this->lastSegment->getTargetResourceType();
489
    }
490
491
    /**
492
     * Gets reference to the ResourceProperty instance targeted by
493
     * the resource path, ResourceProperty will present in the
494
     * following cases:
495
     * if the last segment descriptor describes
496
     *      (a) resource set (after 1 level)
497
     *          http://server/NW.svc/Customers('ALFKI')/Orders
498
     *          http://server/NW.svc/Customers('ALFKI')/Orders(123)
499
     *          http://server/NW.svc/Customers('ALFKI')/$links/Orders
500
     *      (b) resource set reference
501
     *          http://server/NW.svc/Orders(123)/Customer
502
     *          http://server/NW.svc/Orders(123)/$links/Customer
503
     *      (c) $count
504
     *          http://server/NW.svc/Customers/$count
505
     *      (d) Primitive
506
     *          http://server/NW.svc/Customers('ALFKI')/Country
507
     *      (e) $value on primitive type
508
     *          http://server/NW.svc/Customers('ALFKI')/Country/$value
509
     *      (f) Complex
510
     *          http://server/NW.svc/Customers('ALFKI')/Address
511
     *      (g) Bag
512
     *          http://server/NW.svc/Employees(123)/Emails
513
     *      (h) MLE
514
     *          http://server/NW.svc/Employees(123)/$value.
515
     *
516
     * ResourceType will be absent (NULL) in the following cases:
517
     * if the last segment descriptor describes
518
     *      (a) If last segment is the only segment pointing to
519
     *          ResourceSet (single or multiple)
520
     *          http://server/NW.svc/Customers
521
     *          http://server/NW.svc/Customers('ALFKI')
522
     *      (b) Named Stream
523
     *          http://server/NW.svc/Employees(123)/Thumnail48_48
524
     *      (c) metadata
525
     *          http://server/NW.svc/$metadata
526
     *      (d) service directory
527
     *          http://server/NW.svc
528
     *      (e) $bath
529
     *          http://server/NW.svc/$batch
530
     *
531
     * @return ResourceProperty|null
532
     */
533
    public function getProjectedProperty()
534
    {
535
        return  $this->lastSegment->getProjectedProperty();
536
    }
537
538
    /**
539
     * Gets the name of the container for results.
540
     *
541
     * @return string|null
542
     */
543
    public function getContainerName()
544
    {
545
        return $this->_containerName;
546
    }
547
548
    /**
549
     * Sets the name of the container for results.
550
     *
551
     * @param string $containerName The container name
552
     */
553
    public function setContainerName($containerName)
554
    {
555
        $this->_containerName = $containerName;
556
    }
557
558
    /**
559
     * Whether thr request targets a single result or not.
560
     *
561
     * @return bool
562
     */
563
    public function isSingleResult()
564
    {
565
        return $this->lastSegment->isSingleResult();
566
    }
567
568
    /**
569
     * Gets the identifier associated with the the resource path.
570
     *
571
     * @return string
572
     */
573
    public function getIdentifier()
574
    {
575
        return $this->lastSegment->getIdentifier();
576
    }
577
578
    /**
579
     * Gets the request uri.
580
     *
581
     * @return Url
582
     */
583
    public function getRequestUrl()
584
    {
585
        return $this->requestUrl;
586
    }
587
588
    /**
589
     * Gets the value of $skip query option.
590
     *
591
     * @return int|null The value of $skip query option, NULL if $skip is absent
592
     */
593
    public function getSkipCount()
594
    {
595
        return $this->_skipCount;
596
    }
597
598
    /**
599
     * Sets skip value.
600
     *
601
     * @param int $skipCount The value of $skip query option
602
     */
603
    public function setSkipCount($skipCount)
604
    {
605
        $this->_skipCount = $skipCount;
606
    }
607
608
    /**
609
     * Gets the value of take count.
610
     *
611
     * @return int|null The value of take, NULL if no take to be applied
612
     */
613
    public function getTopCount()
614
    {
615
        return $this->_topCount;
616
    }
617
618
    /**
619
     * Sets the value of take count.
620
     *
621
     * @param int $topCount The value of take query option
622
     */
623
    public function setTopCount($topCount)
624
    {
625
        $this->_topCount = $topCount;
626
    }
627
628
    /**
629
     * Gets the value of $top query option.
630
     *
631
     * @return int|null The value of $top query option, NULL if $top is absent
632
     */
633
    public function getTopOptionCount()
634
    {
635
        return $this->_topOptionCount;
636
    }
637
638
    /**
639
     * Sets top value.
640
     *
641
     * @param int $topOptionCount The value of $top query option
642
     */
643
    public function setTopOptionCount($topOptionCount)
644
    {
645
        $this->_topOptionCount = $topOptionCount;
646
    }
647
648
    /**
649
     * Gets sorting (orderby) information, this function return
650
     * sorting information in 3 cases:
651
     * (1) if $orderby option is specified in the request uri
652
     * (2) if $skip or $top option is specified in the request uri
653
     * (3) if server side paging is enabled for the resource targeted
654
     *     by the request uri.
655
     *
656
     * @return InternalOrderByInfo|null
657
     */
658
    public function getInternalOrderByInfo()
659
    {
660
        return $this->internalOrderByInfo;
661
    }
662
663
    /**
664
     * Sets sorting (orderby) information.
665
     *
666
     * @param InternalOrderByInfo &$internalOrderByInfo The sorting information
667
     */
668
    public function setInternalOrderByInfo(InternalOrderByInfo & $internalOrderByInfo)
669
    {
670
        $this->internalOrderByInfo = $internalOrderByInfo;
671
    }
672
673
    /**
674
     * Gets the parsed details for $skiptoken option.
675
     *
676
     * @return InternalSkipTokenInfo|null Returns parsed details of $skiptoken option, NULL if $skiptoken is absent
677
     */
678
    public function getInternalSkipTokenInfo()
679
    {
680
        return $this->_internalSkipTokenInfo;
681
    }
682
683
    /**
684
     * Sets $skiptoken information.
685
     *
686
     * @param InternalSkipTokenInfo &$internalSkipTokenInfo The paging information
687
     */
688
    public function setInternalSkipTokenInfo(
689
        InternalSkipTokenInfo & $internalSkipTokenInfo
690
    ) {
691
        $this->_internalSkipTokenInfo = $internalSkipTokenInfo;
692
    }
693
694
    /**
695
     * @return FilterInfo|null Returns parsed details of $filter option, NULL if $filter is absent
696
     */
697
    public function getFilterInfo()
698
    {
699
        return $this->_filterInfo;
700
    }
701
702
    /**
703
     * @param FilterInfo $filterInfo The filter information
704
     */
705
    public function setFilterInfo(FilterInfo $filterInfo)
706
    {
707
        $this->_filterInfo = $filterInfo;
708
    }
709
710
    /**
711
     * Sets $expand and $select information.
712
     *
713
     * @param RootProjectionNode &$rootProjectionNode Root of the projection tree
714
     */
715
    public function setRootProjectionNode(RootProjectionNode & $rootProjectionNode)
716
    {
717
        $this->_rootProjectionNode = $rootProjectionNode;
718
    }
719
720
    /**
721
     * Gets the root of the tree describing expand and select options,.
722
     *
723
     * @return RootProjectionNode|null Returns parsed details of $expand
724
     *                                 and $select options, NULL if
725
     *                                 $both options are absent
726
     */
727
    public function getRootProjectionNode()
728
    {
729
        return $this->_rootProjectionNode;
730
    }
731
732
    /**
733
     * Gets the count of result set if $count or $inlinecount=allpages
734
     * has been applied otherwise NULL.
735
     *
736
     * @return int|null
737
     */
738
    public function getCountValue()
739
    {
740
        return $this->_countValue;
741
    }
742
743
    /**
744
     * Sets the count of result set.
745
     *
746
     * @param int $countValue The count value
747
     */
748
    public function setCountValue($countValue)
749
    {
750
        $this->_countValue = $countValue;
751
    }
752
753
    /**
754
     * To set the flag indicating the execution status as true.
755
     */
756
    public function setExecuted()
757
    {
758
        $this->_isExecuted = true;
759
    }
760
761
    /**
762
     * To check whether to execute the query using IDSQP.
763
     *
764
     * @return bool True if query need to be executed, False otherwise
765
     */
766
    public function needExecution()
767
    {
768
        return !$this->_isExecuted
769
            && ($this->lastSegment->getTargetKind() != TargetKind::METADATA())
770
            && ($this->lastSegment->getTargetKind() != TargetKind::SERVICE_DIRECTORY());
771
    }
772
773
    /**
774
     * To check if the resource path is a request for link uri.
775
     *
776
     * @return bool True if request is for link uri else false
777
     */
778
    public function isLinkUri()
779
    {
780
        return ($this->_segmentCount > 2) && ($this->segments[$this->_segmentCount - 2]->getTargetKind() == TargetKind::LINK());
781
    }
782
783
    /**
784
     * To check if the resource path is a request for meida resource.
785
     *
786
     * @return bool True if request is for media resource else false
787
     */
788
    public function isMediaResource()
789
    {
790
        return $this->lastSegment->getTargetKind() == TargetKind::MEDIA_RESOURCE();
791
    }
792
793
    /**
794
     * To check if the resource path is a request for named stream.
795
     *
796
     * @return bool True if request is for named stream else false
797
     */
798
    public function isNamedStream()
799
    {
800
        return $this->isMediaResource() && !($this->lastSegment->getIdentifier() === ODataConstants::URI_VALUE_SEGMENT);
801
    }
802
803
    /**
804
     * Get ResourceStreamInfo for the media link entry or named stream request.
805
     *
806
     * @return ResourceStreamInfo|null Instance of ResourceStreamInfo if the
807
     *                                 current request targets named stream, NULL for MLE
808
     */
809
    public function getResourceStreamInfo()
810
    {
811
        //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...
812
        if ($this->isNamedStream()) {
813
            return $this->getTargetResourceType()
814
                ->tryResolveNamedStreamByName(
815
                    $this->lastSegment->getIdentifier()
816
                );
817
        }
818
    }
819
820
    /**
821
     * Gets the resource instance targeted by the request uri.
822
     * Note: This value will be populated after query execution only.
823
     *
824
     * @return mixed
825
     */
826
    public function getTargetResult()
827
    {
828
        return $this->lastSegment->getResult();
829
    }
830
831
    /**
832
     * Gets the OData version the server used to generate the response.
833
     *
834
     * @return Version
835
     */
836
    public function getResponseVersion()
837
    {
838
        return $this->requiredMinResponseVersion;
839
    }
840
841
    /**
842
     * Checks whether etag headers are allowed for this request.
843
     *
844
     * @return bool True if ETag header (If-Match or If-NoneMatch)
845
     *              is allowed for the request, False otherwise
846
     */
847
    public function isETagHeaderAllowed()
848
    {
849
        return $this->lastSegment->isSingleResult()
850
            && ($this->queryType != QueryType::COUNT())
851
            && !$this->isLinkUri()
852
            && (is_null($this->_rootProjectionNode)
853
                || !($this->_rootProjectionNode->isExpansionSpecified())
854
                );
855
    }
856
857
    /**
858
     * Gets collection of known data service versions, currently 1.0, 2.0 and 3.0.
859
     *
860
     * @return Version[]
861
     */
862
    public static function getKnownDataServiceVersions()
863
    {
864
        if (is_null(self::$_knownDataServiceVersions)) {
865
            self::$_knownDataServiceVersions = [
866
                new Version(1, 0),
867
                new Version(2, 0),
868
                new Version(3, 0),
869
            ];
870
        }
871
872
        return self::$_knownDataServiceVersions;
873
    }
874
875
    /**
876
     * This function is used to perform following checking (validation)
877
     * for capability negotiation.
878
     *  (1) Check client request's 'DataServiceVersion' header value is
879
     *      less than or equal to the minimum version required to intercept
880
     *      the response
881
     *  (2) Check client request's 'MaxDataServiceVersion' header value is
882
     *      less than or equal to the version of protocol required to generate
883
     *      the response
884
     *  (3) Check the configured maximum protocol version is less than or equal
885
     *      to the version of protocol required to generate the response
886
     *  In addition to these checking, this function is also responsible for
887
     *  initializing the properties representing 'DataServiceVersion' and
888
     *  'MaxDataServiceVersion'.
889
     *
890
     *
891
     * @throws ODataException If any of the above 3 check fails
892
     */
893
    public function validateVersions()
894
    {
895
896
        //If the request version is below the minimum version required by supplied request arguments..throw an exception
897
        if ($this->requestVersion->compare($this->requiredMinRequestVersion) < 0) {
898
            throw ODataException::createBadRequestError(
899
                Messages::requestVersionTooLow(
900
                    $this->requestVersion->toString(),
901
                    $this->requiredMinRequestVersion->toString()
902
                )
903
            );
904
        }
905
906
        //If the requested max version is below the version required to fulfill the response...throw an exception
907
        if ($this->requestMaxVersion->compare($this->requiredMinResponseVersion) < 0) {
908
            throw ODataException::createBadRequestError(
909
                Messages::requestVersionTooLow(
910
                    $this->requestMaxVersion->toString(),
911
                    $this->requiredMinResponseVersion->toString()
912
                )
913
            );
914
        }
915
916
        //If the max version supported by the service is below the version required to fulfill the response..throw an exception
917
        if ($this->maxServiceVersion->compare($this->requiredMinResponseVersion) < 0) {
918
            throw ODataException::createBadRequestError(
919
                Messages::requestVersionIsBiggerThanProtocolVersion(
920
                    $this->requiredMinResponseVersion->toString(),
921
                    $this->maxServiceVersion->toString()
922
                )
923
            );
924
        }
925
    }
926
927
    /**
928
     * Validates the given version in string format and returns the version as instance of Version.
929
     *
930
     * @param string $versionHeader The DataServiceVersion or MaxDataServiceVersion header value
931
     * @param string $headerName    The name of the header
932
     *
933
     * @throws ODataException If the version is malformed or not supported
934
     *
935
     * @return Version
936
     */
937
    private static function parseVersionHeader($versionHeader, $headerName)
938
    {
939
        $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...
940
        $versionHeader = trim($versionHeader);
941
        $libNameIndex = strpos($versionHeader, ';');
942
        if ($libNameIndex !== false) {
943
            $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...
944
        } else {
945
            $libNameIndex = strlen($versionHeader);
946
        }
947
948
        $dotIndex = -1;
949
        for ($i = 0; $i < $libNameIndex; ++$i) {
950
            if ($versionHeader[$i] == '.') {
951
                //Throw an exception if we find more than 1 dot
952
                if ($dotIndex != -1) {
953
                    throw ODataException::createBadRequestError(
954
                        Messages::requestDescriptionInvalidVersionHeader(
955
                            $versionHeader,
956
                            $headerName
957
                        )
958
                    );
959
                }
960
961
                $dotIndex = $i;
962
            } elseif ($versionHeader[$i] < '0' || $versionHeader[$i] > '9') {
963
                throw ODataException::createBadRequestError(
964
                    Messages::requestDescriptionInvalidVersionHeader(
965
                        $versionHeader,
966
                        $headerName
967
                    )
968
                );
969
            }
970
        }
971
972
        $major = intval(substr($versionHeader, 0, $dotIndex));
973
        $minor = 0;
974
975
        //Apparently the . is optional
976
        if ($dotIndex != -1) {
977
            if ($dotIndex == 0) {
978
                //If it starts with a ., throw an exception
979
                throw ODataException::createBadRequestError(
980
                    Messages::requestDescriptionInvalidVersionHeader(
981
                        $versionHeader,
982
                        $headerName
983
                    )
984
                );
985
            }
986
            $minor = intval(substr($versionHeader, $dotIndex + 1, $libNameIndex));
987
        }
988
989
        $version = new Version($major, $minor);
990
991
        //TODO: move this somewhere...
992
        //$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...
993
        $isSupportedVersion = false;
994
        foreach (self::getKnownDataServiceVersions() as $version1) {
995
            if ($version->compare($version1) == 0) {
996
                $isSupportedVersion = true;
997
                break;
998
            }
999
        }
1000
1001 View Code Duplication
        if (!$isSupportedVersion) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1002
            $availableVersions = null;
1003
            foreach (self::getKnownDataServiceVersions() as $version1) {
1004
                $availableVersions .= $version1->toString() . ', ';
1005
            }
1006
1007
            $availableVersions = rtrim($availableVersions, ', ');
1008
            throw ODataException::createBadRequestError(
1009
                Messages::requestDescriptionUnSupportedVersion(
1010
                    $headerName,
1011
                    $versionHeader,
1012
                    $availableVersions
1013
                )
1014
            );
1015
        }
1016
1017
        return $version;
1018
    }
1019
1020
    /**
1021
     * Gets reference to the UriProcessor instance.
1022
     *
1023
     * @return UriProcessor
1024
     */
1025
    public function getUriProcessor()
1026
    {
1027
        return $this->_uriProcessor;
1028
    }
1029
1030
    /**
1031
     * Set reference to UriProcessor instance.
1032
     *
1033
     * @param UriProcessor $uriProcessor Reference to the UriProcessor
1034
     */
1035
    public function setUriProcessor(UriProcessor $uriProcessor)
1036
    {
1037
        $this->_uriProcessor = $uriProcessor;
1038
    }
1039
}
1040