Hook   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 269
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 48
dl 0
loc 269
rs 10
c 0
b 0
f 0
wmc 21

15 Methods

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