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

Legacy_Storage_Service::enabled()   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 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Carbon_Fields\Service;
4
5
use Carbon_Fields\Field\Field;
6
use Carbon_Fields\Container\Container;
7
use Carbon_Fields\Container\Repository as ContainerRepository;
8
use Carbon_Fields\Value_Set\Value_Set;
9
use Carbon_Fields\Toolset\Key_Toolset;
10
use Carbon_Fields\Datastore\Datastore_Interface;
11
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
12
13
/*
14
 * Service which provides the ability to do meta queries for multi-value fields and nested fields
15
 */
16
class Legacy_Storage_Service extends Service {
17
18
	/**
19
	 * Contaier repository to fetch fields from
20
	 * 
21
	 * @var ContainerRepository
22
	 */
23
	protected $container_repository;
24
25
	/**
26
	 * Key Toolset for key generation and comparison utilities
27
	 * 
28
	 * @var Key_Toolset
29
	 */
30
	protected $key_toolset;
31
32
	/**
33
	 * List of special key suffixes that the Map field uses so save extra data
34
	 * 
35
	 * @var array
36
	 */
37
	protected $map_keys = array( 'lat', 'lng', 'zoom', 'address' );
38
39
	/**
40
	 * Cache of converted storage arrays
41
	 * 
42
	 * @var array
43
	 */
44
	protected $storage_array_cache = array();
45
46
	/**
47
	 * Service constructor
48
	 * 
49
	 * @param ContainerRepository $container_repository
50
	 */
51
	public function __construct( ContainerRepository $container_repository, Key_Toolset $key_toolset ) {
52
		$this->container_repository = $container_repository;
53
		$this->key_toolset = $key_toolset;
54
	}
55
56
	/**
57
	 * Enable the service
58
	 */
59
	protected function enabled() {
60
		add_filter( 'crb_datastore_storage_array', array( $this, 'filter_storage_array' ), 10, 3 );
61
	}
62
63
	/**
64
	 * Disable the service
65
	 */
66
	protected function disabled() {
67
		remove_filter( 'crb_datastore_storage_array', array( $this, 'filter_storage_array' ), 10 );
68
	}
69
70
	/**
71
	 * Check if a key is a legacy map property key
72
	 *
73
	 * @param string $key
74
	 * @return bool
75
	 */
76
	protected function is_legacy_map_key( $key ) {
77
		$map_regex = array_map( function( $map_key ) {
78
			return preg_quote( $map_key, '/' );
79
		}, $this->map_keys );
80
		$map_regex = '/-(' . implode( '|', $map_regex ) . ')$/';
81
		return (bool) preg_match( $map_regex, $key );
82
	}
83
84
	/**
85
	 * Return container instance which uses the passed datastore
86
	 * 
87
	 * @param  Datastore_Interface $datastore
88
	 * @return Container
89
	 */
90
	protected function get_container_for_datastore( Datastore_Interface $datastore ) {
91
		$containers = $this->container_repository->get_containers();
92
		foreach ( $containers as $container ) {
93
			if ( $container->get_datastore() === $datastore ) {
94
				return $container;
95
			}
96
		}
97
		return null;
98
	}
99
100
	/**
101
	 * Get a nested array of field_group permutations suitable for old key parsing
102
	 * 
103
	 * @param  array $fields
104
	 * @return array
105
	 */
106
	protected function get_field_group_permutations( $fields ) {
107
		$permutations = array();
108
109
		foreach ( $fields as $field ) {
110
			if ( is_a( $field, 'Carbon_Fields\\Field\\Complex_Field' ) ) {
111
				$group_names = $field->get_group_names();
112
				foreach ( $group_names as $group_name ) {
113
					$group = $field->get_group_by_name( $group_name );
114
					if ( ! $group ) {
115
						continue;
116
					}
117
					$permutations[] = array(
118
						'field' => $field->get_base_name(),
119
						'group' => $group_name,
120
						'children' => $this->get_field_group_permutations( $group->get_fields() ),
121
					);
122
				}
123
			} else {
124
				$permutations[] = array(
125
					'field' => $field->get_base_name(),
126
					'group' => '',
127
					'children' => array(),
128
				);
129
			}
130
		}
131
132
		return $permutations;
133
	}
134
135
	/**
136
	 * Get array of database table details for datastore
137
	 * 
138
	 * @param Datastore_Interface $datastore
139
	 * @return array
140
	 */
141
	protected function get_table_details_for_datastore( Datastore_Interface $datastore ) {
142
		global $wpdb;
143
144
		$details = array(
145
			'prefix' => '_',
146
			'table_name' => '',
147
			'table_id_column' => '',
148
			'table_key_column' => '',
149
			'table_value_column' => '',
150
		);
151
152
		if ( is_a( $datastore, 'Carbon_Fields\\Datastore\\Theme_Options_Datastore' ) ) {
153
			$details['prefix'] = '';
154
			$details['table_name'] = $wpdb->options;
155
			$details['table_key_column'] = 'option_name';
156
			$details['table_value_column'] = 'option_value';
157
		} else if ( is_a( $datastore, 'Carbon_Fields\\Datastore\\Meta_Datastore' ) ) {
158
			$details['table_name'] = $datastore->get_table_name();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Carbon_Fields\Datastore\Datastore_Interface as the method get_table_name() does only exist in the following implementations of said interface: Carbon_Fields\Datastore\Comment_Meta_Datastore, Carbon_Fields\Datastore\Meta_Datastore, Carbon_Fields\Datastore\Nav_Menu_Item_Datastore, Carbon_Fields\Datastore\Post_Meta_Datastore, Carbon_Fields\Datastore\Term_Meta_Datastore, Carbon_Fields\Datastore\User_Meta_Datastore.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
159
			$details['table_id_column'] = $datastore->get_table_field_name();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Carbon_Fields\Datastore\Datastore_Interface as the method get_table_field_name() does only exist in the following implementations of said interface: Carbon_Fields\Datastore\Comment_Meta_Datastore, Carbon_Fields\Datastore\Meta_Datastore, Carbon_Fields\Datastore\Nav_Menu_Item_Datastore, Carbon_Fields\Datastore\Post_Meta_Datastore, Carbon_Fields\Datastore\Term_Meta_Datastore, Carbon_Fields\Datastore\User_Meta_Datastore.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
160
			$details['table_key_column'] = 'meta_key';
161
			$details['table_value_column'] = 'meta_value';
162
		}
163
164
		return $details;
165
	}
166
167
	/**
168
	 * Get array of sql comparisons for field
169
	 * 
170
	 * @param  Field  $field
171
	 * @param  string $key_prefix
172
	 * @param  string $key_column
173
	 * @return array<string>
174
	 */
175
	protected function get_legacy_sql_comparisons_for_field( Field $field, $key_prefix, $key_column ) {
176
		$field_key_pattern = $key_prefix . $field->get_base_name();
177
		$comparisons = array();
178
179
		if ( is_a( $field, 'Carbon_Fields\\Field\\Complex_Field' ) ) {
180
			$groups = $field->get_group_names();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Carbon_Fields\Field\Field as the method get_group_names() does only exist in the following sub-classes of Carbon_Fields\Field\Field: Carbon_Fields\Field\Complex_Field. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
181
			foreach ( $groups as $group_name ) {
182
				$underscored_group_name = preg_replace( '/^_{0,1}/', '_', $group_name ); // ensure first character is underscore
183
				$comparisons[] = ' `' . $key_column . '` LIKE "' . esc_sql( $field_key_pattern . $underscored_group_name . '-' ) . '%" ';
184
			}
185
		} else {
186
			$comparisons[] = ' `' . $key_column . '` = "' . esc_sql( $field_key_pattern ) . '" ';
187
		}
188
189
		if ( is_a( $field, 'Carbon_Fields\\Field\\Map_Field' ) ) {
190
			foreach ( $this->map_keys as $map_key ) {
191
				$comparisons[] = ' `' . $key_column . '` = "' . esc_sql( $field_key_pattern . '-' . $map_key ) . '" ';
192
			}
193
		}
194
195
		return $comparisons;
196
	}
197
198
	/**
199
	 * Get a key-value array of legacy values for fields in the container of the passed datastore
200
	 *
201
	 * @param Container $container
202
	 * @return array
203
	 */
204
	protected function get_legacy_storage_array_from_database( Container $container ) {
205
		global $wpdb;
206
207
		$datastore = $container->get_datastore();
208
		$table = $this->get_table_details_for_datastore( $datastore );
209
210
		if ( $table['table_id_column'] && ! $datastore->get_id() ) {
211
			return array(); // bail as we have an ID column but no ID to compare with ( e.g. we are in a "create" view )
212
		}
213
214
		$comparisons = array();
215
		$container_fields = $container->get_fields();
216
		foreach ( $container_fields as $field ) {
217
			$comparisons = array_merge( $comparisons, $this->get_legacy_sql_comparisons_for_field( $field, $table['prefix'], $table['table_key_column'] ) );
218
		}
219
220
		if ( empty( $comparisons ) ) {
221
			return array(); // no comparisons to fetch with
222
		}
223
224
		$where_clause = ' ( ' . implode( ' OR ', $comparisons ) . ' ) ';
225
		if ( $table['table_id_column'] ) {
226
			$where_clause = ' `' . $table['table_id_column'] . '` = ' . $datastore->get_id() . ' AND ' . $where_clause;
227
		}
228
		$query = '
229
			SELECT `' . $table['table_key_column'] . '` AS `key`, `' . $table['table_value_column'] . '` AS `value`
230
			FROM `' . $table['table_name'] . '`
231
			WHERE ' . $where_clause . '
232
		';
233
234
		$raw_results = $wpdb->get_results( $query );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
235
236
		$results = array();
237
		foreach ( $raw_results as $result ) {
238
			$results[ $result->key ] = $result->value;
239
		}
240
241
		return $results;
242
	}
243
244
	/**
245
	 * Get a key-value array of CF 1.5 values for fields in the container of the passed datastore
246
	 * 
247
	 * @param  Datastore_Interface $datastore
248
	 * @return array
249
	 */
250
	public function get_legacy_storage_array( Datastore_Interface $datastore ) {
251
		$container = $this->get_container_for_datastore( $datastore );
252
		if ( ! $container ) {
253
			return array(); // unhandled datastore type or no registered containers
254
		}
255
256
		if ( ! isset( $this->storage_array_cache[ $container->id ] ) ) {
257
			$this->storage_array_cache[ $container->id ] = $this->get_legacy_storage_array_from_database( $container );
258
		}
259
260
		return $this->storage_array_cache[ $container->id ];
261
	}
262
263
	/**
264
	 * Get expanded value for key from legacy storage array
265
	 * 
266
	 * @param string $key Legacy key to fetch additional values for
267
	 * @param array $legacy_storage_array key=>value array of legacy data
268
	 * @return mixed
269
	 */
270
	protected function get_value_for_legacy_key( $key, $legacy_storage_array ) {
271
		$value = isset( $legacy_storage_array[ $key ] ) ? $legacy_storage_array[ $key ] : '';
272
273
		$first_map_key = $this->map_keys[0];
274
		if ( isset( $legacy_storage_array[ $key . '-' . $first_map_key ] ) ) {
275
			$value = array(
276
				Value_Set::VALUE_PROPERTY => $value,
277
			);
278
		
279
			foreach ( $this->map_keys as $map_key ) {
280
				$value[ $map_key ] = $legacy_storage_array[ $key . '-' . $map_key ];
281
			}
282
		}
283
284
		return $value;
285
	}
286
287
	/**
288
	 * Convert legacy storage rows to array of row descriptors
289
	 * 
290
	 * @param array $legacy_storage_array
291
	 * @param array $field_group_permutations
292
	 * @return array
293
	 */
294
	protected function legacy_storage_rows_to_row_descriptors( $legacy_storage_array, $field_group_permutations ) {
295
		$row_descriptors = array();
296
297
		foreach ( $legacy_storage_array as $key => $value ) {
298
			if ( $this->is_legacy_map_key( $key ) ) {
299
				continue; // skip legacy map keys as they are handled separately through values
300
			}
301
302
			$value = $this->get_value_for_legacy_key( $key, $legacy_storage_array );
303
			$row_descriptors[] = $this->legacy_storage_row_to_row_descriptor( $key, $value, $field_group_permutations );
304
		}
305
306
		return $row_descriptors;
307
	}
308
309
	/**
310
	 * Get key segmentation regex for a field name
311
	 * 
312
	 * @param  string $field_name
313
	 * @param  string $group_name
314
	 * @return string
315
	 */
316
	protected function get_key_segmentation_regex_for_field_name( $field_name, $group_name = '' ) {
317
		$legacy_group_name = ( $group_name === '_' ) ? $group_name : '_' . $group_name;
318
319
		$regex = '/\A_?' . preg_quote( $field_name, '/' ) . '(?:_(?P<group_index>\d+))?\z/';
320
		if ( $group_name !== '' ) {
321
			$regex = '/\A_?' . preg_quote( $field_name, '/' ) . '(?:_(?P<group_index>\d+))?' . preg_quote( $legacy_group_name, '/' ) . '\z/';
322
		}
323
		return $regex;
324
	}
325
326
	/**
327
	 * Convert legacy storage row to row descriptor
328
	 * 
329
	 * @param  string $key
330
	 * @param  string $value
331
	 * @param  array $field_group_permutations
332
	 * @return array
333
	 */
334
	protected function legacy_storage_row_to_row_descriptor( $key, $value, $field_group_permutations ) {
335
		$key_pieces = explode( '-', $key );
336
		$field_group_level = $field_group_permutations;
337
		$matched_fields = array();
338
339
		foreach ( $key_pieces as $piece ) {
340
			foreach ( $field_group_level as $permutation ) {
341
				$match_regex = $this->get_key_segmentation_regex_for_field_name( $permutation['field'], $permutation['group'] );
342
				
343
				$matches = array();
344
				if ( preg_match( $match_regex, $piece, $matches ) ) {
345
					$match_data = array(
346
						'field' => $permutation['field'],
347
						'group' => $permutation['group'],
348
						'group_index' => ( isset( $matches['group_index'] ) ? intval( $matches['group_index'] ) : -1 ),
349
					);
350
351
					$previous_match_index = count( $matched_fields ) - 1;
352
					if ( isset( $matched_fields[ $previous_match_index ] ) ) {
353
						$matched_fields[ $previous_match_index ]['group_index'] = $match_data['group_index']; // indexes are offset in CF 1.5 complex keys
354
						$match_data['group_index'] = 0;
355
					}
356
					$matched_fields[] = $match_data;
357
358
					$field_group_level = $permutation['children'];
359
					break; // break so next piece foreaches the new field_group_level
360
				}
361
			}
362
		}
363
364
		return array(
365
			'key' => $matched_fields,
366
			'value' => $value,
367
		);
368
	}
369
370
	/**
371
	 * Convert row descriptor to array of new storage key-values
372
	 * 
373
	 * @param  array $row_descriptor
374
	 * @return array
375
	 */
376
	protected function row_descriptor_to_storage_array( $row_descriptor ) {
377
		$storage_array = array();
378
379
		$hierarchy = array_map( function( $match ) {
380
			return $match['field'];
381
		}, $row_descriptor['key'] );
382
383
		$hierarchy_index = array_map( function( $match ) {
384
			return $match['group_index'];
385
		}, array_slice( $row_descriptor['key'], 0, -1 ) ); // last index is not part of the hierarchy index
386
387
		$complex_parents = array_slice( $hierarchy, 0, -1 );
388
		foreach ( $complex_parents as $level => $complex_parent ) {
389
			$complex_parent_hierarchy_index = array_slice( $hierarchy_index, 0, $level );
390
			$complex_parent_value_index = $hierarchy_index[ $level ];
391
392
			$key = $this->key_toolset->get_storage_key( false, array_slice( $hierarchy, 0, $level + 1 ), $complex_parent_hierarchy_index, $complex_parent_value_index, Value_Set::VALUE_PROPERTY );
393
			$group_name = $row_descriptor['key'][ $level ]['group'];
394
			$storage_array[ $key ] = $group_name;
395
		}
396
397
		$unserialized_value = maybe_unserialize( $row_descriptor['value'] );
398
		if ( is_array( $unserialized_value ) ) {
399
			if ( isset( $unserialized_value['value'] ) ) {
400
				// value is a key=>value array - save each property separately
401 View Code Duplication
				foreach ( $unserialized_value as $value_key => $value_item ) {
402
					$key = $this->key_toolset->get_storage_key( false, $hierarchy, $hierarchy_index, 0, $value_key );
403
					$storage_array[ $key ] = $value_item;
404
				}
405
			} else {
406
				// value is a simple array - save each value separately
407
				$i = 0;
408 View Code Duplication
				foreach ( $unserialized_value as $value_item ) {
409
					$key = $this->key_toolset->get_storage_key( false, $hierarchy, $hierarchy_index, $i, Value_Set::VALUE_PROPERTY );
410
					$storage_array[ $key ] = $value_item;
411
					$i++;
412
				}
413
			}
414
		} else if ( $unserialized_value === null ) {
415
			// no value found - add a keepalive key
416
			$key = $this->key_toolset->get_storage_key( false, $hierarchy, $hierarchy_index, 0, '_empty' );
417
			$storage_array[ $key ] = '';
418
		} else {
419
			if ( count( $hierarchy ) === 1 ) {
420
				// Add a basic key as well as simple root fields will load that instead
421
				$key = '_' . $hierarchy[0];
422
				$storage_array[ $key ] = $row_descriptor['value'];
423
			}
424
425
			$key = $this->key_toolset->get_storage_key( false, $hierarchy, $hierarchy_index, 0, Value_Set::VALUE_PROPERTY );
426
			$storage_array[ $key ] = $row_descriptor['value'];
427
		}
428
429
		return $storage_array;
430
	}
431
432
	/**
433
	 * Get all data saved for a datastore in the new key-value format
434
	 * 
435
	 * @param  Datastore_Interface $datastore
436
	 * @return array
437
	 */
438 1
	public function get_storage_array_for_datastore( Datastore_Interface $datastore ) {
439 1
		$legacy_storage_array = $this->get_legacy_storage_array( $datastore );
440 1
		if ( empty( $legacy_storage_array ) ) {
441
			return array(); // no migration data found
442
		}
443
444 1
		$container = $this->get_container_for_datastore( $datastore );
445 1
		$field_group_permutations = $this->get_field_group_permutations( $container->get_fields() );
446 1
		$row_descriptors = $this->legacy_storage_rows_to_row_descriptors( $legacy_storage_array, $field_group_permutations );
447
		
448 1
		$storage_array = array();
449 1
		foreach ( $row_descriptors as $row_descriptor ) {
450 1
			$storage_array = array_merge( $storage_array, $this->row_descriptor_to_storage_array( $row_descriptor ) );
451 1
		}
452
453 1
		return $storage_array;
454
	}
455
456
	/**
457
	 * Get array of new storage key-values matching key patterns
458
	 * 
459
	 * @param  Datastore_Interface $datastore
460
	 * @param  array $storage_key_patterns
461
	 * @return array
462
	 */
463 1
	public function get_storage_array( Datastore_Interface $datastore, $storage_key_patterns ) {
464 1
		$storage_array = $this->get_storage_array_for_datastore( $datastore );
465 1
		$matched_data = array();
466 1 View Code Duplication
		foreach ( $storage_array as $key => $value ) {
467 1
			if ( $this->key_toolset->storage_key_matches_any_pattern( $key, $storage_key_patterns ) ) {
468 1
				$matched_data[] = (object) array(
469 1
					'key' => $key,
470 1
					'value' => $value,
471
				);
472 1
			}
473 1
		}
474 1
		return $matched_data;
475
	}
476
477
	public function filter_storage_array( $storage_array, $datastore, $storage_key_patterns ) {
478
		if ( empty( $storage_array ) ) {
479
			$storage_array = $this->get_storage_array( $datastore, $storage_key_patterns );
480
		}
481
		return $storage_array;
482
	}
483
}