Completed
Push — master ( 9ce1a2...5307f1 )
by Mike
07:53
created

WC_Data::prefix_key()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
nc 2
cc 2
eloc 2
nop 1
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 16 and the first side effect is on line 3.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
* Abstract WC Data Class
8
*
9
* Implemented by classes using the same CRUD(s) pattern.
10
*
11
* @version  2.6.0
12
* @package  WooCommerce/Abstracts
13
* @category Abstract Class
14
* @author   WooThemes
15
*/
16
abstract class WC_Data {
17
18
	/**
19
	 * Core data for this object, name value pairs (name + default value).
20
	 * @var array
21
	 */
22
	protected $_data = array();
23
24
	/**
25
	 * Stores meta in cache for future reads.
26
	 * A group must be set to to enable caching.
27
	 * @var string
28
	 */
29
	protected $_cache_group = '';
30
31
	/**
32
	 * Meta type. This should match up with
33
	 * the types avaiable at https://codex.wordpress.org/Function_Reference/add_metadata.
34
	 * WP defines 'post', 'user', 'comment', and 'term'.
35
	 */
36
	protected $_meta_type = 'post';
37
38
	/**
39
	 * This only needs set if you are using a custom metadata type (for example payment tokens.
40
	 * This should be the name of the field your table uses for associating meta with objects.
41
	 * For example, in payment_tokenmeta, this would be payment_token_id.
42
	 * @var string
43
	 */
44
	protected $object_id_field_for_meta = '';
45
46
	/**
47
	 * Stores additonal meta data.
48
	 * @var array
49
	 */
50
	protected $_meta_data = array();
51
52
	/**
53
	 * Internal meta keys we don't want exposed for the object.
54
	 * @var array
55
	 */
56
	protected $_internal_meta_keys = array();
57
58
	/**
59
	 * Returns the unique ID for this object.
60
	 * @return int
61
	 */
62
	abstract public function get_id();
63
64
	/**
65
	 * Creates new object in the database.
66
	 */
67
	abstract public function create();
68
69
	/**
70
	 * Read object from the database.
71
	 * @param int ID of the object to load.
72
	 */
73
	abstract public function read( $id );
74
75
	/**
76
	 * Updates object data in the database.
77
	 */
78
	abstract public function update();
79
80
	/**
81
	 * Updates object data in the database.
82
	 */
83
	abstract public function delete();
84
85
	/**
86
	 * Save should create or update based on object existance.
87
	 */
88
	abstract public function save();
89
90
	/**
91
	 * Change data to JSON format.
92
	 * @return string Data in JSON format.
93
	 */
94
	public function __toString() {
95
		return json_encode( $this->get_data() );
96
	}
97
98
	/**
99
	 * Returns all data for this object.
100
	 * @return array
101
	 */
102
	public function get_data() {
103
		return array_merge( $this->_data, array( 'meta_data' => $this->get_meta_data() ) );
104
	}
105
106
	/**
107
	 * Get All Meta Data
108
	 * @since 2.6.0
109
	 * @return array
110
	 */
111
	public function get_meta_data() {
112
		return $this->_meta_data;
113
	}
114
115
	/**
116
	 * Internal meta keys we don't want exposed as part of meta_data. This is in
117
	 * addition to all data props with _ prefix.
118
	 * @since 2.6.0
119
	 * @return array()
0 ignored issues
show
Documentation introduced by
The doc-type array() could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

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

Loading history...
120
	 */
121
	protected function prefix_key( $key ) {
122
		return '_' === substr( $key, 0, 1 ) ? $key : '_' . $key;
123
	}
124
125
	/**
126
	 * Internal meta keys we don't want exposed as part of meta_data. This is in
127
	 * addition to all data props with _ prefix.
128
	 * @since 2.6.0
129
	 * @return array()
0 ignored issues
show
Documentation introduced by
The doc-type array() could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

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

Loading history...
130
	 */
131
	protected function get_internal_meta_keys() {
132
		return array_merge( array_map( array( $this, 'prefix_key' ), array_keys( $this->_data ) ), $this->_internal_meta_keys );
133
	}
134
135
	/**
136
	 * Get Meta Data by Key.
137
	 * @since 2.6.0
138
	 * @param  string $key
139
	 * @param  bool $single return first found meta with key, or all with $key
140
	 * @return mixed
141
	 */
142
	public function get_meta( $key = '', $single = true ) {
143
		$meta_ids = array_keys( wp_list_pluck( $this->_meta_data, 'key' ), $key );
144
		$value    = '';
145
146
		if ( $meta_ids ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $meta_ids of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
147
			if ( $single ) {
148
				$value = $this->_meta_data[ current( $meta_ids ) ]->value;
149
			} else {
150
				$value = array_intersect_key( $this->_meta_data, array_flip( $meta_ids ) );
151
			}
152
		}
153
154
		return $value;
155
	}
156
157
	/**
158
	 * Set all meta data from array.
159
	 * @since 2.6.0
160
	 * @param array $data Key/Value pairs
161
	 */
162
	public function set_meta_data( $data ) {
163
		if ( ! empty( $data ) && is_array( $data ) ) {
164
			foreach ( $data as $meta_id => $meta ) {
165
				$meta = (array) $meta;
166
				if ( isset( $meta['key'], $meta['value'] ) ) {
167
					$this->_meta_data[ $meta_id ] = (object) array(
168
						'key'   => $meta['key'],
169
						'value' => $meta['value'],
170
					);
171
				}
172
			}
173
		}
174
	}
175
176
	/**
177
	 * Add meta data.
178
	 * @since 2.6.0
179
	 * @param array $key Meta key
180
	 * @param array $value Meta value
181
	 * @param array $unique Should this be a unique key?
182
	 */
183
	public function add_meta_data( $key, $value, $unique = false ) {
184
		if ( $unique ) {
185
			$meta_ids = array_keys( wp_list_pluck( $this->_meta_data, 'key' ), $key );
186
			$this->_meta_data = array_diff_key( $this->_meta_data, array_fill_keys( $meta_ids, '' ) );
187
		}
188
		$this->_meta_data[ 'new-' . sizeof( $this->_meta_data ) ] = (object) array(
189
			'key'   => $key,
190
			'value' => $value,
191
		);
192
	}
193
194
	/**
195
	 * Update meta data by key or ID, if provided.
196
	 * @since 2.6.0
197
	 * @param  string $key
198
	 * @param  string $value
199
	 * @param  int $meta_id
200
	 */
201
	public function update_meta_data( $key, $value, $meta_id = '' ) {
202
		if ( $meta_id && isset( $this->_meta_data[ $meta_id ] ) ) {
203
			$this->_meta_data[ $meta_id ] = (object) array(
204
				'key'   => $key,
205
				'value' => $value,
206
			);
207
		} else {
208
			$this->add_meta_data( $key, $value, true );
0 ignored issues
show
Documentation introduced by
$key is of type string, but the function expects a array.

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
$value is of type string, but the function expects a array.

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
true is of type boolean, but the function expects a false|array.

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...
209
		}
210
	}
211
212
	/**
213
	 * Delete meta data.
214
	 * @since 2.6.0
215
	 * @param array $key Meta key
216
	 */
217
	public function delete_meta_data( $key ) {
218
		$meta_ids         = array_keys( wp_list_pluck( $this->_meta_data, 'key' ), $key );
219
		$this->_meta_data = array_diff_key( $this->_meta_data, array_fill_keys( $meta_ids, '' ) );
220
	}
221
222
	/**
223
	 * Read Meta Data from the database. Ignore any internal properties.
224
	 * @since 2.6.0
225
	 */
226
	protected function read_meta_data() {
227
		$this->_meta_data = array();
228
		$cache_loaded     = false;
229
230
		if ( ! $this->get_id() ) {
231
			return;
232
		}
233
234
		if ( ! empty ( $this->_cache_group ) ) {
235
			$cache_key   = WC_Cache_Helper::get_cache_prefix( $this->_cache_group ) . $this->get_id();
236
			$cached_meta = wp_cache_get( $cache_key, $this->_cache_group );
237
238
			if ( false !== $cached_meta ) {
239
				$this->_meta_data = $cached_meta;
240
				$cache_loaded = true;
241
			}
242
		}
243
244
		if ( ! $cache_loaded ) {
245
			global $wpdb;
246
			$db_info = $this->_get_db_info();
247
			$raw_meta_data = $wpdb->get_results( $wpdb->prepare( "
248
				SELECT " . $db_info['meta_id_field'] . ", meta_key, meta_value
249
				FROM " . $db_info['table'] . "
250
				WHERE " . $db_info['object_id_field'] . " = %d ORDER BY " . $db_info['meta_id_field'] . "
251
			", $this->get_id() ) );
252
253
			foreach ( $raw_meta_data as $meta ) {
254
				if ( in_array( $meta->meta_key, $this->get_internal_meta_keys() ) ) {
255
					continue;
256
				}
257
258
				$this->_meta_data[ $meta->{$db_info['meta_id_field']} ] = (object) array( 'key' => $meta->meta_key, 'value' => $meta->meta_value );
259
			}
260
261
			if ( ! empty ( $this->_cache_group ) ) {
262
				wp_cache_set( $cache_key, $this->_meta_data, $this->_cache_group );
263
			}
264
		}
265
	}
266
267
	/**
268
	 * Update Meta Data in the database.
269
	 * @since 2.6.0
270
	 */
271
	protected function save_meta_data() {
272
		global $wpdb;
273
		$db_info = $this->_get_db_info();
274
		$all_meta_ids = array_map( 'absint', $wpdb->get_col( $wpdb->prepare( "
275
			SELECT " . $db_info['meta_id_field'] . " FROM " . $db_info['table'] . "
276
			WHERE " . $db_info['object_id_field'] . " = %d", $this->get_id() ) . "
277
			AND meta_key NOT IN ('" . implode( "','", array_map( 'esc_sql', $this->get_internal_meta_keys() ) ) . "');
278
		" ) );
279
		$set_meta_ids = array();
280
281
		foreach ( $this->_meta_data as $meta_id => $meta ) {
282
			if ( 'new' === substr( $meta_id, 0, 3 ) ) {
283
				$set_meta_ids[] = add_metadata( $this->_meta_type, $this->get_id(), $meta->key, $meta->value, false );
284
			} else {
285
				update_metadata_by_mid( $this->_meta_type, $meta_id, $meta->value, $meta->key );
286
				$set_meta_ids[] = absint( $meta_id );
287
			}
288
		}
289
290
		// Delete no longer set meta data
291
		$delete_meta_ids = array_diff( $all_meta_ids, $set_meta_ids );
292
293
		foreach ( $delete_meta_ids as $meta_id ) {
294
			delete_metadata_by_mid( $this->_meta_type, $meta_id );
295
		}
296
297
		if ( ! empty ( $this->_cache_group ) ) {
298
			WC_Cache_Helper::incr_cache_prefix( $this->_cache_group );
299
		}
300
		$this->read_meta_data();
301
	}
302
303
	/**
304
	 * Table structure is slightly different between meta types, this function will return what we need to know.
305
	 * @since 2.6.0
306
	 * @return array Array elements: table, object_id_field, meta_id_field
307
	 */
308
	protected function _get_db_info() {
309
		global $wpdb;
310
311
		$meta_id_field   = 'meta_id'; // for some reason users calls this umeta_id so we need to track this as well.
312
		$table           = $wpdb->prefix;
313
314
		// If we are dealing with a type of metadata that is not a core type, the table should be prefixed.
315
		if ( ! in_array( $this->_meta_type, array( 'post', 'user', 'comment', 'term' ) ) ) {
316
			$table .= 'woocommerce_';
317
		}
318
319
		$table .= $this->_meta_type . 'meta';
320
		$object_id_field = $this->_meta_type . '_id';
321
322
		// Figure out our field names.
323
		if ( 'user' === $this->_meta_type ) {
324
			$meta_id_field   = 'umeta_id';
325
		}
326
327
		if ( ! empty( $this->object_id_field_for_meta ) ) {
328
			$object_id_field = $this->object_id_field_for_meta;
329
		}
330
331
		return array(
332
			'table'           => $table,
333
			'object_id_field' => $object_id_field,
334
			'meta_id_field'   => $meta_id_field,
335
		);
336
	}
337
338
}
339