Completed
Push — trunk ( d2b1ce...f37561 )
by Justin
05:31
created

CMB2_Sanitize   D

Complexity

Total Complexity 93

Size/Duplication

Total Lines 504
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 62.77%

Importance

Changes 0
Metric Value
dl 0
loc 504
ccs 145
cts 231
cp 0.6277
rs 4.8717
c 0
b 0
f 0
wmc 93
lcom 1
cbo 2

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A __call() 0 3 1
A _default_sanitization() 0 4 2
A checkbox() 0 3 2
B text_url() 0 13 5
B colorpicker() 0 15 7
B text_email() 0 14 5
B text_money() 0 23 5
A text_date_timestamp() 0 5 2
B text_datetime_timestamp() 0 22 6
F text_datetime_timestamp_timezone() 0 80 14
A textarea() 0 3 2
A textarea_code() 0 8 2
A file() 0 13 2
A _get_group_file_value_array() 0 16 2
A _save_file_id_value() 0 15 4
A _save_utc_value() 0 3 1
A _new_supporting_field() 0 6 1
B _check_repeat() 0 21 7
A _is_empty_array() 0 7 2
D default_sanitization() 0 47 16
B _taxonomy() 0 37 4

How to fix   Complexity   

Complex Class

Complex classes like CMB2_Sanitize often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CMB2_Sanitize, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * CMB2 field sanitization
4
 *
5
 * @since  0.0.4
6
 *
7
 * @category  WordPress_Plugin
8
 * @package   CMB2
9
 * @author    WebDevStudios
10
 * @license   GPL-2.0+
11
 * @link      http://webdevstudios.com
12
 *
13
 * @method string _id()
14
 */
15
class CMB2_Sanitize {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
16
17
	/**
18
	 * A CMB field object
19
	 * @var CMB2_Field object
20
	 */
21
	public $field;
22
23
	/**
24
	 * Field's value
25
	 * @var mixed
26
	 */
27
	public $value;
28
29
	/**
30
	 * Setup our class vars
31
	 * @since 1.1.0
32
	 * @param CMB2_Field $field A CMB2 field object
33
	 * @param mixed      $value Field value
34
	 */
35 14
	public function __construct( CMB2_Field $field, $value ) {
36 14
		$this->field = $field;
37 14
		$this->value = stripslashes_deep( $value ); // get rid of those evil magic quotes
38 14
	}
39
40
	/**
41
	 * Catchall method if field's 'sanitization_cb' is NOT defined, or field type does not have a corresponding validation method
42
	 * @since  1.0.0
43
	 * @param  string $name      Non-existent method name
44
	 * @param  array  $arguments All arguments passed to the method
45
	 */
46 11
	public function __call( $name, $arguments ) {
47 11
		return $this->default_sanitization();
48
	}
49
50
	/**
51
	 * Default fallback sanitization method. Applies filters.
52
	 * @since  1.0.2
53
	 */
54 11
	public function default_sanitization() {
55
56
		/**
57
		 * This exists for back-compatibility, but validation
58
		 * is not what happens here.
59
		 * @deprecated See documentation for "cmb2_sanitize_{$this->type()}".
60
		 */
61 11
		if ( function_exists( 'apply_filters_deprecated' ) ) {
62
			$override_value = apply_filters_deprecated( "cmb2_validate_{$this->field->type()}", array( null, $this->value, $this->field->object_id, $this->field->args(), $this ), '2.0.0', "cmb2_sanitize_{$this->field->type()}" );
0 ignored issues
show
Documentation introduced by
The property $object_id is declared protected in CMB2_Base. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
63
		} else {
64 11
			$override_value = apply_filters( "cmb2_validate_{$this->field->type()}", null, $this->value, $this->field->object_id, $this->field->args(), $this );
0 ignored issues
show
Documentation introduced by
The property $object_id is declared protected in CMB2_Base. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
65
		}
66
67 11
		if ( null !== $override_value ) {
68
			return $override_value;
69
		}
70
71 11
		$sanitized_value = '';
0 ignored issues
show
Unused Code introduced by
$sanitized_value is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
72 11
		switch ( $this->field->type() ) {
73 11
			case 'wysiwyg':
74 11
			case 'textarea_small':
75 11
			case 'oembed':
76 2
				$sanitized_value = $this->textarea();
77 2
				break;
78 11
			case 'taxonomy_select':
79 11
			case 'taxonomy_radio':
80 11
			case 'taxonomy_radio_inline':
81 11
			case 'taxonomy_multicheck':
82 11
			case 'taxonomy_multicheck_inline':
83 1
				$sanitized_value = $this->_taxonomy();
84 1
				break;
85 11
			case 'multicheck':
86 11
			case 'multicheck_inline':
87 11
			case 'file_list':
88 11
			case 'group':
89
				// no filtering
90 2
				$sanitized_value = $this->value;
91 2
				break;
92 10
			default:
93
				// Handle repeatable fields array
94
				// We'll fallback to 'sanitize_text_field'
95 10
				$sanitized_value = $this->_default_sanitization();
96 10
				break;
97 11
		}
98
99 11
		return $this->_is_empty_array( $sanitized_value ) ? '' : $sanitized_value;
100
	}
101
102
	/**
103
	 * Default sanitization method, sanitize_text_field. Checks if value is array.
104
	 * @since  2.2.4
105
	 * @return mixed  Sanitized value.
106
	 */
107 10
	protected function _default_sanitization() {
108
		// Handle repeatable fields array
109 10
		return is_array( $this->value ) ? array_map( 'sanitize_text_field', $this->value ) : sanitize_text_field( $this->value );
110
	}
111
112
	/**
113
	 * Sets the object terms to the object (if not options-page) and optionally returns the sanitized term values.
114
	 * @since  2.2.4
115
	 * @return mixed  Blank value, or sanitized term values if "cmb2_return_taxonomy_values_{$cmb_id}" is true.
116
	 */
117 1
	protected function _taxonomy() {
118 1
		$sanitized_value = '';
119
120 1
		if ( ! $this->field->args( 'taxonomy' ) ) {
121
			CMB2_Utils::log_if_debug( __METHOD__, __LINE__, "{$this->field->type()} {$this->field->_id()} is missing the 'taxonomy' parameter." );
122
		} else {
123
124 1
			if ( 'options-page' !== $this->field->object_type ) {
0 ignored issues
show
Documentation introduced by
The property $object_type is declared protected in CMB2_Base. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
125 1
				$return_values = true;
126 1
			} else {
127
				wp_set_object_terms( $this->field->object_id, $this->value, $this->field->args( 'taxonomy' ) );
0 ignored issues
show
Documentation introduced by
The property $object_id is declared protected in CMB2_Base. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
128
				$return_values = false;
129
			}
130
131 1
			$cmb_id = $this->field->cmb_id;
0 ignored issues
show
Documentation introduced by
The property $cmb_id is declared protected in CMB2_Base. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
132
133
			/**
134
			 * Filter whether 'taxonomy_*' fields should return their value when being sanitized.
135
			 *
136
			 * By default, these fields do not return a value as we do not want them stored to meta
137
			 * (as they are stored as terms). This allows overriding that and is used by CMB2::get_sanitized_values().
138
			 *
139
			 * The dynamic portion of the hook, $cmb_id, refers to the this field's CMB2 box id.
140
			 *
141
			 * @since 2.2.4
142
			 *
143
			 * @param bool          $return_values By default, this is only true for 'options-page' boxes. To enable:
144
			 *                                     `add_filter( "cmb2_return_taxonomy_values_{$cmb_id}", '__return_true' );`
145
			 * @param CMB2_Sanitize $sanitizer This object.
146
			 */
147 1
			if ( apply_filters( "cmb2_return_taxonomy_values_{$cmb_id}", $return_values, $this ) ) {
148 1
				$sanitized_value = $this->_default_sanitization();
149 1
			}
150
		}
151
152 1
		return $sanitized_value;
153
	}
154
155
	/**
156
	 * Simple checkbox validation
157
	 * @since  1.0.1
158
	 * @return string|false 'on' or false
159
	 */
160
	public function checkbox() {
161
		return $this->value === 'on' ? 'on' : false;
162
	}
163
164
	/**
165
	 * Validate url in a meta value
166
	 * @since  1.0.1
167
	 * @return string        Empty string or escaped url
168
	 */
169 1
	public function text_url() {
170 1
		$protocols = $this->field->args( 'protocols' );
171
		// for repeatable
172 1
		if ( is_array( $this->value ) ) {
173
			foreach ( $this->value as $key => $val ) {
174
				$this->value[ $key ] = $val ? esc_url_raw( $val, $protocols ) : $this->field->get_default();
175
			}
176
		} else {
177 1
			$this->value = $this->value ? esc_url_raw( $this->value, $protocols ) : $this->field->get_default();
178
		}
179
180 1
		return $this->value;
181
	}
182
183
	public function colorpicker() {
184
		// for repeatable
185
		if ( is_array( $this->value ) ) {
186
			$check = $this->value;
187
			$this->value = array();
188
			foreach ( $check as $key => $val ) {
189
				if ( $val && '#' != $val ) {
190
					$this->value[ $key ] = esc_attr( $val );
191
				}
192
			}
193
		} else {
194
			$this->value = ! $this->value || '#' == $this->value ? '' : esc_attr( $this->value );
195
		}
196
		return $this->value;
197
	}
198
199
	/**
200
	 * Validate email in a meta value
201
	 * @since  1.0.1
202
	 * @return string       Empty string or sanitized email
203
	 */
204
	public function text_email() {
205
		// for repeatable
206
		if ( is_array( $this->value ) ) {
207
			foreach ( $this->value as $key => $val ) {
208
				$val = trim( $val );
209
				$this->value[ $key ] = is_email( $val ) ? $val : '';
210
			}
211
		} else {
212
			$this->value = trim( $this->value );
213
			$this->value = is_email( $this->value ) ? $this->value : '';
214
		}
215
216
		return $this->value;
217
	}
218
219
	/**
220
	 * Validate money in a meta value
221
	 * @since  1.0.1
222
	 * @return string Empty string or sanitized money value
223
	 */
224 1
	public function text_money() {
225 1
		if ( ! $this->value ) {
226 1
			return '';
227
		}
228
229 1
		global $wp_locale;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
230
231 1
		$search = array( $wp_locale->number_format['thousands_sep'], $wp_locale->number_format['decimal_point'] );
232 1
		$replace = array( '', '.' );
233
234
		// for repeatable
235 1
		if ( is_array( $this->value ) ) {
236
			foreach ( $this->value as $key => $val ) {
237
				if ( $val ) {
238
					$this->value[ $key ] = number_format_i18n( (float) str_ireplace( $search, $replace, $val ), 2 );
239
				}
240
			}
241
		} else {
242 1
			$this->value = number_format_i18n( (float) str_ireplace( $search, $replace, $this->value ), 2 );
243
		}
244
245 1
		return $this->value;
246
	}
247
248
	/**
249
	 * Converts text date to timestamp
250
	 * @since  1.0.2
251
	 * @return string Timestring
252
	 */
253
	public function text_date_timestamp() {
254
		return is_array( $this->value )
255
			? array_map( array( $this->field, 'get_timestamp_from_value' ), $this->value )
256
			: $this->field->get_timestamp_from_value( $this->value );
257
	}
258
259
	/**
260
	 * Datetime to timestamp
261
	 * @since  1.0.1
262
	 * @return string|array Timestring
263
	 */
264
	public function text_datetime_timestamp( $repeat = false ) {
265
266
		$test = is_array( $this->value ) ? array_filter( $this->value ) : '';
267
		if ( empty( $test ) ) {
268
			return '';
269
		}
270
271
		$repeat_value = $this->_check_repeat( __FUNCTION__, $repeat );
272
		if ( false !== $repeat_value ) {
273
			return $repeat_value;
274
		}
275
276
		if ( isset( $this->value['date'], $this->value['time'] ) ) {
277
			$this->value = $this->field->get_timestamp_from_value( $this->value['date'] . ' ' . $this->value['time'] );
278
		}
279
280
		if ( $tz_offset = $this->field->field_timezone_offset() ) {
281
			$this->value += (int) $tz_offset;
282
		}
283
284
		return $this->value;
285
	}
286
287
	/**
288
	 * Datetime to timestamp with timezone
289
	 * @since  1.0.1
290
	 * @return string       Timestring
291
	 */
292 2
	public function text_datetime_timestamp_timezone( $repeat = false ) {
293 2
		static $utc_values = array();
294
295 2
		$test = is_array( $this->value ) ? array_filter( $this->value ) : '';
296 2
		if ( empty( $test ) ) {
297 1
			return '';
298
		}
299
300 2
		$utc_key = $this->field->_id() . '_utc';
301
302 2
		$repeat_value = $this->_check_repeat( __FUNCTION__, $repeat );
303 2
		if ( false !== $repeat_value ) {
304 1
			if ( ! empty( $utc_values[ $utc_key ] ) ) {
305
				$this->_save_utc_value( $utc_key, $utc_values[ $utc_key ] );
306
				unset( $utc_values[ $utc_key ] );
307
			}
308
309 1
			return $repeat_value;
310
		}
311
312 2
		$tzstring = null;
313
314 2
		if ( is_array( $this->value ) && array_key_exists( 'timezone', $this->value ) ) {
315 2
			$tzstring = $this->value['timezone'];
316 2
		}
317
318 2
		if ( empty( $tzstring ) ) {
319
			$tzstring = CMB2_Utils::timezone_string();
320
		}
321
322 2
		$offset = CMB2_Utils::timezone_offset( $tzstring );
323
324 2
		if ( 'UTC' === substr( $tzstring, 0, 3 ) ) {
325 1
			$tzstring = timezone_name_from_abbr( '', $offset, 0 );
326
			/*
327
			 * timezone_name_from_abbr() returns false if not found based on offset.
328
			 * Since there are currently some invalid timezones in wp_timezone_dropdown(),
329
			 * fallback to an offset of 0 (UTC+0)
330
			 * https://core.trac.wordpress.org/ticket/29205
331
			 */
332 1
			$tzstring = false !== $tzstring ? $tzstring : timezone_name_from_abbr( '', 0, 0 );
333 1
		}
334
335 2
		$full_format = $this->field->args['date_format'] . ' ' . $this->field->args['time_format'];
336 2
		$full_date   = $this->value['date'] . ' ' . $this->value['time'];
337
338
		try {
339
340 2
			$datetime = date_create_from_format( $full_format, $full_date );
341
342 2
			if ( ! is_object( $datetime ) ) {
343
				$this->value = $utc_stamp = '';
344
			} else {
345 2
				$timestamp   = $datetime->setTimezone( new DateTimeZone( $tzstring ) )->getTimestamp();
346 2
				$utc_stamp   = $timestamp - $offset;
347 2
				$this->value = serialize( $datetime );
348
			}
349
350 2
			if ( $this->field->group ) {
351 1
				$this->value = array(
352 1
					'supporting_field_value' => $utc_stamp,
353 1
					'supporting_field_id'    => $utc_key,
354 1
					'value'                  => $this->value,
355
				);
356 1
			} else {
357
				// Save the utc timestamp supporting field
358 1
				if ( $repeat ) {
359
					$utc_values[ $utc_key ][] = $utc_stamp;
360
				} else {
361 1
					$this->_save_utc_value( $utc_key, $utc_stamp );
362
				}
363
			}
364
365 2
		} catch ( Exception $e ) {
366
			$this->value = '';
367
			CMB2_Utils::log_if_debug( __METHOD__, __LINE__, $e->getMessage() );
368
		}
369
370 2
		return $this->value;
371
	}
372
373
	/**
374
	 * Sanitize textareas and wysiwyg fields
375
	 * @since  1.0.1
376
	 * @return string       Sanitized data
377
	 */
378 2
	public function textarea() {
379 2
		return is_array( $this->value ) ? array_map( 'wp_kses_post', $this->value ) : wp_kses_post( $this->value );
380
	}
381
382
	/**
383
	 * Sanitize code textareas
384
	 * @since  1.0.2
385
	 * @return string       Sanitized data
386
	 */
387
	public function textarea_code( $repeat = false ) {
388
		$repeat_value = $this->_check_repeat( __FUNCTION__, $repeat );
389
		if ( false !== $repeat_value ) {
390
			return $repeat_value;
391
		}
392
393
		return htmlspecialchars_decode( stripslashes( $this->value ) );
394
	}
395
396
	/**
397
	 * Handles saving of attachment post ID and sanitizing file url
398
	 * @since  1.1.0
399
	 * @return string        Sanitized url
400
	 */
401 1
	public function file() {
402 1
		$file_id_key = $this->field->_id() . '_id';
403
404 1
		if ( $this->field->group ) {
405
			// Return an array with url/id if saving a group field
406 1
			$this->value = $this->_get_group_file_value_array( $file_id_key );
407 1
		} else {
408
			$this->_save_file_id_value( $file_id_key );
409
			$this->text_url();
410
		}
411
412 1
		return $this->value;
413
	}
414
415
	/**
416
	 * Gets the values for the `file` field type from the data being saved.
417
	 * @since  2.2.0
418
	 */
419 1
	public function _get_group_file_value_array( $id_key ) {
420 1
		$alldata = $this->field->group->data_to_save;
421 1
		$base_id = $this->field->group->_id();
422 1
		$i       = $this->field->group->index;
423
424
		// Check group $alldata data
425 1
		$id_val  = isset( $alldata[ $base_id ][ $i ][ $id_key ] )
426 1
			? absint( $alldata[ $base_id ][ $i ][ $id_key ] )
427 1
			: 0;
428
429
		return array(
430 1
			'value' => $this->text_url(),
431 1
			'supporting_field_value' => $id_val,
432 1
			'supporting_field_id'    => $id_key,
433 1
		);
434
	}
435
436
	/**
437
	 * Peforms saving of `file` attachement's ID
438
	 * @since  1.1.0
439
	 */
440
	public function _save_file_id_value( $file_id_key ) {
441
		$id_field = $this->_new_supporting_field( $file_id_key );
442
443
		// Check standard data_to_save data
444
		$id_val = isset( $this->field->data_to_save[ $file_id_key ] )
445
			? $this->field->data_to_save[ $file_id_key ]
446
			: null;
447
448
		// If there is no ID saved yet, try to get it from the url
449
		if ( $this->value && ! $id_val ) {
450
			$id_val = CMB2_Utils::image_id_from_url( $this->value );
451
		}
452
453
		return $id_field->save_field( $id_val );
454
	}
455
456
	/**
457
	 * Peforms saving of `text_datetime_timestamp_timezone` utc timestamp
458
	 * @since  2.2.0
459
	 */
460 1
	public function _save_utc_value( $utc_key, $utc_stamp ) {
461 1
		return $this->_new_supporting_field( $utc_key )->save_field( $utc_stamp );
462
	}
463
464
	/**
465
	 * Returns a new, supporting, CMB2_Field object based on a new field id.
466
	 * @since  2.2.0
467
	 */
468 1
	public function _new_supporting_field( $new_field_id ) {
469 1
		return $this->field->get_field_clone( array(
470 1
			'id' => $new_field_id,
471 1
			'sanitization_cb' => false,
472 1
		) );
473
	}
474
475
	/**
476
	 * If repeating, loop through and re-apply sanitization method
477
	 * @since  1.1.0
478
	 * @param  string $method Class method
479
	 * @param  bool   $repeat Whether repeating or not
480
	 * @return mixed          Sanitized value
481
	 */
482 2
	public function _check_repeat( $method, $repeat ) {
483 2
		if ( $repeat || ! $this->field->args( 'repeatable' ) ) {
484 2
			return false;
485
		}
486
487 1
		$values_array = $this->value;
488
489 1
		$new_value = array();
490 1
		foreach ( $values_array as $iterator => $this->value ) {
491 1
			if ( $this->value ) {
492 1
				$val = $this->$method( true );
493 1
				if ( ! empty( $val ) ) {
494 1
					$new_value[] = $val;
495 1
				}
496 1
			}
497 1
		}
498
499 1
		$this->value = $new_value;
500
501 1
		return empty( $this->value ) ? null : $this->value;
502
	}
503
504
	/**
505
	 * Determine if passed value is an empty array
506
	 * @since  2.0.6
507
	 * @param  mixed  $to_check Value to check
508
	 * @return boolean          Whether value is an array that's empty
509
	 */
510 11
	public function _is_empty_array( $to_check ) {
511 11
		if ( is_array( $to_check ) ) {
512 4
			$cleaned_up = array_filter( $to_check );
513 4
			return empty( $cleaned_up );
514
		}
515 9
		return false;
516
	}
517
518
}
519