Completed
Branch FET-9795-new-interfaces (ea072c)
by
unknown
127:10 queued 107:02
created

EE_Import   D

Complexity

Total Complexity 104

Size/Duplication

Total Lines 743
Duplicated Lines 6.33 %

Coupling/Cohesion

Components 2
Dependencies 10

Importance

Changes 0
Metric Value
dl 47
loc 743
rs 4.4444
c 0
b 0
f 0
wmc 104
lcom 2
cbo 10

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A instance() 7 7 4
A reset() 0 4 1
B upload_form() 0 29 1
D import() 0 116 21
F save_csv_data_array_to_db() 16 74 13
D save_data_rows_to_db() 7 80 16
A _decide_whether_to_insert_or_update_given_data_from_other_db() 0 10 2
A _decide_whether_to_insert_or_update_given_data_from_same_db() 0 8 2
C _replace_temp_ids_with_mappings() 17 61 13
B _handle_split_term_ids() 0 9 5
B _find_mapping_in() 0 17 6
C _insert_from_data_array() 0 33 7
C _update_from_data_array() 0 52 8
A get_total_inserts() 0 3 1
A get_total_insert_errors() 0 3 1
A get_total_updates() 0 3 1
A get_total_update_errors() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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

1
<?php use EventEspresso\core\interfaces\ResettableInterface;
2
3
if (!defined( 'EVENT_ESPRESSO_VERSION')) exit('No direct script access allowed');
4
do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
5
/**
6
 * EE_Import class
7
 *
8
 * @package				Event Espresso
9
 * @subpackage		includes/functions
10
 * @author					Brent Christensen
11
 *
12
 * ------------------------------------------------------------------------
13
 */
14
 class EE_Import implements ResettableInterface {
15
16
	const do_insert = 'insert';
17
	const do_update = 'update';
18
	const do_nothing = 'nothing';
19
20
21
  // instance of the EE_Import object
22
	private static $_instance = NULL;
23
24
	private static $_csv_array = array();
0 ignored issues
show
Unused Code introduced by
The property $_csv_array is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
25
26
	/**
27
	 *
28
	 * @var array of model names
29
	 */
30
	private static $_model_list = array();
0 ignored issues
show
Unused Code introduced by
The property $_model_list is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
31
32
	private static $_columns_to_save = array();
0 ignored issues
show
Unused Code introduced by
The property $_columns_to_save is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
33
34
	protected $_total_inserts = 0;
35
	protected $_total_updates = 0;
36
	protected $_total_insert_errors = 0;
37
	protected $_total_update_errors = 0;
38
39
40
	/**
41
	 *		private constructor to prevent direct creation
42
	 *		@Constructor
43
	 *		@access private
44
	 *		@return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
45
	 */
46
  private function __construct() {
47
		$this->_total_inserts = 0;
48
		$this->_total_updates = 0;
49
		$this->_total_insert_errors = 0;
50
		$this->_total_update_errors = 0;
51
	}
52
53
54
	/**
55
	 *	@ singleton method used to instantiate class object
56
	 *	@ access public
57
	 *	@return EE_Import
58
	 */
59 View Code Duplication
	public static function instance() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
60
		// check if class object is instantiated
61
		if ( self::$_instance === NULL  or ! is_object( self::$_instance ) or ! ( self::$_instance instanceof EE_Import )) {
62
			self::$_instance = new self();
63
		}
64
		return self::$_instance;
65
	}
66
67
	/**
68
	 * Resets the importer
69
	 * @return EE_Import
70
	 */
71
	public static function reset(){
72
		self::$_instance = null;
73
		return self::instance();
74
	}
75
76
77
78
79
	/**
80
	 *	@ generates HTML for a file upload input and form
81
	 *	@ access 	public
82
	 * 	@param 	string 		$title - heading for the form
83
	 * 	@param 	string 		$intro - additional text explaing what to do
84
	 * 	@param 	string 		$page - EE Admin page to direct form to - in the form "espresso_{pageslug}"
0 ignored issues
show
Bug introduced by
There is no parameter named $page. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
85
	 * 	@param 	string 		$action - EE Admin page route array "action" that form will direct to
86
	 * 	@param 	string 		$type - type of file to import
87
	 *	@ return 	string
88
	 */
89
	public function upload_form ( $title, $intro, $form_url, $action, $type  ) {
90
91
		$form_url = EE_Admin_Page::add_query_args_and_nonce( array( 'action' => $action ), $form_url );
92
93
		ob_start();
94
?>
95
	<div class="ee-upload-form-dv">
96
		<h3><?php echo $title;?></h3>
97
		<p><?php echo $intro;?></p>
98
99
		<form action="<?php echo $form_url?>" method="post" enctype="multipart/form-data">
100
			<input type="hidden" name="csv_submitted" value="TRUE" id="<?php echo time();?>">
101
			<input name="import" type="hidden" value="<?php echo $type;?>" />
102
			<input type="file" name="file[]" size="90" >
103
			<input class="button-primary" type="submit" value="<?php _e( 'Upload File', 'event_espresso' );?>">
104
		</form>
105
106
		<p class="ee-attention">
107
			<b><?php _e( 'Attention', 'event_espresso' );?></b><br/>
108
			<?php echo sprintf( __( 'Accepts .%s file types only.', 'event_espresso' ), $type ) ;?>
109
			<?php echo __( 'Please only import CSV files exported from Event Espresso, or compatible 3rd-party software.', 'event_espresso' );?>
110
		</p>
111
112
	</div>
113
114
<?php
115
		$uploader = ob_get_clean();
116
		return $uploader;
117
	}
118
119
120
121
122
123
	/**
124
	 *	@Import Event Espresso data - some code "borrowed" from event espresso csv_import.php
125
	 *	@access public
126
	 *	@return boolean success
127
	 */
128
	public function import() {
129
130
		require_once( EE_CLASSES . 'EE_CSV.class.php' );
131
		$this->EE_CSV = EE_CSV::instance();
0 ignored issues
show
Bug introduced by
The property EE_CSV does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
132
133
		if ( isset( $_REQUEST['import'] )) {
134
			if( isset( $_POST['csv_submitted'] )) {
135
136
			    switch ( $_FILES['file']['error'][0] ) {
137
			        case UPLOAD_ERR_OK:
138
			            $error_msg = FALSE;
139
			            break;
140
			        case UPLOAD_ERR_INI_SIZE:
141
			            $error_msg = __("'The uploaded file exceeds the upload_max_filesize directive in php.ini.'", "event_espresso");
142
			            break;
143
			        case UPLOAD_ERR_FORM_SIZE:
144
			            $error_msg = __('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', "event_espresso");
145
			            break;
146
			        case UPLOAD_ERR_PARTIAL:
147
			            $error_msg = __('The uploaded file was only partially uploaded.', "event_espresso");
148
			            break;
149
			        case UPLOAD_ERR_NO_FILE:
150
			            $error_msg = __('No file was uploaded.', "event_espresso");
151
			            break;
152
			        case UPLOAD_ERR_NO_TMP_DIR:
153
			            $error_msg = __('Missing a temporary folder.', "event_espresso");
154
			            break;
155
			        case UPLOAD_ERR_CANT_WRITE:
156
			            $error_msg = __('Failed to write file to disk.', "event_espresso");
157
			            break;
158
			        case UPLOAD_ERR_EXTENSION:
159
			            $error_msg = __('File upload stopped by extension.', "event_espresso");
160
			            break;
161
			        default:
162
			            $error_msg = __('An unknown error occurred and the file could not be uploaded', "event_espresso");
163
			            break;
164
			    }
165
166
				if ( ! $error_msg ) {
167
168
				    $filename	= $_FILES['file']['name'][0];
169
					$file_ext 		= substr( strrchr( $filename, '.' ), 1 );
170
				    $file_type 	= $_FILES['file']['type'][0];
0 ignored issues
show
Unused Code introduced by
$file_type is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
171
				    $temp_file	= $_FILES['file']['tmp_name'][0];
172
				    $filesize    	= $_FILES['file']['size'][0] / 1024;//convert from bytes to KB
173
174
					if ( $file_ext=='csv' ) {
175
176
						$max_upload = $this->EE_CSV->get_max_upload_size();//max upload size in KB
177
						if ( $filesize < $max_upload || true) {
178
179
							$wp_upload_dir = str_replace( array( '\\', '/' ), DS, wp_upload_dir());
180
							$path_to_file = $wp_upload_dir['basedir'] . DS . 'espresso' . DS . $filename;
181
182
							if( move_uploaded_file( $temp_file, $path_to_file )) {
183
184
								// convert csv to array
185
								$this->csv_array = $this->EE_CSV->import_csv_to_model_data_array( $path_to_file );
0 ignored issues
show
Bug introduced by
The property csv_array does not seem to exist. Did you mean _csv_array?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
186
187
								// was data successfully stored in an array?
188
								if ( is_array( $this->csv_array ) ) {
0 ignored issues
show
Bug introduced by
The property csv_array does not seem to exist. Did you mean _csv_array?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
189
190
									$import_what = str_replace( 'csv_import_', '', $_REQUEST['action'] );
191
									$import_what = str_replace( '_', ' ', ucwords( $import_what ));
0 ignored issues
show
Unused Code introduced by
$import_what is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
192
									$processed_data = $this->csv_array;
0 ignored issues
show
Bug introduced by
The property csv_array does not seem to exist. Did you mean _csv_array?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
193
									$this->columns_to_save = FALSE;
0 ignored issues
show
Bug introduced by
The property columns_to_save does not seem to exist. Did you mean _columns_to_save?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
194
195
									// if any imports require funcky processing, we'll catch them in the switch
196
									switch ($_REQUEST['action']) {
197
198
										case "import_events";
199
										case "event_list";
200
												$import_what = 'Event Details';
0 ignored issues
show
Unused Code introduced by
$import_what is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
201
										break;
202
203
										case 'groupon_import_csv':
204
											$import_what = 'Groupon Codes';
0 ignored issues
show
Unused Code introduced by
$import_what is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
205
											$processed_data = $this->process_groupon_codes();
0 ignored issues
show
Bug introduced by
The method process_groupon_codes() does not seem to exist on object<EE_Import>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
206
										break;
207
208
									}
209
									// save processed codes to db
210
									if ( $this->save_csv_data_array_to_db( $processed_data, $this->columns_to_save ) ) {
0 ignored issues
show
Bug introduced by
The property columns_to_save does not seem to exist. Did you mean _columns_to_save?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
211
										return TRUE;
212
213
									}
214
								} else {
215
									// no array? must be an error
216
									EE_Error::add_error(sprintf(__("No file seems to have been uploaded", "event_espresso")), __FILE__, __FUNCTION__, __LINE__ );
217
									return FALSE;
218
								}
219
220
							} else {
221
								EE_Error::add_error(sprintf(__("%s was not successfully uploaded", "event_espresso"),$filename), __FILE__, __FUNCTION__, __LINE__ );
222
								return FALSE;
223
							}
224
225
						} else {
226
							EE_Error::add_error( sprintf(__("%s was too large of a file and could not be uploaded. The max filesize is %s' KB.", "event_espresso"),$filename,$max_upload), __FILE__, __FUNCTION__, __LINE__ );
227
							return FALSE;
228
						}
229
230
					} else {
231
						EE_Error::add_error( sprintf(__("%s  had an invalid file extension, not uploaded", "event_espresso"),$filename), __FILE__, __FUNCTION__, __LINE__ );
232
						return FALSE;
233
					}
234
235
				} else {
236
					EE_Error::add_error( $error_msg, __FILE__, __FUNCTION__, __LINE__ );
237
					return FALSE;
238
				}
239
240
			}
241
		}
242
		return;
243
	}
244
245
	/**
246
	 *	Given an array of data (usually from a CSV import) attempts to save that data to the db.
247
	 *	If $model_name ISN'T provided, assumes that this is a 3d array, with toplevel keys being model names,
248
	 *	next level being numeric indexes adn each value representing a model object, and the last layer down
249
	 *	being keys of model fields and their proposed values.
250
	 *	If $model_name IS provided, assumes a 2d array of the bottom two layers previously mentioned.
251
	 *	If the CSV data says (in the metadata row) that it's from the SAME database,
252
	 *	we treat the IDs in the CSV as the normal IDs, and try to update those records. However, if those
253
	 *	IDs DON'T exist in the database, they're treated as temporary IDs,
254
	 *	which can used elsewhere to refer to the same object. Once an item
255
	 *	with a temporary ID gets inserted, we record its mapping from temporary
256
	 *	ID to real ID, and use the real ID in place of the temporary ID
257
	 *	when that temporary ID was used as a foreign key.
258
	 *	If the CSV data says (in the metadata again) that it's from a DIFFERENT database,
259
	 *	we treat all the IDs in the CSV as temporary ID- eg, if the CSV specifies an event with
260
	 *	ID 1, and the database already has an event with ID 1, we assume that's just a coincidence,
261
	 *	and insert a new event, and map it's temporary ID of 1 over to its new real ID.
262
	 *	An important exception are non-auto-increment primary keys. If one entry in the
263
	 *	CSV file has the same ID as one in the DB, we assume they are meant to be
264
	 *	the same item, and instead update the item in the DB with that same ID.
265
	 *	Also note, we remember the mappings permanently. So the 2nd, 3rd, and 10000th
266
	 *	time you import a CSV from a different site, we remember their mappings, and
267
	 * will try to update the item in the DB instead of inserting another item (eg
268
	 * if we previously imported an event with temporary ID 1, and then it got a
269
	 * real ID of 123, we remember that. So the next time we import an event with
270
	 * temporary ID, from the same site, we know that it's real ID is 123, and will
271
	 * update that event, instead of adding a new event).
272
	 *		  @access public
273
	 *			@param array $csv_data_array - the array containing the csv data produced from EE_CSV::import_csv_to_model_data_array()
274
	 *			@param array $fields_to_save - an array containing the csv column names as keys with the corresponding db table fields they will be saved to
0 ignored issues
show
Bug introduced by
There is no parameter named $fields_to_save. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
275
	 *			@return TRUE on success, FALSE on fail
276
	 */
277
	public function save_csv_data_array_to_db( $csv_data_array, $model_name = FALSE ) {
278
279
280
		$success = FALSE;
281
		$error = FALSE;
282
		//whther to treat this import as if it's data froma different database or not
283
		//ie, if it IS from a different database, ignore foreign keys whihf
284
		$export_from_site_a_to_b = true;
285
		// first level of array is not table information but a table name was passed to the function
286
		// array is only two levels deep, so let's fix that by adding a level, else the next steps will fail
287
		if($model_name){
288
			$csv_data_array = array($csv_data_array);
289
		}
290
		// begin looking through the $csv_data_array, expecting the toplevel key to be the model's name...
291
		$old_site_url = 'none-specified';
292
293
		//hanlde metadata
294
		if(isset($csv_data_array[EE_CSV::metadata_header]) ){
295
			$csv_metadata = array_shift($csv_data_array[EE_CSV::metadata_header]);
296
			//ok so its metadata, dont try to save it to ehte db obviously...
297
			if(isset($csv_metadata['site_url']) && $csv_metadata['site_url'] == site_url()){
298
				EE_Error::add_attention(sprintf(__("CSV Data appears to be from the same database, so attempting to update data", "event_espresso")));
299
				$export_from_site_a_to_b = false;
300
			}else{
301
				$old_site_url = isset( $csv_metadata['site_url']) ? $csv_metadata['site_url'] : $old_site_url;
302
				EE_Error::add_attention(sprintf(__("CSV Data appears to be from a different database (%s instead of %s), so we assume IDs in the CSV data DO NOT correspond to IDs in this database", "event_espresso"),$old_site_url,site_url()));
303
			};
304
			unset($csv_data_array[EE_CSV::metadata_header]);
305
		}
306
		/**
307
		* @var $old_db_to_new_db_mapping 2d array: toplevel keys being model names, bottom-level keys being the original key, and
308
		* the value will be the newly-inserted ID.
309
		* If we have already imported data from the same website via CSV, it shoudl be kept in this wp option
310
		*/
311
	   $old_db_to_new_db_mapping = get_option('ee_id_mapping_from'.sanitize_title($old_site_url),array());
312
	   if( $old_db_to_new_db_mapping){
313
		   EE_Error::add_attention(sprintf(__("We noticed you have imported data via CSV from %s before. Because of this, IDs in your CSV have been mapped to their new IDs in %s", "event_espresso"),$old_site_url,site_url()));
314
	   }
315
	   $old_db_to_new_db_mapping = $this->save_data_rows_to_db($csv_data_array, $export_from_site_a_to_b, $old_db_to_new_db_mapping);
0 ignored issues
show
Documentation introduced by
$csv_data_array is of type array, but the function expects a object<type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$export_from_site_a_to_b is of type boolean, but the function expects a object<type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
316
317
		//save the mapping from old db to new db in case they try re-importing the same data from the same website again
318
		update_option('ee_id_mapping_from'.sanitize_title($old_site_url),$old_db_to_new_db_mapping);
319
320 View Code Duplication
		if ( $this->_total_updates > 0 ) {
321
			EE_Error::add_success( sprintf(__("%s existing records in the database were updated.", "event_espresso"),$this->_total_updates));
322
			$success = true;
323
		}
324 View Code Duplication
		if ( $this->_total_inserts > 0 ) {
325
			EE_Error::add_success(sprintf(__("%s new records were added to the database.", "event_espresso"),$this->_total_inserts));
326
			$success = true;
327
		}
328
329 View Code Duplication
		if ( $this->_total_update_errors > 0 ) {
330
			EE_Error::add_error(sprintf(__("'One or more errors occurred, and a total of %s existing records in the database were <strong>not</strong> updated.'", "event_espresso"),$this->_total_update_errors), __FILE__, __FUNCTION__, __LINE__ );
331
			$error = true;
332
		}
333 View Code Duplication
		if ( $this->_total_insert_errors > 0 ) {
334
			EE_Error::add_error(sprintf(__("One or more errors occurred, and a total of %s new records were <strong>not</strong> added to the database.'", "event_espresso"),$this->_total_insert_errors), __FILE__, __FUNCTION__, __LINE__ );
335
			$error = true;
336
		}
337
338
		//lastly, we need to update the datetime and ticket sold amounts
339
		//as those may ahve been affected by this
340
		EEM_Datetime::instance()->update_sold( EEM_Datetime::instance()->get_all() );
0 ignored issues
show
Documentation introduced by
\EEM_Datetime::instance()->get_all() is of type array<integer,object<EE_Base_Class>>, but the function expects a array<integer,object<EE_Datetime>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
341
		EEM_Ticket::instance()->update_tickets_sold(EEM_Ticket::instance()->get_all());
0 ignored issues
show
Documentation introduced by
\EEM_Ticket::instance()->get_all() is of type array<integer,object<EE_Base_Class>>, but the function expects a array<integer,object<EE_Ticket>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
342
343
		// if there was at least one success and absolutely no errors
344
		if ( $success && ! $error ) {
345
			return TRUE;
346
		} else {
347
			return FALSE;
348
		}
349
350
	}
351
352
353
	/**
354
	 * Processes the array of data, given the knowledge that it's from the same database or a different one,
355
	 * and the mapping from temporary IDs to real IDs.
356
	 * If the data is from a different database, we treat the primary keys and their corresponding
357
	 * foreign keys as "temp Ids", basically identifiers that get mapped to real primary keys
358
	 * in the real target database. As items are inserted, their temporary primary keys
359
	 * are mapped to the real IDs in the target database. Also, before doing any update or
360
	 * insert, we replace all the temp ID which are foreign keys with their mapped real IDs.
361
	 * An exception: string primary keys are treated as real IDs, or else we'd need to
362
	 * dynamically generate new string primary keys which would be very awkard for the country table etc.
363
	 * Also, models with no primary key are strange too. We combine use their primar key INDEX (a
364
	 * combination of fields) to create a unique string identifying the row and store
365
	 * those in the mapping.
366
	 *
367
	 * If the data is from the same database, we usually treat primary keys as real IDs.
368
	 * An exception is if there is nothing in the database for that ID. If that's the case,
369
	 * we need to insert a new row for that ID, and then map from the non-existent ID
370
	 * to the newly-inserted real ID.
371
	 * @param type $csv_data_array
372
	 * @param type $export_from_site_a_to_b
373
	 * @param type $old_db_to_new_db_mapping
374
	 * @return array updated $old_db_to_new_db_mapping
375
	 */
376
	public function save_data_rows_to_db( $csv_data_array, $export_from_site_a_to_b, $old_db_to_new_db_mapping ) {
377
		foreach ( $csv_data_array as $model_name_in_csv_data => $model_data_from_import ) {
378
			//now check that assumption was correct. If
379 View Code Duplication
			if ( EE_Registry::instance()->is_model_name($model_name_in_csv_data)) {
380
				$model_name = $model_name_in_csv_data;
381
			}else {
382
				// no table info in the array and no table name passed to the function?? FAIL
383
				EE_Error::add_error( __('No table information was specified and/or found, therefore the import could not be completed','event_espresso'), __FILE__, __FUNCTION__, __LINE__ );
384
				return FALSE;
385
			}
386
			/* @var $model EEM_Base */
387
			$model = EE_Registry::instance()->load_model($model_name);
388
389
			//so without further ado, scanning all the data provided for primary keys and their inital values
390
			foreach ( $model_data_from_import as $model_object_data ) {
391
				//before we do ANYTHING, make sure the csv row wasn't just completely blank
392
				$row_is_completely_empty = true;
393
				foreach($model_object_data as $field){
394
					if($field){
395
						$row_is_completely_empty = false;
396
					}
397
				}
398
				if($row_is_completely_empty){
399
					continue;
400
				}
401
				//find the PK in the row of data (or a combined key if
402
				//there is no primary key)
403
				if($model->has_primary_key_field()){
404
					$id_in_csv =  $model_object_data[$model->primary_key_name()];
405
				}else{
406
					$id_in_csv = $model->get_index_primary_key_string($model_object_data);
407
				}
408
409
410
				$model_object_data = $this->_replace_temp_ids_with_mappings( $model_object_data, $model, $old_db_to_new_db_mapping, $export_from_site_a_to_b );
0 ignored issues
show
Bug introduced by
It seems like $old_db_to_new_db_mapping defined by $this->_update_from_data...d_db_to_new_db_mapping) on line 448 can also be of type array; however, EE_Import::_replace_temp_ids_with_mappings() does only seem to accept object<type>, 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...
411
				//now we need to decide if we're going to add a new model object given the $model_object_data,
412
				//or just update.
413
				if($export_from_site_a_to_b){
414
					$what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_other_db( $id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping );
415
				}else{//this is just a re-import
416
					$what_to_do = $this->_decide_whether_to_insert_or_update_given_data_from_same_db( $id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping );
0 ignored issues
show
Unused Code introduced by
The call to EE_Import::_decide_wheth...ven_data_from_same_db() has too many arguments starting with $old_db_to_new_db_mapping.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
417
				}
418
				if( $what_to_do == self::do_nothing ) {
419
					continue;
420
				}
421
422
				//double-check we actually want to insert, if that's what we're planning
423
				//based on whether this item would be unique in the DB or not
424
				if( $what_to_do == self::do_insert ) {
425
					//we're supposed to be inserting. But wait, will this thing
426
					//be acceptable if inserted?
427
					$conflicting = $model->get_one_conflicting( $model_object_data, false );
428
					if($conflicting){
429
						//ok, this item would conflict if inserted. Just update the item that it conflicts with.
430
						$what_to_do = self::do_update;
431
						//and if this model has a primary key, remember its mapping
432
						if($model->has_primary_key_field()){
433
							$old_db_to_new_db_mapping[$model_name][$id_in_csv] = $conflicting->ID();
434
							$model_object_data[$model->primary_key_name()] = $conflicting->ID();
435
						}else{
436
							//we want to update this conflicting item, instead of inserting a conflicting item
437
							//so we need to make sure they match entirely (its possible that they only conflicted on one field, but we need them to match on other fields
438
							//for the WHERE conditions in the update). At the time of this comment, there were no models like this
439
							foreach($model->get_combined_primary_key_fields() as $key_field){
440
								$model_object_data[$key_field->get_name()] = $conflicting->get($key_field->get_name());
441
							}
442
						}
443
					}
444
				}
445
				if( $what_to_do == self::do_insert ) {
446
					$old_db_to_new_db_mapping = $this->_insert_from_data_array( $id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping );
0 ignored issues
show
Bug introduced by
It seems like $old_db_to_new_db_mapping can also be of type array; however, EE_Import::_insert_from_data_array() does only seem to accept object<type>, 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...
447
				}elseif( $what_to_do == self::do_update ) {
448
					$old_db_to_new_db_mapping = $this->_update_from_data_array( $id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping );
449
				}else{
450
					throw new EE_Error( sprintf( __( 'Programming error. We shoudl be inserting or updating, but instead we are being told to "%s", whifh is invalid', 'event_espresso' ), $what_to_do ) );
451
				}
452
			}
453
		}
454
		return $old_db_to_new_db_mapping;
455
	}
456
457
458
459
	/**
460
	 * Decides whether or not to insert, given that this data is from another database.
461
	 * So, if the primary key of this $model_object_data already exists in the database,
462
	 * it's just a coincidence and we should still insert. The only time we should
463
	 * update is when we know what it maps to, or there's something that would
464
	 * conflict (and we should instead just update that conflicting thing)
465
	 * @param string $id_in_csv
466
	 * @param array $model_object_data by reference so it can be modified
467
	 * @param EEM_Base $model
468
	 * @param array $old_db_to_new_db_mapping by reference so it can be modified
469
	 * @return string one of the consts on this class that starts with do_*
470
	 */
471
	protected function _decide_whether_to_insert_or_update_given_data_from_other_db( $id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping ) {
0 ignored issues
show
Unused Code introduced by
The parameter $model_object_data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
472
		$model_name = $model->get_this_model_name();
473
		//if it's a site-to-site export-and-import, see if this modelobject's id
474
		//in the old data that we know of
475
		if( isset($old_db_to_new_db_mapping[$model_name][$id_in_csv]) ){
476
			return self::do_update;
477
		}else{
478
			return self::do_insert;
479
		}
480
	}
481
482
	/**
483
	 * If this thing basically already exists in the database, we want to update it;
484
	 * otherwise insert it (ie, someone tweaked the CSV file, or the item was
485
	 * deleted in the database so it should be re-inserted)
486
	 * @param type $id_in_csv
487
	 * @param type $model_object_data
488
	 * @param EEM_Base $model
489
	 * @param type $old_db_to_new_db_mapping
0 ignored issues
show
Bug introduced by
There is no parameter named $old_db_to_new_db_mapping. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
490
	 * @return
491
	 */
492
	protected function _decide_whether_to_insert_or_update_given_data_from_same_db( $id_in_csv, $model_object_data, $model ) {
0 ignored issues
show
Unused Code introduced by
The parameter $id_in_csv is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
493
		//in this case, check if this thing ACTUALLY exists in the database
494
		if( $model->get_one_conflicting( $model_object_data ) ){
495
			return self::do_update;
496
		}else{
497
			return self::do_insert;
498
		}
499
	}
500
501
	/**
502
	 * Using the $old_db_to_new_db_mapping array, replaces all the temporary IDs
503
	 * with their mapped real IDs. Eg, if importing from site A to B, the mapping
504
	 * file may indicate that the ID "my_event_id" maps to an actual event ID of 123.
505
	 * So this function searches for any event temp Ids called "my_event_id" and
506
	 * replaces them with 123.
507
	 * Also, if there is no temp ID for the INT foreign keys from another database,
508
	 * replaces them with 0 or the field's default.
509
	 * @param type $model_object_data
510
	 * @param EEM_Base $model
511
	 * @param type $old_db_to_new_db_mapping
512
	 * @param boolean $export_from_site_a_to_b
513
	 * @return array updated model object data with temp IDs removed
514
	 */
515
	protected function _replace_temp_ids_with_mappings( $model_object_data, $model, $old_db_to_new_db_mapping, $export_from_site_a_to_b ) {
516
		//if this model object's primary key is in the mapping, replace it
517
		if( $model->has_primary_key_field() &&
518
				$model->get_primary_key_field()->is_auto_increment() &&
519
				isset( $old_db_to_new_db_mapping[ $model->get_this_model_name() ] ) &&
520
				isset( $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $model_object_data[ $model->primary_key_name() ] ] ) ) {
521
			$model_object_data[ $model->primary_key_name() ] = $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $model_object_data[ $model->primary_key_name() ] ];
522
		}
523
524
		try{
525
			$model_name_field = $model->get_field_containing_related_model_name();
526
			$models_pointed_to_by_model_name_field = $model_name_field->get_model_names_pointed_to();
0 ignored issues
show
Unused Code introduced by
$models_pointed_to_by_model_name_field is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
527
		}catch( EE_Error $e ){
528
			$model_name_field = NULL;
529
			$models_pointed_to_by_model_name_field = array();
0 ignored issues
show
Unused Code introduced by
$models_pointed_to_by_model_name_field is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
530
		}
531
		foreach( $model->field_settings( true )  as $field_obj ){
532
			if( $field_obj instanceof EE_Foreign_Key_Int_Field ) {
533
				$models_pointed_to = $field_obj->get_model_names_pointed_to();
534
				$found_a_mapping = false;
535
				foreach( $models_pointed_to as $model_pointed_to_by_fk ) {
536
537
					if( $model_name_field ){
538
						$value_of_model_name_field = $model_object_data[ $model_name_field->get_name() ];
539 View Code Duplication
						if( $value_of_model_name_field == $model_pointed_to_by_fk ) {
540
							$model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
541
									$model_object_data[ $field_obj->get_name() ],
542
									$model_pointed_to_by_fk,
543
									$old_db_to_new_db_mapping,
544
									$export_from_site_a_to_b );
0 ignored issues
show
Documentation introduced by
$export_from_site_a_to_b is of type boolean, but the function expects a object<type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
545
								$found_a_mapping = true;
0 ignored issues
show
Unused Code introduced by
$found_a_mapping is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
546
								break;
547
						}
548 View Code Duplication
					}else{
549
						$model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
550
								$model_object_data[ $field_obj->get_name() ],
551
								$model_pointed_to_by_fk,
552
								$old_db_to_new_db_mapping,
553
								$export_from_site_a_to_b );
0 ignored issues
show
Documentation introduced by
$export_from_site_a_to_b is of type boolean, but the function expects a object<type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
554
						$found_a_mapping = true;
555
					}
556
					//once we've found a mapping for this field no need to continue
557
					if( $found_a_mapping ) {
558
						break;
559
					}
560
561
562
				}
563
			}else{
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
564
				//it's a string foreign key (which we leave alone, because those are things
565
				//like country names, which we'd really rather not make 2 USAs etc (we'd actually
566
				//prefer to just update one)
567
				//or it's just a regular value that ought to be replaced
568
			}
569
		}
570
		//
571
		if( $model instanceof EEM_Term_Taxonomy ){
572
			$model_object_data = $this->_handle_split_term_ids( $model_object_data );
573
		}
574
		return $model_object_data;
575
	}
576
577
	/**
578
	 * If the data was exported PRE-4.2, but then imported POST-4.2, then the term_id
579
	 * this term-taxonomy refers to may be out-of-date so we need to update it.
580
	 * see https://make.wordpress.org/core/2015/02/16/taxonomy-term-splitting-in-4-2-a-developer-guide/
581
	 * @param type $model_object_data
582
	 * @return array new model object data
583
	 */
584
	protected function _handle_split_term_ids( $model_object_data ){
585
		if( isset( $model_object_data['term_id'] ) && isset( $model_object_data[ 'taxonomy' ]) && apply_filters( 'FHEE__EE_Import__handle_split_term_ids__function_exists', function_exists( 'wp_get_split_term' ), $model_object_data ) ) {
586
			$new_term_id = wp_get_split_term( $model_object_data[ 'term_id' ], $model_object_data[ 'taxonomy' ] );
587
			if( $new_term_id ){
588
				$model_object_data[ 'term_id' ] = $new_term_id;
589
			}
590
		}
591
		return $model_object_data;
592
	}
593
	/**
594
	 * Given the object's ID and its model's name, find it int he mapping data,
595
	 * bearing in mind where it came from
596
	 * @param type $object_id
597
	 * @param string $model_name
598
	 * @param array $old_db_to_new_db_mapping
599
	 * @param type $export_from_site_a_to_b
600
	 * @return int
601
	 */
602
	protected function _find_mapping_in( $object_id, $model_name, $old_db_to_new_db_mapping, $export_from_site_a_to_b) {
603
		if(	isset( $old_db_to_new_db_mapping[ $model_name ][ $object_id ] ) ){
604
605
				return $old_db_to_new_db_mapping[ $model_name ][ $object_id ];
606
			}elseif( $object_id == '0' || $object_id == '' ) {
607
				//leave as-is
608
				return $object_id;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $object_id; (type) is incompatible with the return type documented by EE_Import::_find_mapping_in of type integer.

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...
609
			}elseif( $export_from_site_a_to_b ){
610
				//we couldn't find a mapping for this, and it's from a different site,
611
				//so blank it out
612
				return NULL;
613
			}elseif( ! $export_from_site_a_to_b ) {
614
				//we coudln't find a mapping for this, but it's from thsi DB anyway
615
				//so let's just leave it as-is
616
				return $object_id;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $object_id; (type) is incompatible with the return type documented by EE_Import::_find_mapping_in of type integer.

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...
617
			}
618
	}
619
620
	/**
621
	 *
622
	 * @param type $id_in_csv
623
	 * @param type $model_object_data
624
	 * @param EEM_Base $model
625
	 * @param type $old_db_to_new_db_mapping
626
	 * @return array updated $old_db_to_new_db_mapping
627
	 */
628
	protected function _insert_from_data_array( $id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping ) {
629
		//remove the primary key, if there is one (we don't want it for inserts OR updates)
630
		//we'll put it back in if we need it
631
		if($model->has_primary_key_field() && $model->get_primary_key_field()->is_auto_increment()){
632
			$effective_id = $model_object_data[$model->primary_key_name()];
633
			unset($model_object_data[$model->primary_key_name()]);
634
		}else{
635
			$effective_id = $model->get_index_primary_key_string( $model_object_data );
636
		}
637
		//the model takes care of validating the CSV's input
638
		try{
639
			$new_id = $model->insert($model_object_data);
640
			if( $new_id ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $new_id of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
641
				$old_db_to_new_db_mapping[$model->get_this_model_name()][$id_in_csv] = $new_id;
642
				$this->_total_inserts++;
643
				EE_Error::add_success( sprintf(__("Successfully added new %s (with id %s) with csv data %s", "event_espresso"),$model->get_this_model_name(),$new_id, implode(",",$model_object_data)));
644
			}else{
645
				$this->_total_insert_errors++;
646
				//put the ID used back in there for the error message
647
				if($model->has_primary_key_field()){
648
					$model_object_data[$model->primary_key_name()] = $effective_id;
649
				}
650
				EE_Error::add_error( sprintf(__("Could not insert new %s with the csv data: %s", "event_espresso"),$model->get_this_model_name(),http_build_query($model_object_data)), __FILE__, __FUNCTION__, __LINE__ );
651
			}
652
		}catch(EE_Error $e){
653
			$this->_total_insert_errors++;
654
			if($model->has_primary_key_field()){
655
				$model_object_data[$model->primary_key_name()] = $effective_id;
656
			}
657
			EE_Error::add_error( sprintf(__("Could not insert new %s with the csv data: %s because %s", "event_espresso"),$model->get_this_model_name(),implode(",",$model_object_data),$e->getMessage()), __FILE__, __FUNCTION__, __LINE__ );
658
		}
659
		return $old_db_to_new_db_mapping;
660
	}
661
662
	/**
663
	 * Given the model object data, finds the row to update and updates it
664
	 * @param string|int $id_in_csv
665
	 * @param array $model_object_data
666
	 * @param EEM_Base $model
667
	 * @param array $old_db_to_new_db_mapping
668
	 * @return array updated $old_db_to_new_db_mapping
669
	 */
670
	protected function _update_from_data_array( $id_in_csv,  $model_object_data, $model, $old_db_to_new_db_mapping ) {
671
		try{
672
			//let's keep two copies of the model object data:
673
			//one for performing an update, one for everthing else
674
			$model_object_data_for_update = $model_object_data;
675
			if($model->has_primary_key_field()){
676
				$conditions = array($model->primary_key_name() => $model_object_data[$model->primary_key_name()]);
677
				//remove the primary key because we shouldn't use it for updating
678
				unset($model_object_data_for_update[$model->primary_key_name()]);
679
			}elseif($model->get_combined_primary_key_fields() > 1 ){
680
				$conditions = array();
681
				foreach($model->get_combined_primary_key_fields() as $key_field){
682
					$conditions[$key_field->get_name()] = $model_object_data[$key_field->get_name()];
683
				}
684
			}else{
685
				$model->primary_key_name();//this shoudl just throw an exception, explaining that we dont have a primary key (or a combine dkey)
686
			}
687
688
			$success = $model->update($model_object_data_for_update,array($conditions));
0 ignored issues
show
Bug introduced by
The variable $conditions does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
689
			if($success){
690
				$this->_total_updates++;
691
				EE_Error::add_success( sprintf(__("Successfully updated %s with csv data %s", "event_espresso"),$model->get_this_model_name(),implode(",",$model_object_data_for_update)));
692
				//we should still record the mapping even though it was an update
693
				//because if we were going to insert somethign but it was going to conflict
694
				//we would have last-minute decided to update. So we'd like to know what we updated
695
				//and so we record what record ended up being updated using the mapping
696
				if( $model->has_primary_key_field() ){
697
					$new_key_for_mapping = $model_object_data[ $model->primary_key_name() ];
698
				}else{
699
					//no primary key just a combined key
700
					$new_key_for_mapping = $model->get_index_primary_key_string( $model_object_data );
701
				}
702
				$old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_key_for_mapping;
703
			}else{
704
				$matched_items = $model->get_all(array($conditions));
705
				if( ! $matched_items){
0 ignored issues
show
Bug Best Practice introduced by
The expression $matched_items of type EE_Base_Class[] 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...
706
					//no items were matched (so we shouldn't have updated)... but then we should have inserted? what the heck?
707
					$this->_total_update_errors++;
708
					EE_Error::add_error( sprintf(__("Could not update %s with the csv data: '%s' for an unknown reason (using WHERE conditions %s)", "event_espresso"),$model->get_this_model_name(),http_build_query($model_object_data),http_build_query($conditions)), __FILE__, __FUNCTION__, __LINE__ );
709
				}else{
710
					$this->_total_updates++;
711
					EE_Error::add_success( sprintf(__("%s with csv data '%s' was found in the database and didn't need updating because all the data is identical.", "event_espresso"),$model->get_this_model_name(),implode(",",$model_object_data)));
712
				}
713
			}
714
		}catch(EE_Error $e){
715
			$this->_total_update_errors++;
716
			$basic_message = sprintf(__("Could not update %s with the csv data: %s because %s", "event_espresso"),$model->get_this_model_name(),implode(",",$model_object_data),$e->getMessage());
717
			$debug_message = $basic_message . ' Stack trace: ' . $e->getTraceAsString();
718
			EE_Error::add_error( "$basic_message | $debug_message", __FILE__, __FUNCTION__, __LINE__ );
719
		}
720
		return $old_db_to_new_db_mapping;
721
	}
722
723
	/**
724
	 * Gets the number of inserts performed since importer was instantiated or reset
725
	 * @return int
726
	 */
727
	public function get_total_inserts(){
728
		return $this->_total_inserts;
729
	}
730
	/**
731
	 *  Gets the number of insert errors since importer was instantiated or reset
732
	 * @return int
733
	 */
734
	public function get_total_insert_errors(){
735
		return $this->_total_insert_errors;
736
	}
737
	/**
738
	 *  Gets the number of updates performed since importer was instantiated or reset
739
	 * @return int
740
	 */
741
	public function get_total_updates(){
742
		return $this->_total_updates;
743
	}
744
	/**
745
	 *  Gets the number of update errors since importer was instantiated or reset
746
	 * @return int
747
	 */
748
	public function get_total_update_errors(){
749
		return $this->_total_update_errors;
750
	}
751
752
753
754
755
756
}
757
/* End of file EE_Import.class.php */
758
/* Location: /includes/classes/EE_Import.class.php */
759
?>