Completed
Push — develop ( 6dca76...ee82d0 )
by Evan
02:29
created

Hook::shouldInvoke()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Silk\Event;
4
5
use Silk\Support\Callback;
6
use Silk\Support\NegatedCallback;
7
use Illuminate\Support\Collection;
8
9
class Hook
10
{
11
    /**
12
     * The action or filter handle attached to.
13
     * @var string
14
     */
15
    protected $handle;
16
17
    /**
18
     * The callback object holding the target callable.
19
     * @var Callback
20
     */
21
    protected $callback;
22
23
    /**
24
     * The number of parameters defined in the callback's signature.
25
     * @var int
26
     */
27
    protected $callbackParamCount;
28
29
    /**
30
     * The action or filter priority the callback is registered on.
31
     * @var mixed
32
     */
33
    protected $priority;
34
35
    /**
36
     * The number of times the callback has been invoked.
37
     * @var int
38
     */
39
    protected $iterations;
40
    
41
    /**
42
     * The maximum number of iterations allowed for the callback to be invoked.
43
     * @var int
44
     */
45
    protected $maxIterations;
46
47
    /**
48
     * A collection of conditions which control the the invocation of the callback.
49
     * @var Collection
50
     */
51
    protected $conditions;
52
53
54
    /**
55
     * Create a new Hook instance.
56
     *
57
     * @param  string $handle   Action or filter handle
58
     * @param  int    $priority
59
     *
60
     * @return static
61
     */
62
    public static function on($handle, $priority = 10)
63
    {
64
        return new static($handle, $priority);
65
    }
66
67
    /**
68
     * Create a new Hook instance.
69
     *
70
     * @param  string $handle   Action or filter handle
71
     * @param  int    $priority
72
     */
73
    public function __construct($handle, $priority = 10)
74
    {
75
        $this->handle = $handle;
76
        $this->priority = $priority;
77
    }
78
79
    /**
80
     * Set the callback to be invoked by the action or filter.
81
     *
82
     * @param callable $callback    The callback to be invoked
83
     *
84
     * @return $this
85
     */
86
    public function setCallback(callable $callback)
87
    {
88
        $this->callback = new Callback($callback);
89
        $this->callbackParamCount = $this->callback->parameterCount();
90
91
        return $this;
92
    }
93
94
    /**
95
     * Set the hook in WordPress.
96
     *
97
     * Both actions and filters are registered as filters.
98
     *
99
     * @return $this
100
     */
101
    public function listen()
102
    {
103
        add_filter($this->handle, [$this, 'mediateCallback'], $this->priority, 100);
104
105
        return $this;
106
    }
107
108
    /**
109
     * Unset the hook in WordPress.
110
     *
111
     * @return $this
112
     */
113
    public function remove()
114
    {
115
        remove_filter($this->handle, [$this, 'mediateCallback'], $this->priority);
116
117
        return $this;
118
    }
119
120
    /**
121
     * Control invocation of the callback.
122
     *
123
     * @param mixed $given  The first argument passed to the callback.
124
     *                      Needed to return for filters.
125
     *
126
     * @return mixed        Returned value from Callback
127
     */
128
    public function mediateCallback($given = null)
129
    {
130
        $arguments = func_get_args();
131
132
        if (! $this->shouldInvoke($arguments)) {
133
            return $given;
134
        }
135
136
        if (is_null($returned = $this->invokeCallback($arguments))) {
137
            return $given;
138
        }
139
140
        return $returned;
141
    }
142
143
    /**
144
     * Whether or not the callback should be invoked.
145
     *
146
     * @param  array  $arguments  All arguments passed to the callback
147
     *
148
     * @return bool
149
     */
150
    public function shouldInvoke(array $arguments)
151
    {
152
        if ($this->hasExceededIterations()) {
153
            return false;
154
        }
155
156
        /**
157
         * Check if any of the conditions returns false,
158
         * if so, do not invoke.
159
         */
160
        return ! $this->conditions()->contains(function ($key, $callback) use ($arguments) {
161
            return false === $callback->callArray($arguments);
162
        });
163
    }
164
165
    /**
166
     * Call the callback.
167
     *
168
     * @param  array $arguments  All arguments passed to the callback
169
     *
170
     * @return mixed  The value returned from the callback
171
     */
172
    protected function invokeCallback($arguments)
173
    {
174
        $returned = $this->callback->callArray(
175
            array_slice($arguments, 0, $this->callbackParamCount ?: null)
176
        );
177
178
        $this->iterations++;
179
180
        return $returned;
181
    }
182
183
    /**
184
     * Set the callback to only be invokable one time.
185
     *
186
     * @return $this
187
     */
188
    public function once()
189
    {
190
        $this->onlyXtimes(1);
191
192
        return $this;
193
    }
194
195
    /**
196
     * Set the maximum number of callback invocations to allow.
197
     *
198
     * @param  int $times  The maximum iterations of invocations to allow
199
     *
200
     * @return $this
201
     */
202
    public function onlyXtimes($times)
203
    {
204
        $this->maxIterations = (int) $times;
205
206
        return $this;
207
    }
208
209
    /**
210
     * Prevent the callback from being triggered again.
211
     *
212
     * @return $this
213
     */
214
    public function bypass()
215
    {
216
        $this->onlyXtimes(0);
217
218
        return $this;
219
    }
220
221
    /**
222
     * Set the priority the callback should be registered with.
223
     *
224
     * @param  mixed $priority  The callback priority
225
     *
226
     * @return $this
227
     */
228
    public function withPriority($priority)
229
    {
230
        $this->remove();
231
232
        $this->priority = $priority;
233
234
        $this->listen();
235
236
        return $this;
237
    }
238
239
    /**
240
     * Add a condition to control the invocation of the callback.
241
     *
242
     * @param  callable $condition  A function to evaluate a condition before the
243
     *                              hook's callback is invoked.
244
     *                              If the function returns false, the callback
245
     *                              will not be invoked.
246
     *
247
     * @return $this
248
     */
249
    public function onlyIf(callable $condition)
250
    {
251
        $this->conditions()->push(new Callback($condition));
252
253
        return $this;
254
    }
255
256
    /**
257
     * Add a negated condition to control the invocation of the callback.
258
     *
259
     * @param callable $condition
260
     *
261
     * @return $this
262
     */
263
    public function onlyIfNot(callable $condition)
264
    {
265
        $this->conditions()->push(new NegatedCallback($condition));
266
267
        return $this;
268
    }
269
270
    /**
271
     * Get the collection of callback invocation conditions.
272
     *
273
     * @return Collection
274
     */
275
    protected function conditions()
276
    {
277
        if (is_null($this->conditions)) {
278
            $this->conditions = new Collection;
279
        }
280
281
        return $this->conditions;
282
    }
283
284
    /**
285
     * Whether or not the callback has reached the limit of allowed invocations.
286
     *
287
     * @return boolean  true for limit reached/exceeded, otherwise false
288
     */
289
    protected function hasExceededIterations()
290
    {
291
        return ($this->maxIterations > -1) && ($this->iterations >= $this->maxIterations);
292
    }
293
}
294