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 |
||
| 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 | |||
| 58 | /** |
||
| 59 | * Get the KEEPALIVE_PROPERTY |
||
| 60 | * Needed since php 5.3 cannot parse $instance->property::constant ( but parses $instance::constant ) |
||
| 61 | * |
||
| 62 | * @return string |
||
| 63 | */ |
||
| 64 | public function get_keepalive_property() { |
||
| 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 | 4 | public function get_sanitized_hierarchy_index( $full_hierarchy, $full_hierarchy_index ) { |
|
| 76 | 4 | $full_hierarchy_index = array_slice( $full_hierarchy_index, 0, count( $full_hierarchy ) - 1 ); |
|
| 77 | 4 | if ( empty( $full_hierarchy_index ) ) { |
|
| 78 | 1 | $full_hierarchy_index = array( 0 ); |
|
| 79 | } |
||
| 80 | 4 | $full_hierarchy_index = array_map( 'intval', $full_hierarchy_index ); |
|
| 81 | 4 | 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 ) { |
||
| 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 ) { |
||
| 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 ) { |
||
| 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 | 6 | public function get_storage_key( $is_simple_root_field, $full_hierarchy, $full_hierarchy_index, $value_group_index, $property ) { |
|
| 150 | |||
| 151 | /** |
||
| 152 | * Get a full storage key with optional wildcards for entry and value indexes |
||
| 153 | * |
||
| 154 | * @param bool $is_simple_root_field |
||
| 155 | * @param array<string> $full_hierarchy |
||
| 156 | * @param string $property |
||
| 157 | * @param string $wildcard |
||
| 158 | * @return string |
||
| 159 | */ |
||
| 160 | 3 | public function get_storage_key_with_index_wildcards( $is_simple_root_field, $full_hierarchy, $property = Value_Set::VALUE_PROPERTY, $wildcard = '%' ) { |
|
| 183 | |||
| 184 | /** |
||
| 185 | * Get an array of storage key patterns for use when getting values from storage |
||
| 186 | * |
||
| 187 | * @param bool $is_simple_root_field |
||
| 188 | * @param array<string> $full_hierarchy |
||
| 189 | * @return array |
||
| 190 | */ |
||
| 191 | 3 | public function get_storage_key_getter_patterns( $is_simple_root_field, $full_hierarchy ) { |
|
| 192 | 3 | $patterns = array(); |
|
| 193 | |||
| 194 | 3 | View Code Duplication | if ( $is_simple_root_field ) { |
| 195 | 1 | $key = $this->get_storage_key_for_simple_root_field( $full_hierarchy[ count( $full_hierarchy ) - 1 ] ); |
|
| 196 | 1 | $patterns[ $key ] = static::PATTERN_COMPARISON_EQUAL; |
|
| 197 | } |
||
| 198 | |||
| 199 | 3 | $parents = $full_hierarchy; |
|
| 200 | 3 | $first_parent = array_shift( $parents ); |
|
| 201 | |||
| 202 | 3 | $storage_key = static::KEY_PREFIX . $first_parent . static::SEGMENT_GLUE; |
|
| 203 | 3 | if ( empty( $parents ) ) { |
|
| 204 | 2 | $patterns[ $storage_key ] = static::PATTERN_COMPARISON_STARTS_WITH; |
|
| 205 | } else { |
||
| 206 | 1 | $key = $storage_key . implode( static::SEGMENT_VALUE_GLUE, $parents ) . static::SEGMENT_GLUE; |
|
| 207 | 1 | $patterns[ $key ] = static::PATTERN_COMPARISON_STARTS_WITH; |
|
| 208 | |||
| 209 | 1 | $key = $storage_key . implode( static::SEGMENT_VALUE_GLUE, $parents ) . static::SEGMENT_VALUE_GLUE; |
|
| 210 | 1 | $patterns[ $key ] = static::PATTERN_COMPARISON_STARTS_WITH; |
|
| 211 | } |
||
| 212 | |||
| 213 | 3 | return $patterns; |
|
| 214 | } |
||
| 215 | |||
| 216 | /** |
||
| 217 | * Get an array of storage key patterns for use when deleting values from storage |
||
| 218 | * |
||
| 219 | * @param bool $is_complex_field |
||
| 220 | * @param bool $is_simple_root_field |
||
| 221 | * @param array<string> $full_hierarchy |
||
| 222 | * @param array<int> $full_hierarchy_index |
||
| 223 | * @return array |
||
| 224 | */ |
||
| 225 | 4 | public function get_storage_key_deleter_patterns( $is_complex_field, $is_simple_root_field, $full_hierarchy, $full_hierarchy_index ) { |
|
| 226 | 4 | $full_hierarchy_index = $this->get_sanitized_hierarchy_index( $full_hierarchy, $full_hierarchy_index ); |
|
| 227 | 4 | $patterns = array(); |
|
| 228 | |||
| 229 | 4 | View Code Duplication | if ( $is_simple_root_field ) { |
| 230 | 1 | $key = $this->get_storage_key_for_simple_root_field( $full_hierarchy[ count( $full_hierarchy ) - 1 ] ); |
|
| 231 | 1 | $patterns[ $key ] = static::PATTERN_COMPARISON_EQUAL; |
|
| 232 | } |
||
| 233 | |||
| 234 | 4 | if ( $is_complex_field ) { |
|
| 235 | 2 | $patterns[ $this->get_storage_key_root( $full_hierarchy ) ] = static::PATTERN_COMPARISON_STARTS_WITH; |
|
| 236 | } else { |
||
| 237 | 2 | $patterns[ $this->get_storage_key_prefix( $full_hierarchy, $full_hierarchy_index ) ] = static::PATTERN_COMPARISON_STARTS_WITH; |
|
| 238 | } |
||
| 239 | |||
| 240 | 4 | return $patterns; |
|
| 241 | } |
||
| 242 | |||
| 243 | /** |
||
| 244 | * Convert an array of storage key patterns to a parentheses-enclosed sql comparison |
||
| 245 | * |
||
| 246 | * @param string $table_column |
||
| 247 | * @param array $patterns |
||
| 248 | * @return string |
||
| 249 | */ |
||
| 250 | 2 | public function storage_key_patterns_to_sql( $table_column, $patterns ) { |
|
| 251 | 2 | $sql = array(); |
|
| 252 | |||
| 253 | 2 | foreach ( $patterns as $storage_key => $type ) { |
|
| 254 | 2 | $comparison = ''; |
|
| 255 | switch ( $type ) { |
||
| 256 | 2 | case static::PATTERN_COMPARISON_EQUAL: |
|
| 257 | 1 | $comparison = $table_column . ' = "' . esc_sql( $storage_key ) . '"'; |
|
| 258 | 1 | break; |
|
| 259 | 2 | case static::PATTERN_COMPARISON_STARTS_WITH: |
|
| 260 | 1 | $comparison = $table_column . ' LIKE "' . esc_sql( $storage_key ) . '%"'; |
|
| 261 | 1 | break; |
|
| 262 | default: |
||
| 263 | 1 | Incorrect_Syntax_Exception::raise( 'Unsupported storage key pattern type used: "' . $type . '"' ); |
|
| 264 | break; |
||
| 265 | } |
||
| 266 | |||
| 267 | 1 | $sql[] = $comparison; |
|
| 268 | } |
||
| 269 | |||
| 270 | 1 | return ' ( ' . implode( ' OR ', $sql ) . ' ) '; |
|
| 271 | } |
||
| 272 | |||
| 273 | /** |
||
| 274 | * Check if a storage key matches any pattern |
||
| 275 | * |
||
| 276 | * @param string $storage_key |
||
| 277 | * @param array $patterns |
||
| 278 | * @return bool |
||
| 279 | */ |
||
| 280 | 8 | public function storage_key_matches_any_pattern( $storage_key, $patterns ) { |
|
| 281 | 8 | foreach ( $patterns as $key => $type ) { |
|
| 282 | switch ( $type ) { |
||
| 283 | 8 | case static::PATTERN_COMPARISON_EQUAL: |
|
| 284 | 5 | if ( $storage_key === $key ) { |
|
| 285 | 3 | return true; |
|
| 286 | } |
||
| 287 | 3 | break; |
|
| 288 | 4 | case static::PATTERN_COMPARISON_STARTS_WITH: |
|
| 289 | 3 | $key_length = strlen( $key ); |
|
| 290 | 3 | if ( substr( $storage_key, 0, $key_length ) === $key ) { |
|
| 291 | 1 | return true; |
|
| 292 | } |
||
| 293 | 2 | break; |
|
| 294 | default: |
||
| 295 | 1 | Incorrect_Syntax_Exception::raise( 'Unsupported storage key pattern type used: "' . $type . '"' ); |
|
| 296 | 4 | break; |
|
| 297 | } |
||
| 298 | } |
||
| 299 | 3 | return false; |
|
| 300 | } |
||
| 301 | |||
| 302 | /** |
||
| 303 | * Check if an array of segments has all of it's segments |
||
| 304 | * |
||
| 305 | * @param array<string> $segments_array |
||
| 306 | * @return bool |
||
| 307 | */ |
||
| 308 | 1 | public function is_segments_array_full( $segments_array ) { |
|
| 311 | |||
| 312 | /** |
||
| 313 | * Convert a storage key to an array of it's segments |
||
| 314 | * |
||
| 315 | * @param string $storage_key |
||
| 316 | * @return array<string> |
||
| 317 | */ |
||
| 318 | 1 | public function storage_key_to_segments_array( $storage_key ) { |
|
| 323 | |||
| 324 | /** |
||
| 325 | * Convert a segment to an array of it's values |
||
| 326 | * |
||
| 327 | * @param string $segment |
||
| 328 | * @return array<mixed> |
||
| 329 | */ |
||
| 330 | 1 | public function segment_to_segment_values_array( $segment ) { |
|
| 333 | |||
| 334 | /** |
||
| 335 | * Get a parsed array of storage key segments and values |
||
| 336 | * |
||
| 337 | * @param string $storage_key |
||
| 338 | * @return array |
||
| 339 | */ |
||
| 340 | 3 | public function parse_storage_key( $storage_key ) { |
|
| 369 | } |