Complex classes like EE_Messages_Queue often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use EE_Messages_Queue, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
17 | class EE_Messages_Queue |
||
18 | { |
||
19 | |||
20 | |||
21 | /** |
||
22 | * @type string reference for sending action |
||
23 | */ |
||
24 | const action_sending = 'sending'; |
||
25 | |||
26 | /** |
||
27 | * @type string reference for generation action |
||
28 | */ |
||
29 | const action_generating = 'generation'; |
||
30 | |||
31 | |||
32 | /** |
||
33 | * @type EE_Message_Repository $_message_repository |
||
34 | */ |
||
35 | protected $_message_repository; |
||
36 | |||
37 | /** |
||
38 | * Sets the limit of how many messages are generated per process. |
||
39 | * |
||
40 | * @type int |
||
41 | */ |
||
42 | protected $_batch_count; |
||
43 | |||
44 | |||
45 | /** |
||
46 | * This is an array of cached queue items being stored in this object. |
||
47 | * The array keys will be the ID of the EE_Message in the db if saved. If the EE_Message |
||
48 | * is not saved to the db then its key will be an increment of "UNS" (i.e. UNS1, UNS2 etc.) |
||
49 | * |
||
50 | * @type EE_Message[] |
||
51 | */ |
||
52 | protected $_cached_queue_items; |
||
53 | |||
54 | /** |
||
55 | * Tracks the number of unsaved queue items. |
||
56 | * |
||
57 | * @type int |
||
58 | */ |
||
59 | protected $_unsaved_count = 0; |
||
60 | |||
61 | /** |
||
62 | * used to record if a do_messenger_hooks has already been called for a message type. This prevents multiple |
||
63 | * hooks getting fired if users have setup their action/filter hooks to prevent duplicate calls. |
||
64 | * |
||
65 | * @type array |
||
66 | */ |
||
67 | protected $_did_hook = array(); |
||
68 | |||
69 | |||
70 | /** |
||
71 | * Constructor. |
||
72 | * Setup all the initial properties and load a EE_Message_Repository. |
||
73 | * |
||
74 | * @param \EE_Message_Repository $message_repository |
||
75 | */ |
||
76 | public function __construct(EE_Message_Repository $message_repository) |
||
77 | { |
||
78 | $this->_batch_count = apply_filters('FHEE__EE_Messages_Queue___batch_count', 50); |
||
79 | $this->_message_repository = $message_repository; |
||
80 | } |
||
81 | |||
82 | |||
83 | /** |
||
84 | * Add a EE_Message object to the queue |
||
85 | * |
||
86 | * @param EE_Message $message |
||
87 | * @param array $data This will be an array of data to attach to the object in the repository. If the |
||
88 | * object is persisted, this data will be saved on an extra_meta object related to |
||
89 | * EE_Message. |
||
90 | * @param bool $preview Whether this EE_Message represents a preview or not. |
||
91 | * @param bool $test_send This indicates whether to do a test send instead of actual send. A test send will |
||
92 | * use the messenger send method but typically is based on preview data. |
||
93 | * @return bool Whether the message was successfully added to the repository or not. |
||
94 | */ |
||
95 | public function add(EE_Message $message, $data = array(), $preview = false, $test_send = false) |
||
96 | { |
||
97 | $data['preview'] = $preview; |
||
98 | $data['test_send'] = $test_send; |
||
99 | return $this->_message_repository->add($message, $data); |
||
100 | } |
||
101 | |||
102 | |||
103 | /** |
||
104 | * Removes EE_Message from _queue that matches the given EE_Message if the pointer is on a matching EE_Message |
||
105 | * |
||
106 | * @param EE_Message $message The message to detach from the queue |
||
107 | * @param bool $persist This flag indicates whether to attempt to delete the object from the db as well. |
||
108 | * @return bool |
||
109 | */ |
||
110 | public function remove(EE_Message $message, $persist = false) |
||
111 | { |
||
112 | if ($persist && $this->_message_repository->current() !== $message) { |
||
113 | //get pointer on right message |
||
114 | if ($this->_message_repository->has($message)) { |
||
115 | $this->_message_repository->rewind(); |
||
116 | while ($this->_message_repository->valid()) { |
||
117 | if ($this->_message_repository->current() === $message) { |
||
118 | break; |
||
119 | } |
||
120 | $this->_message_repository->next(); |
||
121 | } |
||
122 | } else { |
||
123 | return false; |
||
124 | } |
||
125 | } |
||
126 | return $persist ? $this->_message_repository->delete() : $this->_message_repository->remove($message); |
||
127 | } |
||
128 | |||
129 | |||
130 | /** |
||
131 | * Persists all queued EE_Message objects to the db. |
||
132 | * |
||
133 | * @param bool $do_hooks_only @see EE_Message_Repository::saveAll |
||
134 | * @return array @see EE_Messages_Repository::saveAll() for return values. |
||
135 | */ |
||
136 | public function save($do_hooks_only = false) |
||
137 | { |
||
138 | return $this->_message_repository->saveAll($do_hooks_only); |
||
139 | } |
||
140 | |||
141 | |||
142 | /** |
||
143 | * @return EE_Message_Repository |
||
144 | */ |
||
145 | public function get_message_repository() |
||
146 | { |
||
147 | return $this->_message_repository; |
||
148 | } |
||
149 | |||
150 | |||
151 | /** |
||
152 | * This does the following things: |
||
153 | * 1. Checks if there is a lock on generation (prevents race conditions). If there is a lock then exits (return |
||
154 | * false). |
||
155 | * 2. If no lock, sets lock, then retrieves a batch of non-generated EE_Message objects and adds to queue |
||
156 | * 3. Returns bool. True = batch ready. False = no batch ready (or nothing available for generation). |
||
157 | * Note: Callers should make sure they release the lock otherwise batch generation will be prevented from |
||
158 | * continuing. The lock is on a transient that is set to expire after one hour as a fallback in case locks are not |
||
159 | * removed. |
||
160 | * |
||
161 | * @return bool true if successfully retrieved batch, false no batch ready. |
||
162 | */ |
||
163 | public function get_batch_to_generate() |
||
164 | { |
||
165 | if ($this->is_locked(EE_Messages_Queue::action_generating)) { |
||
166 | return false; |
||
167 | } |
||
168 | |||
169 | //lock batch generation to prevent race conditions. |
||
170 | $this->lock_queue(EE_Messages_Queue::action_generating); |
||
171 | |||
172 | $query_args = array( |
||
173 | // key 0 = where conditions |
||
174 | 0 => array('STS_ID' => EEM_Message::status_incomplete), |
||
175 | 'order_by' => $this->_get_priority_orderby(), |
||
176 | 'limit' => $this->_batch_count, |
||
177 | ); |
||
178 | $messages = EEM_Message::instance()->get_all($query_args); |
||
179 | |||
180 | if ( ! $messages) { |
||
|
|||
181 | return false; //nothing to generate |
||
182 | } |
||
183 | |||
184 | foreach ($messages as $message) { |
||
185 | if ($message instanceof EE_Message) { |
||
186 | $data = $message->all_extra_meta_array(); |
||
187 | $this->add($message, $data); |
||
188 | } |
||
189 | } |
||
190 | return true; |
||
191 | } |
||
192 | |||
193 | |||
194 | /** |
||
195 | * This does the following things: |
||
196 | * 1. Checks if there is a lock on sending (prevents race conditions). If there is a lock then exits (return |
||
197 | * false). |
||
198 | * 2. Grabs the allowed number of messages to send for the rate_limit. If cannot send any more messages, then |
||
199 | * return false. |
||
200 | * 2. If no lock, sets lock, then retrieves a batch of EE_Message objects, adds to queue and triggers execution. |
||
201 | * 3. On success or unsuccessful send, sets status appropriately. |
||
202 | * 4. Saves messages via the queue |
||
203 | * 5. Releases lock. |
||
204 | * |
||
205 | * @return bool true on success, false if something preventing sending (i.e. lock set). Note: true does not |
||
206 | * necessarily mean that all messages were successfully sent. It just means that this method |
||
207 | * successfully completed. On true, client may want to call $this->count_STS_in_queue( |
||
208 | * EEM_Message::status_failed ) to see if any failed EE_Message objects. Each failed message object |
||
209 | * will also have a saved error message on it to assist with notifying user. |
||
210 | */ |
||
211 | public function get_to_send_batch_and_send() |
||
212 | { |
||
213 | $rate_limit = $this->get_rate_limit(); |
||
214 | if ($rate_limit < 1 || $this->is_locked(EE_Messages_Queue::action_sending)) { |
||
215 | return false; |
||
216 | } |
||
217 | |||
218 | $this->lock_queue(EE_Messages_Queue::action_sending); |
||
219 | |||
220 | $batch = $this->_batch_count < $rate_limit ? $this->_batch_count : $rate_limit; |
||
221 | |||
222 | $query_args = array( |
||
223 | // key 0 = where conditions |
||
224 | 0 => array('STS_ID' => array('IN', EEM_Message::instance()->stati_indicating_to_send())), |
||
225 | 'order_by' => $this->_get_priority_orderby(), |
||
226 | 'limit' => $batch, |
||
227 | ); |
||
228 | |||
229 | $messages_to_send = EEM_Message::instance()->get_all($query_args); |
||
230 | |||
231 | |||
232 | //any to send? |
||
233 | if ( ! $messages_to_send) { |
||
234 | $this->unlock_queue(EE_Messages_Queue::action_sending); |
||
235 | return false; |
||
236 | } |
||
237 | |||
238 | $queue_count = 0; |
||
239 | |||
240 | //add to queue. |
||
241 | foreach ($messages_to_send as $message) { |
||
242 | if ($message instanceof EE_Message) { |
||
243 | $queue_count++; |
||
244 | $this->add($message); |
||
245 | } |
||
246 | } |
||
247 | |||
248 | //send messages (this also updates the rate limit) |
||
249 | $this->execute(); |
||
250 | |||
251 | //release lock |
||
252 | $this->unlock_queue(EE_Messages_Queue::action_sending); |
||
253 | //update rate limit |
||
254 | $this->set_rate_limit($queue_count); |
||
255 | return true; |
||
256 | } |
||
257 | |||
258 | |||
259 | /** |
||
260 | * Locks the queue so that no other queues can call the "batch" methods. |
||
261 | * |
||
262 | * @param string $type The type of queue being locked. |
||
263 | */ |
||
264 | public function lock_queue($type = EE_Messages_Queue::action_generating) |
||
268 | |||
269 | |||
270 | /** |
||
271 | * Unlocks the queue so that batch methods can be used. |
||
272 | * |
||
273 | * @param string $type The type of queue being unlocked. |
||
274 | */ |
||
275 | public function unlock_queue($type = EE_Messages_Queue::action_generating) |
||
279 | |||
280 | |||
281 | /** |
||
282 | * Retrieve the key used for the lock transient. |
||
283 | * |
||
284 | * @param string $type The type of lock. |
||
285 | * @return string |
||
286 | */ |
||
287 | protected function _get_lock_key($type = EE_Messages_Queue::action_generating) |
||
291 | |||
292 | |||
293 | /** |
||
294 | * Retrieve the expiry time for the lock transient. |
||
295 | * |
||
296 | * @param string $type The type of lock |
||
297 | * @return int time to expiry in seconds. |
||
298 | */ |
||
299 | protected function _get_lock_expiry($type = EE_Messages_Queue::action_generating) |
||
303 | |||
304 | |||
305 | /** |
||
306 | * Returns the key used for rate limit transient. |
||
307 | * |
||
308 | * @return string |
||
309 | */ |
||
310 | protected function _get_rate_limit_key() |
||
314 | |||
315 | |||
316 | /** |
||
317 | * Returns the rate limit expiry time. |
||
318 | * |
||
319 | * @return int |
||
320 | */ |
||
321 | protected function _get_rate_limit_expiry() |
||
325 | |||
326 | |||
327 | /** |
||
328 | * Returns the default rate limit for sending messages. |
||
329 | * |
||
330 | * @return int |
||
331 | */ |
||
332 | protected function _default_rate_limit() |
||
336 | |||
337 | |||
338 | /** |
||
339 | * Return the orderby array for priority. |
||
340 | * |
||
341 | * @return array |
||
342 | */ |
||
343 | protected function _get_priority_orderby() |
||
350 | |||
351 | |||
352 | /** |
||
353 | * Returns whether batch methods are "locked" or not. |
||
354 | * |
||
355 | * @param string $type The type of lock being checked for. |
||
356 | * @return bool |
||
357 | */ |
||
358 | public function is_locked($type = EE_Messages_Queue::action_generating) |
||
387 | |||
388 | |||
389 | /** |
||
390 | * Retrieves the rate limit that may be cached as a transient. |
||
391 | * If the rate limit is not set, then this sets the default rate limit and expiry and returns it. |
||
392 | * |
||
393 | * @param bool $return_expiry If true then return the expiry time not the rate_limit. |
||
394 | * @return int |
||
395 | */ |
||
396 | protected function get_rate_limit($return_expiry = false) |
||
413 | |||
414 | |||
415 | /** |
||
416 | * This updates existing rate limit with the new limit which is the old minus the batch. |
||
417 | * |
||
418 | * @param int $batch_completed This sets the new rate limit based on the given batch that was completed. |
||
419 | */ |
||
420 | protected function set_rate_limit($batch_completed) |
||
430 | |||
431 | |||
432 | /** |
||
433 | * This method checks the queue for ANY EE_Message objects with a priority matching the given priority passed in. |
||
434 | * If that exists, then we immediately initiate a non-blocking request to do the requested action type. |
||
435 | * Note: Keep in mind that there is the possibility that the request will not execute if there is already another |
||
436 | * request running on a queue for the given task. |
||
437 | * |
||
438 | * @param string $task This indicates what type of request is going to be initiated. |
||
439 | * @param int $priority This indicates the priority that triggers initiating the request. |
||
440 | */ |
||
441 | public function initiate_request_by_priority($task = 'generate', $priority = EEM_Message::priority_high) |
||
476 | |||
477 | |||
478 | /** |
||
479 | * Loops through the EE_Message objects in the _queue and calls the messenger send methods for each message. |
||
480 | * |
||
481 | * @param bool $save Used to indicate whether to save the message queue after sending |
||
482 | * (default will save). |
||
483 | * @param mixed $sending_messenger (optional) When the sending messenger is different than |
||
484 | * what is on the EE_Message object in the queue. |
||
485 | * For instance, showing the browser view of an email message, |
||
486 | * or giving a pdf generated view of an html document. |
||
487 | * This should be an instance of EE_messenger but if you call this |
||
488 | * method |
||
489 | * intending it to be a sending messenger but a valid one could not be |
||
490 | * retrieved then send in an instance of EE_Error that contains the |
||
491 | * related error message. |
||
492 | * @param bool|int $by_priority When set, this indicates that only messages |
||
493 | * matching the given priority should be executed. |
||
494 | * @return int Number of messages sent. Note, 0 does not mean that no messages were processed. |
||
495 | * Also, if the messenger is an request type messenger (or a preview), |
||
496 | * its entirely possible that the messenger will exit before |
||
497 | */ |
||
498 | public function execute($save = true, $sending_messenger = null, $by_priority = false) |
||
561 | |||
562 | |||
563 | /** |
||
564 | * _process_message |
||
565 | * |
||
566 | * @param EE_Message $message |
||
567 | * @param mixed $sending_messenger (optional) |
||
568 | * @return bool |
||
569 | */ |
||
570 | protected function _process_message(EE_Message $message, $sending_messenger = null) |
||
598 | |||
599 | |||
600 | /** |
||
601 | * The intention of this method is to count how many EE_Message objects |
||
602 | * are in the queue with a given status. |
||
603 | * Example usage: |
||
604 | * After a caller calls the "EE_Message_Queue::execute()" method, the caller can check if there were any failed |
||
605 | * sends by calling $queue->count_STS_in_queue( EEM_Message_Queue::status_failed ). |
||
606 | * |
||
607 | * @param array|string $status Stati to check for in queue |
||
608 | * @return int Count of EE_Message's matching the given status. |
||
609 | */ |
||
610 | public function count_STS_in_queue($status) |
||
622 | |||
623 | |||
624 | /** |
||
625 | * Executes the get_preview method on the provided messenger. |
||
626 | * |
||
627 | * @param EE_Message $message |
||
628 | * @param EE_messenger $messenger |
||
629 | * @param EE_message_type $message_type |
||
630 | * @param $test_send |
||
631 | * @return bool true means all went well, false means, not so much. |
||
632 | */ |
||
633 | protected function _do_preview( |
||
650 | |||
651 | |||
652 | /** |
||
653 | * Executes the send method on the provided messenger |
||
654 | * EE_Messengers are expected to: |
||
655 | * - return true if the send was successful. |
||
656 | * - return false if the send was unsuccessful but can be tried again. |
||
657 | * - throw an Exception if the send was unsuccessful and cannot be tried again. |
||
658 | * |
||
659 | * @param EE_Message $message |
||
660 | * @param EE_messenger $messenger |
||
661 | * @param EE_message_type $message_type |
||
662 | * @return bool true means all went well, false means, not so much. |
||
663 | */ |
||
664 | protected function _do_send(EE_Message $message, EE_messenger $messenger, EE_message_type $message_type) |
||
680 | |||
681 | |||
682 | /** |
||
683 | * This sets any necessary error messages on the message object and its status to failed. |
||
684 | * |
||
685 | * @param EE_Message $message |
||
686 | * @param array $error_messages the response from the messenger. |
||
687 | */ |
||
688 | protected function _set_error_message(EE_Message $message, $error_messages) |
||
709 | |||
710 | } //end EE_Messages_Queue class |
||
711 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.