Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Completed
Pull Request — master (#678)
by Henrique
03:29
created

Validator::__callStatic()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 0
cts 8
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 2
crap 6
1
<?php
2
3
/*
4
 * This file is part of Respect/Validation.
5
 *
6
 * (c) Alexandre Gomes Gaigalas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the "LICENSE.md"
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Respect\Validation;
13
14
use finfo;
15
use ReflectionClass;
16
use Respect\Validation\Exceptions\AllOfException;
17
use Respect\Validation\Exceptions\ComponentException;
18
use Respect\Validation\Exceptions\ValidationException;
19
use Respect\Validation\Rules\AllOf;
20
use Respect\Validation\Rules\Key;
21
22
/**
23
 * @method static Validator age(int $minAge = null, int $maxAge = null)
24
 * @method static Validator allOf()
25
 * @method static Validator alnum(string $additionalChars = null)
26
 * @method static Validator alpha(string $additionalChars = null)
27
 * @method static Validator alwaysInvalid()
28
 * @method static Validator alwaysValid()
29
 * @method static Validator anyOf()
30
 * @method static Validator arrayType()
31
 * @method static Validator arrayVal()
32
 * @method static Validator attribute(string $attributeName, Rule $rule = null, bool $mandatory = true)
33
 * @method static Validator bank(string $countryCode)
34
 * @method static Validator bankAccount(string $countryCode)
35
 * @method static Validator base()
36
 * @method static Validator between(mixed $min = null, mixed $max = null, bool $inclusive = true)
37
 * @method static Validator bic(string $countryCode)
38
 * @method static Validator boolType()
39
 * @method static Validator boolVal()
40
 * @method static Validator bsn()
41
 * @method static Validator call(callable $callable, Rule $rule)
42
 * @method static Validator callableType()
43
 * @method static Validator callback(mixed $callback)
44
 * @method static Validator charset(mixed $charset)
45
 * @method static Validator cnh()
46
 * @method static Validator cnpj()
47
 * @method static Validator consonant(string $additionalChars = null)
48
 * @method static Validator contains(mixed $expectedValue, bool $identical = false)
49
 * @method static Validator countable()
50
 * @method static Validator countryCode()
51
 * @method static Validator currencyCode()
52
 * @method static Validator cpf()
53
 * @method static Validator creditCard(string $brand = null)
54
 * @method static Validator date(string $format = null)
55
 * @method static Validator digit(string $additionalChars = null)
56
 * @method static Validator directory()
57
 * @method static Validator domain(bool $tldCheck = true)
58
 * @method static Validator each(Validatable $itemValidator = null, Validatable $keyValidator = null)
59
 * @method static Validator email()
60
 * @method static Validator endsWith(mixed $endValue, bool $identical = false)
61
 * @method static Validator equals(mixed $compareTo)
62
 * @method static Validator even()
63
 * @method static Validator executable()
64
 * @method static Validator exists()
65
 * @method static Validator extension(string $extension)
66
 * @method static Validator factor(int $dividend)
67
 * @method static Validator falseVal()
68
 * @method static Validator fibonacci()
69
 * @method static Validator file()
70
 * @method static Validator filterVar(int $filter, mixed $options = null)
71
 * @method static Validator finite()
72
 * @method static Validator floatVal()
73
 * @method static Validator floatType()
74
 * @method static Validator graph(string $additionalChars = null)
75
 * @method static Validator hexRgbColor()
76
 * @method static Validator identical(mixed $value)
77
 * @method static Validator identityCard(string $countryCode)
78
 * @method static Validator image(finfo $fileInfo = null)
79
 * @method static Validator imei()
80
 * @method static Validator in(mixed $haystack, bool $compareIdentical = false)
81
 * @method static Validator infinite()
82
 * @method static Validator instance(string $instanceName)
83
 * @method static Validator intVal()
84
 * @method static Validator intType()
85
 * @method static Validator ip(mixed $ipOptions = null)
86
 * @method static Validator iterableType()
87
 * @method static Validator json()
88
 * @method static Validator key(mixed $key, Rule $rule = null, bool $mandatory = true)
89
 * @method static Validator keyNested(string $reference, Validatable $referenceValidator = null, bool $mandatory = true)
90
 * @method static Validator keySet(Key $rule...)
91
 * @method static Validator keyValue(string $comparedKey, string $ruleName, string $baseKey)
92
 * @method static Validator languageCode(string $set)
93
 * @method static Validator leapDate(string $format)
94
 * @method static Validator leapYear()
95
 * @method static Validator length(int $min = null, int $max = null, bool $inclusive = true)
96
 * @method static Validator lowercase()
97
 * @method static Validator macAddress()
98
 * @method static Validator max(mixed $maxValue, bool $inclusive = true)
99
 * @method static Validator mimetype(string $mimetype)
100
 * @method static Validator min(mixed $minValue, bool $inclusive = true)
101
 * @method static Validator minimumAge(int $age)
102
 * @method static Validator multiple(int $multipleOf)
103
 * @method static Validator negative()
104
 * @method static Validator nif()
105
 * @method static Validator no($useLocale = false)
106
 * @method static Validator noneOf()
107
 * @method static Validator not(Rule $rule)
108
 * @method static Validator notBlank()
109
 * @method static Validator notEmpty()
110
 * @method static Validator notOptional()
111
 * @method static Validator noWhitespace()
112
 * @method static Validator nullType()
113
 * @method static Validator numericVal()
114
 * @method static Validator objectType()
115
 * @method static Validator odd()
116
 * @method static Validator optional(Validatable $rule)
117
 * @method static Validator perfectSquare()
118
 * @method static Validator pesel()
119
 * @method static Validator phone()
120
 * @method static Validator phpLabel()
121
 * @method static Validator positive()
122
 * @method static Validator postalCode(string $countryCode)
123
 * @method static Validator primeNumber()
124
 * @method static Validator prnt(string $additionalChars = null)
125
 * @method static Validator punct(string $additionalChars = null)
126
 * @method static Validator readable()
127
 * @method static Validator regex(string $regex)
128
 * @method static Validator resourceType()
129
 * @method static Validator roman()
130
 * @method static Validator scalarVal()
131
 * @method static Validator sf(string $name, array $params = null)
132
 * @method static Validator size(string $minSize = null, string $maxSize = null)
133
 * @method static Validator slug()
134
 * @method static Validator space(string $additionalChars = null)
135
 * @method static Validator startsWith(mixed $startValue, bool $identical = false)
136
 * @method static Validator stringType()
137
 * @method static Validator stringVal()
138
 * @method static Validator subdivisionCode(string $countryCode)
139
 * @method static Validator symbolicLink()
140
 * @method static Validator tld()
141
 * @method static Validator trueVal()
142
 * @method static Validator type(string $type)
143
 * @method static Validator uploaded()
144
 * @method static Validator uppercase()
145
 * @method static Validator url()
146
 * @method static Validator vatin(string $countryCode)
147
 * @method static Validator version()
148
 * @method static Validator videoUrl(string $service = null)
149
 * @method static Validator vowel()
150
 * @method static Validator when(Validatable $if, Validatable $then, Validatable $when = null)
151
 * @method static Validator writable()
152
 * @method static Validator xdigit(string $additionalChars = null)
153
 * @method static Validator yes($useLocale = false)
154
 * @method static Validator zend(mixed $validator, array $params = null)
155
 */
156
class Validator extends AllOf
157
{
158
    protected static $factory;
159
160
    /**
161
     * @return Factory
162
     */
163
    protected static function getFactory()
164
    {
165
        if (!static::$factory instanceof Factory) {
166
            static::$factory = new Factory();
167
        }
168
169
        return static::$factory;
170
    }
171
172
    /**
173
     * @param Factory $factory
174
     */
175
    public static function setFactory($factory)
176
    {
177
        static::$factory = $factory;
178
    }
179
180
    /**
181
     * @param string $rulePrefix
182
     * @param bool   $prepend
183
     */
184
    public static function with($rulePrefix, $prepend = false)
185
    {
186
        if (false === $prepend) {
187
            self::getFactory()->appendRulePrefix($rulePrefix);
188
        } else {
189
            self::getFactory()->prependRulePrefix($rulePrefix);
190
        }
191
    }
192
193
    public function check($input)
194
    {
195
        try {
196
            return parent::check($input);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Respect\Validation\Rules\AllOf as the method check() does only exist in the following sub-classes of Respect\Validation\Rules\AllOf: Respect\Validation\Validator. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
197
        } catch (ValidationException $exception) {
198
            if (count($this->getRules()) == 1 && $this->template) {
0 ignored issues
show
Documentation Bug introduced by
The method getRules does not exist on object<Respect\Validation\Validator>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
199
                $exception->setTemplate($this->template);
0 ignored issues
show
Bug introduced by
The property template does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
200
            }
201
202
            throw $exception;
203
        }
204
    }
205
206
    /**
207
     * @param string $ruleName
208
     * @param array  $arguments
209
     *
210
     * @return Validator
211
     */
212
    public static function __callStatic($ruleName, $arguments)
213
    {
214
        if ('allOf' === $ruleName) {
215
            return static::buildRule($ruleName, $arguments);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return static::buildRule($ruleName, $arguments); (Respect\Validation\Validatable) is incompatible with the return type documented by Respect\Validation\Validator::__callStatic of type Respect\Validation\Validator.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
216
        }
217
218
        $validator = new static();
219
220
        return $validator->__call($ruleName, $arguments);
221
    }
222
223
    /**
224
     * @param mixed $ruleSpec
225
     * @param array $arguments
226
     *
227
     * @return Validatable
228
     */
229
    public static function buildRule($ruleSpec, $arguments = [])
230
    {
231
        try {
232
            return static::getFactory()->rule($ruleSpec, $arguments);
233
        } catch (\Exception $exception) {
234
            throw new ComponentException($exception->getMessage(), $exception->getCode(), $exception);
235
        }
236
    }
237
238
    /**
239
     * @param string $method
240
     * @param array  $arguments
241
     *
242
     * @return self
243
     */
244
    public function __call($method, $arguments)
245
    {
246
        return $this->addRule(static::buildRule($method, $arguments));
0 ignored issues
show
Documentation Bug introduced by
The method addRule does not exist on object<Respect\Validation\Validator>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
247
    }
248
249
    protected function createException()
250
    {
251
        return new AllOfException();
252
    }
253
254
    /**
255
     * Create instance validator.
256
     *
257
     * @return Validator
258
     */
259
    public static function create()
260
    {
261
        $ref = new ReflectionClass(__CLASS__);
262
263
        return $ref->newInstanceArgs(func_get_args());
264
    }
265
}
266