Completed
Pull Request — master (#264)
by Musa
01:48
created

UndefinedPropertySolutionProvider::getSolutions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Facade\Ignition\SolutionProviders;
4
5
use ErrorException;
6
use Facade\IgnitionContracts\BaseSolution;
7
use Facade\IgnitionContracts\HasSolutionsForThrowable;
8
use Illuminate\Support\Collection;
9
use ReflectionClass;
10
use ReflectionProperty;
11
use Throwable;
12
13
class UndefinedPropertySolutionProvider implements HasSolutionsForThrowable
14
{
15
    protected const REGEX = '/([a-zA-Z\\\\]+)::\$([a-zA-Z]+)/m';
16
    protected const MINIMUM_SIMILARITY = 80;
17
18
    public function canSolve(Throwable $throwable): bool
19
    {
20
        if (! $throwable instanceof ErrorException) {
0 ignored issues
show
Bug introduced by
The class ErrorException does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
21
            return false;
22
        }
23
24
        if (is_null($this->getClassAndPropertyFromExceptionMessage($throwable->getMessage()))) {
25
            return false;
26
        }
27
28
        if (!$this->similarPropertyExists($throwable)) {
29
            return false;
30
        }
31
32
        return true;
33
    }
34
35
    public function getSolutions(Throwable $throwable): array
36
    {
37
        return [
38
            BaseSolution::create('Unknown Property')
39
            ->setSolutionDescription($this->getSolutionDescription($throwable)),
40
        ];
41
    }
42
43
    public function getSolutionDescription(Throwable $throwable): string
44
    {
45
        if (! $this->canSolve($throwable) || !$this->similarPropertyExists($throwable)) {
46
            return '';
47
        }
48
49
        extract($this->getClassAndPropertyFromExceptionMessage($throwable->getMessage()), EXTR_OVERWRITE);
0 ignored issues
show
Bug introduced by
$this->getClassAndProper...hrowable->getMessage()) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
50
51
        $possibleProperty = $this->findPossibleProperty($class, $property);
52
53
        return "Did you mean {$class}::\${$possibleProperty->name} ?";
54
    }
55
56
    protected function similarPropertyExists(Throwable $throwable)
57
    {
58
        extract($this->getClassAndPropertyFromExceptionMessage($throwable->getMessage()), EXTR_OVERWRITE);
0 ignored issues
show
Bug introduced by
$this->getClassAndProper...hrowable->getMessage()) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
59
60
        $possibleProperty = $this->findPossibleProperty($class, $property);
61
62
        return $possibleProperty !== null;
63
    }
64
65
    protected function getClassAndPropertyFromExceptionMessage(string $message): ?array
66
    {
67
        if (! preg_match(self::REGEX, $message, $matches)) {
68
            return null;
69
        }
70
71
        return [
72
            'class' => $matches[1],
73
            'property' => $matches[2],
74
        ];
75
    }
76
77
    protected function findPossibleProperty(string $class, string $invalidPropertyName)
78
    {
79
        return $this->getAvailableProperties($class)
80
            ->sortByDesc(function (ReflectionProperty $property) use ($invalidPropertyName) {
81
                similar_text($invalidPropertyName, $property->name, $percentage);
82
83
                return $percentage;
84
            })
85
            ->filter(function (ReflectionProperty $property) use ($invalidPropertyName) {
86
                similar_text($invalidPropertyName, $property->name, $percentage);
87
88
                return $percentage >= self::MINIMUM_SIMILARITY;
89
            })->first();
90
    }
91
92
    protected function getAvailableProperties($class): Collection
93
    {
94
        $class = new ReflectionClass($class);
95
96
        return Collection::make($class->getProperties());
97
    }
98
}
99