Source of file FormatExtension.php
Size: 17,079 Bytes - Last Modified: 2023-11-16T22:56:02+01:00
/home/websites/teicee/packagist/site/phpdoc/conf/../vendor/teicee/twig-bundle/src/Extension/FormatExtension.php
| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 | <?phpnamespace TIC\TwigBundle\Extension; use TIC\TwigBundle\Base\TICTwigExtension as BaseExtension; use TIC\CoreBundle\Util\ConvertHelper; use TIC\CoreBundle\Util\StringHelper; use Symfony\Contracts\Translation\TranslatorInterface; use Twig\TwigFilter; use Twig\TwigFunction; use Twig\Environment; /** * Filtres et fonctions twig de formattage et conversions. * https://symfony.com/doc/current/templating/twig_extension.html */class FormatExtension extends BaseExtension {public function getFilters(): array { return [ new TwigFilter('boolval', [$this, 'boolvalFilter'], ['is_safe'=>['html']] ), new TwigFilter('boolean', [$this, 'booleanFilter'], ['is_safe'=>['html']] ), new TwigFilter('counter', [$this, 'counterFilter'], ), new TwigFilter('jsvalue', [$this, 'jsvalueFilter'], ['is_safe'=>['html']] ), new TwigFilter('stringify', [$this, 'stringifyFilter'], ['is_safe'=>['html']] ), new TwigFilter('slugify', [$this, 'slugifyFilter'] ), new TwigFilter('asciify', [$this, 'asciifyFilter'] ), new TwigFilter('obfuscate', [$this, 'obfuscateFilter'], ['is_safe'=>['html']] ), new TwigFilter('pathEncode',[$this, 'pathEncodeFilter'] ), new TwigFilter('linkurl', [$this, 'linkurlFilter'], ['is_safe'=>['html']] ), new TwigFilter('mailto', [$this, 'mailtoFilter'], ['is_safe'=>['html']] ), new TwigFilter('phone', [$this, 'phoneFilter'], ['is_safe'=>['html']] ), new TwigFilter('price', [$this, 'priceFilter'], ['is_safe'=>['html']] ), new TwigFilter('euro', [$this, 'euroFilter'], ['is_safe'=>['html']] ), new TwigFilter('hsize', [$this, 'hsizeFilter'], ['is_safe'=>['html']] ), new TwigFilter('color', [$this, 'colorFilter'], ['is_safe'=>['html']] ), new TwigFilter('civ', [$this, 'civFilter'], ['is_safe'=>['html']] ), new TwigFilter('roles', [$this, 'rolesFilter'], ['is_safe'=>['html']] ), # new TwigFilter('stars', [$this, 'starsFilter'], ['is_safe'=>['html'], 'needs_environment'=>true] ),new TwigFilter('icon', [$this, 'iconFilter'], ['is_safe'=>['html']] ), new TwigFilter('lnum', [$this, 'lnumFilter'] ), new TwigFilter('numStep', [$this, 'numStepFilter'] ), new TwigFilter('labelize', [$this, 'labelizeFilter'], ['is_safe'=>['html']] ), new TwigFilter('ereplace', [$this, 'eReplaceFilter'], ['is_safe'=>['html']] ), new TwigFilter('transList', [$this, 'transListFilter'] ), ]; } public function getFunctions(): array { return [ # new TwigFunction('example', [$this, 'exampleFunction'] ),]; } public function __construct(TranslatorInterface $translator = null) { $this->translator = $translator; } /** * */public function boolvalFilter($value, $true_val, $false_val = "") { if (! \is_bool($value)) return $value; return ($value) ? $true_val : $false_val; } /** * Retourne le libellé d'un booléen (ou null) correspondant à la variable (token traduisible). * * @param mixed $value Variable à interpréter pour retourner un état booléen (true/false/null) * @param string $default Libellé à retourner si aucune correspondance booléenne trouvée pour $value * @param bool $trans Recherche d'une traduction pour le token $value (si c'est une chaine) * @return string Libellé d'un booléen (token traduisible ou déjà traduit) */public function booleanFilter($value, string $default = '-', bool $trans = false): string { $bool = $this->getBool($value, $default); if ($bool === null) $text = self::$strings['null']; elseif ($bool === true) $text = self::$strings['true']; elseif ($bool === false) $text = self::$strings['false']; return ($trans) ? $this->trans($text) : $text; } /** * Retourne une quantité à partir d'une variable de type quelconque (intval, floatval, count...). * @TODO: ajouter des options pour la présentation des nombres (number_format) * * @param mixed $value Valeur dont une quantité est à déterminer * @return int|float Valeur numérique (ou null si indéterminé) */public function counterFilter($value) { return ConvertHelper::counter($value); } /** * Transformation d'une variable PHP quelque soit son type pour son affichage en Javascript (avec échappements). */public function jsvalueFilter($value): string { switch (true) { case \is_null($value) : return 'null'; case \is_bool($value) : return ($value) ? 'true' : 'false'; case \is_string($value) : return '"' . StringHelper::jsescape($value) . '"'; case \is_array($value) : return json_encode($value); case \is_object($value) : return json_encode($value); default : return $value; } } /** * Transformation d'une variable PHP quelque soit son type dans sa forme correspondante en chaine de caractères. */public function stringifyFilter($value) { return ConvertHelper::stringify($value); } /** * Retourne une chaine ASCII en convertissant les caractères étendus. * * @param string $text Texte en UTF-8 à transformer * @return string Chaine équivalente avec les caractères 7 bits */public function asciifyFilter(string $text): string { return StringHelper::asciify($text); } /** * Retourne une chaine canonisée à partir d'une chaine donnée. * * @param string $text Texte en UTF-8 à slugifier * @return string Slug avec uniquement les caractères [a-z], [0-9], '.', '_' et '-' */public function slugifyFilter(string $text): string { return StringHelper::slugify($text); } /** * Transformation d'une chaine de caractère en code Javascript son camouflage dans une page HTML (utile pour emails). */public function obfuscateFilter($string, $delta = null) { if ($delta === null) $delta = \rand(2, 22); $value = ''; foreach (\str_split($string) as $c) $value.= \chr(\ord($c) + $delta); return \sprintf('<script type="text/javascript">document.write(atob("%s").replace(/./g, function(c){ return String.fromCharCode(c.charCodeAt(0) - %d); }))</script>', \base64_encode($value), $delta ); } /** * Encodage pour échappements de chaques parties du chemin d'une URL (en conservant les '/'). */public function pathEncodeFilter($url) { if (! \is_string($url)) return $url; return \implode('/', \array_map('rawurlencode', \explode('/', $url))); } /** * Affichage d'un lien HTML avec son libellé cliquable pour une URL. */public function linkurlFilter(?string $url, ?string $label = null): ?string { if (null === $label) $label = $url; if (empty($url)) return $label; return \sprintf('<a href="%s" target="_blank">%s</a>', \htmlspecialchars($url), \htmlspecialchars($label)); } /** * Affichage d'un lien HTML avec son libellé cliquable pour une (ou plusieurs) adresse(s) email. * * @param string|array $emails Adresse(s) email(s) à afficher (séparateur virgule sur chaine) * @param string|bool $label Texte à afficher sur le lien, null pour laisser la valeur source * ou booléen pour valeur formattée (true=complet / false=adresse seul) * @param string $glue Séparateur pour la concaténation des adresses retournées (json_encode si null) * @return string Chaine HTML avec la (ou les) adresse(s) avec lien mailto: (si adresse reconnue) */public function mailtoFilter(mixed $emails, mixed $label = false, ?string $glue = ', '): ?string { if ($emails === null) return null; if (! \is_array($emails)) $emails = \explode(',', $emails); $html = array(); foreach ($emails as $email) { if ($email === null) continue; $email = \trim($email); if ($email === '') continue; if (\preg_match('/^(.*[\s<])?([^@<>\s"\']+@[^@<>\s\'"]+)[^@]*$/', $email, $match)) { $addr = $match[2]; $full = \trim($match[1], " \n\r\t\v\x00\"<>"); $full = \strlen($full) ? \sprintf('"%s" <%s>', $full, $addr) : $addr; $text = \is_string($label) ? $label : $email; if ($label === true) $text = $full; elseif ($label === false) $text = $addr; $html[] = \sprintf('<span class="mailto %4$s"><a href="mailto:%1$s" title="%2$s">%3$s</a></span>', \htmlspecialchars($full), \htmlspecialchars($this->trans('widget.mailto.link_title') . $addr), \htmlspecialchars($text), ($text === $addr) ? "mailaddr" : "mailtext" ); } else $html[] = \htmlspecialchars($email); } return ($glue === null) ? \json_encode($html) : \implode($glue, $html); } /** * Formattage d'un numéro de téléphone pour affichage. * * @param mixed $value Variable à formatter comme numéro de téléphone (liste possible) * @param string $default Libellé à retourner si la valeur est vide * @param bool $html Utilisation d'entités HTML dans le formatage (activé par défaut !) * @param mixed $inter Préfixe international à ajouter (aucun par défaut, "+33" si true) * @return string Numéro(s) de téléphone formattés (séparés par "\n" si plusieurs) */public function phoneFilter($value, string $default='—', bool $html = true, $inter = ''): string { if ($inter === true) $inter = "+33"; if (empty($html) && ($default == '—')) $default = '-'; $phones = array(); $values = \is_array($value) ? $value : array($value); foreach ($values as $value) { $phone = \trim($value); if (empty($phone)) $phone = $default; elseif (\preg_match('/^(\(?\d?\)?\d)[\s\.\-]*(\d\d)[\s\.\-]*(\d\d)[\s\.\-]*(\d\d)[\s\.\-]*(\d{2,8})$/', $phone, $m)) { // (0)1 23 45 67 89 01-23-45-67-89 1 23 45 67 89 0123456789 ... $phone = \trim(\sprintf('%s %02d %02d %02d %02d %02d', $inter, $m[1], $m[2], $m[3], $m[4], $m[5])); } elseif (\preg_match('/^(\+\d{1,3})[\s\.\-]+(\(?\d?\)?\d)[\s\.\-]*(\d\d)[\s\.\-]*(\d\d)[\s\.\-]*(\d\d)[\s\.\-]*(\d{2,8})$/', $phone, $m)) { // +33 (0)1 23 45 67 89 +33-01-23-45-67-89 +33 1 23 45 67 89 ... $phone = \trim(\sprintf('%s %02d %02d %02d %02d %02d', ($inter===false)?"":$m[1], $m[2], $m[3], $m[4], $m[5], $m[6])); } elseif (\preg_match('/^(\+\d{1,3})[\s\.\-]*(\(?\d\)?\d)[\s\.\-]*(\d\d)[\s\.\-]*(\d\d)[\s\.\-]*(\d\d)[\s\.\-]*(\d{2,8})$/', $phone, $m)) { // +330123456789 ... $phone = \trim(\sprintf('%s %02d %02d %02d %02d %02d', ($inter===false)?"":$m[1], $m[2], $m[3], $m[4], $m[5], $m[6])); } $phones[] = ($html) ? \str_replace(' ', ' ', $phone) : $phone; } return \implode("\n", $phones); } /** * Formattage d'un prix pour affichage */public function priceFilter($number, $default = null, string $devise = ' €', string $space =' '): ?string { # $locale = \setlocale(LC_MONETARY, 'fr_FR');# return \money_format('%.2n', $number);if (\is_string($number)) $number = \floatval(\strtr($number, ',', '.')); if (($default !== null) && (\abs($number) < 0.001)) return $default; // NOTE: number_format applique un PHP_ROUND_HALF_UP si besoin return \number_format($number, 2, ',', $space) . $devise; } /** * Formattage d'un prix pour affichage en euros. */public function euroFilter($number, $default = null) { return $this->priceFilter($number, $default, ' €', ''); } /** * Formattage d'un quantité d'octets pour affichage "pour humain" (avec l'unité la plus proche). * * @param integer $bytes Valeur en octets * @param array $units Liste des unités (liste par défaut si non array) * @param integer $prec Précision (nombre de décimales) * @param integer $base Calculs en base 2 ou en base 10 * @return string */public function hsizeFilter($bytes, $units = null, int $prec = 1, int $base = 2) { return ConvertHelper::hsize($bytes, $units, $prec, $base); } /** * Formattage d'un aperçu en HTML d'une couleur (avec ou sans son code). */public function colorFilter($color, $with_label = false) { if (empty($color)) return ''; if ($with_label === true) $with_label = 'right'; $html = \sprintf('<span class="tic-color" style="background-color: %s;"> ', $color); switch (\trim(\strtolower($with_label))) { case 'left' : $html = \htmlspecialchars($color) . '</span> ' . $html . '</span>'; break; case 'right' : $html.= '</span> ' . \htmlspecialchars($color); break; case 'inside' : $html.= \htmlspecialchars($color) . ' </span>'; break; default : $html.= '</span>'; break; } return $html; } /** * Formattage d'une civilité dans sa version traduite à partir de son code interne. */public function civFilter($value, $abbr = false, $trans = true) { if ($value === null || $value === '') return ''; if (\strpos($value, '.') !== FALSE) $value = \str_replace('.', '', $value); $text = \sprintf('ext.%s.%s', ($abbr)?'civ':'civilite', \strtolower($value)); return ($trans) ? $this->trans($text) : $text; } /** * Formattage du (ou des) rôle(s) d'un utilisateur en utilisant les traductions. */public function rolesFilter($roles, $join = false, $trans = true) { if ($roles === null || empty($roles)) return ($join === false) ? array() : ""; if (! \is_array($roles)) $roles = \explode(',', $roles); \sort($roles); $labels = array(); foreach ($roles as $role) { if ($role === 'ROLE_USER') continue; $label = 'app.user.' . \strtolower(\str_replace('ROLE_', 'roles.', $role)); $labels[] = ($trans) ? $this->trans($label) : $label; } if ($join === false) return $labels; if ($join === true) $join = ", "; return \implode($join, $labels); } /** * public function starsFilter(\Twig_Environment $env, $value, $symbol = null) { if (empty($symbol)) { $asset = $env->getFunction('asset')->getCallable(); $symbol = \call_user_func($asset, 'bundles/ticcore/images/bootstrap-star-rating/star.png'); } $html = '<sup class="tic_stars" title="' . $value . '">'; for ($n = 0; $n < $value; $n++) { $html.= \sprintf('<img src="%s" alt="*" />', $symbol); } $html.= '</sup>'; return $html; } *//** * Génération d'un tag HTML pour afficher un icone FontAwesome ou Bootstrap (avec quelques alias utiles). * * @param string $name Classe Bootstrap ou FontAwesome (ex 'fas-user', 'fab-user', 'envelope', 'ban-circle'...) * @param string $state Ajout d'une classe de type 'text-<state>' (ex: primary, info, success, warning, danger) */public function iconFilter(string $name, string $state=''): string { return $this->getIcon($name, $state=''); } /** * Formattage d'un nombre (selon les règles d'affichage françaises). */public function lnumFilter($number, $precision = null) { return \number_format((float)$number, $precision, ',', ' '); # return \sprintf('%.0'.$precision.'f', $number);} /** * Validation/transformation d'un nombre pour respecter un "pas" (multiplieur). */public function numStepFilter($number, $step = 5, $default = '') { if (! \is_numeric($number)) return $default; return \intval(\round($number / $step) * $step); } /** * Génération automatique d'un libellé (token de traduction) à partir d'un champ de formulaire. */public function labelizeFilter($view_or_data, $type = 'label') { $data = $view_or_data; if (\is_object($view_or_data)) { $data = array( 'path' => $view_or_data->parent->vars['id'], 'name' => $view_or_data->vars['name'], ); if (isset($view_or_data->parent->vars['label_path'])) { $data['path'] = $view_or_data->parent->vars['label_path']; } if (isset($view_or_data->parent->parent->parent->parent->vars) && $view_or_data->parent->parent->parent->vars['name'] == 'translations') { $data['path'] = $view_or_data->parent->parent->parent->parent->vars['id']; } } $path = \preg_replace('/__([a-zA-Z\-]+)\d\d\d__/', '__$1__', $data['path']); $parent = \strtr($path . '$', array( '__name__' => '', # '_form_' => '.','_form_' => '.' . $type . '-', # '_form$' => '.','_form$' => '.' . $type . '.', '$' => '.', '_' => '.', )); # return preg_replace('/\.\d*\./', '.', $parent) . $type . '.' $data['name'];if (! \preg_match('/\.' . $type . '[\.\-]/', $parent)) $parent.= $type . "."; return \preg_replace('/\.\d*\./', '.', $parent) . $data['name']; } /** * Application d'une RegExp de substitution. */public function eReplaceFilter(string $subject, string $pattern, string $replacement): string { return \preg_replace($pattern, $replacement, $subject); } /** * Application du filtre de traduction (trans) sur une liste de valeurs. */public function transListFilter(array $values): array { \array_walk($values, function(&$text) { $text = $this->trans($text); }); return $values; } } |