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

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