Completed
Pull Request — master (#23)
by Erin
03:28
created

Assertion::assert()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 2
Metric Value
c 3
b 0
f 2
dl 0
loc 18
rs 9.4285
cc 2
eloc 10
nc 2
nop 1
1
<?php
2
namespace Peridot\Leo;
3
4
use Peridot\Leo\Matcher\MatcherInterface;
5
use Peridot\Leo\Responder\ResponderInterface;
6
7
/**
8
 * Assertion is responsible for asserting it's actual value
9
 * against a MatcherInterface and responding to the results of a match.
10
 *
11
 * @method Assertion equal() equal(mixed $expected, string $message = "") Asserts that actual and expected values are strictly equal
12
 * @method Assertion with() with(array $args) Stores an array of values to be used with callable assertions
13
 * @method Assertion throw() throw(string $exceptionType, string $exceptionMessage = "", $message = "") Invokes the actual value as a function and matches the exception type was thrown, and optionally matches the exception message
14
 * @method Assertion a() a(string $expected, string $message = "") Asserts that the actual value given has the same type as the expected type string
15
 * @method Assertion an() an(string $expected, string $message = "") Asserts that the actual value given has the same type as the expected type string
16
 * @method Assertion include() include(mixed $expected, string $message = "") Asserts that the actual string or array includes the expected value
17
 * @method Assertion contain() contain(mixed $expected, string $message = "") Asserts that the actual string or array includes the expected value
18
 * @method Assertion ok() ok(string $message = "") Asserts that the actual value is truthy - that is true when cast to (bool)
19
 * @method Assertion true() true(string $message = "") Asserts that the actual value is strictly equal to true
20
 * @method Assertion false() false(string $message = "") Asserts that the actual value is strictly equal to false
21
 * @method Assertion null() null(string $message = "") Asserts that the actual value is null
22
 * @method Assertion empty() empty(string $message = "") Asserts that the actual value is empty
23
 * @method Assertion above() above(mixed $expected, string $message = "") Asserts that the actual value is greater than the expected value
24
 * @method Assertion least() least(mixed $expected, string $message = "") Asserts that the actual value is greater than or equal to the expected value
25
 * @method Assertion below() below(mixed $expected, string $message = "") Asserts that the actual value is less than the expected value
26
 * @method Assertion most() most(mixed $expected, string $message = "") Asserts that the actual value is less than or equal to the expected value
27
 * @method Assertion within() within(int $lowerBound, int $upperBound, string $message = "") Asserts that the actual value is between the upper and lower bounds (inclusive)
28
 * @method Assertion instanceof() instanceof(string $expected, string $message = "") Asserts that the actual value is an instance of the expected class string
29
 * @method Assertion property() property(string $name, mixed $value = "", string $message = "") Asserts that the actual array or object has the expected property and optionally asserts the property value against an expected value
30
 * @method Assertion length() length(mixed $expected, string $message = "") Asserts the actual array, string, or Countable has the expected length
31
 * @method Assertion match() match(string $pattern, string $message = "") Asserts that the actual value matches the expected regular expression
32
 * @method Assertion string() string(string $expected, string $message = "") Asserts that the actual string contains the expected substring
33
 * @method Assertion keys() keys(array $keys, string $message = "") Asserts the actual object or array has keys equivalent to the expected keys
34
 * @method Assertion satisfy() satisfy(callable $predicate, string $message = "") Asserts that the actual value satisfies the expected predicate. The expected predicate will be passed the actual value and should return true or false
35
 *
36
 * @property-read Assertion $to a language chain
37
 * @property-read Assertion $be a language chain
38
 * @property-read Assertion $been a language chain
39
 * @property-read Assertion $is a language chain
40
 * @property-read Assertion $and a language chain
41
 * @property-read Assertion $has a language chain
42
 * @property-read Assertion $have a language chain
43
 * @property-read Assertion $with a language chain
44
 * @property-read Assertion $that a language chain
45
 * @property-read Assertion $at a language chain
46
 * @property-read Assertion $of a language chain
47
 * @property-read Assertion $same a language chain
48
 * @property-read Assertion $an a language chain
49
 * @property-read Assertion $a a language chain
50
 * @property-read Assertion $not flags the Assertion as negated
51
 * @property-read Assertion $loosely enables loose equality assertion using the ->equal() assertion
52
 * @property-read Assertion $contain enables the contain flag for use with the ->keys() assertion
53
 * @property-read Assertion $include enables the contain flag for use with the ->keys() assertion
54
 * @property-read Assertion $ok a lazy property that performs an ->ok() assertion
55
 * @property-read Assertion $true a lazy property that performs a ->true() assertion
56
 * @property-read Assertion $false a lazy property that performs a ->false() assertion
57
 * @property-read Assertion $null a lazy property that performs a ->null() assertion
58
 * @property-read Assertion $empty a lazy property that performs an ->empty() assertion
59
 * @property-read Assertion $length enables the length flag for use with countable assertions such as ->above(), ->least(), ->below(), ->most(), and ->within()
60
 * @property-read Assertion $deep enables the deep flag for use with assertions that need to traverse structures like the ->property() assertion
61
 *
62
 * @package Peridot\Leo
63
 */
64
final class Assertion
65
{
66
    use DynamicObjectTrait;
67
68
    /**
69
     * A static cache for memoized properties.
70
     *
71
     * @var array
72
     */
73
    private static $propertyCache = [];
74
75
    /**
76
     * @var ResponderInterface
77
     */
78
    protected $responder;
79
80
    /**
81
     * @var mixed
82
     */
83
    protected $actual;
84
85
    /**
86
     * @param ResponderInterface $responder
87
     */
88
    public function __construct(ResponderInterface $responder, $actual = null)
89
    {
90
        $this->responder = $responder;
91
        $this->actual = $actual;
92
    }
93
94
    /**
95
     * Returns the current ResponderInterface assigned to this Assertion.
96
     *
97
     * @return ResponderInterface
98
     */
99
    public function getResponder()
100
    {
101
        return $this->responder;
102
    }
103
104
    /**
105
     * Delegate methods to assertion methods
106
     *
107
     * @param $method
108
     * @param $args
109
     * @return mixed
110
     */
111
    public function __call($method, $args)
112
    {
113
        if (!isset($this->methods[$method])) {
114
            throw new \BadMethodCallException("Method $method does not exist");
115
        }
116
117
        return $this->request($this->methods[$method], $args);
118
    }
119
120
    /**
121
     * @param $name
122
     * @return mixed
123
     */
124
    public function __get($name)
125
    {
126
        if (!isset($this->properties[$name])) {
127
            throw new \DomainException("Property $name not found");
128
        }
129
130
        if (array_key_exists($name, self::$propertyCache)) {
131
            return self::$propertyCache[$name];
132
        }
133
134
        $property = $this->properties[$name];
135
        $result = $this->request($property['factory']);
136
137
        if ($property['memoize']) {
138
            self::$propertyCache[$name] = $result;
139
        }
140
141
        return $result;
142
    }
143
144
    /**
145
     * A request to an Assertion will attempt to resolve
146
     * the result as an assertion before returning the result.
147
     *
148
     * @param callable $fn
149
     * @return mixed
150
     */
151
    public function request(callable $fn, array $arguments = [])
152
    {
153
        $result = call_user_func_array($fn, $arguments);
154
155
        if ($result instanceof MatcherInterface) {
156
            return $this->assert($result);
157
        }
158
159
        return $result;
160
    }
161
162
    /**
163
     * Extend calls the given callable - or file that returns a callable - and passes the current Assertion instance
164
     * to it. Assertion can be extended via the ->addMethod(), ->flag(), and ->addProperty()
165
     * methods.
166
     *
167
     * @param callable $fn
168
     */
169
    public function extend($fn)
170
    {
171
        if (is_string($fn) && file_exists($fn)) {
172
            $fn = include $fn;
173
        }
174
175
        if (is_callable($fn)) {
176
            return call_user_func($fn, $this);
177
        }
178
179
        throw new \InvalidArgumentException("Assertion::extend requires a callable or a file that returns one");
180
    }
181
182
    /**
183
     * Set the actual value used for matching expectations against.
184
     *
185
     * @param $actual
186
     * @return $this
187
     */
188
    public function setActual($actual)
189
    {
190
        $this->actual = $actual;
191
        return $this;
192
    }
193
194
    /**
195
     * Return the actual value being asserted against.
196
     *
197
     * @return mixed
198
     */
199
    public function getActual()
200
    {
201
        return $this->actual;
202
    }
203
204
    /**
205
     * Assert against the given matcher.
206
     *
207
     * @param $result
208
     * @return $this
209
     */
210
    public function assert(MatcherInterface $matcher)
211
    {
212
        if ($this->flag('not')) {
213
            $matcher->invert();
214
        }
215
216
        $match = $matcher
217
            ->setAssertion($this)
218
            ->match($this->getActual());
219
220
        $message = $this->flag('message');
221
222
        $this->clearFlags();
223
224
        $this->responder->respond($match, $matcher->getTemplate(), $message);
225
226
        return $this;
227
    }
228
}
229