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

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