vendor/contao/core-bundle/src/Resources/contao/library/Contao/Widget.php line 599

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Contao.
  4.  *
  5.  * (c) Leo Feyer
  6.  *
  7.  * @license LGPL-3.0-or-later
  8.  */
  9. namespace Contao;
  10. use Contao\Database\Result;
  11. use Doctrine\DBAL\Types\Types;
  12. /**
  13.  * Generates and validates form fields
  14.  *
  15.  * The class functions as abstract parent class for all widget classes and
  16.  * provides methods to generate the form field markup and to validate the form
  17.  * field input.
  18.  *
  19.  * Usage:
  20.  *
  21.  *     $widget = new TextField();
  22.  *     $widget->name = 'test';
  23.  *     $widget->label = 'Test';
  24.  *
  25.  *     if ($_POST)
  26.  *     {
  27.  *         $widget->validate();
  28.  *
  29.  *         if (!$widget->hasErrors())
  30.  *         {
  31.  *             echo $widget->value;
  32.  *         }
  33.  *     }
  34.  *
  35.  * @property string        $id                 The field ID
  36.  * @property string        $name               the field name
  37.  * @property string        $label              The field label
  38.  * @property mixed         $value              The field value
  39.  * @property string        $class              One or more CSS classes
  40.  * @property string        $prefix             The CSS class prefix
  41.  * @property string        $template           The template name
  42.  * @property string        $wizard             The field wizard markup
  43.  * @property string        $alt                The alternative text
  44.  * @property string        $style              The style attribute
  45.  * @property string        $accesskey          The key to focus the field
  46.  * @property integer       $tabindex           The tabindex of the field
  47.  * @property boolean       $disabled           Adds the disabled attribute
  48.  * @property boolean       $readonly           Adds the readonly attribute
  49.  * @property boolean       $autofocus          Adds the autofocus attribute
  50.  * @property boolean       $required           Adds the required attribute
  51.  * @property string        $onblur             The blur event
  52.  * @property string        $onchange           The change event
  53.  * @property string        $onclick            The click event
  54.  * @property string        $ondblclick         The double click event
  55.  * @property string        $onfocus            The focus event
  56.  * @property string        $onmousedown        The mouse down event
  57.  * @property string        $onmousemove        The mouse move event
  58.  * @property string        $onmouseout         The mouse out event
  59.  * @property string        $onmouseover        The mouse over event
  60.  * @property string        $onmouseup          The mouse up event
  61.  * @property string        $onkeydown          The key down event
  62.  * @property string        $onkeypress         The key press event
  63.  * @property string        $onkeyup            The key up event
  64.  * @property string        $onselect           The select event
  65.  * @property boolean       $mandatory          The field value must not be empty
  66.  * @property boolean       $nospace            Do not allow whitespace characters
  67.  * @property boolean       $allowHtml          Allow HTML tags in the field value
  68.  * @property boolean       $storeFile          Store uploaded files in a given folder
  69.  * @property boolean       $useHomeDir         Store uploaded files in the user's home directory
  70.  * @property boolean       $trailingSlash      Add or remove a trailing slash
  71.  * @property boolean       $spaceToUnderscore  Convert spaces to underscores
  72.  * @property boolean       $doNotTrim          Do not trim the user input
  73.  * @property string        $forAttribute       The "for" attribute
  74.  * @property DataContainer $dataContainer      The data container object
  75.  * @property Result        $activeRecord       The active record
  76.  * @property string        $mandatoryField     The "mandatory field" label
  77.  * @property string        $customTpl          A custom template name
  78.  * @property string        $slabel             The submit button label
  79.  * @property boolean       $preserveTags       Preserve HTML tags
  80.  * @property boolean       $decodeEntities     Decode HTML entities
  81.  * @property boolean       $useRawRequestData  Use the raw request data from the Symfony request
  82.  * @property integer       $minlength          The minimum length
  83.  * @property integer       $maxlength          The maximum length
  84.  * @property integer       $minval             The minimum value
  85.  * @property integer       $maxval             The maximum value
  86.  * @property integer       $rgxp               The regular expression name
  87.  * @property boolean       $isHexColor         The field value is a hex color
  88.  * @property string        $strTable           The table name
  89.  * @property string        $strField           The field name
  90.  * @property string        $xlabel
  91.  * @property string        $customRgxp
  92.  * @property string        $errorMsg
  93.  * @property integer       $currentRecord
  94.  * @property integer       $rowClass
  95.  * @property integer       $rowClassConfirm
  96.  * @property integer       $storeValues
  97.  * @property boolean       $includeBlankOption
  98.  * @property string        $blankOptionLabel
  99.  */
  100. abstract class Widget extends Controller
  101. {
  102.     use TemplateInheritance;
  103.     /**
  104.      * Id
  105.      * @var integer
  106.      */
  107.     protected $strId;
  108.     /**
  109.      * Name
  110.      * @var string
  111.      */
  112.     protected $strName;
  113.     /**
  114.      * Label
  115.      * @var string
  116.      */
  117.     protected $strLabel;
  118.     /**
  119.      * Value
  120.      * @var mixed
  121.      */
  122.     protected $varValue;
  123.     /**
  124.      * Input callback
  125.      * @var callable
  126.      */
  127.     protected $inputCallback;
  128.     /**
  129.      * CSS class
  130.      * @var string
  131.      */
  132.     protected $strClass;
  133.     /**
  134.      * CSS class prefix
  135.      * @var string
  136.      */
  137.     protected $strPrefix;
  138.     /**
  139.      * Wizard
  140.      * @var string
  141.      */
  142.     protected $strWizard;
  143.     /**
  144.      * Errors
  145.      * @var array
  146.      */
  147.     protected $arrErrors = array();
  148.     /**
  149.      * Attributes
  150.      * @var array
  151.      */
  152.     protected $arrAttributes = array();
  153.     /**
  154.      * Configuration
  155.      * @var array
  156.      */
  157.     protected $arrConfiguration = array();
  158.     /**
  159.      * Options
  160.      * @var array
  161.      */
  162.     protected $arrOptions = array();
  163.     /**
  164.      * Submit indicator
  165.      * @var boolean
  166.      */
  167.     protected $blnSubmitInput false;
  168.     /**
  169.      * For attribute indicator
  170.      * @var boolean
  171.      */
  172.     protected $blnForAttribute false;
  173.     /**
  174.      * Data container
  175.      * @var object
  176.      */
  177.     protected $objDca;
  178.     /**
  179.      * Initialize the object
  180.      *
  181.      * @param array $arrAttributes An optional attributes array
  182.      */
  183.     public function __construct($arrAttributes=null)
  184.     {
  185.         parent::__construct();
  186.         $this->addAttributes($arrAttributes);
  187.     }
  188.     /**
  189.      * Set an object property
  190.      *
  191.      * @param string $strKey   The property name
  192.      * @param mixed  $varValue The property value
  193.      */
  194.     public function __set($strKey$varValue)
  195.     {
  196.         switch ($strKey)
  197.         {
  198.             case 'id':
  199.                 $this->strId $varValue;
  200.                 break;
  201.             case 'name':
  202.                 $this->strName $varValue;
  203.                 break;
  204.             case 'label':
  205.                 $this->strLabel $varValue;
  206.                 break;
  207.             case 'value':
  208.                 $this->varValue StringUtil::deserialize($varValue);
  209.                 // Decrypt the value if it is encrypted
  210.                 if ($this->arrConfiguration['encrypt'] ?? null)
  211.                 {
  212.                     $this->varValue Encryption::decrypt($this->varValue);
  213.                 }
  214.                 break;
  215.             case 'class':
  216.                 if ($varValue && strpos($this->strClass ?? ''$varValue) === false)
  217.                 {
  218.                     $this->strClass trim($this->strClass ' ' $varValue);
  219.                 }
  220.                 break;
  221.             case 'prefix':
  222.                 $this->strPrefix $varValue;
  223.                 break;
  224.             case 'template':
  225.                 $this->strTemplate $varValue;
  226.                 break;
  227.             case 'wizard':
  228.                 $this->strWizard $varValue;
  229.                 break;
  230.             case 'autocomplete':
  231.             case 'autocorrect':
  232.             case 'autocapitalize':
  233.             case 'spellcheck':
  234.                 if (\is_bool($varValue))
  235.                 {
  236.                     $varValue $varValue 'on' 'off';
  237.                 }
  238.                 // no break
  239.             case 'alt':
  240.             case 'style':
  241.             case 'accesskey':
  242.             case 'onblur':
  243.             case 'onchange':
  244.             case 'onclick':
  245.             case 'ondblclick':
  246.             case 'onfocus':
  247.             case 'onmousedown':
  248.             case 'onmousemove':
  249.             case 'onmouseout':
  250.             case 'onmouseover':
  251.             case 'onmouseup':
  252.             case 'onkeydown':
  253.             case 'onkeypress':
  254.             case 'onkeyup':
  255.             case 'onselect':
  256.                 $this->arrAttributes[$strKey] = $varValue;
  257.                 break;
  258.             case 'tabindex':
  259.                 if ($varValue 0)
  260.                 {
  261.                     trigger_deprecation('contao/core-bundle''4.12''Using a tabindex value greater than 0 has been deprecated and will no longer work in Contao 5.0.');
  262.                     $this->arrAttributes['tabindex'] = $varValue;
  263.                 }
  264.                 break;
  265.             case 'disabled':
  266.             case 'readonly':
  267.                 $this->blnSubmitInput $varValue false true;
  268.                 // no break
  269.             case 'autofocus':
  270.                 if ($varValue)
  271.                 {
  272.                     $this->arrAttributes[$strKey] = $strKey;
  273.                 }
  274.                 else
  275.                 {
  276.                     unset($this->arrAttributes[$strKey]);
  277.                 }
  278.                 break;
  279.             case 'required':
  280.                 if ($varValue)
  281.                 {
  282.                     $this->strClass trim($this->strClass ' mandatory');
  283.                 }
  284.                 // no break
  285.             case 'mandatory':
  286.             case 'nospace':
  287.             case 'allowHtml':
  288.             case 'storeFile':
  289.             case 'useHomeDir':
  290.             case 'storeValues':
  291.             case 'trailingSlash':
  292.             case 'spaceToUnderscore':
  293.             case 'doNotTrim':
  294.             case 'useRawRequestData':
  295.                 $this->arrConfiguration[$strKey] = $varValue true false;
  296.                 break;
  297.             case 'forAttribute':
  298.                 $this->blnForAttribute $varValue;
  299.                 break;
  300.             case 'dataContainer':
  301.                 $this->objDca $varValue;
  302.                 break;
  303.             case strncmp($strKey'ng-'3) === 0:
  304.             case strncmp($strKey'data-'5) === 0:
  305.                 $this->arrAttributes[$strKey] = $varValue;
  306.                 break;
  307.             default:
  308.                 $this->arrConfiguration[$strKey] = $varValue;
  309.                 break;
  310.         }
  311.     }
  312.     /**
  313.      * Return an object property
  314.      *
  315.      * @param string $strKey The property name
  316.      *
  317.      * @return string The property value
  318.      */
  319.     public function __get($strKey)
  320.     {
  321.         switch ($strKey)
  322.         {
  323.             case 'id':
  324.                 return $this->strId;
  325.             case 'name':
  326.                 return $this->strName;
  327.             case 'label':
  328.                 return $this->strLabel;
  329.             case 'value':
  330.                 // Encrypt the value
  331.                 if (isset($this->arrConfiguration['encrypt']) && $this->arrConfiguration['encrypt'])
  332.                 {
  333.                     return Encryption::encrypt($this->varValue);
  334.                 }
  335.                 if ($this->varValue === '')
  336.                 {
  337.                     return $this->getEmptyStringOrNull();
  338.                 }
  339.                 return $this->varValue;
  340.             case 'class':
  341.                 return $this->strClass;
  342.             case 'prefix':
  343.                 return $this->strPrefix;
  344.             case 'template':
  345.                 return $this->strTemplate;
  346.             case 'wizard':
  347.                 return $this->strWizard;
  348.             case 'required':
  349.                 return $this->arrConfiguration[$strKey];
  350.             case 'forAttribute':
  351.                 return $this->blnForAttribute;
  352.             case 'dataContainer':
  353.                 return $this->objDca;
  354.             case 'activeRecord':
  355.                 return $this->objDca->activeRecord;
  356.             default:
  357.                 if (isset($this->arrAttributes[$strKey]))
  358.                 {
  359.                     return $this->arrAttributes[$strKey];
  360.                 }
  361.                 if (isset($this->arrConfiguration[$strKey]))
  362.                 {
  363.                     return $this->arrConfiguration[$strKey];
  364.                 }
  365.                 break;
  366.         }
  367.         return parent::__get($strKey);
  368.     }
  369.     /**
  370.      * Check whether an object property exists
  371.      *
  372.      * @param string $strKey The property name
  373.      *
  374.      * @return boolean True if the property exists
  375.      */
  376.     public function __isset($strKey)
  377.     {
  378.         switch ($strKey)
  379.         {
  380.             case 'id':
  381.                 return isset($this->strId);
  382.             case 'name':
  383.                 return isset($this->strName);
  384.             case 'label':
  385.                 return isset($this->strLabel);
  386.             case 'value':
  387.                 return isset($this->varValue);
  388.             case 'class':
  389.                 return isset($this->strClass);
  390.             case 'template':
  391.                 return isset($this->strTemplate);
  392.             case 'wizard':
  393.                 return isset($this->strWizard);
  394.             case 'required':
  395.                 return isset($this->arrConfiguration[$strKey]);
  396.             case 'forAttribute':
  397.                 return isset($this->blnForAttribute);
  398.             case 'dataContainer':
  399.                 return isset($this->objDca);
  400.             case 'activeRecord':
  401.                 return isset($this->objDca->activeRecord);
  402.             default:
  403.                 return isset($this->arrAttributes[$strKey]) || isset($this->arrConfiguration[$strKey]);
  404.         }
  405.     }
  406.     /**
  407.      * Add an attribute
  408.      *
  409.      * @param string $strName  The attribute name
  410.      * @param mixed  $varValue The attribute value
  411.      */
  412.     public function addAttribute($strName$varValue)
  413.     {
  414.         $this->arrAttributes[$strName] = $varValue;
  415.     }
  416.     /**
  417.      * Add an error message
  418.      *
  419.      * @param string $strError The error message
  420.      */
  421.     public function addError($strError)
  422.     {
  423.         $this->class 'error';
  424.         $this->arrErrors[] = $strError;
  425.     }
  426.     /**
  427.      * Return true if the widget has errors
  428.      *
  429.      * @return boolean True if there are errors
  430.      */
  431.     public function hasErrors()
  432.     {
  433.         return !empty($this->arrErrors);
  434.     }
  435.     /**
  436.      * Return the errors array
  437.      *
  438.      * @return array An array of error messages
  439.      */
  440.     public function getErrors()
  441.     {
  442.         return $this->arrErrors;
  443.     }
  444.     /**
  445.      * Return a particular error as string
  446.      *
  447.      * @param integer $intIndex The message index
  448.      *
  449.      * @return string The corresponding error message
  450.      */
  451.     public function getErrorAsString($intIndex=0)
  452.     {
  453.         return $this->arrErrors[$intIndex];
  454.     }
  455.     /**
  456.      * Return all errors as string separated by a given separator
  457.      *
  458.      * @param string $strSeparator An optional separator (defaults to "<br>")
  459.      *
  460.      * @return string The error messages string
  461.      */
  462.     public function getErrorsAsString($strSeparator=null)
  463.     {
  464.         if ($strSeparator === null)
  465.         {
  466.             $strSeparator '<br' $this->strTagEnding "\n";
  467.         }
  468.         return $this->hasErrors() ? implode($strSeparator$this->arrErrors) : '';
  469.     }
  470.     /**
  471.      * Return a particular error as HTML string
  472.      *
  473.      * @param integer $intIndex The message index
  474.      *
  475.      * @return string The HTML markup of the corresponding error message
  476.      */
  477.     public function getErrorAsHTML($intIndex=0)
  478.     {
  479.         return $this->hasErrors() ? sprintf('<p class="%s">%s</p>', ((TL_MODE == 'BE') ? 'tl_error tl_tip' 'error'), $this->arrErrors[$intIndex]) : '';
  480.     }
  481.     /**
  482.      * Return true if the widgets submits user input
  483.      *
  484.      * @return boolean True if the widget submits user input
  485.      */
  486.     public function submitInput()
  487.     {
  488.         return $this->blnSubmitInput;
  489.     }
  490.     /**
  491.      * Parse the template file and return it as string
  492.      *
  493.      * @param array $arrAttributes An optional attributes array
  494.      *
  495.      * @return string The template markup
  496.      */
  497.     public function parse($arrAttributes=null)
  498.     {
  499.         if (!$this->strTemplate)
  500.         {
  501.             return '';
  502.         }
  503.         $this->addAttributes($arrAttributes);
  504.         $this->mandatoryField $GLOBALS['TL_LANG']['MSC']['mandatory'];
  505.         if ($this->customTpl)
  506.         {
  507.             $request System::getContainer()->get('request_stack')->getCurrentRequest();
  508.             // Use the custom template unless it is a back end request
  509.             if (!$request || !System::getContainer()->get('contao.routing.scope_matcher')->isBackendRequest($request))
  510.             {
  511.                 $this->strTemplate $this->customTpl;
  512.             }
  513.         }
  514.         $strBuffer $this->inherit();
  515.         // HOOK: add custom parse filters (see #5553)
  516.         if (isset($GLOBALS['TL_HOOKS']['parseWidget']) && \is_array($GLOBALS['TL_HOOKS']['parseWidget']))
  517.         {
  518.             foreach ($GLOBALS['TL_HOOKS']['parseWidget'] as $callback)
  519.             {
  520.                 $this->import($callback[0]);
  521.                 $strBuffer $this->{$callback[0]}->{$callback[1]}($strBuffer$this);
  522.             }
  523.         }
  524.         return $strBuffer;
  525.     }
  526.     /**
  527.      * Generate the label and return it as string
  528.      *
  529.      * @return string The label markup
  530.      */
  531.     public function generateLabel()
  532.     {
  533.         if (!$this->strLabel)
  534.         {
  535.             return '';
  536.         }
  537.         return sprintf(
  538.             '<label%s%s>%s%s%s</label>',
  539.             ($this->blnForAttribute ' for="ctrl_' $this->strId '"' ''),
  540.             ($this->strClass ' class="' $this->strClass '"' ''),
  541.             ($this->mandatory '<span class="invisible">' $GLOBALS['TL_LANG']['MSC']['mandatory'] . ' </span>' ''),
  542.             $this->strLabel,
  543.             ($this->mandatory '<span class="mandatory">*</span>' '')
  544.         );
  545.     }
  546.     /**
  547.      * Generate the widget and return it as string
  548.      *
  549.      * @return string The widget markup
  550.      */
  551.     abstract public function generate();
  552.     /**
  553.      * Generate the widget with error message and return it as string
  554.      *
  555.      * @param boolean $blnSwitchOrder If true, the error message will be shown below the field
  556.      *
  557.      * @return string The form field markup
  558.      */
  559.     public function generateWithError($blnSwitchOrder=false)
  560.     {
  561.         $strWidget $this->generate();
  562.         $strError $this->getErrorAsHTML();
  563.         return $blnSwitchOrder $strWidget $strError $strError $strWidget;
  564.     }
  565.     /**
  566.      * Return all attributes as string
  567.      *
  568.      * @param array $arrStrip An optional array with attributes to strip
  569.      *
  570.      * @return string The attributes string
  571.      */
  572.     public function getAttributes($arrStrip=array())
  573.     {
  574.         $strAttributes '';
  575.         foreach (array_keys($this->arrAttributes) as $strKey)
  576.         {
  577.             if (!\in_array($strKey$arrStrip))
  578.             {
  579.                 $strAttributes .= $this->getAttribute($strKey);
  580.             }
  581.         }
  582.         return $strAttributes;
  583.     }
  584.     /**
  585.      * Return a single attribute
  586.      *
  587.      * @param string $strKey The attribute name
  588.      *
  589.      * @return string The attribute markup
  590.      */
  591.     public function getAttribute($strKey)
  592.     {
  593.         if (!isset($this->arrAttributes[$strKey]))
  594.         {
  595.             return '';
  596.         }
  597.         $varValue $this->arrAttributes[$strKey];
  598.         // Prevent the autofocus attribute from being added multiple times (see #8281)
  599.         if ($strKey == 'autofocus')
  600.         {
  601.             unset($this->arrAttributes[$strKey]);
  602.         }
  603.         if ($strKey == 'disabled' || $strKey == 'readonly' || $strKey == 'required' || $strKey == 'autofocus' || $strKey == 'multiple')
  604.         {
  605.             return ' ' $strKey;
  606.         }
  607.         if ('' !== (string) $varValue)
  608.         {
  609.             return ' ' $strKey '="' StringUtil::specialchars($varValue) . '"';
  610.         }
  611.         return '';
  612.     }
  613.     /**
  614.      * Set a callback to fetch the widget input instead of using getPost()
  615.      *
  616.      * @param callable|null $callback The callback
  617.      *
  618.      * @return $this The widget object
  619.      */
  620.     public function setInputCallback(callable $callback=null)
  621.     {
  622.         $this->inputCallback $callback;
  623.         return $this;
  624.     }
  625.     /**
  626.      * Validate the user input and set the value
  627.      */
  628.     public function validate()
  629.     {
  630.         $varValue $this->validator($this->getPost($this->strName));
  631.         if ($this->hasErrors())
  632.         {
  633.             $this->class 'error';
  634.         }
  635.         $this->varValue $varValue;
  636.     }
  637.     /**
  638.      * Find and return a $_POST variable
  639.      *
  640.      * @param string $strKey The variable name
  641.      *
  642.      * @return mixed The variable value
  643.      */
  644.     protected function getPost($strKey)
  645.     {
  646.         if (\is_callable($this->inputCallback))
  647.         {
  648.             return ($this->inputCallback)();
  649.         }
  650.         if ($this->useRawRequestData === true)
  651.         {
  652.             $request System::getContainer()->get('request_stack')->getCurrentRequest();
  653.             return $request->request->get($strKey);
  654.         }
  655.         $strMethod $this->allowHtml 'postHtml' 'post';
  656.         if ($this->preserveTags)
  657.         {
  658.             $strMethod 'postRaw';
  659.         }
  660.         // Support arrays (thanks to Andreas Schempp)
  661.         $arrParts explode('['str_replace(']''', (string) $strKey));
  662.         $varValue Input::$strMethod(array_shift($arrParts), $this->decodeEntities);
  663.         foreach ($arrParts as $part)
  664.         {
  665.             if (!\is_array($varValue))
  666.             {
  667.                 break;
  668.             }
  669.             $varValue $varValue[$part] ?? null;
  670.         }
  671.         return $varValue;
  672.     }
  673.     /**
  674.      * Recursively validate an input variable
  675.      *
  676.      * @param mixed $varInput The user input
  677.      *
  678.      * @return mixed The original or modified user input
  679.      */
  680.     protected function validator($varInput)
  681.     {
  682.         if (\is_array($varInput))
  683.         {
  684.             foreach ($varInput as $k=>$v)
  685.             {
  686.                 $varInput[$k] = $this->validator($v);
  687.             }
  688.             return $varInput;
  689.         }
  690.         if (!$this->doNotTrim)
  691.         {
  692.             $varInput trim($varInput);
  693.         }
  694.         if ((string) $varInput === '')
  695.         {
  696.             if (!$this->mandatory)
  697.             {
  698.                 return '';
  699.             }
  700.             if (!$this->strLabel)
  701.             {
  702.                 $this->addError($GLOBALS['TL_LANG']['ERR']['mdtryNoLabel']);
  703.             }
  704.             else
  705.             {
  706.                 $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['mandatory'], $this->strLabel));
  707.             }
  708.         }
  709.         if ($this->minlength && $varInput && mb_strlen($varInput) < $this->minlength)
  710.         {
  711.             $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['minlength'], $this->strLabel$this->minlength));
  712.         }
  713.         if ($this->maxlength && $varInput && mb_strlen($varInput) > $this->maxlength)
  714.         {
  715.             $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['maxlength'], $this->strLabel$this->maxlength));
  716.         }
  717.         if ($this->minval && is_numeric($varInput) && $varInput $this->minval)
  718.         {
  719.             $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['minval'], $this->strLabel$this->minval));
  720.         }
  721.         if ($this->maxval && is_numeric($varInput) && $varInput $this->maxval)
  722.         {
  723.             $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['maxval'], $this->strLabel$this->maxval));
  724.         }
  725.         if ($this->rgxp)
  726.         {
  727.             switch ($this->rgxp)
  728.             {
  729.                 case strncmp($this->rgxp'digit_'6) === 0:
  730.                     // Special validation rule for style sheets
  731.                     $textual explode('_'$this->rgxp);
  732.                     array_shift($textual);
  733.                     if (\in_array($varInput$textual) || strncmp($varInput'$'1) === 0)
  734.                     {
  735.                         break;
  736.                     }
  737.                     // no break
  738.                 case 'digit':
  739.                     // Support decimal commas and convert them automatically (see #3488)
  740.                     if (substr_count($varInput',') == && strpos($varInput'.') === false)
  741.                     {
  742.                         $varInput str_replace(',''.'$varInput);
  743.                     }
  744.                     if (!Validator::isNumeric($varInput))
  745.                     {
  746.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['digit'], $this->strLabel));
  747.                     }
  748.                     break;
  749.                 case 'natural':
  750.                     if (!Validator::isNatural($varInput))
  751.                     {
  752.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['natural'], $this->strLabel));
  753.                     }
  754.                     break;
  755.                 case 'alpha':
  756.                     if (!Validator::isAlphabetic($varInput))
  757.                     {
  758.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['alpha'], $this->strLabel));
  759.                     }
  760.                     break;
  761.                 case 'alnum':
  762.                     if (!Validator::isAlphanumeric($varInput))
  763.                     {
  764.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['alnum'], $this->strLabel));
  765.                     }
  766.                     break;
  767.                 case 'extnd':
  768.                     if (!Validator::isExtendedAlphanumeric(html_entity_decode($varInput)))
  769.                     {
  770.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['extnd'], $this->strLabel));
  771.                     }
  772.                     break;
  773.                 case 'date':
  774.                     if (!Validator::isDate($varInput))
  775.                     {
  776.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['date'], Date::getInputFormat(Date::getNumericDateFormat())));
  777.                     }
  778.                     else
  779.                     {
  780.                         // Validate the date (see #5086)
  781.                         try
  782.                         {
  783.                             new Date($varInputDate::getNumericDateFormat());
  784.                         }
  785.                         catch (\OutOfBoundsException $e)
  786.                         {
  787.                             $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['invalidDate'], $varInput));
  788.                         }
  789.                     }
  790.                     break;
  791.                 case 'time':
  792.                     if (!Validator::isTime($varInput))
  793.                     {
  794.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['time'], Date::getInputFormat(Date::getNumericTimeFormat())));
  795.                     }
  796.                     break;
  797.                 case 'datim':
  798.                     if (!Validator::isDatim($varInput))
  799.                     {
  800.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['dateTime'], Date::getInputFormat(Date::getNumericDatimFormat())));
  801.                     }
  802.                     else
  803.                     {
  804.                         // Validate the date (see #5086)
  805.                         try
  806.                         {
  807.                             new Date($varInputDate::getNumericDatimFormat());
  808.                         }
  809.                         catch (\OutOfBoundsException $e)
  810.                         {
  811.                             $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['invalidDate'], $varInput));
  812.                         }
  813.                     }
  814.                     break;
  815.                 case 'friendly':
  816.                     list ($strName$varInput) = StringUtil::splitFriendlyEmail($varInput);
  817.                     // no break
  818.                 case 'email':
  819.                     if (!Validator::isEmail($varInput))
  820.                     {
  821.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['email'], $this->strLabel));
  822.                     }
  823.                     if ($this->rgxp == 'friendly' && !empty($strName))
  824.                     {
  825.                         $varInput $strName ' [' $varInput ']';
  826.                     }
  827.                     break;
  828.                 case 'emails':
  829.                     // Check whether the current value is list of valid e-mail addresses
  830.                     $arrEmails StringUtil::trimsplit(','$varInput);
  831.                     foreach ($arrEmails as $strEmail)
  832.                     {
  833.                         $strEmail Idna::encodeEmail($strEmail);
  834.                         if (!Validator::isEmail($strEmail))
  835.                         {
  836.                             $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['emails'], $this->strLabel));
  837.                             break;
  838.                         }
  839.                     }
  840.                     break;
  841.                 case 'url':
  842.                     $varInput StringUtil::specialcharsUrl($varInput);
  843.                     if ($this->decodeEntities)
  844.                     {
  845.                         $varInput StringUtil::decodeEntities($varInput);
  846.                     }
  847.                     if (!Validator::isUrl($varInput))
  848.                     {
  849.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['url'], $this->strLabel));
  850.                     }
  851.                     break;
  852.                 case 'alias':
  853.                     if (!Validator::isAlias($varInput))
  854.                     {
  855.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['alias'], $this->strLabel));
  856.                     }
  857.                     break;
  858.                 case 'folderalias':
  859.                     if (!Validator::isFolderAlias($varInput))
  860.                     {
  861.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['folderalias'], $this->strLabel));
  862.                     }
  863.                     break;
  864.                 case 'phone':
  865.                     if (!Validator::isPhone(html_entity_decode($varInput)))
  866.                     {
  867.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['phone'], $this->strLabel));
  868.                     }
  869.                     break;
  870.                 case 'prcnt':
  871.                     if (!Validator::isPercent($varInput))
  872.                     {
  873.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['prcnt'], $this->strLabel));
  874.                     }
  875.                     break;
  876.                 case 'locale':
  877.                     if (!Validator::isLocale($varInput))
  878.                     {
  879.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['locale'], $this->strLabel));
  880.                     }
  881.                     break;
  882.                 case 'language':
  883.                     if (!Validator::isLanguage($varInput))
  884.                     {
  885.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['language'], $this->strLabel));
  886.                     }
  887.                     break;
  888.                 case 'google+':
  889.                     if (!Validator::isGooglePlusId($varInput))
  890.                     {
  891.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['invalidGoogleId'], $this->strLabel));
  892.                     }
  893.                     break;
  894.                 case 'fieldname':
  895.                     if (!Validator::isFieldName($varInput))
  896.                     {
  897.                         $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['invalidFieldName'], $this->strLabel));
  898.                     }
  899.                     break;
  900.                 // HOOK: pass unknown tags to callback functions
  901.                 default:
  902.                     if (isset($GLOBALS['TL_HOOKS']['addCustomRegexp']) && \is_array($GLOBALS['TL_HOOKS']['addCustomRegexp']))
  903.                     {
  904.                         foreach ($GLOBALS['TL_HOOKS']['addCustomRegexp'] as $callback)
  905.                         {
  906.                             $this->import($callback[0]);
  907.                             $break $this->{$callback[0]}->{$callback[1]}($this->rgxp$varInput$this);
  908.                             // Stop the loop if a callback returned true
  909.                             if ($break === true)
  910.                             {
  911.                                 break;
  912.                             }
  913.                         }
  914.                     }
  915.                     break;
  916.             }
  917.         }
  918.         if ($this->isHexColor && $varInput && strncmp($varInput'$'1) !== 0)
  919.         {
  920.             $varInput preg_replace('/[^a-f0-9]+/i'''$varInput);
  921.         }
  922.         if ($this->nospace && preg_match('/[\t ]+/'$varInput))
  923.         {
  924.             $this->addError(sprintf($GLOBALS['TL_LANG']['ERR']['noSpace'], $this->strLabel));
  925.         }
  926.         if ($this->spaceToUnderscore)
  927.         {
  928.             $varInput preg_replace('/\s+/''_'trim($varInput));
  929.         }
  930.         if (\is_bool($this->trailingSlash) && $varInput)
  931.         {
  932.             $varInput preg_replace('/\/+$/'''$varInput) . ($this->trailingSlash '/' '');
  933.         }
  934.         return $varInput;
  935.     }
  936.     /**
  937.      * Take an associative array and add it to the object's attributes
  938.      *
  939.      * @param array $arrAttributes An array of attributes
  940.      */
  941.     public function addAttributes($arrAttributes)
  942.     {
  943.         if (!\is_array($arrAttributes))
  944.         {
  945.             return;
  946.         }
  947.         foreach ($arrAttributes as $k=>$v)
  948.         {
  949.             $this->$k $v;
  950.         }
  951.     }
  952.     /**
  953.      * Check whether an option is checked
  954.      *
  955.      * @param array $arrOption The options array
  956.      *
  957.      * @return string The "checked" attribute or an empty string
  958.      */
  959.     protected function isChecked($arrOption)
  960.     {
  961.         if (empty($this->varValue) && empty($_POST) && ($arrOption['default'] ?? null))
  962.         {
  963.             return static::optionChecked(11);
  964.         }
  965.         return static::optionChecked($arrOption['value'] ?? null$this->varValue);
  966.     }
  967.     /**
  968.      * Check whether an option is selected
  969.      *
  970.      * @param array $arrOption The options array
  971.      *
  972.      * @return string The "selected" attribute or an empty string
  973.      */
  974.     protected function isSelected($arrOption)
  975.     {
  976.         if (empty($this->varValue) && empty($_POST) && ($arrOption['default'] ?? null))
  977.         {
  978.             return static::optionSelected(11);
  979.         }
  980.         return static::optionSelected($arrOption['value'] ?? null$this->varValue);
  981.     }
  982.     /**
  983.      * Return a "selected" attribute if the option is selected
  984.      *
  985.      * @param string $strOption The option to check
  986.      * @param mixed  $varValues One or more values to check against
  987.      *
  988.      * @return string The attribute or an empty string
  989.      */
  990.     public static function optionSelected($strOption$varValues)
  991.     {
  992.         if ($strOption === '')
  993.         {
  994.             return '';
  995.         }
  996.         return (\is_array($varValues) ? \in_array($strOption$varValues) : $strOption == $varValues) ? ' selected' '';
  997.     }
  998.     /**
  999.      * Return a "checked" attribute if the option is checked
  1000.      *
  1001.      * @param string $strOption The option to check
  1002.      * @param mixed  $varValues One or more values to check against
  1003.      *
  1004.      * @return string The attribute or an empty string
  1005.      */
  1006.     public static function optionChecked($strOption$varValues)
  1007.     {
  1008.         if ($strOption === '')
  1009.         {
  1010.             return '';
  1011.         }
  1012.         return (\is_array($varValues) ? \in_array($strOption$varValues) : $strOption == $varValues) ? ' checked' '';
  1013.     }
  1014.     /**
  1015.      * Check whether an input is one of the given options
  1016.      *
  1017.      * @param mixed $varInput The input string or array
  1018.      *
  1019.      * @return boolean True if the selected option exists
  1020.      */
  1021.     protected function isValidOption($varInput)
  1022.     {
  1023.         if (!\is_array($varInput))
  1024.         {
  1025.             $varInput = array($varInput);
  1026.         }
  1027.         // Check each option
  1028.         foreach ($varInput as $strInput)
  1029.         {
  1030.             $blnFound false;
  1031.             foreach ($this->arrOptions as $v)
  1032.             {
  1033.                 // Single dimensional array
  1034.                 if (\array_key_exists('value'$v))
  1035.                 {
  1036.                     if ($strInput == $v['value'])
  1037.                     {
  1038.                         $blnFound true;
  1039.                     }
  1040.                 }
  1041.                 // Multi-dimensional array
  1042.                 else
  1043.                 {
  1044.                     foreach ($v as $vv)
  1045.                     {
  1046.                         if ($strInput == $vv['value'])
  1047.                         {
  1048.                             $blnFound true;
  1049.                         }
  1050.                     }
  1051.                 }
  1052.             }
  1053.             if (!$blnFound)
  1054.             {
  1055.                 return false;
  1056.             }
  1057.         }
  1058.         return true;
  1059.     }
  1060.     /**
  1061.      * Extract the Widget attributes from a Data Container array
  1062.      *
  1063.      * @param array                     $arrData  The field configuration array
  1064.      * @param string                    $strName  The field name in the form
  1065.      * @param mixed                     $varValue The field value
  1066.      * @param string                    $strField The field name in the database
  1067.      * @param string                    $strTable The table name in the database
  1068.      * @param DataContainer|Module|null $objDca   An optional DataContainer or Module object
  1069.      *
  1070.      * @return array An attributes array that can be passed to a widget
  1071.      */
  1072.     public static function getAttributesFromDca($arrData$strName$varValue=null$strField=''$strTable=''$objDca=null)
  1073.     {
  1074.         $arrAttributes $arrData['eval'] ?? array();
  1075.         if (method_exists(System::getContainer(), 'getParameterBag'))
  1076.         {
  1077.             $objParameterBag System::getContainer()->getParameterBag();
  1078.             foreach ($arrAttributes as $strAttrKey => $varAttrValue)
  1079.             {
  1080.                 if (!\is_string($varAttrValue) || !preg_match('/%[a-z][a-z0-9_]*\.[a-z0-9_.]+%/i'$varAttrValue))
  1081.                 {
  1082.                     continue;
  1083.                 }
  1084.                 $varAttrValue $objParameterBag->resolveValue($varAttrValue);
  1085.                 $varAttrValue $objParameterBag->unescapeValue($varAttrValue);
  1086.                 $arrAttributes[$strAttrKey] = $varAttrValue;
  1087.             }
  1088.         }
  1089.         $arrAttributes['id'] = $strName;
  1090.         $arrAttributes['name'] = $strName;
  1091.         $arrAttributes['strField'] = $strField;
  1092.         $arrAttributes['strTable'] = $strTable;
  1093.         $arrAttributes['label'] = (($label = \is_array($arrData['label'] ?? null) ? $arrData['label'][0] : $arrData['label'] ?? null) !== null) ? $label $strField;
  1094.         $arrAttributes['description'] = $arrData['label'][1] ?? null;
  1095.         $arrAttributes['type'] = $arrData['inputType'] ?? null;
  1096.         $arrAttributes['dataContainer'] = $objDca;
  1097.         $arrAttributes['value'] = StringUtil::deserialize($varValue);
  1098.         // Internet Explorer does not support onchange for checkboxes and radio buttons
  1099.         if ($arrData['eval']['submitOnChange'] ?? null)
  1100.         {
  1101.             if (($arrData['inputType'] ?? null) == 'checkbox' || ($arrData['inputType'] ?? null) == 'checkboxWizard' || ($arrData['inputType'] ?? null) == 'radio' || ($arrData['inputType'] ?? null) == 'radioTable')
  1102.             {
  1103.                 $arrAttributes['onclick'] = trim(($arrAttributes['onclick'] ?? '') . " Backend.autoSubmit('" $strTable "')");
  1104.             }
  1105.             else
  1106.             {
  1107.                 $arrAttributes['onchange'] = trim(($arrAttributes['onchange'] ?? '') . " Backend.autoSubmit('" $strTable "')");
  1108.             }
  1109.         }
  1110.         if (!empty($arrData['eval']['preserveTags']))
  1111.         {
  1112.             $arrAttributes['allowHtml'] = true;
  1113.         }
  1114.         if (!isset($arrAttributes['allowHtml']))
  1115.         {
  1116.             $rte $arrData['eval']['rte'] ?? '';
  1117.             $arrAttributes['allowHtml'] = 'ace|html' === $rte || === strpos($rte'tiny');
  1118.         }
  1119.         // Decode entities if HTML is allowed
  1120.         if ($arrAttributes['allowHtml'] || ($arrData['inputType'] ?? null) == 'fileTree')
  1121.         {
  1122.             $arrAttributes['decodeEntities'] = true;
  1123.         }
  1124.         // Add Ajax event
  1125.         if (($arrData['inputType'] ?? null) == 'checkbox' && ($arrData['eval']['submitOnChange'] ?? null) && \is_array($GLOBALS['TL_DCA'][$strTable]['subpalettes'] ?? null) && \array_key_exists($strField$GLOBALS['TL_DCA'][$strTable]['subpalettes']))
  1126.         {
  1127.             $arrAttributes['onclick'] = "AjaxRequest.toggleSubpalette(this, 'sub_" $strName "', '" $strField "')";
  1128.         }
  1129.         // Options callback
  1130.         if (\is_array($arrData['options_callback'] ?? null))
  1131.         {
  1132.             $arrCallback $arrData['options_callback'];
  1133.             $arrData['options'] = static::importStatic($arrCallback[0])->{$arrCallback[1]}($objDca);
  1134.         }
  1135.         elseif (\is_callable($arrData['options_callback'] ?? null))
  1136.         {
  1137.             $arrData['options'] = $arrData['options_callback']($objDca);
  1138.         }
  1139.         // Foreign key
  1140.         elseif (isset($arrData['foreignKey']))
  1141.         {
  1142.             $arrKey explode('.'$arrData['foreignKey'], 2);
  1143.             $objOptions Database::getInstance()->query("SELECT id, " $arrKey[1] . " AS value FROM " $arrKey[0] . " WHERE tstamp>0 ORDER BY value");
  1144.             $arrData['options'] = array();
  1145.             while ($objOptions->next())
  1146.             {
  1147.                 $arrData['options'][$objOptions->id] = $objOptions->value;
  1148.             }
  1149.         }
  1150.         // Add default option to single checkbox
  1151.         if (($arrData['inputType'] ?? null) == 'checkbox' && !isset($arrData['options']) && !isset($arrData['options_callback']) && !isset($arrData['foreignKey']))
  1152.         {
  1153.             if (TL_MODE == 'FE' && isset($arrAttributes['description']))
  1154.             {
  1155.                 $arrAttributes['options'][] = array('value'=>1'label'=>$arrAttributes['description']);
  1156.             }
  1157.             else
  1158.             {
  1159.                 $arrAttributes['options'][] = array('value'=>1'label'=>$arrAttributes['label']);
  1160.             }
  1161.         }
  1162.         // Add options
  1163.         if (\is_array($arrData['options'] ?? null))
  1164.         {
  1165.             $blnIsAssociative = ($arrData['eval']['isAssociative'] ?? null) || ArrayUtil::isAssoc($arrData['options'] ?? null);
  1166.             $blnUseReference = isset($arrData['reference']);
  1167.             if (($arrData['eval']['includeBlankOption'] ?? null) && !($arrData['eval']['multiple'] ?? null))
  1168.             {
  1169.                 $strLabel $arrData['eval']['blankOptionLabel'] ?? '-';
  1170.                 $arrAttributes['options'][] = array('value'=>'''label'=>$strLabel);
  1171.             }
  1172.             $unknown = (array) $arrAttributes['value'];
  1173.             foreach ($arrData['options'] as $k=>$v)
  1174.             {
  1175.                 if (!\is_array($v))
  1176.                 {
  1177.                     $value $blnIsAssociative $k $v;
  1178.                     if (($i array_search($value$unknown)) !== false)
  1179.                     {
  1180.                         unset($unknown[$i]);
  1181.                     }
  1182.                     $arrAttributes['options'][] = array('value'=>$value'label'=>($blnUseReference && isset($arrData['reference'][$v]) ? ((($ref = (\is_array($arrData['reference'][$v]) ? $arrData['reference'][$v][0] : $arrData['reference'][$v])) != false) ? $ref $v) : $v));
  1183.                     continue;
  1184.                 }
  1185.                 $key $blnUseReference && isset($arrData['reference'][$k]) ? ((($ref = (\is_array($arrData['reference'][$k]) ? $arrData['reference'][$k][0] : $arrData['reference'][$k])) != false) ? $ref $k) : $k;
  1186.                 $blnIsAssoc ArrayUtil::isAssoc($v);
  1187.                 foreach ($v as $kk=>$vv)
  1188.                 {
  1189.                     $value $blnIsAssoc $kk $vv;
  1190.                     if (($i array_search($value$unknown)) !== false)
  1191.                     {
  1192.                         unset($unknown[$i]);
  1193.                     }
  1194.                     $arrAttributes['options'][$key][] = array('value'=>$value'label'=>($blnUseReference && isset($arrData['reference'][$vv]) ? ((($ref = (\is_array($arrData['reference'][$vv]) ? $arrData['reference'][$vv][0] : $arrData['reference'][$vv])) != false) ? $ref $vv) : $vv));
  1195.                 }
  1196.             }
  1197.             $arrAttributes['unknownOption'] = array_filter($unknown);
  1198.         }
  1199.         if (\is_array($arrAttributes['sql'] ?? null) && !isset($arrAttributes['sql']['columnDefinition']))
  1200.         {
  1201.             if (!isset($arrAttributes['maxlength']) && isset($arrAttributes['sql']['length']))
  1202.             {
  1203.                 $arrAttributes['maxlength'] = $arrAttributes['sql']['length'];
  1204.             }
  1205.             if (!isset($arrAttributes['unique']) && isset($arrAttributes['sql']['customSchemaOptions']['unique']))
  1206.             {
  1207.                 $arrAttributes['unique'] = $arrAttributes['sql']['customSchemaOptions']['unique'];
  1208.             }
  1209.         }
  1210.         // Convert timestamps
  1211.         if ($varValue !== null && $varValue !== '' && \in_array($arrData['eval']['rgxp'] ?? null, array('date''time''datim')))
  1212.         {
  1213.             $objDate = new Date($varValueDate::getFormatFromRgxp($arrData['eval']['rgxp']));
  1214.             $arrAttributes['value'] = $objDate->{$arrData['eval']['rgxp']};
  1215.         }
  1216.         // Convert URL insert tags
  1217.         if ($varValue && 'url' === ($arrData['eval']['rgxp'] ?? null))
  1218.         {
  1219.             $arrAttributes['value'] = str_replace('|urlattr}}''}}'$varValue);
  1220.         }
  1221.         // Add the "rootNodes" array as attribute (see #3563)
  1222.         if (isset($arrData['rootNodes']) && !isset($arrData['eval']['rootNodes']))
  1223.         {
  1224.             $arrAttributes['rootNodes'] = $arrData['rootNodes'];
  1225.         }
  1226.         // HOOK: add custom logic
  1227.         if (isset($GLOBALS['TL_HOOKS']['getAttributesFromDca']) && \is_array($GLOBALS['TL_HOOKS']['getAttributesFromDca']))
  1228.         {
  1229.             foreach ($GLOBALS['TL_HOOKS']['getAttributesFromDca'] as $callback)
  1230.             {
  1231.                 $arrAttributes = static::importStatic($callback[0])->{$callback[1]}($arrAttributes$objDca);
  1232.             }
  1233.         }
  1234.         // Warn if someone uses the "encrypt" flag (see #8589)
  1235.         if (isset($arrAttributes['encrypt']))
  1236.         {
  1237.             trigger_deprecation('contao/core-bundle''4.0''Using the "encrypt" flag' . (!empty($strTable) && !empty($strField) ? ' on ' $strTable '.' $strField '') . ' has been deprecated and will no longer work in Contao 5.0. Use the load and save callbacks with a third-party library such as OpenSSL or phpseclib instead.');
  1238.         }
  1239.         return $arrAttributes;
  1240.     }
  1241.     /**
  1242.      * Return the empty value based on the SQL string
  1243.      *
  1244.      * @return string|integer|null The empty value
  1245.      */
  1246.     public function getEmptyValue()
  1247.     {
  1248.         if (!isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]['sql']))
  1249.         {
  1250.             return '';
  1251.         }
  1252.         return static::getEmptyValueByFieldType($GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]['sql']);
  1253.     }
  1254.     /**
  1255.      * Return the empty value based on the SQL string
  1256.      *
  1257.      * @param string|array $sql The SQL string
  1258.      *
  1259.      * @return string|integer|null The empty value
  1260.      */
  1261.     public static function getEmptyValueByFieldType($sql)
  1262.     {
  1263.         if (empty($sql))
  1264.         {
  1265.             return '';
  1266.         }
  1267.         if (\is_array($sql))
  1268.         {
  1269.             if (isset($sql['columnDefinition']))
  1270.             {
  1271.                 $sql $sql['columnDefinition'];
  1272.             }
  1273.             else
  1274.             {
  1275.                 if (isset($sql['notnull']) && !$sql['notnull'])
  1276.                 {
  1277.                     return null;
  1278.                 }
  1279.                 if (\in_array($sql['type'], array(Types::BIGINTTypes::DECIMALTypes::INTEGERTypes::SMALLINTTypes::FLOAT)))
  1280.                 {
  1281.                     return 0;
  1282.                 }
  1283.                 if ($sql['type'] === Types::BOOLEAN)
  1284.                 {
  1285.                     return false;
  1286.                 }
  1287.                 return '';
  1288.             }
  1289.         }
  1290.         if (stripos($sql'NOT NULL') === false)
  1291.         {
  1292.             return null;
  1293.         }
  1294.         $type strtolower(preg_replace('/^([A-Za-z]+)[( ].*$/''$1'$sql));
  1295.         if (\in_array($type, array('int''integer''tinyint''smallint''mediumint''bigint''float''double''dec''decimal')))
  1296.         {
  1297.             return 0;
  1298.         }
  1299.         return '';
  1300.     }
  1301.     /**
  1302.      * Return either an empty string or null based on the SQL string
  1303.      *
  1304.      * @return string|int|null The empty value
  1305.      */
  1306.     public function getEmptyStringOrNull()
  1307.     {
  1308.         if (!isset($GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]['sql']))
  1309.         {
  1310.             return '';
  1311.         }
  1312.         return static::getEmptyStringOrNullByFieldType($GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->strField]['sql']);
  1313.     }
  1314.     /**
  1315.      * Return either an empty string or null based on the SQL string
  1316.      *
  1317.      * @param string $sql The SQL string
  1318.      *
  1319.      * @return string|null The empty string or null
  1320.      */
  1321.     public static function getEmptyStringOrNullByFieldType($sql)
  1322.     {
  1323.         if (empty($sql))
  1324.         {
  1325.             return '';
  1326.         }
  1327.         return static::getEmptyValueByFieldType($sql) === null null '';
  1328.     }
  1329.     /**
  1330.      * Generate a submit button
  1331.      *
  1332.      * @return string The submit button markup
  1333.      *
  1334.      * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  1335.      */
  1336.     protected function addSubmit()
  1337.     {
  1338.         trigger_deprecation('contao/core-bundle''4.0''Using "Contao\Widget::addSubmit()" has been deprecated and will no longer work in Contao 5.0.');
  1339.         return '';
  1340.     }
  1341. }
  1342. class_alias(Widget::class, 'Widget');