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

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