Test Setup Failed
Pull Request — master (#46)
by Alex
03:08
created

MetadataTrait::polyglotKeyMethodNames()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 9.4285
cc 3
eloc 6
nc 4
nop 1
crap 12
1
<?php
2
namespace AlgoWeb\PODataLaravel\Models;
3
4
use Illuminate\Database\Eloquent\Relations\BelongsTo;
5
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
6
use Illuminate\Database\Eloquent\Relations\MorphMany;
7
use Illuminate\Database\Eloquent\Relations\MorphOne;
8
use Illuminate\Database\Eloquent\Relations\MorphToMany;
9
use Illuminate\Support\Facades\App as App;
10
use Illuminate\Database\Eloquent\Relations\Relation;
11
use POData\Providers\Metadata\ResourceStreamInfo;
12
use POData\Providers\Metadata\Type\EdmPrimitiveType;
13
use Illuminate\Database\Eloquent\Model;
14
15
trait MetadataTrait
16
{
17
    /*
18
     * Array to record mapping between doctrine types and OData types
19
     */
20
    protected $mapping = [
21
        'integer' => EdmPrimitiveType::INT32,
22
        'string' => EdmPrimitiveType::STRING,
23
        'datetime' => EdmPrimitiveType::DATETIME,
24
        'float' => EdmPrimitiveType::SINGLE,
25
        'decimal' => EdmPrimitiveType::DECIMAL,
26
        'text' => EdmPrimitiveType::STRING,
27
        'boolean' => EdmPrimitiveType::BOOLEAN,
28 3
        'blob' => "stream"
29
    ];
30 3
31
    /*
32
     * Retrieve and assemble this model's metadata for OData packaging
33 2
     */
34 2
    public function metadata()
35
    {
36 2
        assert($this instanceof Model, get_class($this));
37
38 2
        // Break these out separately to enable separate reuse
39 1
        $connect = $this->getConnection();
40
        $builder = $connect->getSchemaBuilder();
41
42 1
        $table = $this->getTable();
43 1
44 1
        if (!$builder->hasTable($table)) {
45
            return [];
46 1
        }
47
48 1
        $columns = $builder->getColumnListing($table);
49 1
        $mask = $this->metadataMask();
50 1
        $columns = array_intersect($columns, $mask);
51
52 1
        $tableData = [];
53 1
54 1
        $rawFoo = $connect->getDoctrineSchemaManager()->listTableColumns($table);
55
        $foo = [];
56 1
        $getters = $this->collectGetters();
57
58 1
        foreach ($rawFoo as $key => $val) {
59 1
            // Work around glitch in Doctrine when reading from MariaDB which added ` characters to root key value
60 1
            $key = trim($key, '`');
61 1
            $foo[$key] = $val;
62 1
        }
63 1
64 1
        foreach ($columns as $column) {
65
            // Doctrine schema manager returns columns with lowercased names
66 1
            $rawColumn = $foo[strtolower($column)];
67
            $nullable = !($rawColumn->getNotNull());
68
            $fillable = in_array($column, $this->getFillable());
69
            $rawType = $rawColumn->getType();
70
            $type = $rawType->getName();
71
            $tableData[$column] = ['type' => $type, 'nullable' => $nullable, 'fillable' => $fillable];
72
        }
73 4
74
        foreach ($getters as $get) {
75 4
            $tableData[$get] = ['type' => 'text', 'nullable' => false, 'fillable' => false];
76
        }
77 4
78 4
        return $tableData;
79 4
    }
80 2
81 4
    /*
82 1
     * Return the set of fields that are permitted to be in metadata
83 1
     * - following same visible-trumps-hidden guideline as Laravel
84
     */
85 4
    public function metadataMask()
86
    {
87
        $attribs = array_keys($this->getAllAttributes());
88
89
        $visible = $this->getVisible();
90
        $hidden = $this->getHidden();
91 5
        if (0 < count($visible)) {
92
            assert(!empty($visible));
93 5
            $attribs = array_intersect($visible, $attribs);
94 5
        } elseif (0 < count($hidden)) {
95 1
            assert(!empty($hidden));
96
            $attribs = array_diff($attribs, $hidden);
97
        }
98 4
99
        return $attribs;
100 4
    }
101
102 4
    /*
103 4
     * Get the endpoint name being exposed
104 4
     *
105 4
     */
106 4
    public function getEndpointName()
107 4
    {
108
        $endpoint = isset($this->endpoint) ? $this->endpoint : null;
109 4
110 1
        if (!isset($endpoint)) {
111 1
            $bitter = get_class();
112 1
            $name = substr($bitter, strrpos($bitter, '\\')+1);
113 1
            return strtolower($name);
114
        }
115 4
        return strtolower($endpoint);
116 4
    }
117
118 4
    /*
119
     * Assemble this model's OData metadata as xml schema
120
     */
121
    public function getXmlSchema($MetaNamespace = "Data")
122
    {
123
        $raw = $this->metadata();
124
        if ([] == $raw) {
125
            return null;
126
        }
127
128
        $metadata = App::make('metadata');
129
130
        $rf = new \ReflectionClass(get_class($this));
131
        $complex = $metadata->addEntityType($rf, $rf->getShortName(), $MetaNamespace);
132
        $keyName = $this->getKeyName();
133
        if (null != $keyName) {
134
            $metadata->addKeyProperty($complex, $keyName, $this->mapping[$raw[$keyName]['type']]);
135
        }
136
137
        foreach ($raw as $key => $secret) {
138
            if ($key == $keyName) {
139
                continue;
140
            }
141
            if ($secret['type'] == "blob") {
142
                $complex->setMediaLinkEntry(true);
143
                $streamInfo = new ResourceStreamInfo($key);
144
                assert($complex->isMediaLinkEntry());
145
                $complex->addNamedStream($streamInfo);
146
                continue;
147
            }
148
            $metadata->addPrimitiveProperty($complex, $key, $this->mapping[$secret['type']]); // tag as isBag?
149
        }
150
151
        return $complex;
152
    }
153
154
    public function hookUpRelationships($entityTypes, $resourceSets)
155
    {
156
        assert(is_array($entityTypes) && is_array($resourceSets), "Both entityTypes and resourceSets must be arrays");
157
        $metadata = App::make('metadata');
158
        $rel = $this->getRelationshipsFromMethods();
159
        $thisClass = get_class($this);
160
        $thisInTypes = array_key_exists($thisClass, $entityTypes);
161
        $thisInSets = array_key_exists($thisClass, $resourceSets);
162
163
        if (!($thisInSets && $thisInTypes)) {
164
            return $rel;
165
        }
166
167
        $resourceType = $entityTypes[$thisClass];
168
        // if $r is in $combined keys, then its in keyspaces of both $entityTypes and $resourceSets
169
        $combinedKeys = array_intersect(array_keys($entityTypes), array_keys($resourceSets));
170 View Code Duplication
        foreach ($rel["HasOne"] as $n => $r) {
171
            $r = trim($r, '\\');
172
            if (in_array($r, $combinedKeys)) {
173
                $targResourceSet = $resourceSets[$r];
174
                $metadata->addResourceReferenceProperty($resourceType, $n, $targResourceSet);
175 3
            }
176
        }
177 3 View Code Duplication
        foreach ($rel["HasMany"] as $n => $r) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
178
            $r = trim($r, '\\');
179 3
            if (in_array($r, $combinedKeys)) {
180 3
                $targResourceSet = $resourceSets[$r];
181 3
                $metadata->addResourceSetReferenceProperty($resourceType, $n, $targResourceSet);
182 3
            }
183 3
        }
184 3
        return $rel;
185 3
    }
186 3
187 3
    public function getRelationships()
188 3
    {
189
        $hooks = [];
190 3
191
        $rels = $this->getRelationshipsFromMethods(true);
192 3
193 3
        $this->getRelationshipsUnknownPolyMorph($rels, $hooks);
194 3
195 3
        $this->getRelationshipsKnownPolyMorph($rels, $hooks);
196 3
197 3
        $this->getRelationshipsHasOne($rels, $hooks);
198 3
199 3
        $this->getRelationshipsHasMany($rels, $hooks);
200 3
201 3
        return $hooks;
202
    }
203 3
204 3
    protected function getAllAttributes()
205 3
    {
206 3
        // Adapted from http://stackoverflow.com/a/33514981
207 3
        // $columns = $this->getFillable();
208 3
        // Another option is to get all columns for the table like so:
209 3
        $builder = $this->getConnection()->getSchemaBuilder();
210 3
        $columns = $builder->getColumnListing($this->getTable());
211
        // but it's safer to just get the fillable fields
212 3
213 3
        $attributes = $this->getAttributes();
214 3
215
        foreach ($columns as $column) {
216 3
            if (!array_key_exists($column, $attributes)) {
217 3
                $attributes[$column] = null;
218 3
            }
219 3
        }
220 3
221
        $methods = $this->collectGetters();
222 1
223 3
        foreach ($methods as $method) {
224
            $attributes[$method] = null;
225 1
        }
226
227 1
        return $attributes;
228
    }
229 1
230
    protected function getRelationshipsFromMethods($biDir = false)
231 3
    {
232 2
        $model = $this;
233 2
        $relationships = array(
234 3
            "HasOne" => array(),
235 3
            "UnknownPolyMorphSide"=>array(),
236 3
            "HasMany"=>array(),
237 3
            "KnownPolyMorphSide"=>array()
238 3
        );
239 3
        $methods = get_class_methods($model);
240 3
        if (!empty($methods)) {
241
            foreach ($methods as $method) {
242
                if (!method_exists('Illuminate\Database\Eloquent\Model', $method)
243
                ) {
244
                    //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
245
                    $reflection = new \ReflectionMethod($model, $method);
246
247
                    $file = new \SplFileObject($reflection->getFileName());
248
                    $file->seek($reflection->getStartLine()-1);
249
                    $code = '';
250
                    while ($file->key() < $reflection->getEndLine()) {
251
                        $code .= $file->current();
252
                        $file->next();
253
                    }
254
255
                    $code = trim(preg_replace('/\s\s+/', '', $code));
256
                    assert(false !== stripos($code, 'function'), 'Function definition must have keyword \'function\'');
257
                    $begin = strpos($code, 'function(');
258
                    $code = substr($code, $begin, strrpos($code, '}')-$begin+1);
259
                    $lastCode = $code[strlen($code)-1];
260
                    assert("}" == $lastCode, "Final character of function definition must be closing brace");
261
                    foreach (array(
262
                                'hasMany',
263
                                'hasManyThrough',
264
                                'belongsToMany',
265
                                'hasOne',
266
                                'belongsTo',
267
                                'morphOne',
268
                                'morphTo',
269
                                'morphMany',
270
                                'morphToMany',
271
                                'morphedByMany'
272
                                ) as $relation) {
273
                        $search = '$this->'.$relation.'(';
274
                        if ($pos = stripos($code, $search)) {
275
                            //Resolve the relation's model to a Relation object.
276
                            $relationObj = $model->$method();
277
                            if ($relationObj instanceof Relation) {
278
                                $relatedModel = '\\'.get_class($relationObj->getRelated());
279
                                $relations = [
280
                                    'hasManyThrough',
281
                                    'belongsToMany',
282
                                    'hasMany',
283
                                    'morphMany',
284
                                    'morphToMany',
285
                                    'morphedByMany'
286
                                ];
287
                                if (in_array($relation, $relations)) {
288
                                    //Collection or array of models (because Collection is Arrayable)
289
                                    $relationships["HasMany"][$method] = $biDir ? $relationObj : $relatedModel;
290
                                } elseif ($relation === "morphTo") {
291
                                    // Model isn't specified because relation is polymorphic
292
                                    $relationships["UnknownPolyMorphSide"][$method] =
293
                                        $biDir ? $relationObj : '\Illuminate\Database\Eloquent\Model|\Eloquent';
294
                                } else {
295
                                    //Single model is returned
296
                                    $relationships["HasOne"][$method] = $biDir ? $relationObj : $relatedModel;
297
                                }
298
                                if (in_array($relation, ["morphMany", "morphOne", "morphedByMany"])) {
299
                                    $relationships["KnownPolyMorphSide"][$method] =
300
                                        $biDir ? $relationObj : $relatedModel;
301
                                }
302
                                if (in_array($relation, ["morphToMany"])) {
303
                                    $relationships["UnknownPolyMorphSide"][$method] =
304
                                        $biDir ? $relationObj : $relatedModel;
305
                                }
306
                            }
307
                        }
308
                    }
309
                }
310
            }
311
        }
312
        return $relationships;
313
    }
314
315
    /**
316
     * Get the visible attributes for the model.
317
     *
318
     * @return array
319
     */
320
    public abstract function getVisible();
321
322
    /**
323
     * Get the hidden attributes for the model.
324
     *
325
     * @return array
326
     */
327
    public abstract function getHidden();
328
329
    /**
330
     * Get the primary key for the model.
331
     *
332
     * @return string
333
     */
334
    public abstract function getKeyName();
335
336
    /**
337
     * Get the current connection name for the model.
338
     *
339
     * @return string
340
     */
341
    public abstract function getConnectionName();
342
343
    /**
344
     * Get the database connection for the model.
345
     *
346
     * @return \Illuminate\Database\Connection
347
     */
348
    public abstract function getConnection();
349
350
    /**
351
     * Get all of the current attributes on the model.
352
     *
353
     * @return array
354
     */
355
    public abstract function getAttributes();
356
357
    /**
358
     * Get the table associated with the model.
359
     *
360
     * @return string
361
     */
362
    public abstract function getTable();
363
364
    /**
365
     * Get the fillable attributes for the model.
366
     *
367
     * @return array
368
     */
369
    public abstract function getFillable();
370
371
    /**
372
     * Dig up all defined getters on the model
373
     *
374
     * @return array
375
     */
376
    protected function collectGetters()
377
    {
378
        $getterz = [];
379
        $methods = get_class_methods($this);
380
        foreach ($methods as $method) {
381
            if (starts_with($method, 'get') && ends_with($method, 'Attribute') && 'getAttribute' != $method) {
382
                $getterz[] = $method;
383
            }
384
        }
385
        $methods = [];
386
387
        foreach ($getterz as $getter) {
388
            $residual = substr($getter, 3);
389
            $residual = substr($residual, 0, -9);
390
            $methods[] = $residual;
391
        }
392
        return $methods;
393
    }
394
395
    /**
396
     * @param $foo
397
     * @return array
398
     */
399
    private function polyglotKeyMethodNames($foo)
400
    {
401
        $fkMethodName = method_exists($foo, 'getQualifiedForeignKeyName')
402
            ? 'getQualifiedForeignKeyName' : 'getQualifiedForeignPivotKeyName';
403
        $rkMethodName = method_exists($foo, 'getQualifiedRelatedKeyName')
404
            ? 'getQualifiedRelatedKeyName' : 'getQualifiedRelatedPivotKeyName';
405
        return array($fkMethodName, $rkMethodName);
406
    }
407
408
    /**
409
     * @param $hooks
410
     * @param $first
411
     * @param $property
412
     * @param $last
413
     * @param $mult
414
     * @param $targ
415
     */
416
    private function addRelationsHook(&$hooks, $first, $property, $last, $mult, $targ)
417
    {
418
        if (!isset($hooks[$first])) {
419
            $hooks[$first] = [];
420
        }
421
        $hooks[$first][$targ] = [
422
            'property' => $property,
423
            'local' => $last,
424
            'multiplicity' => $mult
425
        ];
426
    }
427
428
    /**
429
     * @param $rels
430
     * @param $hooks
431
     */
432
    private function getRelationshipsHasMany($rels, &$hooks)
433
    {
434
        foreach ($rels['HasMany'] as $property => $foo) {
435
            if ($foo instanceof MorphMany || $foo instanceof MorphToMany) {
436
                continue;
437
            }
438
            $isBelong = $foo instanceof BelongsToMany;
439
            $mult = '*';
440
            $targ = get_class($foo->getRelated());
441
442
            if ($isBelong) {
443
                list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo);
444
            }
445
446
            $keyRaw = $isBelong ? $foo->$fkMethodName() : $foo->getForeignKeyName();
447
            $keySegments = explode('.', $keyRaw);
448
            $keyName = $keySegments[count($keySegments) - 1];
449
            $localRaw = $isBelong ? $foo->$rkMethodName() : $foo->getQualifiedParentKeyName();
450
            $localSegments = explode('.', $localRaw);
451
            $localName = $localSegments[count($localSegments) - 1];
452
            $first = $keyName;
453
            $last = $localName;
454
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
455
        }
456
    }
457
458
    /**
459
     * @param $rels
460
     * @param $hooks
461
     */
462
    private function getRelationshipsHasOne($rels, &$hooks)
463
    {
464
        foreach ($rels['HasOne'] as $property => $foo) {
465
            if ($foo instanceof MorphOne) {
466
                continue;
467
            }
468
            $isBelong = $foo instanceof BelongsTo;
469
            $mult = $isBelong ? '1' : '0..1';
470
            $targ = get_class($foo->getRelated());
471
            $keyName = $isBelong ? $foo->getForeignKey() : $foo->getForeignKeyName();
472
            $localRaw = $isBelong ? $foo->getOwnerKey() : $foo->getQualifiedParentKeyName();
473
            $localSegments = explode('.', $localRaw);
474
            $localName = $localSegments[count($localSegments) - 1];
475
            $first = $isBelong ? $localName : $keyName;
476
            $last = $isBelong ? $keyName : $localName;
477
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
478
        }
479
    }
480
481
    /**
482
     * @param $rels
483
     * @param $hooks
484
     */
485
    private function getRelationshipsKnownPolyMorph($rels, &$hooks)
486
    {
487
        foreach ($rels['KnownPolyMorphSide'] as $property => $foo) {
488
            $isMany = $foo instanceof MorphToMany;
489
            $targ = get_class($foo->getRelated());
490
            $mult = $isMany ? '*' : $foo instanceof MorphMany ? '*' : '1';
491
            $mult = $foo instanceof MorphOne ? '0..1' : $mult;
492
493
            if ($isMany) {
494
                list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo);
495
            }
496
497
            $keyRaw = $isMany ? $foo->$fkMethodName() : $foo->getForeignKeyName();
0 ignored issues
show
Bug introduced by
The variable $fkMethodName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
498
            $keySegments = explode('.', $keyRaw);
499
            $keyName = $keySegments[count($keySegments) - 1];
500
            $localRaw = $isMany ? $foo->$rkMethodName() : $foo->getQualifiedParentKeyName();
0 ignored issues
show
Bug introduced by
The variable $rkMethodName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
501
            $localSegments = explode('.', $localRaw);
502
            $localName = $localSegments[count($localSegments) - 1];
503
            $first = $isMany ? $keyName : $localName;
504
            $last = $isMany ? $localName : $keyName;
505
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
506
        }
507
    }
508
509
    /**
510
     * @param $rels
511
     * @param $hooks
512
     */
513
    private function getRelationshipsUnknownPolyMorph($rels, &$hooks)
514
    {
515
        foreach ($rels['UnknownPolyMorphSide'] as $property => $foo) {
516
            $isMany = $foo instanceof MorphToMany;
517
            $targ = get_class($foo->getRelated());
518
            $mult = $isMany ? '*' : '1';
519
520
            if ($isMany) {
521
                list($fkMethodName, $rkMethodName) = $this->polyglotKeyMethodNames($foo);
522
            }
523
524
            $keyRaw = $isMany ? $foo->$fkMethodName() : $foo->getForeignKey();
0 ignored issues
show
Bug introduced by
The variable $fkMethodName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
525
            $keySegments = explode('.', $keyRaw);
526
            $keyName = $keySegments[count($keySegments) - 1];
527
            $localRaw = $isMany ? $foo->$rkMethodName() : $foo->getQualifiedParentKeyName();
0 ignored issues
show
Bug introduced by
The variable $rkMethodName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
528
            $localSegments = explode('.', $localRaw);
529
            $localName = $localSegments[count($localSegments) - 1];
530
531
            $first = $keyName;
532
            $last = $localName;
533
            $this->addRelationsHook($hooks, $first, $property, $last, $mult, $targ);
534
        }
535
    }
536
}
537