Completed
Branch BUG-9623-config-log (c144cd)
by
unknown
527:42 queued 509:06
created

EE_Messages_Generator   F

Complexity

Total Complexity 83

Size/Duplication

Total Lines 717
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 19

Importance

Changes 2
Bugs 1 Features 0
Metric Value
dl 0
loc 717
rs 1.263
c 2
b 1
f 0
wmc 83
lcom 1
cbo 19

16 Methods

Rating   Name   Duplication   Size   Complexity  
B generate() 0 63 6
B _generate() 0 41 5
A __construct() 0 13 1
A generation_queue() 0 3 1
A _reset_current_properties() 0 8 2
C _get_message_template_group() 0 75 12
A _get_templates() 0 13 4
B _assemble_messages() 0 33 6
F _setup_message_object() 0 78 15
A _verify() 0 12 3
B _valid_addressees() 0 14 6
D _validate_messenger_and_message_type() 0 32 9
B _validate_and_setup_data() 0 37 5
A _set_data_handler() 0 14 3
A _prepare_data_for_queue() 0 8 2
A create_and_add_message_to_queue() 0 21 3

How to fix   Complexity   

Complex Class

Complex classes like EE_Messages_Generator 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_Generator, and based on these observations, apply Extract Interface, too.

1
<?php if ( ! defined( 'EVENT_ESPRESSO_VERSION' )) { exit( 'No direct script access allowed' ); }
2
3
/**
4
 * This class is used for generating EE_Message objects with given info.
5
 *
6
 * @package    Event Espresso
7
 * @subpackage messages
8
 * @author     Darren Ethier
9
 * @since      4.9.0
10
 */
11
class EE_Messages_Generator {
12
13
14
	/**
15
	 * @type EE_Messages_Data_Handler_Collection
16
	 */
17
	protected $_data_handler_collection;
18
19
	/**
20
	 * @type  EE_Message_Template_Group_Collection
21
	 */
22
	protected $_template_collection;
23
24
	/**
25
	 * This will hold the data handler for the current EE_Message being generated.
26
	 * @type EE_Messages_incoming_data
27
	 */
28
	protected $_current_data_handler;
29
30
	/**
31
	 * This holds the EE_Messages_Queue that contains the messages to generate.
32
	 * @type EE_Messages_Queue
33
	 */
34
	protected $_generation_queue;
35
36
	/**
37
	 * This holds the EE_Messages_Queue that will store the generated EE_Message objects.
38
	 * @type EE_Messages_Queue
39
	 */
40
	protected $_ready_queue;
41
42
	/**
43
	 * This is a container for any error messages that get created through the generation
44
	 * process.
45
	 * @type array
46
	 */
47
	protected $_error_msg = array();
48
49
	/**
50
	 * Flag used to set when the current EE_Message in the generation queue has been verified.
51
	 * @type bool
52
	 */
53
	protected $_verified = false;
54
55
	/**
56
	 * This will hold the current messenger object corresponding with the current EE_Message in the generation queue.
57
	 *
58
	 * @type EE_messenger
59
	 */
60
	protected $_current_messenger;
61
62
	/**
63
	 * This will hold the current message type object corresponding with the current EE_Message in the generation queue.
64
	 * @type EE_message_type
65
	 */
66
	protected $_current_message_type;
67
68
	/**
69
	 * @type EEH_Parse_Shortcodes
70
	 */
71
	protected $_shortcode_parser;
72
73
74
75
	/**
76
	 * @param EE_Messages_Queue                     $generation_queue
77
	 * @param \EE_Messages_Queue                    $ready_queue
78
	 * @param \EE_Messages_Data_Handler_Collection  $data_handler_collection
79
	 * @param \EE_Message_Template_Group_Collection $template_collection
80
	 * @param \EEH_Parse_Shortcodes                 $shortcode_parser
81
	 */
82
	public function __construct(
83
		EE_Messages_Queue $generation_queue,
84
		EE_Messages_Queue $ready_queue,
85
		EE_Messages_Data_Handler_Collection $data_handler_collection,
86
		EE_Message_Template_Group_Collection $template_collection,
87
		EEH_Parse_Shortcodes $shortcode_parser
88
	) {
89
		$this->_generation_queue = $generation_queue;
90
		$this->_ready_queue = $ready_queue;
91
		$this->_data_handler_collection = $data_handler_collection;
92
		$this->_template_collection = $template_collection;
93
		$this->_shortcode_parser = $shortcode_parser;
94
	}
95
96
97
98
	/**
99
	 * @return EE_Messages_Queue
100
	 */
101
	public function generation_queue() {
102
		return $this->_generation_queue;
103
	}
104
105
106
107
108
109
	/**
110
	 *  This iterates through the provided queue and generates the EE_Message objects.
111
	 *  When iterating through the queue, the queued item that served as the base for generating other EE_Message objects
112
	 *  gets removed and the new EE_Message objects get added to a NEW queue.  The NEW queue is then returned for the
113
	 *  caller to decide what to do with it.
114
	 *
115
	 * @param   bool    $save   Whether to save the EE_Message objects in the new queue or just return.
116
	 *
117
	 * @return EE_Messages_Queue  The new queue for holding generated EE_Message objects.
118
	 */
119
	public function generate( $save = true ) {
120
		//iterate through the messages in the queue, generate, and add to new queue.
121
		$this->_generation_queue->get_message_repository()->rewind();
122
		while ( $this->_generation_queue->get_message_repository()->valid() ) {
123
			//reset "current" properties
124
			$this->_reset_current_properties();
125
126
			/** @type EE_Message $msg */
127
			$msg = $this->_generation_queue->get_message_repository()->current();
128
129
			if ( $this->_verify() ) {
130
				//let's get generating!
131
				$this->_generate();
132
			}
133
134
			/**
135
			 * need to get the next object and capture it for setting manually after deletes.  The reason is that when
136
			 * an object is removed from the repo then valid for the next object will fail.
137
			 */
138
			$this->_generation_queue->get_message_repository()->next();
139
			$next_msg = $this->_generation_queue->get_message_repository()->current();
140
			//restore pointer to current item
141
			$this->_generation_queue->get_message_repository()->set_current( $msg );
142
143
			//if there are error messages then let's set the status and the error message.
144
			if ( $this->_error_msg ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_error_msg of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
145
				//if the status is already debug only, then let's leave it at that.
146
				if ( $msg->STS_ID() !== EEM_Message::status_debug_only ) {
147
					$msg->set_STS_ID( EEM_Message::status_failed );
148
				}
149
				$msg->set_error_message(
150
					__( 'Message failed to generate for the following reasons: ' )
151
					. "\n"
152
					. implode( "\n", $this->_error_msg )
153
				);
154
				$msg->set_modified( time() );
155
			} else {
156
				//remove from db
157
				$this->_generation_queue->get_message_repository()->delete();
158
			}
159
			//next item
160
			$this->_generation_queue->get_message_repository()->set_current( $next_msg );
161
		}
162
163
		//generation queue is ALWAYS saved to record any errors in the generation process.
164
		$this->_generation_queue->save();
165
166
		/**
167
		 * save _ready_queue if flag set.
168
		 * Note: The EE_Message objects have values set via the EE_Base_Class::set_field_or_extra_meta() method.  This
169
		 * means if a field was added that is not a valid database column.  The EE_Message was already saved to the db
170
		 * so a EE_Extra_Meta entry could be created and attached to the EE_Message.  In those cases the save flag is
171
		 * irrelevant.
172
		 */
173
		if ( $save ) {
174
			$this->_ready_queue->save();
175
		}
176
177
		//final reset of properties
178
		$this->_reset_current_properties();
179
180
		return $this->_ready_queue;
181
	}
182
183
184
	/**
185
	 * This resets all the properties used for holding "current" values corresponding to the current EE_Message object
186
	 * in the generation queue.
187
	 */
188
	protected function _reset_current_properties() {
189
		$this->_verified = false;
190
		//make sure any _data value in the current message type is reset
191
		if ( $this->_current_message_type instanceof EE_message_type ) {
192
			$this->_current_message_type->reset_data();
193
		}
194
		$this->_current_messenger = $this->_current_message_type = $this->_current_data_handler = null;
195
	}
196
197
198
199
200
201
	/**
202
	 * This proceeds with the actual generation of a message.  By the time this is called, there should already be a
203
	 * $_current_data_handler set and all incoming information should be validated for the current EE_Message in the
204
	 * _generating_queue.
205
	 *
206
	 * @return bool Whether the message was successfully generated or not.
207
	 */
208
	protected function _generate() {
209
		//double check verification has run and that everything is ready to work with (saves us having to validate everything again).
210
		if ( ! $this->_verified ) {
211
			return false; //get out because we don't have a valid setup to work with.
212
		}
213
214
215
		try {
216
			$addressees = $this->_current_message_type->get_addressees(
217
				$this->_current_data_handler,
218
				$this->_generation_queue->get_message_repository()->current()->context()
219
			);
220
		} catch ( EE_Error $e ) {
221
			$this->_error_msg[] = $e->getMessage();
222
			return false;
223
		}
224
225
226
		//if no addressees then get out because there is nothing to generation (possible bad data).
227
		if ( ! $this->_valid_addressees( $addressees ) ) {
228
			$this->_generation_queue->get_message_repository()->current()->set_STS_ID( EEM_Message::status_debug_only );
229
			$this->_error_msg[] = __( 'This is not a critical error but an informational notice. Unable to generate messages EE_Messages_Addressee objects.  There were no attendees prepared by the data handler.
230
			  Sometimes this is because messages only get generated for certain registration statuses. For example, the ticket notice message type only goes to approved registrations.', 'event_espresso' );
231
			return false;
232
		}
233
234
		$message_template_group = $this->_get_message_template_group();
235
236
		//in the unlikely event there is no EE_Message_Template_Group available, get out!
237
		if ( ! $message_template_group instanceof EE_Message_Template_Group ) {
238
			$this->_error_msg[] = __( 'Unable to get the Message Templates for the Message being generated.  No message template group accessible.', 'event_espresso' );
239
			return false;
240
		}
241
242
		//get formatted templates for using to parse and setup EE_Message objects.
243
		$templates = $this->_get_templates( $message_template_group );
244
245
246
		//setup new EE_Message objects (and add to _ready_queue)
247
		return $this->_assemble_messages( $addressees, $templates, $message_template_group );
248
	}
249
250
251
252
253
254
	/**
255
	 * Retrieves the message template group being used for generating messages.
256
	 * Note: this also utilizes the EE_Message_Template_Group_Collection to avoid having to hit the db multiple times.
257
	 *
258
	 * @return EE_Message_Template_Group | null
259
	 */
260
	protected function _get_message_template_group() {
261
		//is there a GRP_ID already on the EE_Message object?  If there is, then a specific template has been requested
262
		//so let's use that.
263
		$GRP_ID = $this->_generation_queue->get_message_repository()->current()->GRP_ID();
264
265
		if ( $GRP_ID ) {
266
			//attempt to retrieve from repo first
267
			$GRP = $this->_template_collection->get_by_ID( $GRP_ID );
268
			if ( $GRP instanceof EE_Message_Template_Group ) {
269
				return $GRP;  //got it!
270
			}
271
272
			//nope don't have it yet.  Get from DB then add to repo
273
			$GRP = EEM_Message_Template_Group::instance()->get_one_by_ID( $GRP_ID );
274
			if ( $GRP instanceof EE_Message_Template_Group ) {
275
				$this->_template_collection->add( $GRP );
276
			}
277
			return $GRP;
278
		}
279
280
		//whatcha still doing here?  Oh, no Message Template Group yet I see.  Okay let's see if we can get it for you.
281
282
		//defaults
283
		$EVT_ID = 0;
284
285
		$template_qa = array(
286
			'MTP_is_active' => true,
287
			'MTP_messenger' => $this->_current_messenger->name,
288
			'MTP_message_type' => $this->_current_message_type->name,
289
		);
290
291
		//in vanilla EE we're assuming there's only one event.
292
		//However, if there are multiple events then we'll just use the default templates instead of different
293
		// templates per event (which could create problems).
294
		if ( count( $this->_current_data_handler->events ) === 1 ) {
295
			foreach ( $this->_current_data_handler->events as $event ) {
296
				$EVT_ID = $event['ID'];
297
			}
298
		}
299
300
		//before going any further, let's see if its in the queue
301
		$GRP = $this->_template_collection->get_by_key( $this->_template_collection->get_key( $this->_current_messenger->name, $this->_current_message_type->name, $EVT_ID ) );
302
303
		if ( $GRP instanceof EE_Message_Template_Group ) {
304
			return $GRP;
305
		}
306
307
		//nope still no GRP?
308
		//first we get the global template in case it has an override set.
309
		$global_template_qa = array_merge( array( 'MTP_is_global' => true ), $template_qa );
310
		$global_GRP = EEM_Message_Template_Group::instance()->get_one( array( $global_template_qa ) );
311
312
		//if this is an override, then we just return it.
313
		if ( $global_GRP instanceof EE_Message_Template_Group && $global_GRP->get( 'MTP_is_override' ) ) {
314
			$this->_template_collection->add( $global_GRP, $EVT_ID );
315
			return $global_GRP;
316
		}
317
318
		//STILL here? Okay that means we want to see if there is event specific group and if there is we return it,
319
		//otherwise we return the global group we retrieved.
320
		if ( $EVT_ID ) {
321
			$template_qa['Event.EVT_ID'] = $EVT_ID;
322
		}
323
324
		$GRP = EEM_Message_Template_Group::instance()->get_one( array( $template_qa ) );
325
		$GRP = $GRP instanceof EE_Message_Template_Group ? $GRP : $global_GRP;
326
327
		if ( $GRP instanceof EE_Message_Template_Group ) {
328
			$this->_template_collection->add( $GRP, $EVT_ID );
329
			return $GRP;
330
		}
331
332
		//nothing, nada, there ain't no group from what you fed the machine. (Getting here is a very hard thing to do).
333
		return null;
334
	}
335
336
337
338
339
340
	/**
341
	 *  Retrieves formatted array of template information for each context specific to the given  EE_Message_Template_Group
342
	 *
343
	 * @param   EE_Message_Template_Group
344
	 *
345
	 * @return  array   The returned array is in this structure:
346
	 *                  array(
347
	 *                      'field_name' => array(
348
	 *                          'context' => 'content'
349
	 *                      )
350
	 *                  )
351
	 */
352
	protected function _get_templates( EE_Message_Template_Group $message_template_group ) {
353
		$templates = array();
354
		$context_templates = $message_template_group->context_templates();
355
		foreach ( $context_templates as $context => $template_fields ) {
356
			foreach ( $template_fields as $template_field => $template_obj ) {
0 ignored issues
show
Bug introduced by
The expression $template_fields of type object<EE_Message_Template> is not traversable.
Loading history...
357
				if ( ! $template_obj instanceof EE_Message_Template ) {
358
					continue;
359
				}
360
				$templates[ $template_field ][ $context ] = $template_obj->get( 'MTP_content' );
361
			}
362
		}
363
		return $templates;
364
	}
365
366
367
368
369
370
371
	/**
372
	 * Assembles new fully generated EE_Message objects and adds to _ready_queue
373
	 *
374
	 * @param array $addressees  Array of EE_Messages_Addressee objects indexed by message type context.
375
	 * @param array $templates   formatted array of templates used for parsing data.
376
	 * @param EE_Message_Template_Group $message_template_group
377
	 * @return bool   true if message generation went a-ok.  false if some sort of exception occurred.  Note: The method will
378
	 *                attempt to generate ALL EE_Message objects and add to the _ready_queue.  Successfully generated messages
379
	 *                get added to the queue with EEM_Message::status_idle, unsuccessfully generated messages will get added
380
	 *                to the queue as EEM_Message::status_failed.  Very rarely should "false" be returned from this method.
381
	 */
382
	protected function _assemble_messages( $addressees, $templates, EE_Message_Template_Group $message_template_group ) {
383
384
		//if templates are empty then get out because we can't generate anything.
385
		if ( ! $templates ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $templates of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
386
			return false;
387
		}
388
389
		//We use this as the counter for generated messages because don't forget we may be executing this inside of a
390
		//generation_queue.  So _ready_queue may have generated EE_Message objects already.
391
		$generated_count = 0;
392
		foreach ( $addressees as $context => $recipients ) {
393
			foreach ( $recipients as $recipient ) {
394
				$message = $this->_setup_message_object( $context, $recipient, $templates, $message_template_group );
395
				if ( $message instanceof EE_Message ) {
396
					$this->_ready_queue->add(
397
						$message,
398
						array(),
399
						$this->_generation_queue->get_message_repository()->is_preview(),
400
						$this->_generation_queue->get_message_repository()->is_test_send()
401
					);
402
					$generated_count++;
403
				}
404
405
				//if the current MSG being generated is for a test send then we'll only use ONE message in the generation.
406
				if ( $this->_generation_queue->get_message_repository()->is_test_send() ) {
407
					break 2;
408
				}
409
			}
410
		}
411
412
		//if there are no generated messages then something else fatal went wrong.
413
		return $generated_count > 0;
414
	}
415
416
417
418
419
420
	/**
421
	 * @param string $context   The context for the generated message.
422
	 * @param EE_Messages_Addressee $recipient
423
	 * @param array  $templates  formatted array of templates used for parsing data.
424
	 * @param EE_Message_Template_Group $message_template_group
425
	 * @return EE_Message | bool  (false is used when no EE_Message is generated)
426
	 */
427
	protected function _setup_message_object(
428
		$context,
429
		EE_Messages_Addressee $recipient,
430
		$templates,
431
		EE_Message_Template_Group $message_template_group
432
	) {
433
		//stuff we already know
434
		$transaction_id = $recipient->txn instanceof EE_Transaction ? $recipient->txn->ID() : 0;
435
		$transaction_id = empty( $transaction_id ) && $this->_current_data_handler->txn instanceof EE_Transaction
436
			? $this->_current_data_handler->txn->ID()
437
			: $transaction_id;
438
		$message_fields = array(
439
			'GRP_ID'           => $message_template_group->ID(),
440
			'TXN_ID'           => $transaction_id,
441
			'MSG_messenger'    => $this->_current_messenger->name,
442
			'MSG_message_type' => $this->_current_message_type->name,
443
			'MSG_context'      => $context,
444
		);
445
446
		//recipient id and type should be on the EE_Messages_Addressee object but if this is empty, let's try to grab the
447
		//info from the att_obj found in the EE_Messages_Addressee object.
448
		if ( empty( $recipient->recipient_id ) || empty( $recipient->recipient_type ) ) {
449
			$message_fields['MSG_recipient_ID'] = $recipient->att_obj instanceof EE_Attendee
450
				? $recipient->att_obj->ID()
451
				: 0;
452
			$message_fields['MSG_recipient_type'] = 'Attendee';
453
		} else {
454
			$message_fields['MSG_recipient_ID'] = $recipient->recipient_id;
455
			$message_fields['MSG_recipient_type'] = $recipient->recipient_type;
456
		}
457
		$message = EE_Message_Factory::create( $message_fields );
458
459
		//grab valid shortcodes for shortcode parser
460
		$mt_shortcodes = $this->_current_message_type->get_valid_shortcodes();
461
		$m_shortcodes = $this->_current_messenger->get_valid_shortcodes();
462
463
		//if the 'to' field is empty (messages will ALWAYS have a "to" field, then we get out because that means this
464
		//context is turned off) EXCEPT if we're previewing
465
		if ( empty( $templates['to'][ $context ] )
466
		     && ! $this->_generation_queue->get_message_repository()->is_preview()
467
		     && ! $this->_current_messenger->allow_empty_to_field() ) {
468
			//we silently exit here and do NOT record a fail because the message is "turned off" by having no "to" field.
469
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by EE_Messages_Generator::_setup_message_object of type EE_Message.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
470
		}
471
		$error_msg = array();
472
		foreach ( $templates as $field => $field_context ) {
473
			$error_msg = array();
474
			//let's setup the valid shortcodes for the incoming context.
475
			$valid_shortcodes = $mt_shortcodes[ $context ];
476
			//merge in valid shortcodes for the field.
477
			$shortcodes = isset($m_shortcodes[ $field ]) ? $m_shortcodes[ $field ] : $valid_shortcodes;
478
			if ( isset( $templates[ $field ][ $context ] ) ) {
479
				//prefix field.
480
				$column_name = 'MSG_' . $field;
481
				try {
482
					$content = $this->_shortcode_parser->parse_message_template(
483
						$templates[ $field ][ $context ],
484
						$recipient,
485
						$shortcodes,
486
						$this->_current_message_type,
487
						$this->_current_messenger,
488
						$message );
489
					$message->set_field_or_extra_meta( $column_name, $content );
490
				} catch ( EE_Error $e ) {
491
					$error_msg[] = sprintf( __( 'There was a problem generating the content for the field %s: %s', 'event_espresso' ), $field, $e->getMessage() );
492
					$message->set_STS_ID( EEM_Message::status_failed );
493
				}
494
			}
495
		}
496
497
		if ( $message->STS_ID() === EEM_Message::status_failed ) {
498
			$error_msg = __( 'There were problems generating this message:', 'event_espresso' ) . "\n" . implode( "\n", $error_msg );
499
			$message->set_error_message( $error_msg );
500
		} else {
501
			$message->set_STS_ID( EEM_Message::status_idle );
502
		}
503
		return $message;
504
	}
505
506
507
508
	/**
509
	 * This verifies that the incoming array has a EE_messenger object and a EE_message_type object and sets appropriate
510
	 * error message if either is missing.
511
	 *
512
	 * @return bool         true means there were no errors, false means there were errors.
513
	 */
514
	protected function _verify() {
515
		//reset error message to an empty array.
516
		$this->_error_msg = array();
517
		$valid = true;
518
		$valid = $valid ? $this->_validate_messenger_and_message_type() : $valid;
519
		$valid = $valid ? $this->_validate_and_setup_data() : $valid;
520
521
		//set the verified flag so we know everything has been validated.
522
		$this->_verified = $valid;
523
524
		return $valid;
525
	}
526
527
528
	/**
529
	 * This accepts an array and validates that it is an array indexed by context with each value being an array of
530
	 * EE_Messages_Addressee objects.
531
	 *
532
	 * @param array $addressees  Keys correspond to contexts for the message type and values are EE_Messages_Addressee[]
533
	 * @return bool
534
	 */
535
	protected function _valid_addressees( $addressees ) {
536
		if ( ! $addressees || ! is_array( $addressees ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $addressees of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
537
			return false;
538
		}
539
540
		foreach( $addressees as $addressee_array ) {
541
			foreach ( $addressee_array as $addressee ) {
542
				if ( ! $addressee instanceof EE_Messages_Addressee ) {
543
					return false;
544
				}
545
			}
546
		}
547
		return true;
548
	}
549
550
551
552
553
554
	/**
555
	 * This validates the messenger, message type, and presences of generation data for the current EE_Message in the queue.
556
	 * This process sets error messages if something is wrong.
557
	 *
558
	 * @return bool   true is if there are no errors.  false is if there is.
559
	 */
560
	protected function _validate_messenger_and_message_type() {
561
562
		//first are there any existing error messages?  If so then return.
563
		if ( $this->_error_msg ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_error_msg of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
564
			return false;
565
		}
566
		/** @type EE_Message $message */
567
		$message = $this->_generation_queue->get_message_repository()->current();
568
		try {
569
			$this->_current_messenger = $message->valid_messenger( true ) ? $message->messenger_object() : null;
570
		} catch ( Exception $e ) {
571
			$this->_error_msg[] = $e->getMessage();
572
		}
573
		try {
574
			$this->_current_message_type = $message->valid_message_type( true ) ? $message->message_type_object() : null;
575
		} catch ( Exception $e ) {
576
			$this->_error_msg[] = $e->getMessage();
577
		}
578
579
		/**
580
		 * Check if there is any generation data, but only if this is not for a preview.
581
		 */
582
		if ( ! $this->_generation_queue->get_message_repository()->get_generation_data()
583
		     && (
584
			     ! $this->_generation_queue->get_message_repository()->is_preview()
585
			     && $this->_generation_queue->get_message_repository()->get_data_handler() !== 'EE_Messages_Preview_incoming_data' )
586
		) {
587
			$this->_error_msg[] = __( 'There is no generation data for this message. Unable to generate.' );
588
		}
589
590
		return empty( $this->_error_msg );
591
	}
592
593
594
595
596
597
	/**
598
	 * This method retrieves the expected data handler for the message type and validates the generation data for that
599
	 * data handler.
600
	 *
601
	 * @return bool true means there are no errors.  false means there were errors (and handler did not get setup).
602
	 */
603
	protected function _validate_and_setup_data() {
604
605
		//First, are there any existing error messages?  If so, return because if there were errors elsewhere this can't
606
		//be used anyways.
607
		if ( $this->_error_msg ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_error_msg of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
608
			return false;
609
		}
610
611
		$generation_data = $this->_generation_queue->get_message_repository()->get_generation_data();
612
613
		/** @type EE_Messages_incoming_data $data_handler_class_name - well not really... just the class name actually */
614
		$data_handler_class_name = $this->_generation_queue->get_message_repository()->get_data_handler()
615
			? $this->_generation_queue->get_message_repository()->get_data_handler()
616
			: 'EE_Messages_' .  $this->_current_message_type->get_data_handler( $generation_data ) . '_incoming_data';
617
618
		//If this EE_Message is for a preview, then let's switch out to the preview data handler.
619
		if ( $this->_generation_queue->get_message_repository()->is_preview() ) {
620
			$data_handler_class_name  = 'EE_Messages_Preview_incoming_data';
621
		}
622
623
		//First get the class name for the data handler (and also verifies it exists.
624
		if ( ! class_exists( $data_handler_class_name ) ) {
625
			$this->_error_msg[] = sprintf(
626
				__( 'The included data handler class name does not match any valid, accessible, "EE_Messages_incoming_data" classes.  Looking for %s.', 'event_espresso' ),
627
				$data_handler_class_name
628
			);
629
			return false;
630
		}
631
632
		//convert generation_data for data_handler_instantiation.
633
		$generation_data = $data_handler_class_name::convert_data_from_persistent_storage( $generation_data );
634
635
		//note, this may set error messages as well.
636
		$this->_set_data_handler( $generation_data, $data_handler_class_name );
0 ignored issues
show
Bug introduced by
It seems like $data_handler_class_name can also be of type object<EE_Messages_incoming_data>; however, EE_Messages_Generator::_set_data_handler() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
637
638
		return empty( $this->_error_msg );
639
	}
640
641
642
643
644
645
	/**
646
	 * Sets the $_current_data_handler property that is used for generating the current EE_Message in the queue, and
647
	 * adds it to the _data repository.
648
	 *
649
	 * @param mixed     $generating_data        This is data expected by the instantiated data handler.
650
	 * @param string    $data_handler_class_name This is the reference string indicating what data handler is being
651
	 *                                          instantiated.
652
	 *
653
	 * @return void.
0 ignored issues
show
Documentation introduced by
The doc-type void. could not be parsed: Unknown type name "void." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
654
	 */
655
	protected function _set_data_handler( $generating_data, $data_handler_class_name ) {
656
		//valid classname for the data handler.  Now let's setup the key for the data handler repository to see if there
657
		//is already a ready data handler in the repository.
658
		$this->_current_data_handler = $this->_data_handler_collection->get_by_key( $this->_data_handler_collection->get_key( $data_handler_class_name, $generating_data ) );
659
		if ( ! $this->_current_data_handler instanceof EE_messages_incoming_data ) {
660
			//no saved data_handler in the repo so let's set one up and add it to the repo.
661
			try {
662
				$this->_current_data_handler = new $data_handler_class_name( $generating_data );
663
				$this->_data_handler_collection->add( $this->_current_data_handler, $generating_data );
664
			} catch( EE_Error $e ) {
665
				$this->_error_msg[] = $e->get_error();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $this->_error_msg[] is correct as $e->get_error() (which targets EE_Error::get_error()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
666
			}
667
		}
668
	}
669
670
671
672
673
674
	/**
675
	 * The queued EE_Message for generation does not save the data used for generation as objects
676
	 * because serialization of those objects could be problematic if the data is saved to the db.
677
	 * So this method calls the static method on the associated data_handler for the given message_type
678
	 * and that preps the data for later instantiation when generating.
679
	 *
680
	 * @param EE_Message_To_Generate $message_to_generate
681
	 * @param bool                   $preview Indicate whether this is being used for a preview or not.
682
	 * @return mixed Prepped data for persisting to the queue.  false is returned if unable to prep data.
683
	 */
684
	protected function _prepare_data_for_queue( EE_Message_To_Generate $message_to_generate, $preview ) {
685
		/** @type EE_Messages_incoming_data $data_handler - well not really... just the class name actually */
686
		$data_handler = $message_to_generate->get_data_handler_class_name( $preview );
687
		if ( ! $message_to_generate->valid() ) {
688
			return false; //unable to get the data because the info in the EE_Message_To_Generate class is invalid.
689
		}
690
		return $data_handler::convert_data_for_persistent_storage( $message_to_generate->data() );
691
	}
692
693
694
695
696
697
	/**
698
	 * This sets up a EEM_Message::status_incomplete EE_Message object and adds it to the generation queue.
699
	 *
700
	 * @param EE_Message_To_Generate $message_to_generate
701
	 * @param bool                   $test_send Whether this is just a test send or not.  Typically used for previews.
702
	 */
703
	public function create_and_add_message_to_queue( EE_Message_To_Generate $message_to_generate, $test_send = false ) {
704
		//prep data
705
		$data = $this->_prepare_data_for_queue( $message_to_generate, $message_to_generate->preview() );
706
707
		$message = $message_to_generate->get_EE_Message();
708
709
		//is there a GRP_ID in the request?
710
		if ( $GRP_ID = EE_Registry::instance()->REQ->get( 'GRP_ID' ) ) {
711
			$message->set_GRP_ID( $GRP_ID );
712
		}
713
714
		if ( $data === false ) {
715
			$message->set_STS_ID( EEM_Message::status_failed );
716
			$message->set_error_message( __( 'Unable to prepare data for persistence to the database.', 'event_espresso' ) );
717
		} else {
718
			//make sure that the data handler is cached on the message as well
719
			$data['data_handler_class_name'] = $message_to_generate->get_data_handler_class_name();
720
		}
721
722
		$this->_generation_queue->add( $message, $data, $message_to_generate->preview(), $test_send );
723
	}
724
725
726
727
} //end EE_Messages_Generator