1 | <?php |
||
31 | class WordPoints_Hook_Router { |
||
32 | |||
33 | /** |
||
34 | * The hooks app object. |
||
35 | * |
||
36 | * @since 1.0.0 |
||
37 | * |
||
38 | * @var WordPoints_Hooks |
||
39 | */ |
||
40 | protected $hooks; |
||
41 | |||
42 | /** |
||
43 | * The actions registry object. |
||
44 | * |
||
45 | * @since 1.0.0 |
||
46 | * |
||
47 | * @var WordPoints_Hook_Actions |
||
48 | */ |
||
49 | protected $actions; |
||
50 | |||
51 | /** |
||
52 | * The events registry object. |
||
53 | * |
||
54 | * @since 1.0.0 |
||
55 | * |
||
56 | * @var WordPoints_Hook_Events |
||
57 | */ |
||
58 | protected $events; |
||
59 | |||
60 | /** |
||
61 | * The actions, indexed by WordPress action/filter hooks. |
||
62 | * |
||
63 | * The indexes are of this format: "$action_or_filter_name,$priority". |
||
64 | * |
||
65 | * @since 1.0.0 |
||
66 | * |
||
67 | * @var array |
||
68 | */ |
||
69 | protected $action_index = array(); |
||
70 | |||
71 | /** |
||
72 | * The events, indexed by action slug and action type. |
||
73 | * |
||
74 | * @since 1.0.0 |
||
75 | * |
||
76 | * @var array |
||
77 | */ |
||
78 | protected $event_index = array(); |
||
79 | |||
80 | /** |
||
81 | * @since 1.0.0 |
||
82 | */ |
||
83 | public function __call( $name, $args ) { |
||
84 | |||
85 | $this->route_action( $name, $args ); |
||
86 | |||
87 | // Return the first value, in case it is hooked to a filter. |
||
88 | $return = null; |
||
89 | if ( isset( $args[0] ) ) { |
||
90 | $return = $args[0]; |
||
91 | } |
||
92 | |||
93 | return $return; |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * Routes a WordPress action to WordPoints hook actions, and fires their events. |
||
98 | * |
||
99 | * @since 1.0.0 |
||
100 | * |
||
101 | * @param string $name The action ID. This is not the slug of a hook action, but |
||
102 | * rather a unique ID for the WordPress action based on the |
||
103 | * action name and the priority. |
||
104 | * @param array $args The args the action was fired with. |
||
105 | */ |
||
106 | protected function route_action( $name, $args ) { |
||
107 | |||
108 | if ( ! isset( $this->action_index[ $name ] ) ) { |
||
109 | return; |
||
110 | } |
||
111 | |||
112 | // We might normally do this in the constructor, however, the events |
||
113 | // registry attempts to access the router in its own constructor. The result |
||
114 | // of attempting to do this before the router itself has been fully |
||
115 | // constructed is that the events registry gets null instead of the router. |
||
116 | if ( ! isset( $this->hooks ) ) { |
||
117 | |||
118 | $hooks = wordpoints_hooks(); |
||
119 | |||
120 | $this->hooks = $hooks; |
||
121 | $this->events = $hooks->events; |
||
122 | $this->actions = $hooks->actions; |
||
123 | } |
||
124 | |||
125 | foreach ( $this->action_index[ $name ]['actions'] as $slug => $data ) { |
||
126 | |||
127 | if ( ! isset( $this->event_index[ $slug ] ) ) { |
||
128 | continue; |
||
129 | } |
||
130 | |||
131 | $action_object = $this->actions->get( $slug, $args, $data ); |
||
132 | |||
133 | if ( ! ( $action_object instanceof WordPoints_Hook_ActionI ) ) { |
||
134 | continue; |
||
135 | } |
||
136 | |||
137 | if ( ! $action_object->should_fire() ) { |
||
138 | continue; |
||
139 | } |
||
140 | |||
141 | foreach ( $this->event_index[ $slug ] as $type => $events ) { |
||
142 | foreach ( $events as $event_slug => $unused ) { |
||
143 | |||
144 | if ( ! $this->events->is_registered( $event_slug ) ) { |
||
145 | continue; |
||
146 | } |
||
147 | |||
148 | $event_args = $this->events->args->get_children( $event_slug, array( $action_object ) ); |
||
149 | |||
150 | if ( empty( $event_args ) ) { |
||
151 | continue; |
||
152 | } |
||
153 | |||
154 | $event_args = new WordPoints_Hook_Event_Args( $event_args ); |
||
155 | |||
156 | $this->hooks->fire( $type, $event_slug, $event_args ); |
||
157 | } |
||
158 | } |
||
159 | } |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * Register an action with the router. |
||
164 | * |
||
165 | * The arg number will be automatically determined based on $data['arg_index'] |
||
166 | * and $data['requirements']. So in most cases $arg_number may be omitted. |
||
167 | * |
||
168 | * @since 1.0.0 |
||
169 | * |
||
170 | * @param string $slug The slug of the action. |
||
171 | * @param array $args { |
||
172 | * Other arguments. |
||
173 | * |
||
174 | * @type string $action The name of the WordPress action for this hook action. |
||
175 | * @type int $priority The priority for the WordPress action. Default: 10. |
||
176 | * @type int $arg_number The number of args the action object expects. Default: 1. |
||
177 | * @type array $data { |
||
178 | * Args that will be passed to the action object's constructor. |
||
179 | * |
||
180 | * @type int[] $arg_index List of args (starting from 0), indexed by slug. |
||
181 | * @type array $requirements List of requirements, indexed by arg index (from 0). |
||
182 | * } |
||
183 | * } |
||
184 | */ |
||
185 | public function add_action( $slug, array $args ) { |
||
186 | |||
187 | $priority = 10; |
||
188 | if ( isset( $args['priority'] ) ) { |
||
189 | $priority = $args['priority']; |
||
190 | } |
||
191 | |||
192 | if ( ! isset( $args['action'] ) ) { |
||
193 | return; |
||
194 | } |
||
195 | |||
196 | $method = "{$args['action']},{$priority}"; |
||
197 | |||
198 | $this->action_index[ $method ]['actions'][ $slug ] = array(); |
||
199 | |||
200 | $arg_number = 1; |
||
201 | |||
202 | if ( isset( $args['data'] ) ) { |
||
203 | |||
204 | if ( isset( $args['data']['arg_index'] ) ) { |
||
205 | $arg_number = 1 + max( $args['data']['arg_index'] ); |
||
206 | } |
||
207 | |||
208 | if ( isset( $args['data']['requirements'] ) ) { |
||
209 | $requirements = 1 + max( array_keys( $args['data']['requirements'] ) ); |
||
210 | |||
211 | if ( $requirements > $arg_number ) { |
||
212 | $arg_number = $requirements; |
||
213 | } |
||
214 | } |
||
215 | |||
216 | $this->action_index[ $method ]['actions'][ $slug ] = $args['data']; |
||
217 | } |
||
218 | |||
219 | if ( isset( $args['arg_number'] ) ) { |
||
220 | $arg_number = $args['arg_number']; |
||
221 | } |
||
222 | |||
223 | // If this action is already being routed, and will have enough args, we |
||
224 | // don't need to hook to it again. |
||
225 | if ( |
||
226 | isset( $this->action_index[ $method ]['arg_number'] ) |
||
227 | && $this->action_index[ $method ]['arg_number'] >= $arg_number |
||
228 | ) { |
||
229 | return; |
||
230 | } |
||
231 | |||
232 | $this->action_index[ $method ]['arg_number'] = $arg_number; |
||
233 | |||
234 | add_action( $args['action'], array( $this, $method ), $priority, $arg_number ); |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * Deregister an action with the router. |
||
239 | * |
||
240 | * @since 1.0.0 |
||
241 | * |
||
242 | * @param string $slug The action slug. |
||
243 | */ |
||
244 | public function remove_action( $slug ) { |
||
245 | |||
246 | foreach ( $this->action_index as $method => $data ) { |
||
247 | if ( isset( $data['actions'][ $slug ] ) ) { |
||
248 | |||
249 | unset( $this->action_index[ $method ]['actions'][ $slug ] ); |
||
250 | |||
251 | if ( empty( $this->action_index[ $method ]['actions'] ) ) { |
||
252 | |||
253 | unset( $this->action_index[ $method ] ); |
||
254 | |||
255 | list( $action, $priority ) = explode( ',', $method ); |
||
256 | |||
257 | remove_action( $action, array( $this, $method ), $priority ); |
||
258 | } |
||
259 | } |
||
260 | } |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * Hook an event to an action. |
||
265 | * |
||
266 | * @since 1.0.0 |
||
267 | * |
||
268 | * @param string $event_slug The slug of the event. |
||
269 | * @param string $action_slug The slug of the action. |
||
270 | * @param string $action_type The type of action. Default is 'fire'. |
||
271 | */ |
||
272 | public function add_event_to_action( $event_slug, $action_slug, $action_type = 'fire' ) { |
||
275 | |||
276 | /** |
||
277 | * Unhook an event from an action. |
||
278 | * |
||
279 | * @since 1.0.0 |
||
280 | * |
||
281 | * @param string $event_slug The slug of the event. |
||
282 | * @param string $action_slug The slug of the action. |
||
283 | * @param string $action_type The type of action. Default is 'fire'. |
||
284 | */ |
||
285 | public function remove_event_from_action( $event_slug, $action_slug, $action_type = 'fire' ) { |
||
288 | |||
289 | /** |
||
290 | * Get the event index. |
||
291 | * |
||
292 | * @since 1.0.0 |
||
293 | * |
||
294 | * @return array[] The event index. |
||
295 | */ |
||
296 | public function get_event_index() { |
||
304 | } |
||
305 | |||
307 |