CMB2_Sanitize

CMB2 field sanitization.

Defined (1)

The class is defined in the following location(s).

/includes/CMB2_Sanitize.php  
  1. class CMB2_Sanitize { 
  2.  
  3. /** 
  4. * A CMB field object 
  5. * @var CMB2_Field object 
  6. */ 
  7. public $field; 
  8.  
  9. /** 
  10. * Field's value 
  11. * @var mixed 
  12. */ 
  13. public $value; 
  14.  
  15. /** 
  16. * Setup our class vars 
  17. * @since 1.1.0 
  18. * @param CMB2_Field $field A CMB2 field object 
  19. * @param mixed $value Field value 
  20. */ 
  21. public function __construct( CMB2_Field $field, $value ) { 
  22. $this->field = $field; 
  23. $this->value = stripslashes_deep( $value ); // get rid of those evil magic quotes 
  24.  
  25. /** 
  26. * Catchall method if field's 'sanitization_cb' is NOT defined, or field type does not have a corresponding validation method 
  27. * @since 1.0.0 
  28. * @param string $name Non-existent method name 
  29. * @param array $arguments All arguments passed to the method 
  30. */ 
  31. public function __call( $name, $arguments ) { 
  32. return $this->default_sanitization( $this->value ); 
  33.  
  34. /** 
  35. * Default fallback sanitization method. Applies filters. 
  36. * @since 1.0.2 
  37. */ 
  38. public function default_sanitization() { 
  39.  
  40. /** 
  41. * Filter the value before it is saved. 
  42. * The dynamic portion of the hook name, $this->field->type(), refers to the field type. 
  43. * Passing a non-null value to the filter will short-circuit saving 
  44. * the field value, saving the passed value instead. 
  45. * @param bool|mixed $override_value Sanitization/Validation override value to return. 
  46. * Default false to skip it. 
  47. * @param mixed $value The value to be saved to this field. 
  48. * @param int $object_id The ID of the object where the value will be saved 
  49. * @param array $field_args The current field's arguments 
  50. * @param object $sanitizer This `CMB2_Sanitize` object 
  51. */ 
  52. $override_value = apply_filters( "cmb2_sanitize_{$this->field->type()}", null, $this->value, $this->field->object_id, $this->field->args(), $this ); 
  53. /** 
  54. * This exists for back-compatibility, but validation 
  55. * is not what happens here. 
  56. * @deprecated See documentation above. 
  57. */ 
  58. $override_value = apply_filters( "cmb2_validate_{$this->field->type()}", $override_value, $this->value, $this->field->object_id, $this->field->args(), $this ); 
  59.  
  60. if ( null !== $override_value ) { 
  61. return $override_value; 
  62.  
  63. $sanitized_value = ''; 
  64. switch ( $this->field->type() ) { 
  65. case 'wysiwyg': 
  66. // $value = wp_kses( $this->value ); 
  67. // break; 
  68. case 'textarea_small': 
  69. $sanitized_value = $this->textarea( $this->value ); 
  70. break; 
  71. case 'taxonomy_select': 
  72. case 'taxonomy_radio': 
  73. case 'taxonomy_radio_inline': 
  74. case 'taxonomy_multicheck': 
  75. case 'taxonomy_multicheck_inline': 
  76. if ( $this->field->args( 'taxonomy' ) ) { 
  77. wp_set_object_terms( $this->field->object_id, $this->value, $this->field->args( 'taxonomy' ) ); 
  78. break; 
  79. case 'multicheck': 
  80. case 'multicheck_inline': 
  81. case 'file_list': 
  82. case 'oembed': 
  83. case 'group': 
  84. // no filtering 
  85. $sanitized_value = $this->value; 
  86. break; 
  87. default: 
  88. // Handle repeatable fields array 
  89. // We'll fallback to 'sanitize_text_field' 
  90. $sanitized_value = is_array( $this->value ) ? array_map( 'sanitize_text_field', $this->value ) : call_user_func( 'sanitize_text_field', $this->value ); 
  91. break; 
  92.  
  93. return $this->_is_empty_array( $sanitized_value ) ? '' : $sanitized_value; 
  94.  
  95. /** 
  96. * Simple checkbox validation 
  97. * @since 1.0.1 
  98. * @return string|false 'on' or false 
  99. */ 
  100. public function checkbox() { 
  101. return $this->value === 'on' ? 'on' : false; 
  102.  
  103. /** 
  104. * Validate url in a meta value 
  105. * @since 1.0.1 
  106. * @return string Empty string or escaped url 
  107. */ 
  108. public function text_url() { 
  109. $protocols = $this->field->args( 'protocols' ); 
  110. // for repeatable 
  111. if ( is_array( $this->value ) ) { 
  112. foreach ( $this->value as $key => $val ) { 
  113. $this->value[ $key ] = $val ? esc_url_raw( $val, $protocols ) : $this->field->args( 'default' ); 
  114. } else { 
  115. $this->value = $this->value ? esc_url_raw( $this->value, $protocols ) : $this->field->args( 'default' ); 
  116.  
  117. return $this->value; 
  118.  
  119. public function colorpicker() { 
  120. // for repeatable 
  121. if ( is_array( $this->value ) ) { 
  122. $check = $this->value; 
  123. $this->value = array(); 
  124. foreach ( $check as $key => $val ) { 
  125. if ( $val && '#' != $val ) { 
  126. $this->value[ $key ] = esc_attr( $val ); 
  127. } else { 
  128. $this->value = ! $this->value || '#' == $this->value ? '' : esc_attr( $this->value ); 
  129. return $this->value; 
  130.  
  131. /** 
  132. * Validate email in a meta value 
  133. * @since 1.0.1 
  134. * @return string Empty string or sanitized email 
  135. */ 
  136. public function text_email() { 
  137. // for repeatable 
  138. if ( is_array( $this->value ) ) { 
  139. foreach ( $this->value as $key => $val ) { 
  140. $val = trim( $val ); 
  141. $this->value[ $key ] = is_email( $val ) ? $val : ''; 
  142. } else { 
  143. $this->value = trim( $this->value ); 
  144. $this->value = is_email( $this->value ) ? $this->value : ''; 
  145.  
  146. return $this->value; 
  147.  
  148. /** 
  149. * Validate money in a meta value 
  150. * @since 1.0.1 
  151. * @return string Empty string or sanitized money value 
  152. */ 
  153. public function text_money() { 
  154.  
  155. global $wp_locale; 
  156.  
  157. $search = array( $wp_locale->number_format['thousands_sep'], $wp_locale->number_format['decimal_point'] ); 
  158. $replace = array( '', '.' ); 
  159.  
  160. // for repeatable 
  161. if ( is_array( $this->value ) ) { 
  162. foreach ( $this->value as $key => $val ) { 
  163. $this->value[ $key ] = number_format_i18n( (float) str_ireplace( $search, $replace, $val ), 2 ); 
  164. } else { 
  165. $this->value = number_format_i18n( (float) str_ireplace( $search, $replace, $this->value ), 2 ); 
  166.  
  167. return $this->value; 
  168.  
  169. /** 
  170. * Converts text date to timestamp 
  171. * @since 1.0.2 
  172. * @return string Timestring 
  173. */ 
  174. public function text_date_timestamp() { 
  175. return is_array( $this->value ) ? array_map( 'strtotime', $this->value ) : strtotime( $this->value ); 
  176.  
  177. /** 
  178. * Datetime to timestamp 
  179. * @since 1.0.1 
  180. * @return string Timestring 
  181. */ 
  182. public function text_datetime_timestamp( $repeat = false ) { 
  183.  
  184. $test = is_array( $this->value ) ? array_filter( $this->value ) : ''; 
  185. if ( empty( $test ) ) { 
  186. return ''; 
  187.  
  188. if ( $repeat_value = $this->_check_repeat( __FUNCTION__, $repeat ) ) { 
  189. return $repeat_value; 
  190.  
  191. $this->value = strtotime( $this->value['date'] . ' ' . $this->value['time'] ); 
  192.  
  193. if ( $tz_offset = $this->field->field_timezone_offset() ) { 
  194. $this->value += $tz_offset; 
  195.  
  196. return $this->value; 
  197.  
  198. /** 
  199. * Datetime to imestamp with timezone 
  200. * @since 1.0.1 
  201. * @return string Timestring 
  202. */ 
  203. public function text_datetime_timestamp_timezone( $repeat = false ) { 
  204.  
  205. $test = is_array( $this->value ) ? array_filter( $this->value ) : ''; 
  206. if ( empty( $test ) ) { 
  207. return ''; 
  208.  
  209. if ( $repeat_value = $this->_check_repeat( __FUNCTION__, $repeat ) ) { 
  210. return $repeat_value; 
  211.  
  212. $tzstring = null; 
  213.  
  214. if ( is_array( $this->value ) && array_key_exists( 'timezone', $this->value ) ) { 
  215. $tzstring = $this->value['timezone']; 
  216.  
  217. if ( empty( $tzstring ) ) { 
  218. $tzstring = cmb2_utils()->timezone_string(); 
  219.  
  220. $offset = cmb2_utils()->timezone_offset( $tzstring ); 
  221.  
  222. if ( 'UTC' === substr( $tzstring, 0, 3 ) ) { 
  223. $tzstring = timezone_name_from_abbr( '', $offset, 0 ); 
  224. /** 
  225. * timezone_name_from_abbr() returns false if not found based on offset. 
  226. * Since there are currently some invalid timezones in wp_timezone_dropdown(),  
  227. * fallback to an offset of 0 (UTC+0) 
  228. * https://core.trac.wordpress.org/ticket/29205 
  229. */ 
  230. $tzstring = false !== $tzstring ? $tzstring : timezone_name_from_abbr( '', 0, 0 ); 
  231.  
  232. try { 
  233. $this->value = new DateTime( $this->value['date'] . ' ' . $this->value['time'], new DateTimeZone( $tzstring ) ); 
  234. $this->value = serialize( $this->value ); 
  235. } catch ( Exception $e ) { 
  236. if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { 
  237. error_log( 'CMB2_Sanitize:::text_datetime_timestamp_timezone, ' . __LINE__ . ': ' . print_r( $e->getMessage(), true ) ); 
  238.  
  239. return $this->value; 
  240.  
  241. /** 
  242. * Sanitize textareas and wysiwyg fields 
  243. * @since 1.0.1 
  244. * @return string Sanitized data 
  245. */ 
  246. public function textarea() { 
  247. return is_array( $this->value ) ? array_map( 'wp_kses_post', $this->value ) : wp_kses_post( $this->value ); 
  248.  
  249. /** 
  250. * Sanitize code textareas 
  251. * @since 1.0.2 
  252. * @return string Sanitized data 
  253. */ 
  254. public function textarea_code( $repeat = false ) { 
  255. if ( $repeat_value = $this->_check_repeat( __FUNCTION__, $repeat ) ) { 
  256. return $repeat_value; 
  257.  
  258. return htmlspecialchars_decode( stripslashes( $this->value ) ); 
  259.  
  260. /** 
  261. * Peforms saving of `file` attachement's ID 
  262. * @since 1.1.0 
  263. */ 
  264. public function _save_file_id() { 
  265. $group = $this->field->group; 
  266. $args = $this->field->args(); 
  267. $args['id'] = $args['_id'] . '_id'; 
  268.  
  269. unset( $args['_id'], $args['_name'] ); 
  270. // And get new field object 
  271. $field = new CMB2_Field( array( 
  272. 'field_args' => $args,  
  273. 'group_field' => $group,  
  274. 'object_id' => $this->field->object_id,  
  275. 'object_type' => $this->field->object_type,  
  276. ) ); 
  277. $id_key = $field->_id(); 
  278. $id_val_old = $field->escaped_value( 'absint' ); 
  279.  
  280. if ( $group ) { 
  281. // Check group $_POST data 
  282. $i = $group->index; 
  283. $base_id = $group->_id(); 
  284. $id_val = isset( $_POST[ $base_id ][ $i ][ $id_key ] ) ? absint( $_POST[ $base_id ][ $i ][ $id_key ] ) : 0; 
  285.  
  286. } else { 
  287. // Check standard $_POST data 
  288. $id_val = isset( $_POST[ $field->id() ] ) ? $_POST[ $field->id() ] : null; 
  289.  
  290.  
  291. // If there is no ID saved yet, try to get it from the url 
  292. if ( $this->value && ! $id_val ) { 
  293. $id_val = cmb2_utils()->image_id_from_url( $this->value ); 
  294.  
  295. if ( $group ) { 
  296. return array( 
  297. 'attach_id' => $id_val,  
  298. 'field_id' => $id_key,  
  299. ); 
  300.  
  301. if ( $id_val && $id_val != $id_val_old ) { 
  302. return $field->update_data( $id_val ); 
  303. } elseif ( empty( $id_val ) && $id_val_old ) { 
  304. return $field->remove_data( $id_val_old ); 
  305.  
  306. /** 
  307. * Handles saving of attachment post ID and sanitizing file url 
  308. * @since 1.1.0 
  309. * @return string Sanitized url 
  310. */ 
  311. public function file() { 
  312. $id_value = $this->_save_file_id( $this->value ); 
  313. $clean = $this->text_url( $this->value ); 
  314.  
  315. // Return an array with url/id if saving a group field 
  316. return $this->field->group ? array_merge( array( 'url' => $clean ), $id_value ) : $clean; 
  317.  
  318. /** 
  319. * If repeating, loop through and re-apply sanitization method 
  320. * @since 1.1.0 
  321. * @param string $method Class method 
  322. * @param bool $repeat Whether repeating or not 
  323. * @return mixed Sanitized value 
  324. */ 
  325. public function _check_repeat( $method, $repeat ) { 
  326. if ( $repeat || ! $this->field->args( 'repeatable' ) ) { 
  327. return; 
  328. $new_value = array(); 
  329. foreach ( $this->value as $iterator => $val ) { 
  330. $new_value[] = $this->$method( $val, true ); 
  331. return $new_value; 
  332.  
  333. /** 
  334. * Determine if passed value is an empty array 
  335. * @since 2.0.6 
  336. * @param mixed $to_check Value to check 
  337. * @return boolean Whether value is an array that's empty 
  338. */ 
  339. public function _is_empty_array( $to_check ) { 
  340. if ( is_array( $to_check ) ) { 
  341. $cleaned_up = array_filter( $to_check ); 
  342. return empty( $cleaned_up ); 
  343. return false; 
  344.