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:26
created

Validator::with()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 8
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 oneOf(Validatable $v1, Validatable $v_)
117
 * @method static Validator optional(Validatable $rule)
118
 * @method static Validator perfectSquare()
119
 * @method static Validator pesel()
120
 * @method static Validator phone()
121
 * @method static Validator phpLabel()
122
 * @method static Validator positive()
123
 * @method static Validator postalCode(string $countryCode)
124
 * @method static Validator primeNumber()
125
 * @method static Validator prnt(string $additionalChars = null)
126
 * @method static Validator punct(string $additionalChars = null)
127
 * @method static Validator readable()
128
 * @method static Validator regex(string $regex)
129
 * @method static Validator resourceType()
130
 * @method static Validator roman()
131
 * @method static Validator scalarVal()
132
 * @method static Validator sf(string $name, array $params = null)
133
 * @method static Validator size(string $minSize = null, string $maxSize = null)
134
 * @method static Validator slug()
135
 * @method static Validator space(string $additionalChars = null)
136
 * @method static Validator startsWith(mixed $startValue, bool $identical = false)
137
 * @method static Validator stringType()
138
 * @method static Validator stringVal()
139
 * @method static Validator subdivisionCode(string $countryCode)
140
 * @method static Validator symbolicLink()
141
 * @method static Validator tld()
142
 * @method static Validator trueVal()
143
 * @method static Validator type(string $type)
144
 * @method static Validator uploaded()
145
 * @method static Validator uppercase()
146
 * @method static Validator url()
147
 * @method static Validator vatin(string $countryCode)
148
 * @method static Validator version()
149
 * @method static Validator videoUrl(string $service = null)
150
 * @method static Validator vowel()
151
 * @method static Validator when(Validatable $if, Validatable $then, Validatable $when = null)
152
 * @method static Validator writable()
153
 * @method static Validator xdigit(string $additionalChars = null)
154
 * @method static Validator yes($useLocale = false)
155
 * @method static Validator zend(mixed $validator, array $params = null)
156
 */
157
class Validator extends AllOf
158
{
159
    protected static $factory;
160
161
    /**
162
     * @return Factory
163
     */
164
    protected static function getFactory()
165
    {
166
        if (!static::$factory instanceof Factory) {
167
            static::$factory = new Factory();
168
        }
169
170
        return static::$factory;
171
    }
172
173
    /**
174
     * @param Factory $factory
175
     */
176
    public static function setFactory($factory)
177
    {
178
        static::$factory = $factory;
179
    }
180
181
    /**
182
     * @param string $rulePrefix
183
     * @param bool   $prepend
184
     */
185
    public static function with($rulePrefix, $prepend = false)
186
    {
187
        if (false === $prepend) {
188
            self::getFactory()->appendRulePrefix($rulePrefix);
189
        } else {
190
            self::getFactory()->prependRulePrefix($rulePrefix);
191
        }
192
    }
193
194
    public function check($input)
195
    {
196
        try {
197
            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...
198
        } catch (ValidationException $exception) {
199
            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...
200
                $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...
201
            }
202
203
            throw $exception;
204
        }
205
    }
206
207
    /**
208
     * @param string $ruleName
209
     * @param array  $arguments
210
     *
211
     * @return Validator
212
     */
213
    public static function __callStatic($ruleName, $arguments)
214
    {
215
        if ('allOf' === $ruleName) {
216
            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...
217
        }
218
219
        $validator = new static();
220
221
        return $validator->__call($ruleName, $arguments);
222
    }
223
224
    /**
225
     * @param mixed $ruleSpec
226
     * @param array $arguments
227
     *
228
     * @return Validatable
229
     */
230
    public static function buildRule($ruleSpec, $arguments = [])
231
    {
232
        try {
233
            return static::getFactory()->rule($ruleSpec, $arguments);
234
        } catch (\Exception $exception) {
235
            throw new ComponentException($exception->getMessage(), $exception->getCode(), $exception);
236
        }
237
    }
238
239
    /**
240
     * @param string $method
241
     * @param array  $arguments
242
     *
243
     * @return self
244
     */
245
    public function __call($method, $arguments)
246
    {
247
        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...
248
    }
249
250
    protected function createException()
251
    {
252
        return new AllOfException();
253
    }
254
255
    /**
256
     * Create instance validator.
257
     *
258
     * @return Validator
259
     */
260
    public static function create()
261
    {
262
        $ref = new ReflectionClass(__CLASS__);
263
264
        return $ref->newInstanceArgs(func_get_args());
265
    }
266
}
267