Completed
Push — test/legacy-storage-service ( 13c02e...9939d7 )
by
unknown
02:29
created

Key_Toolset::get_storage_key_prefix()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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