Completed
Push — master ( 92226c...9ce1a2 )
by Mike
07:32
created

WC_Data::read_meta_data()   C

Complexity

Conditions 8
Paths 22

Size

Total Lines 40
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 40
rs 5.3846
nc 22
cc 8
eloc 25
nop 0
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
	 * Stores meta in cache for future reads.
20
	 * A group must be set to to enable caching.
21
	 * @var string
22
	 */
23
	protected $_cache_group = '';
24
25
	/**
26
	 * Meta type. This should match up with
27
	 * the types avaiable at https://codex.wordpress.org/Function_Reference/add_metadata.
28
	 * WP defines 'post', 'user', 'comment', and 'term'.
29
	 */
30
	protected $_meta_type = 'post';
31
32
	/**
33
	 * This only needs set if you are using a custom metadata type (for example payment tokens.
34
	 * This should be the name of the field your table uses for associating meta with objects.
35
	 * For example, in payment_tokenmeta, this would be payment_token_id.
36
	 * @var string
37
	 */
38
	protected $object_id_field_for_meta = '';
39
40
	/**
41
	 * Stores additonal meta data.
42
	 * @var array
43
	 */
44
	protected $_meta_data = array();
45
46
	/**
47
	 * Internal meta keys we don't want exposed for the object.
48
	 * @var array
49
	 */
50
	protected $_internal_meta_keys = array();
51
52
	/**
53
	 * Returns the unique ID for this object.
54
	 * @return int
55
	 */
56
	abstract public function get_id();
57
58
	/**
59
	 * Returns all data for this object.
60
	 * @return array
61
	 */
62
	abstract public function get_data();
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
	 * Get All Meta Data
92
	 * @since 2.6.0
93
	 * @return array
94
	 */
95
	public function get_meta_data() {
96
		return $this->_meta_data;
97
	}
98
99
	/**
100
	 * Internal meta keys we don't want exposed as part of meta_data. This is in
101
	 * addition to all data props with _ prefix.
102
	 * @since 2.6.0
103
	 * @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...
104
	 */
105
	protected function prefix_key( $key ) {
106
		return '_' === substr( $key, 0, 1 ) ? $key : '_' . $key;
107
	}
108
109
	/**
110
	 * Internal meta keys we don't want exposed as part of meta_data. This is in
111
	 * addition to all data props with _ prefix.
112
	 * @since 2.6.0
113
	 * @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...
114
	 */
115
	protected function get_internal_meta_keys() {
116
		return array_merge( array_map( array( $this, 'prefix_key' ), array_keys( $this->_data ) ), $this->_internal_meta_keys );
117
	}
118
119
	/**
120
	 * Get Meta Data by Key.
121
	 * @since 2.6.0
122
	 * @param  string $key
123
	 * @param  bool $single return first found meta with key, or all with $key
124
	 * @return mixed
125
	 */
126
	public function get_meta( $key = '', $single = true ) {
127
		$meta_ids = array_keys( wp_list_pluck( $this->_meta_data, 'key' ), $key );
128
		$value    = '';
129
130
		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...
131
			if ( $single ) {
132
				$value = $this->_meta_data[ current( $meta_ids ) ]->value;
133
			} else {
134
				$value = array_intersect_key( $this->_meta_data, array_flip( $meta_ids ) );
135
			}
136
		}
137
138
		return $value;
139
	}
140
141
	/**
142
	 * Set all meta data from array.
143
	 * @since 2.6.0
144
	 * @param array $data Key/Value pairs
145
	 */
146
	public function set_meta_data( $data ) {
147
		if ( ! empty( $data ) && is_array( $data ) ) {
148
			foreach ( $data as $meta_id => $meta ) {
149
				$meta = (array) $meta;
150
				if ( isset( $meta['key'], $meta['value'] ) ) {
151
					$this->_meta_data[ $meta_id ] = (object) array(
152
						'key'   => $meta['key'],
153
						'value' => $meta['value'],
154
					);
155
				}
156
			}
157
		}
158
	}
159
160
	/**
161
	 * Add meta data.
162
	 * @since 2.6.0
163
	 * @param array $key Meta key
164
	 * @param array $value Meta value
165
	 * @param array $unique Should this be a unique key?
166
	 */
167
	public function add_meta_data( $key, $value, $unique = false ) {
168
		if ( $unique ) {
169
			$meta_ids = array_keys( wp_list_pluck( $this->_meta_data, 'key' ), $key );
170
			$this->_meta_data = array_diff_key( $this->_meta_data, array_fill_keys( $meta_ids, '' ) );
171
		}
172
		$this->_meta_data[ 'new-' . sizeof( $this->_meta_data ) ] = (object) array(
173
			'key'   => $key,
174
			'value' => $value,
175
		);
176
	}
177
178
	/**
179
	 * Update meta data by key or ID, if provided.
180
	 * @since 2.6.0
181
	 * @param  string $key
182
	 * @param  string $value
183
	 * @param  int $meta_id
184
	 */
185
	public function update_meta_data( $key, $value, $meta_id = '' ) {
186
		if ( $meta_id && isset( $this->_meta_data[ $meta_id ] ) ) {
187
			$this->_meta_data[ $meta_id ] = (object) array(
188
				'key'   => $key,
189
				'value' => $value,
190
			);
191
		} else {
192
			$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...
193
		}
194
	}
195
196
	/**
197
	 * Delete meta data.
198
	 * @since 2.6.0
199
	 * @param array $key Meta key
200
	 */
201
	public function delete_meta_data( $key ) {
202
		$meta_ids         = array_keys( wp_list_pluck( $this->_meta_data, 'key' ), $key );
203
		$this->_meta_data = array_diff_key( $this->_meta_data, array_fill_keys( $meta_ids, '' ) );
204
	}
205
206
	/**
207
	 * Read Meta Data from the database. Ignore any internal properties.
208
	 * @since 2.6.0
209
	 */
210
	protected function read_meta_data() {
211
		$this->_meta_data = array();
212
		$cache_loaded     = false;
213
214
		if ( ! $this->get_id() ) {
215
			return;
216
		}
217
218
		if ( ! empty ( $this->_cache_group ) ) {
219
			$cache_key   = WC_Cache_Helper::get_cache_prefix( $this->_cache_group ) . $this->get_id();
220
			$cached_meta = wp_cache_get( $cache_key, $this->_cache_group );
221
222
			if ( false !== $cached_meta ) {
223
				$this->_meta_data = $cached_meta;
224
				$cache_loaded = true;
225
			}
226
		}
227
228
		if ( ! $cache_loaded ) {
229
			global $wpdb;
230
			$db_info = $this->_get_db_info();
231
			$raw_meta_data = $wpdb->get_results( $wpdb->prepare( "
232
				SELECT " . $db_info['meta_id_field'] . ", meta_key, meta_value
233
				FROM " . $db_info['table'] . "
234
				WHERE " . $db_info['object_id_field'] . " = %d ORDER BY " . $db_info['meta_id_field'] . "
235
			", $this->get_id() ) );
236
237
			foreach ( $raw_meta_data as $meta ) {
238
				if ( in_array( $meta->meta_key, $this->get_internal_meta_keys() ) ) {
239
					continue;
240
				}
241
242
				$this->_meta_data[ $meta->{$db_info['meta_id_field']} ] = (object) array( 'key' => $meta->meta_key, 'value' => $meta->meta_value );
243
			}
244
245
			if ( ! empty ( $this->_cache_group ) ) {
246
				wp_cache_set( $cache_key, $this->_meta_data, $this->_cache_group );
247
			}
248
		}
249
	}
250
251
	/**
252
	 * Update Meta Data in the database.
253
	 * @since 2.6.0
254
	 */
255
	protected function save_meta_data() {
256
		global $wpdb;
257
		$db_info = $this->_get_db_info();
258
		$all_meta_ids = array_map( 'absint', $wpdb->get_col( $wpdb->prepare( "
259
			SELECT " . $db_info['meta_id_field'] . " FROM " . $db_info['table'] . "
260
			WHERE " . $db_info['object_id_field'] . " = %d", $this->get_id() ) . "
261
			AND meta_key NOT IN ('" . implode( "','", array_map( 'esc_sql', $this->get_internal_meta_keys() ) ) . "');
262
		" ) );
263
		$set_meta_ids = array();
264
265
		foreach ( $this->_meta_data as $meta_id => $meta ) {
266
			if ( 'new' === substr( $meta_id, 0, 3 ) ) {
267
				$set_meta_ids[] = add_metadata( $this->_meta_type, $this->get_id(), $meta->key, $meta->value, false );
268
			} else {
269
				update_metadata_by_mid( $this->_meta_type, $meta_id, $meta->value, $meta->key );
270
				$set_meta_ids[] = absint( $meta_id );
271
			}
272
		}
273
274
		// Delete no longer set meta data
275
		$delete_meta_ids = array_diff( $all_meta_ids, $set_meta_ids );
276
277
		foreach ( $delete_meta_ids as $meta_id ) {
278
			delete_metadata_by_mid( $this->_meta_type, $meta_id );
279
		}
280
281
		if ( ! empty ( $this->_cache_group ) ) {
282
			WC_Cache_Helper::incr_cache_prefix( $this->_cache_group );
283
		}
284
		$this->read_meta_data();
285
	}
286
287
	/**
288
	 * Table structure is slightly different between meta types, this function will return what we need to know.
289
	 * @since 2.6.0
290
	 * @return array Array elements: table, object_id_field, meta_id_field
291
	 */
292
	protected function _get_db_info() {
293
		global $wpdb;
294
295
		$meta_id_field   = 'meta_id'; // for some reason users calls this umeta_id so we need to track this as well.
296
		$table           = $wpdb->prefix;
297
298
		// If we are dealing with a type of metadata that is not a core type, the table should be prefixed.
299
		if ( ! in_array( $this->_meta_type, array( 'post', 'user', 'comment', 'term' ) ) ) {
300
			$table .= 'woocommerce_';
301
		}
302
303
		$table .= $this->_meta_type . 'meta';
304
		$object_id_field = $this->_meta_type . '_id';
305
306
		// Figure out our field names.
307
		if ( 'user' === $this->_meta_type ) {
308
			$meta_id_field   = 'umeta_id';
309
		}
310
311
		if ( ! empty( $this->object_id_field_for_meta ) ) {
312
			$object_id_field = $this->object_id_field_for_meta;
313
		}
314
315
		return array(
316
			'table'           => $table,
317
			'object_id_field' => $object_id_field,
318
			'meta_id_field'   => $meta_id_field,
319
		);
320
	}
321
322
}
323