Completed
Pull Request — master (#125)
by
unknown
02:25 queued 48s
created

getAvailableMethods()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 9.536
c 0
b 0
f 0
cc 2
nc 1
nop 1
1
<?php
2
3
namespace Facade\Ignition\SolutionProviders;
4
5
use BadMethodCallException;
6
use Facade\IgnitionContracts\BaseSolution;
7
use Facade\IgnitionContracts\HasSolutionsForThrowable;
8
use Illuminate\Support\Collection;
9
use Illuminate\Support\Str;
10
use Illuminate\Validation\Validator;
11
use ReflectionClass;
12
use ReflectionMethod;
13
use Throwable;
14
15
class UnknownValidationSolutionProvider implements HasSolutionsForThrowable
16
{
17
    protected const REGEX = '/([a-zA-Z\\\\]+)::([a-zA-Z]+)/m';
18
19
    public function canSolve(Throwable $throwable): bool
20
    {
21
        if (! $throwable instanceof BadMethodCallException) {
22
            return false;
23
        }
24
25
        if (is_null($this->getClassAndMethodFromExceptionMessage($throwable->getMessage()))) {
26
            return false;
27
        }
28
29
        return true;
30
    }
31
32
    public function getSolutions(Throwable $throwable): array
33
    {
34
        return [
35
            BaseSolution::create('Unknown Validation Rule')
36
                ->setSolutionDescription($this->getSolutionDescription($throwable)),
37
        ];
38
    }
39
40
    public function getSolutionDescription(Throwable $throwable): string
41
    {
42
        if (! $this->canSolve($throwable)) {
43
            return '';
44
        }
45
46
        extract($this->getClassAndMethodFromExceptionMessage($throwable->getMessage()), EXTR_OVERWRITE);
0 ignored issues
show
Bug introduced by
$this->getClassAndMethod...hrowable->getMessage()) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
47
48
        $possibleMethod = $this->findPossibleMethod($class, $method);
49
        $rule           = lcfirst(str_replace('validate', '', $possibleMethod));
50
51
        return "Did you mean `{$rule}` ?";
52
    }
53
54
    protected function getClassAndMethodFromExceptionMessage(string $message): ?array
55
    {
56
        if (! preg_match(self::REGEX, $message, $matches)) {
57
            return null;
58
        }
59
60
        if ($matches[1] !== Validator::class) {
61
            return null;
62
        }
63
64
        if (! Str::startsWith($matches[2], 'validate')) {
65
            return null;
66
        }
67
68
        return [
69
            'class'  => $matches[1],
70
            'method' => $matches[2],
71
        ];
72
    }
73
74
    protected function findPossibleMethod(string $class, string $invalidMethodName)
75
    {
76
        return $this->getAvailableMethods($class)
77
            ->sortByDesc(function (string $method) use ($invalidMethodName) {
78
                similar_text($invalidMethodName, $method, $percentage);
79
80
                return $percentage;
81
            })->first();
82
    }
83
84
    protected function getAvailableMethods($class): Collection
85
    {
86
        $class = new ReflectionClass($class);
87
88
        $extensions = Collection::make((\Illuminate\Support\Facades\Validator::make([], []))->extensions)
0 ignored issues
show
Bug introduced by
Accessing extensions on the interface Illuminate\Contracts\Validation\Validator suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
89
            ->keys()
90
            ->map(function (string $extension) {
91
                return 'validate' . ucfirst($extension);
92
            });
93
94
        return Collection::make($class->getMethods())
95
            ->filter(function (ReflectionMethod $method) {
96
                return Str::startsWith($method->name, 'validate') && ! in_array($method->name, [
97
                    'validate',
98
                    'validated',
99
                    'validateAttribute',
100
                    'validateUsingCustomRule'
101
                ]);
102
            })
103
            ->map(function (ReflectionMethod $method) {
104
                return $method->name;
105
            })
106
            ->merge($extensions);
107
    }
108
}
109