Completed
Branch TASK-9118-extensions-page (04eaec)
by
unknown
780:19 queued 765:32
created

EE_Import   F

Complexity

Total Complexity 104

Size/Duplication

Total Lines 743
Duplicated Lines 6.33 %

Coupling/Cohesion

Components 2
Dependencies 11
Metric Value
wmc 104
lcom 2
cbo 11
dl 47
loc 743
rs 3.9999

18 Methods

Rating   Name   Duplication   Size   Complexity  
A reset() 0 4 1
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
A instance() 7 7 4
A __construct() 0 6 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

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 if (!defined('EVENT_ESPRESSO_VERSION')) exit('No direct script access allowed');
2
do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
3
/**
4
 * EE_Import class
5
 *
6
 * @package				Event Espresso
7
 * @subpackage		includes/functions
8
 * @author					Brent Christensen
9
 *
10
 * ------------------------------------------------------------------------
11
 */
12
 class EE_Import {
13
14
	const do_insert = 'insert';
15
	const do_update = 'update';
16
	const do_nothing = 'nothing';
17
18
19
  // instance of the EE_Import object
20
	private static $_instance = NULL;
21
22
	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...
23
24
	/**
25
	 *
26
	 * @var array of model names
27
	 */
28
	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...
29
30
	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...
31
32
	protected $_total_inserts = 0;
33
	protected $_total_updates = 0;
34
	protected $_total_insert_errors = 0;
35
	protected $_total_update_errors = 0;
36
37
38
	/**
39
	 *		private constructor to prevent direct creation
40
	 *		@Constructor
41
	 *		@access private
42
	 *		@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...
43
	 */
44
  private function __construct() {
45
		$this->_total_inserts = 0;
46
		$this->_total_updates = 0;
47
		$this->_total_insert_errors = 0;
48
		$this->_total_update_errors = 0;
49
	}
50
51
52
	/**
53
	 *	@ singleton method used to instantiate class object
54
	 *	@ access public
55
	 *	@return EE_Import
56
	 */
57 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...
58
		// check if class object is instantiated
59
		if ( self::$_instance === NULL  or ! is_object( self::$_instance ) or ! ( self::$_instance instanceof EE_Import )) {
60
			self::$_instance = new self();
61
		}
62
		return self::$_instance;
63
	}
64
65
	/**
66
	 * Resets the importer
67
	 * @return EE_Import
68
	 */
69
	public static function reset(){
70
		self::$_instance = null;
71
		return self::instance();
72
	}
73
74
75
76
77
	/**
78
	 *	@ generates HTML for a file upload input and form
79
	 *	@ access 	public
80
	 * 	@param 	string 		$title - heading for the form
81
	 * 	@param 	string 		$intro - additional text explaing what to do
82
	 * 	@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...
83
	 * 	@param 	string 		$action - EE Admin page route array "action" that form will direct to
84
	 * 	@param 	string 		$type - type of file to import
85
	 *	@ return 	string
86
	 */
87
	public function upload_form ( $title, $intro, $form_url, $action, $type  ) {
88
89
		$form_url = EE_Admin_Page::add_query_args_and_nonce( array( 'action' => $action ), $form_url );
90
91
		ob_start();
92
?>
93
	<div class="ee-upload-form-dv">
94
		<h3><?php echo $title;?></h3>
95
		<p><?php echo $intro;?></p>
96
97
		<form action="<?php echo $form_url?>" method="post" enctype="multipart/form-data">
98
			<input type="hidden" name="csv_submitted" value="TRUE" id="<?php echo time();?>">
99
			<input name="import" type="hidden" value="<?php echo $type;?>" />
100
			<input type="file" name="file[]" size="90" >
101
			<input class="button-primary" type="submit" value="<?php _e( 'Upload File', 'event_espresso' );?>">
102
		</form>
103
104
		<p class="ee-attention">
105
			<b><?php _e( 'Attention', 'event_espresso' );?></b><br/>
106
			<?php echo sprintf( __( 'Accepts .%s file types only.', 'event_espresso' ), $type ) ;?>
107
			<?php echo __( 'Please only import CSV files exported from Event Espresso, or compatible 3rd-party software.', 'event_espresso' );?>
108
		</p>
109
110
	</div>
111
112
<?php
113
		$uploader = ob_get_clean();
114
		return $uploader;
115
	}
116
117
118
119
120
121
	/**
122
	 *	@Import Event Espresso data - some code "borrowed" from event espresso csv_import.php
123
	 *	@access public
124
	 *	@return boolean success
125
	 */
126
	public function import() {
127
128
		require_once( EE_CLASSES . 'EE_CSV.class.php' );
129
		$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...
130
131
		if ( isset( $_REQUEST['import'] )) {
132
			if( isset( $_POST['csv_submitted'] )) {
133
134
			    switch ( $_FILES['file']['error'][0] ) {
135
			        case UPLOAD_ERR_OK:
136
			            $error_msg = FALSE;
137
			            break;
138
			        case UPLOAD_ERR_INI_SIZE:
139
			            $error_msg = __("'The uploaded file exceeds the upload_max_filesize directive in php.ini.'", "event_espresso");
140
			            break;
141
			        case UPLOAD_ERR_FORM_SIZE:
142
			            $error_msg = __('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.', "event_espresso");
143
			            break;
144
			        case UPLOAD_ERR_PARTIAL:
145
			            $error_msg = __('The uploaded file was only partially uploaded.', "event_espresso");
146
			            break;
147
			        case UPLOAD_ERR_NO_FILE:
148
			            $error_msg = __('No file was uploaded.', "event_espresso");
149
			            break;
150
			        case UPLOAD_ERR_NO_TMP_DIR:
151
			            $error_msg = __('Missing a temporary folder.', "event_espresso");
152
			            break;
153
			        case UPLOAD_ERR_CANT_WRITE:
154
			            $error_msg = __('Failed to write file to disk.', "event_espresso");
155
			            break;
156
			        case UPLOAD_ERR_EXTENSION:
157
			            $error_msg = __('File upload stopped by extension.', "event_espresso");
158
			            break;
159
			        default:
160
			            $error_msg = __('An unknown error occurred and the file could not be uploaded', "event_espresso");
161
			            break;
162
			    }
163
164
				if ( ! $error_msg ) {
165
166
				    $filename	= $_FILES['file']['name'][0];
167
					$file_ext 		= substr( strrchr( $filename, '.' ), 1 );
168
				    $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...
169
				    $temp_file	= $_FILES['file']['tmp_name'][0];
170
				    $filesize    	= $_FILES['file']['size'][0] / 1024;//convert from bytes to KB
171
172
					if ( $file_ext=='csv' ) {
173
174
						$max_upload = $this->EE_CSV->get_max_upload_size();//max upload size in KB
175
						if ( $filesize < $max_upload || true) {
176
177
							$wp_upload_dir = str_replace( array( '\\', '/' ), DS, wp_upload_dir());
178
							$path_to_file = $wp_upload_dir['basedir'] . DS . 'espresso' . DS . $filename;
179
180
							if( move_uploaded_file( $temp_file, $path_to_file )) {
181
182
								// convert csv to array
183
								$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...
184
185
								// was data successfully stored in an array?
186
								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...
187
188
									$import_what = str_replace( 'csv_import_', '', $_REQUEST['action'] );
189
									$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...
190
									$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...
191
									$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...
192
193
									// if any imports require funcky processing, we'll catch them in the switch
194
									switch ($_REQUEST['action']) {
195
196
										case "import_events";
197
										case "event_list";
198
												$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...
199
										break;
200
201
										case 'groupon_import_csv':
202
											$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...
203
											$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...
204
										break;
205
206
									}
207
									// save processed codes to db
208
									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...
209
										return TRUE;
210
211
									}
212
								} else {
213
									// no array? must be an error
214
									EE_Error::add_error(sprintf(__("No file seems to have been uploaded", "event_espresso")), __FILE__, __FUNCTION__, __LINE__ );
215
									return FALSE;
216
								}
217
218
							} else {
219
								EE_Error::add_error(sprintf(__("%s was not successfully uploaded", "event_espresso"),$filename), __FILE__, __FUNCTION__, __LINE__ );
220
								return FALSE;
221
							}
222
223
						} else {
224
							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__ );
225
							return FALSE;
226
						}
227
228
					} else {
229
						EE_Error::add_error( sprintf(__("%s  had an invalid file extension, not uploaded", "event_espresso"),$filename), __FILE__, __FUNCTION__, __LINE__ );
230
						return FALSE;
231
					}
232
233
				} else {
234
					EE_Error::add_error( $error_msg, __FILE__, __FUNCTION__, __LINE__ );
235
					return FALSE;
236
				}
237
238
			}
239
		}
240
		return;
241
	}
242
243
	/**
244
	 *	Given an array of data (usually from a CSV import) attempts to save that data to the db.
245
	 *	If $model_name ISN'T provided, assumes that this is a 3d array, with toplevel keys being model names,
246
	 *	next level being numeric indexes adn each value representing a model object, and the last layer down
247
	 *	being keys of model fields and their proposed values.
248
	 *	If $model_name IS provided, assumes a 2d array of the bottom two layers previously mentioned.
249
	 *	If the CSV data says (in the metadata row) that it's from the SAME database,
250
	 *	we treat the IDs in the CSV as the normal IDs, and try to update those records. However, if those
251
	 *	IDs DON'T exist in the database, they're treated as temporary IDs,
252
	 *	which can used elsewhere to refer to the same object. Once an item
253
	 *	with a temporary ID gets inserted, we record its mapping from temporary
254
	 *	ID to real ID, and use the real ID in place of the temporary ID
255
	 *	when that temporary ID was used as a foreign key.
256
	 *	If the CSV data says (in the metadata again) that it's from a DIFFERENT database,
257
	 *	we treat all the IDs in the CSV as temporary ID- eg, if the CSV specifies an event with
258
	 *	ID 1, and the database already has an event with ID 1, we assume that's just a coincidence,
259
	 *	and insert a new event, and map it's temporary ID of 1 over to its new real ID.
260
	 *	An important exception are non-auto-increment primary keys. If one entry in the
261
	 *	CSV file has the same ID as one in the DB, we assume they are meant to be
262
	 *	the same item, and instead update the item in the DB with that same ID.
263
	 *	Also note, we remember the mappings permanently. So the 2nd, 3rd, and 10000th
264
	 *	time you import a CSV from a different site, we remember their mappings, and
265
	 * will try to update the item in the DB instead of inserting another item (eg
266
	 * if we previously imported an event with temporary ID 1, and then it got a
267
	 * real ID of 123, we remember that. So the next time we import an event with
268
	 * temporary ID, from the same site, we know that it's real ID is 123, and will
269
	 * update that event, instead of adding a new event).
270
	 *		  @access public
271
	 *			@param array $csv_data_array - the array containing the csv data produced from EE_CSV::import_csv_to_model_data_array()
272
	 *			@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...
273
	 *			@return TRUE on success, FALSE on fail
274
	 */
275
	public function save_csv_data_array_to_db( $csv_data_array, $model_name = FALSE ) {
276
277
278
		$success = FALSE;
279
		$error = FALSE;
280
		//whther to treat this import as if it's data froma different database or not
281
		//ie, if it IS from a different database, ignore foreign keys whihf
282
		$export_from_site_a_to_b = true;
283
		// first level of array is not table information but a table name was passed to the function
284
		// array is only two levels deep, so let's fix that by adding a level, else the next steps will fail
285
		if($model_name){
286
			$csv_data_array = array($csv_data_array);
287
		}
288
		// begin looking through the $csv_data_array, expecting the toplevel key to be the model's name...
289
		$old_site_url = 'none-specified';
290
291
		//hanlde metadata
292
		if(isset($csv_data_array[EE_CSV::metadata_header]) ){
293
			$csv_metadata = array_shift($csv_data_array[EE_CSV::metadata_header]);
294
			//ok so its metadata, dont try to save it to ehte db obviously...
295
			if(isset($csv_metadata['site_url']) && $csv_metadata['site_url'] == site_url()){
296
				EE_Error::add_attention(sprintf(__("CSV Data appears to be from the same database, so attempting to update data", "event_espresso")));
297
				$export_from_site_a_to_b = false;
298
			}else{
299
				$old_site_url = isset( $csv_metadata['site_url']) ? $csv_metadata['site_url'] : $old_site_url;
300
				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()));
301
			};
302
			unset($csv_data_array[EE_CSV::metadata_header]);
303
		}
304
		/**
305
		* @var $old_db_to_new_db_mapping 2d array: toplevel keys being model names, bottom-level keys being the original key, and
306
		* the value will be the newly-inserted ID.
307
		* If we have already imported data from the same website via CSV, it shoudl be kept in this wp option
308
		*/
309
	   $old_db_to_new_db_mapping = get_option('ee_id_mapping_from'.sanitize_title($old_site_url),array());
310
	   if( $old_db_to_new_db_mapping){
311
		   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()));
312
	   }
313
	   $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...
314
315
		//save the mapping from old db to new db in case they try re-importing the same data from the same website again
316
		update_option('ee_id_mapping_from'.sanitize_title($old_site_url),$old_db_to_new_db_mapping);
317
318 View Code Duplication
		if ( $this->_total_updates > 0 ) {
319
			EE_Error::add_success( sprintf(__("%s existing records in the database were updated.", "event_espresso"),$this->_total_updates));
320
			$success = true;
321
		}
322 View Code Duplication
		if ( $this->_total_inserts > 0 ) {
323
			EE_Error::add_success(sprintf(__("%s new records were added to the database.", "event_espresso"),$this->_total_inserts));
324
			$success = true;
325
		}
326
327 View Code Duplication
		if ( $this->_total_update_errors > 0 ) {
328
			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__ );
329
			$error = true;
330
		}
331 View Code Duplication
		if ( $this->_total_insert_errors > 0 ) {
332
			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__ );
333
			$error = true;
334
		}
335
336
		//lastly, we need to update the datetime and ticket sold amounts
337
		//as those may ahve been affected by this
338
		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...
339
		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...
340
341
		// if there was at least one success and absolutely no errors
342
		if ( $success && ! $error ) {
343
			return TRUE;
344
		} else {
345
			return FALSE;
346
		}
347
348
	}
349
350
351
	/**
352
	 * Processes the array of data, given the knowledge that it's from the same database or a different one,
353
	 * and the mapping from temporary IDs to real IDs.
354
	 * If the data is from a different database, we treat the primary keys and their corresponding
355
	 * foreign keys as "temp Ids", basically identifiers that get mapped to real primary keys
356
	 * in the real target database. As items are inserted, their temporary primary keys
357
	 * are mapped to the real IDs in the target database. Also, before doing any update or
358
	 * insert, we replace all the temp ID which are foreign keys with their mapped real IDs.
359
	 * An exception: string primary keys are treated as real IDs, or else we'd need to
360
	 * dynamically generate new string primary keys which would be very awkard for the country table etc.
361
	 * Also, models with no primary key are strange too. We combine use their primar key INDEX (a
362
	 * combination of fields) to create a unique string identifying the row and store
363
	 * those in the mapping.
364
	 *
365
	 * If the data is from the same database, we usually treat primary keys as real IDs.
366
	 * An exception is if there is nothing in the database for that ID. If that's the case,
367
	 * we need to insert a new row for that ID, and then map from the non-existent ID
368
	 * to the newly-inserted real ID.
369
	 * @param type $csv_data_array
370
	 * @param type $export_from_site_a_to_b
371
	 * @param type $old_db_to_new_db_mapping
372
	 * @return array updated $old_db_to_new_db_mapping
373
	 */
374
	public function save_data_rows_to_db( $csv_data_array, $export_from_site_a_to_b, $old_db_to_new_db_mapping ) {
375
		foreach ( $csv_data_array as $model_name_in_csv_data => $model_data_from_import ) {
376
			//now check that assumption was correct. If
377 View Code Duplication
			if ( EE_Registry::instance()->is_model_name($model_name_in_csv_data)) {
378
				$model_name = $model_name_in_csv_data;
379
			}else {
380
				// no table info in the array and no table name passed to the function?? FAIL
381
				EE_Error::add_error( __('No table information was specified and/or found, therefore the import could not be completed','event_espresso'), __FILE__, __FUNCTION__, __LINE__ );
382
				return FALSE;
383
			}
384
			/* @var $model EEM_Base */
385
			$model = EE_Registry::instance()->load_model($model_name);
386
387
			//so without further ado, scanning all the data provided for primary keys and their inital values
388
			foreach ( $model_data_from_import as $model_object_data ) {
389
				//before we do ANYTHING, make sure the csv row wasn't just completely blank
390
				$row_is_completely_empty = true;
391
				foreach($model_object_data as $field){
392
					if($field){
393
						$row_is_completely_empty = false;
394
					}
395
				}
396
				if($row_is_completely_empty){
397
					continue;
398
				}
399
				//find the PK in the row of data (or a combined key if
400
				//there is no primary key)
401
				if($model->has_primary_key_field()){
402
					$id_in_csv =  $model_object_data[$model->primary_key_name()];
403
				}else{
404
					$id_in_csv = $model->get_index_primary_key_string($model_object_data);
405
				}
406
407
408
				$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 446 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...
409
				//now we need to decide if we're going to add a new model object given the $model_object_data,
410
				//or just update.
411
				if($export_from_site_a_to_b){
412
					$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 );
413
				}else{//this is just a re-import
414
					$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...
415
				}
416
				if( $what_to_do == self::do_nothing ) {
417
					continue;
418
				}
419
420
				//double-check we actually want to insert, if that's what we're planning
421
				//based on whether this item would be unique in the DB or not
422
				if( $what_to_do == self::do_insert ) {
423
					//we're supposed to be inserting. But wait, will this thing
424
					//be acceptable if inserted?
425
					$conflicting = $model->get_one_conflicting( $model_object_data, false );
426
					if($conflicting){
427
						//ok, this item would conflict if inserted. Just update the item that it conflicts with.
428
						$what_to_do = self::do_update;
429
						//and if this model has a primary key, remember its mapping
430
						if($model->has_primary_key_field()){
431
							$old_db_to_new_db_mapping[$model_name][$id_in_csv] = $conflicting->ID();
432
							$model_object_data[$model->primary_key_name()] = $conflicting->ID();
433
						}else{
434
							//we want to update this conflicting item, instead of inserting a conflicting item
435
							//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
436
							//for the WHERE conditions in the update). At the time of this comment, there were no models like this
437
							foreach($model->get_combined_primary_key_fields() as $key_field){
438
								$model_object_data[$key_field->get_name()] = $conflicting->get($key_field->get_name());
439
							}
440
						}
441
					}
442
				}
443
				if( $what_to_do == self::do_insert ) {
444
					$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...
445
				}elseif( $what_to_do == self::do_update ) {
446
					$old_db_to_new_db_mapping = $this->_update_from_data_array( $id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping );
447
				}else{
448
					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 ) );
449
				}
450
			}
451
		}
452
		return $old_db_to_new_db_mapping;
453
	}
454
455
456
457
	/**
458
	 * Decides whether or not to insert, given that this data is from another database.
459
	 * So, if the primary key of this $model_object_data already exists in the database,
460
	 * it's just a coincidence and we should still insert. The only time we should
461
	 * update is when we know what it maps to, or there's something that would
462
	 * conflict (and we should instead just update that conflicting thing)
463
	 * @param string $id_in_csv
464
	 * @param array $model_object_data by reference so it can be modified
465
	 * @param EEM_Base $model
466
	 * @param array $old_db_to_new_db_mapping by reference so it can be modified
467
	 * @return string one of the consts on this class that starts with do_*
468
	 */
469
	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...
470
		$model_name = $model->get_this_model_name();
471
		//if it's a site-to-site export-and-import, see if this modelobject's id
472
		//in the old data that we know of
473
		if( isset($old_db_to_new_db_mapping[$model_name][$id_in_csv]) ){
474
			return self::do_update;
475
		}else{
476
			return self::do_insert;
477
		}
478
	}
479
480
	/**
481
	 * If this thing basically already exists in the database, we want to update it;
482
	 * otherwise insert it (ie, someone tweaked the CSV file, or the item was
483
	 * deleted in the database so it should be re-inserted)
484
	 * @param type $id_in_csv
485
	 * @param type $model_object_data
486
	 * @param EEM_Base $model
487
	 * @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...
488
	 * @return
489
	 */
490
	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...
491
		//in this case, check if this thing ACTUALLY exists in the database
492
		if( $model->get_one_conflicting( $model_object_data ) ){
493
			return self::do_update;
494
		}else{
495
			return self::do_insert;
496
		}
497
	}
498
499
	/**
500
	 * Using the $old_db_to_new_db_mapping array, replaces all the temporary IDs
501
	 * with their mapped real IDs. Eg, if importing from site A to B, the mapping
502
	 * file may indicate that the ID "my_event_id" maps to an actual event ID of 123.
503
	 * So this function searches for any event temp Ids called "my_event_id" and
504
	 * replaces them with 123.
505
	 * Also, if there is no temp ID for the INT foreign keys from another database,
506
	 * replaces them with 0 or the field's default.
507
	 * @param type $model_object_data
508
	 * @param EEM_Base $model
509
	 * @param type $old_db_to_new_db_mapping
510
	 * @param boolean $export_from_site_a_to_b
511
	 * @return array updated model object data with temp IDs removed
512
	 */
513
	protected function _replace_temp_ids_with_mappings( $model_object_data, $model, $old_db_to_new_db_mapping, $export_from_site_a_to_b ) {
514
		//if this model object's primary key is in the mapping, replace it
515
		if( $model->has_primary_key_field() &&
516
				$model->get_primary_key_field()->is_auto_increment() &&
517
				isset( $old_db_to_new_db_mapping[ $model->get_this_model_name() ] ) &&
518
				isset( $old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $model_object_data[ $model->primary_key_name() ] ] ) ) {
519
			$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() ] ];
520
		}
521
522
		try{
523
			$model_name_field = $model->get_field_containing_related_model_name();
524
			$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...
525
		}catch( EE_Error $e ){
526
			$model_name_field = NULL;
527
			$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...
528
		}
529
		foreach( $model->field_settings( true )  as $field_obj ){
530
			if( $field_obj instanceof EE_Foreign_Key_Int_Field ) {
531
				$models_pointed_to = $field_obj->get_model_names_pointed_to();
532
				$found_a_mapping = false;
533
				foreach( $models_pointed_to as $model_pointed_to_by_fk ) {
534
535
					if( $model_name_field ){
536
						$value_of_model_name_field = $model_object_data[ $model_name_field->get_name() ];
537 View Code Duplication
						if( $value_of_model_name_field == $model_pointed_to_by_fk ) {
538
							$model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
539
									$model_object_data[ $field_obj->get_name() ],
540
									$model_pointed_to_by_fk,
541
									$old_db_to_new_db_mapping,
542
									$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...
543
								$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...
544
								break;
545
						}
546 View Code Duplication
					}else{
547
						$model_object_data[ $field_obj->get_name() ] = $this->_find_mapping_in(
548
								$model_object_data[ $field_obj->get_name() ],
549
								$model_pointed_to_by_fk,
550
								$old_db_to_new_db_mapping,
551
								$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...
552
						$found_a_mapping = true;
553
					}
554
					//once we've found a mapping for this field no need to continue
555
					if( $found_a_mapping ) {
556
						break;
557
					}
558
559
560
				}
561
			}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...
562
				//it's a string foreign key (which we leave alone, because those are things
563
				//like country names, which we'd really rather not make 2 USAs etc (we'd actually
564
				//prefer to just update one)
565
				//or it's just a regular value that ought to be replaced
566
			}
567
		}
568
		//
569
		if( $model instanceof EEM_Term_Taxonomy ){
570
			$model_object_data = $this->_handle_split_term_ids( $model_object_data );
571
		}
572
		return $model_object_data;
573
	}
574
575
	/**
576
	 * If the data was exported PRE-4.2, but then imported POST-4.2, then the term_id
577
	 * this term-taxonomy refers to may be out-of-date so we need to update it.
578
	 * see https://make.wordpress.org/core/2015/02/16/taxonomy-term-splitting-in-4-2-a-developer-guide/
579
	 * @param type $model_object_data
580
	 * @return array new model object data
581
	 */
582
	protected function _handle_split_term_ids( $model_object_data ){
583
		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 ) ) {
584
			$new_term_id = wp_get_split_term( $model_object_data[ 'term_id' ], $model_object_data[ 'taxonomy' ] );
585
			if( $new_term_id ){
586
				$model_object_data[ 'term_id' ] = $new_term_id;
587
			}
588
		}
589
		return $model_object_data;
590
	}
591
	/**
592
	 * Given the object's ID and its model's name, find it int he mapping data,
593
	 * bearing in mind where it came from
594
	 * @param type $object_id
595
	 * @param string $model_name
596
	 * @param array $old_db_to_new_db_mapping
597
	 * @param type $export_from_site_a_to_b
598
	 * @return int
599
	 */
600
	protected function _find_mapping_in( $object_id, $model_name, $old_db_to_new_db_mapping, $export_from_site_a_to_b) {
601
		if(	isset( $old_db_to_new_db_mapping[ $model_name ][ $object_id ] ) ){
602
603
				return $old_db_to_new_db_mapping[ $model_name ][ $object_id ];
604
			}elseif( $object_id == '0' || $object_id == '' ) {
605
				//leave as-is
606
				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...
607
			}elseif( $export_from_site_a_to_b ){
608
				//we couldn't find a mapping for this, and it's from a different site,
609
				//so blank it out
610
				return NULL;
611
			}elseif( ! $export_from_site_a_to_b ) {
612
				//we coudln't find a mapping for this, but it's from thsi DB anyway
613
				//so let's just leave it as-is
614
				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...
615
			}
616
	}
617
618
	/**
619
	 *
620
	 * @param type $id_in_csv
621
	 * @param type $model_object_data
622
	 * @param EEM_Base $model
623
	 * @param type $old_db_to_new_db_mapping
624
	 * @return array updated $old_db_to_new_db_mapping
625
	 */
626
	protected function _insert_from_data_array( $id_in_csv, $model_object_data, $model, $old_db_to_new_db_mapping ) {
627
		//remove the primary key, if there is one (we don't want it for inserts OR updates)
628
		//we'll put it back in if we need it
629
		if($model->has_primary_key_field() && $model->get_primary_key_field()->is_auto_increment()){
630
			$effective_id = $model_object_data[$model->primary_key_name()];
631
			unset($model_object_data[$model->primary_key_name()]);
632
		}else{
633
			$effective_id = $model->get_index_primary_key_string( $model_object_data );
634
		}
635
		//the model takes care of validating the CSV's input
636
		try{
637
			$new_id = $model->insert($model_object_data);
638
			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...
639
				$old_db_to_new_db_mapping[$model->get_this_model_name()][$id_in_csv] = $new_id;
640
				$this->_total_inserts++;
641
				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)));
642
			}else{
643
				$this->_total_insert_errors++;
644
				//put the ID used back in there for the error message
645
				if($model->has_primary_key_field()){
646
					$model_object_data[$model->primary_key_name()] = $effective_id;
647
				}
648
				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__ );
649
			}
650
		}catch(EE_Error $e){
651
			$this->_total_insert_errors++;
652
			if($model->has_primary_key_field()){
653
				$model_object_data[$model->primary_key_name()] = $effective_id;
654
			}
655
			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__ );
656
		}
657
		return $old_db_to_new_db_mapping;
658
	}
659
660
	/**
661
	 * Given the model object data, finds the row to update and updates it
662
	 * @param string|int $id_in_csv
663
	 * @param array $model_object_data
664
	 * @param EEM_Base $model
665
	 * @param array $old_db_to_new_db_mapping
666
	 * @return array updated $old_db_to_new_db_mapping
667
	 */
668
	protected function _update_from_data_array( $id_in_csv,  $model_object_data, $model, $old_db_to_new_db_mapping ) {
669
		try{
670
			//let's keep two copies of the model object data:
671
			//one for performing an update, one for everthing else
672
			$model_object_data_for_update = $model_object_data;
673
			if($model->has_primary_key_field()){
674
				$conditions = array($model->primary_key_name() => $model_object_data[$model->primary_key_name()]);
675
				//remove the primary key because we shouldn't use it for updating
676
				unset($model_object_data_for_update[$model->primary_key_name()]);
677
			}elseif($model->get_combined_primary_key_fields() > 1 ){
678
				$conditions = array();
679
				foreach($model->get_combined_primary_key_fields() as $key_field){
680
					$conditions[$key_field->get_name()] = $model_object_data[$key_field->get_name()];
681
				}
682
			}else{
683
				$model->primary_key_name();//this shoudl just throw an exception, explaining that we dont have a primary key (or a combine dkey)
684
			}
685
686
			$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...
687
			if($success){
688
				$this->_total_updates++;
689
				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)));
690
				//we should still record the mapping even though it was an update
691
				//because if we were going to insert somethign but it was going to conflict
692
				//we would have last-minute decided to update. So we'd like to know what we updated
693
				//and so we record what record ended up being updated using the mapping
694
				if( $model->has_primary_key_field() ){
695
					$new_key_for_mapping = $model_object_data[ $model->primary_key_name() ];
696
				}else{
697
					//no primary key just a combined key
698
					$new_key_for_mapping = $model->get_index_primary_key_string( $model_object_data );
699
				}
700
				$old_db_to_new_db_mapping[ $model->get_this_model_name() ][ $id_in_csv ] = $new_key_for_mapping;
701
			}else{
702
				$matched_items = $model->get_all(array($conditions));
703
				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...
704
					//no items were matched (so we shouldn't have updated)... but then we should have inserted? what the heck?
705
					$this->_total_update_errors++;
706
					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__ );
707
				}else{
708
					$this->_total_updates++;
709
					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)));
710
				}
711
			}
712
		}catch(EE_Error $e){
713
			$this->_total_update_errors++;
714
			$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());
715
			$debug_message = $basic_message . ' Stack trace: ' . $e->getTraceAsString();
716
			EE_Error::add_error( "$basic_message | $debug_message", __FILE__, __FUNCTION__, __LINE__ );
717
		}
718
		return $old_db_to_new_db_mapping;
719
	}
720
721
	/**
722
	 * Gets the number of inserts performed since importer was instantiated or reset
723
	 * @return int
724
	 */
725
	public function get_total_inserts(){
726
		return $this->_total_inserts;
727
	}
728
	/**
729
	 *  Gets the number of insert errors since importer was instantiated or reset
730
	 * @return int
731
	 */
732
	public function get_total_insert_errors(){
733
		return $this->_total_insert_errors;
734
	}
735
	/**
736
	 *  Gets the number of updates performed since importer was instantiated or reset
737
	 * @return int
738
	 */
739
	public function get_total_updates(){
740
		return $this->_total_updates;
741
	}
742
	/**
743
	 *  Gets the number of update errors since importer was instantiated or reset
744
	 * @return int
745
	 */
746
	public function get_total_update_errors(){
747
		return $this->_total_update_errors;
748
	}
749
750
751
752
753
754
}
755
/* End of file EE_Import.class.php */
756
/* Location: /includes/classes/EE_Import.class.php */
757
?>