Completed
Push — milestone/2.0 ( 9667df...be4281 )
by
unknown
05:12
created

Key_Toolset::parse_storage_key()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 18
nc 5
nop 1
dl 0
loc 27
ccs 0
cts 3
cp 0
crap 20
rs 8.5806
c 0
b 0
f 0
1
<?php
2
3
namespace Carbon_Fields\Key_Toolset;
4
5
use \Carbon_Fields\Value_Set\Value_Set;
6
use \Carbon_Fields\Exception\Incorrect_Syntax_Exception;
7
8
class Key_Toolset {
9
10
	/**
11
	 * Prefix appended to all keys
12
	 */
13
	const KEY_PREFIX = '_';
14
15
	/**
16
	 * Glue characters between segments in keys
17
	 */
18
	const SEGMENT_GLUE = '|';
19
20
	/**
21
	 * Glue characters between segments values in keys
22
	 */
23
	const SEGMENT_VALUE_GLUE = ':';
24
25
	/**
26
	 * Number of segments in a key
27
	 */
28
	const TOTAL_SEGMENTS = 5;
29
30
	/**
31
	 * "Equal" storage key pattern comparison type constant
32
	 */
33
	const PATTERN_COMPARISON_EQUAL = '=';
34
35
	/**
36
	 * "Starts with" storage key pattern comparison type constant
37
	 */
38
	const PATTERN_COMPARISON_STARTS_WITH = '^';
39
40
	/**
41
	 * Get sanitized hierarchy index for hierarchy
42
	 *
43
	 * @param array<string> $full_hierarchy
44
	 * @param array<int> $full_hierarchy_index
45
	 * @return array<int>
46
	 */
47
	public function get_sanitized_hierarchy_index( $full_hierarchy, $full_hierarchy_index ) {
48
		$full_hierarchy_index = array_slice( $full_hierarchy_index, 0, count( $full_hierarchy ) - 1 );
49
		if ( empty( $full_hierarchy_index ) ) {
50
			$full_hierarchy_index = array( 0 );
51
		}
52
		$full_hierarchy_index = array_map( 'intval', $full_hierarchy_index );
53
		return $full_hierarchy_index;
54
	}
55
56
	/**
57
	 * Get a storage key for a simple root field
58
	 *
59
	 * @param string $field_base_name
60
	 * @return string
61
	 */
62
	protected function get_storage_key_for_simple_root_field( $field_base_name ) {
63
		$storage_key = static::KEY_PREFIX . $field_base_name;
64
		return $storage_key;
65
	}
66
67
	/**
68
	 * Get a storage key for the root field
69
	 * Suitable for deleting entire trees of values (e.g. Complex_Field)
70
	 *
71
	 * @param array $full_hierarchy
72
	 * @return string
73
	 */
74
	protected function get_storage_key_root( $full_hierarchy ) {
75
		$first_parent = array_shift( $full_hierarchy );
76
		$storage_key = static::KEY_PREFIX . $first_parent . static::SEGMENT_GLUE;
77
		return $storage_key;
78
	}
79
80
	/**
81
	 * Get a storage key up to the hierarchy index segment
82
	 * Suitable for getting and deleting multiple values for a single field
83
	 *
84
	 * @param array<string> $full_hierarchy
85
	 * @param array<int> $full_hierarchy_index
86
	 * @return string
87
	 */
88
	protected function get_storage_key_prefix( $full_hierarchy, $full_hierarchy_index ) {
89
		$full_hierarchy_index = $this->get_sanitized_hierarchy_index( $full_hierarchy, $full_hierarchy_index );
90
		$parents = $full_hierarchy;
91
		$first_parent = array_shift( $parents );
92
93
		$storage_key = static::KEY_PREFIX . 
94
			$first_parent . 
95
			static::SEGMENT_GLUE . 
96
			implode( static::SEGMENT_VALUE_GLUE, $parents ) . 
97
			static::SEGMENT_GLUE . 
98
			implode( static::SEGMENT_VALUE_GLUE, $full_hierarchy_index ) . 
99
			static::SEGMENT_GLUE;
100
101
		return $storage_key;
102
	}
103
104
	/**
105
	 * Get a full storage key for a single field value
106
	 *
107
	 * @param bool $is_simple_root_field
108
	 * @param array<string> $full_hierarchy
109
	 * @param array<int> $full_hierarchy_index
110
	 * @param int $value_group_index
111
	 * @param string $value_key
112
	 * @return string
113
	 */
114
	public function get_storage_key( $is_simple_root_field, $full_hierarchy, $full_hierarchy_index, $value_group_index, $value_key ) {
115
		$full_hierarchy_index = $this->get_sanitized_hierarchy_index( $full_hierarchy, $full_hierarchy_index );
116
		if ( $is_simple_root_field && $value_key === Value_Set::VALUE_PROPERTY ) {
117
			return $this->get_storage_key_for_simple_root_field( array_shift( $full_hierarchy ) );
118
		}
119
		$storage_key = $this->get_storage_key_prefix( $full_hierarchy, $full_hierarchy_index ) . intval( $value_group_index ) . static::SEGMENT_GLUE . $value_key;
120
		return $storage_key;
121
	}
122
123
	/**
124
	 * Get a full storage key with optional wildcards for entry and value indexes
125
	 *
126
	 * @param bool $is_simple_root_field
127
	 * @param array<string> $full_hierarchy
128
	 * @param string $value_key
129
	 * @param string $wildcard
130
	 * @return string
131
	 */
132
	public function get_storage_key_with_index_wildcards( $is_simple_root_field, $full_hierarchy, $value_key = Value_Set::VALUE_PROPERTY, $wildcard = '%' ) {
133
		if ( $is_simple_root_field && $value_key === Value_Set::VALUE_PROPERTY ) {
134
			return $this->get_storage_key_for_simple_root_field( array_shift( $full_hierarchy ) );
135
		}
136
137
		$parents = $full_hierarchy;
138
		$first_parent = array_shift( $parents );
139
		$hierarchy = array_slice( $full_hierarchy, 0, -1 );
140
		$hierarchy_index = ! empty( $hierarchy ) ? $wildcard : '0';
141
		$value_group_index = $wildcard;
142
143
		$storage_key = static::KEY_PREFIX . 
144
			$first_parent . 
145
			static::SEGMENT_GLUE . 
146
			implode( static::SEGMENT_VALUE_GLUE, $parents ) . 
147
			static::SEGMENT_GLUE . 
148
			$hierarchy_index . 
149
			static::SEGMENT_GLUE . 
150
			$value_group_index . 
151
			static::SEGMENT_GLUE . 
152
			$value_key;
153
		return $storage_key;
154
	}
155
156
	/**
157
	 * Get an array of storage key patterns for use when getting values from storage
158
	 *
159
	 * @param bool $is_simple_root_field
160
	 * @param array<string> $full_hierarchy
161
	 * @return array
162
	 */
163
	public function get_storage_key_getter_patterns( $is_simple_root_field, $full_hierarchy ) {
164
		$patterns = array();
165
166 View Code Duplication
		if ( $is_simple_root_field ) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across 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...
167
			$key = $this->get_storage_key_for_simple_root_field( $full_hierarchy[ count( $full_hierarchy ) - 1 ] );
168
			$patterns[ $key ] = static::PATTERN_COMPARISON_EQUAL;
169
		}
170
		
171
		$parents = $full_hierarchy;
172
		$first_parent = array_shift( $parents );
173
174
		$storage_key = static::KEY_PREFIX . $first_parent . static::SEGMENT_GLUE;
175
		if ( empty( $parents ) ) {
176
			$patterns[ $storage_key ] = static::PATTERN_COMPARISON_STARTS_WITH;
177
		} else {
178
			$key = $storage_key . implode( static::SEGMENT_VALUE_GLUE, $parents ) . static::SEGMENT_GLUE;
179
			$patterns[ $key ] = static::PATTERN_COMPARISON_STARTS_WITH;
180
181
			$key = $storage_key . implode( static::SEGMENT_VALUE_GLUE, $parents ) . static::SEGMENT_VALUE_GLUE;
182
			$patterns[ $key ] = static::PATTERN_COMPARISON_STARTS_WITH;
183
		}
184
185
		return $patterns;
186
	}
187
188
	/**
189
	 * Get an array of storage key patterns for use when deleting values from storage
190
	 *
191
	 * @param bool $is_complex_field
192
	 * @param bool $is_simple_root_field
193
	 * @param array<string> $full_hierarchy
194
	 * @param array<int> $full_hierarchy_index
195
	 * @return array
196
	 */
197
	public function get_storage_key_deleter_patterns( $is_complex_field, $is_simple_root_field, $full_hierarchy, $full_hierarchy_index ) {
198
		$full_hierarchy_index = $this->get_sanitized_hierarchy_index( $full_hierarchy, $full_hierarchy_index );
199
		$patterns = array();
200
		
201 View Code Duplication
		if ( $is_simple_root_field ) {
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across 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...
202
			$key = $this->get_storage_key_for_simple_root_field( $full_hierarchy[ count( $full_hierarchy ) - 1 ] );
203
			$patterns[ $key ] = static::PATTERN_COMPARISON_EQUAL;
204
		}
205
		
206
		if ( $is_complex_field ) {
207
			$patterns[ $this->get_storage_key_root( $full_hierarchy ) ] = static::PATTERN_COMPARISON_STARTS_WITH;
208
		} else {
209
			$patterns[ $this->get_storage_key_prefix( $full_hierarchy, $full_hierarchy_index ) ] = static::PATTERN_COMPARISON_STARTS_WITH;
210
		}
211
212
		return $patterns;
213
	}
214
215
	/**
216
	 * Convert an array of storage key patterns to a parentheses-enclosed sql comparison
217
	 *
218
	 * @param string $table_column
219
	 * @param array $patterns
220
	 * @return string
221
	 */
222
	public function storage_key_patterns_to_sql( $table_column, $patterns ) {
223
		$sql = array();
224
225
		foreach ( $patterns as $storage_key => $type ) {
226
			$comparison = '';
227
			switch ( $type ) {
228
				case static::PATTERN_COMPARISON_EQUAL:
229
					$comparison = $table_column . ' = "' . esc_sql( $storage_key ) . '"';
230
					break;
231
				case static::PATTERN_COMPARISON_STARTS_WITH:
232
					$comparison = $table_column . ' LIKE "' . esc_sql( $storage_key ) . '%"';
233
					break;
234
				default:
235
					Incorrect_Syntax_Exception::raise( 'Unsupported storage key pattern type used: "' . $type . '"' );
236
					break;
237
			}
238
239
			$sql[] = $comparison;
240
		}
241
242
		return ' ( ' . implode( ' OR ', $sql ) . ' ) ';
243
	}
244
245
	/**
246
	 * Check if a storage key matches any pattern
247
	 * 
248
	 * @param string $storage_key
249
	 * @param array $patterns
250
	 * @return bool
251
	 */
252
	public function storage_key_matches_any_pattern( $storage_key, $patterns ) {
253
		foreach ( $patterns as $key => $type ) {
254
			switch ( $type ) {
255
				case static::PATTERN_COMPARISON_EQUAL:
256
					if ( $storage_key === $key ) {
257
						return true;
258
					}
259
					break;
260
				case static::PATTERN_COMPARISON_STARTS_WITH:
261
					$key_length = strlen( $key );
262
					if ( substr( $storage_key, 0, $key_length ) === $key ) {
263
						return true;
264
					}
265
					break;
266
				default:
267
					Incorrect_Syntax_Exception::raise( 'Unsupported storage key pattern type used: "' . $type . '"' );
268
					break;
269
			}
270
		}
271
		return false;
272
	}
273
274
	/**
275
	 * Check if an array of segments has all of it's segments
276
	 * 
277
	 * @param array<string> $segments_array
278
	 * @return bool
279
	 */
280
	public function is_segments_array_full( $segments_array ) {
281
		return ( count( $segments_array ) === static::TOTAL_SEGMENTS );
282
	}
283
284
	/**
285
	 * Convert a storage key to an array of it's segments
286
	 * 
287
	 * @param string $storage_key
288
	 * @return array<string>
289
	 */
290
	public function storage_key_to_segments_array( $storage_key ) {
291
		$key = substr( $storage_key, 1 ); // drop the key prefix
292
		$segments = explode( static::SEGMENT_GLUE, $key );
293
		return $segments;
294
	}
295
296
	/**
297
	 * Convert a segment to an array of it's values
298
	 * 
299
	 * @param string $segment
300
	 * @return array<mixed>
301
	 */
302
	public function segment_to_segment_values_array( $segment ) {
303
		return explode( static::SEGMENT_VALUE_GLUE , $segment );
304
	}
305
306
	/**
307
	 * Get a parsed array of storage key segments and values
308
	 * 
309
	 * @param string $storage_key
310
	 * @return array
311
	 */
312
	public function parse_storage_key( $storage_key ) {
313
		$parsed = array(
314
			'root' => '',
315
			'hierarchy' => array(),
316
			'hierarchy_index' => array( 0 ),
317
			'value_index' => 0,
318
			'property' => Value_Set::VALUE_PROPERTY,
319
		);
320
321
		$segments = $this->storage_key_to_segments_array( $storage_key );
322
		$parsed['root'] = $segments[0];
323
		if ( $this->is_segments_array_full( $segments ) ) {
324
			if ( ! empty( $segments[1] ) ) {
325
				$parsed['hierarchy'] = explode( static::SEGMENT_VALUE_GLUE, $segments[1] );
326
			}
327
			if ( ! empty( $segments[2] ) ) {
328
				$parsed['hierarchy_index'] = array_map( 'intval', explode( static::SEGMENT_VALUE_GLUE, $segments[2] ) );
329
			}
330
			$parsed['value_index'] = intval( $segments[3] );
331
			$parsed['property'] = $segments[4];
332
		}
333
334
		// Add utility values
335
		$parsed['full_hierarchy'] = array_merge( array( $parsed['root'] ), $parsed['hierarchy'] );
336
337
		return $parsed;
338
	}
339
}