Completed
Pull Request — master (#199)
by Alex
08:41
created

LaravelReadQuery::getResourceSet()   F

Complexity

Conditions 18
Paths 1026

Size

Total Lines 95
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 14
Bugs 1 Features 0
Metric Value
eloc 55
c 14
b 1
f 0
dl 0
loc 95
rs 0.7
cc 18
nc 1026
nop 9

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace AlgoWeb\PODataLaravel\Query;
4
5
use AlgoWeb\PODataLaravel\Enums\ActionVerb;
6
use AlgoWeb\PODataLaravel\Models\MetadataTrait;
7
use Illuminate\Database\Eloquent\Builder;
8
use Illuminate\Database\Eloquent\Collection;
9
use Illuminate\Database\Eloquent\Model;
10
use Illuminate\Database\Eloquent\Relations\Relation;
11
use Illuminate\Support\Facades\App;
12
use POData\Common\InvalidOperationException;
13
use POData\Common\ODataException;
14
use POData\Providers\Metadata\ResourceProperty;
15
use POData\Providers\Metadata\ResourceSet;
16
use POData\Providers\Query\QueryResult;
17
use POData\Providers\Query\QueryType;
18
use POData\UriProcessor\QueryProcessor\ExpressionParser\FilterInfo;
19
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
20
use POData\UriProcessor\QueryProcessor\SkipTokenParser\SkipTokenInfo;
21
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor;
22
use Symfony\Component\Process\Exception\InvalidArgumentException;
23
24
class LaravelReadQuery extends LaravelBaseQuery
25
{
26
    /**
27
     * Gets collection of entities belongs to an entity set
28
     * IE: http://host/EntitySet
29
     *  http://host/EntitySet?$skip=10&$top=5&filter=Prop gt Value.
30
     *
31
     * @param QueryType                $queryType            Is this is a query for a count, entities,
32
     *                                                       or entities-with-count?
33
     * @param ResourceSet              $resourceSet          The entity set containing the entities to fetch
34
     * @param FilterInfo|null          $filterInfo           The $filter parameter of the OData query.  NULL if absent
35
     * @param null|InternalOrderByInfo $orderBy              sorted order if we want to get the data in some
36
     *                                                       specific order
37
     * @param int|null                 $top                  number of records which need to be retrieved
38
     * @param int|null                 $skip                 number of records which need to be skipped
39
     * @param SkipTokenInfo|null       $skipToken            value indicating what records to skip
40
     * @param string[]|null            $eagerLoad            array of relations to eager load
41
     * @param Model|Relation|null      $sourceEntityInstance Starting point of query
42
     *
43
     * @return QueryResult
44
     * @throws InvalidArgumentException
45
     * @throws InvalidOperationException
46
     * @throws \ReflectionException
47
     * @throws ODataException
48
     */
49
    public function getResourceSet(
50
        QueryType $queryType,
51
        ResourceSet $resourceSet,
52
        FilterInfo $filterInfo = null,
53
        $orderBy = null,
54
        $top = null,
55
        $skip = null,
56
        SkipTokenInfo $skipToken = null,
57
        array $eagerLoad = null,
58
        $sourceEntityInstance = null
59
    ) {
60
        $rawLoad = $this->processEagerLoadList($eagerLoad);
61
62
        $sourceEntityInstance = $this->checkSourceInstance($sourceEntityInstance, $resourceSet);
63
64
        /** @var MetadataTrait $model */
65
        $model = $sourceEntityInstance instanceof Model ? $sourceEntityInstance : $sourceEntityInstance->getRelated();
66
        $modelLoad = $model->getEagerLoad();
67
        $keyName = $model->getKeyName();
68
        $tableName = $model->getTable();
69
70
        if (null === $keyName) {
0 ignored issues
show
introduced by
The condition null === $keyName is always false.
Loading history...
71
            throw new InvalidOperationException('Key name not retrieved');
72
        }
73
        $rawLoad = array_values(array_unique(array_merge($rawLoad, $modelLoad)));
74
75
        $checkInstance = $sourceEntityInstance instanceof Model ? $sourceEntityInstance : null;
76
        $this->checkAuth($sourceEntityInstance, $checkInstance);
77
78
        $result          = new QueryResult();
79
        $result->results = null;
80
        $result->count   = null;
81
82
        if (null != $orderBy) {
83
            foreach ($orderBy->getOrderByInfo()->getOrderByPathSegments() as $order) {
84
                foreach ($order->getSubPathSegments() as $subOrder) {
85
                    $subName = $subOrder->getName();
86
                    $subName = $tableName.'.'.$subName;
87
                    $sourceEntityInstance = $sourceEntityInstance->orderBy(
88
                        $subName,
89
                        $order->isAscending() ? 'asc' : 'desc'
90
                    );
91
                }
92
            }
93
        }
94
95
        // throttle up for trench run
96
        if (null != $skipToken) {
97
            $sourceEntityInstance = $this->processSkipToken($skipToken, $sourceEntityInstance);
98
        }
99
100
        if (!isset($skip)) {
101
            $skip = 0;
102
        }
103
        if (!isset($top)) {
104
            $top = PHP_INT_MAX;
105
        }
106
107
        $nullFilter = !isset($filterInfo);
108
        $isvalid = null;
109
        if (isset($filterInfo)) {
110
            $method = 'return ' . $filterInfo->getExpressionAsString() . ';';
111
            $clln = $resourceSet->getResourceType()->getName();
112
            $isvalid = function ($inputD) use ($clln, $method) {
113
                $$clln = $inputD;
114
                return eval($method);
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
115
            };
116
        }
117
118
        list($bulkSetCount, $resultSet, $resultCount, $skip) = $this->applyFiltering(
119
            $top,
120
            $skip,
121
            $sourceEntityInstance,
122
            $nullFilter,
123
            $rawLoad,
124
            $isvalid
125
        );
126
127
        if (isset($top)) {
128
            $resultSet = $resultSet->take($top);
129
        }
130
131
        $qVal = $queryType;
132
        if (QueryType::ENTITIES() == $qVal || QueryType::ENTITIES_WITH_COUNT() == $qVal) {
133
            $result->results = [];
134
            foreach ($resultSet as $res) {
135
                $result->results[] = $res;
136
            }
137
        }
138
        if (QueryType::COUNT() == $qVal || QueryType::ENTITIES_WITH_COUNT() == $qVal) {
139
            $result->count = $resultCount;
140
        }
141
        $hazMore = $bulkSetCount > $skip + count($resultSet);
142
        $result->hasMore = $hazMore;
143
        return $result;
144
    }
145
146
    /**
147
     * Get related resource set for a resource
148
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection
149
     * http://host/EntitySet?$expand=NavigationPropertyToCollection.
150
     *
151
     * @param QueryType          $queryType            Is this is a query for a count, entities, or entities-with-count
152
     * @param ResourceSet        $sourceResourceSet    The entity set containing the source entity
153
     * @param Model              $sourceEntityInstance The source entity instance
154
     * @param ResourceSet        $targetResourceSet    The resource set pointed to by the navigation property
155
     * @param ResourceProperty   $targetProperty       The navigation property to retrieve
156
     * @param FilterInfo|null    $filter               The $filter parameter of the OData query.  NULL if none specified
157
     * @param mixed|null         $orderBy              sorted order if we want to get the data in some specific order
158
     * @param int|null           $top                  number of records which need to be retrieved
159
     * @param int|null           $skip                 number of records which need to be skipped
160
     * @param SkipTokenInfo|null $skipToken            value indicating what records to skip
161
     *
162
     * @return QueryResult
163
     * @throws InvalidOperationException
164
     * @throws ODataException
165
     * @throws \ReflectionException
166
     */
167
    public function getRelatedResourceSet(
168
        QueryType $queryType,
169
        ResourceSet $sourceResourceSet,
170
        Model $sourceEntityInstance,
171
        /** @noinspection PhpUnusedParameterInspection */
172
        ResourceSet $targetResourceSet,
173
        ResourceProperty $targetProperty,
174
        FilterInfo $filter = null,
175
        $orderBy = null,
176
        $top = null,
177
        $skip = null,
178
        SkipTokenInfo $skipToken = null
179
    ) {
180
        $this->checkAuth($sourceEntityInstance);
181
182
        $propertyName = $targetProperty->getName();
183
        $results = $sourceEntityInstance->$propertyName();
184
185
        return $this->getResourceSet(
186
            $queryType,
187
            $sourceResourceSet,
188
            $filter,
189
            $orderBy,
190
            $top,
191
            $skip,
192
            $skipToken,
193
            null,
194
            $results
195
        );
196
    }
197
198
    /**
199
     * Gets an entity instance from an entity set identified by a key
200
     * IE: http://host/EntitySet(1L)
201
     * http://host/EntitySet(KeyA=2L,KeyB='someValue').
202
     *
203
     * @param ResourceSet        $resourceSet   The entity set containing the entity to fetch
204
     * @param KeyDescriptor|null $keyDescriptor The key identifying the entity to fetch
205
     * @param string[]|null      $eagerLoad     array of relations to eager load
206
     *
207
     * @return Model|null Returns entity instance if found else null
208
     * @throws \Exception;
209
     */
210
    public function getResourceFromResourceSet(
211
        ResourceSet $resourceSet,
212
        KeyDescriptor $keyDescriptor = null,
213
        array $eagerLoad = null
214
    ) {
215
        return $this->getResource($resourceSet, $keyDescriptor, [], $eagerLoad);
216
    }
217
218
219
    /**
220
     * Common method for getResourceFromRelatedResourceSet() and getResourceFromResourceSet().
221
     *
222
     * @param ResourceSet|null    $resourceSet
223
     * @param KeyDescriptor|null  $keyDescriptor
224
     * @param Model|Relation|null $sourceEntityInstance Starting point of query
225
     * @param array               $whereCondition
226
     * @param string[]|null       $eagerLoad            array of relations to eager load
227
     *
228
     * @return Model|null
229
     * @throws \Exception
230
     */
231
    public function getResource(
232
        ResourceSet $resourceSet = null,
233
        KeyDescriptor $keyDescriptor = null,
234
        array $whereCondition = [],
235
        array $eagerLoad = null,
236
        $sourceEntityInstance = null
237
    ) {
238
        if (null == $resourceSet && null == $sourceEntityInstance) {
239
            $msg = 'Must supply at least one of a resource set and source entity.';
240
            throw new \Exception($msg);
241
        }
242
243
        $sourceEntityInstance = $this->checkSourceInstance($sourceEntityInstance, $resourceSet);
244
245
        $this->checkAuth($sourceEntityInstance);
246
        $modelLoad = null;
247
        if ($sourceEntityInstance instanceof Model) {
248
            $modelLoad = $sourceEntityInstance->getEagerLoad();
249
        } elseif ($sourceEntityInstance instanceof Relation) {
250
            /** @var MetadataTrait $model */
251
            $model = $sourceEntityInstance->getRelated();
252
            $modelLoad = $model->getEagerLoad();
253
        }
254
        if (!(isset($modelLoad))) {
255
            throw new InvalidOperationException('');
256
        }
257
258
        $this->processKeyDescriptor(/** @scrutinizer ignore-type */$sourceEntityInstance, $keyDescriptor);
259
        foreach ($whereCondition as $fieldName => $fieldValue) {
260
            $sourceEntityInstance = $sourceEntityInstance->where($fieldName, $fieldValue);
261
        }
262
263
        $sourceEntityInstance = $sourceEntityInstance->get();
264
        $sourceCount = $sourceEntityInstance->count();
265
        if (0 == $sourceCount) {
266
            return null;
267
        }
268
        $result = $sourceEntityInstance->first();
269
270
        return $result;
271
    }
272
273
    /**
274
     * Get related resource for a resource
275
     * IE: http://host/EntitySet(1L)/NavigationPropertyToSingleEntity
276
     * http://host/EntitySet?$expand=NavigationPropertyToSingleEntity.
277
     *
278
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
279
     * @param Model            $sourceEntityInstance the source entity instance
280
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity pointed to by the nav property
281
     * @param ResourceProperty $targetProperty       The navigation property to fetch
282
     *
283
     * @return Model|null The related resource if found else null
284
     * @throws ODataException
285
     * @throws InvalidOperationException
286
     * @throws \ReflectionException
287
     */
288
    public function getRelatedResourceReference(
289
        /** @noinspection PhpUnusedParameterInspection */
290
        ResourceSet $sourceResourceSet,
291
        Model $sourceEntityInstance,
292
        ResourceSet $targetResourceSet,
293
        ResourceProperty $targetProperty
294
    ) {
295
        $this->checkAuth($sourceEntityInstance);
296
297
        $propertyName = $targetProperty->getName();
298
        $propertyName = $this->getLaravelRelationName($propertyName);
299
        $result = $sourceEntityInstance->$propertyName()->first();
300
        if (null === $result) {
301
            return null;
302
        }
303
        if (!$result instanceof Model) {
304
            throw new InvalidOperationException('Model not retrieved from Eloquent relation');
305
        }
306
        if ($targetProperty->getResourceType()->getInstanceType()->getName() != get_class($result)) {
307
            return null;
308
        }
309
        return $result;
310
    }
311
312
    /**
313
     * Gets a related entity instance from an entity set identified by a key
314
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection(33).
315
     *
316
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
317
     * @param Model            $sourceEntityInstance the source entity instance
318
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity to fetch
319
     * @param ResourceProperty $targetProperty       the metadata of the target property
320
     * @param KeyDescriptor    $keyDescriptor        The key identifying the entity to fetch
321
     *
322
     * @return Model|null Returns entity instance if found else null
323
     * @throws InvalidOperationException
324
     * @throws \Exception
325
     */
326
    public function getResourceFromRelatedResourceSet(
327
        /** @noinspection PhpUnusedParameterInspection */
328
        ResourceSet $sourceResourceSet,
329
        Model $sourceEntityInstance,
330
        ResourceSet $targetResourceSet,
331
        ResourceProperty $targetProperty,
332
        KeyDescriptor $keyDescriptor
333
    ) {
334
        $propertyName = $targetProperty->getName();
335
        if (!method_exists($sourceEntityInstance, $propertyName)) {
336
            $msg = 'Relation method, ' . $propertyName . ', does not exist on supplied entity.';
337
            throw new InvalidArgumentException($msg);
338
        }
339
        // take key descriptor and turn it into where clause here, rather than in getResource call
340
        $sourceEntityInstance = $sourceEntityInstance->$propertyName();
341
        $this->processKeyDescriptor($sourceEntityInstance, $keyDescriptor);
342
        $result = $this->getResource(null, null, [], [], $sourceEntityInstance);
343
        if (!(null == $result || $result instanceof Model)) {
0 ignored issues
show
introduced by
$result is always a sub-type of Illuminate\Database\Eloquent\Model.
Loading history...
344
            $msg = 'GetResourceFromRelatedResourceSet must return an entity or null';
345
            throw new InvalidOperationException($msg);
346
        }
347
        return $result;
348
    }
349
350
    /**
351
     * @param  ResourceSet $resourceSet
352
     * @return mixed
353
     * @throws \ReflectionException
354
     */
355
    protected function getSourceEntityInstance(ResourceSet $resourceSet)
356
    {
357
        $entityClassName = $resourceSet->getResourceType()->getInstanceType()->name;
358
        return App::make($entityClassName);
359
    }
360
361
    /**
362
     * @param Model|Relation|null $source
363
     * @param ResourceSet|null $resourceSet
364
     * @return Model|Relation|mixed|null
365
     * @throws \ReflectionException
366
     */
367
    protected function checkSourceInstance($source, ResourceSet $resourceSet = null)
368
    {
369
        if (!(null == $source || $source instanceof Model || $source instanceof Relation)) {
0 ignored issues
show
introduced by
$source is always a sub-type of Illuminate\Database\Eloquent\Relations\Relation.
Loading history...
370
            $msg = 'Source entity instance must be null, a model, or a relation.';
371
            throw new InvalidArgumentException($msg);
372
        }
373
374
        if (null == $source) {
375
            $source = $this->getSourceEntityInstance(/** @scrutinizer ignore-type */$resourceSet);
376
        }
377
378
        return $source;
379
    }
380
381
    /**
382
     * @param Model|Relation|null $sourceEntityInstance
383
     * @param null|mixed $checkInstance
384
     *
385
     * @throws ODataException
386
     */
387
    private function checkAuth($sourceEntityInstance, $checkInstance = null)
388
    {
389
        $check = array_reduce([$sourceEntityInstance, $checkInstance], function ($carry, $item) {
390
            if ($item instanceof Model || $item instanceof Relation) {
391
                return $item;
392
            }
393
        }, null);
394
        $sourceName = null !== $check ? get_class($check) : null;
395
        if (!$this->getAuth()->canAuth(ActionVerb::READ(), $sourceName, $check)) {
396
            throw new ODataException('Access denied', 403);
397
        }
398
    }
399
400
    /**
401
     * @param Model|Builder $sourceEntityInstance
402
     * @param  KeyDescriptor|null        $keyDescriptor
403
     * @throws InvalidOperationException
404
     */
405
    private function processKeyDescriptor(&$sourceEntityInstance, KeyDescriptor $keyDescriptor = null)
406
    {
407
        if ($keyDescriptor) {
408
            $table = ($sourceEntityInstance instanceof Model) ? $sourceEntityInstance->getTable().'.' : '';
409
            foreach ($keyDescriptor->getValidatedNamedValues() as $key => $value) {
410
                $trimValue = trim($value[0], '\'');
411
                $sourceEntityInstance = $sourceEntityInstance->where($table.$key, $trimValue);
412
            }
413
        }
414
    }
415
416
    /**
417
     * @param  string[]|null $eagerLoad
418
     * @return array
419
     * @throws InvalidOperationException
420
     */
421
    private function processEagerLoadList(array $eagerLoad = null)
422
    {
423
        $load = (null === $eagerLoad) ? [] : $eagerLoad;
424
        $rawLoad = [];
425
        foreach ($load as $line) {
426
            if (!is_string($line)) {
427
                throw new InvalidOperationException('Eager-load elements must be non-empty strings');
428
            }
429
            $lineParts = explode('/', $line);
430
            $numberOfParts = count($lineParts);
431
            for ($i = 0; $i<$numberOfParts; $i++) {
432
                $lineParts[$i] = $this->getLaravelRelationName($lineParts[$i]);
433
            }
434
            $remixLine = implode('.', $lineParts);
435
            $rawLoad[] = $remixLine;
436
        }
437
        return $rawLoad;
438
    }
439
440
    /**
441
     * @param  string $odataProperty
442
     * @return string
443
     */
444
    private function getLaravelRelationName($odataProperty)
445
    {
446
        $laravelProperty = $odataProperty;
447
        $pos = strrpos($laravelProperty, '_');
448
        if ($pos !== false) {
449
            $laravelProperty = substr($laravelProperty, 0, $pos);
450
        }
451
        return $laravelProperty;
452
    }
453
454
    /**
455
     * @param SkipTokenInfo $skipToken
456
     * @param Model|Builder $sourceEntityInstance
457
     * @return mixed
458
     * @throws InvalidOperationException
459
     */
460
    protected function processSkipToken(SkipTokenInfo $skipToken, $sourceEntityInstance)
461
    {
462
        $parameters = [];
463
        $processed = [];
464
        $segments = $skipToken->getOrderByInfo()->getOrderByPathSegments();
465
        $values = $skipToken->getOrderByKeysInToken();
466
        $numValues = count($values);
467
        if ($numValues != count($segments)) {
468
            $msg = 'Expected '.count($segments).', got '.$numValues;
469
            throw new InvalidOperationException($msg);
470
        }
471
472
        for ($i = 0; $i < $numValues; $i++) {
473
            $relation = $segments[$i]->isAscending() ? '>' : '<';
474
            $name = $segments[$i]->getSubPathSegments()[0]->getName();
475
            $parameters[$name] = ['direction' => $relation, 'value' => trim($values[$i][0], '\'')];
0 ignored issues
show
Bug introduced by
$values[$i][0] of type POData\Providers\Metadata\Type\IType is incompatible with the type string expected by parameter $str of trim(). ( Ignorable by Annotation )

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

475
            $parameters[$name] = ['direction' => $relation, 'value' => trim(/** @scrutinizer ignore-type */ $values[$i][0], '\'')];
Loading history...
476
        }
477
478
        foreach ($parameters as $name => $line) {
479
            $processed[$name] = ['direction' => $line['direction'], 'value' => $line['value']];
480
            $sourceEntityInstance = $sourceEntityInstance
481
                ->orWhere(
482
                    function (Builder $query) use ($processed) {
483
                        foreach ($processed as $key => $proc) {
484
                            $query->where($key, $proc['direction'], $proc['value']);
485
                        }
486
                    }
487
                );
488
            // now we've handled the later-in-order segment for this key, drop it back to equality in prep
489
            // for next key - same-in-order for processed keys and later-in-order for next
490
            $processed[$name]['direction'] = '=';
491
        }
492
        return $sourceEntityInstance;
493
    }
494
495
    /**
496
     * @param $top
497
     * @param $skip
498
     * @param Model|Builder $sourceEntityInstance
499
     * @param $nullFilter
500
     * @param $rawLoad
501
     * @param callable|null $isvalid
502
     * @return array
503
     * @throws InvalidOperationException
504
     */
505
    protected function applyFiltering(
506
        $top,
507
        $skip,
508
        $sourceEntityInstance,
509
        $nullFilter,
510
        $rawLoad,
511
        callable $isvalid = null
512
    ) {
513
        $bulkSetCount = $sourceEntityInstance->count();
514
        $bigSet = 20000 < $bulkSetCount;
515
516
        if ($nullFilter) {
517
            // default no-filter case, palm processing off to database engine - is a lot faster
518
            $resultSet = $sourceEntityInstance->skip($skip)->take($top)->with($rawLoad)
519
                ->get();
520
            $resultCount = $bulkSetCount;
521
        } elseif ($bigSet) {
522
            if (!(isset($isvalid))) {
523
                $msg = 'Filter closure not set';
524
                throw new InvalidOperationException($msg);
525
            }
526
            $resultSet = new Collection([]);
527
            $rawCount = 0;
528
            $rawTop = null === $top ? $bulkSetCount : $top;
529
530
            // loop thru, chunk by chunk, to reduce chances of exhausting memory
531
            $sourceEntityInstance->chunk(
532
                5000,
533
                function (Collection $results) use ($isvalid, &$skip, &$resultSet, &$rawCount, $rawTop) {
534
                    // apply filter
535
                    $results = $results->filter($isvalid);
536
                    // need to iterate through full result set to find count of items matching filter,
537
                    // so we can't bail out early
538
                    $rawCount += $results->count();
539
                    // now bolt on filtrate to accumulating result set if we haven't accumulated enough bitz
540
                    if ($rawTop > $resultSet->count() + $skip) {
541
                        $resultSet = collect(array_merge($resultSet->all(), $results->all()));
542
                        $sliceAmount = min($skip, $resultSet->count());
543
                        $resultSet = $resultSet->slice($sliceAmount);
544
                        $skip -= $sliceAmount;
545
                    }
546
                }
547
            );
548
549
            // clean up residual to-be-skipped records
550
            $resultSet = $resultSet->slice($skip);
551
            $resultCount = $rawCount;
552
        } else {
553
            /** @var Collection $resultSet */
554
            $resultSet = $sourceEntityInstance->with($rawLoad)->get();
555
            $resultSet = $resultSet->filter($isvalid);
556
            $resultCount = $resultSet->count();
557
558
            if (isset($skip)) {
559
                $resultSet = $resultSet->slice($skip);
560
            }
561
        }
562
        return [$bulkSetCount, $resultSet, $resultCount, $skip];
563
    }
564
}
565