1 | <?php |
||
2 | namespace Kahlan; |
||
3 | |||
4 | use Exception; |
||
5 | use Kahlan\Util\Text; |
||
6 | |||
7 | /** |
||
8 | * Class Arg |
||
9 | * |
||
10 | * @method static Arg toBe(mixed $expected) passes if actual === expected |
||
11 | * @method static Arg toEqual(mixed $expected) passes if actual == expected |
||
12 | * @method static Arg toBeTruthy() passes if actual is truthy |
||
13 | * @method static Arg toBeFalsy() passes if actual is falsy |
||
14 | * @method static Arg toBeEmpty() passes if actual is falsy |
||
15 | * @method static Arg toBeNull() passes if actual is null |
||
16 | * @method static Arg toBeA(string $expected) passes if actual is of the expected type |
||
17 | * @method static Arg toBeAn(string $expected) passes if actual is of the expected type (toBeA alias) |
||
18 | * @method static Arg toBeAnInstanceOf(string $expected) passes if actual is an instance of expected |
||
19 | * @method static Arg toHaveLength(int $expected) passes if actual has the expected length |
||
20 | * @method static Arg toContain(mixed $expected) passes if actual contain the expected value |
||
21 | * @method static Arg toContainKey(mixed $expected) passes if actual contain the expected key |
||
22 | * @method static Arg toContainKeys(mixed $expected) passes if actual contain the expected keys (toContainKey alias) |
||
23 | * @method static Arg toBeCloseTo(float $expected, int $precision) passes if actual is close to expected in some precision |
||
24 | * @method static Arg toBeGreaterThan(mixed $expected) passes if actual if greater than expected |
||
25 | * @method static Arg toBeLessThan(mixed $expected) passes if actual is less than expected |
||
26 | * @method static Arg toThrow(mixed $expected = null) passes if actual throws the expected exception |
||
27 | * @method static Arg toMatch(string $expected) passes if actual matches the expected regexp |
||
28 | * @method static Arg toEcho(string $expected) passes if actual echoes the expected string |
||
29 | * @method static Arg toMatchEcho(string $expected) passes if actual echoes matches the expected string |
||
30 | * @method static Arg toReceive(string $expected) passes if the expected method as been called on actual |
||
31 | * @method static Arg toReceiveNext(string $expected) passes if the expected method as been called on actual after some other method |
||
32 | */ |
||
33 | class Arg |
||
34 | { |
||
35 | /** |
||
36 | * Class dependencies. |
||
37 | * |
||
38 | * @var array |
||
39 | */ |
||
40 | protected static $_classes = [ |
||
41 | 'matcher' => Matcher::class |
||
42 | ]; |
||
43 | |||
44 | /** |
||
45 | * The matcher name. |
||
46 | * |
||
47 | * @var string |
||
48 | */ |
||
49 | protected $_name = ''; |
||
50 | |||
51 | /** |
||
52 | * The array of fully namespaced matcher classname. |
||
53 | * |
||
54 | * @var array |
||
55 | */ |
||
56 | protected $_matchers = []; |
||
57 | |||
58 | /** |
||
59 | * The expected arguments. |
||
60 | * |
||
61 | * @var array |
||
62 | */ |
||
63 | protected $_args = []; |
||
64 | |||
65 | /** |
||
66 | * If `true`, the result of the test will be inverted. |
||
67 | * |
||
68 | * @var boolean |
||
69 | */ |
||
70 | protected $_not = false; |
||
71 | |||
72 | /** |
||
73 | * Constructor |
||
74 | * |
||
75 | * @param array $config The argument matcher options. Possible values are: |
||
76 | * - `'not'` _boolean_: indicate if the matcher is a negative matcher. |
||
77 | * - `'matcher'` _string_ : the fully namespaced matcher class name. |
||
78 | * - `'args'` _string_ : the expected arcuments. |
||
79 | */ |
||
80 | public function __construct($config = []) |
||
81 | { |
||
82 | 14 | $defaults = ['name' => '', 'not' => false, 'matchers' => [], 'args' => []]; |
|
83 | 14 | $config += $defaults; |
|
84 | |||
85 | 14 | $this->_name = $config['name']; |
|
86 | 14 | $this->_not = $config['not']; |
|
87 | 14 | $this->_matchers = $config['matchers']; |
|
88 | 14 | $this->_args = $config['args']; |
|
89 | } |
||
90 | |||
91 | /** |
||
92 | * Create an Argument Matcher |
||
93 | * |
||
94 | * @param string $name The name of the matcher. |
||
95 | * @param array $args The arguments to pass to the matcher. |
||
96 | * @return boolean |
||
97 | */ |
||
98 | public static function __callStatic($name, $args) |
||
99 | { |
||
100 | 14 | $not = false; |
|
101 | if (preg_match('/^not/', $name)) { |
||
102 | 2 | $matcher = lcfirst(substr($name, 3)); |
|
103 | 2 | $not = true; |
|
104 | } else { |
||
105 | 14 | $matcher = $name; |
|
106 | } |
||
107 | 14 | $class = static::$_classes['matcher']; |
|
108 | if ($matchers = $class::get($matcher, true)) { |
||
109 | 14 | return new static(compact('name', 'matchers', 'not', 'args')); |
|
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||
110 | } |
||
111 | 2 | throw new Exception("Unexisting matchers attached to `'{$name}'`."); |
|
112 | } |
||
113 | |||
114 | /** |
||
115 | * Check if `$actual` matches the matcher. |
||
116 | * |
||
117 | * @param string $actual The actual value. |
||
118 | * @return boolean Returns `true` on success and `false` otherwise. |
||
119 | */ |
||
120 | public function match($actual) |
||
121 | { |
||
122 | 12 | $target = null; |
|
123 | 12 | $matcher = null; |
|
124 | foreach ($this->_matchers as $target => $value) { |
||
125 | if (!$target) { |
||
126 | 12 | $matcher = $value; |
|
127 | 12 | continue; |
|
128 | } |
||
129 | if ($actual instanceof $target) { |
||
130 | 2 | $matcher = $value; |
|
131 | } |
||
132 | } |
||
133 | if (!$matcher) { |
||
134 | 2 | throw new Exception("Unexisting matcher attached to `'{$this->_name}'` for `{$target}`."); |
|
135 | } |
||
136 | 12 | $args = $this->_args; |
|
137 | 12 | array_unshift($args, $actual); |
|
138 | 12 | $boolean = call_user_func_array($matcher . '::match', $args); |
|
139 | 12 | return $this->_not ? !$boolean : $boolean; |
|
140 | } |
||
141 | |||
142 | /** |
||
143 | * Returns the description of this argument matcher. |
||
144 | * |
||
145 | * @return string The description of this argument matcher. |
||
146 | */ |
||
147 | public function __toString() |
||
148 | { |
||
149 | return sprintf( |
||
150 | '%s(%s)', |
||
151 | $this->_name, |
||
152 | implode( |
||
153 | ', ', |
||
154 | array_map([\Kahlan\Arg::class, '_describeArg'], $this->_args) |
||
155 | ) |
||
156 | 2 | ); |
|
157 | } |
||
158 | |||
159 | /** |
||
160 | * Generate an inline string representation of an argument. |
||
161 | * |
||
162 | * @param mixed $arg The argument. |
||
163 | * @return string The dumped string. |
||
164 | */ |
||
165 | public static function _describeArg($arg) |
||
166 | { |
||
167 | if (is_array($arg)) { |
||
168 | 2 | return sprintf('array[%d]', count($arg)); |
|
169 | } |
||
170 | if (is_object($arg)) { |
||
171 | 2 | return sprintf('object[%s]', get_class($arg)); |
|
172 | } |
||
173 | |||
174 | 2 | return Text::toString($arg); |
|
175 | } |
||
176 | } |
||
177 |