modifiers[$modifier])) { return $this->modifiers[$modifier]; } switch ($modifier) { case 'cat': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CatModifierCompiler(); break; case 'count_characters': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountCharactersModifierCompiler(); break; case 'count_paragraphs': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountParagraphsModifierCompiler(); break; case 'count_sentences': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountSentencesModifierCompiler(); break; case 'count_words': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\CountWordsModifierCompiler(); break; case 'default': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\DefaultModifierCompiler(); break; case 'empty': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\EmptyModifierCompiler(); break; case 'escape': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\EscapeModifierCompiler(); break; case 'from_charset': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\FromCharsetModifierCompiler(); break; case 'indent': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\IndentModifierCompiler(); break; case 'is_array': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\IsArrayModifierCompiler(); break; case 'isset': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\IssetModifierCompiler(); break; case 'json_encode': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\JsonEncodeModifierCompiler(); break; case 'lower': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\LowerModifierCompiler(); break; case 'nl2br': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\Nl2brModifierCompiler(); break; case 'noprint': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\NoPrintModifierCompiler(); break; case 'raw': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\RawModifierCompiler(); break; case 'round': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\RoundModifierCompiler(); break; case 'str_repeat': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StrRepeatModifierCompiler(); break; case 'string_format': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StringFormatModifierCompiler(); break; case 'strip': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StripModifierCompiler(); break; case 'strip_tags': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StripTagsModifierCompiler(); break; case 'strlen': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\StrlenModifierCompiler(); break; case 'substr': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\SubstrModifierCompiler(); break; case 'to_charset': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\ToCharsetModifierCompiler(); break; case 'unescape': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\UnescapeModifierCompiler(); break; case 'upper': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\UpperModifierCompiler(); break; case 'wordwrap': $this->modifiers[$modifier] = new \Smarty\Compile\Modifier\WordWrapModifierCompiler(); break; } return $this->modifiers[$modifier] ?? null; } public function getModifierCallback(string $modifierName) { switch ($modifierName) { case 'capitalize': return [$this, 'smarty_modifier_capitalize']; case 'count': return [$this, 'smarty_modifier_count']; case 'date_format': return [$this, 'smarty_modifier_date_format']; case 'debug_print_var': return [$this, 'smarty_modifier_debug_print_var']; case 'escape': return [$this, 'smarty_modifier_escape']; case 'explode': return [$this, 'smarty_modifier_explode']; case 'implode': return [$this, 'smarty_modifier_implode']; case 'in_array': return [$this, 'smarty_modifier_in_array']; case 'join': return [$this, 'smarty_modifier_join']; case 'mb_wordwrap': return [$this, 'smarty_modifier_mb_wordwrap']; case 'number_format': return [$this, 'smarty_modifier_number_format']; case 'regex_replace': return [$this, 'smarty_modifier_regex_replace']; case 'replace': return [$this, 'smarty_modifier_replace']; case 'spacify': return [$this, 'smarty_modifier_spacify']; case 'split': return [$this, 'smarty_modifier_split']; case 'truncate': return [$this, 'smarty_modifier_truncate']; } return null; } public function getFunctionHandler(string $functionName): ?\Smarty\FunctionHandler\FunctionHandlerInterface { if (isset($this->functionHandlers[$functionName])) { return $this->functionHandlers[$functionName]; } switch ($functionName) { case 'count': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Count(); break; case 'counter': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Counter(); break; case 'cycle': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Cycle(); break; case 'fetch': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Fetch(); break; case 'html_checkboxes': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlCheckboxes(); break; case 'html_image': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlImage(); break; case 'html_options': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlOptions(); break; case 'html_radios': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlRadios(); break; case 'html_select_date': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlSelectDate(); break; case 'html_select_time': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlSelectTime(); break; case 'html_table': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\HtmlTable(); break; case 'mailto': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Mailto(); break; case 'math': $this->functionHandlers[$functionName] = new \Smarty\FunctionHandler\Math(); break; } return $this->functionHandlers[$functionName] ?? null; } public function getBlockHandler(string $blockTagName): ?\Smarty\BlockHandler\BlockHandlerInterface { switch ($blockTagName) { case 'textformat': $this->blockHandlers[$blockTagName] = new \Smarty\BlockHandler\TextFormat(); break; } return $this->blockHandlers[$blockTagName] ?? null; } /** * Smarty spacify modifier plugin * Type: modifier * Name: spacify * Purpose: add spaces between characters in a string * * @author Monte Ohrt * * @param string $string input string * @param string $spacify_char string to insert between characters. * * @return string */ public function smarty_modifier_spacify($string, $spacify_char = ' ') { // well… what about charsets besides latin and UTF-8? return implode($spacify_char, preg_split('//' . \Smarty\Smarty::$_UTF8_MODIFIER, $string, -1, PREG_SPLIT_NO_EMPTY)); } /** * Smarty capitalize modifier plugin * Type: modifier * Name: capitalize * Purpose: capitalize words in the string * {@internal {$string|capitalize:true:true} is the fastest option for MBString enabled systems }} * * @param string $string string to capitalize * @param boolean $uc_digits also capitalize "x123" to "X123" * @param boolean $lc_rest capitalize first letters, lowercase all following letters "aAa" to "Aaa" * * @return string capitalized string * @author Monte Ohrt * @author Rodney Rehm */ public function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = false) { $string = (string) $string; if ($lc_rest) { // uppercase (including hyphenated words) $upper_string = mb_convert_case($string, MB_CASE_TITLE, \Smarty\Smarty::$_CHARSET); } else { // uppercase word breaks $upper_string = preg_replace_callback( "!(^|[^\p{L}'])([\p{Ll}])!S" . \Smarty\Smarty::$_UTF8_MODIFIER, function ($matches) { return stripslashes($matches[1]) . mb_convert_case(stripslashes($matches[2]), MB_CASE_UPPER, \Smarty\Smarty::$_CHARSET); }, $string ); } // check uc_digits case if (!$uc_digits) { if (preg_match_all( "!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . \Smarty\Smarty::$_UTF8_MODIFIER, $string, $matches, PREG_OFFSET_CAPTURE ) ) { foreach ($matches[ 1 ] as $match) { $upper_string = substr_replace( $upper_string, mb_strtolower($match[ 0 ], \Smarty\Smarty::$_CHARSET), $match[ 1 ], strlen($match[ 0 ]) ); } } } $upper_string = preg_replace_callback( "!((^|\s)['\"])(\w)!" . \Smarty\Smarty::$_UTF8_MODIFIER, function ($matches) { return stripslashes( $matches[ 1 ]) . mb_convert_case(stripslashes($matches[ 3 ]), MB_CASE_UPPER, \Smarty\Smarty::$_CHARSET ); }, $upper_string ); return $upper_string; } /** * Smarty count modifier plugin * Type: modifier * Name: count * Purpose: counts all elements in an array or in a Countable object * Input: * - Countable|array: array or object to count * - mode: int defaults to 0 for normal count mode, if set to 1 counts recursive * * @param mixed $arrayOrObject input array/object * @param int $mode count mode * * @return int */ public function smarty_modifier_count($arrayOrObject, $mode = 0) { /* * @see https://www.php.net/count * > Prior to PHP 8.0.0, if the parameter was neither an array nor an object that implements the Countable interface, * > 1 would be returned, unless value was null, in which case 0 would be returned. */ if ($arrayOrObject instanceof \Countable || is_array($arrayOrObject)) { return count($arrayOrObject, (int) $mode); } elseif ($arrayOrObject === null) { return 0; } return 1; } /** * Smarty date_format modifier plugin * Type: modifier * Name: date_format * Purpose: format datestamps via strftime * Input: * - string: input date string * - format: strftime format for output * - default_date: default date if $string is empty * * @author Monte Ohrt * * @param string $string input date string * @param string $format strftime format for output * @param string $default_date default date if $string is empty * @param string $formatter either 'strftime' or 'auto' * * @return string |void * @uses smarty_make_timestamp() */ public function smarty_modifier_date_format($string, $format = null, $default_date = '', $formatter = 'auto') { if ($format === null) { $format = \Smarty\Smarty::$_DATE_FORMAT; } if (!empty($string) && $string !== '0000-00-00' && $string !== '0000-00-00 00:00:00') { $timestamp = smarty_make_timestamp($string); } elseif (!empty($default_date)) { $timestamp = smarty_make_timestamp($default_date); } else { return; } if ($formatter === 'strftime' || ($formatter === 'auto' && strpos($format, '%') !== false)) { if (\Smarty\Smarty::$_IS_WINDOWS) { $_win_from = array( '%D', '%h', '%n', '%r', '%R', '%t', '%T' ); $_win_to = array( '%m/%d/%y', '%b', "\n", '%I:%M:%S %p', '%H:%M', "\t", '%H:%M:%S' ); if (strpos($format, '%e') !== false) { $_win_from[] = '%e'; $_win_to[] = sprintf('%\' 2d', date('j', $timestamp)); } if (strpos($format, '%l') !== false) { $_win_from[] = '%l'; $_win_to[] = sprintf('%\' 2d', date('h', $timestamp)); } $format = str_replace($_win_from, $_win_to, $format); } // @ to suppress deprecation errors when running in PHP8.1 or higher. return @strftime($format, $timestamp); } else { return date($format, $timestamp); } } /** * Smarty debug_print_var modifier plugin * Type: modifier * Name: debug_print_var * Purpose: formats variable contents for display in the console * * @author Monte Ohrt * * @param array|object $var variable to be formatted * @param int $max maximum recursion depth if $var is an array or object * @param int $length maximum string length if $var is a string * @param int $depth actual recursion depth * @param array $objects processed objects in actual depth to prevent recursive object processing * * @return string */ public function smarty_modifier_debug_print_var($var, $max = 10, $length = 40, $depth = 0, $objects = array()) { $_replace = array("\n" => '\n', "\r" => '\r', "\t" => '\t'); switch (gettype($var)) { case 'array': $results = 'Array (' . count($var) . ')'; if ($depth === $max) { break; } foreach ($var as $curr_key => $curr_val) { $results .= '
' . str_repeat(' ', $depth * 2) . '' . htmlspecialchars(strtr($curr_key, $_replace)) . ' => ' . $this->smarty_modifier_debug_print_var($curr_val, $max, $length, ++$depth, $objects); $depth--; } break; case 'object': $object_vars = get_object_vars($var); $results = '' . get_class($var) . ' Object (' . count($object_vars) . ')'; if (in_array($var, $objects)) { $results .= ' called recursive'; break; } if ($depth === $max) { break; } $objects[] = $var; foreach ($object_vars as $curr_key => $curr_val) { $results .= '
' . str_repeat(' ', $depth * 2) . ' ->' . htmlspecialchars(strtr($curr_key, $_replace)) . ' = ' . $this->smarty_modifier_debug_print_var($curr_val, $max, $length, ++$depth, $objects); $depth--; } break; case 'boolean': case 'NULL': case 'resource': if (true === $var) { $results = 'true'; } elseif (false === $var) { $results = 'false'; } elseif (null === $var) { $results = 'null'; } else { $results = htmlspecialchars((string)$var); } $results = '' . $results . ''; break; case 'integer': case 'float': $results = htmlspecialchars((string)$var); break; case 'string': $results = strtr($var, $_replace); if (mb_strlen($var, \Smarty\Smarty::$_CHARSET) > $length) { $results = mb_substr($var, 0, $length - 3, \Smarty\Smarty::$_CHARSET) . '...'; } $results = htmlspecialchars('"' . $results . '"', ENT_QUOTES, \Smarty\Smarty::$_CHARSET); break; case 'unknown type': default: $results = strtr((string)$var, $_replace); if (mb_strlen($results, \Smarty\Smarty::$_CHARSET) > $length) { $results = mb_substr($results, 0, $length - 3, \Smarty\Smarty::$_CHARSET) . '...'; } $results = htmlspecialchars($results, ENT_QUOTES, \Smarty\Smarty::$_CHARSET); } return $results; } /** * Smarty escape modifier plugin * Type: modifier * Name: escape * Purpose: escape string for output * * @author Monte Ohrt * * @param string $string input string * @param string $esc_type escape type * @param string $char_set character set, used for htmlspecialchars() or htmlentities() * @param boolean $double_encode encode already encoded entitites again, used for htmlspecialchars() or htmlentities() * * @return string escaped input string */ public function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true) { if (!$char_set) { $char_set = \Smarty\Smarty::$_CHARSET; } $string = (string)$string; switch ($esc_type) { case 'html': return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); // no break case 'htmlall': $string = mb_convert_encoding($string, 'UTF-8', $char_set); return htmlentities($string, ENT_QUOTES, 'UTF-8', $double_encode); // no break case 'url': return rawurlencode($string); case 'urlpathinfo': return str_replace('%2F', '/', rawurlencode($string)); case 'quotes': // escape unescaped single quotes return preg_replace("%(?mb_to_unicode($string, \Smarty\Smarty::$_CHARSET) as $unicode) { $return .= '&#x' . strtoupper(dechex($unicode)) . ';'; } return $return; case 'decentity': $return = ''; foreach ($this->mb_to_unicode($string, \Smarty\Smarty::$_CHARSET) as $unicode) { $return .= '&#' . $unicode . ';'; } return $return; case 'javascript': // escape quotes and backslashes, newlines, etc. return strtr( $string, array( '\\' => '\\\\', "'" => "\\'", '"' => '\\"', "\r" => '\\r', "\n" => '\\n', ' '<\/', // see https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements '