Completed
Push — master ( 56c97f...f4aeff )
by Christopher
14s
created

RequestDescription::readData()   D

Complexity

Conditions 9
Paths 9

Size

Total Lines 27
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

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