2026-02-25 06:59:34 +00:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Symfony\Component\DomCrawler\Field ;
/**
* ChoiceFormField represents a choice form field .
*
* It is constructed from an HTML select tag , or an HTML checkbox , or radio inputs .
*
* @ author Fabien Potencier < fabien @ symfony . com >
*/
class ChoiceFormField extends FormField
{
2026-02-27 00:03:00 +00:00
private string $type ;
private bool $multiple ;
private array $options ;
private bool $validationDisabled = false ;
2026-02-25 06:59:34 +00:00
/**
* Returns true if the field should be included in the submitted values .
*
* @ return bool true if the field should be included in the submitted values , false otherwise
*/
2026-02-27 00:03:00 +00:00
public function hasValue () : bool
2026-02-25 06:59:34 +00:00
{
// don't send a value for unchecked checkboxes
2026-02-27 00:03:00 +00:00
if ( \in_array ( $this -> type , [ 'checkbox' , 'radio' ], true ) && null === $this -> value ) {
2026-02-25 06:59:34 +00:00
return false ;
}
return true ;
}
/**
* Check if the current selected option is disabled .
*/
2026-02-27 00:03:00 +00:00
public function isDisabled () : bool
2026-02-25 06:59:34 +00:00
{
2026-02-27 00:03:00 +00:00
if ( 'checkbox' === $this -> type ) {
return parent :: isDisabled ();
}
2026-02-25 06:59:34 +00:00
if ( parent :: isDisabled () && 'select' === $this -> type ) {
return true ;
}
foreach ( $this -> options as $option ) {
if ( $option [ 'value' ] == $this -> value && $option [ 'disabled' ]) {
return true ;
}
}
return false ;
}
/**
* Sets the value of the field .
*/
2026-02-27 00:03:00 +00:00
public function select ( string | array | bool $value ) : void
2026-02-25 06:59:34 +00:00
{
$this -> setValue ( $value );
}
/**
* Ticks a checkbox .
*
* @ throws \LogicException When the type provided is not correct
*/
2026-02-27 00:03:00 +00:00
public function tick () : void
2026-02-25 06:59:34 +00:00
{
if ( 'checkbox' !== $this -> type ) {
2026-02-27 00:03:00 +00:00
throw new \LogicException ( \sprintf ( 'You cannot tick "%s" as it is not a checkbox (%s).' , $this -> name , $this -> type ));
2026-02-25 06:59:34 +00:00
}
$this -> setValue ( true );
}
/**
* Unticks a checkbox .
*
* @ throws \LogicException When the type provided is not correct
*/
2026-02-27 00:03:00 +00:00
public function untick () : void
2026-02-25 06:59:34 +00:00
{
if ( 'checkbox' !== $this -> type ) {
2026-02-27 00:03:00 +00:00
throw new \LogicException ( \sprintf ( 'You cannot untick "%s" as it is not a checkbox (%s).' , $this -> name , $this -> type ));
2026-02-25 06:59:34 +00:00
}
$this -> setValue ( false );
}
/**
* Sets the value of the field .
*
* @ throws \InvalidArgumentException When value type provided is not correct
*/
2026-02-27 00:03:00 +00:00
public function setValue ( string | array | bool | null $value ) : void
2026-02-25 06:59:34 +00:00
{
if ( 'checkbox' === $this -> type && false === $value ) {
// uncheck
$this -> value = null ;
} elseif ( 'checkbox' === $this -> type && true === $value ) {
// check
$this -> value = $this -> options [ 0 ][ 'value' ];
} else {
if ( \is_array ( $value )) {
if ( ! $this -> multiple ) {
2026-02-27 00:03:00 +00:00
throw new \InvalidArgumentException ( \sprintf ( 'The value for "%s" cannot be an array.' , $this -> name ));
2026-02-25 06:59:34 +00:00
}
foreach ( $value as $v ) {
if ( ! $this -> containsOption ( $v , $this -> options )) {
2026-02-27 00:03:00 +00:00
throw new \InvalidArgumentException ( \sprintf ( 'Input "%s" cannot take "%s" as a value (possible values: "%s").' , $this -> name , $v , implode ( '", "' , $this -> availableOptionValues ())));
2026-02-25 06:59:34 +00:00
}
}
} elseif ( ! $this -> containsOption ( $value , $this -> options )) {
2026-02-27 00:03:00 +00:00
throw new \InvalidArgumentException ( \sprintf ( 'Input "%s" cannot take "%s" as a value (possible values: "%s").' , $this -> name , $value , implode ( '", "' , $this -> availableOptionValues ())));
2026-02-25 06:59:34 +00:00
}
if ( $this -> multiple ) {
$value = ( array ) $value ;
}
if ( \is_array ( $value )) {
$this -> value = $value ;
} else {
parent :: setValue ( $value );
}
}
}
/**
* Adds a choice to the current ones .
*
* @ throws \LogicException When choice provided is not multiple nor radio
*
* @ internal
*/
2026-02-27 00:03:00 +00:00
public function addChoice ( \DOMElement $node ) : void
2026-02-25 06:59:34 +00:00
{
if ( ! $this -> multiple && 'radio' !== $this -> type ) {
2026-02-27 00:03:00 +00:00
throw new \LogicException ( \sprintf ( 'Unable to add a choice for "%s" as it is not multiple or is not a radio button.' , $this -> name ));
2026-02-25 06:59:34 +00:00
}
$option = $this -> buildOptionValue ( $node );
$this -> options [] = $option ;
if ( $node -> hasAttribute ( 'checked' )) {
$this -> value = $option [ 'value' ];
}
}
/**
* Returns the type of the choice field ( radio , select , or checkbox ) .
*/
2026-02-27 00:03:00 +00:00
public function getType () : string
2026-02-25 06:59:34 +00:00
{
return $this -> type ;
}
/**
* Returns true if the field accepts multiple values .
*/
2026-02-27 00:03:00 +00:00
public function isMultiple () : bool
2026-02-25 06:59:34 +00:00
{
return $this -> multiple ;
}
/**
* Initializes the form field .
*
* @ throws \LogicException When node type is incorrect
*/
2026-02-27 00:03:00 +00:00
protected function initialize () : void
2026-02-25 06:59:34 +00:00
{
if ( 'input' !== $this -> node -> nodeName && 'select' !== $this -> node -> nodeName ) {
2026-02-27 00:03:00 +00:00
throw new \LogicException ( \sprintf ( 'A ChoiceFormField can only be created from an input or select tag (%s given).' , $this -> node -> nodeName ));
2026-02-25 06:59:34 +00:00
}
if ( 'input' === $this -> node -> nodeName && 'checkbox' !== strtolower ( $this -> node -> getAttribute ( 'type' )) && 'radio' !== strtolower ( $this -> node -> getAttribute ( 'type' ))) {
2026-02-27 00:03:00 +00:00
throw new \LogicException ( \sprintf ( 'A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is "%s").' , $this -> node -> getAttribute ( 'type' )));
2026-02-25 06:59:34 +00:00
}
$this -> value = null ;
$this -> options = [];
$this -> multiple = false ;
if ( 'input' == $this -> node -> nodeName ) {
$this -> type = strtolower ( $this -> node -> getAttribute ( 'type' ));
$optionValue = $this -> buildOptionValue ( $this -> node );
$this -> options [] = $optionValue ;
if ( $this -> node -> hasAttribute ( 'checked' )) {
$this -> value = $optionValue [ 'value' ];
}
} else {
$this -> type = 'select' ;
if ( $this -> node -> hasAttribute ( 'multiple' )) {
$this -> multiple = true ;
$this -> value = [];
$this -> name = str_replace ( '[]' , '' , $this -> name );
}
$found = false ;
foreach ( $this -> xpath -> query ( 'descendant::option' , $this -> node ) as $option ) {
$optionValue = $this -> buildOptionValue ( $option );
$this -> options [] = $optionValue ;
if ( $option -> hasAttribute ( 'selected' )) {
$found = true ;
if ( $this -> multiple ) {
$this -> value [] = $optionValue [ 'value' ];
} else {
$this -> value = $optionValue [ 'value' ];
}
}
}
// if no option is selected and if it is a simple select box, take the first option as the value
2026-02-27 00:03:00 +00:00
if ( ! $found && ! $this -> multiple && $this -> options ) {
2026-02-25 06:59:34 +00:00
$this -> value = $this -> options [ 0 ][ 'value' ];
}
}
}
/**
* Returns option value with associated disabled flag .
*/
private function buildOptionValue ( \DOMElement $node ) : array
{
$option = [];
$defaultDefaultValue = 'select' === $this -> node -> nodeName ? '' : 'on' ;
2026-02-27 00:03:00 +00:00
$defaultValue = ( isset ( $node -> nodeValue ) && $node -> nodeValue ) ? $node -> nodeValue : $defaultDefaultValue ;
2026-02-25 06:59:34 +00:00
$option [ 'value' ] = $node -> hasAttribute ( 'value' ) ? $node -> getAttribute ( 'value' ) : $defaultValue ;
$option [ 'disabled' ] = $node -> hasAttribute ( 'disabled' );
return $option ;
}
/**
* Checks whether given value is in the existing options .
*
2026-02-27 00:03:00 +00:00
* @ internal
2026-02-25 06:59:34 +00:00
*/
2026-02-27 00:03:00 +00:00
public function containsOption ( string $optionValue , array $options ) : bool
2026-02-25 06:59:34 +00:00
{
if ( $this -> validationDisabled ) {
return true ;
}
foreach ( $options as $option ) {
if ( $option [ 'value' ] == $optionValue ) {
return true ;
}
}
return false ;
}
/**
* Returns list of available field options .
*
2026-02-27 00:03:00 +00:00
* @ internal
2026-02-25 06:59:34 +00:00
*/
2026-02-27 00:03:00 +00:00
public function availableOptionValues () : array
2026-02-25 06:59:34 +00:00
{
$values = [];
foreach ( $this -> options as $option ) {
$values [] = $option [ 'value' ];
}
return $values ;
}
/**
* Disables the internal validation of the field .
*
2026-02-27 00:03:00 +00:00
* @ internal
*
* @ return $this
2026-02-25 06:59:34 +00:00
*/
2026-02-27 00:03:00 +00:00
public function disableValidation () : static
2026-02-25 06:59:34 +00:00
{
$this -> validationDisabled = true ;
return $this ;
}
}