Completed
Pull Request — master (#137)
by Christopher
02:48
created

LaravelReadQuery::getLaravelRelationName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 1
1
<?php
2
3
namespace AlgoWeb\PODataLaravel\Query;
4
5
use AlgoWeb\PODataLaravel\Auth\NullAuthProvider;
6
use AlgoWeb\PODataLaravel\Enums\ActionVerb;
7
use AlgoWeb\PODataLaravel\Interfaces\AuthInterface;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Database\Eloquent\Relations\Relation;
10
use Illuminate\Support\Facades\App;
11
use POData\Common\InvalidOperationException;
12
use POData\Common\ODataException;
13
use POData\Providers\Metadata\ResourceProperty;
14
use POData\Providers\Metadata\ResourceSet;
15
use POData\Providers\Query\QueryResult;
16
use POData\Providers\Query\QueryType;
17
use POData\UriProcessor\QueryProcessor\ExpressionParser\FilterInfo;
18
use POData\UriProcessor\QueryProcessor\OrderByParser\InternalOrderByInfo;
19
use POData\UriProcessor\QueryProcessor\SkipTokenParser\SkipTokenInfo;
20
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\KeyDescriptor;
21
use Symfony\Component\Process\Exception\InvalidArgumentException;
22
23
class LaravelReadQuery
24
{
25
    const PK = 'PrimaryKey';
26
27
    protected $auth;
28
29
    public function __construct(AuthInterface $auth = null)
30
    {
31
        $this->auth = isset($auth) ? $auth : new NullAuthProvider();
32
    }
33
34
    /**
35
     * Gets collection of entities belongs to an entity set
36
     * IE: http://host/EntitySet
37
     *  http://host/EntitySet?$skip=10&$top=5&filter=Prop gt Value.
38
     *
39
     * @param QueryType                $queryType            Is this is a query for a count, entities,
40
     *                                                       or entities-with-count?
41
     * @param ResourceSet              $resourceSet          The entity set containing the entities to fetch
42
     * @param FilterInfo|null          $filterInfo           The $filter parameter of the OData query.  NULL if absent
43
     * @param null|InternalOrderByInfo $orderBy              sorted order if we want to get the data in some
44
     *                                                       specific order
45
     * @param int|null                 $top                  number of records which need to be retrieved
46
     * @param int|null                 $skip                 number of records which need to be skipped
47
     * @param SkipTokenInfo|null       $skipToken            value indicating what records to skip
48
     * @param string[]|null            $eagerLoad            array of relations to eager load
49
     * @param Model|Relation|null      $sourceEntityInstance Starting point of query
50
     *
51
     * @return QueryResult
52
     */
53
    public function getResourceSet(
54
        QueryType $queryType,
55
        ResourceSet $resourceSet,
56
        $filterInfo = null,
57
        $orderBy = null,
58
        $top = null,
59
        $skip = null,
60
        $skipToken = null,
61
        array $eagerLoad = null,
62
        $sourceEntityInstance = null
63
    ) {
64
        if (null != $filterInfo && !($filterInfo instanceof FilterInfo)) {
65
            $msg = 'Filter info must be either null or instance of FilterInfo.';
66
            throw new InvalidArgumentException($msg);
67
        }
68
        if (null != $skipToken && !($skipToken instanceof SkipTokenInfo)) {
69
            $msg = 'Skip token must be either null or instance of SkipTokenInfo.';
70
            throw new InvalidArgumentException($msg);
71
        }
72
        $rawLoad = $this->processEagerLoadList($eagerLoad);
73
        $modelLoad = [];
74
75
        $this->checkSourceInstance($sourceEntityInstance);
76
        if (null == $sourceEntityInstance) {
77
            $sourceEntityInstance = $this->getSourceEntityInstance($resourceSet);
78
        }
79
80
        $keyName = null;
81
        if ($sourceEntityInstance instanceof Model) {
82
            $modelLoad = $sourceEntityInstance->getEagerLoad();
83
            $keyName = $sourceEntityInstance->getKeyName();
84
        } elseif ($sourceEntityInstance instanceof Relation) {
85
            $modelLoad = $sourceEntityInstance->getRelated()->getEagerLoad();
86
            $keyName = $sourceEntityInstance->getRelated()->getKeyName();
87
        }
88
        assert(isset($keyName));
89
        $rawLoad = array_values(array_unique(array_merge($rawLoad, $modelLoad)));
90
91
        $checkInstance = $sourceEntityInstance instanceof Model ? $sourceEntityInstance : null;
92
        $this->checkAuth($sourceEntityInstance, $checkInstance);
93
94
        $result          = new QueryResult();
95
        $result->results = null;
96
        $result->count   = null;
97
98
        if (null != $orderBy) {
99
            foreach ($orderBy->getOrderByInfo()->getOrderByPathSegments() as $order) {
100
                foreach ($order->getSubPathSegments() as $subOrder) {
101
                    $subName = $subOrder->getName();
102
                    $subName = (self::PK == $subName) ? $keyName : $subName;
103
                    $sourceEntityInstance = $sourceEntityInstance->orderBy(
104
                        $subName,
105
                        $order->isAscending() ? 'asc' : 'desc'
106
                    );
107
                }
108
            }
109
        }
110
111
        // throttle up for trench run
112
        if (null != $skipToken) {
113
            $parameters = [];
114
            $processed = [];
115
            $segments = $skipToken->getOrderByInfo()->getOrderByPathSegments();
116
            $values = $skipToken->getOrderByKeysInToken();
117
            $numValues = count($values);
118
            assert($numValues == count($segments));
119
120
            for ($i = 0; $i < $numValues; $i++) {
121
                $relation = $segments[$i]->isAscending() ? '>' : '<';
122
                $name = $segments[$i]->getSubPathSegments()[0]->getName();
123
                $name = (self::PK == $name) ? $keyName : $name;
124
                $parameters[$name] = ['direction' => $relation, 'value' => trim($values[$i][0], '\'')];
125
            }
126
127
            foreach ($parameters as $name => $line) {
128
                $processed[$name] = ['direction' => $line['direction'], 'value' => $line['value']];
129
                $sourceEntityInstance = $sourceEntityInstance
130
                    ->orWhere(
131
                        function ($query) use ($processed) {
132
                            foreach ($processed as $key => $proc) {
133
                                $query->where($key, $proc['direction'], $proc['value']);
134
                            }
135
                        }
136
                    );
137
                // now we've handled the later-in-order segment for this key, drop it back to equality in prep
138
                // for next key - same-in-order for processed keys and later-in-order for next
139
                $processed[$name]['direction'] = '=';
140
            }
141
        }
142
143
        if (!isset($skip)) {
144
            $skip = 0;
145
        }
146
        if (!isset($top)) {
147
            $top = PHP_INT_MAX;
148
        }
149
150
        $nullFilter = true;
151
        $isvalid = null;
152
        if (isset($filterInfo)) {
153
            $method = 'return ' . $filterInfo->getExpressionAsString() . ';';
154
            $clln = '$' . $resourceSet->getResourceType()->getName();
155
            $isvalid = create_function($clln, $method);
0 ignored issues
show
Security Best Practice introduced by
The use of create_function is highly discouraged, better use a closure.

create_function can pose a great security vulnerability as it is similar to eval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.

// Instead of
$function = create_function('$a, $b', 'return $a + $b');

// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
156
            $nullFilter = false;
157
        }
158
159
        $bulkSetCount = $sourceEntityInstance->count();
160
        $bigSet = 20000 < $bulkSetCount;
161
162
        if ($nullFilter) {
163
            // default no-filter case, palm processing off to database engine - is a lot faster
164
            $resultSet = $sourceEntityInstance->skip($skip)->take($top)->with($rawLoad)->get();
165
            $resultCount = $bulkSetCount;
166
        } elseif ($bigSet) {
167
            assert(isset($isvalid), 'Filter closure not set');
168
            $resultSet = collect([]);
169
            $rawCount = 0;
170
            $rawTop = null === $top ? $bulkSetCount : $top;
171
172
            // loop thru, chunk by chunk, to reduce chances of exhausting memory
173
            $sourceEntityInstance->chunk(
174
                5000,
175
                function ($results) use ($isvalid, &$skip, &$resultSet, &$rawCount, $rawTop) {
176
                    // apply filter
177
                    $results = $results->filter($isvalid);
178
                    // need to iterate through full result set to find count of items matching filter,
179
                    // so we can't bail out early
180
                    $rawCount += $results->count();
181
                    // now bolt on filtrate to accumulating result set if we haven't accumulated enough bitz
182
                    if ($rawTop > $resultSet->count()+$skip) {
183
                        $resultSet = collect(array_merge($resultSet->all(), $results->all()));
184
                        $sliceAmount = min($skip, $resultSet->count());
185
                        $resultSet = $resultSet->slice($sliceAmount);
186
                        $skip -= $sliceAmount;
187
                    }
188
                }
189
            );
190
191
            // clean up residual to-be-skipped records
192
            $resultSet = $resultSet->slice($skip);
193
            $resultCount = $rawCount;
194
        } else {
195
            if ($sourceEntityInstance instanceof Model) {
196
                $sourceEntityInstance = $sourceEntityInstance->getQuery();
197
            }
198
            $resultSet = $sourceEntityInstance->with($rawLoad)->get();
199
            $resultSet = $resultSet->filter($isvalid);
200
            $resultCount = $resultSet->count();
201
202
            if (isset($skip)) {
203
                $resultSet = $resultSet->slice($skip);
204
            }
205
        }
206
207
        if (isset($top)) {
208
            $resultSet = $resultSet->take($top);
209
        }
210
211
        if (QueryType::ENTITIES() == $queryType || QueryType::ENTITIES_WITH_COUNT() == $queryType) {
212
            $result->results = [];
213
            foreach ($resultSet as $res) {
214
                $result->results[] = $res;
215
            }
216
        }
217
        if (QueryType::COUNT() == $queryType || QueryType::ENTITIES_WITH_COUNT() == $queryType) {
218
            $result->count = $resultCount;
219
        }
220
        $hazMore = $bulkSetCount > $skip+count($resultSet);
221
        $result->hasMore = $hazMore;
222
        return $result;
223
    }
224
225
    /**
226
     * Get related resource set for a resource
227
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection
228
     * http://host/EntitySet?$expand=NavigationPropertyToCollection.
229
     *
230
     * @param QueryType          $queryType            Is this is a query for a count, entities, or entities-with-count
231
     * @param ResourceSet        $sourceResourceSet    The entity set containing the source entity
232
     * @param Model              $sourceEntityInstance The source entity instance
233
     * @param ResourceSet        $targetResourceSet    The resource set pointed to by the navigation property
234
     * @param ResourceProperty   $targetProperty       The navigation property to retrieve
235
     * @param FilterInfo|null    $filter               The $filter parameter of the OData query.  NULL if none specified
236
     * @param mixed|null         $orderBy              sorted order if we want to get the data in some specific order
237
     * @param int|null           $top                  number of records which need to be retrieved
238
     * @param int|null           $skip                 number of records which need to be skipped
239
     * @param SkipTokenInfo|null $skipToken            value indicating what records to skip
240
     *
241
     * @return QueryResult
242
     */
243
    public function getRelatedResourceSet(
244
        QueryType $queryType,
245
        ResourceSet $sourceResourceSet,
246
        Model $sourceEntityInstance,
247
        ResourceSet $targetResourceSet,
0 ignored issues
show
Unused Code introduced by
The parameter $targetResourceSet is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
248
        ResourceProperty $targetProperty,
249
        FilterInfo $filter = null,
250
        $orderBy = null,
251
        $top = null,
252
        $skip = null,
253
        SkipTokenInfo $skipToken = null
254
    ) {
255
        $this->checkAuth($sourceEntityInstance);
256
257
        $propertyName = $targetProperty->getName();
258
        $results = $sourceEntityInstance->$propertyName();
259
260
        return $this->getResourceSet(
261
            $queryType,
262
            $sourceResourceSet,
263
            $filter,
264
            $orderBy,
265
            $top,
266
            $skip,
267
            $skipToken,
268
            null,
269
            $results
270
        );
271
    }
272
273
    /**
274
     * Gets an entity instance from an entity set identified by a key
275
     * IE: http://host/EntitySet(1L)
276
     * http://host/EntitySet(KeyA=2L,KeyB='someValue').
277
     *
278
     * @param ResourceSet        $resourceSet   The entity set containing the entity to fetch
279
     * @param KeyDescriptor|null $keyDescriptor The key identifying the entity to fetch
280
     * @param string[]|null      $eagerLoad     array of relations to eager load
281
     *
282
     * @return Model|null Returns entity instance if found else null
283
     */
284
    public function getResourceFromResourceSet(
285
        ResourceSet $resourceSet,
286
        KeyDescriptor $keyDescriptor = null,
287
        array $eagerLoad = null
288
    ) {
289
        return $this->getResource($resourceSet, $keyDescriptor, [], $eagerLoad);
290
    }
291
292
293
    /**
294
     * Common method for getResourceFromRelatedResourceSet() and getResourceFromResourceSet().
295
     *
296
     * @param ResourceSet|null    $resourceSet
297
     * @param KeyDescriptor|null  $keyDescriptor
298
     * @param Model|Relation|null $sourceEntityInstance Starting point of query
299
     * $param array               $whereCondition
300
     * @param string[]|null       $eagerLoad            array of relations to eager load
301
     *
302
     * @return Model|null
303
     */
304
    public function getResource(
305
        ResourceSet $resourceSet = null,
306
        KeyDescriptor $keyDescriptor = null,
307
        array $whereCondition = [],
308
        array $eagerLoad = null,
309
        $sourceEntityInstance = null
310
    ) {
311
        if (null == $resourceSet && null == $sourceEntityInstance) {
312
            $msg = 'Must supply at least one of a resource set and source entity.';
313
            throw new \Exception($msg);
314
        }
315
316
        $this->checkSourceInstance($sourceEntityInstance);
317
        $rawLoad = $this->processEagerLoadList($eagerLoad);
318
319
        if (null == $sourceEntityInstance) {
320
            assert(null != $resourceSet);
321
            $sourceEntityInstance = $this->getSourceEntityInstance($resourceSet);
322
        }
323
324
        $this->checkAuth($sourceEntityInstance);
325
        if ($sourceEntityInstance instanceof Model) {
326
            $modelLoad = $sourceEntityInstance->getEagerLoad();
327
        } elseif ($sourceEntityInstance instanceof Relation) {
328
            $modelLoad = $sourceEntityInstance->getRelated()->getEagerLoad();
329
        }
330
        assert(isset($modelLoad));
331
332
        $this->processKeyDescriptor($sourceEntityInstance, $keyDescriptor);
333
        foreach ($whereCondition as $fieldName => $fieldValue) {
334
            $sourceEntityInstance = $sourceEntityInstance->where($fieldName, $fieldValue);
335
        }
336
337
        $rawLoad = array_values(array_unique(array_merge($rawLoad, $modelLoad)));
0 ignored issues
show
Unused Code introduced by
$rawLoad is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
338
        $sourceEntityInstance = $sourceEntityInstance->get();
339
        $sourceCount = $sourceEntityInstance->count();
340
        if (0 == $sourceCount) {
341
            return null;
342
        }
343
        $result = $sourceEntityInstance->first();
344
        $result->PrimaryKey = $result->getKey();
345
346
        return $result;
347
    }
348
349
    /**
350
     * Get related resource for a resource
351
     * IE: http://host/EntitySet(1L)/NavigationPropertyToSingleEntity
352
     * http://host/EntitySet?$expand=NavigationPropertyToSingleEntity.
353
     *
354
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
355
     * @param Model            $sourceEntityInstance the source entity instance
356
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity pointed to by the nav property
357
     * @param ResourceProperty $targetProperty       The navigation property to fetch
358
     *
359
     * @return object|null The related resource if found else null
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use null|Model.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
360
     */
361
    public function getRelatedResourceReference(
362
        ResourceSet $sourceResourceSet,
0 ignored issues
show
Unused Code introduced by
The parameter $sourceResourceSet is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
363
        Model $sourceEntityInstance,
364
        ResourceSet $targetResourceSet,
365
        ResourceProperty $targetProperty
366
    ) {
367
        $this->checkAuth($sourceEntityInstance);
368
369
        $propertyName = $targetProperty->getName();
370
        $propertyName = $this->getLaravelRelationName($propertyName);
371
        $result = $sourceEntityInstance->$propertyName;
372
        if (null === $result) {
373
            return null;
374
        }
375
        assert($result instanceof Model, get_class($result));
376
        if($targetProperty->getResourceType()->getInstanceType()->getName() != get_class($result)){
377
            return null;
378
        }
379
        return $result;
380
    }
381
382
    /**
383
     * Gets a related entity instance from an entity set identified by a key
384
     * IE: http://host/EntitySet(1L)/NavigationPropertyToCollection(33).
385
     *
386
     * @param ResourceSet      $sourceResourceSet    The entity set containing the source entity
387
     * @param Model            $sourceEntityInstance the source entity instance
388
     * @param ResourceSet      $targetResourceSet    The entity set containing the entity to fetch
389
     * @param ResourceProperty $targetProperty       the metadata of the target property
390
     * @param KeyDescriptor    $keyDescriptor        The key identifying the entity to fetch
391
     *
392
     * @return Model|null Returns entity instance if found else null
393
     */
394
    public function getResourceFromRelatedResourceSet(
395
        ResourceSet $sourceResourceSet,
0 ignored issues
show
Unused Code introduced by
The parameter $sourceResourceSet is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
396
        Model $sourceEntityInstance,
397
        ResourceSet $targetResourceSet,
398
        ResourceProperty $targetProperty,
399
        KeyDescriptor $keyDescriptor
400
    ) {
401
        $propertyName = $targetProperty->getName();
402
        if (!method_exists($sourceEntityInstance, $propertyName)) {
403
            $msg = 'Relation method, ' . $propertyName . ', does not exist on supplied entity.';
404
            throw new InvalidArgumentException($msg);
405
        }
406
        // take key descriptor and turn it into where clause here, rather than in getResource call
407
        $sourceEntityInstance = $sourceEntityInstance->$propertyName();
408
        $this->processKeyDescriptor($sourceEntityInstance, $keyDescriptor);
409
        $result = $this->getResource(null, null, [], [], $sourceEntityInstance);
410
        assert(
411
            $result instanceof Model || null == $result,
412
            'GetResourceFromRelatedResourceSet must return an entity or null'
413
        );
414
        return $result;
415
    }
416
417
418
    /**
419
     * @param  ResourceSet $resourceSet
420
     * @return mixed
421
     */
422
    protected function getSourceEntityInstance(ResourceSet $resourceSet)
423
    {
424
        $entityClassName = $resourceSet->getResourceType()->getInstanceType()->name;
425
        return App::make($entityClassName);
426
    }
427
428
    /**
429
     * @param Model|Relation|null $source
430
     */
431
    protected function checkSourceInstance($source)
432
    {
433
        if (!(null == $source || $source instanceof Model || $source instanceof Relation)) {
434
            $msg = 'Source entity instance must be null, a model, or a relation.';
435
            throw new InvalidArgumentException($msg);
436
        }
437
    }
438
439
    protected function getAuth()
440
    {
441
        return $this->auth;
442
    }
443
444
    /**
445
     * @param $sourceEntityInstance
446
     * @param null|mixed $checkInstance
447
     *
448
     * @throws ODataException
449
     */
450
    private function checkAuth($sourceEntityInstance, $checkInstance = null)
451
    {
452
        $check = $checkInstance instanceof Model ? $checkInstance
453
            : $checkInstance instanceof Relation ? $checkInstance
454
                : $sourceEntityInstance instanceof Model ? $sourceEntityInstance
455
                    : $sourceEntityInstance instanceof Relation ? $sourceEntityInstance
456
                        : null;
457
        if (!$this->getAuth()->canAuth(ActionVerb::READ(), get_class($sourceEntityInstance), $check)) {
458
            throw new ODataException('Access denied', 403);
459
        }
460
    }
461
462
    /**
463
     * @param $sourceEntityInstance
464
     * @param  KeyDescriptor|null        $keyDescriptor
465
     * @throws InvalidOperationException
466
     */
467
    private function processKeyDescriptor(&$sourceEntityInstance, KeyDescriptor $keyDescriptor = null)
468
    {
469
        if ($keyDescriptor) {
470
            foreach ($keyDescriptor->getValidatedNamedValues() as $key => $value) {
471
                $key = (self::PK == $key) ? $sourceEntityInstance->getKeyName() : $key;
472
                $trimValue = trim($value[0], '\'');
473
                $sourceEntityInstance = $sourceEntityInstance->where($key, $trimValue);
474
            }
475
        }
476
    }
477
478
    /**
479
     * @param string[]|null $eagerLoad
480
     * @return array
481
     */
482
    private function processEagerLoadList(array $eagerLoad = null)
483
    {
484
        $load = (null === $eagerLoad) ? [] : $eagerLoad;
485
        $rawLoad = [];
486
        foreach ($load as $line) {
487
            assert(is_string($line), 'Eager-load elements must be non-empty strings');
488
            $lineParts = explode("/",$line);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal / does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
489
            $numberOfParts = count($lineParts);
490
            for($i = 0; $i<$numberOfParts;$i++){
491
                $lineParts[$i] = $this->getLaravelRelationName($lineParts[$i]);
492
            }
493
            $remixLine = implode(".",$lineParts);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal . does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
494
            $rawLoad[] = $remixLine;
495
        }
496
        return $rawLoad;
497
    }
498
499
    private function getLaravelRelationName($odataProperty){
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
500
        $laravelProperty = $odataProperty;
501
        $pos = strrpos($laravelProperty, "_");
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal _ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
502
        if($pos !== false){
503
            $laravelProperty = substr($laravelProperty,0,$pos);
504
        }
505
        return $laravelProperty;
506
    }
507
508
}
509