Failed Conditions
Push — master ( b549f2...090668 )
by Remco
10:21 queued 03:46
created

Install::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
1
<?php
2
/**
3
 * Mollie install.
4
 *
5
 * @author    Pronamic <[email protected]>
6
 * @copyright 2005-2020 Pronamic
7
 * @license   GPL-3.0-or-later
8
 * @package   Pronamic\WordPress\Pay
9
 */
10
11
namespace Pronamic\WordPress\Pay\Gateways\Mollie;
12
13
/**
14
 * Title: Mollie install.
15
 * Description:
16
 * Copyright: 2005-2020 Pronamic
17
 * Company: Pronamic
18
 *
19
 * @author  Remco Tolsma
20
 * @version 2.1.0
21
 * @since   2.1.0
22
 */
23
class Install {
24
	/**
25
	 * Integration.
26
	 *
27
	 * @var Integration
28
	 */
29
	private $integration;
30
31
	/**
32
	 * Construct install.
33
	 *
34
	 * @link https://github.com/woocommerce/woocommerce/blob/4.0.0/includes/class-woocommerce.php#L368
35
	 * @link https://github.com/woocommerce/woocommerce/blob/4.0.0/includes/class-wc-install.php#L1568
36
	 * @link https://github.com/woocommerce/woocommerce/blob/4.0.0/includes/class-wc-install.php#L153-L166
37
	 * @param Integration $integration Integration.
38
	 */
39 7
	public function __construct( Integration $integration ) {
40 7
		$this->integration = $integration;
41
42 7
		add_action( 'init', array( $this, 'check_version' ), 5 );
43 7
	}
44
45
	/**
46
	 * Check version.
47
	 *
48
	 * @link https://github.com/woocommerce/woocommerce/blob/4.0.0/includes/class-wc-install.php#L168-L178
49
	 * @return void
50
	 */
51
	public function check_version() {
52
		$version_option = \strval( $this->integration->get_version_option() );
53
		$version        = \strval( $this->integration->get_version() );
54
55
		if ( version_compare( $version_option, $version, '<' ) ) {
56
			$this->install();
57
		}
58
	}
59
60
	/**
61
	 * Install.
62
	 *
63
	 * @link https://github.com/woocommerce/woocommerce/blob/4.0.0/includes/class-wc-install.php#L272-L306
64
	 * @return void
65
	 */
66
	public function install() {
67
		$this->create_tables();
68
		$this->add_foreign_keys();
69
		$this->convert_user_meta();
70
71
		$this->integration->update_version_option();
72
	}
73
74
	/**
75
	 * Create tables.
76
	 *
77
	 * @link https://github.com/woocommerce/woocommerce/blob/4.0.0/includes/class-wc-install.php#L630-L720
78
	 * @return void
79
	 */
80
	private function create_tables() {
81
		global $wpdb;
82
83
		/**
84
		 * Requirements.
85
		 */
86
		require_once ABSPATH . 'wp-admin/includes/upgrade.php';
87
88
		/**
89
		 * Table options.
90
		 *
91
		 * In MySQL 5.6, InnoDB is the default MySQL storage engine. Unless you
92
		 * have configured a different default storage engine,  issuing a
93
		 * CREATE TABLE statement without an ENGINE= clause creates an InnoDB
94
		 * table.
95
		 *
96
		 * @link https://dev.mysql.com/doc/refman/5.6/en/innodb-introduction.html
97
		 *
98
		 * If a storage engine is specified that is not available, MySQL uses
99
		 * the default engine instead. Normally, this is MyISAM. For example,
100
		 * if a table definition includes the ENGINE=INNODB option but the MySQL
101
		 * server does not support INNODB tables, the table is created as a
102
		 * MyISAM table.
103
		 *
104
		 * @link https://dev.mysql.com/doc/refman/5.6/en/create-table.html
105
		 */
106
		$table_options = 'ENGINE=InnoDB ' . $wpdb->get_charset_collate();
107
108
		/**
109
		 * Queries.
110
		 *
111
		 * @link https://github.com/WordPress/WordPress/blob/5.3/wp-admin/includes/schema.php
112
		 */
113
		$queries = "
114
			CREATE TABLE $wpdb->pronamic_pay_mollie_organizations (
115
				id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
116
				mollie_id varchar(16) NOT NULL,
117
				name varchar(128) DEFAULT NULL,
118
				email varchar(100) DEFAULT NULL,
119
				PRIMARY KEY  ( id ),
120
				UNIQUE KEY mollie_id ( mollie_id )
121
			) $table_options;
122
			CREATE TABLE $wpdb->pronamic_pay_mollie_profiles (
123
				id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
124
				mollie_id varchar(16) NOT NULL,
125
				organization_id bigint(20) unsigned DEFAULT NULL,
126
				name varchar(128) DEFAULT NULL,
127
				email varchar(100) DEFAULT NULL,
128
				api_key_test varchar(35) DEFAULT NULL,
129
				api_key_live varchar(35) DEFAULT NULL,
130
				PRIMARY KEY  ( id ),
131
				UNIQUE KEY mollie_id ( mollie_id ),
132
				KEY organization_id ( organization_id )
133
			) $table_options;
134
			CREATE TABLE $wpdb->pronamic_pay_mollie_customers (
135
				id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
136
				mollie_id varchar(16) NOT NULL,
137
				organization_id bigint(20) unsigned DEFAULT NULL,
138
				profile_id bigint(20) unsigned DEFAULT NULL,
139
				test_mode tinyint(1) NOT NULL,
140
				email varchar(100) DEFAULT NULL,
141
				name varchar(255) DEFAULT NULL,
142
				PRIMARY KEY  ( id ),
143
				UNIQUE KEY mollie_id ( mollie_id ),
144
				KEY organization_id ( organization_id ),
145
				KEY profile_id ( profile_id ),
146
				KEY test_mode ( test_mode ),
147
				KEY email ( email )
148
			) $table_options;
149
			CREATE TABLE $wpdb->pronamic_pay_mollie_customer_users (
150
				id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
151
				customer_id bigint(20) unsigned NOT NULL,
152
				user_id bigint(20) unsigned NOT NULL,
153
				PRIMARY KEY  ( id ),
154
				UNIQUE KEY customer_user ( customer_id, user_id )
155
			) $table_options;
156
		";
157
158
		/**
159
		 * Execute.
160
		 *
161
		 * @link https://developer.wordpress.org/reference/functions/dbdelta/
162
		 * @link https://github.com/WordPress/WordPress/blob/5.3/wp-admin/includes/upgrade.php#L2538-L2915
163
		 */
164
		\dbDelta( $queries );
165
	}
166
167
	/**
168
	 * Add foreign keys.
169
	 *
170
	 * @return void
171
	 */
172
	private function add_foreign_keys() {
173
		global $wpdb;
174
175
		/**
176
		 * Foreign keys.
177
		 *
178
		 * @link https://core.trac.wordpress.org/ticket/19207
179
		 * @link https://dev.mysql.com/doc/refman/5.6/en/create-table-foreign-keys.html
180
		 */
181
		$data = array(
182
			(object) array(
183
				'table' => $wpdb->pronamic_pay_mollie_profiles,
184
				'name'  => 'fk_profile_organization_id',
185
				'query' => "
186
					ALTER TABLE  $wpdb->pronamic_pay_mollie_profiles
187
					ADD CONSTRAINT fk_profile_organization_id
188
					FOREIGN KEY ( organization_id )
189
					REFERENCES $wpdb->pronamic_pay_mollie_organizations ( id )
190
					ON DELETE RESTRICT
191
					ON UPDATE RESTRICT
192
					;
193
				",
194
			),
195
			(object) array(
196
				'table' => $wpdb->pronamic_pay_mollie_customers,
197
				'name'  => 'fk_customer_organization_id',
198
				'query' => "
199
					ALTER TABLE $wpdb->pronamic_pay_mollie_customers
200
					ADD CONSTRAINT fk_customer_organization_id
201
					FOREIGN KEY ( organization_id )
202
					REFERENCES $wpdb->pronamic_pay_mollie_organizations ( id )
203
					ON DELETE RESTRICT
204
					ON UPDATE RESTRICT
205
					;
206
				",
207
			),
208
			(object) array(
209
				'table' => $wpdb->pronamic_pay_mollie_customers,
210
				'name'  => 'fk_customer_profile_id',
211
				'query' => "
212
					ALTER TABLE $wpdb->pronamic_pay_mollie_customers
213
					ADD CONSTRAINT fk_customer_profile_id
214
					FOREIGN KEY ( profile_id )
215
					REFERENCES $wpdb->pronamic_pay_mollie_profiles ( id )
216
					ON DELETE RESTRICT
217
					ON UPDATE RESTRICT
218
					;
219
				",
220
			),
221
			(object) array(
222
				'table' => $wpdb->pronamic_pay_mollie_customer_users,
223
				'name'  => 'fk_customer_id',
224
				'query' => "
225
					ALTER TABLE $wpdb->pronamic_pay_mollie_customer_users
226
					ADD CONSTRAINT fk_customer_id
227
					FOREIGN KEY customer_id ( customer_id )
228
					REFERENCES $wpdb->pronamic_pay_mollie_customers ( id )
229
					ON DELETE RESTRICT
230
					ON UPDATE RESTRICT
231
					;
232
				",
233
			),
234
			(object) array(
235
				'table' => $wpdb->pronamic_pay_mollie_customer_users,
236
				'name'  => 'fk_customer_user_id',
237
				'query' => "
238
					ALTER TABLE $wpdb->pronamic_pay_mollie_customer_users
239
					ADD CONSTRAINT fk_customer_user_id
240
					FOREIGN KEY user_id ( user_id )
241
					REFERENCES $wpdb->users ( id )
242
					ON DELETE CASCADE
243
					ON UPDATE CASCADE
244
					;
245
				",
246
			),
247
		);
248
249
		foreach ( $data as $item ) {
250
			try {
251
				$this->add_foreign_key( $item );
252
			} catch ( \Exception $e ) {
253
				// Foreign keys are not strictly required.
254
				continue;
255
			}
256
		}
257
	}
258
259
	/**
260
	 * Add specified foreign key.
261
	 *
262
	 * @param object $item Foreig key data.
263
	 * @return void
264
	 * @throws \Exception Throws exception when adding foreign key fails.
265
	 */
266
	private function add_foreign_key( $item ) {
267
		global $wpdb;
268
269
		/**
270
		 * Check if foreign key exists
271
		 *
272
		 * @link https://github.com/woocommerce/woocommerce/blob/3.9.0/includes/class-wc-install.php#L663-L681
273
		 */
274
		$result = $wpdb->get_var(
275
			$wpdb->prepare(
276
				"
277
			SELECT COUNT(*)
278
			FROM information_schema.TABLE_CONSTRAINTS
279
			WHERE CONSTRAINT_SCHEMA = %s
280
			AND CONSTRAINT_NAME = %s
281
			AND CONSTRAINT_TYPE = 'FOREIGN KEY'
282
			AND TABLE_NAME = %s
283
			",
284
				$wpdb->dbname,
285
				$item->name,
286
				$item->table
287
			)
288
		);
289
290
		if ( null === $result ) {
291
			throw new \Exception(
292
				\sprintf(
293
					'Could not count foreign keys: %s, database error: %s.',
294
					$item->name,
295
					$wpdb->last_error
296
				)
297
			);
298
		}
299
300
		$number_constraints = \intval( $result );
301
302
		if ( 0 === $number_constraints ) {
303
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared.
304
			$result = $wpdb->query( $item->query );
305
306
			if ( false === $result ) {
307
				throw new \Exception(
308
					\sprintf(
309
						'Could not add foreign key: %s, database error: %s.',
310
						$item->name,
311
						$wpdb->last_error
312
					)
313
				);
314
			}
315
		}
316
	}
317
318
	/**
319
	 * Convert user meta.
320
	 *
321
	 * @return void
322
	 * @throws \Exception Throws exception when database update query fails.
323
	 */
324
	private function convert_user_meta() {
325
		global $wpdb;
326
327
		$query = "
328
			INSERT IGNORE INTO $wpdb->pronamic_pay_mollie_customers (
329
				mollie_id,
330
				test_mode
331
			)
332
			SELECT
333
				meta_value AS mollie_id,
334
				'_pronamic_pay_mollie_customer_id_test' = meta_key AS test_mode
335
			FROM
336
				$wpdb->usermeta
337
			WHERE
338
				meta_key IN (
339
					'_pronamic_pay_mollie_customer_id',
340
					'_pronamic_pay_mollie_customer_id_test'
341
				)
342
					AND
343
				meta_value != ''
344
			;
345
		";
346
347
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared.
348
		$result = $wpdb->query( $query );
349
350
		if ( false === $result ) {
351
			throw new \Exception(
352
				sprintf(
353
					'Could not convert user meta, database error: %s.',
354
					$wpdb->last_error
355
				)
356
			);
357
		}
358
359
		$query = "
360
			INSERT IGNORE INTO $wpdb->pronamic_pay_mollie_customer_users (
361
				customer_id,
362
				user_id
363
			)
364
			SELECT
365
				mollie_customer.id AS mollie_customer_id,
366
				wp_user.ID AS wp_user_id
367
			FROM
368
				$wpdb->pronamic_pay_mollie_customers AS mollie_customer
369
					INNER JOIN
370
				$wpdb->usermeta AS wp_user_meta
371
						ON wp_user_meta.meta_value = mollie_customer.mollie_id
372
					INNER JOIN
373
				$wpdb->users AS wp_user
374
						ON wp_user_meta.user_id = wp_user.ID
375
			WHERE
376
				wp_user_meta.meta_key IN (
377
					'_pronamic_pay_mollie_customer_id',
378
					'_pronamic_pay_mollie_customer_id_test'
379
				)
380
					AND
381
				wp_user_meta.meta_value != ''
382
			;
383
		";
384
385
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared.
386
		$result = $wpdb->query( $query );
387
388
		if ( false === $result ) {
389
			throw new \Exception(
390
				sprintf(
391
					'Could not convert user meta, database error: %s.',
392
					$wpdb->last_error
393
				)
394
			);
395
		}
396
	}
397
}
398