Completed
Branch milestone/2.0 (00cc11)
by htmlBurger
04:07
created

Key_Toolset   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 332
Duplicated Lines 2.41 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 83.22%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 8
loc 332
ccs 119
cts 143
cp 0.8322
rs 9
c 1
b 0
f 0
wmc 35
lcom 1
cbo 1

14 Methods

Rating   Name   Duplication   Size   Complexity  
A get_storage_key_root() 0 5 1
A get_sanitized_hierarchy_index() 0 8 2
A get_storage_key_for_simple_root_field() 0 4 1
A get_storage_key_prefix() 0 15 1
A get_storage_key() 0 8 3
B get_storage_key_with_index_wildcards() 0 23 4
B get_storage_key_getter_patterns() 4 24 3
A get_storage_key_deleter_patterns() 4 17 3
B storage_key_patterns_to_sql() 0 22 4
B storage_key_matches_any_pattern() 0 21 6
A is_segments_array_full() 0 3 1
A storage_key_to_segments_array() 0 5 1
A segment_to_segment_values_array() 0 3 1
B parse_storage_key() 0 27 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 4
	public function get_sanitized_hierarchy_index( $full_hierarchy, $full_hierarchy_index ) {
48 4
		$full_hierarchy_index = array_slice( $full_hierarchy_index, 0, count( $full_hierarchy ) - 1 );
49 4
		if ( empty( $full_hierarchy_index ) ) {
50 1
			$full_hierarchy_index = array( 0 );
51 1
		}
52 4
		$full_hierarchy_index = array_map( 'intval', $full_hierarchy_index );
53 4
		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 6
	public function get_storage_key( $is_simple_root_field, $full_hierarchy, $full_hierarchy_index, $value_group_index, $value_key ) {
115 6
		$full_hierarchy_index = $this->get_sanitized_hierarchy_index( $full_hierarchy, $full_hierarchy_index );
116 6
		if ( $is_simple_root_field && $value_key === Value_Set::VALUE_PROPERTY ) {
117 2
			return $this->get_storage_key_for_simple_root_field( array_shift( $full_hierarchy ) );
118
		}
119 4
		$storage_key = $this->get_storage_key_prefix( $full_hierarchy, $full_hierarchy_index ) . intval( $value_group_index ) . static::SEGMENT_GLUE . $value_key;
120 4
		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 3
	public function get_storage_key_with_index_wildcards( $is_simple_root_field, $full_hierarchy, $value_key = Value_Set::VALUE_PROPERTY, $wildcard = '%' ) {
133 3
		if ( $is_simple_root_field && $value_key === Value_Set::VALUE_PROPERTY ) {
134 1
			return $this->get_storage_key_for_simple_root_field( array_shift( $full_hierarchy ) );
135
		}
136
137 2
		$parents = $full_hierarchy;
138 2
		$first_parent = array_shift( $parents );
139 2
		$hierarchy = array_slice( $full_hierarchy, 0, -1 );
140 2
		$hierarchy_index = ! empty( $hierarchy ) ? $wildcard : '0';
141 2
		$value_group_index = $wildcard;
142
143 2
		$storage_key = static::KEY_PREFIX . 
144 2
			$first_parent . 
145 2
			static::SEGMENT_GLUE . 
146 2
			implode( static::SEGMENT_VALUE_GLUE, $parents ) . 
147 2
			static::SEGMENT_GLUE . 
148 2
			$hierarchy_index . 
149 2
			static::SEGMENT_GLUE . 
150 2
			$value_group_index . 
151 2
			static::SEGMENT_GLUE . 
152 2
			$value_key;
153 2
		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 3
	public function get_storage_key_getter_patterns( $is_simple_root_field, $full_hierarchy ) {
164 3
		$patterns = array();
165
166 3 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 1
			$key = $this->get_storage_key_for_simple_root_field( $full_hierarchy[ count( $full_hierarchy ) - 1 ] );
168 1
			$patterns[ $key ] = static::PATTERN_COMPARISON_EQUAL;
169 1
		}
170
		
171 3
		$parents = $full_hierarchy;
172 3
		$first_parent = array_shift( $parents );
173
174 3
		$storage_key = static::KEY_PREFIX . $first_parent . static::SEGMENT_GLUE;
175 3
		if ( empty( $parents ) ) {
176 2
			$patterns[ $storage_key ] = static::PATTERN_COMPARISON_STARTS_WITH;
177 2
		} else {
178 1
			$key = $storage_key . implode( static::SEGMENT_VALUE_GLUE, $parents ) . static::SEGMENT_GLUE;
179 1
			$patterns[ $key ] = static::PATTERN_COMPARISON_STARTS_WITH;
180
181 1
			$key = $storage_key . implode( static::SEGMENT_VALUE_GLUE, $parents ) . static::SEGMENT_VALUE_GLUE;
182 1
			$patterns[ $key ] = static::PATTERN_COMPARISON_STARTS_WITH;
183
		}
184
185 3
		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 4
	public function get_storage_key_deleter_patterns( $is_complex_field, $is_simple_root_field, $full_hierarchy, $full_hierarchy_index ) {
198 4
		$full_hierarchy_index = $this->get_sanitized_hierarchy_index( $full_hierarchy, $full_hierarchy_index );
199 4
		$patterns = array();
200
		
201 4 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 1
			$key = $this->get_storage_key_for_simple_root_field( $full_hierarchy[ count( $full_hierarchy ) - 1 ] );
203 1
			$patterns[ $key ] = static::PATTERN_COMPARISON_EQUAL;
204 1
		}
205
		
206 4
		if ( $is_complex_field ) {
207 2
			$patterns[ $this->get_storage_key_root( $full_hierarchy ) ] = static::PATTERN_COMPARISON_STARTS_WITH;
208 2
		} else {
209 2
			$patterns[ $this->get_storage_key_prefix( $full_hierarchy, $full_hierarchy_index ) ] = static::PATTERN_COMPARISON_STARTS_WITH;
210
		}
211
212 4
		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 2
	public function storage_key_patterns_to_sql( $table_column, $patterns ) {
223 2
		$sql = array();
224
225 2
		foreach ( $patterns as $storage_key => $type ) {
226 2
			$comparison = '';
227
			switch ( $type ) {
228 2
				case static::PATTERN_COMPARISON_EQUAL:
229 1
					$comparison = $table_column . ' = "' . esc_sql( $storage_key ) . '"';
230 1
					break;
231 2
				case static::PATTERN_COMPARISON_STARTS_WITH:
232 1
					$comparison = $table_column . ' LIKE "' . esc_sql( $storage_key ) . '%"';
233 1
					break;
234 1
				default:
235 1
					Incorrect_Syntax_Exception::raise( 'Unsupported storage key pattern type used: "' . $type . '"' );
236
					break;
237 1
			}
238
239 1
			$sql[] = $comparison;
240 1
		}
241
242 1
		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 6
	public function storage_key_matches_any_pattern( $storage_key, $patterns ) {
253 6
		foreach ( $patterns as $key => $type ) {
254
			switch ( $type ) {
255 6
				case static::PATTERN_COMPARISON_EQUAL:
256 4
					if ( $storage_key === $key ) {
257 2
						return true;
258
					}
259 2
					break;
260 3
				case static::PATTERN_COMPARISON_STARTS_WITH:
261 3
					$key_length = strlen( $key );
262 3
					if ( substr( $storage_key, 0, $key_length ) === $key ) {
263 1
						return true;
264
					}
265 2
					break;
266
				default:
267
					Incorrect_Syntax_Exception::raise( 'Unsupported storage key pattern type used: "' . $type . '"' );
268
					break;
269
			}
270 3
		}
271 3
		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 1
	public function is_segments_array_full( $segments_array ) {
281 1
		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 1
	public function storage_key_to_segments_array( $storage_key ) {
291 1
		$key = substr( $storage_key, 1 ); // drop the key prefix
292 1
		$segments = explode( static::SEGMENT_GLUE, $key );
293 1
		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 1
	public function segment_to_segment_values_array( $segment ) {
303 1
		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 3
	public function parse_storage_key( $storage_key ) {
313
		$parsed = array(
314 3
			'root' => '',
315 3
			'hierarchy' => array(),
316 3
			'hierarchy_index' => array( 0 ),
317 3
			'value_index' => 0,
318 3
			'property' => Value_Set::VALUE_PROPERTY,
319 3
		);
320
321 3
		$segments = $this->storage_key_to_segments_array( $storage_key );
322 3
		$parsed['root'] = $segments[0];
323 3
		if ( $this->is_segments_array_full( $segments ) ) {
324 2
			if ( ! empty( $segments[1] ) ) {
325 1
				$parsed['hierarchy'] = explode( static::SEGMENT_VALUE_GLUE, $segments[1] );
326 1
			}
327 2
			if ( ! empty( $segments[2] ) ) {
328 2
				$parsed['hierarchy_index'] = array_map( 'intval', explode( static::SEGMENT_VALUE_GLUE, $segments[2] ) );
329 2
			}
330 2
			$parsed['value_index'] = intval( $segments[3] );
331 2
			$parsed['property'] = $segments[4];
332 2
		}
333
334
		// Add utility values
335 3
		$parsed['full_hierarchy'] = array_merge( array( $parsed['root'] ), $parsed['hierarchy'] );
336
337 3
		return $parsed;
338
	}
339
}