Completed
Push — milestone/2_0/react-ui ( 360d36...2d5d55 )
by
unknown
09:55 queued 02:31
created

Key_Toolset::is_segments_array_full()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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