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 ) { |
|
|
|
|
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 ) { |
|
|
|
|
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
|
|
|
} |
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.