Completed
Push — master ( 632a5c...b470b8 )
by Evan
03:17
created

Hook::remove()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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