RequestDescription::getRequestUrl()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace POData\UriProcessor;
6
7
use POData\Common\Messages;
8
use POData\Common\MimeTypes;
9
use POData\Common\ODataConstants;
10
use POData\Common\ODataException;
11
use POData\Common\Url;
12
use POData\Common\Version;
13
use POData\ObjectModel\ODataEntry;
14
use POData\ObjectModel\ODataFeed;
15
use POData\OperationContext\IHTTPRequest;
16
use POData\Providers\Metadata\ResourceProperty;
17
use POData\Providers\Metadata\ResourceSetWrapper;
18
use POData\Providers\Metadata\ResourceStreamInfo;
19
use POData\Providers\Metadata\ResourceType;
20
use POData\Providers\Query\QueryType;
21
use POData\Readers\ODataReaderRegistry;
22
use POData\UriProcessor\Interfaces\IUriProcessor;
23
use POData\UriProcessor\QueryProcessor\ExpandProjectionParser\RootProjectionNode;
24
use POData\UriProcessor\QueryProcessor\ExpressionParser\FilterInfo;
25
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
26
use POData\UriProcessor\QueryProcessor\SkipTokenParser\InternalSkipTokenInfo;
27
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\SegmentDescriptor;
28
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetKind;
29
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetSource;
30
31
/**
32
 * Class RequestDescription.
33
 */
34
class RequestDescription
35
{
36
    /**
37
     * Collection of known data service versions.
38
     *
39
     * @var Version[]
40
     */
41
    private static $knownDataServiceVersions = null;
42
    /**
43
     * The count option specified in the request.
44
     *
45
     * @var QueryType
46
     */
47
    public $queryType;
48
    /**
49
     * Holds the value of HTTP 'DataServiceVersion' header in the request,
50
     * DataServiceVersion header value states the version of the
51
     * Open Data Protocol used by the client to generate the request.
52
     * Refer http://www.odata.org/developers/protocols/overview#ProtocolVersioning.
53
     *
54
     * @var Version
55
     */
56
    private $requestVersion = null;
57
    /**
58
     * Holds the value of HTTP 'MaxDataServiceVersion' header in the request,
59
     * MaxDataServiceVersion header value specifies the maximum version number
60
     * the client can accept in a response.
61
     * Refer http://www.odata.org/developers/protocols/overview#ProtocolVersioning.
62
     *
63
     * @var Version
64
     */
65
    private $requestMaxVersion = null;
66
    /**
67
     * This is the value of 'DataServiceVersion' header to be output in the response. this header
68
     * value states the OData version the server used to generate the response.
69
     * While processing the query and result set this value will be keeps on
70
     * updating, after every update this is compared against the
71
     * 'MaxDataServiceVersion' header in the client request to see whether the
72
     * client can interpret the response or not. The client should use this
73
     * value to determine whether it can correctly interpret the response or not.
74
     * Refer http://www.odata.org/developers/protocols/overview#ProtocolVersioning.
75
     *
76
     * @var Version
77
     */
78
    private $requiredMinResponseVersion;
79
    /**
80
     * The minimum client version requirement, This value keeps getting updated
81
     * during processing of query, this is compared against the
82
     * DataServiceVersion header in the client request and if the client request
83
     * is less than this value then we fail the request (e.g. $count request
84
     * was sent but client said it was Version 1.0).
85
     *
86
     * @var Version
87
     */
88
    private $requiredMinRequestVersion;
89
    /** @var Version */
90
    private $maxServiceVersion;
91
    /**
92
     * @var Url
93
     */
94
    private $requestUrl;
95
    /**
96
     * Collection of SegmentDescriptor containing information about
97
     * each segment in the resource path part of the request uri.
98
     *
99
     * @var SegmentDescriptor[]
100
     */
101
    private $segments;
102
    /**
103
     * Holds reference to the last segment descriptor.
104
     *
105
     * @var SegmentDescriptor
106
     */
107
    private $lastSegment;
108
    /**
109
     * The name of the container for results.
110
     *
111
     * @var string|null
112
     */
113
    private $containerName;
114
    /**
115
     * Number of segments.
116
     *
117
     * @var int
118
     */
119
    private $segmentCount;
120
121
    /**
122
     * Holds the value of $skip query option, if no $skip option
123
     * found then this parameter will be NULL.
124
     *
125
     * @var int|null
126
     */
127
    private $skipCount;
128
129
    /**
130
     * Holds the value of take count, this value is depends on
131
     * presence of $top option and configured page size.
132
     *
133
     * @var int|null
134
     */
135
    private $topCount;
136
137
    /**
138
     * Holds the value of $top query option, if no $top option
139
     * found then this parameter will be NULL.
140
     *
141
     * @var int|null
142
     */
143
    private $topOptionCount;
144
145
    /**
146
     * Holds the parsed details for sorting, this will
147
     * be set in 3 cases
148
     * (1) if $orderby option is specified in the request uri
149
     * (2) if $skip or $top option is specified in the request uri
150
     * (3) if server side paging is enabled for the resource
151
     *     targeted by the request uri.
152
     *
153
     * @var InternalOrderByInfo|null
154
     */
155
    private $internalOrderByInfo;
156
157
    /**
158
     * Holds the parsed details for $skiptoken option, this will
159
     * be NULL if $skiptoken option is absent.
160
     *
161
     * @var InternalSkipTokenInfo|null
162
     */
163
    private $internalSkipTokenInfo;
164
165
    /**
166
     * Holds the parsed details for $filter option, this will be NULL if $filter option is absent.
167
     *
168
     * @var FilterInfo|null
169
     */
170
    private $filterInfo;
171
172
    /**
173
     * Holds reference to the root of the tree describing expand
174
     * and select information, this field will be NULL if no
175
     * $expand or $select specified in the request uri.
176
     *
177
     * @var RootProjectionNode|null
178
     */
179
    private $rootProjectionNode;
180
181
    /**
182
     * Holds number of entities in the result set, if either $count or
183
     * $inlinecount=allpages is specified, otherwise NULL.
184
     *
185
     *
186
     * @var int|null
187
     */
188
    private $countValue;
189
190
    /**
191
     * Data of request from request body.
192
     *
193
     * @var array|string|ODataFeed|ODataEntry|null
194
     */
195
    private $data;
196
197
    /**
198
     * Flag indicating status of query execution.
199
     *
200
     * @var bool
201
     */
202
    private $isExecuted;
203
204
    /**
205
     * Reference to Uri processor.
206
     *
207
     * @var IUriProcessor
208
     */
209
    private $uriProcessor;
210
    /**
211
     * @var ODataReaderRegistry
212
     */
213
    private $readerRegistry;
214
215
    /**
216
     * @param  SegmentDescriptor[] $segmentDescriptors Description of segments
217
     *                                                 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|null         $dataType
223
     * @param  IHTTPRequest|null   $payload
224
     * @param  ODataReaderRegistry $readerRegistry
225
     * @throws ODataException
226
     */
227
    public function __construct(
228
        array $segmentDescriptors,
229
        Url $requestUri,
230
        Version $serviceMaxVersion,
231
        $requestVersion,
232
        $maxRequestVersion,
233
        $dataType = null,
234
        IHTTPRequest $payload = null,
235
        ODataReaderRegistry $readerRegistry = null
236
    ) {
237
        $this->readerRegistry = $readerRegistry;
238
        $this->segments       = $segmentDescriptors;
239
        $this->segmentCount   = count($this->segments);
240
        $this->requestUrl     = $requestUri;
241
        $this->lastSegment    = $segmentDescriptors[$this->segmentCount - 1];
242
        $this->queryType      = QueryType::ENTITIES();
243
        //we use this for validation checks down in validateVersions...
244
        //but maybe we should check that outside of this object...
245
        $this->maxServiceVersion = $serviceMaxVersion;
246
247
        //Per OData 1 & 2 spec we must return the smallest size
248
        //We start at 1.0 and move it up as features are requested
249
        $this->requiredMinResponseVersion = clone Version::v1();
250
        $this->requiredMinRequestVersion  = clone Version::v1();
251
252
        //see http://www.odata.org/documentation/odata-v2-documentation/overview/#ProtocolVersioning
253
        //if requestVersion isn't there, use Service Max Version
254
        $this->requestVersion = null === $requestVersion
255
            ? $serviceMaxVersion
256
            : self::parseVersionHeader($requestVersion, ODataConstants::ODATAVERSIONHEADER);
257
258
        //if max version isn't there, use the request version
259
        $this->requestMaxVersion = null === $maxRequestVersion
260
            ? $this->requestVersion
261
            : self::parseVersionHeader($maxRequestVersion, ODataConstants::ODATAMAXVERSIONHEADER);
262
263
        //if it's OData v3..things change a bit
264
        if ($this->maxServiceVersion == Version::v3()) {
265
            if (null === $maxRequestVersion) {
266
                //if max request version isn't specified we use the service max version instead of the request version
267
                //thus we favour newer versions
268
                $this->requestMaxVersion = $this->maxServiceVersion;
269
            }
270
271
            //also we change min response version to be the max version, again favoring later things
272
            //note that if the request max version is specified, it is still respected
273
            $this->requiredMinResponseVersion = clone $this->requestMaxVersion;
274
        }
275
276
        $this->containerName         = null;
277
        $this->skipCount             = null;
278
        $this->topCount              = null;
279
        $this->topOptionCount        = null;
280
        $this->internalOrderByInfo   = null;
281
        $this->internalSkipTokenInfo = null;
282
283
        $this->filterInfo = null;
284
        $this->countValue = null;
285
        $this->isExecuted = false;
286
        $this->data       = isset($payload) ? $payload->getAllInput() : null;
287
288
        // Define data from request body
289
        if (null !== $dataType) {
290
            $this->readData($dataType);
291
        }
292
    }
293
294
    /**
295
     * Validates the given version in string format and returns the version as instance of Version.
296
     *
297
     * @param string $versionHeader The DataServiceVersion or MaxDataServiceVersion header value
298
     * @param string $headerName    The name of the header
299
     *
300
     * @throws ODataException If the version is malformed or not supported
301
     * @return Version
302
     */
303
    private static function parseVersionHeader($versionHeader, $headerName)
304
    {
305
        $versionHeader = trim($versionHeader);
306
        $libNameIndex  = strpos($versionHeader, ';');
307
        if (false === $libNameIndex) {
308
            $libNameIndex = strlen($versionHeader);
309
        }
310
311
        $dotIndex      = -1;
312
        $badVersionMsg = Messages::requestDescriptionInvalidVersionHeader(
313
            $versionHeader,
314
            $headerName
315
        );
316
        for ($i = 0; $i < $libNameIndex; ++$i) {
317
            if ($versionHeader[$i] == '.') {
318
                //Throw an exception if we find more than 1 dot
319
                if ($dotIndex != -1) {
320
                    throw ODataException::createBadRequestError($badVersionMsg);
321
                }
322
323
                $dotIndex = $i;
324
            } elseif ($versionHeader[$i] < '0' || $versionHeader[$i] > '9') {
325
                throw ODataException::createBadRequestError($badVersionMsg);
326
            }
327
        }
328
329
        $major = intval(substr($versionHeader, 0, $dotIndex));
330
        $minor = 0;
331
332
        //Apparently the . is optional
333
        if ($dotIndex != -1) {
334
            if ($dotIndex == 0) {
335
                //If it starts with a ., throw an exception
336
                throw ODataException::createBadRequestError($badVersionMsg);
337
            }
338
            $minor = intval(substr($versionHeader, $dotIndex + 1, $libNameIndex));
339
        }
340
341
        $version = new Version($major, $minor);
342
343
        //TODO: move this somewhere...
344
        //$this->validateVersions();
345
        $isSupportedVersion = false;
346
        foreach (self::getKnownDataServiceVersions() as $version1) {
347
            if ($version->compare($version1) == 0) {
348
                $isSupportedVersion = true;
349
                break;
350
            }
351
        }
352
353
        if (!$isSupportedVersion) {
354
            $availableVersions = null;
355
            foreach (self::getKnownDataServiceVersions() as $version1) {
356
                $availableVersions .= $version1->toString() . ', ';
357
            }
358
359
            $availableVersions = rtrim($availableVersions, ', ');
0 ignored issues
show
Bug introduced by
It seems like $availableVersions can also be of type null; however, parameter $string of rtrim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

359
            $availableVersions = rtrim(/** @scrutinizer ignore-type */ $availableVersions, ', ');
Loading history...
360
            throw ODataException::createBadRequestError(
361
                Messages::requestDescriptionUnSupportedVersion(
362
                    $headerName,
363
                    $versionHeader,
364
                    $availableVersions
365
                )
366
            );
367
        }
368
369
        return $version;
370
    }
371
372
    /**
373
     * Gets collection of known data service versions, currently 1.0, 2.0 and 3.0.
374
     *
375
     * @return Version[]
376
     */
377
    public static function getKnownDataServiceVersions()
378
    {
379
        if (null === self::$knownDataServiceVersions) {
0 ignored issues
show
introduced by
The condition null === self::knownDataServiceVersions is always false.
Loading history...
380
            self::$knownDataServiceVersions = [
381
                new Version(1, 0),
382
                new Version(2, 0),
383
                new Version(3, 0),
384
            ];
385
        }
386
387
        return self::$knownDataServiceVersions;
388
    }
389
390
    /**
391
     * Define request data from body.
392
     *
393
     * @param  string $dataType
394
     * @return void
395
     */
396
    private function readData($dataType)
397
    {
398
        if (null === $this->data) {
399
            return;
400
        }
401
        $string = $this->data;
402
        if ($dataType === MimeTypes::MIME_APPLICATION_JSON) {
403
            $data       = !is_array($string) ? json_decode($string, true) : $string;
0 ignored issues
show
Bug introduced by
It seems like $string can also be of type POData\ObjectModel\ODataEntry and POData\ObjectModel\ODataFeed; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

403
            $data       = !is_array($string) ? json_decode(/** @scrutinizer ignore-type */ $string, true) : $string;
Loading history...
404
            $this->data = $data;
405
            return;
406
        }
407
408
        $reader = $this->readerRegistry->getReader($this->requestVersion, $dataType);
409
        if ($reader !== null) {
410
            if (is_array($string) && 1 == count($string)) {
411
                $string = $string[0];
412
            }
413
            if (0 == strlen(trim($string))) {
414
                return;
415
            }
416
            $this->data = $reader->read($string);
417
        }
418
    }
419
420
    /**
421
     * Get request data from body.
422
     */
423
    public function getData()
424
    {
425
        return $this->data;
426
    }
427
428
    /**
429
     * Raise the minimum client version requirement for this request and
430
     * perform capability negotiation.
431
     *
432
     * @param int $major The major segment of the version
433
     * @param int $minor The minor segment of the version
434
     *
435
     * @throws ODataException If capability negotiation fails
436
     */
437
    public function raiseMinVersionRequirement($major, $minor)
438
    {
439
        if ($this->requiredMinRequestVersion->raiseVersion($major, $minor)) {
440
            $this->validateVersions();
441
        }
442
    }
443
444
    /**
445
     * This function is used to perform following checking (validation)
446
     * for capability negotiation.
447
     *  (1) Check client request's 'DataServiceVersion' header value is
448
     *      less than or equal to the minimum version required to intercept
449
     *      the response
450
     *  (2) Check client request's 'MaxDataServiceVersion' header value is
451
     *      less than or equal to the version of protocol required to generate
452
     *      the response
453
     *  (3) Check the configured maximum protocol version is less than or equal
454
     *      to the version of protocol required to generate the response
455
     *  In addition to these checking, this function is also responsible for
456
     *  initializing the properties representing 'DataServiceVersion' and
457
     *  'MaxDataServiceVersion'.
458
     *
459
     *
460
     * @throws ODataException If any of the above 3 check fails
461
     */
462
    public function validateVersions()
463
    {
464
465
        //If the request version is below the minimum version required by supplied request arguments..throw an exception
466
        if ($this->requestVersion->compare($this->requiredMinRequestVersion) < 0) {
467
            throw ODataException::createBadRequestError(
468
                Messages::requestVersionTooLow(
469
                    $this->requestVersion->toString(),
470
                    $this->requiredMinRequestVersion->toString()
471
                )
472
            );
473
        }
474
475
        //If the requested max version is below the version required to fulfill the response...throw an exception
476
        if ($this->requestMaxVersion->compare($this->requiredMinResponseVersion) < 0) {
477
            throw ODataException::createBadRequestError(
478
                Messages::requestVersionTooLow(
479
                    $this->requestMaxVersion->toString(),
480
                    $this->requiredMinResponseVersion->toString()
481
                )
482
            );
483
        }
484
485
        //If the max version supported by the service is below the version required to fulfill the response..
486
        //throw an exception
487
        if ($this->maxServiceVersion->compare($this->requiredMinResponseVersion) < 0) {
488
            throw ODataException::createBadRequestError(
489
                Messages::requestVersionIsBiggerThanProtocolVersion(
490
                    $this->requiredMinResponseVersion->toString(),
491
                    $this->maxServiceVersion->toString()
492
                )
493
            );
494
        }
495
    }
496
497
    /**
498
     * Raise the response version for this request and perform capability negotiation.
499
     *
500
     *
501
     * @param int $major The major segment of the version
502
     * @param int $minor The minor segment of the version
503
     *
504
     * @throws ODataException If capability negotiation fails
505
     */
506
    public function raiseResponseVersion($major, $minor)
507
    {
508
        if ($this->requiredMinResponseVersion->raiseVersion($major, $minor)) {
509
            $this->validateVersions();
510
        }
511
    }
512
513
    /**
514
     * Gets collection of segment descriptors containing information about
515
     * each segment in the resource path part of the request uri.
516
     *
517
     * @return SegmentDescriptor[]
518
     */
519
    public function getSegments()
520
    {
521
        return $this->segments;
522
    }
523
524
    /**
525
     * Gets reference to the descriptor of last segment.
526
     *
527
     * @return SegmentDescriptor
528
     */
529
    public function getLastSegment()
530
    {
531
        return $this->lastSegment;
532
    }
533
534
    /**
535
     * Gets kind of resource targeted by the resource path.
536
     *
537
     * @return TargetKind
538
     */
539
    public function getTargetKind()
540
    {
541
        return $this->lastSegment->getTargetKind();
542
    }
543
544
    /**
545
     * Gets kind of 'source of data' targeted by the resource path.
546
     *
547
     * @return TargetSource
548
     */
549
    public function getTargetSource()
550
    {
551
        return $this->lastSegment->getTargetSource();
552
    }
553
554
    /**
555
     * Gets reference to the ResourceSetWrapper instance targeted by
556
     * the resource path, ResourceSetWrapper will present in the
557
     * following cases:
558
     * if the last segment descriptor describes
559
     *      (a) resource set
560
     *          http://server/NW.svc/Customers
561
     *          http://server/NW.svc/Customers('ALFKI')
562
     *          http://server/NW.svc/Customers('ALFKI')/Orders
563
     *          http://server/NW.svc/Customers('ALFKI')/Orders(123)
564
     *          http://server/NW.svc/Customers('ALFKI')/$links/Orders
565
     *      (b) resource set reference
566
     *          http://server/NW.svc/Orders(123)/Customer
567
     *          http://server/NW.svc/Orders(123)/$links/Customer
568
     *      (c) $count
569
     *          http://server/NW.svc/Customers/$count
570
     * ResourceSet wrapper will be absent (NULL) in the following cases:
571
     * if the last segment descriptor describes
572
     *      (a) Primitive
573
     *          http://server/NW.svc/Customers('ALFKI')/Country
574
     *      (b) $value on primitive type
575
     *          http://server/NW.svc/Customers('ALFKI')/Country/$value
576
     *      (c) Complex
577
     *          http://server/NW.svc/Customers('ALFKI')/Address
578
     *      (d) Bag
579
     *          http://server/NW.svc/Employees(123)/Emails
580
     *      (e) MLE
581
     *          http://server/NW.svc/Employees(123)/$value
582
     *      (f) Named Stream
583
     *          http://server/NW.svc/Employees(123)/Thumbnail48_48
584
     *      (g) metadata
585
     *          http://server/NW.svc/$metadata
586
     *      (h) service directory
587
     *          http://server/NW.svc
588
     *      (i) $bath
589
     *          http://server/NW.svc/$batch.
590
     *
591
     * @return ResourceSetWrapper|null
592
     */
593
    public function getTargetResourceSetWrapper()
594
    {
595
        return $this->lastSegment->getTargetResourceSetWrapper();
596
    }
597
598
    /**
599
     * Gets reference to the ResourceProperty instance targeted by
600
     * the resource path, ResourceProperty will present in the
601
     * following cases:
602
     * if the last segment descriptor describes
603
     *      (a) resource set (after 1 level)
604
     *          http://server/NW.svc/Customers('ALFKI')/Orders
605
     *          http://server/NW.svc/Customers('ALFKI')/Orders(123)
606
     *          http://server/NW.svc/Customers('ALFKI')/$links/Orders
607
     *      (b) resource set reference
608
     *          http://server/NW.svc/Orders(123)/Customer
609
     *          http://server/NW.svc/Orders(123)/$links/Customer
610
     *      (c) $count
611
     *          http://server/NW.svc/Customers/$count
612
     *      (d) Primitive
613
     *          http://server/NW.svc/Customers('ALFKI')/Country
614
     *      (e) $value on primitive type
615
     *          http://server/NW.svc/Customers('ALFKI')/Country/$value
616
     *      (f) Complex
617
     *          http://server/NW.svc/Customers('ALFKI')/Address
618
     *      (g) Bag
619
     *          http://server/NW.svc/Employees(123)/Emails
620
     *      (h) MLE
621
     *          http://server/NW.svc/Employees(123)/$value.
622
     *
623
     * ResourceType will be absent (NULL) in the following cases:
624
     * if the last segment descriptor describes
625
     *      (a) If last segment is the only segment pointing to
626
     *          ResourceSet (single or multiple)
627
     *          http://server/NW.svc/Customers
628
     *          http://server/NW.svc/Customers('ALFKI')
629
     *      (b) Named Stream
630
     *          http://server/NW.svc/Employees(123)/Thumbnail48_48
631
     *      (c) metadata
632
     *          http://server/NW.svc/$metadata
633
     *      (d) service directory
634
     *          http://server/NW.svc
635
     *      (e) $bath
636
     *          http://server/NW.svc/$batch
637
     *
638
     * @return ResourceProperty|null
639
     */
640
    public function getProjectedProperty()
641
    {
642
        return $this->lastSegment->getProjectedProperty();
643
    }
644
645
    /**
646
     * Gets the name of the container for results.
647
     *
648
     * @return string
649
     */
650
    public function getContainerName()
651
    {
652
        return $this->containerName;
653
    }
654
655
    /**
656
     * Sets the name of the container for results.
657
     *
658
     * @param string $containerName The container name
659
     */
660
    public function setContainerName($containerName)
661
    {
662
        $this->containerName = $containerName;
663
    }
664
665
    /**
666
     * Whether thr request targets a single result or not.
667
     *
668
     * @return bool
669
     */
670
    public function isSingleResult()
671
    {
672
        return $this->lastSegment->isSingleResult();
673
    }
674
675
    /**
676
     * Gets the identifier associated with the the resource path.
677
     *
678
     * @return string
679
     */
680
    public function getIdentifier()
681
    {
682
        return $this->lastSegment->getIdentifier();
683
    }
684
685
    /**
686
     * Gets the request uri.
687
     *
688
     * @return Url
689
     */
690
    public function getRequestUrl()
691
    {
692
        return $this->requestUrl;
693
    }
694
695
    /**
696
     * Gets the value of $skip query option.
697
     *
698
     * @return int|null The value of $skip query option, NULL if $skip is absent
699
     */
700
    public function getSkipCount()
701
    {
702
        return $this->skipCount;
703
    }
704
705
    /**
706
     * Sets skip value.
707
     *
708
     * @param int $skipCount The value of $skip query option
709
     */
710
    public function setSkipCount($skipCount)
711
    {
712
        $this->skipCount = $skipCount;
713
    }
714
715
    /**
716
     * Gets the value of take count.
717
     *
718
     * @return int|null The value of take, NULL if no take to be applied
719
     */
720
    public function getTopCount()
721
    {
722
        return $this->topCount;
723
    }
724
725
    /**
726
     * Sets the value of take count.
727
     *
728
     * @param int $topCount The value of take query option
729
     */
730
    public function setTopCount($topCount)
731
    {
732
        $this->topCount = $topCount;
733
    }
734
735
    /**
736
     * Gets the value of $top query option.
737
     *
738
     * @return int|null The value of $top query option, NULL if $top is absent
739
     */
740
    public function getTopOptionCount()
741
    {
742
        return $this->topOptionCount;
743
    }
744
745
    /**
746
     * Sets top value.
747
     *
748
     * @param int $topOptionCount The value of $top query option
749
     */
750
    public function setTopOptionCount($topOptionCount)
751
    {
752
        $this->topOptionCount = $topOptionCount;
753
    }
754
755
    /**
756
     * Gets sorting (orderby) information, this function return
757
     * sorting information in 3 cases:
758
     * (1) if $orderby option is specified in the request uri
759
     * (2) if $skip or $top option is specified in the request uri
760
     * (3) if server side paging is enabled for the resource targeted
761
     *     by the request uri.
762
     *
763
     * @return InternalOrderByInfo|null
764
     */
765
    public function getInternalOrderByInfo()
766
    {
767
        return $this->internalOrderByInfo;
768
    }
769
770
    /**
771
     * Sets sorting (orderby) information.
772
     *
773
     * @param InternalOrderByInfo &$internalOrderByInfo The sorting information
774
     */
775
    public function setInternalOrderByInfo(InternalOrderByInfo &$internalOrderByInfo)
776
    {
777
        $this->internalOrderByInfo = $internalOrderByInfo;
778
    }
779
780
    /**
781
     * Gets the parsed details for $skiptoken option.
782
     *
783
     * @return InternalSkipTokenInfo|null Returns parsed details of $skiptoken option, NULL if $skiptoken is absent
784
     */
785
    public function getInternalSkipTokenInfo()
786
    {
787
        return $this->internalSkipTokenInfo;
788
    }
789
790
    /**
791
     * Sets $skiptoken information.
792
     *
793
     * @param InternalSkipTokenInfo &$internalSkipTokenInfo The paging information
794
     */
795
    public function setInternalSkipTokenInfo(
796
        InternalSkipTokenInfo &$internalSkipTokenInfo
797
    ) {
798
        $this->internalSkipTokenInfo = $internalSkipTokenInfo;
799
    }
800
801
    /**
802
     * @return FilterInfo|null Returns parsed details of $filter option, NULL if $filter is absent
803
     */
804
    public function getFilterInfo()
805
    {
806
        return $this->filterInfo;
807
    }
808
809
    /**
810
     * @param FilterInfo $filterInfo The filter information
811
     */
812
    public function setFilterInfo(FilterInfo $filterInfo)
813
    {
814
        $this->filterInfo = $filterInfo;
815
    }
816
817
    /**
818
     * Gets the root of the tree describing expand and select options,.
819
     *
820
     * @return RootProjectionNode|null Returns parsed details of $expand
821
     *                                 and $select options, NULL if
822
     *                                 $both options are absent
823
     */
824
    public function getRootProjectionNode()
825
    {
826
        return $this->rootProjectionNode;
827
    }
828
829
    /**
830
     * Sets $expand and $select information.
831
     *
832
     * @param RootProjectionNode &$rootProjectionNode Root of the projection tree
833
     */
834
    public function setRootProjectionNode(RootProjectionNode &$rootProjectionNode)
835
    {
836
        $this->rootProjectionNode = $rootProjectionNode;
837
    }
838
839
    /**
840
     * Gets the count of result set if $count or $inlinecount=allpages
841
     * has been applied otherwise NULL.
842
     *
843
     * @return int|null
844
     */
845
    public function getCountValue()
846
    {
847
        return $this->countValue;
848
    }
849
850
    /**
851
     * Sets the count of result set.
852
     *
853
     * @param int $countValue The count value
854
     */
855
    public function setCountValue($countValue)
856
    {
857
        $this->countValue = $countValue;
858
    }
859
860
    /**
861
     * To set the flag indicating the execution status as true.
862
     */
863
    public function setExecuted()
864
    {
865
        $this->isExecuted = true;
866
    }
867
868
    /**
869
     * To check whether to execute the query using IDSQP.
870
     *
871
     * @return bool True if query need to be executed, False otherwise
872
     */
873
    public function needExecution()
874
    {
875
        return !$this->isExecuted
876
            && ($this->lastSegment->getTargetKind() != TargetKind::METADATA())
877
            && ($this->lastSegment->getTargetKind() != TargetKind::SERVICE_DIRECTORY());
878
    }
879
880
    /**
881
     * Get ResourceStreamInfo for the media link entry or named stream request.
882
     *
883
     * @return ResourceStreamInfo|null Instance of ResourceStreamInfo if the
884
     *                                 current request targets named stream, NULL for MLE
885
     */
886
    public function getResourceStreamInfo()
887
    {
888
        //assert($this->isMediaResource)
889
        if ($this->isNamedStream()) {
890
            return $this->getTargetResourceType()
891
                ->tryResolveNamedStreamByName(
892
                    $this->lastSegment->getIdentifier()
893
                );
894
        }
895
        return null;
896
    }
897
898
    /**
899
     * To check if the resource path is a request for named stream.
900
     *
901
     * @return bool True if request is for named stream else false
902
     */
903
    public function isNamedStream()
904
    {
905
        return $this->isMediaResource() && !($this->lastSegment->getIdentifier() === ODataConstants::URI_VALUE_SEGMENT);
906
    }
907
908
    /**
909
     * To check if the resource path is a request for media resource.
910
     *
911
     * @return bool True if request is for media resource else false
912
     */
913
    public function isMediaResource()
914
    {
915
        return $this->lastSegment->getTargetKind() == TargetKind::MEDIA_RESOURCE();
916
    }
917
918
    /**
919
     * Gets reference to the ResourceType instance targeted by
920
     * the resource path, ResourceType will present in the
921
     * following cases:
922
     * if the last segment descriptor describes
923
     *      (a) resource set
924
     *          http://server/NW.svc/Customers
925
     *          http://server/NW.svc/Customers('ALFKI')
926
     *          http://server/NW.svc/Customers('ALFKI')/Orders
927
     *          http://server/NW.svc/Customers('ALFKI')/Orders(123)
928
     *          http://server/NW.svc/Customers('ALFKI')/$links/Orders
929
     *      (b) resource set reference
930
     *          http://server/NW.svc/Orders(123)/Customer
931
     *          http://server/NW.svc/Orders(123)/$links/Customer
932
     *      (c) $count
933
     *          http://server/NW.svc/Customers/$count
934
     *      (d) Primitive
935
     *          http://server/NW.svc/Customers('ALFKI')/Country
936
     *      (e) $value on primitive type
937
     *          http://server/NW.svc/Customers('ALFKI')/Country/$value
938
     *      (f) Complex
939
     *          http://server/NW.svc/Customers('ALFKI')/Address
940
     *      (g) Bag
941
     *          http://server/NW.svc/Employees(123)/Emails
942
     *      (h) MLE
943
     *          http://server/NW.svc/Employees(123)/$value
944
     *      (i) Named Stream
945
     *          http://server/NW.svc/Employees(123)/Thumbnail48_48
946
     * ResourceType will be absent (NULL) in the following cases:
947
     * if the last segment descriptor describes
948
     *      (a) metadata
949
     *          http://server/NW.svc/$metadata
950
     *      (b) service directory
951
     *          http://server/NW.svc
952
     *      (c) $bath
953
     *          http://server/NW.svc/$batch.
954
     *
955
     * @return ResourceType|null
956
     */
957
    public function getTargetResourceType()
958
    {
959
        return $this->lastSegment->getTargetResourceType();
960
    }
961
962
    /**
963
     * Gets the resource instance targeted by the request uri.
964
     * Note: This value will be populated after query execution only.
965
     *
966
     * @return mixed
967
     */
968
    public function getTargetResult()
969
    {
970
        return $this->lastSegment->getResult();
971
    }
972
973
    /**
974
     * Gets the OData version the server used to generate the response.
975
     *
976
     * @return Version
977
     */
978
    public function getResponseVersion()
979
    {
980
        return $this->requiredMinResponseVersion;
981
    }
982
983
    /**
984
     * Checks whether etag headers are allowed for this request.
985
     *
986
     * @return bool True if ETag header (If-Match or If-NoneMatch)
987
     *              is allowed for the request, False otherwise
988
     */
989
    public function isETagHeaderAllowed()
990
    {
991
        return $this->lastSegment->isSingleResult()
992
            && ($this->queryType != QueryType::COUNT())
993
            && !$this->isLinkUri()
994
            && (
995
                null === $this->rootProjectionNode
996
                || !($this->rootProjectionNode->isExpansionSpecified())
997
            );
998
    }
999
1000
    /**
1001
     * To check if the resource path is a request for link uri.
1002
     *
1003
     * @return bool True if request is for link uri else false
1004
     */
1005
    public function isLinkUri()
1006
    {
1007
        return ($this->segmentCount > 2)
1008
            && ($this->segments[$this->segmentCount - 2]->getTargetKind() == TargetKind::LINK());
1009
    }
1010
1011
    /**
1012
     * Gets reference to the IUriProcessor instance.
1013
     *
1014
     * @return IUriProcessor
1015
     */
1016
    public function getUriProcessor()
1017
    {
1018
        return $this->uriProcessor;
1019
    }
1020
1021
    /**
1022
     * Set reference to IUriProcessor instance.
1023
     *
1024
     * @param IUriProcessor $uriProcessor Reference to the UriProcessor
1025
     */
1026
    public function setUriProcessor(IUriProcessor $uriProcessor)
1027
    {
1028
        $this->uriProcessor = $uriProcessor;
1029
    }
1030
}
1031