PluginProbe ʕ •ᴥ•ʔ
Advanced Custom Fields (ACF®) / 6.8.7
Advanced Custom Fields (ACF®) v6.8.7
6.8.9 6.8.8 6.8.7 6.8.6 6.8.5 6.8.4 6.8.3 6.8.2 6.8.1 5.8.5 5.8.6 5.8.7 5.8.8 5.8.9 5.9.0 5.9.1 5.9.2 5.9.3 5.9.4 5.9.5 5.9.6 5.9.7 5.9.8 5.9.9 6.0.0 6.0.1 6.0.2 6.0.3 6.0.4 6.0.5 6.0.6 6.0.7 6.1.0 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.1.6 6.1.7 6.1.8 6.2.0 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.2.6 6.2.6.1 6.2.7 6.2.8 6.2.9 6.3.0 6.3.1 6.3.10.2 6.3.11 6.3.12 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.6.1 6.4.0 6.4.0.1 6.4.1 6.4.2 6.4.3 6.5.0 6.5.1 6.6.0 6.6.1 6.6.2 6.7.0 6.7.1 6.7.2 6.8.0 trunk 1.0.0 1.0.2 1.0.3 1.0.5 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.1.1 2.1.3 2.1.4 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.6 3.0.7 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.5.7 3.5.8 4.0.0 4.0.1 4.0.2 4.0.3 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.1.8 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.3.7 4.3.8 4.3.9 4.4.0 4.4.1 4.4.10 4.4.11 4.4.12 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.4.7 4.4.8 4.4.9 5.10 5.10.1 5.10.2 5.11 5.11.1 5.11.2 5.11.3 5.11.4 5.12 5.12.1 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.6.10 5.6.2 5.6.3 5.6.4 5.6.5 5.6.6 5.6.7 5.6.8 5.6.9 5.7.0 5.7.1 5.7.10 5.7.12 5.7.13 5.7.2 5.7.3 5.7.4 5.7.5 5.7.6 5.7.7 5.7.8 5.7.9 5.8.0 5.8.1 5.8.10 5.8.11 5.8.12 5.8.13 5.8.14 5.8.2 5.8.3 5.8.4
advanced-custom-fields / includes / fields / class-acf-field-checkbox.php
advanced-custom-fields / includes / fields Last commit date
class-acf-field-accordion.php 5 months ago class-acf-field-button-group.php 5 months ago class-acf-field-checkbox.php 2 months ago class-acf-field-color_picker.php 5 months ago class-acf-field-date_picker.php 5 months ago class-acf-field-date_time_picker.php 5 months ago class-acf-field-email.php 5 months ago class-acf-field-file.php 5 months ago class-acf-field-google-map.php 5 months ago class-acf-field-group.php 5 months ago class-acf-field-icon_picker.php 6 months ago class-acf-field-image.php 1 month ago class-acf-field-link.php 5 months ago class-acf-field-message.php 6 months ago class-acf-field-number.php 5 months ago class-acf-field-oembed.php 2 months ago class-acf-field-output.php 6 months ago class-acf-field-page_link.php 1 month ago class-acf-field-password.php 5 months ago class-acf-field-post_object.php 1 month ago class-acf-field-radio.php 2 months ago class-acf-field-range.php 5 months ago class-acf-field-relationship.php 1 month ago class-acf-field-select.php 2 months ago class-acf-field-separator.php 6 months ago class-acf-field-tab.php 6 months ago class-acf-field-taxonomy.php 2 months ago class-acf-field-text.php 5 months ago class-acf-field-textarea.php 5 months ago class-acf-field-time_picker.php 5 months ago class-acf-field-true_false.php 5 months ago class-acf-field-url.php 5 months ago class-acf-field-user.php 2 months ago class-acf-field-wysiwyg.php 5 months ago class-acf-field.php 5 months ago index.php 2 years ago
class-acf-field-checkbox.php
609 lines
1 <?php
2 /**
3 * @package ACF
4 * @author WP Engine
5 *
6 * © 2026 Advanced Custom Fields (ACF®). All rights reserved.
7 * "ACF" is a trademark of WP Engine.
8 * Licensed under the GNU General Public License v2 or later.
9 * https://www.gnu.org/licenses/gpl-2.0.html
10 */
11
12 if ( ! class_exists( 'acf_field_checkbox' ) ) :
13
14 class acf_field_checkbox extends acf_field {
15
16 /**
17 * A local store of all values for de-duplication.
18 *
19 * @var array
20 */
21 private array $_values; //phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore -- backwards compatibility.
22
23 /**
24 * An internal boolean tracking if all checkboxes are checked.
25 *
26 * @var boolean
27 */
28 private bool $_all_checked; //phpcs:ignore PSR2.Classes.PropertyDeclaration.Underscore -- backwards compatibility.
29
30 /**
31 * This function will setup the field type data
32 *
33 * @type function
34 * @date 5/03/2014
35 * @since 5.0.0
36 *
37 * @param n/a
38 * @return n/a
39 */
40 function initialize() {
41
42 // vars
43 $this->name = 'checkbox';
44 $this->label = __( 'Checkbox', 'acf' );
45 $this->category = 'choice';
46 $this->description = __( 'A group of checkbox inputs that allow the user to select one, or multiple values that you specify.', 'acf' );
47 $this->preview_image = acf_get_url() . '/assets/images/field-type-previews/field-preview-checkbox.png';
48 $this->doc_url = acf_add_url_utm_tags( 'https://www.advancedcustomfields.com/resources/checkbox/', 'docs', 'field-type-selection' );
49 $this->defaults = array(
50 'layout' => 'vertical',
51 'choices' => array(),
52 'default_value' => '',
53 'allow_custom' => 0,
54 'save_custom' => 0,
55 'toggle' => 0,
56 'return_format' => 'value',
57 'custom_choice_button_text' => __( 'Add new choice', 'acf' ),
58 );
59 }
60
61
62 /**
63 * Create the HTML interface for your field
64 *
65 * @param $field (array) the $field being rendered
66 *
67 * @type action
68 * @since 3.6
69 * @date 23/01/13
70 *
71 * @param $field (array) the $field being edited
72 * @return n/a
73 */
74 function render_field( $field ) {
75
76 // reset vars
77 $this->_values = array();
78 $this->_all_checked = true;
79
80 // ensure array
81 $field['value'] = acf_get_array( $field['value'] );
82 $field['choices'] = acf_get_array( $field['choices'] );
83
84 // hiden input
85 acf_hidden_input( array( 'name' => $field['name'] ) );
86
87 // vars
88 $li = '';
89 $ul = array(
90 'class' => 'acf-checkbox-list',
91 'role' => 'group',
92 );
93
94 // Add aria-labelledby if field has an ID for proper screen reader announcement
95 if ( ! empty( $field['id'] ) ) {
96 $ul['aria-labelledby'] = $field['id'] . '-label';
97 }
98
99 // append to class
100 $ul['class'] .= ' ' . ( $field['layout'] == 'horizontal' ? 'acf-hl' : 'acf-bl' );
101 $ul['class'] .= ' ' . $field['class'];
102
103 // checkbox saves an array
104 $field['name'] .= '[]';
105
106 // choices
107 if ( ! empty( $field['choices'] ) ) {
108
109 // choices
110 $li .= $this->render_field_choices( $field );
111
112 // toggle
113 if ( $field['toggle'] ) {
114 $li = $this->render_field_toggle( $field ) . $li;
115 }
116 }
117
118 // custom
119 if ( $field['allow_custom'] ) {
120 $li .= $this->render_field_custom( $field );
121 }
122
123 // return
124 echo '<ul ' . acf_esc_attrs( $ul ) . '>' . "\n" . $li . '</ul>' . "\n"; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped by specific render methods above.
125 }
126
127
128 /**
129 * description
130 *
131 * @type function
132 * @date 15/7/17
133 * @since 5.6.0
134 *
135 * @param $post_id (int)
136 * @return $post_id (int)
137 */
138 function render_field_choices( $field ) {
139
140 // walk
141 return $this->walk( $field['choices'], $field );
142 }
143
144 /**
145 * Validates values for the checkbox field
146 *
147 * @since 6.0.0
148 *
149 * @param boolean $valid If the field is valid.
150 * @param mixed $value The value to validate.
151 * @param array $field The main field array.
152 * @param string $input The input element's name attribute.
153 * @return boolean
154 */
155 public function validate_value( $valid, $value, $field, $input ) {
156 if ( ! is_array( $value ) || empty( $field['allow_custom'] ) ) {
157 return $valid;
158 }
159
160 foreach ( $value as $value ) {
161 if ( empty( $value ) && $value !== '0' ) {
162 return __( 'Checkbox custom values cannot be empty. Uncheck any empty values.', 'acf' );
163 }
164 }
165
166 return $valid;
167 }
168
169 /**
170 * description
171 *
172 * @type function
173 * @date 15/7/17
174 * @since 5.6.0
175 *
176 * @param $post_id (int)
177 * @return $post_id (int)
178 */
179 function render_field_toggle( $field ) {
180
181 // vars
182 $atts = array(
183 'type' => 'checkbox',
184 'class' => 'acf-checkbox-toggle',
185 'label' => __( 'Toggle All', 'acf' ),
186 );
187
188 // custom label
189 if ( is_string( $field['toggle'] ) ) {
190 $atts['label'] = $field['toggle'];
191 }
192
193 // checked
194 if ( $this->_all_checked ) {
195 $atts['checked'] = 'checked';
196 }
197
198 // return
199 return '<li>' . acf_get_checkbox_input( $atts ) . '</li>' . "\n";
200 }
201
202
203 /**
204 * description
205 *
206 * @type function
207 * @date 15/7/17
208 * @since 5.6.0
209 *
210 * @param $post_id (int)
211 * @return $post_id (int)
212 */
213 function render_field_custom( $field ) {
214
215 // vars
216 $html = '';
217
218 // loop
219 foreach ( $field['value'] as $value ) {
220
221 // ignore if already eixsts
222 if ( isset( $field['choices'][ $value ] ) ) {
223 continue;
224 }
225
226 // vars
227 $esc_value = esc_attr( $value );
228 $text_input = array(
229 'name' => $field['name'],
230 'value' => $value,
231 );
232
233 // bail early if choice already exists
234 if ( in_array( $esc_value, $this->_values ) ) {
235 continue;
236 }
237
238 // append
239 $html .= '<li><input class="acf-checkbox-custom" type="checkbox" checked="checked" />' . acf_get_text_input( $text_input ) . '</li>' . "\n";
240 }
241
242 // append button
243 $html .= '<li><a href="#" class="button acf-add-checkbox">' . esc_attr( $field['custom_choice_button_text'] ) . '</a></li>' . "\n";
244
245 // return
246 return $html;
247 }
248
249
250 function walk( $choices = array(), $args = array(), $depth = 0 ) {
251
252 // bail early if no choices
253 if ( empty( $choices ) ) {
254 return '';
255 }
256
257 // defaults
258 $args = wp_parse_args(
259 $args,
260 array(
261 'id' => '',
262 'type' => 'checkbox',
263 'name' => '',
264 'value' => array(),
265 'disabled' => array(),
266 )
267 );
268
269 // vars
270 $html = '';
271
272 // sanitize values for 'selected' matching
273 if ( $depth == 0 ) {
274 $args['value'] = array_map( 'esc_attr', $args['value'] );
275 $args['disabled'] = array_map( 'esc_attr', $args['disabled'] );
276 }
277
278 // loop
279 foreach ( $choices as $value => $label ) {
280
281 // open
282 $html .= '<li>';
283
284 // optgroup
285 if ( is_array( $label ) ) {
286 $html .= '<ul>' . "\n";
287 $html .= $this->walk( $label, $args, $depth + 1 );
288 $html .= '</ul>';
289
290 // option
291 } else {
292
293 // vars
294 $esc_value = esc_attr( $value );
295 $atts = array(
296 'id' => $args['id'] . '-' . str_replace( ' ', '-', $value ),
297 'type' => $args['type'],
298 'name' => $args['name'],
299 'value' => $value,
300 'label' => $label,
301 );
302
303 // selected
304 if ( in_array( $esc_value, $args['value'] ) ) {
305 $atts['checked'] = 'checked';
306 } else {
307 $this->_all_checked = false;
308 }
309
310 // disabled
311 if ( in_array( $esc_value, $args['disabled'] ) ) {
312 $atts['disabled'] = 'disabled';
313 }
314
315 // store value added
316 $this->_values[] = $esc_value;
317
318 // append
319 $html .= acf_get_checkbox_input( $atts );
320 }
321
322 // close
323 $html .= '</li>' . "\n";
324 }
325
326 // return
327 return $html;
328 }
329
330
331
332 /**
333 * Create extra options for your field. This is rendered when editing a field.
334 * The value of $field['name'] can be used (like bellow) to save extra data to the $field
335 *
336 * @type action
337 * @since 3.6
338 * @date 23/01/13
339 *
340 * @param $field - an array holding all the field's data
341 */
342 function render_field_settings( $field ) {
343 // Encode choices (convert from array).
344 $field['choices'] = acf_encode_choices( $field['choices'] );
345 $field['default_value'] = acf_encode_choices( $field['default_value'], false );
346
347 acf_render_field_setting(
348 $field,
349 array(
350 'label' => __( 'Choices', 'acf' ),
351 'instructions' => __( 'Enter each choice on a new line.', 'acf' ) . '<br />' . __( 'For more control, you may specify both a value and label like this:', 'acf' ) . '<br /><span class="acf-field-setting-example">' . __( 'red : Red', 'acf' ) . '</span>',
352 'type' => 'textarea',
353 'name' => 'choices',
354 )
355 );
356
357 acf_render_field_setting(
358 $field,
359 array(
360 'label' => __( 'Default Value', 'acf' ),
361 'instructions' => __( 'Enter each default value on a new line', 'acf' ),
362 'type' => 'textarea',
363 'name' => 'default_value',
364 )
365 );
366
367 acf_render_field_setting(
368 $field,
369 array(
370 'label' => __( 'Return Value', 'acf' ),
371 'instructions' => __( 'Specify the returned value on front end', 'acf' ),
372 'type' => 'radio',
373 'name' => 'return_format',
374 'layout' => 'horizontal',
375 'choices' => array(
376 'value' => __( 'Value', 'acf' ),
377 'label' => __( 'Label', 'acf' ),
378 'array' => __( 'Both (Array)', 'acf' ),
379 ),
380 )
381 );
382 }
383
384 /**
385 * Renders the field settings used in the "Validation" tab.
386 *
387 * @since 6.0
388 *
389 * @param array $field The field settings array.
390 * @return void
391 */
392 function render_field_validation_settings( $field ) {
393 acf_render_field_setting(
394 $field,
395 array(
396 'label' => __( 'Allow Custom Values', 'acf' ),
397 'name' => 'allow_custom',
398 'type' => 'true_false',
399 'ui' => 1,
400 'instructions' => __( "Allow 'custom' values to be added", 'acf' ),
401 )
402 );
403
404 acf_render_field_setting(
405 $field,
406 array(
407 'label' => __( 'Save Custom Values', 'acf' ),
408 'name' => 'save_custom',
409 'type' => 'true_false',
410 'ui' => 1,
411 'instructions' => __( "Save 'custom' values to the field's choices", 'acf' ),
412 'conditions' => array(
413 'field' => 'allow_custom',
414 'operator' => '==',
415 'value' => 1,
416 ),
417 )
418 );
419 }
420
421 /**
422 * Renders the field settings used in the "Presentation" tab.
423 *
424 * @since 6.0
425 *
426 * @param array $field The field settings array.
427 * @return void
428 */
429 function render_field_presentation_settings( $field ) {
430 acf_render_field_setting(
431 $field,
432 array(
433 'label' => __( 'Layout', 'acf' ),
434 'instructions' => '',
435 'type' => 'radio',
436 'name' => 'layout',
437 'layout' => 'horizontal',
438 'choices' => array(
439 'vertical' => __( 'Vertical', 'acf' ),
440 'horizontal' => __( 'Horizontal', 'acf' ),
441 ),
442 )
443 );
444
445 acf_render_field_setting(
446 $field,
447 array(
448 'label' => __( 'Add Toggle All', 'acf' ),
449 'instructions' => __( 'Prepend an extra checkbox to toggle all choices', 'acf' ),
450 'name' => 'toggle',
451 'type' => 'true_false',
452 'ui' => 1,
453 )
454 );
455 }
456
457 /**
458 * This filter is appied to the $field before it is saved to the database
459 *
460 * @type filter
461 * @since 3.6
462 * @date 23/01/13
463 *
464 * @param $field - the field array holding all the field options
465 * @param $post_id - the field group ID (post_type = acf)
466 *
467 * @return $field - the modified field
468 */
469 function update_field( $field ) {
470
471 // Decode choices (convert to array).
472 $field['choices'] = acf_decode_choices( $field['choices'] );
473 $field['default_value'] = acf_decode_choices( $field['default_value'], true );
474 return $field;
475 }
476
477
478 /**
479 * Filters the $value before it is updated in the db.
480 *
481 * @since 3.6
482 * @date 23/01/13
483 *
484 * @param mixed $value The value which will be saved in the database.
485 * @param integer|string $post_id The post_id of which the value will be saved.
486 * @param array $field The field array holding all the field options.
487 *
488 * @return mixed
489 */
490 function update_value( $value, $post_id, $field ) {
491 // bail early if is empty
492 if ( empty( $value ) ) {
493 return $value;
494 }
495
496 // select -> update_value()
497 $value = acf_get_field_type( 'select' )->update_value( $value, $post_id, $field );
498
499 // save_custom
500 if ( $field['save_custom'] ) {
501
502 // get raw $field (may have been changed via repeater field)
503 // if field is local, it won't have an ID
504 $selector = $field['ID'] ? $field['ID'] : $field['key'];
505 $field = acf_get_field( $selector );
506 if ( ! $field ) {
507 return false;
508 }
509
510 // bail early if no ID (JSON only)
511 if ( ! $field['ID'] ) {
512 return $value;
513 }
514
515 acf_get_field_type( 'select' )->append_user_choices_to_field( $value, $post_id, $field );
516 }
517
518 // return
519 return $value;
520 }
521
522 /**
523 * This function will translate field settings
524 *
525 * @type function
526 * @date 8/03/2016
527 * @since 5.3.2
528 *
529 * @param $field (array)
530 * @return $field
531 */
532 function translate_field( $field ) {
533
534 return acf_get_field_type( 'select' )->translate_field( $field );
535 }
536
537
538 /**
539 * This filter is appied to the $value after it is loaded from the db and before it is returned to the template
540 *
541 * @type filter
542 * @since 3.6
543 * @date 23/01/13
544 *
545 * @param $value (mixed) the value which was loaded from the database
546 * @param $post_id (mixed) the post_id from which the value was loaded
547 * @param $field (array) the field array holding all the field options
548 *
549 * @return $value (mixed) the modified value
550 */
551 function format_value( $value, $post_id, $field ) {
552
553 // Bail early if is empty.
554 if ( acf_is_empty( $value ) ) {
555 return array();
556 }
557
558 // Always convert to array of items.
559 $value = acf_array( $value );
560
561 // Return.
562 return acf_get_field_type( 'select' )->format_value( $value, $post_id, $field );
563 }
564
565 /**
566 * Return the schema array for the REST API.
567 *
568 * @param array $field
569 * @return array
570 */
571 public function get_rest_schema( array $field ) {
572 $schema = array(
573 'type' => array( 'integer', 'string', 'array', 'null' ),
574 'required' => isset( $field['required'] ) && $field['required'],
575 'items' => array(
576 'type' => array( 'string', 'integer' ),
577 ),
578 );
579
580 if ( isset( $field['default_value'] ) && '' !== $field['default_value'] ) {
581 $schema['default'] = $field['default_value'];
582 }
583
584 // If we allow custom values, nothing else to do here.
585 if ( ! empty( $field['allow_custom'] ) ) {
586 return $schema;
587 }
588
589 $schema['items']['enum'] = acf_get_field_type( 'select' )->format_rest_choices( $field['choices'] );
590
591 return $schema;
592 }
593
594 /**
595 * Returns an array of JSON-LD Property output types that are supported by this field type.
596 *
597 * @since 6.8
598 *
599 * @return string[]
600 */
601 public function get_jsonld_output_types(): array {
602 return array( 'Text' );
603 }
604 }
605
606 // initialize
607 acf_register_field_type( 'acf_field_checkbox' );
608 endif; // class_exists check
609