Completed
Branch BUG-9647-cpt-queries (303307)
by
unknown
31:50 queued 15:24
created

EE_Messages_Generator   F

Complexity

Total Complexity 82

Size/Duplication

Total Lines 712
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 19

Importance

Changes 0
Metric Value
dl 0
loc 712
rs 1.263
c 0
b 0
f 0
wmc 82
lcom 1
cbo 19

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A generation_queue() 0 3 1
B generate() 0 63 6
A _reset_current_properties() 0 8 2
B _generate() 0 41 5
C _get_message_template_group() 0 75 12
A _get_templates() 0 13 4
B _assemble_messages() 0 28 5
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_queue()->rewind();
122
		while ( $this->_generation_queue->get_queue()->valid() ) {
123
			//reset "current" properties
124
			$this->_reset_current_properties();
125
126
			/** @type EE_Message $msg */
127
			$msg = $this->_generation_queue->get_queue()->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_queue()->next();
139
			$next_msg = $this->_generation_queue->get_queue()->current();
140
			//restore pointer to current item
141
			$this->_generation_queue->get_queue()->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_queue()->delete();
158
			}
159
			//next item
160
			$this->_generation_queue->get_queue()->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_queue()->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_queue()->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_queue()->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_queue()->is_preview(),
400
						$this->_generation_queue->get_queue()->is_test_send()
401
					);
402
					$generated_count++;
403
				}
404
			}
405
		}
406
407
		//if there are no generated messages then something else fatal went wrong.
408
		return $generated_count > 0;
409
	}
410
411
412
413
414
415
	/**
416
	 * @param string $context   The context for the generated message.
417
	 * @param EE_Messages_Addressee $recipient
418
	 * @param array  $templates  formatted array of templates used for parsing data.
419
	 * @param EE_Message_Template_Group $message_template_group
420
	 * @return EE_Message | bool  (false is used when no EE_Message is generated)
421
	 */
422
	protected function _setup_message_object(
423
		$context,
424
		EE_Messages_Addressee $recipient,
425
		$templates,
426
		EE_Message_Template_Group $message_template_group
427
	) {
428
		//stuff we already know
429
		$transaction_id = $recipient->txn instanceof EE_Transaction ? $recipient->txn->ID() : 0;
430
		$transaction_id = empty( $transaction_id ) && $this->_current_data_handler->txn instanceof EE_Transaction
431
			? $this->_current_data_handler->txn->ID()
432
			: $transaction_id;
433
		$message_fields = array(
434
			'GRP_ID'           => $message_template_group->ID(),
435
			'TXN_ID'           => $transaction_id,
436
			'MSG_messenger'    => $this->_current_messenger->name,
437
			'MSG_message_type' => $this->_current_message_type->name,
438
			'MSG_context'      => $context,
439
		);
440
441
		//recipient id and type should be on the EE_Messages_Addressee object but if this is empty, let's try to grab the
442
		//info from the att_obj found in the EE_Messages_Addressee object.
443
		if ( empty( $recipient->recipient_id ) || empty( $recipient->recipient_type ) ) {
444
			$message_fields['MSG_recipient_ID'] = $recipient->att_obj instanceof EE_Attendee
445
				? $recipient->att_obj->ID()
446
				: 0;
447
			$message_fields['MSG_recipient_type'] = 'Attendee';
448
		} else {
449
			$message_fields['MSG_recipient_ID'] = $recipient->recipient_id;
450
			$message_fields['MSG_recipient_type'] = $recipient->recipient_type;
451
		}
452
		$message = EE_Message_Factory::create( $message_fields );
453
454
		//grab valid shortcodes for shortcode parser
455
		$mt_shortcodes = $this->_current_message_type->get_valid_shortcodes();
456
		$m_shortcodes = $this->_current_messenger->get_valid_shortcodes();
457
458
		//if the 'to' field is empty (messages will ALWAYS have a "to" field, then we get out because that means this
459
		//context is turned off) EXCEPT if we're previewing
460
		if ( empty( $templates['to'][ $context ] )
461
		     && ! $this->_generation_queue->get_queue()->is_preview()
462
		     && ! $this->_current_messenger->allow_empty_to_field() ) {
463
			//we silently exit here and do NOT record a fail because the message is "turned off" by having no "to" field.
464
			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...
465
		}
466
		$error_msg = array();
467
		foreach ( $templates as $field => $field_context ) {
468
			$error_msg = array();
469
			//let's setup the valid shortcodes for the incoming context.
470
			$valid_shortcodes = $mt_shortcodes[ $context ];
471
			//merge in valid shortcodes for the field.
472
			$shortcodes = isset($m_shortcodes[ $field ]) ? $m_shortcodes[ $field ] : $valid_shortcodes;
473
			if ( isset( $templates[ $field ][ $context ] ) ) {
474
				//prefix field.
475
				$column_name = 'MSG_' . $field;
476
				try {
477
					$content = $this->_shortcode_parser->parse_message_template(
478
						$templates[ $field ][ $context ],
479
						$recipient,
480
						$shortcodes,
481
						$this->_current_message_type,
482
						$this->_current_messenger,
483
						$message );
484
					$message->set_field_or_extra_meta( $column_name, $content );
485
				} catch ( EE_Error $e ) {
486
					$error_msg[] = sprintf( __( 'There was a problem generating the content for the field %s: %s', 'event_espresso' ), $field, $e->getMessage() );
487
					$message->set_STS_ID( EEM_Message::status_failed );
488
				}
489
			}
490
		}
491
492
		if ( $message->STS_ID() === EEM_Message::status_failed ) {
493
			$error_msg = __( 'There were problems generating this message:', 'event_espresso' ) . "\n" . implode( "\n", $error_msg );
494
			$message->set_error_message( $error_msg );
495
		} else {
496
			$message->set_STS_ID( EEM_Message::status_idle );
497
		}
498
		return $message;
499
	}
500
501
502
503
	/**
504
	 * This verifies that the incoming array has a EE_messenger object and a EE_message_type object and sets appropriate
505
	 * error message if either is missing.
506
	 *
507
	 * @return bool         true means there were no errors, false means there were errors.
508
	 */
509
	protected function _verify() {
510
		//reset error message to an empty array.
511
		$this->_error_msg = array();
512
		$valid = true;
513
		$valid = $valid ? $this->_validate_messenger_and_message_type() : $valid;
514
		$valid = $valid ? $this->_validate_and_setup_data() : $valid;
515
516
		//set the verified flag so we know everything has been validated.
517
		$this->_verified = $valid;
518
519
		return $valid;
520
	}
521
522
523
	/**
524
	 * This accepts an array and validates that it is an array indexed by context with each value being an array of
525
	 * EE_Messages_Addressee objects.
526
	 *
527
	 * @param array $addressees  Keys correspond to contexts for the message type and values are EE_Messages_Addressee[]
528
	 * @return bool
529
	 */
530
	protected function _valid_addressees( $addressees ) {
531
		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...
532
			return false;
533
		}
534
535
		foreach( $addressees as $addressee_array ) {
536
			foreach ( $addressee_array as $addressee ) {
537
				if ( ! $addressee instanceof EE_Messages_Addressee ) {
538
					return false;
539
				}
540
			}
541
		}
542
		return true;
543
	}
544
545
546
547
548
549
	/**
550
	 * This validates the messenger, message type, and presences of generation data for the current EE_Message in the queue.
551
	 * This process sets error messages if something is wrong.
552
	 *
553
	 * @return bool   true is if there are no errors.  false is if there is.
554
	 */
555
	protected function _validate_messenger_and_message_type() {
556
557
		//first are there any existing error messages?  If so then return.
558
		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...
559
			return false;
560
		}
561
		/** @type EE_Message $message */
562
		$message = $this->_generation_queue->get_queue()->current();
563
		try {
564
			$this->_current_messenger = $message->valid_messenger( true ) ? $message->messenger_object() : null;
565
		} catch ( Exception $e ) {
566
			$this->_error_msg[] = $e->getMessage();
567
		}
568
		try {
569
			$this->_current_message_type = $message->valid_message_type( true ) ? $message->message_type_object() : null;
570
		} catch ( Exception $e ) {
571
			$this->_error_msg[] = $e->getMessage();
572
		}
573
574
		/**
575
		 * Check if there is any generation data, but only if this is not for a preview.
576
		 */
577
		if ( ! $this->_generation_queue->get_queue()->get_generation_data()
578
		     && (
579
			     ! $this->_generation_queue->get_queue()->is_preview()
580
			     && $this->_generation_queue->get_queue()->get_data_handler() !== 'EE_Messages_Preview_incoming_data' )
581
		) {
582
			$this->_error_msg[] = __( 'There is no generation data for this message. Unable to generate.' );
583
		}
584
585
		return empty( $this->_error_msg );
586
	}
587
588
589
590
591
592
	/**
593
	 * This method retrieves the expected data handler for the message type and validates the generation data for that
594
	 * data handler.
595
	 *
596
	 * @return bool true means there are no errors.  false means there were errors (and handler did not get setup).
597
	 */
598
	protected function _validate_and_setup_data() {
599
600
		//First, are there any existing error messages?  If so, return because if there were errors elsewhere this can't
601
		//be used anyways.
602
		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...
603
			return false;
604
		}
605
606
		$generation_data = $this->_generation_queue->get_queue()->get_generation_data();
607
608
		/** @type EE_Messages_incoming_data $data_handler_class_name - well not really... just the class name actually */
609
		$data_handler_class_name = $this->_generation_queue->get_queue()->get_data_handler()
610
			? $this->_generation_queue->get_queue()->get_data_handler()
611
			: 'EE_Messages_' .  $this->_current_message_type->get_data_handler( $generation_data ) . '_incoming_data';
612
613
		//If this EE_Message is for a preview, then let's switch out to the preview data handler.
614
		if ( $this->_generation_queue->get_queue()->is_preview() ) {
615
			$data_handler_class_name  = 'EE_Messages_Preview_incoming_data';
616
		}
617
618
		//First get the class name for the data handler (and also verifies it exists.
619
		if ( ! class_exists( $data_handler_class_name ) ) {
620
			$this->_error_msg[] = sprintf(
621
				__( 'The included data handler class name does not match any valid, accessible, "EE_Messages_incoming_data" classes.  Looking for %s.', 'event_espresso' ),
622
				$data_handler_class_name
623
			);
624
			return false;
625
		}
626
627
		//convert generation_data for data_handler_instantiation.
628
		$generation_data = $data_handler_class_name::convert_data_from_persistent_storage( $generation_data );
629
630
		//note, this may set error messages as well.
631
		$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...
632
633
		return empty( $this->_error_msg );
634
	}
635
636
637
638
639
640
	/**
641
	 * Sets the $_current_data_handler property that is used for generating the current EE_Message in the queue, and
642
	 * adds it to the _data repository.
643
	 *
644
	 * @param mixed     $generating_data        This is data expected by the instantiated data handler.
645
	 * @param string    $data_handler_class_name This is the reference string indicating what data handler is being
646
	 *                                          instantiated.
647
	 *
648
	 * @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...
649
	 */
650
	protected function _set_data_handler( $generating_data, $data_handler_class_name ) {
651
		//valid classname for the data handler.  Now let's setup the key for the data handler repository to see if there
652
		//is already a ready data handler in the repository.
653
		$this->_current_data_handler = $this->_data_handler_collection->get_by_key( $this->_data_handler_collection->get_key( $data_handler_class_name, $generating_data ) );
654
		if ( ! $this->_current_data_handler instanceof EE_messages_incoming_data ) {
655
			//no saved data_handler in the repo so let's set one up and add it to the repo.
656
			try {
657
				$this->_current_data_handler = new $data_handler_class_name( $generating_data );
658
				$this->_data_handler_collection->add( $this->_current_data_handler, $generating_data );
659
			} catch( EE_Error $e ) {
660
				$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...
661
			}
662
		}
663
	}
664
665
666
667
668
669
	/**
670
	 * The queued EE_Message for generation does not save the data used for generation as objects
671
	 * because serialization of those objects could be problematic if the data is saved to the db.
672
	 * So this method calls the static method on the associated data_handler for the given message_type
673
	 * and that preps the data for later instantiation when generating.
674
	 *
675
	 * @param EE_Message_To_Generate $message_to_generate
676
	 * @param bool                   $preview Indicate whether this is being used for a preview or not.
677
	 * @return mixed Prepped data for persisting to the queue.  false is returned if unable to prep data.
678
	 */
679
	protected function _prepare_data_for_queue( EE_Message_To_Generate $message_to_generate, $preview ) {
680
		/** @type EE_Messages_incoming_data $data_handler - well not really... just the class name actually */
681
		$data_handler = $message_to_generate->get_data_handler_class_name( $preview );
682
		if ( ! $message_to_generate->valid() ) {
683
			return false; //unable to get the data because the info in the EE_Message_To_Generate class is invalid.
684
		}
685
		return $data_handler::convert_data_for_persistent_storage( $message_to_generate->data() );
686
	}
687
688
689
690
691
692
	/**
693
	 * This sets up a EEM_Message::status_incomplete EE_Message object and adds it to the generation queue.
694
	 *
695
	 * @param EE_Message_To_Generate $message_to_generate
696
	 * @param bool                   $test_send Whether this is just a test send or not.  Typically used for previews.
697
	 */
698
	public function create_and_add_message_to_queue( EE_Message_To_Generate $message_to_generate, $test_send = false ) {
699
		//prep data
700
		$data = $this->_prepare_data_for_queue( $message_to_generate, $message_to_generate->preview() );
701
702
		$message = $message_to_generate->get_EE_Message();
703
704
		//is there a GRP_ID in the request?
705
		if ( $GRP_ID = EE_Registry::instance()->REQ->get( 'GRP_ID' ) ) {
706
			$message->set_GRP_ID( $GRP_ID );
707
		}
708
709
		if ( $data === false ) {
710
			$message->set_STS_ID( EEM_Message::status_failed );
711
			$message->set_error_message( __( 'Unable to prepare data for persistence to the database.', 'event_espresso' ) );
712
		} else {
713
			//make sure that the data handler is cached on the message as well
714
			$data['data_handler_class_name'] = $message_to_generate->get_data_handler_class_name();
715
		}
716
717
		$this->_generation_queue->add( $message, $data, $message_to_generate->preview(), $test_send );
718
	}
719
720
721
722
} //end EE_Messages_Generator