Passed
Pull Request — master (#221)
by Christopher
06:28 queued 01:03
created

ModelReflectionHelper::getCodeForMethod()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 16
rs 9.9
cc 2
nc 2
nop 1
1
<?php
2
3
4
namespace AlgoWeb\PODataLaravel\Models;
5
6
7
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Database\Eloquent\Relations\Relation;
10
use Mockery\Mock;
11
use POData\Common\InvalidOperationException;
12
13
abstract class ModelReflectionHelper
14
{
15
    protected static $relTypes = [
16
        'hasMany',
17
        'hasManyThrough',
18
        'belongsToMany',
19
        'hasOne',
20
        'belongsTo',
21
        'morphOne',
22
        'morphTo',
23
        'morphMany',
24
        'morphToMany',
25
        'morphedByMany'
26
    ];
27
28
    /**
29
     * @param  \ReflectionMethod         $method
30
     * @return string
31
     */
32
    public static function getCodeForMethod(\ReflectionMethod $method) : string
33
    {
34
        $fileName = $method->getFileName();
35
36
        $file = new \SplFileObject($fileName);
37
        $file->seek($method->getStartLine() - 1);
38
        $code = '';
39
        while ($file->key() < $method->getEndLine()) {
40
            $code .= $file->current();
41
            $file->next();
42
        }
43
44
        $code = trim(preg_replace('/\s\s+/', '', $code));
45
        $begin = strpos($code, 'function(');
46
        $code = substr($code, $begin, strrpos($code, '}') - $begin + 1);
47
        return $code;
48
    }
49
50
    /**
51
     * @param  Model $model
52
     * @return array
53
     */
54
    public static function getModelClassMethods(Model $model):array
55
    {
56
        // TODO: Handle case when Mock::class not present
57
        return array_diff(
58
            get_class_methods($model),
59
            get_class_methods(Model::class),
60
            get_class_methods(Mock::class),
61
            get_class_methods(MetadataTrait::class)
62
        );
63
    }
64
65
    /**
66
     * @param Model $model
67
     * @return array|string[]
68
     * @throws \ReflectionException
69
     */
70
    public static function getRelationshipsFromMethods(Model $model): array
71
    {
72
        $relationships = [];
73
        $methods = self::getModelClassMethods($model);
74
        foreach ($methods as $method) {
75
            //Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
76
            $reflection = new \ReflectionMethod($model, $method);
77
            $code = self::getCodeForMethod($reflection);
78
            foreach (static::$relTypes as $relation) {
79
                //Resolve the relation's model to a Relation object.
80
                if (
81
                    !stripos($code, sprintf('$this->%s(', $relation)) ||
82
                    !(($relationObj = $model->$method()) instanceof Relation) ||
83
                    !in_array(MetadataTrait::class, class_uses($relationObj->getRelated()))
84
                ) {
85
                    continue;
86
                }
87
                $relationships[]= $method;
88
            }
89
        }
90
        return $relationships;
91
    }
92
}
93