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(); 
  33.  
  34. /** 
  35. * Default fallback sanitization method. Applies filters. 
  36. * @since 1.0.2 
  37. */ 
  38. public function default_sanitization() { 
  39.  
  40. /** 
  41. * This exists for back-compatibility, but validation 
  42. * is not what happens here. 
  43. * @deprecated See documentation for "cmb2_sanitize_{$this->type()}". 
  44. */ 
  45. if ( function_exists( 'apply_filters_deprecated' ) ) { 
  46. $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()}" ); 
  47. } else { 
  48. $override_value = apply_filters( "cmb2_validate_{$this->field->type()}", null, $this->value, $this->field->object_id, $this->field->args(), $this ); 
  49.  
  50. if ( null !== $override_value ) { 
  51. return $override_value; 
  52.  
  53. $sanitized_value = ''; 
  54. switch ( $this->field->type() ) { 
  55. case 'wysiwyg': 
  56. case 'textarea_small': 
  57. case 'oembed': 
  58. $sanitized_value = $this->textarea(); 
  59. break; 
  60. case 'taxonomy_select': 
  61. case 'taxonomy_radio': 
  62. case 'taxonomy_radio_inline': 
  63. case 'taxonomy_multicheck': 
  64. case 'taxonomy_multicheck_inline': 
  65. if ( $this->field->args( 'taxonomy' ) ) { 
  66. wp_set_object_terms( $this->field->object_id, $this->value, $this->field->args( 'taxonomy' ) ); 
  67. } else { 
  68. CMB2_Utils::log_if_debug( __METHOD__, __LINE__, "{$this->field->type()} {$this->field->_id()} is missing the 'taxonomy' parameter." ); 
  69. break; 
  70. case 'multicheck': 
  71. case 'multicheck_inline': 
  72. case 'file_list': 
  73. case 'group': 
  74. // no filtering 
  75. $sanitized_value = $this->value; 
  76. break; 
  77. default: 
  78. // Handle repeatable fields array 
  79. // We'll fallback to 'sanitize_text_field' 
  80. $sanitized_value = is_array( $this->value ) ? array_map( 'sanitize_text_field', $this->value ) : sanitize_text_field( $this->value ); 
  81. break; 
  82.  
  83. return $this->_is_empty_array( $sanitized_value ) ? '' : $sanitized_value; 
  84.  
  85. /** 
  86. * Simple checkbox validation 
  87. * @since 1.0.1 
  88. * @return string|false 'on' or false 
  89. */ 
  90. public function checkbox() { 
  91. return $this->value === 'on' ? 'on' : false; 
  92.  
  93. /** 
  94. * Validate url in a meta value 
  95. * @since 1.0.1 
  96. * @return string Empty string or escaped url 
  97. */ 
  98. public function text_url() { 
  99. $protocols = $this->field->args( 'protocols' ); 
  100. // for repeatable 
  101. if ( is_array( $this->value ) ) { 
  102. foreach ( $this->value as $key => $val ) { 
  103. $this->value[ $key ] = $val ? esc_url_raw( $val, $protocols ) : $this->field->get_default(); 
  104. } else { 
  105. $this->value = $this->value ? esc_url_raw( $this->value, $protocols ) : $this->field->get_default(); 
  106.  
  107. return $this->value; 
  108.  
  109. public function colorpicker() { 
  110. // for repeatable 
  111. if ( is_array( $this->value ) ) { 
  112. $check = $this->value; 
  113. $this->value = array(); 
  114. foreach ( $check as $key => $val ) { 
  115. if ( $val && '#' != $val ) { 
  116. $this->value[ $key ] = esc_attr( $val ); 
  117. } else { 
  118. $this->value = ! $this->value || '#' == $this->value ? '' : esc_attr( $this->value ); 
  119. return $this->value; 
  120.  
  121. /** 
  122. * Validate email in a meta value 
  123. * @since 1.0.1 
  124. * @return string Empty string or sanitized email 
  125. */ 
  126. public function text_email() { 
  127. // for repeatable 
  128. if ( is_array( $this->value ) ) { 
  129. foreach ( $this->value as $key => $val ) { 
  130. $val = trim( $val ); 
  131. $this->value[ $key ] = is_email( $val ) ? $val : ''; 
  132. } else { 
  133. $this->value = trim( $this->value ); 
  134. $this->value = is_email( $this->value ) ? $this->value : ''; 
  135.  
  136. return $this->value; 
  137.  
  138. /** 
  139. * Validate money in a meta value 
  140. * @since 1.0.1 
  141. * @return string Empty string or sanitized money value 
  142. */ 
  143. public function text_money() { 
  144. if ( ! $this->value ) { 
  145. return ''; 
  146.  
  147. global $wp_locale; 
  148.  
  149. $search = array( $wp_locale->number_format['thousands_sep'], $wp_locale->number_format['decimal_point'] ); 
  150. $replace = array( '', '.' ); 
  151.  
  152. // for repeatable 
  153. if ( is_array( $this->value ) ) { 
  154. foreach ( $this->value as $key => $val ) { 
  155. if ( $val ) { 
  156. $this->value[ $key ] = number_format_i18n( (float) str_ireplace( $search, $replace, $val ), 2 ); 
  157. } else { 
  158. $this->value = number_format_i18n( (float) str_ireplace( $search, $replace, $this->value ), 2 ); 
  159.  
  160. return $this->value; 
  161.  
  162. /** 
  163. * Converts text date to timestamp 
  164. * @since 1.0.2 
  165. * @return string Timestring 
  166. */ 
  167. public function text_date_timestamp() { 
  168. return is_array( $this->value ) 
  169. ? array_map( array( $this->field, 'get_timestamp_from_value' ), $this->value ) 
  170. : $this->field->get_timestamp_from_value( $this->value ); 
  171.  
  172. /** 
  173. * Datetime to timestamp 
  174. * @since 1.0.1 
  175. * @return string Timestring 
  176. */ 
  177. public function text_datetime_timestamp( $repeat = false ) { 
  178.  
  179. $test = is_array( $this->value ) ? array_filter( $this->value ) : ''; 
  180. if ( empty( $test ) ) { 
  181. return ''; 
  182.  
  183. $repeat_value = $this->_check_repeat( __FUNCTION__, $repeat ); 
  184. if ( false !== $repeat_value ) { 
  185. return $repeat_value; 
  186.  
  187. if ( isset( $this->value['date'], $this->value['time'] ) ) { 
  188. $this->value = $this->field->get_timestamp_from_value( $this->value['date'] . ' ' . $this->value['time'] ); 
  189.  
  190. if ( $tz_offset = $this->field->field_timezone_offset() ) { 
  191. $this->value += (int) $tz_offset; 
  192.  
  193. return $this->value; 
  194.  
  195. /** 
  196. * Datetime to timestamp with timezone 
  197. * @since 1.0.1 
  198. * @return string Timestring 
  199. */ 
  200. public function text_datetime_timestamp_timezone( $repeat = false ) { 
  201. static $utc_values = array(); 
  202.  
  203. $test = is_array( $this->value ) ? array_filter( $this->value ) : ''; 
  204. if ( empty( $test ) ) { 
  205. return ''; 
  206.  
  207. $utc_key = $this->field->_id() . '_utc'; 
  208.  
  209. $repeat_value = $this->_check_repeat( __FUNCTION__, $repeat ); 
  210. if ( false !== $repeat_value ) { 
  211. if ( ! empty( $utc_values[ $utc_key ] ) ) { 
  212. $this->_save_utc_value( $utc_key, $utc_values[ $utc_key ] ); 
  213. unset( $utc_values[ $utc_key ] ); 
  214.  
  215. return $repeat_value; 
  216.  
  217. $tzstring = null; 
  218.  
  219. if ( is_array( $this->value ) && array_key_exists( 'timezone', $this->value ) ) { 
  220. $tzstring = $this->value['timezone']; 
  221.  
  222. if ( empty( $tzstring ) ) { 
  223. $tzstring = CMB2_Utils::timezone_string(); 
  224.  
  225. $offset = CMB2_Utils::timezone_offset( $tzstring ); 
  226.  
  227. if ( 'UTC' === substr( $tzstring, 0, 3 ) ) { 
  228. $tzstring = timezone_name_from_abbr( '', $offset, 0 ); 
  229. /** 
  230. * timezone_name_from_abbr() returns false if not found based on offset. 
  231. * Since there are currently some invalid timezones in wp_timezone_dropdown(),  
  232. * fallback to an offset of 0 (UTC+0) 
  233. * https://core.trac.wordpress.org/ticket/29205 
  234. */ 
  235. $tzstring = false !== $tzstring ? $tzstring : timezone_name_from_abbr( '', 0, 0 ); 
  236.  
  237. $full_format = $this->field->args['date_format'] . ' ' . $this->field->args['time_format']; 
  238. $full_date = $this->value['date'] . ' ' . $this->value['time']; 
  239.  
  240. try { 
  241.  
  242. $datetime = date_create_from_format( $full_format, $full_date ); 
  243.  
  244. if ( ! is_object( $datetime ) ) { 
  245. $this->value = $utc_stamp = ''; 
  246. } else { 
  247. $timestamp = $datetime->setTimezone( new DateTimeZone( $tzstring ) )->getTimestamp(); 
  248. $utc_stamp = $timestamp - $offset; 
  249. $this->value = serialize( $datetime ); 
  250.  
  251. if ( $this->field->group ) { 
  252. $this->value = array( 
  253. 'supporting_field_value' => $utc_stamp,  
  254. 'supporting_field_id' => $utc_key,  
  255. 'value' => $this->value,  
  256. ); 
  257. } else { 
  258. // Save the utc timestamp supporting field 
  259. if ( $repeat ) { 
  260. $utc_values[ $utc_key ][] = $utc_stamp; 
  261. } else { 
  262. $this->_save_utc_value( $utc_key, $utc_stamp ); 
  263.  
  264. } catch ( Exception $e ) { 
  265. $this->value = ''; 
  266. CMB2_Utils::log_if_debug( __METHOD__, __LINE__, $e->getMessage() ); 
  267.  
  268. return $this->value; 
  269.  
  270. /** 
  271. * Sanitize textareas and wysiwyg fields 
  272. * @since 1.0.1 
  273. * @return string Sanitized data 
  274. */ 
  275. public function textarea() { 
  276. return is_array( $this->value ) ? array_map( 'wp_kses_post', $this->value ) : wp_kses_post( $this->value ); 
  277.  
  278. /** 
  279. * Sanitize code textareas 
  280. * @since 1.0.2 
  281. * @return string Sanitized data 
  282. */ 
  283. public function textarea_code( $repeat = false ) { 
  284. $repeat_value = $this->_check_repeat( __FUNCTION__, $repeat ); 
  285. if ( false !== $repeat_value ) { 
  286. return $repeat_value; 
  287.  
  288. return htmlspecialchars_decode( stripslashes( $this->value ) ); 
  289.  
  290. /** 
  291. * Handles saving of attachment post ID and sanitizing file url 
  292. * @since 1.1.0 
  293. * @return string Sanitized url 
  294. */ 
  295. public function file() { 
  296. $file_id_key = $this->field->_id() . '_id'; 
  297.  
  298. if ( $this->field->group ) { 
  299. // Return an array with url/id if saving a group field 
  300. $this->value = $this->_get_group_file_value_array( $file_id_key ); 
  301. } else { 
  302. $this->_save_file_id_value( $file_id_key ); 
  303. $this->text_url(); 
  304.  
  305. return $this->value; 
  306.  
  307. /** 
  308. * Gets the values for the `file` field type from the data being saved. 
  309. * @since 2.2.0 
  310. */ 
  311. public function _get_group_file_value_array( $id_key ) { 
  312. $alldata = $this->field->group->data_to_save; 
  313. $base_id = $this->field->group->_id(); 
  314. $i = $this->field->group->index; 
  315.  
  316. // Check group $alldata data 
  317. $id_val = isset( $alldata[ $base_id ][ $i ][ $id_key ] ) 
  318. ? absint( $alldata[ $base_id ][ $i ][ $id_key ] ) 
  319. : 0; 
  320.  
  321. return array( 
  322. 'value' => $this->text_url(),  
  323. 'supporting_field_value' => $id_val,  
  324. 'supporting_field_id' => $id_key,  
  325. ); 
  326.  
  327. /** 
  328. * Peforms saving of `file` attachement's ID 
  329. * @since 1.1.0 
  330. */ 
  331. public function _save_file_id_value( $file_id_key ) { 
  332. $id_field = $this->_new_supporting_field( $file_id_key ); 
  333.  
  334. // Check standard data_to_save data 
  335. $id_val = isset( $this->field->data_to_save[ $file_id_key ] ) 
  336. ? $this->field->data_to_save[ $file_id_key ] 
  337. : null; 
  338.  
  339. // If there is no ID saved yet, try to get it from the url 
  340. if ( $this->value && ! $id_val ) { 
  341. $id_val = CMB2_Utils::image_id_from_url( $this->value ); 
  342.  
  343. return $id_field->save_field( $id_val ); 
  344.  
  345. /** 
  346. * Peforms saving of `text_datetime_timestamp_timezone` utc timestamp 
  347. * @since 2.2.0 
  348. */ 
  349. public function _save_utc_value( $utc_key, $utc_stamp ) { 
  350. return $this->_new_supporting_field( $utc_key )->save_field( $utc_stamp ); 
  351.  
  352. /** 
  353. * Returns a new, supporting, CMB2_Field object based on a new field id. 
  354. * @since 2.2.0 
  355. */ 
  356. public function _new_supporting_field( $new_field_id ) { 
  357. return $this->field->get_field_clone( array( 
  358. 'id' => $new_field_id,  
  359. 'sanitization_cb' => false,  
  360. ) ); 
  361.  
  362. /** 
  363. * If repeating, loop through and re-apply sanitization method 
  364. * @since 1.1.0 
  365. * @param string $method Class method 
  366. * @param bool $repeat Whether repeating or not 
  367. * @return mixed Sanitized value 
  368. */ 
  369. public function _check_repeat( $method, $repeat ) { 
  370. if ( $repeat || ! $this->field->args( 'repeatable' ) ) { 
  371. return false; 
  372.  
  373. $values_array = $this->value; 
  374.  
  375. $new_value = array(); 
  376. foreach ( $values_array as $iterator => $this->value ) { 
  377. if ( $this->value ) { 
  378. $val = $this->$method( true ); 
  379. if ( ! empty( $val ) ) { 
  380. $new_value[] = $val; 
  381.  
  382. $this->value = $new_value; 
  383.  
  384. return empty( $this->value ) ? null : $this->value; 
  385.  
  386. /** 
  387. * Determine if passed value is an empty array 
  388. * @since 2.0.6 
  389. * @param mixed $to_check Value to check 
  390. * @return boolean Whether value is an array that's empty 
  391. */ 
  392. public function _is_empty_array( $to_check ) { 
  393. if ( is_array( $to_check ) ) { 
  394. $cleaned_up = array_filter( $to_check ); 
  395. return empty( $cleaned_up ); 
  396. return false; 
  397.