Issues (300)

src/Arg.php (1 issue)

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
The expression return new static(compac...chers', 'not', 'args')) returns the type Kahlan\Arg which is incompatible with the documented return type boolean.
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