Completed
Push — b0.3.0 ( 88e5b8...d9e524 )
by Sebastian
03:56
created

CustomRule::parseClosureArgs()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 6.2017

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 5
nop 1
dl 0
loc 21
ccs 7
cts 11
cp 0.6364
crap 6.2017
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Linna Filter
5
 *
6
 * @author Sebastian Rapetti <[email protected]>
7
 * @copyright (c) 2018, Sebastian Rapetti
8
 * @license http://opensource.org/licenses/MIT MIT License
9
 */
10
declare(strict_types=1);
11
12
namespace Linna\Filter\Rules;
13
14
use Closure;
15
use InvalidArgumentException;
16
use ReflectionFunction;
17
18
/**
19
 * Add custom rule to filter.
20
 */
21
class CustomRule implements RuleValidateInterface
22
{
23
    /**
24
     * @var array Rule properties
25
     */
26
    public $config = [
27
        'full_class' => __CLASS__,
28
        'alias' => [],
29
        'args_count' => 0,
30
        'args_type' => [],
31
    ];
32
33
    /**
34
     * @var callable Rule custom function for validate method.
35
     */
36
    private $callback;
37
38
    /**
39
     * @var string Error message
40
     */
41
    private $message = '';
42
43
    /**
44
     * Class Constructor.
45
     *
46
     * @param array    $alias Rule aliases
47
     * @param Closure  $test  Rule custom function for validation
48
     */
49 5
    public function __construct(array $alias, Closure $test)
50
    {
51 5
        $this->parseAlias($alias);
52 4
        $this->parseClosure($test);
53
54 1
        $this->message = "Value provided not pass CustomRule ({$alias[0]}) test";
55 1
    }
56
57
    /**
58
     * Parse alias
59
     *
60
     * @param array $alias
61
     *
62
     * @throws InvalidArgumentException if no alias provided for rule.
63
     */
64 5
    private function parseAlias(array $alias): void
65
    {
66 5
        if (count($alias) === 0) {
67 1
            throw new InvalidArgumentException('Rule test function must have at least one alias.');
68
        }
69
70 4
        $this->config['alias'] = array_map('strtolower', $alias);
71 4
    }
72
73
    /**
74
     * Parse test function for validate method.
75
     *
76
     * @param Closure $test
77
     *
78
     * @throws InvalidArgumentException if test function no dot have return type, if
79
     *                                  return type not bool or not void, if function do not
80
     *                                  have at least one parameter.
81
     */
82 4
    private function parseClosure(Closure $test): void
83
    {
84 4
        $reflection = new ReflectionFunction($test);
85
86 4
        if (!$reflection->hasReturnType()) {
87 1
            throw new InvalidArgumentException('Rule test function do not have return type.');
88
        }
89
90 3
        if (!in_array((string) $reflection->getReturnType(), ['bool', 'void'])) {
91 1
            throw new InvalidArgumentException('Rule test function return type must be bool or void.');
92
        }
93
94 2
        $this->parseClosureArgs($reflection);
95
96 1
        $this->callback = $test;
97 1
    }
98
99
    /**
100
     * Parse test function arguments.
101
     *
102
     * @param ReflectionFunction $reflection
103
     *
104
     * @throws InvalidArgumentException if test function do not have parameters.
105
     */
106 2
    private function parseClosureArgs(ReflectionFunction &$reflection): void
107
    {
108 2
        $parameters = $reflection->getParameters();
109
110 2
        if (count($parameters) === 0) {
111 1
            throw new InvalidArgumentException('Rule test function must have at least one argument.');
112
        }
113
114
        //remove firs param, the received value
115 1
        array_shift($parameters);
116
117 1
        $this->config['args_count'] = count($parameters);
118
119 1
        foreach ($parameters as $param) {
120
            if ($param->hasType()) {
121
                if (in_array((string) $param->getType(), ['int', 'float'])) {
122
                    $this->config['args_type'][] = 'number';
123
                }
124
            }
125
126
            $this->config['args_type'][] = 'string';
127
        }
128 1
    }
129
130
    /**
131
     * Validate.
132
     *
133
     * @return bool
134
     */
135 1
    public function validate(): bool
136
    {
137 1
        $args = func_get_args();
138
139 1
        return !call_user_func_array($this->callback, $args);
140
    }
141
142
    /**
143
     * Return error message.
144
     *
145
     * @return string Error message
146
     */
147 1
    public function getMessage(): string
148
    {
149 1
        return $this->message;
150
    }
151
}
152