Файловый менеджер - Редактировать - /home/easybachat/hisabat365.com/4a7891/symfony.tar
Ðазад
string/Inflector/FrenchInflector.php 0000644 00000013513 15060133305 0013562 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Inflector; /** * French inflector. * * This class does only inflect nouns; not adjectives nor composed words like "soixante-dix". */ final class FrenchInflector implements InflectorInterface { /** * A list of all rules for pluralise. * * @see https://la-conjugaison.nouvelobs.com/regles/grammaire/le-pluriel-des-noms-121.php */ private const PLURALIZE_REGEXP = [ // First entry: regexp // Second entry: replacement // Words finishing with "s", "x" or "z" are invariables // Les mots finissant par "s", "x" ou "z" sont invariables ['/(s|x|z)$/i', '\1'], // Words finishing with "eau" are pluralized with a "x" // Les mots finissant par "eau" prennent tous un "x" au pluriel ['/(eau)$/i', '\1x'], // Words finishing with "au" are pluralized with a "x" excepted "landau" // Les mots finissant par "au" prennent un "x" au pluriel sauf "landau" ['/^(landau)$/i', '\1s'], ['/(au)$/i', '\1x'], // Words finishing with "eu" are pluralized with a "x" excepted "pneu", "bleu", "émeu" // Les mots finissant en "eu" prennent un "x" au pluriel sauf "pneu", "bleu", "émeu" ['/^(pneu|bleu|émeu)$/i', '\1s'], ['/(eu)$/i', '\1x'], // Words finishing with "al" are pluralized with a "aux" excepted // Les mots finissant en "al" se terminent en "aux" sauf ['/^(bal|carnaval|caracal|chacal|choral|corral|étal|festival|récital|val)$/i', '\1s'], ['/al$/i', '\1aux'], // Aspirail, bail, corail, émail, fermail, soupirail, travail, vantail et vitrail font leur pluriel en -aux ['/^(aspir|b|cor|ém|ferm|soupir|trav|vant|vitr)ail$/i', '\1aux'], // Bijou, caillou, chou, genou, hibou, joujou et pou qui prennent un x au pluriel ['/^(bij|caill|ch|gen|hib|jouj|p)ou$/i', '\1oux'], // Invariable words ['/^(cinquante|soixante|mille)$/i', '\1'], // French titles ['/^(mon|ma)(sieur|dame|demoiselle|seigneur)$/', 'mes\2s'], ['/^(Mon|Ma)(sieur|dame|demoiselle|seigneur)$/', 'Mes\2s'], ]; /** * A list of all rules for singularize. */ private const SINGULARIZE_REGEXP = [ // First entry: regexp // Second entry: replacement // Aspirail, bail, corail, émail, fermail, soupirail, travail, vantail et vitrail font leur pluriel en -aux ['/((aspir|b|cor|ém|ferm|soupir|trav|vant|vitr))aux$/i', '\1ail'], // Words finishing with "eau" are pluralized with a "x" // Les mots finissant par "eau" prennent tous un "x" au pluriel ['/(eau)x$/i', '\1'], // Words finishing with "al" are pluralized with a "aux" expected // Les mots finissant en "al" se terminent en "aux" sauf ['/(amir|anim|arsen|boc|can|capit|capor|chev|crist|génér|hopit|hôpit|idé|journ|littor|loc|m|mét|minér|princip|radic|termin)aux$/i', '\1al'], // Words finishing with "au" are pluralized with a "x" excepted "landau" // Les mots finissant par "au" prennent un "x" au pluriel sauf "landau" ['/(au)x$/i', '\1'], // Words finishing with "eu" are pluralized with a "x" excepted "pneu", "bleu", "émeu" // Les mots finissant en "eu" prennent un "x" au pluriel sauf "pneu", "bleu", "émeu" ['/(eu)x$/i', '\1'], // Words finishing with "ou" are pluralized with a "s" excepted bijou, caillou, chou, genou, hibou, joujou, pou // Les mots finissant par "ou" prennent un "s" sauf bijou, caillou, chou, genou, hibou, joujou, pou ['/(bij|caill|ch|gen|hib|jouj|p)oux$/i', '\1ou'], // French titles ['/^mes(dame|demoiselle)s$/', 'ma\1'], ['/^Mes(dame|demoiselle)s$/', 'Ma\1'], ['/^mes(sieur|seigneur)s$/', 'mon\1'], ['/^Mes(sieur|seigneur)s$/', 'Mon\1'], // Default rule ['/s$/i', ''], ]; /** * A list of words which should not be inflected. * This list is only used by singularize. */ private const UNINFLECTED = '/^(abcès|accès|abus|albatros|anchois|anglais|autobus|bois|brebis|carquois|cas|chas|colis|concours|corps|cours|cyprès|décès|devis|discours|dos|embarras|engrais|entrelacs|excès|fils|fois|gâchis|gars|glas|héros|intrus|jars|jus|kermès|lacis|legs|lilas|marais|mars|matelas|mépris|mets|mois|mors|obus|os|palais|paradis|parcours|pardessus|pays|plusieurs|poids|pois|pouls|printemps|processus|progrès|puits|pus|rabais|radis|recors|recours|refus|relais|remords|remous|rictus|rhinocéros|repas|rubis|sans|sas|secours|sens|souris|succès|talus|tapis|tas|taudis|temps|tiers|univers|velours|verglas|vernis|virus)$/i'; public function singularize(string $plural): array { if ($this->isInflectedWord($plural)) { return [$plural]; } foreach (self::SINGULARIZE_REGEXP as $rule) { [$regexp, $replace] = $rule; if (1 === preg_match($regexp, $plural)) { return [preg_replace($regexp, $replace, $plural)]; } } return [$plural]; } public function pluralize(string $singular): array { if ($this->isInflectedWord($singular)) { return [$singular]; } foreach (self::PLURALIZE_REGEXP as $rule) { [$regexp, $replace] = $rule; if (1 === preg_match($regexp, $singular)) { return [preg_replace($regexp, $replace, $singular)]; } } return [$singular.'s']; } private function isInflectedWord(string $word): bool { return 1 === preg_match(self::UNINFLECTED, $word); } } string/Inflector/EnglishInflector.php 0000644 00000040230 15060133305 0013742 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Inflector; final class EnglishInflector implements InflectorInterface { /** * Map English plural to singular suffixes. * * @see http://english-zone.com/spelling/plurals.html */ private const PLURAL_MAP = [ // First entry: plural suffix, reversed // Second entry: length of plural suffix // Third entry: Whether the suffix may succeed a vowel // Fourth entry: Whether the suffix may succeed a consonant // Fifth entry: singular suffix, normal // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) ['a', 1, true, true, ['on', 'um']], // nebulae (nebula) ['ea', 2, true, true, 'a'], // services (service) ['secivres', 8, true, true, 'service'], // mice (mouse), lice (louse) ['eci', 3, false, true, 'ouse'], // geese (goose) ['esee', 4, false, true, 'oose'], // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius) ['i', 1, true, true, 'us'], // men (man), women (woman) ['nem', 3, true, true, 'man'], // children (child) ['nerdlihc', 8, true, true, 'child'], // oxen (ox) ['nexo', 4, false, false, 'ox'], // indices (index), appendices (appendix), prices (price) ['seci', 4, false, true, ['ex', 'ix', 'ice']], // codes (code) ['sedoc', 5, false, true, 'code'], // selfies (selfie) ['seifles', 7, true, true, 'selfie'], // zombies (zombie) ['seibmoz', 7, true, true, 'zombie'], // movies (movie) ['seivom', 6, true, true, 'movie'], // names (name) ['seman', 5, true, false, 'name'], // conspectuses (conspectus), prospectuses (prospectus) ['sesutcep', 8, true, true, 'pectus'], // feet (foot) ['teef', 4, true, true, 'foot'], // geese (goose) ['eseeg', 5, true, true, 'goose'], // teeth (tooth) ['hteet', 5, true, true, 'tooth'], // news (news) ['swen', 4, true, true, 'news'], // series (series) ['seires', 6, true, true, 'series'], // babies (baby) ['sei', 3, false, true, 'y'], // accesses (access), addresses (address), kisses (kiss) ['sess', 4, true, false, 'ss'], // statuses (status) ['sesutats', 8, true, true, 'status'], // analyses (analysis), ellipses (ellipsis), fungi (fungus), // neuroses (neurosis), theses (thesis), emphases (emphasis), // oases (oasis), crises (crisis), houses (house), bases (base), // atlases (atlas) ['ses', 3, true, true, ['s', 'se', 'sis']], // objectives (objective), alternative (alternatives) ['sevit', 5, true, true, 'tive'], // drives (drive) ['sevird', 6, false, true, 'drive'], // lives (life), wives (wife) ['sevi', 4, false, true, 'ife'], // moves (move) ['sevom', 5, true, true, 'move'], // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf), caves (cave), staves (staff) ['sev', 3, true, true, ['f', 've', 'ff']], // axes (axis), axes (ax), axes (axe) ['sexa', 4, false, false, ['ax', 'axe', 'axis']], // indexes (index), matrixes (matrix) ['sex', 3, true, false, 'x'], // quizzes (quiz) ['sezz', 4, true, false, 'z'], // bureaus (bureau) ['suae', 4, false, true, 'eau'], // fees (fee), trees (tree), employees (employee) ['see', 3, true, true, 'ee'], // edges (edge) ['segd', 4, true, true, 'dge'], // roses (rose), garages (garage), cassettes (cassette), // waltzes (waltz), heroes (hero), bushes (bush), arches (arch), // shoes (shoe) ['se', 2, true, true, ['', 'e']], // status (status) ['sutats', 6, true, true, 'status'], // tags (tag) ['s', 1, true, true, ''], // chateaux (chateau) ['xuae', 4, false, true, 'eau'], // people (person) ['elpoep', 6, true, true, 'person'], ]; /** * Map English singular to plural suffixes. * * @see http://english-zone.com/spelling/plurals.html */ private const SINGULAR_MAP = [ // First entry: singular suffix, reversed // Second entry: length of singular suffix // Third entry: Whether the suffix may succeed a vowel // Fourth entry: Whether the suffix may succeed a consonant // Fifth entry: plural suffix, normal // axes (axis) ['sixa', 4, false, false, 'axes'], // criterion (criteria) ['airetirc', 8, false, false, 'criterion'], // nebulae (nebula) ['aluben', 6, false, false, 'nebulae'], // children (child) ['dlihc', 5, true, true, 'children'], // prices (price) ['eci', 3, false, true, 'ices'], // services (service) ['ecivres', 7, true, true, 'services'], // lives (life), wives (wife) ['efi', 3, false, true, 'ives'], // selfies (selfie) ['eifles', 6, true, true, 'selfies'], // movies (movie) ['eivom', 5, true, true, 'movies'], // lice (louse) ['esuol', 5, false, true, 'lice'], // mice (mouse) ['esuom', 5, false, true, 'mice'], // geese (goose) ['esoo', 4, false, true, 'eese'], // houses (house), bases (base) ['es', 2, true, true, 'ses'], // geese (goose) ['esoog', 5, true, true, 'geese'], // caves (cave) ['ev', 2, true, true, 'ves'], // drives (drive) ['evird', 5, false, true, 'drives'], // objectives (objective), alternative (alternatives) ['evit', 4, true, true, 'tives'], // moves (move) ['evom', 4, true, true, 'moves'], // staves (staff) ['ffats', 5, true, true, 'staves'], // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf) ['ff', 2, true, true, 'ffs'], // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf) ['f', 1, true, true, ['fs', 'ves']], // arches (arch) ['hc', 2, true, true, 'ches'], // bushes (bush) ['hs', 2, true, true, 'shes'], // teeth (tooth) ['htoot', 5, true, true, 'teeth'], // albums (album) ['mubla', 5, true, true, 'albums'], // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) ['mu', 2, true, true, 'a'], // men (man), women (woman) ['nam', 3, true, true, 'men'], // people (person) ['nosrep', 6, true, true, ['persons', 'people']], // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) ['noi', 3, true, true, 'ions'], // coupon (coupons) ['nop', 3, true, true, 'pons'], // seasons (season), treasons (treason), poisons (poison), lessons (lesson) ['nos', 3, true, true, 'sons'], // icons (icon) ['noc', 3, true, true, 'cons'], // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) ['no', 2, true, true, 'a'], // echoes (echo) ['ohce', 4, true, true, 'echoes'], // heroes (hero) ['oreh', 4, true, true, 'heroes'], // atlases (atlas) ['salta', 5, true, true, 'atlases'], // irises (iris) ['siri', 4, true, true, 'irises'], // analyses (analysis), ellipses (ellipsis), neuroses (neurosis) // theses (thesis), emphases (emphasis), oases (oasis), // crises (crisis) ['sis', 3, true, true, 'ses'], // accesses (access), addresses (address), kisses (kiss) ['ss', 2, true, false, 'sses'], // syllabi (syllabus) ['suballys', 8, true, true, 'syllabi'], // buses (bus) ['sub', 3, true, true, 'buses'], // circuses (circus) ['suc', 3, true, true, 'cuses'], // hippocampi (hippocampus) ['supmacoppih', 11, false, false, 'hippocampi'], // campuses (campus) ['sup', 3, true, true, 'puses'], // status (status) ['sutats', 6, true, true, ['status', 'statuses']], // conspectuses (conspectus), prospectuses (prospectus) ['sutcep', 6, true, true, 'pectuses'], // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius) ['su', 2, true, true, 'i'], // news (news) ['swen', 4, true, true, 'news'], // feet (foot) ['toof', 4, true, true, 'feet'], // chateaux (chateau), bureaus (bureau) ['uae', 3, false, true, ['eaus', 'eaux']], // oxen (ox) ['xo', 2, false, false, 'oxen'], // hoaxes (hoax) ['xaoh', 4, true, false, 'hoaxes'], // indices (index) ['xedni', 5, false, true, ['indicies', 'indexes']], // boxes (box) ['xo', 2, false, true, 'oxes'], // indexes (index), matrixes (matrix) ['x', 1, true, false, ['cies', 'xes']], // appendices (appendix) ['xi', 2, false, true, 'ices'], // babies (baby) ['y', 1, false, true, 'ies'], // quizzes (quiz) ['ziuq', 4, true, false, 'quizzes'], // waltzes (waltz) ['z', 1, true, true, 'zes'], ]; /** * A list of words which should not be inflected, reversed. */ private const UNINFLECTED = [ '', // data 'atad', // deer 'reed', // equipment 'tnempiuqe', // feedback 'kcabdeef', // fish 'hsif', // health 'htlaeh', // history 'yrotsih', // info 'ofni', // information 'noitamrofni', // money 'yenom', // moose 'esoom', // series 'seires', // sheep 'peehs', // species 'seiceps', // traffic 'ciffart', // aircraft 'tfarcria', // hardware 'erawdrah', ]; public function singularize(string $plural): array { $pluralRev = strrev($plural); $lowerPluralRev = strtolower($pluralRev); $pluralLength = \strlen($lowerPluralRev); // Check if the word is one which is not inflected, return early if so if (\in_array($lowerPluralRev, self::UNINFLECTED, true)) { return [$plural]; } // The outer loop iterates over the entries of the plural table // The inner loop $j iterates over the characters of the plural suffix // in the plural table to compare them with the characters of the actual // given plural suffix foreach (self::PLURAL_MAP as $map) { $suffix = $map[0]; $suffixLength = $map[1]; $j = 0; // Compare characters in the plural table and of the suffix of the // given plural one by one while ($suffix[$j] === $lowerPluralRev[$j]) { // Let $j point to the next character ++$j; // Successfully compared the last character // Add an entry with the singular suffix to the singular array if ($j === $suffixLength) { // Is there any character preceding the suffix in the plural string? if ($j < $pluralLength) { $nextIsVowel = str_contains('aeiou', $lowerPluralRev[$j]); if (!$map[2] && $nextIsVowel) { // suffix may not succeed a vowel but next char is one break; } if (!$map[3] && !$nextIsVowel) { // suffix may not succeed a consonant but next char is one break; } } $newBase = substr($plural, 0, $pluralLength - $suffixLength); $newSuffix = $map[4]; // Check whether the first character in the plural suffix // is uppercased. If yes, uppercase the first character in // the singular suffix too $firstUpper = ctype_upper($pluralRev[$j - 1]); if (\is_array($newSuffix)) { $singulars = []; foreach ($newSuffix as $newSuffixEntry) { $singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry); } return $singulars; } return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)]; } // Suffix is longer than word if ($j === $pluralLength) { break; } } } // Assume that plural and singular is identical return [$plural]; } public function pluralize(string $singular): array { $singularRev = strrev($singular); $lowerSingularRev = strtolower($singularRev); $singularLength = \strlen($lowerSingularRev); // Check if the word is one which is not inflected, return early if so if (\in_array($lowerSingularRev, self::UNINFLECTED, true)) { return [$singular]; } // The outer loop iterates over the entries of the singular table // The inner loop $j iterates over the characters of the singular suffix // in the singular table to compare them with the characters of the actual // given singular suffix foreach (self::SINGULAR_MAP as $map) { $suffix = $map[0]; $suffixLength = $map[1]; $j = 0; // Compare characters in the singular table and of the suffix of the // given plural one by one while ($suffix[$j] === $lowerSingularRev[$j]) { // Let $j point to the next character ++$j; // Successfully compared the last character // Add an entry with the plural suffix to the plural array if ($j === $suffixLength) { // Is there any character preceding the suffix in the plural string? if ($j < $singularLength) { $nextIsVowel = str_contains('aeiou', $lowerSingularRev[$j]); if (!$map[2] && $nextIsVowel) { // suffix may not succeed a vowel but next char is one break; } if (!$map[3] && !$nextIsVowel) { // suffix may not succeed a consonant but next char is one break; } } $newBase = substr($singular, 0, $singularLength - $suffixLength); $newSuffix = $map[4]; // Check whether the first character in the singular suffix // is uppercased. If yes, uppercase the first character in // the singular suffix too $firstUpper = ctype_upper($singularRev[$j - 1]); if (\is_array($newSuffix)) { $plurals = []; foreach ($newSuffix as $newSuffixEntry) { $plurals[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry); } return $plurals; } return [$newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix)]; } // Suffix is longer than word if ($j === $singularLength) { break; } } } // Assume that plural is singular with a trailing `s` return [$singular.'s']; } } string/Inflector/InflectorInterface.php 0000644 00000001503 15060133305 0014251 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Inflector; interface InflectorInterface { /** * Returns the singular forms of a string. * * If the method can't determine the form with certainty, several possible singulars are returned. * * @return string[] */ public function singularize(string $plural): array; /** * Returns the plural forms of a string. * * If the method can't determine the form with certainty, several possible plurals are returned. * * @return string[] */ public function pluralize(string $singular): array; } string/Exception/ExceptionInterface.php 0000644 00000000521 15060133305 0014272 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Exception; interface ExceptionInterface extends \Throwable { } string/Exception/RuntimeException.php 0000644 00000000560 15060133305 0014020 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Exception; class RuntimeException extends \RuntimeException implements ExceptionInterface { } string/Exception/InvalidArgumentException.php 0000644 00000000600 15060133305 0015461 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Exception; class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface { } string/Slugger/SluggerInterface.php 0000644 00000001314 15060133305 0013417 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Slugger; use Symfony\Component\String\AbstractUnicodeString; /** * Creates a URL-friendly slug from a given string. * * @author Titouan Galopin <galopintitouan@gmail.com> */ interface SluggerInterface { /** * Creates a slug for the given string and locale, using appropriate transliteration when needed. */ public function slug(string $string, string $separator = '-', ?string $locale = null): AbstractUnicodeString; } string/Slugger/AsciiSlugger.php 0000644 00000015563 15060133305 0012562 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String\Slugger; use Symfony\Component\Emoji\EmojiTransliterator; use Symfony\Component\String\AbstractUnicodeString; use Symfony\Component\String\UnicodeString; use Symfony\Contracts\Translation\LocaleAwareInterface; if (!interface_exists(LocaleAwareInterface::class)) { throw new \LogicException('You cannot use the "Symfony\Component\String\Slugger\AsciiSlugger" as the "symfony/translation-contracts" package is not installed. Try running "composer require symfony/translation-contracts".'); } /** * @author Titouan Galopin <galopintitouan@gmail.com> */ class AsciiSlugger implements SluggerInterface, LocaleAwareInterface { private const LOCALE_TO_TRANSLITERATOR_ID = [ 'am' => 'Amharic-Latin', 'ar' => 'Arabic-Latin', 'az' => 'Azerbaijani-Latin', 'be' => 'Belarusian-Latin', 'bg' => 'Bulgarian-Latin', 'bn' => 'Bengali-Latin', 'de' => 'de-ASCII', 'el' => 'Greek-Latin', 'fa' => 'Persian-Latin', 'he' => 'Hebrew-Latin', 'hy' => 'Armenian-Latin', 'ka' => 'Georgian-Latin', 'kk' => 'Kazakh-Latin', 'ky' => 'Kirghiz-Latin', 'ko' => 'Korean-Latin', 'mk' => 'Macedonian-Latin', 'mn' => 'Mongolian-Latin', 'or' => 'Oriya-Latin', 'ps' => 'Pashto-Latin', 'ru' => 'Russian-Latin', 'sr' => 'Serbian-Latin', 'sr_Cyrl' => 'Serbian-Latin', 'th' => 'Thai-Latin', 'tk' => 'Turkmen-Latin', 'uk' => 'Ukrainian-Latin', 'uz' => 'Uzbek-Latin', 'zh' => 'Han-Latin', ]; private \Closure|array $symbolsMap = [ 'en' => ['@' => 'at', '&' => 'and'], ]; private bool|string $emoji = false; /** * Cache of transliterators per locale. * * @var \Transliterator[] */ private array $transliterators = []; public function __construct( private ?string $defaultLocale = null, array|\Closure|null $symbolsMap = null, ) { $this->symbolsMap = $symbolsMap ?? $this->symbolsMap; } public function setLocale(string $locale): void { $this->defaultLocale = $locale; } public function getLocale(): string { return $this->defaultLocale; } /** * @param bool|string $emoji true will use the same locale, * false will disable emoji, * and a string to use a specific locale */ public function withEmoji(bool|string $emoji = true): static { if (false !== $emoji && !class_exists(EmojiTransliterator::class)) { throw new \LogicException(sprintf('You cannot use the "%s()" method as the "symfony/emoji" package is not installed. Try running "composer require symfony/emoji".', __METHOD__)); } $new = clone $this; $new->emoji = $emoji; return $new; } public function slug(string $string, string $separator = '-', ?string $locale = null): AbstractUnicodeString { $locale ??= $this->defaultLocale; $transliterator = []; if ($locale && ('de' === $locale || str_starts_with($locale, 'de_'))) { // Use the shortcut for German in UnicodeString::ascii() if possible (faster and no requirement on intl) $transliterator = ['de-ASCII']; } elseif (\function_exists('transliterator_transliterate') && $locale) { $transliterator = (array) $this->createTransliterator($locale); } if ($emojiTransliterator = $this->createEmojiTransliterator($locale)) { $transliterator[] = $emojiTransliterator; } if ($this->symbolsMap instanceof \Closure) { // If the symbols map is passed as a closure, there is no need to fallback to the parent locale // as the closure can just provide substitutions for all locales of interest. $symbolsMap = $this->symbolsMap; array_unshift($transliterator, static fn ($s) => $symbolsMap($s, $locale)); } $unicodeString = (new UnicodeString($string))->ascii($transliterator); if (\is_array($this->symbolsMap)) { $map = null; if (isset($this->symbolsMap[$locale])) { $map = $this->symbolsMap[$locale]; } else { $parent = self::getParentLocale($locale); if ($parent && isset($this->symbolsMap[$parent])) { $map = $this->symbolsMap[$parent]; } } if ($map) { foreach ($map as $char => $replace) { $unicodeString = $unicodeString->replace($char, ' '.$replace.' '); } } } return $unicodeString ->replaceMatches('/[^A-Za-z0-9]++/', $separator) ->trim($separator) ; } private function createTransliterator(string $locale): ?\Transliterator { if (\array_key_exists($locale, $this->transliterators)) { return $this->transliterators[$locale]; } // Exact locale supported, cache and return if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$locale] ?? null) { return $this->transliterators[$locale] = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id); } // Locale not supported and no parent, fallback to any-latin if (!$parent = self::getParentLocale($locale)) { return $this->transliterators[$locale] = null; } // Try to use the parent locale (ie. try "de" for "de_AT") and cache both locales if ($id = self::LOCALE_TO_TRANSLITERATOR_ID[$parent] ?? null) { $transliterator = \Transliterator::create($id.'/BGN') ?? \Transliterator::create($id); } return $this->transliterators[$locale] = $this->transliterators[$parent] = $transliterator ?? null; } private function createEmojiTransliterator(?string $locale): ?EmojiTransliterator { if (\is_string($this->emoji)) { $locale = $this->emoji; } elseif (!$this->emoji) { return null; } while (null !== $locale) { try { return EmojiTransliterator::create("emoji-$locale"); } catch (\IntlException) { $locale = self::getParentLocale($locale); } } return null; } private static function getParentLocale(?string $locale): ?string { if (!$locale) { return null; } if (false === $str = strrchr($locale, '_')) { // no parent locale return null; } return substr($locale, 0, -\strlen($str)); } } string/AbstractString.php 0000644 00000044730 15060133305 0011521 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; use Symfony\Component\String\Exception\ExceptionInterface; use Symfony\Component\String\Exception\InvalidArgumentException; use Symfony\Component\String\Exception\RuntimeException; /** * Represents a string of abstract characters. * * Unicode defines 3 types of "characters" (bytes, code points and grapheme clusters). * This class is the abstract type to use as a type-hint when the logic you want to * implement doesn't care about the exact variant it deals with. * * @author Nicolas Grekas <p@tchwork.com> * @author Hugo Hamon <hugohamon@neuf.fr> * * @throws ExceptionInterface */ abstract class AbstractString implements \Stringable, \JsonSerializable { public const PREG_PATTERN_ORDER = \PREG_PATTERN_ORDER; public const PREG_SET_ORDER = \PREG_SET_ORDER; public const PREG_OFFSET_CAPTURE = \PREG_OFFSET_CAPTURE; public const PREG_UNMATCHED_AS_NULL = \PREG_UNMATCHED_AS_NULL; public const PREG_SPLIT = 0; public const PREG_SPLIT_NO_EMPTY = \PREG_SPLIT_NO_EMPTY; public const PREG_SPLIT_DELIM_CAPTURE = \PREG_SPLIT_DELIM_CAPTURE; public const PREG_SPLIT_OFFSET_CAPTURE = \PREG_SPLIT_OFFSET_CAPTURE; protected string $string = ''; protected ?bool $ignoreCase = false; abstract public function __construct(string $string = ''); /** * Unwraps instances of AbstractString back to strings. * * @return string[]|array */ public static function unwrap(array $values): array { foreach ($values as $k => $v) { if ($v instanceof self) { $values[$k] = $v->__toString(); } elseif (\is_array($v) && $values[$k] !== $v = static::unwrap($v)) { $values[$k] = $v; } } return $values; } /** * Wraps (and normalizes) strings in instances of AbstractString. * * @return static[]|array */ public static function wrap(array $values): array { $i = 0; $keys = null; foreach ($values as $k => $v) { if (\is_string($k) && '' !== $k && $k !== $j = (string) new static($k)) { $keys ??= array_keys($values); $keys[$i] = $j; } if (\is_string($v)) { $values[$k] = new static($v); } elseif (\is_array($v) && $values[$k] !== $v = static::wrap($v)) { $values[$k] = $v; } ++$i; } return null !== $keys ? array_combine($keys, $values) : $values; } /** * @param string|string[] $needle */ public function after(string|iterable $needle, bool $includeNeedle = false, int $offset = 0): static { $str = clone $this; $i = \PHP_INT_MAX; if (\is_string($needle)) { $needle = [$needle]; } foreach ($needle as $n) { $n = (string) $n; $j = $this->indexOf($n, $offset); if (null !== $j && $j < $i) { $i = $j; $str->string = $n; } } if (\PHP_INT_MAX === $i) { return $str; } if (!$includeNeedle) { $i += $str->length(); } return $this->slice($i); } /** * @param string|string[] $needle */ public function afterLast(string|iterable $needle, bool $includeNeedle = false, int $offset = 0): static { $str = clone $this; $i = null; if (\is_string($needle)) { $needle = [$needle]; } foreach ($needle as $n) { $n = (string) $n; $j = $this->indexOfLast($n, $offset); if (null !== $j && $j >= $i) { $i = $offset = $j; $str->string = $n; } } if (null === $i) { return $str; } if (!$includeNeedle) { $i += $str->length(); } return $this->slice($i); } abstract public function append(string ...$suffix): static; /** * @param string|string[] $needle */ public function before(string|iterable $needle, bool $includeNeedle = false, int $offset = 0): static { $str = clone $this; $i = \PHP_INT_MAX; if (\is_string($needle)) { $needle = [$needle]; } foreach ($needle as $n) { $n = (string) $n; $j = $this->indexOf($n, $offset); if (null !== $j && $j < $i) { $i = $j; $str->string = $n; } } if (\PHP_INT_MAX === $i) { return $str; } if ($includeNeedle) { $i += $str->length(); } return $this->slice(0, $i); } /** * @param string|string[] $needle */ public function beforeLast(string|iterable $needle, bool $includeNeedle = false, int $offset = 0): static { $str = clone $this; $i = null; if (\is_string($needle)) { $needle = [$needle]; } foreach ($needle as $n) { $n = (string) $n; $j = $this->indexOfLast($n, $offset); if (null !== $j && $j >= $i) { $i = $offset = $j; $str->string = $n; } } if (null === $i) { return $str; } if ($includeNeedle) { $i += $str->length(); } return $this->slice(0, $i); } /** * @return int[] */ public function bytesAt(int $offset): array { $str = $this->slice($offset, 1); return '' === $str->string ? [] : array_values(unpack('C*', $str->string)); } abstract public function camel(): static; /** * @return static[] */ abstract public function chunk(int $length = 1): array; public function collapseWhitespace(): static { $str = clone $this; $str->string = trim(preg_replace("/(?:[ \n\r\t\x0C]{2,}+|[\n\r\t\x0C])/", ' ', $str->string), " \n\r\t\x0C"); return $str; } /** * @param string|string[] $needle */ public function containsAny(string|iterable $needle): bool { return null !== $this->indexOf($needle); } /** * @param string|string[] $suffix */ public function endsWith(string|iterable $suffix): bool { if (\is_string($suffix)) { throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); } foreach ($suffix as $s) { if ($this->endsWith((string) $s)) { return true; } } return false; } public function ensureEnd(string $suffix): static { if (!$this->endsWith($suffix)) { return $this->append($suffix); } $suffix = preg_quote($suffix); $regex = '{('.$suffix.')(?:'.$suffix.')++$}D'; return $this->replaceMatches($regex.($this->ignoreCase ? 'i' : ''), '$1'); } public function ensureStart(string $prefix): static { $prefix = new static($prefix); if (!$this->startsWith($prefix)) { return $this->prepend($prefix); } $str = clone $this; $i = $prefixLen = $prefix->length(); while ($this->indexOf($prefix, $i) === $i) { $str = $str->slice($prefixLen); $i += $prefixLen; } return $str; } /** * @param string|string[] $string */ public function equalsTo(string|iterable $string): bool { if (\is_string($string)) { throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); } foreach ($string as $s) { if ($this->equalsTo((string) $s)) { return true; } } return false; } abstract public function folded(): static; public function ignoreCase(): static { $str = clone $this; $str->ignoreCase = true; return $str; } /** * @param string|string[] $needle */ public function indexOf(string|iterable $needle, int $offset = 0): ?int { if (\is_string($needle)) { throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); } $i = \PHP_INT_MAX; foreach ($needle as $n) { $j = $this->indexOf((string) $n, $offset); if (null !== $j && $j < $i) { $i = $j; } } return \PHP_INT_MAX === $i ? null : $i; } /** * @param string|string[] $needle */ public function indexOfLast(string|iterable $needle, int $offset = 0): ?int { if (\is_string($needle)) { throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); } $i = null; foreach ($needle as $n) { $j = $this->indexOfLast((string) $n, $offset); if (null !== $j && $j >= $i) { $i = $offset = $j; } } return $i; } public function isEmpty(): bool { return '' === $this->string; } abstract public function join(array $strings, ?string $lastGlue = null): static; public function jsonSerialize(): string { return $this->string; } abstract public function length(): int; abstract public function lower(): static; /** * Matches the string using a regular expression. * * Pass PREG_PATTERN_ORDER or PREG_SET_ORDER as $flags to get all occurrences matching the regular expression. * * @return array All matches in a multi-dimensional array ordered according to flags */ abstract public function match(string $regexp, int $flags = 0, int $offset = 0): array; abstract public function padBoth(int $length, string $padStr = ' '): static; abstract public function padEnd(int $length, string $padStr = ' '): static; abstract public function padStart(int $length, string $padStr = ' '): static; abstract public function prepend(string ...$prefix): static; public function repeat(int $multiplier): static { if (0 > $multiplier) { throw new InvalidArgumentException(sprintf('Multiplier must be positive, %d given.', $multiplier)); } $str = clone $this; $str->string = str_repeat($str->string, $multiplier); return $str; } abstract public function replace(string $from, string $to): static; abstract public function replaceMatches(string $fromRegexp, string|callable $to): static; abstract public function reverse(): static; abstract public function slice(int $start = 0, ?int $length = null): static; abstract public function snake(): static; abstract public function splice(string $replacement, int $start = 0, ?int $length = null): static; /** * @return static[] */ public function split(string $delimiter, ?int $limit = null, ?int $flags = null): array { if (null === $flags) { throw new \TypeError('Split behavior when $flags is null must be implemented by child classes.'); } if ($this->ignoreCase) { $delimiter .= 'i'; } set_error_handler(static fn ($t, $m) => throw new InvalidArgumentException($m)); try { if (false === $chunks = preg_split($delimiter, $this->string, $limit, $flags)) { throw new RuntimeException('Splitting failed with error: '.preg_last_error_msg()); } } finally { restore_error_handler(); } $str = clone $this; if (self::PREG_SPLIT_OFFSET_CAPTURE & $flags) { foreach ($chunks as &$chunk) { $str->string = $chunk[0]; $chunk[0] = clone $str; } } else { foreach ($chunks as &$chunk) { $str->string = $chunk; $chunk = clone $str; } } return $chunks; } /** * @param string|string[] $prefix */ public function startsWith(string|iterable $prefix): bool { if (\is_string($prefix)) { throw new \TypeError(sprintf('Method "%s()" must be overridden by class "%s" to deal with non-iterable values.', __FUNCTION__, static::class)); } foreach ($prefix as $prefix) { if ($this->startsWith((string) $prefix)) { return true; } } return false; } abstract public function title(bool $allWords = false): static; public function toByteString(?string $toEncoding = null): ByteString { $b = new ByteString(); $toEncoding = \in_array($toEncoding, ['utf8', 'utf-8', 'UTF8'], true) ? 'UTF-8' : $toEncoding; if (null === $toEncoding || $toEncoding === $fromEncoding = $this instanceof AbstractUnicodeString || preg_match('//u', $b->string) ? 'UTF-8' : 'Windows-1252') { $b->string = $this->string; return $b; } try { $b->string = mb_convert_encoding($this->string, $toEncoding, 'UTF-8'); } catch (\ValueError $e) { if (!\function_exists('iconv')) { throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); } $b->string = iconv('UTF-8', $toEncoding, $this->string); } return $b; } public function toCodePointString(): CodePointString { return new CodePointString($this->string); } public function toString(): string { return $this->string; } public function toUnicodeString(): UnicodeString { return new UnicodeString($this->string); } abstract public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static; abstract public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static; /** * @param string|string[] $prefix */ public function trimPrefix($prefix): static { if (\is_array($prefix) || $prefix instanceof \Traversable) { // don't use is_iterable(), it's slow foreach ($prefix as $s) { $t = $this->trimPrefix($s); if ($t->string !== $this->string) { return $t; } } return clone $this; } $str = clone $this; if ($prefix instanceof self) { $prefix = $prefix->string; } else { $prefix = (string) $prefix; } if ('' !== $prefix && \strlen($this->string) >= \strlen($prefix) && 0 === substr_compare($this->string, $prefix, 0, \strlen($prefix), $this->ignoreCase)) { $str->string = substr($this->string, \strlen($prefix)); } return $str; } abstract public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static; /** * @param string|string[] $suffix */ public function trimSuffix($suffix): static { if (\is_array($suffix) || $suffix instanceof \Traversable) { // don't use is_iterable(), it's slow foreach ($suffix as $s) { $t = $this->trimSuffix($s); if ($t->string !== $this->string) { return $t; } } return clone $this; } $str = clone $this; if ($suffix instanceof self) { $suffix = $suffix->string; } else { $suffix = (string) $suffix; } if ('' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase)) { $str->string = substr($this->string, 0, -\strlen($suffix)); } return $str; } public function truncate(int $length, string $ellipsis = '', bool $cut = true): static { $stringLength = $this->length(); if ($stringLength <= $length) { return clone $this; } $ellipsisLength = '' !== $ellipsis ? (new static($ellipsis))->length() : 0; if ($length < $ellipsisLength) { $ellipsisLength = 0; } if (!$cut) { if (null === $length = $this->indexOf([' ', "\r", "\n", "\t"], ($length ?: 1) - 1)) { return clone $this; } $length += $ellipsisLength; } $str = $this->slice(0, $length - $ellipsisLength); return $ellipsisLength ? $str->trimEnd()->append($ellipsis) : $str; } abstract public function upper(): static; /** * Returns the printable length on a terminal. */ abstract public function width(bool $ignoreAnsiDecoration = true): int; public function wordwrap(int $width = 75, string $break = "\n", bool $cut = false): static { $lines = '' !== $break ? $this->split($break) : [clone $this]; $chars = []; $mask = ''; if (1 === \count($lines) && '' === $lines[0]->string) { return $lines[0]; } foreach ($lines as $i => $line) { if ($i) { $chars[] = $break; $mask .= '#'; } foreach ($line->chunk() as $char) { $chars[] = $char->string; $mask .= ' ' === $char->string ? ' ' : '?'; } } $string = ''; $j = 0; $b = $i = -1; $mask = wordwrap($mask, $width, '#', $cut); while (false !== $b = strpos($mask, '#', $b + 1)) { for (++$i; $i < $b; ++$i) { $string .= $chars[$j]; unset($chars[$j++]); } if ($break === $chars[$j] || ' ' === $chars[$j]) { unset($chars[$j++]); } $string .= $break; } $str = clone $this; $str->string = $string.implode('', $chars); return $str; } public function __sleep(): array { return ['string']; } public function __clone() { $this->ignoreCase = false; } public function __toString(): string { return $this->string; } } string/ByteString.php 0000644 00000034741 15060133305 0010662 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; use Random\Randomizer; use Symfony\Component\String\Exception\ExceptionInterface; use Symfony\Component\String\Exception\InvalidArgumentException; use Symfony\Component\String\Exception\RuntimeException; /** * Represents a binary-safe string of bytes. * * @author Nicolas Grekas <p@tchwork.com> * @author Hugo Hamon <hugohamon@neuf.fr> * * @throws ExceptionInterface */ class ByteString extends AbstractString { private const ALPHABET_ALPHANUMERIC = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; public function __construct(string $string = '') { $this->string = $string; } /* * The following method was derived from code of the Hack Standard Library (v4.40 - 2020-05-03) * * https://github.com/hhvm/hsl/blob/80a42c02f036f72a42f0415e80d6b847f4bf62d5/src/random/private.php#L16 * * Code subject to the MIT license (https://github.com/hhvm/hsl/blob/master/LICENSE). * * Copyright (c) 2004-2020, Facebook, Inc. (https://www.facebook.com/) */ public static function fromRandom(int $length = 16, ?string $alphabet = null): self { if ($length <= 0) { throw new InvalidArgumentException(sprintf('A strictly positive length is expected, "%d" given.', $length)); } $alphabet ??= self::ALPHABET_ALPHANUMERIC; $alphabetSize = \strlen($alphabet); $bits = (int) ceil(log($alphabetSize, 2.0)); if ($bits <= 0 || $bits > 56) { throw new InvalidArgumentException('The length of the alphabet must in the [2^1, 2^56] range.'); } if (\PHP_VERSION_ID >= 80300) { return new static((new Randomizer())->getBytesFromString($alphabet, $length)); } $ret = ''; while ($length > 0) { $urandomLength = (int) ceil(2 * $length * $bits / 8.0); $data = random_bytes($urandomLength); $unpackedData = 0; $unpackedBits = 0; for ($i = 0; $i < $urandomLength && $length > 0; ++$i) { // Unpack 8 bits $unpackedData = ($unpackedData << 8) | \ord($data[$i]); $unpackedBits += 8; // While we have enough bits to select a character from the alphabet, keep // consuming the random data for (; $unpackedBits >= $bits && $length > 0; $unpackedBits -= $bits) { $index = ($unpackedData & ((1 << $bits) - 1)); $unpackedData >>= $bits; // Unfortunately, the alphabet size is not necessarily a power of two. // Worst case, it is 2^k + 1, which means we need (k+1) bits and we // have around a 50% chance of missing as k gets larger if ($index < $alphabetSize) { $ret .= $alphabet[$index]; --$length; } } } } return new static($ret); } public function bytesAt(int $offset): array { $str = $this->string[$offset] ?? ''; return '' === $str ? [] : [\ord($str)]; } public function append(string ...$suffix): static { $str = clone $this; $str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix); return $str; } public function camel(): static { $str = clone $this; $parts = explode(' ', trim(ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/', ' ', $this->string)))); $parts[0] = 1 !== \strlen($parts[0]) && ctype_upper($parts[0]) ? $parts[0] : lcfirst($parts[0]); $str->string = implode('', $parts); return $str; } public function chunk(int $length = 1): array { if (1 > $length) { throw new InvalidArgumentException('The chunk length must be greater than zero.'); } if ('' === $this->string) { return []; } $str = clone $this; $chunks = []; foreach (str_split($this->string, $length) as $chunk) { $str->string = $chunk; $chunks[] = clone $str; } return $chunks; } public function endsWith(string|iterable|AbstractString $suffix): bool { if ($suffix instanceof AbstractString) { $suffix = $suffix->string; } elseif (!\is_string($suffix)) { return parent::endsWith($suffix); } return '' !== $suffix && \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix), null, $this->ignoreCase); } public function equalsTo(string|iterable|AbstractString $string): bool { if ($string instanceof AbstractString) { $string = $string->string; } elseif (!\is_string($string)) { return parent::equalsTo($string); } if ('' !== $string && $this->ignoreCase) { return 0 === strcasecmp($string, $this->string); } return $string === $this->string; } public function folded(): static { $str = clone $this; $str->string = strtolower($str->string); return $str; } public function indexOf(string|iterable|AbstractString $needle, int $offset = 0): ?int { if ($needle instanceof AbstractString) { $needle = $needle->string; } elseif (!\is_string($needle)) { return parent::indexOf($needle, $offset); } if ('' === $needle) { return null; } $i = $this->ignoreCase ? stripos($this->string, $needle, $offset) : strpos($this->string, $needle, $offset); return false === $i ? null : $i; } public function indexOfLast(string|iterable|AbstractString $needle, int $offset = 0): ?int { if ($needle instanceof AbstractString) { $needle = $needle->string; } elseif (!\is_string($needle)) { return parent::indexOfLast($needle, $offset); } if ('' === $needle) { return null; } $i = $this->ignoreCase ? strripos($this->string, $needle, $offset) : strrpos($this->string, $needle, $offset); return false === $i ? null : $i; } public function isUtf8(): bool { return '' === $this->string || preg_match('//u', $this->string); } public function join(array $strings, ?string $lastGlue = null): static { $str = clone $this; $tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : ''; $str->string = implode($this->string, $strings).$tail; return $str; } public function length(): int { return \strlen($this->string); } public function lower(): static { $str = clone $this; $str->string = strtolower($str->string); return $str; } public function match(string $regexp, int $flags = 0, int $offset = 0): array { $match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match'; if ($this->ignoreCase) { $regexp .= 'i'; } set_error_handler(static fn ($t, $m) => throw new InvalidArgumentException($m)); try { if (false === $match($regexp, $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { throw new RuntimeException('Matching failed with error: '.preg_last_error_msg()); } } finally { restore_error_handler(); } return $matches; } public function padBoth(int $length, string $padStr = ' '): static { $str = clone $this; $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_BOTH); return $str; } public function padEnd(int $length, string $padStr = ' '): static { $str = clone $this; $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_RIGHT); return $str; } public function padStart(int $length, string $padStr = ' '): static { $str = clone $this; $str->string = str_pad($this->string, $length, $padStr, \STR_PAD_LEFT); return $str; } public function prepend(string ...$prefix): static { $str = clone $this; $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$str->string; return $str; } public function replace(string $from, string $to): static { $str = clone $this; if ('' !== $from) { $str->string = $this->ignoreCase ? str_ireplace($from, $to, $this->string) : str_replace($from, $to, $this->string); } return $str; } public function replaceMatches(string $fromRegexp, string|callable $to): static { if ($this->ignoreCase) { $fromRegexp .= 'i'; } $replace = \is_array($to) || $to instanceof \Closure ? 'preg_replace_callback' : 'preg_replace'; set_error_handler(static fn ($t, $m) => throw new InvalidArgumentException($m)); try { if (null === $string = $replace($fromRegexp, $to, $this->string)) { $lastError = preg_last_error(); foreach (get_defined_constants(true)['pcre'] as $k => $v) { if ($lastError === $v && str_ends_with($k, '_ERROR')) { throw new RuntimeException('Matching failed with '.$k.'.'); } } throw new RuntimeException('Matching failed with unknown error code.'); } } finally { restore_error_handler(); } $str = clone $this; $str->string = $string; return $str; } public function reverse(): static { $str = clone $this; $str->string = strrev($str->string); return $str; } public function slice(int $start = 0, ?int $length = null): static { $str = clone $this; $str->string = (string) substr($this->string, $start, $length ?? \PHP_INT_MAX); return $str; } public function snake(): static { $str = $this->camel(); $str->string = strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], '\1_\2', $str->string)); return $str; } public function splice(string $replacement, int $start = 0, ?int $length = null): static { $str = clone $this; $str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX); return $str; } public function split(string $delimiter, ?int $limit = null, ?int $flags = null): array { if (1 > $limit ??= \PHP_INT_MAX) { throw new InvalidArgumentException('Split limit must be a positive integer.'); } if ('' === $delimiter) { throw new InvalidArgumentException('Split delimiter is empty.'); } if (null !== $flags) { return parent::split($delimiter, $limit, $flags); } $str = clone $this; $chunks = $this->ignoreCase ? preg_split('{'.preg_quote($delimiter).'}iD', $this->string, $limit) : explode($delimiter, $this->string, $limit); foreach ($chunks as &$chunk) { $str->string = $chunk; $chunk = clone $str; } return $chunks; } public function startsWith(string|iterable|AbstractString $prefix): bool { if ($prefix instanceof AbstractString) { $prefix = $prefix->string; } elseif (!\is_string($prefix)) { return parent::startsWith($prefix); } return '' !== $prefix && 0 === ($this->ignoreCase ? strncasecmp($this->string, $prefix, \strlen($prefix)) : strncmp($this->string, $prefix, \strlen($prefix))); } public function title(bool $allWords = false): static { $str = clone $this; $str->string = $allWords ? ucwords($str->string) : ucfirst($str->string); return $str; } public function toUnicodeString(?string $fromEncoding = null): UnicodeString { return new UnicodeString($this->toCodePointString($fromEncoding)->string); } public function toCodePointString(?string $fromEncoding = null): CodePointString { $u = new CodePointString(); if (\in_array($fromEncoding, [null, 'utf8', 'utf-8', 'UTF8', 'UTF-8'], true) && preg_match('//u', $this->string)) { $u->string = $this->string; return $u; } set_error_handler(static fn ($t, $m) => throw new InvalidArgumentException($m)); try { try { $validEncoding = false !== mb_detect_encoding($this->string, $fromEncoding ?? 'Windows-1252', true); } catch (InvalidArgumentException $e) { if (!\function_exists('iconv')) { throw $e; } $u->string = iconv($fromEncoding ?? 'Windows-1252', 'UTF-8', $this->string); return $u; } } finally { restore_error_handler(); } if (!$validEncoding) { throw new InvalidArgumentException(sprintf('Invalid "%s" string.', $fromEncoding ?? 'Windows-1252')); } $u->string = mb_convert_encoding($this->string, 'UTF-8', $fromEncoding ?? 'Windows-1252'); return $u; } public function trim(string $chars = " \t\n\r\0\x0B\x0C"): static { $str = clone $this; $str->string = trim($str->string, $chars); return $str; } public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C"): static { $str = clone $this; $str->string = rtrim($str->string, $chars); return $str; } public function trimStart(string $chars = " \t\n\r\0\x0B\x0C"): static { $str = clone $this; $str->string = ltrim($str->string, $chars); return $str; } public function upper(): static { $str = clone $this; $str->string = strtoupper($str->string); return $str; } public function width(bool $ignoreAnsiDecoration = true): int { $string = preg_match('//u', $this->string) ? $this->string : preg_replace('/[\x80-\xFF]/', '?', $this->string); return (new CodePointString($string))->width($ignoreAnsiDecoration); } } string/LazyString.php 0000644 00000010444 15060133305 0010670 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; /** * A string whose value is computed lazily by a callback. * * @author Nicolas Grekas <p@tchwork.com> */ class LazyString implements \Stringable, \JsonSerializable { private \Closure|string $value; /** * @param callable|array $callback A callable or a [Closure, method] lazy-callable */ public static function fromCallable(callable|array $callback, mixed ...$arguments): static { if (\is_array($callback) && !\is_callable($callback) && !(($callback[0] ?? null) instanceof \Closure || 2 < \count($callback))) { throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a callable or a [Closure, method] lazy-callable, "%s" given.', __METHOD__, '['.implode(', ', array_map('get_debug_type', $callback)).']')); } $lazyString = new static(); $lazyString->value = static function () use (&$callback, &$arguments): string { static $value; if (null !== $arguments) { if (!\is_callable($callback)) { $callback[0] = $callback[0](); $callback[1] ??= '__invoke'; } $value = $callback(...$arguments); $callback = !\is_scalar($value) && !$value instanceof \Stringable ? self::getPrettyName($callback) : 'callable'; $arguments = null; } return $value ?? ''; }; return $lazyString; } public static function fromStringable(string|int|float|bool|\Stringable $value): static { if (\is_object($value)) { return static::fromCallable($value->__toString(...)); } $lazyString = new static(); $lazyString->value = (string) $value; return $lazyString; } /** * Tells whether the provided value can be cast to string. */ final public static function isStringable(mixed $value): bool { return \is_string($value) || $value instanceof \Stringable || \is_scalar($value); } /** * Casts scalars and stringable objects to strings. * * @throws \TypeError When the provided value is not stringable */ final public static function resolve(\Stringable|string|int|float|bool $value): string { return $value; } public function __toString(): string { if (\is_string($this->value)) { return $this->value; } try { return $this->value = ($this->value)(); } catch (\Throwable $e) { if (\TypeError::class === $e::class && __FILE__ === $e->getFile()) { $type = explode(', ', $e->getMessage()); $type = substr(array_pop($type), 0, -\strlen(' returned')); $r = new \ReflectionFunction($this->value); $callback = $r->getStaticVariables()['callback']; $e = new \TypeError(sprintf('Return value of %s() passed to %s::fromCallable() must be of the type string, %s returned.', $callback, static::class, $type)); } throw $e; } } public function __sleep(): array { $this->__toString(); return ['value']; } public function jsonSerialize(): string { return $this->__toString(); } private function __construct() { } private static function getPrettyName(callable $callback): string { if (\is_string($callback)) { return $callback; } if (\is_array($callback)) { $class = \is_object($callback[0]) ? get_debug_type($callback[0]) : $callback[0]; $method = $callback[1]; } elseif ($callback instanceof \Closure) { $r = new \ReflectionFunction($callback); if ($r->isAnonymous() || !$class = $r->getClosureCalledClass()) { return $r->name; } $class = $class->name; $method = $r->name; } else { $class = get_debug_type($callback); $method = '__invoke'; } return $class.'::'.$method; } } string/CodePointString.php 0000644 00000017050 15060133305 0011635 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; use Symfony\Component\String\Exception\ExceptionInterface; use Symfony\Component\String\Exception\InvalidArgumentException; /** * Represents a string of Unicode code points encoded as UTF-8. * * @author Nicolas Grekas <p@tchwork.com> * @author Hugo Hamon <hugohamon@neuf.fr> * * @throws ExceptionInterface */ class CodePointString extends AbstractUnicodeString { public function __construct(string $string = '') { if ('' !== $string && !preg_match('//u', $string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $this->string = $string; } public function append(string ...$suffix): static { $str = clone $this; $str->string .= 1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix); if (!preg_match('//u', $str->string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } return $str; } public function chunk(int $length = 1): array { if (1 > $length) { throw new InvalidArgumentException('The chunk length must be greater than zero.'); } if ('' === $this->string) { return []; } $rx = '/('; while (65535 < $length) { $rx .= '.{65535}'; $length -= 65535; } $rx .= '.{'.$length.'})/us'; $str = clone $this; $chunks = []; foreach (preg_split($rx, $this->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY) as $chunk) { $str->string = $chunk; $chunks[] = clone $str; } return $chunks; } public function codePointsAt(int $offset): array { $str = $offset ? $this->slice($offset, 1) : $this; return '' === $str->string ? [] : [mb_ord($str->string, 'UTF-8')]; } public function endsWith(string|iterable|AbstractString $suffix): bool { if ($suffix instanceof AbstractString) { $suffix = $suffix->string; } elseif (!\is_string($suffix)) { return parent::endsWith($suffix); } if ('' === $suffix || !preg_match('//u', $suffix)) { return false; } if ($this->ignoreCase) { return preg_match('{'.preg_quote($suffix).'$}iuD', $this->string); } return \strlen($this->string) >= \strlen($suffix) && 0 === substr_compare($this->string, $suffix, -\strlen($suffix)); } public function equalsTo(string|iterable|AbstractString $string): bool { if ($string instanceof AbstractString) { $string = $string->string; } elseif (!\is_string($string)) { return parent::equalsTo($string); } if ('' !== $string && $this->ignoreCase) { return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8'); } return $string === $this->string; } public function indexOf(string|iterable|AbstractString $needle, int $offset = 0): ?int { if ($needle instanceof AbstractString) { $needle = $needle->string; } elseif (!\is_string($needle)) { return parent::indexOf($needle, $offset); } if ('' === $needle) { return null; } $i = $this->ignoreCase ? mb_stripos($this->string, $needle, $offset, 'UTF-8') : mb_strpos($this->string, $needle, $offset, 'UTF-8'); return false === $i ? null : $i; } public function indexOfLast(string|iterable|AbstractString $needle, int $offset = 0): ?int { if ($needle instanceof AbstractString) { $needle = $needle->string; } elseif (!\is_string($needle)) { return parent::indexOfLast($needle, $offset); } if ('' === $needle) { return null; } $i = $this->ignoreCase ? mb_strripos($this->string, $needle, $offset, 'UTF-8') : mb_strrpos($this->string, $needle, $offset, 'UTF-8'); return false === $i ? null : $i; } public function length(): int { return mb_strlen($this->string, 'UTF-8'); } public function prepend(string ...$prefix): static { $str = clone $this; $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$this->string; if (!preg_match('//u', $str->string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } return $str; } public function replace(string $from, string $to): static { $str = clone $this; if ('' === $from || !preg_match('//u', $from)) { return $str; } if ('' !== $to && !preg_match('//u', $to)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } if ($this->ignoreCase) { $str->string = implode($to, preg_split('{'.preg_quote($from).'}iuD', $this->string)); } else { $str->string = str_replace($from, $to, $this->string); } return $str; } public function slice(int $start = 0, ?int $length = null): static { $str = clone $this; $str->string = mb_substr($this->string, $start, $length, 'UTF-8'); return $str; } public function splice(string $replacement, int $start = 0, ?int $length = null): static { if (!preg_match('//u', $replacement)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $str = clone $this; $start = $start ? \strlen(mb_substr($this->string, 0, $start, 'UTF-8')) : 0; $length = $length ? \strlen(mb_substr($this->string, $start, $length, 'UTF-8')) : $length; $str->string = substr_replace($this->string, $replacement, $start, $length ?? \PHP_INT_MAX); return $str; } public function split(string $delimiter, ?int $limit = null, ?int $flags = null): array { if (1 > $limit ??= \PHP_INT_MAX) { throw new InvalidArgumentException('Split limit must be a positive integer.'); } if ('' === $delimiter) { throw new InvalidArgumentException('Split delimiter is empty.'); } if (null !== $flags) { return parent::split($delimiter.'u', $limit, $flags); } if (!preg_match('//u', $delimiter)) { throw new InvalidArgumentException('Split delimiter is not a valid UTF-8 string.'); } $str = clone $this; $chunks = $this->ignoreCase ? preg_split('{'.preg_quote($delimiter).'}iuD', $this->string, $limit) : explode($delimiter, $this->string, $limit); foreach ($chunks as &$chunk) { $str->string = $chunk; $chunk = clone $str; } return $chunks; } public function startsWith(string|iterable|AbstractString $prefix): bool { if ($prefix instanceof AbstractString) { $prefix = $prefix->string; } elseif (!\is_string($prefix)) { return parent::startsWith($prefix); } if ('' === $prefix || !preg_match('//u', $prefix)) { return false; } if ($this->ignoreCase) { return 0 === mb_stripos($this->string, $prefix, 0, 'UTF-8'); } return 0 === strncmp($this->string, $prefix, \strlen($prefix)); } } string/CHANGELOG.md 0000644 00000001740 15060133305 0007661 0 ustar 00 CHANGELOG ========= 7.1 --- * Add `localeLower()`, `localeUpper()`, `localeTitle()` methods to `AbstractUnicodeString` 6.2 --- * Add support for emoji in `AsciiSlugger` 5.4 --- * Add `trimSuffix()` and `trimPrefix()` methods 5.3 --- * Made `AsciiSlugger` fallback to parent locale's symbolsMap 5.2.0 ----- * added a `FrenchInflector` class 5.1.0 ----- * added the `AbstractString::reverse()` method * made `AbstractString::width()` follow POSIX.1-2001 * added `LazyString` which provides memoizing stringable objects * The component is not marked as `@experimental` anymore * added the `s()` helper method to get either an `UnicodeString` or `ByteString` instance, depending of the input string UTF-8 compliancy * added `$cut` parameter to `Symfony\Component\String\AbstractString::truncate()` * added `AbstractString::containsAny()` * allow passing a string of custom characters to `ByteString::fromRandom()` 5.0.0 ----- * added the component as experimental string/composer.json 0000644 00000002571 15060133305 0010575 0 ustar 00 { "name": "symfony/string", "type": "library", "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", "keywords": ["string", "utf8", "utf-8", "grapheme", "i18n", "unicode"], "homepage": "https://symfony.com", "license": "MIT", "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "require": { "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { "symfony/error-handler": "^6.4|^7.0", "symfony/emoji": "^7.1", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", "symfony/var-exporter": "^6.4|^7.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "autoload": { "psr-4": { "Symfony\\Component\\String\\": "" }, "files": [ "Resources/functions.php" ], "exclude-from-classmap": [ "/Tests/" ] }, "minimum-stability": "dev" } string/LICENSE 0000644 00000002054 15060133305 0007054 0 ustar 00 Copyright (c) 2019-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. string/UnicodeString.php 0000644 00000030364 15060133305 0011342 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; use Symfony\Component\String\Exception\ExceptionInterface; use Symfony\Component\String\Exception\InvalidArgumentException; /** * Represents a string of Unicode grapheme clusters encoded as UTF-8. * * A letter followed by combining characters (accents typically) form what Unicode defines * as a grapheme cluster: a character as humans mean it in written texts. This class knows * about the concept and won't split a letter apart from its combining accents. It also * ensures all string comparisons happen on their canonically-composed representation, * ignoring e.g. the order in which accents are listed when a letter has many of them. * * @see https://unicode.org/reports/tr15/ * * @author Nicolas Grekas <p@tchwork.com> * @author Hugo Hamon <hugohamon@neuf.fr> * * @throws ExceptionInterface */ class UnicodeString extends AbstractUnicodeString { public function __construct(string $string = '') { if ('' === $string || normalizer_is_normalized($this->string = $string)) { return; } if (false === $string = normalizer_normalize($string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $this->string = $string; } public function append(string ...$suffix): static { $str = clone $this; $str->string = $this->string.(1 >= \count($suffix) ? ($suffix[0] ?? '') : implode('', $suffix)); if (normalizer_is_normalized($str->string)) { return $str; } if (false === $string = normalizer_normalize($str->string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $str->string = $string; return $str; } public function chunk(int $length = 1): array { if (1 > $length) { throw new InvalidArgumentException('The chunk length must be greater than zero.'); } if ('' === $this->string) { return []; } $rx = '/('; while (65535 < $length) { $rx .= '\X{65535}'; $length -= 65535; } $rx .= '\X{'.$length.'})/u'; $str = clone $this; $chunks = []; foreach (preg_split($rx, $this->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY) as $chunk) { $str->string = $chunk; $chunks[] = clone $str; } return $chunks; } public function endsWith(string|iterable|AbstractString $suffix): bool { if ($suffix instanceof AbstractString) { $suffix = $suffix->string; } elseif (!\is_string($suffix)) { return parent::endsWith($suffix); } $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; normalizer_is_normalized($suffix, $form) ?: $suffix = normalizer_normalize($suffix, $form); if ('' === $suffix || false === $suffix) { return false; } if ($this->ignoreCase) { return 0 === mb_stripos(grapheme_extract($this->string, \strlen($suffix), \GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix)), $suffix, 0, 'UTF-8'); } return $suffix === grapheme_extract($this->string, \strlen($suffix), \GRAPHEME_EXTR_MAXBYTES, \strlen($this->string) - \strlen($suffix)); } public function equalsTo(string|iterable|AbstractString $string): bool { if ($string instanceof AbstractString) { $string = $string->string; } elseif (!\is_string($string)) { return parent::equalsTo($string); } $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; normalizer_is_normalized($string, $form) ?: $string = normalizer_normalize($string, $form); if ('' !== $string && false !== $string && $this->ignoreCase) { return \strlen($string) === \strlen($this->string) && 0 === mb_stripos($this->string, $string, 0, 'UTF-8'); } return $string === $this->string; } public function indexOf(string|iterable|AbstractString $needle, int $offset = 0): ?int { if ($needle instanceof AbstractString) { $needle = $needle->string; } elseif (!\is_string($needle)) { return parent::indexOf($needle, $offset); } $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; normalizer_is_normalized($needle, $form) ?: $needle = normalizer_normalize($needle, $form); if ('' === $needle || false === $needle) { return null; } try { $i = $this->ignoreCase ? grapheme_stripos($this->string, $needle, $offset) : grapheme_strpos($this->string, $needle, $offset); } catch (\ValueError) { return null; } return false === $i ? null : $i; } public function indexOfLast(string|iterable|AbstractString $needle, int $offset = 0): ?int { if ($needle instanceof AbstractString) { $needle = $needle->string; } elseif (!\is_string($needle)) { return parent::indexOfLast($needle, $offset); } $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; normalizer_is_normalized($needle, $form) ?: $needle = normalizer_normalize($needle, $form); if ('' === $needle || false === $needle) { return null; } $string = $this->string; if (0 > $offset) { // workaround https://bugs.php.net/74264 if (0 > $offset += grapheme_strlen($needle)) { $string = grapheme_substr($string, 0, $offset); } $offset = 0; } $i = $this->ignoreCase ? grapheme_strripos($string, $needle, $offset) : grapheme_strrpos($string, $needle, $offset); return false === $i ? null : $i; } public function join(array $strings, ?string $lastGlue = null): static { $str = parent::join($strings, $lastGlue); normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); return $str; } public function length(): int { return grapheme_strlen($this->string); } public function normalize(int $form = self::NFC): static { $str = clone $this; if (\in_array($form, [self::NFC, self::NFKC], true)) { normalizer_is_normalized($str->string, $form) ?: $str->string = normalizer_normalize($str->string, $form); } elseif (!\in_array($form, [self::NFD, self::NFKD], true)) { throw new InvalidArgumentException('Unsupported normalization form.'); } elseif (!normalizer_is_normalized($str->string, $form)) { $str->string = normalizer_normalize($str->string, $form); $str->ignoreCase = null; } return $str; } public function prepend(string ...$prefix): static { $str = clone $this; $str->string = (1 >= \count($prefix) ? ($prefix[0] ?? '') : implode('', $prefix)).$this->string; if (normalizer_is_normalized($str->string)) { return $str; } if (false === $string = normalizer_normalize($str->string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $str->string = $string; return $str; } public function replace(string $from, string $to): static { $str = clone $this; normalizer_is_normalized($from) ?: $from = normalizer_normalize($from); if ('' !== $from && false !== $from) { $tail = $str->string; $result = ''; $indexOf = $this->ignoreCase ? 'grapheme_stripos' : 'grapheme_strpos'; while ('' !== $tail && false !== $i = $indexOf($tail, $from)) { $slice = grapheme_substr($tail, 0, $i); $result .= $slice.$to; $tail = substr($tail, \strlen($slice) + \strlen($from)); } $str->string = $result.$tail; if (normalizer_is_normalized($str->string)) { return $str; } if (false === $string = normalizer_normalize($str->string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $str->string = $string; } return $str; } public function replaceMatches(string $fromRegexp, string|callable $to): static { $str = parent::replaceMatches($fromRegexp, $to); normalizer_is_normalized($str->string) ?: $str->string = normalizer_normalize($str->string); return $str; } public function slice(int $start = 0, ?int $length = null): static { $str = clone $this; $str->string = (string) grapheme_substr($this->string, $start, $length ?? 2147483647); return $str; } public function splice(string $replacement, int $start = 0, ?int $length = null): static { $str = clone $this; $start = $start ? \strlen(grapheme_substr($this->string, 0, $start)) : 0; $length = $length ? \strlen(grapheme_substr($this->string, $start, $length ?? 2147483647)) : $length; $str->string = substr_replace($this->string, $replacement, $start, $length ?? 2147483647); if (normalizer_is_normalized($str->string)) { return $str; } if (false === $string = normalizer_normalize($str->string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $str->string = $string; return $str; } public function split(string $delimiter, ?int $limit = null, ?int $flags = null): array { if (1 > $limit ??= 2147483647) { throw new InvalidArgumentException('Split limit must be a positive integer.'); } if ('' === $delimiter) { throw new InvalidArgumentException('Split delimiter is empty.'); } if (null !== $flags) { return parent::split($delimiter.'u', $limit, $flags); } normalizer_is_normalized($delimiter) ?: $delimiter = normalizer_normalize($delimiter); if (false === $delimiter) { throw new InvalidArgumentException('Split delimiter is not a valid UTF-8 string.'); } $str = clone $this; $tail = $this->string; $chunks = []; $indexOf = $this->ignoreCase ? 'grapheme_stripos' : 'grapheme_strpos'; while (1 < $limit && false !== $i = $indexOf($tail, $delimiter)) { $str->string = grapheme_substr($tail, 0, $i); $chunks[] = clone $str; $tail = substr($tail, \strlen($str->string) + \strlen($delimiter)); --$limit; } $str->string = $tail; $chunks[] = clone $str; return $chunks; } public function startsWith(string|iterable|AbstractString $prefix): bool { if ($prefix instanceof AbstractString) { $prefix = $prefix->string; } elseif (!\is_string($prefix)) { return parent::startsWith($prefix); } $form = null === $this->ignoreCase ? \Normalizer::NFD : \Normalizer::NFC; normalizer_is_normalized($prefix, $form) ?: $prefix = normalizer_normalize($prefix, $form); if ('' === $prefix || false === $prefix) { return false; } if ($this->ignoreCase) { return 0 === mb_stripos(grapheme_extract($this->string, \strlen($prefix), \GRAPHEME_EXTR_MAXBYTES), $prefix, 0, 'UTF-8'); } return $prefix === grapheme_extract($this->string, \strlen($prefix), \GRAPHEME_EXTR_MAXBYTES); } public function __wakeup(): void { if (!\is_string($this->string)) { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string); } public function __clone() { if (null === $this->ignoreCase) { normalizer_is_normalized($this->string) ?: $this->string = normalizer_normalize($this->string); } $this->ignoreCase = false; } } string/AbstractUnicodeString.php 0000644 00000067140 15060133305 0013030 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; use Symfony\Component\String\Exception\ExceptionInterface; use Symfony\Component\String\Exception\InvalidArgumentException; use Symfony\Component\String\Exception\RuntimeException; /** * Represents a string of abstract Unicode characters. * * Unicode defines 3 types of "characters" (bytes, code points and grapheme clusters). * This class is the abstract type to use as a type-hint when the logic you want to * implement is Unicode-aware but doesn't care about code points vs grapheme clusters. * * @author Nicolas Grekas <p@tchwork.com> * * @throws ExceptionInterface */ abstract class AbstractUnicodeString extends AbstractString { public const NFC = \Normalizer::NFC; public const NFD = \Normalizer::NFD; public const NFKC = \Normalizer::NFKC; public const NFKD = \Normalizer::NFKD; // all ASCII letters sorted by typical frequency of occurrence private const ASCII = "\x20\x65\x69\x61\x73\x6E\x74\x72\x6F\x6C\x75\x64\x5D\x5B\x63\x6D\x70\x27\x0A\x67\x7C\x68\x76\x2E\x66\x62\x2C\x3A\x3D\x2D\x71\x31\x30\x43\x32\x2A\x79\x78\x29\x28\x4C\x39\x41\x53\x2F\x50\x22\x45\x6A\x4D\x49\x6B\x33\x3E\x35\x54\x3C\x44\x34\x7D\x42\x7B\x38\x46\x77\x52\x36\x37\x55\x47\x4E\x3B\x4A\x7A\x56\x23\x48\x4F\x57\x5F\x26\x21\x4B\x3F\x58\x51\x25\x59\x5C\x09\x5A\x2B\x7E\x5E\x24\x40\x60\x7F\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"; // the subset of folded case mappings that is not in lower case mappings private const FOLD_FROM = ['İ', 'µ', 'ſ', "\xCD\x85", 'ς', 'ϐ', 'ϑ', 'ϕ', 'ϖ', 'ϰ', 'ϱ', 'ϵ', 'ẛ', "\xE1\xBE\xBE", 'ß', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'և', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'ẚ', 'ẞ', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'ᾐ', 'ᾑ', 'ᾒ', 'ᾓ', 'ᾔ', 'ᾕ', 'ᾖ', 'ᾗ', 'ᾘ', 'ᾙ', 'ᾚ', 'ᾛ', 'ᾜ', 'ᾝ', 'ᾞ', 'ᾟ', 'ᾠ', 'ᾡ', 'ᾢ', 'ᾣ', 'ᾤ', 'ᾥ', 'ᾦ', 'ᾧ', 'ᾨ', 'ᾩ', 'ᾪ', 'ᾫ', 'ᾬ', 'ᾭ', 'ᾮ', 'ᾯ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'ᾼ', 'ῂ', 'ῃ', 'ῄ', 'ῆ', 'ῇ', 'ῌ', 'ῒ', 'ῖ', 'ῗ', 'ῢ', 'ῤ', 'ῦ', 'ῧ', 'ῲ', 'ῳ', 'ῴ', 'ῶ', 'ῷ', 'ῼ', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'ſt', 'st', 'ﬓ', 'ﬔ', 'ﬕ', 'ﬖ', 'ﬗ']; private const FOLD_TO = ['i̇', 'μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', 'ṡ', 'ι', 'ss', 'ʼn', 'ǰ', 'ΐ', 'ΰ', 'եւ', 'ẖ', 'ẗ', 'ẘ', 'ẙ', 'aʾ', 'ss', 'ὐ', 'ὒ', 'ὔ', 'ὖ', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἀι', 'ἁι', 'ἂι', 'ἃι', 'ἄι', 'ἅι', 'ἆι', 'ἇι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ἠι', 'ἡι', 'ἢι', 'ἣι', 'ἤι', 'ἥι', 'ἦι', 'ἧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὠι', 'ὡι', 'ὢι', 'ὣι', 'ὤι', 'ὥι', 'ὦι', 'ὧι', 'ὰι', 'αι', 'άι', 'ᾶ', 'ᾶι', 'αι', 'ὴι', 'ηι', 'ήι', 'ῆ', 'ῆι', 'ηι', 'ῒ', 'ῖ', 'ῗ', 'ῢ', 'ῤ', 'ῦ', 'ῧ', 'ὼι', 'ωι', 'ώι', 'ῶ', 'ῶι', 'ωι', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'st', 'st', 'մն', 'մե', 'մի', 'վն', 'մխ']; // the subset of https://github.com/unicode-org/cldr/blob/master/common/transforms/Latin-ASCII.xml that is not in NFKD private const TRANSLIT_FROM = ['Æ', 'Ð', 'Ø', 'Þ', 'ß', 'æ', 'ð', 'ø', 'þ', 'Đ', 'đ', 'Ħ', 'ħ', 'ı', 'ĸ', 'Ŀ', 'ŀ', 'Ł', 'ł', 'ʼn', 'Ŋ', 'ŋ', 'Œ', 'œ', 'Ŧ', 'ŧ', 'ƀ', 'Ɓ', 'Ƃ', 'ƃ', 'Ƈ', 'ƈ', 'Ɖ', 'Ɗ', 'Ƌ', 'ƌ', 'Ɛ', 'Ƒ', 'ƒ', 'Ɠ', 'ƕ', 'Ɩ', 'Ɨ', 'Ƙ', 'ƙ', 'ƚ', 'Ɲ', 'ƞ', 'Ƣ', 'ƣ', 'Ƥ', 'ƥ', 'ƫ', 'Ƭ', 'ƭ', 'Ʈ', 'Ʋ', 'Ƴ', 'ƴ', 'Ƶ', 'ƶ', 'DŽ', 'Dž', 'dž', 'Ǥ', 'ǥ', 'ȡ', 'Ȥ', 'ȥ', 'ȴ', 'ȵ', 'ȶ', 'ȷ', 'ȸ', 'ȹ', 'Ⱥ', 'Ȼ', 'ȼ', 'Ƚ', 'Ⱦ', 'ȿ', 'ɀ', 'Ƀ', 'Ʉ', 'Ɇ', 'ɇ', 'Ɉ', 'ɉ', 'Ɍ', 'ɍ', 'Ɏ', 'ɏ', 'ɓ', 'ɕ', 'ɖ', 'ɗ', 'ɛ', 'ɟ', 'ɠ', 'ɡ', 'ɢ', 'ɦ', 'ɧ', 'ɨ', 'ɪ', 'ɫ', 'ɬ', 'ɭ', 'ɱ', 'ɲ', 'ɳ', 'ɴ', 'ɶ', 'ɼ', 'ɽ', 'ɾ', 'ʀ', 'ʂ', 'ʈ', 'ʉ', 'ʋ', 'ʏ', 'ʐ', 'ʑ', 'ʙ', 'ʛ', 'ʜ', 'ʝ', 'ʟ', 'ʠ', 'ʣ', 'ʥ', 'ʦ', 'ʪ', 'ʫ', 'ᴀ', 'ᴁ', 'ᴃ', 'ᴄ', 'ᴅ', 'ᴆ', 'ᴇ', 'ᴊ', 'ᴋ', 'ᴌ', 'ᴍ', 'ᴏ', 'ᴘ', 'ᴛ', 'ᴜ', 'ᴠ', 'ᴡ', 'ᴢ', 'ᵫ', 'ᵬ', 'ᵭ', 'ᵮ', 'ᵯ', 'ᵰ', 'ᵱ', 'ᵲ', 'ᵳ', 'ᵴ', 'ᵵ', 'ᵶ', 'ᵺ', 'ᵻ', 'ᵽ', 'ᵾ', 'ᶀ', 'ᶁ', 'ᶂ', 'ᶃ', 'ᶄ', 'ᶅ', 'ᶆ', 'ᶇ', 'ᶈ', 'ᶉ', 'ᶊ', 'ᶌ', 'ᶍ', 'ᶎ', 'ᶏ', 'ᶑ', 'ᶒ', 'ᶓ', 'ᶖ', 'ᶙ', 'ẚ', 'ẜ', 'ẝ', 'ẞ', 'Ỻ', 'ỻ', 'Ỽ', 'ỽ', 'Ỿ', 'ỿ', '©', '®', '₠', '₢', '₣', '₤', '₧', '₺', '₹', 'ℌ', '℞', '㎧', '㎮', '㏆', '㏗', '㏞', '㏟', '¼', '½', '¾', '⅓', '⅔', '⅕', '⅖', '⅗', '⅘', '⅙', '⅚', '⅛', '⅜', '⅝', '⅞', '⅟', '〇', '‘', '’', '‚', '‛', '“', '”', '„', '‟', '′', '″', '〝', '〞', '«', '»', '‹', '›', '‐', '‑', '‒', '–', '—', '―', '︱', '︲', '﹘', '‖', '⁄', '⁅', '⁆', '⁎', '、', '。', '〈', '〉', '《', '》', '〔', '〕', '〘', '〙', '〚', '〛', '︑', '︒', '︹', '︺', '︽', '︾', '︿', '﹀', '﹑', '﹝', '﹞', '⦅', '⦆', '。', '、', '×', '÷', '−', '∕', '∖', '∣', '∥', '≪', '≫', '⦅', '⦆']; private const TRANSLIT_TO = ['AE', 'D', 'O', 'TH', 'ss', 'ae', 'd', 'o', 'th', 'D', 'd', 'H', 'h', 'i', 'q', 'L', 'l', 'L', 'l', '\'n', 'N', 'n', 'OE', 'oe', 'T', 't', 'b', 'B', 'B', 'b', 'C', 'c', 'D', 'D', 'D', 'd', 'E', 'F', 'f', 'G', 'hv', 'I', 'I', 'K', 'k', 'l', 'N', 'n', 'OI', 'oi', 'P', 'p', 't', 'T', 't', 'T', 'V', 'Y', 'y', 'Z', 'z', 'DZ', 'Dz', 'dz', 'G', 'g', 'd', 'Z', 'z', 'l', 'n', 't', 'j', 'db', 'qp', 'A', 'C', 'c', 'L', 'T', 's', 'z', 'B', 'U', 'E', 'e', 'J', 'j', 'R', 'r', 'Y', 'y', 'b', 'c', 'd', 'd', 'e', 'j', 'g', 'g', 'G', 'h', 'h', 'i', 'I', 'l', 'l', 'l', 'm', 'n', 'n', 'N', 'OE', 'r', 'r', 'r', 'R', 's', 't', 'u', 'v', 'Y', 'z', 'z', 'B', 'G', 'H', 'j', 'L', 'q', 'dz', 'dz', 'ts', 'ls', 'lz', 'A', 'AE', 'B', 'C', 'D', 'D', 'E', 'J', 'K', 'L', 'M', 'O', 'P', 'T', 'U', 'V', 'W', 'Z', 'ue', 'b', 'd', 'f', 'm', 'n', 'p', 'r', 'r', 's', 't', 'z', 'th', 'I', 'p', 'U', 'b', 'd', 'f', 'g', 'k', 'l', 'm', 'n', 'p', 'r', 's', 'v', 'x', 'z', 'a', 'd', 'e', 'e', 'i', 'u', 'a', 's', 's', 'SS', 'LL', 'll', 'V', 'v', 'Y', 'y', '(C)', '(R)', 'CE', 'Cr', 'Fr.', 'L.', 'Pts', 'TL', 'Rs', 'x', 'Rx', 'm/s', 'rad/s', 'C/kg', 'pH', 'V/m', 'A/m', ' 1/4', ' 1/2', ' 3/4', ' 1/3', ' 2/3', ' 1/5', ' 2/5', ' 3/5', ' 4/5', ' 1/6', ' 5/6', ' 1/8', ' 3/8', ' 5/8', ' 7/8', ' 1/', '0', '\'', '\'', ',', '\'', '"', '"', ',,', '"', '\'', '"', '"', '"', '<<', '>>', '<', '>', '-', '-', '-', '-', '-', '-', '-', '-', '-', '||', '/', '[', ']', '*', ',', '.', '<', '>', '<<', '>>', '[', ']', '[', ']', '[', ']', ',', '.', '[', ']', '<<', '>>', '<', '>', ',', '[', ']', '((', '))', '.', ',', '*', '/', '-', '/', '\\', '|', '||', '<<', '>>', '((', '))']; private static array $transliterators = []; private static array $tableZero; private static array $tableWide; public static function fromCodePoints(int ...$codes): static { $string = ''; foreach ($codes as $code) { if (0x80 > $code %= 0x200000) { $string .= \chr($code); } elseif (0x800 > $code) { $string .= \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); } elseif (0x10000 > $code) { $string .= \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); } else { $string .= \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); } } return new static($string); } /** * Generic UTF-8 to ASCII transliteration. * * Install the intl extension for best results. * * @param string[]|\Transliterator[]|\Closure[] $rules See "*-Latin" rules from Transliterator::listIDs() */ public function ascii(array $rules = []): self { $str = clone $this; $s = $str->string; $str->string = ''; array_unshift($rules, 'nfd'); $rules[] = 'latin-ascii'; if (\function_exists('transliterator_transliterate')) { $rules[] = 'any-latin/bgn'; } $rules[] = 'nfkd'; $rules[] = '[:nonspacing mark:] remove'; while (\strlen($s) - 1 > $i = strspn($s, self::ASCII)) { if (0 < --$i) { $str->string .= substr($s, 0, $i); $s = substr($s, $i); } if (!$rule = array_shift($rules)) { $rules = []; // An empty rule interrupts the next ones } if ($rule instanceof \Transliterator) { $s = $rule->transliterate($s); } elseif ($rule instanceof \Closure) { $s = $rule($s); } elseif ($rule) { if ('nfd' === $rule = strtolower($rule)) { normalizer_is_normalized($s, self::NFD) ?: $s = normalizer_normalize($s, self::NFD); } elseif ('nfkd' === $rule) { normalizer_is_normalized($s, self::NFKD) ?: $s = normalizer_normalize($s, self::NFKD); } elseif ('[:nonspacing mark:] remove' === $rule) { $s = preg_replace('/\p{Mn}++/u', '', $s); } elseif ('latin-ascii' === $rule) { $s = str_replace(self::TRANSLIT_FROM, self::TRANSLIT_TO, $s); } elseif ('de-ascii' === $rule) { $s = preg_replace("/([AUO])\u{0308}(?=\p{Ll})/u", '$1e', $s); $s = str_replace(["a\u{0308}", "o\u{0308}", "u\u{0308}", "A\u{0308}", "O\u{0308}", "U\u{0308}"], ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'], $s); } elseif (\function_exists('transliterator_transliterate')) { if (null === $transliterator = self::$transliterators[$rule] ??= \Transliterator::create($rule)) { if ('any-latin/bgn' === $rule) { $rule = 'any-latin'; $transliterator = self::$transliterators[$rule] ??= \Transliterator::create($rule); } if (null === $transliterator) { throw new InvalidArgumentException(sprintf('Unknown transliteration rule "%s".', $rule)); } self::$transliterators['any-latin/bgn'] = $transliterator; } $s = $transliterator->transliterate($s); } } elseif (!\function_exists('iconv')) { $s = preg_replace('/[^\x00-\x7F]/u', '?', $s); } else { $s = @preg_replace_callback('/[^\x00-\x7F]/u', static function ($c) { $c = (string) iconv('UTF-8', 'ASCII//TRANSLIT', $c[0]); if ('' === $c && '' === iconv('UTF-8', 'ASCII//TRANSLIT', '²')) { throw new \LogicException(sprintf('"%s" requires a translit-able iconv implementation, try installing "gnu-libiconv" if you\'re using Alpine Linux.', static::class)); } return 1 < \strlen($c) ? ltrim($c, '\'`"^~') : ('' !== $c ? $c : '?'); }, $s); } } $str->string .= $s; return $str; } public function camel(): static { $str = clone $this; $str->string = str_replace(' ', '', preg_replace_callback('/\b.(?![A-Z]{2,})/u', static function ($m) { static $i = 0; return 1 === ++$i ? ('İ' === $m[0] ? 'i̇' : mb_strtolower($m[0], 'UTF-8')) : mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8'); }, preg_replace('/[^\pL0-9]++/u', ' ', $this->string))); return $str; } /** * @return int[] */ public function codePointsAt(int $offset): array { $str = $this->slice($offset, 1); if ('' === $str->string) { return []; } $codePoints = []; foreach (preg_split('//u', $str->string, -1, \PREG_SPLIT_NO_EMPTY) as $c) { $codePoints[] = mb_ord($c, 'UTF-8'); } return $codePoints; } public function folded(bool $compat = true): static { $str = clone $this; if (!$compat || !\defined('Normalizer::NFKC_CF')) { $str->string = normalizer_normalize($str->string, $compat ? \Normalizer::NFKC : \Normalizer::NFC); $str->string = mb_strtolower(str_replace(self::FOLD_FROM, self::FOLD_TO, $str->string), 'UTF-8'); } else { $str->string = normalizer_normalize($str->string, \Normalizer::NFKC_CF); } return $str; } public function join(array $strings, ?string $lastGlue = null): static { $str = clone $this; $tail = null !== $lastGlue && 1 < \count($strings) ? $lastGlue.array_pop($strings) : ''; $str->string = implode($this->string, $strings).$tail; if (!preg_match('//u', $str->string)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } return $str; } public function lower(): static { $str = clone $this; $str->string = mb_strtolower(str_replace('İ', 'i̇', $str->string), 'UTF-8'); return $str; } /** * @param string $locale In the format language_region (e.g. tr_TR) */ public function localeLower(string $locale): static { if (null !== $transliterator = $this->getLocaleTransliterator($locale, 'Lower')) { $str = clone $this; $str->string = $transliterator->transliterate($str->string); return $str; } return $this->lower(); } public function match(string $regexp, int $flags = 0, int $offset = 0): array { $match = ((\PREG_PATTERN_ORDER | \PREG_SET_ORDER) & $flags) ? 'preg_match_all' : 'preg_match'; if ($this->ignoreCase) { $regexp .= 'i'; } set_error_handler(static fn ($t, $m) => throw new InvalidArgumentException($m)); try { if (false === $match($regexp.'u', $this->string, $matches, $flags | \PREG_UNMATCHED_AS_NULL, $offset)) { throw new RuntimeException('Matching failed with error: '.preg_last_error_msg()); } } finally { restore_error_handler(); } return $matches; } public function normalize(int $form = self::NFC): static { if (!\in_array($form, [self::NFC, self::NFD, self::NFKC, self::NFKD])) { throw new InvalidArgumentException('Unsupported normalization form.'); } $str = clone $this; normalizer_is_normalized($str->string, $form) ?: $str->string = normalizer_normalize($str->string, $form); return $str; } public function padBoth(int $length, string $padStr = ' '): static { if ('' === $padStr || !preg_match('//u', $padStr)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $pad = clone $this; $pad->string = $padStr; return $this->pad($length, $pad, \STR_PAD_BOTH); } public function padEnd(int $length, string $padStr = ' '): static { if ('' === $padStr || !preg_match('//u', $padStr)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $pad = clone $this; $pad->string = $padStr; return $this->pad($length, $pad, \STR_PAD_RIGHT); } public function padStart(int $length, string $padStr = ' '): static { if ('' === $padStr || !preg_match('//u', $padStr)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } $pad = clone $this; $pad->string = $padStr; return $this->pad($length, $pad, \STR_PAD_LEFT); } public function replaceMatches(string $fromRegexp, string|callable $to): static { if ($this->ignoreCase) { $fromRegexp .= 'i'; } if (\is_array($to) || $to instanceof \Closure) { $replace = 'preg_replace_callback'; $to = static function (array $m) use ($to): string { $to = $to($m); if ('' !== $to && (!\is_string($to) || !preg_match('//u', $to))) { throw new InvalidArgumentException('Replace callback must return a valid UTF-8 string.'); } return $to; }; } elseif ('' !== $to && !preg_match('//u', $to)) { throw new InvalidArgumentException('Invalid UTF-8 string.'); } else { $replace = 'preg_replace'; } set_error_handler(static fn ($t, $m) => throw new InvalidArgumentException($m)); try { if (null === $string = $replace($fromRegexp.'u', $to, $this->string)) { $lastError = preg_last_error(); foreach (get_defined_constants(true)['pcre'] as $k => $v) { if ($lastError === $v && str_ends_with($k, '_ERROR')) { throw new RuntimeException('Matching failed with '.$k.'.'); } } throw new RuntimeException('Matching failed with unknown error code.'); } } finally { restore_error_handler(); } $str = clone $this; $str->string = $string; return $str; } public function reverse(): static { $str = clone $this; $str->string = implode('', array_reverse(preg_split('/(\X)/u', $str->string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY))); return $str; } public function snake(): static { $str = $this->camel(); $str->string = mb_strtolower(preg_replace(['/(\p{Lu}+)(\p{Lu}\p{Ll})/u', '/([\p{Ll}0-9])(\p{Lu})/u'], '\1_\2', $str->string), 'UTF-8'); return $str; } public function title(bool $allWords = false): static { $str = clone $this; $limit = $allWords ? -1 : 1; $str->string = preg_replace_callback('/\b./u', static fn (array $m): string => mb_convert_case($m[0], \MB_CASE_TITLE, 'UTF-8'), $str->string, $limit); return $str; } /** * @param string $locale In the format language_region (e.g. tr_TR) */ public function localeTitle(string $locale): static { if (null !== $transliterator = $this->getLocaleTransliterator($locale, 'Title')) { $str = clone $this; $str->string = $transliterator->transliterate($str->string); return $str; } return $this->title(); } public function trim(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static { if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { throw new InvalidArgumentException('Invalid UTF-8 chars.'); } $chars = preg_quote($chars); $str = clone $this; $str->string = preg_replace("{^[$chars]++|[$chars]++$}uD", '', $str->string); return $str; } public function trimEnd(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static { if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { throw new InvalidArgumentException('Invalid UTF-8 chars.'); } $chars = preg_quote($chars); $str = clone $this; $str->string = preg_replace("{[$chars]++$}uD", '', $str->string); return $str; } public function trimPrefix($prefix): static { if (!$this->ignoreCase) { return parent::trimPrefix($prefix); } $str = clone $this; if ($prefix instanceof \Traversable) { $prefix = iterator_to_array($prefix, false); } elseif ($prefix instanceof parent) { $prefix = $prefix->string; } $prefix = implode('|', array_map('preg_quote', (array) $prefix)); $str->string = preg_replace("{^(?:$prefix)}iuD", '', $this->string); return $str; } public function trimStart(string $chars = " \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}"): static { if (" \t\n\r\0\x0B\x0C\u{A0}\u{FEFF}" !== $chars && !preg_match('//u', $chars)) { throw new InvalidArgumentException('Invalid UTF-8 chars.'); } $chars = preg_quote($chars); $str = clone $this; $str->string = preg_replace("{^[$chars]++}uD", '', $str->string); return $str; } public function trimSuffix($suffix): static { if (!$this->ignoreCase) { return parent::trimSuffix($suffix); } $str = clone $this; if ($suffix instanceof \Traversable) { $suffix = iterator_to_array($suffix, false); } elseif ($suffix instanceof parent) { $suffix = $suffix->string; } $suffix = implode('|', array_map('preg_quote', (array) $suffix)); $str->string = preg_replace("{(?:$suffix)$}iuD", '', $this->string); return $str; } public function upper(): static { $str = clone $this; $str->string = mb_strtoupper($str->string, 'UTF-8'); return $str; } /** * @param string $locale In the format language_region (e.g. tr_TR) */ public function localeUpper(string $locale): static { if (null !== $transliterator = $this->getLocaleTransliterator($locale, 'Upper')) { $str = clone $this; $str->string = $transliterator->transliterate($str->string); return $str; } return $this->upper(); } public function width(bool $ignoreAnsiDecoration = true): int { $width = 0; $s = str_replace(["\x00", "\x05", "\x07"], '', $this->string); if (str_contains($s, "\r")) { $s = str_replace(["\r\n", "\r"], "\n", $s); } if (!$ignoreAnsiDecoration) { $s = preg_replace('/[\p{Cc}\x7F]++/u', '', $s); } foreach (explode("\n", $s) as $s) { if ($ignoreAnsiDecoration) { $s = preg_replace('/(?:\x1B(?: \[ [\x30-\x3F]*+ [\x20-\x2F]*+ [\x40-\x7E] | [P\]X^_] .*? \x1B\\\\ | [\x41-\x7E] )|[\p{Cc}\x7F]++)/xu', '', $s); } $lineWidth = $this->wcswidth($s); if ($lineWidth > $width) { $width = $lineWidth; } } return $width; } private function pad(int $len, self $pad, int $type): static { $sLen = $this->length(); if ($len <= $sLen) { return clone $this; } $padLen = $pad->length(); $freeLen = $len - $sLen; $len = $freeLen % $padLen; switch ($type) { case \STR_PAD_RIGHT: return $this->append(str_repeat($pad->string, intdiv($freeLen, $padLen)).($len ? $pad->slice(0, $len) : '')); case \STR_PAD_LEFT: return $this->prepend(str_repeat($pad->string, intdiv($freeLen, $padLen)).($len ? $pad->slice(0, $len) : '')); case \STR_PAD_BOTH: $freeLen /= 2; $rightLen = ceil($freeLen); $len = $rightLen % $padLen; $str = $this->append(str_repeat($pad->string, intdiv($rightLen, $padLen)).($len ? $pad->slice(0, $len) : '')); $leftLen = floor($freeLen); $len = $leftLen % $padLen; return $str->prepend(str_repeat($pad->string, intdiv($leftLen, $padLen)).($len ? $pad->slice(0, $len) : '')); default: throw new InvalidArgumentException('Invalid padding type.'); } } /** * Based on https://github.com/jquast/wcwidth, a Python implementation of https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c. */ private function wcswidth(string $string): int { $width = 0; foreach (preg_split('//u', $string, -1, \PREG_SPLIT_NO_EMPTY) as $c) { $codePoint = mb_ord($c, 'UTF-8'); if (0 === $codePoint // NULL || 0x034F === $codePoint // COMBINING GRAPHEME JOINER || (0x200B <= $codePoint && 0x200F >= $codePoint) // ZERO WIDTH SPACE to RIGHT-TO-LEFT MARK || 0x2028 === $codePoint // LINE SEPARATOR || 0x2029 === $codePoint // PARAGRAPH SEPARATOR || (0x202A <= $codePoint && 0x202E >= $codePoint) // LEFT-TO-RIGHT EMBEDDING to RIGHT-TO-LEFT OVERRIDE || (0x2060 <= $codePoint && 0x2063 >= $codePoint) // WORD JOINER to INVISIBLE SEPARATOR ) { continue; } // Non printable characters if (32 > $codePoint // C0 control characters || (0x07F <= $codePoint && 0x0A0 > $codePoint) // C1 control characters and DEL ) { return -1; } self::$tableZero ??= require __DIR__.'/Resources/data/wcswidth_table_zero.php'; if ($codePoint >= self::$tableZero[0][0] && $codePoint <= self::$tableZero[$ubound = \count(self::$tableZero) - 1][1]) { $lbound = 0; while ($ubound >= $lbound) { $mid = floor(($lbound + $ubound) / 2); if ($codePoint > self::$tableZero[$mid][1]) { $lbound = $mid + 1; } elseif ($codePoint < self::$tableZero[$mid][0]) { $ubound = $mid - 1; } else { continue 2; } } } self::$tableWide ??= require __DIR__.'/Resources/data/wcswidth_table_wide.php'; if ($codePoint >= self::$tableWide[0][0] && $codePoint <= self::$tableWide[$ubound = \count(self::$tableWide) - 1][1]) { $lbound = 0; while ($ubound >= $lbound) { $mid = floor(($lbound + $ubound) / 2); if ($codePoint > self::$tableWide[$mid][1]) { $lbound = $mid + 1; } elseif ($codePoint < self::$tableWide[$mid][0]) { $ubound = $mid - 1; } else { $width += 2; continue 2; } } } ++$width; } return $width; } private function getLocaleTransliterator(string $locale, string $id): ?\Transliterator { $rule = $locale.'-'.$id; if (\array_key_exists($rule, self::$transliterators)) { return self::$transliterators[$rule]; } if (null !== $transliterator = self::$transliterators[$rule] = \Transliterator::create($rule)) { return $transliterator; } // Try to find a parent locale (nl_BE -> nl) if (false === $i = strpos($locale, '_')) { return null; } $parentRule = substr_replace($locale, '-'.$id, $i); // Parent locale was already cached, return and store as current locale if (\array_key_exists($parentRule, self::$transliterators)) { return self::$transliterators[$rule] = self::$transliterators[$parentRule]; } // Create transliterator based on parent locale and cache the result on both initial and parent locale values $transliterator = \Transliterator::create($parentRule); return self::$transliterators[$rule] = self::$transliterators[$parentRule] = $transliterator; } } string/Resources/functions.php 0000644 00000001524 15060133305 0012543 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\String; if (!\function_exists(u::class)) { function u(?string $string = ''): UnicodeString { return new UnicodeString($string ?? ''); } } if (!\function_exists(b::class)) { function b(?string $string = ''): ByteString { return new ByteString($string ?? ''); } } if (!\function_exists(s::class)) { /** * @return UnicodeString|ByteString */ function s(?string $string = ''): AbstractString { $string ??= ''; return preg_match('//u', $string) ? new UnicodeString($string) : new ByteString($string); } } string/Resources/data/wcswidth_table_wide.php 0000644 00000030456 15060133305 0015465 0 ustar 00 <?php /* * This file has been auto-generated by the Symfony String Component for internal use. * * Unicode version: 15.1.0 * Date: 2023-09-13T11:47:12+00:00 */ return [ [ 4352, 4447, ], [ 8986, 8987, ], [ 9001, 9001, ], [ 9002, 9002, ], [ 9193, 9196, ], [ 9200, 9200, ], [ 9203, 9203, ], [ 9725, 9726, ], [ 9748, 9749, ], [ 9800, 9811, ], [ 9855, 9855, ], [ 9875, 9875, ], [ 9889, 9889, ], [ 9898, 9899, ], [ 9917, 9918, ], [ 9924, 9925, ], [ 9934, 9934, ], [ 9940, 9940, ], [ 9962, 9962, ], [ 9970, 9971, ], [ 9973, 9973, ], [ 9978, 9978, ], [ 9981, 9981, ], [ 9989, 9989, ], [ 9994, 9995, ], [ 10024, 10024, ], [ 10060, 10060, ], [ 10062, 10062, ], [ 10067, 10069, ], [ 10071, 10071, ], [ 10133, 10135, ], [ 10160, 10160, ], [ 10175, 10175, ], [ 11035, 11036, ], [ 11088, 11088, ], [ 11093, 11093, ], [ 11904, 11929, ], [ 11931, 12019, ], [ 12032, 12245, ], [ 12272, 12287, ], [ 12288, 12288, ], [ 12289, 12291, ], [ 12292, 12292, ], [ 12293, 12293, ], [ 12294, 12294, ], [ 12295, 12295, ], [ 12296, 12296, ], [ 12297, 12297, ], [ 12298, 12298, ], [ 12299, 12299, ], [ 12300, 12300, ], [ 12301, 12301, ], [ 12302, 12302, ], [ 12303, 12303, ], [ 12304, 12304, ], [ 12305, 12305, ], [ 12306, 12307, ], [ 12308, 12308, ], [ 12309, 12309, ], [ 12310, 12310, ], [ 12311, 12311, ], [ 12312, 12312, ], [ 12313, 12313, ], [ 12314, 12314, ], [ 12315, 12315, ], [ 12316, 12316, ], [ 12317, 12317, ], [ 12318, 12319, ], [ 12320, 12320, ], [ 12321, 12329, ], [ 12330, 12333, ], [ 12334, 12335, ], [ 12336, 12336, ], [ 12337, 12341, ], [ 12342, 12343, ], [ 12344, 12346, ], [ 12347, 12347, ], [ 12348, 12348, ], [ 12349, 12349, ], [ 12350, 12350, ], [ 12353, 12438, ], [ 12441, 12442, ], [ 12443, 12444, ], [ 12445, 12446, ], [ 12447, 12447, ], [ 12448, 12448, ], [ 12449, 12538, ], [ 12539, 12539, ], [ 12540, 12542, ], [ 12543, 12543, ], [ 12549, 12591, ], [ 12593, 12686, ], [ 12688, 12689, ], [ 12690, 12693, ], [ 12694, 12703, ], [ 12704, 12735, ], [ 12736, 12771, ], [ 12783, 12783, ], [ 12784, 12799, ], [ 12800, 12830, ], [ 12832, 12841, ], [ 12842, 12871, ], [ 12880, 12880, ], [ 12881, 12895, ], [ 12896, 12927, ], [ 12928, 12937, ], [ 12938, 12976, ], [ 12977, 12991, ], [ 12992, 13055, ], [ 13056, 13311, ], [ 13312, 19903, ], [ 19968, 40959, ], [ 40960, 40980, ], [ 40981, 40981, ], [ 40982, 42124, ], [ 42128, 42182, ], [ 43360, 43388, ], [ 44032, 55203, ], [ 63744, 64109, ], [ 64110, 64111, ], [ 64112, 64217, ], [ 64218, 64255, ], [ 65040, 65046, ], [ 65047, 65047, ], [ 65048, 65048, ], [ 65049, 65049, ], [ 65072, 65072, ], [ 65073, 65074, ], [ 65075, 65076, ], [ 65077, 65077, ], [ 65078, 65078, ], [ 65079, 65079, ], [ 65080, 65080, ], [ 65081, 65081, ], [ 65082, 65082, ], [ 65083, 65083, ], [ 65084, 65084, ], [ 65085, 65085, ], [ 65086, 65086, ], [ 65087, 65087, ], [ 65088, 65088, ], [ 65089, 65089, ], [ 65090, 65090, ], [ 65091, 65091, ], [ 65092, 65092, ], [ 65093, 65094, ], [ 65095, 65095, ], [ 65096, 65096, ], [ 65097, 65100, ], [ 65101, 65103, ], [ 65104, 65106, ], [ 65108, 65111, ], [ 65112, 65112, ], [ 65113, 65113, ], [ 65114, 65114, ], [ 65115, 65115, ], [ 65116, 65116, ], [ 65117, 65117, ], [ 65118, 65118, ], [ 65119, 65121, ], [ 65122, 65122, ], [ 65123, 65123, ], [ 65124, 65126, ], [ 65128, 65128, ], [ 65129, 65129, ], [ 65130, 65131, ], [ 65281, 65283, ], [ 65284, 65284, ], [ 65285, 65287, ], [ 65288, 65288, ], [ 65289, 65289, ], [ 65290, 65290, ], [ 65291, 65291, ], [ 65292, 65292, ], [ 65293, 65293, ], [ 65294, 65295, ], [ 65296, 65305, ], [ 65306, 65307, ], [ 65308, 65310, ], [ 65311, 65312, ], [ 65313, 65338, ], [ 65339, 65339, ], [ 65340, 65340, ], [ 65341, 65341, ], [ 65342, 65342, ], [ 65343, 65343, ], [ 65344, 65344, ], [ 65345, 65370, ], [ 65371, 65371, ], [ 65372, 65372, ], [ 65373, 65373, ], [ 65374, 65374, ], [ 65375, 65375, ], [ 65376, 65376, ], [ 65504, 65505, ], [ 65506, 65506, ], [ 65507, 65507, ], [ 65508, 65508, ], [ 65509, 65510, ], [ 94176, 94177, ], [ 94178, 94178, ], [ 94179, 94179, ], [ 94180, 94180, ], [ 94192, 94193, ], [ 94208, 100343, ], [ 100352, 101119, ], [ 101120, 101589, ], [ 101632, 101640, ], [ 110576, 110579, ], [ 110581, 110587, ], [ 110589, 110590, ], [ 110592, 110847, ], [ 110848, 110882, ], [ 110898, 110898, ], [ 110928, 110930, ], [ 110933, 110933, ], [ 110948, 110951, ], [ 110960, 111355, ], [ 126980, 126980, ], [ 127183, 127183, ], [ 127374, 127374, ], [ 127377, 127386, ], [ 127488, 127490, ], [ 127504, 127547, ], [ 127552, 127560, ], [ 127568, 127569, ], [ 127584, 127589, ], [ 127744, 127776, ], [ 127789, 127797, ], [ 127799, 127868, ], [ 127870, 127891, ], [ 127904, 127946, ], [ 127951, 127955, ], [ 127968, 127984, ], [ 127988, 127988, ], [ 127992, 127994, ], [ 127995, 127999, ], [ 128000, 128062, ], [ 128064, 128064, ], [ 128066, 128252, ], [ 128255, 128317, ], [ 128331, 128334, ], [ 128336, 128359, ], [ 128378, 128378, ], [ 128405, 128406, ], [ 128420, 128420, ], [ 128507, 128511, ], [ 128512, 128591, ], [ 128640, 128709, ], [ 128716, 128716, ], [ 128720, 128722, ], [ 128725, 128727, ], [ 128732, 128735, ], [ 128747, 128748, ], [ 128756, 128764, ], [ 128992, 129003, ], [ 129008, 129008, ], [ 129292, 129338, ], [ 129340, 129349, ], [ 129351, 129535, ], [ 129648, 129660, ], [ 129664, 129672, ], [ 129680, 129725, ], [ 129727, 129733, ], [ 129742, 129755, ], [ 129760, 129768, ], [ 129776, 129784, ], [ 131072, 173791, ], [ 173792, 173823, ], [ 173824, 177977, ], [ 177978, 177983, ], [ 177984, 178205, ], [ 178206, 178207, ], [ 178208, 183969, ], [ 183970, 183983, ], [ 183984, 191456, ], [ 191457, 191471, ], [ 191472, 192093, ], [ 192094, 194559, ], [ 194560, 195101, ], [ 195102, 195103, ], [ 195104, 196605, ], [ 196608, 201546, ], [ 201547, 201551, ], [ 201552, 205743, ], [ 205744, 262141, ], ]; string/Resources/data/wcswidth_table_zero.php 0000644 00000035202 15060133305 0015506 0 ustar 00 <?php /* * This file has been auto-generated by the Symfony String Component for internal use. * * Unicode version: 15.1.0 * Date: 2023-09-13T11:47:13+00:00 */ return [ [ 768, 879, ], [ 1155, 1159, ], [ 1160, 1161, ], [ 1425, 1469, ], [ 1471, 1471, ], [ 1473, 1474, ], [ 1476, 1477, ], [ 1479, 1479, ], [ 1552, 1562, ], [ 1611, 1631, ], [ 1648, 1648, ], [ 1750, 1756, ], [ 1759, 1764, ], [ 1767, 1768, ], [ 1770, 1773, ], [ 1809, 1809, ], [ 1840, 1866, ], [ 1958, 1968, ], [ 2027, 2035, ], [ 2045, 2045, ], [ 2070, 2073, ], [ 2075, 2083, ], [ 2085, 2087, ], [ 2089, 2093, ], [ 2137, 2139, ], [ 2200, 2207, ], [ 2250, 2273, ], [ 2275, 2306, ], [ 2362, 2362, ], [ 2364, 2364, ], [ 2369, 2376, ], [ 2381, 2381, ], [ 2385, 2391, ], [ 2402, 2403, ], [ 2433, 2433, ], [ 2492, 2492, ], [ 2497, 2500, ], [ 2509, 2509, ], [ 2530, 2531, ], [ 2558, 2558, ], [ 2561, 2562, ], [ 2620, 2620, ], [ 2625, 2626, ], [ 2631, 2632, ], [ 2635, 2637, ], [ 2641, 2641, ], [ 2672, 2673, ], [ 2677, 2677, ], [ 2689, 2690, ], [ 2748, 2748, ], [ 2753, 2757, ], [ 2759, 2760, ], [ 2765, 2765, ], [ 2786, 2787, ], [ 2810, 2815, ], [ 2817, 2817, ], [ 2876, 2876, ], [ 2879, 2879, ], [ 2881, 2884, ], [ 2893, 2893, ], [ 2901, 2902, ], [ 2914, 2915, ], [ 2946, 2946, ], [ 3008, 3008, ], [ 3021, 3021, ], [ 3072, 3072, ], [ 3076, 3076, ], [ 3132, 3132, ], [ 3134, 3136, ], [ 3142, 3144, ], [ 3146, 3149, ], [ 3157, 3158, ], [ 3170, 3171, ], [ 3201, 3201, ], [ 3260, 3260, ], [ 3263, 3263, ], [ 3270, 3270, ], [ 3276, 3277, ], [ 3298, 3299, ], [ 3328, 3329, ], [ 3387, 3388, ], [ 3393, 3396, ], [ 3405, 3405, ], [ 3426, 3427, ], [ 3457, 3457, ], [ 3530, 3530, ], [ 3538, 3540, ], [ 3542, 3542, ], [ 3633, 3633, ], [ 3636, 3642, ], [ 3655, 3662, ], [ 3761, 3761, ], [ 3764, 3772, ], [ 3784, 3790, ], [ 3864, 3865, ], [ 3893, 3893, ], [ 3895, 3895, ], [ 3897, 3897, ], [ 3953, 3966, ], [ 3968, 3972, ], [ 3974, 3975, ], [ 3981, 3991, ], [ 3993, 4028, ], [ 4038, 4038, ], [ 4141, 4144, ], [ 4146, 4151, ], [ 4153, 4154, ], [ 4157, 4158, ], [ 4184, 4185, ], [ 4190, 4192, ], [ 4209, 4212, ], [ 4226, 4226, ], [ 4229, 4230, ], [ 4237, 4237, ], [ 4253, 4253, ], [ 4957, 4959, ], [ 5906, 5908, ], [ 5938, 5939, ], [ 5970, 5971, ], [ 6002, 6003, ], [ 6068, 6069, ], [ 6071, 6077, ], [ 6086, 6086, ], [ 6089, 6099, ], [ 6109, 6109, ], [ 6155, 6157, ], [ 6159, 6159, ], [ 6277, 6278, ], [ 6313, 6313, ], [ 6432, 6434, ], [ 6439, 6440, ], [ 6450, 6450, ], [ 6457, 6459, ], [ 6679, 6680, ], [ 6683, 6683, ], [ 6742, 6742, ], [ 6744, 6750, ], [ 6752, 6752, ], [ 6754, 6754, ], [ 6757, 6764, ], [ 6771, 6780, ], [ 6783, 6783, ], [ 6832, 6845, ], [ 6846, 6846, ], [ 6847, 6862, ], [ 6912, 6915, ], [ 6964, 6964, ], [ 6966, 6970, ], [ 6972, 6972, ], [ 6978, 6978, ], [ 7019, 7027, ], [ 7040, 7041, ], [ 7074, 7077, ], [ 7080, 7081, ], [ 7083, 7085, ], [ 7142, 7142, ], [ 7144, 7145, ], [ 7149, 7149, ], [ 7151, 7153, ], [ 7212, 7219, ], [ 7222, 7223, ], [ 7376, 7378, ], [ 7380, 7392, ], [ 7394, 7400, ], [ 7405, 7405, ], [ 7412, 7412, ], [ 7416, 7417, ], [ 7616, 7679, ], [ 8400, 8412, ], [ 8413, 8416, ], [ 8417, 8417, ], [ 8418, 8420, ], [ 8421, 8432, ], [ 11503, 11505, ], [ 11647, 11647, ], [ 11744, 11775, ], [ 12330, 12333, ], [ 12441, 12442, ], [ 42607, 42607, ], [ 42608, 42610, ], [ 42612, 42621, ], [ 42654, 42655, ], [ 42736, 42737, ], [ 43010, 43010, ], [ 43014, 43014, ], [ 43019, 43019, ], [ 43045, 43046, ], [ 43052, 43052, ], [ 43204, 43205, ], [ 43232, 43249, ], [ 43263, 43263, ], [ 43302, 43309, ], [ 43335, 43345, ], [ 43392, 43394, ], [ 43443, 43443, ], [ 43446, 43449, ], [ 43452, 43453, ], [ 43493, 43493, ], [ 43561, 43566, ], [ 43569, 43570, ], [ 43573, 43574, ], [ 43587, 43587, ], [ 43596, 43596, ], [ 43644, 43644, ], [ 43696, 43696, ], [ 43698, 43700, ], [ 43703, 43704, ], [ 43710, 43711, ], [ 43713, 43713, ], [ 43756, 43757, ], [ 43766, 43766, ], [ 44005, 44005, ], [ 44008, 44008, ], [ 44013, 44013, ], [ 64286, 64286, ], [ 65024, 65039, ], [ 65056, 65071, ], [ 66045, 66045, ], [ 66272, 66272, ], [ 66422, 66426, ], [ 68097, 68099, ], [ 68101, 68102, ], [ 68108, 68111, ], [ 68152, 68154, ], [ 68159, 68159, ], [ 68325, 68326, ], [ 68900, 68903, ], [ 69291, 69292, ], [ 69373, 69375, ], [ 69446, 69456, ], [ 69506, 69509, ], [ 69633, 69633, ], [ 69688, 69702, ], [ 69744, 69744, ], [ 69747, 69748, ], [ 69759, 69761, ], [ 69811, 69814, ], [ 69817, 69818, ], [ 69826, 69826, ], [ 69888, 69890, ], [ 69927, 69931, ], [ 69933, 69940, ], [ 70003, 70003, ], [ 70016, 70017, ], [ 70070, 70078, ], [ 70089, 70092, ], [ 70095, 70095, ], [ 70191, 70193, ], [ 70196, 70196, ], [ 70198, 70199, ], [ 70206, 70206, ], [ 70209, 70209, ], [ 70367, 70367, ], [ 70371, 70378, ], [ 70400, 70401, ], [ 70459, 70460, ], [ 70464, 70464, ], [ 70502, 70508, ], [ 70512, 70516, ], [ 70712, 70719, ], [ 70722, 70724, ], [ 70726, 70726, ], [ 70750, 70750, ], [ 70835, 70840, ], [ 70842, 70842, ], [ 70847, 70848, ], [ 70850, 70851, ], [ 71090, 71093, ], [ 71100, 71101, ], [ 71103, 71104, ], [ 71132, 71133, ], [ 71219, 71226, ], [ 71229, 71229, ], [ 71231, 71232, ], [ 71339, 71339, ], [ 71341, 71341, ], [ 71344, 71349, ], [ 71351, 71351, ], [ 71453, 71455, ], [ 71458, 71461, ], [ 71463, 71467, ], [ 71727, 71735, ], [ 71737, 71738, ], [ 71995, 71996, ], [ 71998, 71998, ], [ 72003, 72003, ], [ 72148, 72151, ], [ 72154, 72155, ], [ 72160, 72160, ], [ 72193, 72202, ], [ 72243, 72248, ], [ 72251, 72254, ], [ 72263, 72263, ], [ 72273, 72278, ], [ 72281, 72283, ], [ 72330, 72342, ], [ 72344, 72345, ], [ 72752, 72758, ], [ 72760, 72765, ], [ 72767, 72767, ], [ 72850, 72871, ], [ 72874, 72880, ], [ 72882, 72883, ], [ 72885, 72886, ], [ 73009, 73014, ], [ 73018, 73018, ], [ 73020, 73021, ], [ 73023, 73029, ], [ 73031, 73031, ], [ 73104, 73105, ], [ 73109, 73109, ], [ 73111, 73111, ], [ 73459, 73460, ], [ 73472, 73473, ], [ 73526, 73530, ], [ 73536, 73536, ], [ 73538, 73538, ], [ 78912, 78912, ], [ 78919, 78933, ], [ 92912, 92916, ], [ 92976, 92982, ], [ 94031, 94031, ], [ 94095, 94098, ], [ 94180, 94180, ], [ 113821, 113822, ], [ 118528, 118573, ], [ 118576, 118598, ], [ 119143, 119145, ], [ 119163, 119170, ], [ 119173, 119179, ], [ 119210, 119213, ], [ 119362, 119364, ], [ 121344, 121398, ], [ 121403, 121452, ], [ 121461, 121461, ], [ 121476, 121476, ], [ 121499, 121503, ], [ 121505, 121519, ], [ 122880, 122886, ], [ 122888, 122904, ], [ 122907, 122913, ], [ 122915, 122916, ], [ 122918, 122922, ], [ 123023, 123023, ], [ 123184, 123190, ], [ 123566, 123566, ], [ 123628, 123631, ], [ 124140, 124143, ], [ 125136, 125142, ], [ 125252, 125258, ], [ 917760, 917999, ], ]; string/README.md 0000644 00000001053 15060133305 0007324 0 ustar 00 String Component ================ The String component provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way. Resources --------- * [Documentation](https://symfony.com/doc/current/components/string.html) * [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) var-dumper/Exception/ThrowingCasterException.php 0000644 00000001212 15060133305 0016107 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Exception; /** * @author Nicolas Grekas <p@tchwork.com> */ class ThrowingCasterException extends \Exception { /** * @param \Throwable $prev The exception thrown from the caster */ public function __construct(\Throwable $prev) { parent::__construct('Unexpected '.$prev::class.' thrown from a caster: '.$prev->getMessage(), 0, $prev); } } var-dumper/Command/Descriptor/DumpDescriptorInterface.php 0000644 00000001142 15060133305 0017612 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Command\Descriptor; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\VarDumper\Cloner\Data; /** * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ interface DumpDescriptorInterface { public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void; } var-dumper/Command/Descriptor/HtmlDescriptor.php 0000644 00000007174 15060133305 0016003 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Command\Descriptor; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\HtmlDumper; /** * Describe collected data clones for html output. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> * * @final */ class HtmlDescriptor implements DumpDescriptorInterface { private HtmlDumper $dumper; private bool $initialized = false; public function __construct(HtmlDumper $dumper) { $this->dumper = $dumper; } public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void { if (!$this->initialized) { $styles = file_get_contents(__DIR__.'/../../Resources/css/htmlDescriptor.css'); $scripts = file_get_contents(__DIR__.'/../../Resources/js/htmlDescriptor.js'); $output->writeln("<style>$styles</style><script>$scripts</script>"); $this->initialized = true; } $title = '-'; if (isset($context['request'])) { $request = $context['request']; $controller = "<span class='dumped-tag'>{$this->dumper->dump($request['controller'], true, ['maxDepth' => 0])}</span>"; $title = sprintf('<code>%s</code> <a href="%s">%s</a>', $request['method'], $uri = $request['uri'], $uri); $dedupIdentifier = $request['identifier']; } elseif (isset($context['cli'])) { $title = '<code>$ </code>'.$context['cli']['command_line']; $dedupIdentifier = $context['cli']['identifier']; } else { $dedupIdentifier = uniqid('', true); } $sourceDescription = ''; if (isset($context['source'])) { $source = $context['source']; $projectDir = $source['project_dir'] ?? null; $sourceDescription = sprintf('%s on line %d', $source['name'], $source['line']); if (isset($source['file_link'])) { $sourceDescription = sprintf('<a href="%s">%s</a>', $source['file_link'], $sourceDescription); } } $isoDate = $this->extractDate($context, 'c'); $tags = array_filter([ 'controller' => $controller ?? null, 'project dir' => $projectDir ?? null, ]); $output->writeln(<<<HTML <article data-dedup-id="$dedupIdentifier"> <header> <div class="row"> <h2 class="col">$title</h2> <time class="col text-small" title="$isoDate" datetime="$isoDate"> {$this->extractDate($context)} </time> </div> {$this->renderTags($tags)} </header> <section class="body"> <p class="text-small"> $sourceDescription </p> {$this->dumper->dump($data, true)} </section> </article> HTML ); } private function extractDate(array $context, string $format = 'r'): string { return date($format, (int) $context['timestamp']); } private function renderTags(array $tags): string { if (!$tags) { return ''; } $renderedTags = ''; foreach ($tags as $key => $value) { $renderedTags .= sprintf('<li><span class="badge">%s</span>%s</li>', $key, $value); } return <<<HTML <div class="row"> <ul class="tags"> $renderedTags </ul> </div> HTML; } } var-dumper/Command/Descriptor/CliDescriptor.php 0000644 00000005144 15060133305 0015601 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Command\Descriptor; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\CliDumper; /** * Describe collected data clones for cli output. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> * * @final */ class CliDescriptor implements DumpDescriptorInterface { private CliDumper $dumper; private mixed $lastIdentifier = null; public function __construct(CliDumper $dumper) { $this->dumper = $dumper; } public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void { $io = $output instanceof SymfonyStyle ? $output : new SymfonyStyle(new ArrayInput([]), $output); $this->dumper->setColors($output->isDecorated()); $rows = [['date', date('r', (int) $context['timestamp'])]]; $lastIdentifier = $this->lastIdentifier; $this->lastIdentifier = $clientId; $section = "Received from client #$clientId"; if (isset($context['request'])) { $request = $context['request']; $this->lastIdentifier = $request['identifier']; $section = sprintf('%s %s', $request['method'], $request['uri']); if ($controller = $request['controller']) { $rows[] = ['controller', rtrim($this->dumper->dump($controller, true), "\n")]; } } elseif (isset($context['cli'])) { $this->lastIdentifier = $context['cli']['identifier']; $section = '$ '.$context['cli']['command_line']; } if ($this->lastIdentifier !== $lastIdentifier) { $io->section($section); } if (isset($context['source'])) { $source = $context['source']; $sourceInfo = sprintf('%s on line %d', $source['name'], $source['line']); if ($fileLink = $source['file_link'] ?? null) { $sourceInfo = sprintf('<href=%s>%s</>', $fileLink, $sourceInfo); } $rows[] = ['source', $sourceInfo]; $file = $source['file_relative'] ?? $source['file']; $rows[] = ['file', $file]; } $io->table([], $rows); $this->dumper->dump($data); $io->newLine(); } } var-dumper/Command/ServerDumpCommand.php 0000644 00000007426 15060133305 0014315 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Command; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor; use Symfony\Component\VarDumper\Command\Descriptor\DumpDescriptorInterface; use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Server\DumpServer; /** * Starts a dump server to collect and output dumps on a single place with multiple formats support. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> * * @final */ #[AsCommand(name: 'server:dump', description: 'Start a dump server that collects and displays dumps in a single place')] class ServerDumpCommand extends Command { private DumpServer $server; /** @var DumpDescriptorInterface[] */ private array $descriptors; public function __construct(DumpServer $server, array $descriptors = []) { $this->server = $server; $this->descriptors = $descriptors + [ 'cli' => new CliDescriptor(new CliDumper()), 'html' => new HtmlDescriptor(new HtmlDumper()), ]; parent::__construct(); } protected function configure(): void { $this ->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format (%s)', implode(', ', $this->getAvailableFormats())), 'cli') ->setHelp(<<<'EOF' <info>%command.name%</info> starts a dump server that collects and displays dumps in a single place for debugging you application: <info>php %command.full_name%</info> You can consult dumped data in HTML format in your browser by providing the <comment>--format=html</comment> option and redirecting the output to a file: <info>php %command.full_name% --format="html" > dump.html</info> EOF ) ; } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $format = $input->getOption('format'); if (!$descriptor = $this->descriptors[$format] ?? null) { throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $format)); } $errorIo = $io->getErrorStyle(); $errorIo->title('Symfony Var Dumper Server'); $this->server->start(); $errorIo->success(sprintf('Server listening on %s', $this->server->getHost())); $errorIo->comment('Quit the server with CONTROL-C.'); $this->server->listen(function (Data $data, array $context, int $clientId) use ($descriptor, $io) { $descriptor->describe($io, $data, $context, $clientId); }); return 0; } public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestOptionValuesFor('format')) { $suggestions->suggestValues($this->getAvailableFormats()); } } private function getAvailableFormats(): array { return array_keys($this->descriptors); } } var-dumper/Dumper/AbstractDumper.php 0000644 00000013726 15060133305 0013520 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\DumperInterface; /** * Abstract mechanism for dumping a Data object. * * @author Nicolas Grekas <p@tchwork.com> */ abstract class AbstractDumper implements DataDumperInterface, DumperInterface { public const DUMP_LIGHT_ARRAY = 1; public const DUMP_STRING_LENGTH = 2; public const DUMP_COMMA_SEPARATOR = 4; public const DUMP_TRAILING_COMMA = 8; /** @var callable|resource|string|null */ public static $defaultOutput = 'php://output'; protected string $line = ''; /** @var callable|null */ protected $lineDumper; /** @var resource|null */ protected $outputStream; protected string $decimalPoint = '.'; protected string $indentPad = ' '; protected int $flags; private string $charset = ''; /** * @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput * @param string|null $charset The default character encoding to use for non-UTF8 strings * @param int $flags A bit field of static::DUMP_* constants to fine tune dumps representation */ public function __construct($output = null, ?string $charset = null, int $flags = 0) { $this->flags = $flags; $this->setCharset($charset ?: \ini_get('php.output_encoding') ?: \ini_get('default_charset') ?: 'UTF-8'); $this->setOutput($output ?: static::$defaultOutput); if (!$output && \is_string(static::$defaultOutput)) { static::$defaultOutput = $this->outputStream; } } /** * Sets the output destination of the dumps. * * @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path * * @return callable|resource|string|null The previous output destination */ public function setOutput($output) { $prev = $this->outputStream ?? $this->lineDumper; if (\is_callable($output)) { $this->outputStream = null; $this->lineDumper = $output; } else { if (\is_string($output)) { $output = fopen($output, 'w'); } $this->outputStream = $output; $this->lineDumper = $this->echoLine(...); } return $prev; } /** * Sets the default character encoding to use for non-UTF8 strings. * * @return string The previous charset */ public function setCharset(string $charset): string { $prev = $this->charset; $charset = strtoupper($charset); $charset = 'UTF-8' === $charset || 'UTF8' === $charset ? 'CP1252' : $charset; $this->charset = $charset; return $prev; } /** * Sets the indentation pad string. * * @param string $pad A string that will be prepended to dumped lines, repeated by nesting level * * @return string The previous indent pad */ public function setIndentPad(string $pad): string { $prev = $this->indentPad; $this->indentPad = $pad; return $prev; } /** * Dumps a Data object. * * @param callable|resource|string|true|null $output A line dumper callable, an opened stream, an output path or true to return the dump * * @return string|null The dump as string when $output is true */ public function dump(Data $data, $output = null): ?string { if ($locale = $this->flags & (self::DUMP_COMMA_SEPARATOR | self::DUMP_TRAILING_COMMA) ? setlocale(\LC_NUMERIC, 0) : null) { setlocale(\LC_NUMERIC, 'C'); } if ($returnDump = true === $output) { $output = fopen('php://memory', 'r+'); } if ($output) { $prevOutput = $this->setOutput($output); } try { $data->dump($this); $this->dumpLine(-1); if ($returnDump) { $result = stream_get_contents($output, -1, 0); fclose($output); return $result; } } finally { if ($output) { $this->setOutput($prevOutput); } if ($locale) { setlocale(\LC_NUMERIC, $locale); } } return null; } /** * Dumps the current line. * * @param int $depth The recursive depth in the dumped structure for the line being dumped, * or -1 to signal the end-of-dump to the line dumper callable */ protected function dumpLine(int $depth): void { ($this->lineDumper)($this->line, $depth, $this->indentPad); $this->line = ''; } /** * Generic line dumper callback. */ protected function echoLine(string $line, int $depth, string $indentPad): void { if (-1 !== $depth) { fwrite($this->outputStream, str_repeat($indentPad, $depth).$line."\n"); } } /** * Converts a non-UTF-8 string to UTF-8. */ protected function utf8Encode(?string $s): ?string { if (null === $s || preg_match('//u', $s)) { return $s; } if (!\function_exists('iconv')) { throw new \RuntimeException('Unable to convert a non-UTF-8 string to UTF-8: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.'); } if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) { return $c; } if ('CP1252' !== $this->charset && false !== $c = @iconv('CP1252', 'UTF-8', $s)) { return $c; } return iconv('CP850', 'UTF-8', $s); } } var-dumper/Dumper/HtmlDumper.php 0000644 00000102462 15060133305 0012655 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Cursor; use Symfony\Component\VarDumper\Cloner\Data; /** * HtmlDumper dumps variables as HTML. * * @author Nicolas Grekas <p@tchwork.com> */ class HtmlDumper extends CliDumper { /** @var callable|resource|string|null */ public static $defaultOutput = 'php://output'; protected static $themes = [ 'dark' => [ 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', 'num' => 'font-weight:bold; color:#1299DA', 'const' => 'font-weight:bold', 'str' => 'font-weight:bold; color:#56DB3A', 'note' => 'color:#1299DA', 'ref' => 'color:#A0A0A0', 'public' => 'color:#FFFFFF', 'protected' => 'color:#FFFFFF', 'private' => 'color:#FFFFFF', 'meta' => 'color:#B729D9', 'key' => 'color:#56DB3A', 'index' => 'color:#1299DA', 'ellipsis' => 'color:#FF8400', 'ns' => 'user-select:none;', ], 'light' => [ 'default' => 'background:none; color:#CC7832; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', 'num' => 'font-weight:bold; color:#1299DA', 'const' => 'font-weight:bold', 'str' => 'font-weight:bold; color:#629755;', 'note' => 'color:#6897BB', 'ref' => 'color:#6E6E6E', 'public' => 'color:#262626', 'protected' => 'color:#262626', 'private' => 'color:#262626', 'meta' => 'color:#B729D9', 'key' => 'color:#789339', 'index' => 'color:#1299DA', 'ellipsis' => 'color:#CC7832', 'ns' => 'user-select:none;', ], ]; protected ?string $dumpHeader = null; protected string $dumpPrefix = '<pre class=sf-dump id=%s data-indent-pad="%s">'; protected string $dumpSuffix = '</pre><script>Sfdump(%s)</script>'; protected string $dumpId; protected bool $colors = true; protected $headerIsDumped = false; protected int $lastDepth = -1; private array $displayOptions = [ 'maxDepth' => 1, 'maxStringLength' => 160, 'fileLinkFormat' => null, ]; private array $extraDisplayOptions = []; public function __construct($output = null, ?string $charset = null, int $flags = 0) { AbstractDumper::__construct($output, $charset, $flags); $this->dumpId = 'sf-dump-'.mt_rand(); $this->displayOptions['fileLinkFormat'] = \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); $this->styles = static::$themes['dark'] ?? self::$themes['dark']; } public function setStyles(array $styles): void { $this->headerIsDumped = false; $this->styles = $styles + $this->styles; } public function setTheme(string $themeName): void { if (!isset(static::$themes[$themeName])) { throw new \InvalidArgumentException(sprintf('Theme "%s" does not exist in class "%s".', $themeName, static::class)); } $this->setStyles(static::$themes[$themeName]); } /** * Configures display options. * * @param array $displayOptions A map of display options to customize the behavior */ public function setDisplayOptions(array $displayOptions): void { $this->headerIsDumped = false; $this->displayOptions = $displayOptions + $this->displayOptions; } /** * Sets an HTML header that will be dumped once in the output stream. */ public function setDumpHeader(?string $header): void { $this->dumpHeader = $header; } /** * Sets an HTML prefix and suffix that will encapse every single dump. */ public function setDumpBoundaries(string $prefix, string $suffix): void { $this->dumpPrefix = $prefix; $this->dumpSuffix = $suffix; } public function dump(Data $data, $output = null, array $extraDisplayOptions = []): ?string { $this->extraDisplayOptions = $extraDisplayOptions; $result = parent::dump($data, $output); $this->dumpId = 'sf-dump-'.mt_rand(); return $result; } /** * Dumps the HTML header. */ protected function getDumpHeader(): string { $this->headerIsDumped = $this->outputStream ?? $this->lineDumper; if (null !== $this->dumpHeader) { return $this->dumpHeader; } $line = str_replace('{$options}', json_encode($this->displayOptions, \JSON_FORCE_OBJECT), <<<'EOHTML' <script> Sfdump = window.Sfdump || (function (doc) { doc.documentElement.classList.add('sf-js-enabled'); var rxEsc = /([.*+?^${}()|\[\]\/\\])/g, idRx = /\bsf-dump-\d+-ref[012]\w+\b/, keyHint = 0 <= navigator.platform.toUpperCase().indexOf('MAC') ? 'Cmd' : 'Ctrl', addEventListener = function (e, n, cb) { e.addEventListener(n, cb, false); }; if (!doc.addEventListener) { addEventListener = function (element, eventName, callback) { element.attachEvent('on' + eventName, function (e) { e.preventDefault = function () {e.returnValue = false;}; e.target = e.srcElement; callback(e); }); }; } function toggle(a, recursive) { var s = a.nextSibling || {}, oldClass = s.className, arrow, newClass; if (/\bsf-dump-compact\b/.test(oldClass)) { arrow = '▼'; newClass = 'sf-dump-expanded'; } else if (/\bsf-dump-expanded\b/.test(oldClass)) { arrow = '▶'; newClass = 'sf-dump-compact'; } else { return false; } if (doc.createEvent && s.dispatchEvent) { var event = doc.createEvent('Event'); event.initEvent('sf-dump-expanded' === newClass ? 'sfbeforedumpexpand' : 'sfbeforedumpcollapse', true, false); s.dispatchEvent(event); } a.lastChild.innerHTML = arrow; s.className = s.className.replace(/\bsf-dump-(compact|expanded)\b/, newClass); if (recursive) { try { a = s.querySelectorAll('.'+oldClass); for (s = 0; s < a.length; ++s) { if (-1 == a[s].className.indexOf(newClass)) { a[s].className = newClass; a[s].previousSibling.lastChild.innerHTML = arrow; } } } catch (e) { } } return true; }; function collapse(a, recursive) { var s = a.nextSibling || {}, oldClass = s.className; if (/\bsf-dump-expanded\b/.test(oldClass)) { toggle(a, recursive); return true; } return false; }; function expand(a, recursive) { var s = a.nextSibling || {}, oldClass = s.className; if (/\bsf-dump-compact\b/.test(oldClass)) { toggle(a, recursive); return true; } return false; }; function collapseAll(root) { var a = root.querySelector('a.sf-dump-toggle'); if (a) { collapse(a, true); expand(a); return true; } return false; } function reveal(node) { var previous, parents = []; while ((node = node.parentNode || {}) && (previous = node.previousSibling) && 'A' === previous.tagName) { parents.push(previous); } if (0 !== parents.length) { parents.forEach(function (parent) { expand(parent); }); return true; } return false; } function highlight(root, activeNode, nodes) { resetHighlightedNodes(root); Array.from(nodes||[]).forEach(function (node) { if (!/\bsf-dump-highlight\b/.test(node.className)) { node.className = node.className + ' sf-dump-highlight'; } }); if (!/\bsf-dump-highlight-active\b/.test(activeNode.className)) { activeNode.className = activeNode.className + ' sf-dump-highlight-active'; } } function resetHighlightedNodes(root) { Array.from(root.querySelectorAll('.sf-dump-str, .sf-dump-key, .sf-dump-public, .sf-dump-protected, .sf-dump-private')).forEach(function (strNode) { strNode.className = strNode.className.replace(/\bsf-dump-highlight\b/, ''); strNode.className = strNode.className.replace(/\bsf-dump-highlight-active\b/, ''); }); } return function (root, x) { root = doc.getElementById(root); var indentRx = new RegExp('^('+(root.getAttribute('data-indent-pad') || ' ').replace(rxEsc, '\\$1')+')+', 'm'), options = {$options}, elt = root.getElementsByTagName('A'), len = elt.length, i = 0, s, h, t = []; while (i < len) t.push(elt[i++]); for (i in x) { options[i] = x[i]; } function a(e, f) { addEventListener(root, e, function (e, n) { if ('A' == e.target.tagName) { f(e.target, e); } else if ('A' == e.target.parentNode.tagName) { f(e.target.parentNode, e); } else { n = /\bsf-dump-ellipsis\b/.test(e.target.className) ? e.target.parentNode : e.target; if ((n = n.nextElementSibling) && 'A' == n.tagName) { if (!/\bsf-dump-toggle\b/.test(n.className)) { n = n.nextElementSibling || n; } f(n, e, true); } } }); }; function isCtrlKey(e) { return e.ctrlKey || e.metaKey; } function xpathString(str) { var parts = str.match(/[^'"]+|['"]/g).map(function (part) { if ("'" == part) { return '"\'"'; } if ('"' == part) { return "'\"'"; } return "'" + part + "'"; }); return "concat(" + parts.join(",") + ", '')"; } function xpathHasClass(className) { return "contains(concat(' ', normalize-space(@class), ' '), ' " + className +" ')"; } a('mouseover', function (a, e, c) { if (c) { e.target.style.cursor = "pointer"; } }); a('click', function (a, e, c) { if (/\bsf-dump-toggle\b/.test(a.className)) { e.preventDefault(); if (!toggle(a, isCtrlKey(e))) { var r = doc.getElementById(a.getAttribute('href').slice(1)), s = r.previousSibling, f = r.parentNode, t = a.parentNode; t.replaceChild(r, a); f.replaceChild(a, s); t.insertBefore(s, r); f = f.firstChild.nodeValue.match(indentRx); t = t.firstChild.nodeValue.match(indentRx); if (f && t && f[0] !== t[0]) { r.innerHTML = r.innerHTML.replace(new RegExp('^'+f[0].replace(rxEsc, '\\$1'), 'mg'), t[0]); } if (/\bsf-dump-compact\b/.test(r.className)) { toggle(s, isCtrlKey(e)); } } if (c) { } else if (doc.getSelection) { try { doc.getSelection().removeAllRanges(); } catch (e) { doc.getSelection().empty(); } } else { doc.selection.empty(); } } else if (/\bsf-dump-str-toggle\b/.test(a.className)) { e.preventDefault(); e = a.parentNode.parentNode; e.className = e.className.replace(/\bsf-dump-str-(expand|collapse)\b/, a.parentNode.className); } }); elt = root.getElementsByTagName('SAMP'); len = elt.length; i = 0; while (i < len) t.push(elt[i++]); len = t.length; for (i = 0; i < len; ++i) { elt = t[i]; if ('SAMP' == elt.tagName) { a = elt.previousSibling || {}; if ('A' != a.tagName) { a = doc.createElement('A'); a.className = 'sf-dump-ref'; elt.parentNode.insertBefore(a, elt); } else { a.innerHTML += ' '; } a.title = (a.title ? a.title+'\n[' : '[')+keyHint+'+click] Expand all children'; a.innerHTML += elt.className == 'sf-dump-compact' ? '<span>▶</span>' : '<span>▼</span>'; a.className += ' sf-dump-toggle'; x = 1; if ('sf-dump' != elt.parentNode.className) { x += elt.parentNode.getAttribute('data-depth')/1; } } else if (/\bsf-dump-ref\b/.test(elt.className) && (a = elt.getAttribute('href'))) { a = a.slice(1); elt.className += ' sf-dump-hover'; elt.className += ' '+a; if (/[\[{]$/.test(elt.previousSibling.nodeValue)) { a = a != elt.nextSibling.id && doc.getElementById(a); try { s = a.nextSibling; elt.appendChild(a); s.parentNode.insertBefore(a, s); if (/^[@#]/.test(elt.innerHTML)) { elt.innerHTML += ' <span>▶</span>'; } else { elt.innerHTML = '<span>▶</span>'; elt.className = 'sf-dump-ref'; } elt.className += ' sf-dump-toggle'; } catch (e) { if ('&' == elt.innerHTML.charAt(0)) { elt.innerHTML = '…'; elt.className = 'sf-dump-ref'; } } } } } if (doc.evaluate && Array.from && root.children.length > 1) { root.setAttribute('tabindex', 0); SearchState = function () { this.nodes = []; this.idx = 0; }; SearchState.prototype = { next: function () { if (this.isEmpty()) { return this.current(); } this.idx = this.idx < (this.nodes.length - 1) ? this.idx + 1 : 0; return this.current(); }, previous: function () { if (this.isEmpty()) { return this.current(); } this.idx = this.idx > 0 ? this.idx - 1 : (this.nodes.length - 1); return this.current(); }, isEmpty: function () { return 0 === this.count(); }, current: function () { if (this.isEmpty()) { return null; } return this.nodes[this.idx]; }, reset: function () { this.nodes = []; this.idx = 0; }, count: function () { return this.nodes.length; }, }; function showCurrent(state) { var currentNode = state.current(), currentRect, searchRect; if (currentNode) { reveal(currentNode); highlight(root, currentNode, state.nodes); if ('scrollIntoView' in currentNode) { currentNode.scrollIntoView(true); currentRect = currentNode.getBoundingClientRect(); searchRect = search.getBoundingClientRect(); if (currentRect.top < (searchRect.top + searchRect.height)) { window.scrollBy(0, -(searchRect.top + searchRect.height + 5)); } } } counter.textContent = (state.isEmpty() ? 0 : state.idx + 1) + ' of ' + state.count(); } var search = doc.createElement('div'); search.className = 'sf-dump-search-wrapper sf-dump-search-hidden'; search.innerHTML = ' <input type="text" class="sf-dump-search-input"> <span class="sf-dump-search-count">0 of 0<\/span> <button type="button" class="sf-dump-search-input-previous" tabindex="-1"> <svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 1331l-166 165q-19 19-45 19t-45-19L896 965l-531 531q-19 19-45 19t-45-19l-166-165q-19-19-19-45.5t19-45.5l742-741q19-19 45-19t45 19l742 741q19 19 19 45.5t-19 45.5z"\/><\/svg> <\/button> <button type="button" class="sf-dump-search-input-next" tabindex="-1"> <svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 808l-742 741q-19 19-45 19t-45-19L109 808q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"\/><\/svg> <\/button> '; root.insertBefore(search, root.firstChild); var state = new SearchState(); var searchInput = search.querySelector('.sf-dump-search-input'); var counter = search.querySelector('.sf-dump-search-count'); var searchInputTimer = 0; var previousSearchQuery = ''; addEventListener(searchInput, 'keyup', function (e) { var searchQuery = e.target.value; /* Don't perform anything if the pressed key didn't change the query */ if (searchQuery === previousSearchQuery) { return; } previousSearchQuery = searchQuery; clearTimeout(searchInputTimer); searchInputTimer = setTimeout(function () { state.reset(); collapseAll(root); resetHighlightedNodes(root); if ('' === searchQuery) { counter.textContent = '0 of 0'; return; } var classMatches = [ "sf-dump-str", "sf-dump-key", "sf-dump-public", "sf-dump-protected", "sf-dump-private", ].map(xpathHasClass).join(' or '); var xpathResult = doc.evaluate('.//span[' + classMatches + '][contains(translate(child::text(), ' + xpathString(searchQuery.toUpperCase()) + ', ' + xpathString(searchQuery.toLowerCase()) + '), ' + xpathString(searchQuery.toLowerCase()) + ')]', root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); while (node = xpathResult.iterateNext()) state.nodes.push(node); showCurrent(state); }, 400); }); Array.from(search.querySelectorAll('.sf-dump-search-input-next, .sf-dump-search-input-previous')).forEach(function (btn) { addEventListener(btn, 'click', function (e) { e.preventDefault(); -1 !== e.target.className.indexOf('next') ? state.next() : state.previous(); searchInput.focus(); collapseAll(root); showCurrent(state); }) }); addEventListener(root, 'keydown', function (e) { var isSearchActive = !/\bsf-dump-search-hidden\b/.test(search.className); if ((114 === e.keyCode && !isSearchActive) || (isCtrlKey(e) && 70 === e.keyCode)) { /* F3 or CMD/CTRL + F */ if (70 === e.keyCode && document.activeElement === searchInput) { /* * If CMD/CTRL + F is hit while having focus on search input, * the user probably meant to trigger browser search instead. * Let the browser execute its behavior: */ return; } e.preventDefault(); search.className = search.className.replace(/\bsf-dump-search-hidden\b/, ''); searchInput.focus(); } else if (isSearchActive) { if (27 === e.keyCode) { /* ESC key */ search.className += ' sf-dump-search-hidden'; e.preventDefault(); resetHighlightedNodes(root); searchInput.value = ''; } else if ( (isCtrlKey(e) && 71 === e.keyCode) /* CMD/CTRL + G */ || 13 === e.keyCode /* Enter */ || 114 === e.keyCode /* F3 */ ) { e.preventDefault(); e.shiftKey ? state.previous() : state.next(); collapseAll(root); showCurrent(state); } } }); } if (0 >= options.maxStringLength) { return; } try { elt = root.querySelectorAll('.sf-dump-str'); len = elt.length; i = 0; t = []; while (i < len) t.push(elt[i++]); len = t.length; for (i = 0; i < len; ++i) { elt = t[i]; s = elt.innerText || elt.textContent; x = s.length - options.maxStringLength; if (0 < x) { h = elt.innerHTML; elt[elt.innerText ? 'innerText' : 'textContent'] = s.substring(0, options.maxStringLength); elt.className += ' sf-dump-str-collapse'; elt.innerHTML = '<span class=sf-dump-str-collapse>'+h+'<a class="sf-dump-ref sf-dump-str-toggle" title="Collapse"> ◀</a></span>'+ '<span class=sf-dump-str-expand>'+elt.innerHTML+'<a class="sf-dump-ref sf-dump-str-toggle" title="'+x+' remaining characters"> ▶</a></span>'; } } } catch (e) { } }; })(document); </script><style> .sf-js-enabled pre.sf-dump .sf-dump-compact, .sf-js-enabled .sf-dump-str-collapse .sf-dump-str-collapse, .sf-js-enabled .sf-dump-str-expand .sf-dump-str-expand { display: none; } .sf-dump-hover:hover { background-color: #B729D9; color: #FFF !important; border-radius: 2px; } pre.sf-dump { display: block; white-space: pre; padding: 5px; overflow: initial !important; } pre.sf-dump:after { content: ""; visibility: hidden; display: block; height: 0; clear: both; } pre.sf-dump span { display: inline-flex; } pre.sf-dump a { text-decoration: none; cursor: pointer; border: 0; outline: none; color: inherit; } pre.sf-dump img { max-width: 50em; max-height: 50em; margin: .5em 0 0 0; padding: 0; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAHUlEQVQY02O8zAABilCaiQEN0EeA8QuUcX9g3QEAAjcC5piyhyEAAAAASUVORK5CYII=) #D3D3D3; } pre.sf-dump .sf-dump-ellipsis { display: inline-block; overflow: visible; text-overflow: ellipsis; max-width: 5em; white-space: nowrap; overflow: hidden; vertical-align: top; } pre.sf-dump .sf-dump-ellipsis+.sf-dump-ellipsis { max-width: none; } pre.sf-dump code { display:inline; padding:0; background:none; } .sf-dump-public.sf-dump-highlight, .sf-dump-protected.sf-dump-highlight, .sf-dump-private.sf-dump-highlight, .sf-dump-str.sf-dump-highlight, .sf-dump-key.sf-dump-highlight { background: rgba(111, 172, 204, 0.3); border: 1px solid #7DA0B1; border-radius: 3px; } .sf-dump-public.sf-dump-highlight-active, .sf-dump-protected.sf-dump-highlight-active, .sf-dump-private.sf-dump-highlight-active, .sf-dump-str.sf-dump-highlight-active, .sf-dump-key.sf-dump-highlight-active { background: rgba(253, 175, 0, 0.4); border: 1px solid #ffa500; border-radius: 3px; } pre.sf-dump .sf-dump-search-hidden { display: none !important; } pre.sf-dump .sf-dump-search-wrapper { font-size: 0; white-space: nowrap; margin-bottom: 5px; display: flex; position: -webkit-sticky; position: sticky; top: 5px; } pre.sf-dump .sf-dump-search-wrapper > * { vertical-align: top; box-sizing: border-box; height: 21px; font-weight: normal; border-radius: 0; background: #FFF; color: #757575; border: 1px solid #BBB; } pre.sf-dump .sf-dump-search-wrapper > input.sf-dump-search-input { padding: 3px; height: 21px; font-size: 12px; border-right: none; border-top-left-radius: 3px; border-bottom-left-radius: 3px; color: #000; min-width: 15px; width: 100%; } pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next, pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous { background: #F2F2F2; outline: none; border-left: none; font-size: 0; line-height: 0; } pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next { border-top-right-radius: 3px; border-bottom-right-radius: 3px; } pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next > svg, pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous > svg { pointer-events: none; width: 12px; height: 12px; } pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-count { display: inline-block; padding: 0 5px; margin: 0; border-left: none; line-height: 21px; font-size: 12px; } EOHTML ); foreach ($this->styles as $class => $style) { $line .= 'pre.sf-dump'.('default' === $class ? ', pre.sf-dump' : '').' .sf-dump-'.$class.'{'.$style.'}'; } $line .= 'pre.sf-dump .sf-dump-ellipsis-note{'.$this->styles['note'].'}'; return $this->dumpHeader = preg_replace('/\s+/', ' ', $line).'</style>'.$this->dumpHeader; } public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut): void { if ('' === $str && isset($cursor->attr['img-data'], $cursor->attr['content-type'])) { $this->dumpKey($cursor); $this->line .= $this->style('default', $cursor->attr['img-size'] ?? '', []); $this->line .= $cursor->depth >= $this->displayOptions['maxDepth'] ? ' <samp class=sf-dump-compact>' : ' <samp class=sf-dump-expanded>'; $this->endValue($cursor); $this->line .= $this->indentPad; $this->line .= sprintf('<img src="data:%s;base64,%s" /></samp>', $cursor->attr['content-type'], base64_encode($cursor->attr['img-data'])); $this->endValue($cursor); } else { parent::dumpString($cursor, $str, $bin, $cut); } } public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild): void { if (Cursor::HASH_OBJECT === $type) { $cursor->attr['depth'] = $cursor->depth; } parent::enterHash($cursor, $type, $class, false); if ($cursor->skipChildren || $cursor->depth >= $this->displayOptions['maxDepth']) { $cursor->skipChildren = false; $eol = ' class=sf-dump-compact>'; } else { $this->expandNextHash = false; $eol = ' class=sf-dump-expanded>'; } if ($hasChild) { $this->line .= '<samp data-depth='.($cursor->depth + 1); if ($cursor->refIndex) { $r = Cursor::HASH_OBJECT !== $type ? 1 - (Cursor::HASH_RESOURCE !== $type) : 2; $r .= $r && 0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->refIndex; $this->line .= sprintf(' id=%s-ref%s', $this->dumpId, $r); } $this->line .= $eol; $this->dumpLine($cursor->depth); } } public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut): void { $this->dumpEllipsis($cursor, $hasChild, $cut); if ($hasChild) { $this->line .= '</samp>'; } parent::leaveHash($cursor, $type, $class, $hasChild, 0); } protected function style(string $style, string $value, array $attr = []): string { if ('' === $value && ('label' !== $style || !isset($attr['file']) && !isset($attr['href']))) { return ''; } $v = esc($value); if ('ref' === $style) { if (empty($attr['count'])) { return sprintf('<a class=sf-dump-ref>%s</a>', $v); } $r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1); return sprintf('<a class=sf-dump-ref href=#%s-ref%s title="%d occurrences">%s</a>', $this->dumpId, $r, 1 + $attr['count'], $v); } if ('const' === $style && isset($attr['value'])) { $style .= sprintf(' title="%s"', esc(\is_scalar($attr['value']) ? $attr['value'] : json_encode($attr['value']))); } elseif ('public' === $style) { $style .= sprintf(' title="%s"', empty($attr['dynamic']) ? 'Public property' : 'Runtime added dynamic property'); } elseif ('str' === $style && 1 < $attr['length']) { $style .= sprintf(' title="%d%s characters"', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : ''); } elseif ('note' === $style && 0 < ($attr['depth'] ?? 0) && false !== $c = strrpos($value, '\\')) { $style .= ' title=""'; $attr += [ 'ellipsis' => \strlen($value) - $c, 'ellipsis-type' => 'note', 'ellipsis-tail' => 1, ]; } elseif ('protected' === $style) { $style .= ' title="Protected property"'; } elseif ('meta' === $style && isset($attr['title'])) { $style .= sprintf(' title="%s"', esc($this->utf8Encode($attr['title']))); } elseif ('private' === $style) { $style .= sprintf(' title="Private property defined in class: `%s`"', esc($this->utf8Encode($attr['class']))); } if (isset($attr['ellipsis'])) { $class = 'sf-dump-ellipsis'; if (isset($attr['ellipsis-type'])) { $class = sprintf('"%s sf-dump-ellipsis-%s"', $class, $attr['ellipsis-type']); } $label = esc(substr($value, -$attr['ellipsis'])); $style = str_replace(' title="', " title=\"$v\n", $style); $v = sprintf('<span class=%s>%s</span>', $class, substr($v, 0, -\strlen($label))); if (!empty($attr['ellipsis-tail'])) { $tail = \strlen(esc(substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']))); $v .= sprintf('<span class=%s>%s</span>%s', $class, substr($label, 0, $tail), substr($label, $tail)); } else { $v .= $label; } } $map = static::$controlCharsMap; $v = "<span class=sf-dump-{$style}>".preg_replace_callback(static::$controlCharsRx, function ($c) use ($map) { $s = $b = '<span class="sf-dump-default'; $c = $c[$i = 0]; if ($ns = "\r" === $c[$i] || "\n" === $c[$i]) { $s .= ' sf-dump-ns'; } $s .= '">'; do { if (("\r" === $c[$i] || "\n" === $c[$i]) !== $ns) { $s .= '</span>'.$b; if ($ns = !$ns) { $s .= ' sf-dump-ns'; } $s .= '">'; } $s .= $map[$c[$i]] ?? sprintf('\x%02X', \ord($c[$i])); } while (isset($c[++$i])); return $s.'</span>'; }, $v).'</span>'; if (!($attr['binary'] ?? false)) { $v = preg_replace_callback(static::$unicodeCharsRx, function ($c) { return '<span class=sf-dump-default>\u{'.strtoupper(dechex(mb_ord($c[0]))).'}</span>'; }, $v); } if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) { $attr['href'] = $href; } if (isset($attr['href'])) { if ('label' === $style) { $v .= '^'; } $target = isset($attr['file']) ? '' : ' target="_blank"'; $v = sprintf('<a href="%s"%s rel="noopener noreferrer">%s</a>', esc($this->utf8Encode($attr['href'])), $target, $v); } if (isset($attr['lang'])) { $v = sprintf('<code class="%s">%s</code>', esc($attr['lang']), $v); } if ('label' === $style) { $v .= ' '; } return $v; } protected function dumpLine(int $depth, bool $endOfValue = false): void { if (-1 === $this->lastDepth) { $this->line = sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line; } if ($this->headerIsDumped !== ($this->outputStream ?? $this->lineDumper)) { $this->line = $this->getDumpHeader().$this->line; } if (-1 === $depth) { $args = ['"'.$this->dumpId.'"']; if ($this->extraDisplayOptions) { $args[] = json_encode($this->extraDisplayOptions, \JSON_FORCE_OBJECT); } // Replace is for BC $this->line .= sprintf(str_replace('"%s"', '%s', $this->dumpSuffix), implode(', ', $args)); } $this->lastDepth = $depth; $this->line = mb_encode_numericentity($this->line, [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8'); if (-1 === $depth) { AbstractDumper::dumpLine(0); } AbstractDumper::dumpLine($depth); } private function getSourceLink(string $file, int $line): string|false { $options = $this->extraDisplayOptions + $this->displayOptions; if ($fmt = $options['fileLinkFormat']) { return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line); } return false; } } function esc(string $str): string { return htmlspecialchars($str, \ENT_QUOTES, 'UTF-8'); } var-dumper/Dumper/CliDumper.php 0000644 00000055432 15060133305 0012464 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\VarDumper\Cloner\Cursor; use Symfony\Component\VarDumper\Cloner\Stub; /** * CliDumper dumps variables for command line output. * * @author Nicolas Grekas <p@tchwork.com> */ class CliDumper extends AbstractDumper { public static bool $defaultColors; /** @var callable|resource|string|null */ public static $defaultOutput = 'php://stdout'; protected bool $colors; protected int $maxStringWidth = 0; protected array $styles = [ // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics 'default' => '0;38;5;208', 'num' => '1;38;5;38', 'const' => '1;38;5;208', 'str' => '1;38;5;113', 'note' => '38;5;38', 'ref' => '38;5;247', 'public' => '', 'protected' => '', 'private' => '', 'meta' => '38;5;170', 'key' => '38;5;113', 'index' => '38;5;38', ]; protected static string $controlCharsRx = '/[\x00-\x1F\x7F]+/'; protected static array $controlCharsMap = [ "\t" => '\t', "\n" => '\n', "\v" => '\v', "\f" => '\f', "\r" => '\r', "\033" => '\e', ]; protected static string $unicodeCharsRx = "/[\u{00A0}\u{00AD}\u{034F}\u{061C}\u{115F}\u{1160}\u{17B4}\u{17B5}\u{180E}\u{2000}-\u{200F}\u{202F}\u{205F}\u{2060}-\u{2064}\u{206A}-\u{206F}\u{3000}\u{2800}\u{3164}\u{FEFF}\u{FFA0}\u{1D159}\u{1D173}-\u{1D17A}]/u"; protected bool $collapseNextHash = false; protected bool $expandNextHash = false; private array $displayOptions = [ 'fileLinkFormat' => null, ]; private bool $handlesHrefGracefully; public function __construct($output = null, ?string $charset = null, int $flags = 0) { parent::__construct($output, $charset, $flags); if ('\\' === \DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) { // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI $this->setStyles([ 'default' => '31', 'num' => '1;34', 'const' => '1;31', 'str' => '1;32', 'note' => '34', 'ref' => '1;30', 'meta' => '35', 'key' => '32', 'index' => '34', ]); } $this->displayOptions['fileLinkFormat'] = class_exists(FileLinkFormatter::class) ? new FileLinkFormatter() : (\ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l'); } /** * Enables/disables colored output. */ public function setColors(bool $colors): void { $this->colors = $colors; } /** * Sets the maximum number of characters per line for dumped strings. */ public function setMaxStringWidth(int $maxStringWidth): void { $this->maxStringWidth = $maxStringWidth; } /** * Configures styles. * * @param array $styles A map of style names to style definitions */ public function setStyles(array $styles): void { $this->styles = $styles + $this->styles; } /** * Configures display options. * * @param array $displayOptions A map of display options to customize the behavior */ public function setDisplayOptions(array $displayOptions): void { $this->displayOptions = $displayOptions + $this->displayOptions; } public function dumpScalar(Cursor $cursor, string $type, string|int|float|bool|null $value): void { $this->dumpKey($cursor); $this->collapseNextHash = $this->expandNextHash = false; $style = 'const'; $attr = $cursor->attr; switch ($type) { case 'default': $style = 'default'; break; case 'label': $this->styles += ['label' => $this->styles['default']]; $style = 'label'; break; case 'integer': $style = 'num'; if (isset($this->styles['integer'])) { $style = 'integer'; } break; case 'double': $style = 'num'; if (isset($this->styles['float'])) { $style = 'float'; } $value = match (true) { \INF === $value => 'INF', -\INF === $value => '-INF', is_nan($value) => 'NAN', default => !str_contains($value = (string) $value, $this->decimalPoint) ? $value .= $this->decimalPoint.'0' : $value, }; break; case 'NULL': $value = 'null'; break; case 'boolean': $value = $value ? 'true' : 'false'; break; default: $attr += ['value' => $this->utf8Encode($value)]; $value = $this->utf8Encode($type); break; } $this->line .= $this->style($style, $value, $attr); $this->endValue($cursor); } public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut): void { $this->dumpKey($cursor); $this->collapseNextHash = $this->expandNextHash = false; $attr = $cursor->attr; if ($bin) { $str = $this->utf8Encode($str); } if ('' === $str) { $this->line .= '""'; if ($cut) { $this->line .= '…'.$cut; } $this->endValue($cursor); } else { $attr += [ 'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0, 'binary' => $bin, ]; $str = $bin && str_contains($str, "\0") ? [$str] : explode("\n", $str); if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) { unset($str[1]); $str[0] .= "\n"; } $m = \count($str) - 1; $i = $lineCut = 0; if (self::DUMP_STRING_LENGTH & $this->flags) { $this->line .= '('.$attr['length'].') '; } if ($bin) { $this->line .= 'b'; } if ($m) { $this->line .= '"""'; $this->dumpLine($cursor->depth); } else { $this->line .= '"'; } foreach ($str as $str) { if ($i < $m) { $str .= "\n"; } if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = mb_strlen($str, 'UTF-8')) { $str = mb_substr($str, 0, $this->maxStringWidth, 'UTF-8'); $lineCut = $len - $this->maxStringWidth; } if ($m && 0 < $cursor->depth) { $this->line .= $this->indentPad; } if ('' !== $str) { $this->line .= $this->style('str', $str, $attr); } if ($i++ == $m) { if ($m) { if ('' !== $str) { $this->dumpLine($cursor->depth); if (0 < $cursor->depth) { $this->line .= $this->indentPad; } } $this->line .= '"""'; } else { $this->line .= '"'; } if ($cut < 0) { $this->line .= '…'; $lineCut = 0; } elseif ($cut) { $lineCut += $cut; } } if ($lineCut) { $this->line .= '…'.$lineCut; $lineCut = 0; } if ($i > $m) { $this->endValue($cursor); } else { $this->dumpLine($cursor->depth); } } } } public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild): void { $this->colors ??= $this->supportsColors(); $this->dumpKey($cursor); $this->expandNextHash = false; $attr = $cursor->attr; if ($this->collapseNextHash) { $cursor->skipChildren = true; $this->collapseNextHash = $hasChild = false; } $class = $this->utf8Encode($class); if (Cursor::HASH_OBJECT === $type) { $prefix = $class && 'stdClass' !== $class ? $this->style('note', $class, $attr).(empty($attr['cut_hash']) ? ' {' : '') : '{'; } elseif (Cursor::HASH_RESOURCE === $type) { $prefix = $this->style('note', $class.' resource', $attr).($hasChild ? ' {' : ' '); } else { $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class).' [' : '['; } if (($cursor->softRefCount || 0 < $cursor->softRefHandle) && empty($attr['cut_hash'])) { $prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), ['count' => $cursor->softRefCount]); } elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) { $prefix .= $this->style('ref', '&'.$cursor->hardRefTo, ['count' => $cursor->hardRefCount]); } elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) { $prefix = substr($prefix, 0, -1); } $this->line .= $prefix; if ($hasChild) { $this->dumpLine($cursor->depth); } } public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut): void { if (empty($cursor->attr['cut_hash'])) { $this->dumpEllipsis($cursor, $hasChild, $cut); $this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : '')); } $this->endValue($cursor); } /** * Dumps an ellipsis for cut children. * * @param bool $hasChild When the dump of the hash has child item * @param int $cut The number of items the hash has been cut by */ protected function dumpEllipsis(Cursor $cursor, bool $hasChild, int $cut): void { if ($cut) { $this->line .= ' …'; if (0 < $cut) { $this->line .= $cut; } if ($hasChild) { $this->dumpLine($cursor->depth + 1); } } } /** * Dumps a key in a hash structure. */ protected function dumpKey(Cursor $cursor): void { if (null !== $key = $cursor->hashKey) { if ($cursor->hashKeyIsBinary) { $key = $this->utf8Encode($key); } $attr = ['binary' => $cursor->hashKeyIsBinary]; $bin = $cursor->hashKeyIsBinary ? 'b' : ''; $style = 'key'; switch ($cursor->hashType) { default: case Cursor::HASH_INDEXED: if (self::DUMP_LIGHT_ARRAY & $this->flags) { break; } $style = 'index'; // no break case Cursor::HASH_ASSOC: if (\is_int($key)) { $this->line .= $this->style($style, $key).' => '; } else { $this->line .= $bin.'"'.$this->style($style, $key).'" => '; } break; case Cursor::HASH_RESOURCE: $key = "\0~\0".$key; // no break case Cursor::HASH_OBJECT: if (!isset($key[0]) || "\0" !== $key[0]) { $this->line .= '+'.$bin.$this->style('public', $key).': '; } elseif (0 < strpos($key, "\0", 1)) { $key = explode("\0", substr($key, 1), 2); switch ($key[0][0]) { case '+': // User inserted keys $attr['dynamic'] = true; $this->line .= '+'.$bin.'"'.$this->style('public', $key[1], $attr).'": '; break 2; case '~': $style = 'meta'; if (isset($key[0][1])) { parse_str(substr($key[0], 1), $attr); $attr += ['binary' => $cursor->hashKeyIsBinary]; } break; case '*': $style = 'protected'; $bin = '#'.$bin; break; default: $attr['class'] = $key[0]; $style = 'private'; $bin = '-'.$bin; break; } if (isset($attr['collapse'])) { if ($attr['collapse']) { $this->collapseNextHash = true; } else { $this->expandNextHash = true; } } $this->line .= $bin.$this->style($style, $key[1], $attr).($attr['separator'] ?? ': '); } else { // This case should not happen $this->line .= '-'.$bin.'"'.$this->style('private', $key, ['class' => '']).'": '; } break; } if ($cursor->hardRefTo) { $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), ['count' => $cursor->hardRefCount]).' '; } } } /** * Decorates a value with some style. * * @param string $style The type of style being applied * @param string $value The value being styled * @param array $attr Optional context information */ protected function style(string $style, string $value, array $attr = []): string { $this->colors ??= $this->supportsColors(); $this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100) && !isset($_SERVER['IDEA_INITIAL_DIRECTORY']); if (isset($attr['ellipsis'], $attr['ellipsis-type'])) { $prefix = substr($value, 0, -$attr['ellipsis']); if ('cli' === \PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === \DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && str_starts_with($prefix, $_SERVER[$pwd])) { $prefix = '.'.substr($prefix, \strlen($_SERVER[$pwd])); } if (!empty($attr['ellipsis-tail'])) { $prefix .= substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']); $value = substr($value, -$attr['ellipsis'] + $attr['ellipsis-tail']); } else { $value = substr($value, -$attr['ellipsis']); } $value = $this->style('default', $prefix).$this->style($style, $value); goto href; } $map = static::$controlCharsMap; $startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : ''; $endCchr = $this->colors ? "\033[m\033[{$this->styles[$style]}m" : ''; $value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) { $s = $startCchr; $c = $c[$i = 0]; do { $s .= $map[$c[$i]] ?? sprintf('\x%02X', \ord($c[$i])); } while (isset($c[++$i])); return $s.$endCchr; }, $value, -1, $cchrCount); if (!($attr['binary'] ?? false)) { $value = preg_replace_callback(static::$unicodeCharsRx, function ($c) use (&$cchrCount, $startCchr, $endCchr) { ++$cchrCount; return $startCchr.'\u{'.strtoupper(dechex(mb_ord($c[0]))).'}'.$endCchr; }, $value); } if ($this->colors && '' !== $value) { if ($cchrCount && "\033" === $value[0]) { $value = substr($value, \strlen($startCchr)); } else { $value = "\033[{$this->styles[$style]}m".$value; } if ($cchrCount && str_ends_with($value, $endCchr)) { $value = substr($value, 0, -\strlen($endCchr)); } else { $value .= "\033[{$this->styles['default']}m"; } } href: if ($this->colors && $this->handlesHrefGracefully) { if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) { if ('note' === $style) { $value .= "\033]8;;{$href}\033\\^\033]8;;\033\\"; } else { $attr['href'] = $href; } } if (isset($attr['href'])) { if ('label' === $style) { $value .= '^'; } $value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\"; } } if ('label' === $style && '' !== $value) { $value .= ' '; } return $value; } protected function supportsColors(): bool { if ($this->outputStream !== static::$defaultOutput) { return $this->hasColorSupport($this->outputStream); } if (isset(static::$defaultColors)) { return static::$defaultColors; } if (isset($_SERVER['argv'][1])) { $colors = $_SERVER['argv']; $i = \count($colors); while (--$i > 0) { if (isset($colors[$i][5])) { switch ($colors[$i]) { case '--ansi': case '--color': case '--color=yes': case '--color=force': case '--color=always': case '--colors=always': return static::$defaultColors = true; case '--no-ansi': case '--color=no': case '--color=none': case '--color=never': case '--colors=never': return static::$defaultColors = false; } } } } $h = stream_get_meta_data($this->outputStream) + ['wrapper_type' => null]; $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'w') : $this->outputStream; return static::$defaultColors = $this->hasColorSupport($h); } protected function dumpLine(int $depth, bool $endOfValue = false): void { if ($this->colors ??= $this->supportsColors()) { $this->line = sprintf("\033[%sm%s\033[m", $this->styles['default'], $this->line); } parent::dumpLine($depth); } protected function endValue(Cursor $cursor): void { if (-1 === $cursor->hashType) { return; } if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) { if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) { $this->line .= ','; } elseif (self::DUMP_COMMA_SEPARATOR & $this->flags && 1 < $cursor->hashLength - $cursor->hashIndex) { $this->line .= ','; } } $this->dumpLine($cursor->depth, true); } /** * Returns true if the stream supports colorization. * * Reference: Composer\XdebugHandler\Process::supportsColor * https://github.com/composer/xdebug-handler */ private function hasColorSupport(mixed $stream): bool { if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) { return false; } // Follow https://no-color.org/ if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) { return false; } // Detect msysgit/mingw and assume this is a tty because detection // does not work correctly, see https://github.com/composer/composer/issues/9690 if (!@stream_isatty($stream) && !\in_array(strtoupper((string) getenv('MSYSTEM')), ['MINGW32', 'MINGW64'], true)) { return false; } if ('\\' === \DIRECTORY_SEPARATOR && @sapi_windows_vt100_support($stream)) { return true; } if ('Hyper' === getenv('TERM_PROGRAM') || false !== getenv('COLORTERM') || false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') ) { return true; } if ('dumb' === $term = (string) getenv('TERM')) { return false; } // See https://github.com/chalk/supports-color/blob/d4f413efaf8da045c5ab440ed418ef02dbb28bf1/index.js#L157 return preg_match('/^((screen|xterm|vt100|vt220|putty|rxvt|ansi|cygwin|linux).*)|(.*-256(color)?(-bce)?)$/', $term); } /** * Returns true if the Windows terminal supports true color. * * Note that this does not check an output stream, but relies on environment * variables from known implementations, or a PHP and Windows version that * supports true color. */ private function isWindowsTrueColor(): bool { $result = 183 <= getenv('ANSICON_VER') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM') || 'Hyper' === getenv('TERM_PROGRAM'); if (!$result) { $version = sprintf( '%s.%s.%s', PHP_WINDOWS_VERSION_MAJOR, PHP_WINDOWS_VERSION_MINOR, PHP_WINDOWS_VERSION_BUILD ); $result = $version >= '10.0.15063'; } return $result; } private function getSourceLink(string $file, int $line): string|false { if ($fmt = $this->displayOptions['fileLinkFormat']) { return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file.'#L'.$line); } return false; } } var-dumper/Dumper/DataDumperInterface.php 0000644 00000001044 15060133305 0014435 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; /** * DataDumperInterface for dumping Data objects. * * @author Nicolas Grekas <p@tchwork.com> */ interface DataDumperInterface { /** * @return string|null */ public function dump(Data $data); } var-dumper/Dumper/ContextualizedDumper.php 0000644 00000002314 15060133305 0014746 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; /** * @author Kévin Thérage <therage.kevin@gmail.com> */ class ContextualizedDumper implements DataDumperInterface { private DataDumperInterface $wrappedDumper; private array $contextProviders; /** * @param ContextProviderInterface[] $contextProviders */ public function __construct(DataDumperInterface $wrappedDumper, array $contextProviders) { $this->wrappedDumper = $wrappedDumper; $this->contextProviders = $contextProviders; } public function dump(Data $data): ?string { $context = $data->getContext(); foreach ($this->contextProviders as $contextProvider) { $context[$contextProvider::class] = $contextProvider->getContext(); } return $this->wrappedDumper->dump($data->withContext($context)); } } var-dumper/Dumper/ServerDumper.php 0000644 00000003147 15060133305 0013217 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; use Symfony\Component\VarDumper\Server\Connection; /** * ServerDumper forwards serialized Data clones to a server. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ class ServerDumper implements DataDumperInterface { private Connection $connection; private ?DataDumperInterface $wrappedDumper; /** * @param string $host The server host * @param DataDumperInterface|null $wrappedDumper A wrapped instance used whenever we failed contacting the server * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name */ public function __construct(string $host, ?DataDumperInterface $wrappedDumper = null, array $contextProviders = []) { $this->connection = new Connection($host, $contextProviders); $this->wrappedDumper = $wrappedDumper; } public function getContextProviders(): array { return $this->connection->getContextProviders(); } public function dump(Data $data): ?string { if (!$this->connection->write($data) && $this->wrappedDumper) { return $this->wrappedDumper->dump($data); } return null; } } var-dumper/Dumper/ContextProvider/RequestContextProvider.php 0000644 00000002730 15060133305 0020440 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\VarDumper\Caster\ReflectionCaster; use Symfony\Component\VarDumper\Cloner\VarCloner; /** * Tries to provide context from a request. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ final class RequestContextProvider implements ContextProviderInterface { private RequestStack $requestStack; private VarCloner $cloner; public function __construct(RequestStack $requestStack) { $this->requestStack = $requestStack; $this->cloner = new VarCloner(); $this->cloner->setMaxItems(0); $this->cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); } public function getContext(): ?array { if (null === $request = $this->requestStack->getCurrentRequest()) { return null; } $controller = $request->attributes->get('_controller'); return [ 'uri' => $request->getUri(), 'method' => $request->getMethod(), 'controller' => $controller ? $this->cloner->cloneVar($controller) : $controller, 'identifier' => spl_object_hash($request), ]; } } var-dumper/Dumper/ContextProvider/CliContextProvider.php 0000644 00000001446 15060133305 0017522 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; /** * Tries to provide context on CLI. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ final class CliContextProvider implements ContextProviderInterface { public function getContext(): ?array { if ('cli' !== \PHP_SAPI) { return null; } return [ 'command_line' => $commandLine = implode(' ', $_SERVER['argv'] ?? []), 'identifier' => hash('crc32b', $commandLine.$_SERVER['REQUEST_TIME_FLOAT']), ]; } } var-dumper/Dumper/ContextProvider/ContextProviderInterface.php 0000644 00000001031 15060133305 0020701 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; /** * Interface to provide contextual data about dump data clones sent to a server. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ interface ContextProviderInterface { public function getContext(): ?array; } var-dumper/Dumper/ContextProvider/SourceContextProvider.php 0000644 00000011641 15060133305 0020251 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\VarDumper; use Twig\Template; /** * Tries to provide context from sources (class name, file, line, code excerpt, ...). * * @author Nicolas Grekas <p@tchwork.com> * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ final class SourceContextProvider implements ContextProviderInterface { private int $limit; private ?string $charset; private ?string $projectDir; private ?FileLinkFormatter $fileLinkFormatter; public function __construct(?string $charset = null, ?string $projectDir = null, ?FileLinkFormatter $fileLinkFormatter = null, int $limit = 9) { $this->charset = $charset; $this->projectDir = $projectDir; $this->fileLinkFormatter = $fileLinkFormatter; $this->limit = $limit; } public function getContext(): ?array { $trace = debug_backtrace(\DEBUG_BACKTRACE_PROVIDE_OBJECT | \DEBUG_BACKTRACE_IGNORE_ARGS, $this->limit); $file = $trace[1]['file']; $line = $trace[1]['line']; $name = '-' === $file || 'Standard input code' === $file ? 'Standard input code' : false; $fileExcerpt = false; for ($i = 2; $i < $this->limit; ++$i) { if (isset($trace[$i]['class'], $trace[$i]['function']) && 'dump' === $trace[$i]['function'] && VarDumper::class === $trace[$i]['class'] ) { $file = $trace[$i]['file'] ?? $file; $line = $trace[$i]['line'] ?? $line; while (++$i < $this->limit) { if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && !str_starts_with($trace[$i]['function'], 'call_user_func')) { $file = $trace[$i]['file']; $line = $trace[$i]['line']; break; } elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof Template) { $template = $trace[$i]['object']; $name = $template->getTemplateName(); $src = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : false); $info = $template->getDebugInfo(); if (isset($info[$trace[$i - 1]['line']])) { $line = $info[$trace[$i - 1]['line']]; $file = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : null; if ($src) { $src = explode("\n", $src); $fileExcerpt = []; for ($i = max($line - 3, 1), $max = min($line + 3, \count($src)); $i <= $max; ++$i) { $fileExcerpt[] = '<li'.($i === $line ? ' class="selected"' : '').'><code>'.$this->htmlEncode($src[$i - 1]).'</code></li>'; } $fileExcerpt = '<ol start="'.max($line - 3, 1).'">'.implode("\n", $fileExcerpt).'</ol>'; } } break; } } break; } } if (false === $name) { $name = str_replace('\\', '/', $file); $name = substr($name, strrpos($name, '/') + 1); } $context = ['name' => $name, 'file' => $file, 'line' => $line]; $context['file_excerpt'] = $fileExcerpt; if (null !== $this->projectDir) { $context['project_dir'] = $this->projectDir; if (str_starts_with($file, $this->projectDir)) { $context['file_relative'] = ltrim(substr($file, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); } } if ($this->fileLinkFormatter && $fileLink = $this->fileLinkFormatter->format($context['file'], $context['line'])) { $context['file_link'] = $fileLink; } return $context; } private function htmlEncode(string $s): string { $html = ''; $dumper = new HtmlDumper(function ($line) use (&$html) { $html .= $line; }, $this->charset); $dumper->setDumpHeader(''); $dumper->setDumpBoundaries('', ''); $cloner = new VarCloner(); $dumper->dump($cloner->cloneVar($s)); return substr(strip_tags($html), 1, -1); } } var-dumper/Test/VarDumperTestTrait.php 0000644 00000005073 15060133305 0014030 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Test; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; /** * @author Nicolas Grekas <p@tchwork.com> */ trait VarDumperTestTrait { /** * @internal */ private array $varDumperConfig = [ 'casters' => [], 'flags' => null, ]; protected function setUpVarDumper(array $casters, ?int $flags = null): void { $this->varDumperConfig['casters'] = $casters; $this->varDumperConfig['flags'] = $flags; } /** * @after */ protected function tearDownVarDumper(): void { $this->varDumperConfig['casters'] = []; $this->varDumperConfig['flags'] = null; } public function assertDumpEquals(mixed $expected, mixed $data, int $filter = 0, string $message = '') { $this->assertSame($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); } public function assertDumpMatchesFormat(mixed $expected, mixed $data, int $filter = 0, string $message = '') { $this->assertStringMatchesFormat($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); } protected function getDump(mixed $data, string|int|null $key = null, int $filter = 0): ?string { if (null === $flags = $this->varDumperConfig['flags']) { $flags = getenv('DUMP_LIGHT_ARRAY') ? CliDumper::DUMP_LIGHT_ARRAY : 0; $flags |= getenv('DUMP_STRING_LENGTH') ? CliDumper::DUMP_STRING_LENGTH : 0; $flags |= getenv('DUMP_COMMA_SEPARATOR') ? CliDumper::DUMP_COMMA_SEPARATOR : 0; } $cloner = new VarCloner(); $cloner->addCasters($this->varDumperConfig['casters']); $cloner->setMaxItems(-1); $dumper = new CliDumper(null, null, $flags); $dumper->setColors(false); $data = $cloner->cloneVar($data, $filter)->withRefHandles(false); if (null !== $key && null === $data = $data->seek($key)) { return null; } return rtrim($dumper->dump($data, true)); } private function prepareExpectation(mixed $expected, int $filter): string { if (!\is_string($expected)) { $expected = $this->getDump($expected, null, $filter); } return rtrim($expected); } } var-dumper/Server/DumpServer.php 0000644 00000006072 15060133305 0012702 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Server; use Psr\Log\LoggerInterface; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\Stub; /** * A server collecting Data clones sent by a ServerDumper. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> * * @final */ class DumpServer { private string $host; private ?LoggerInterface $logger; /** * @var resource|null */ private $socket; public function __construct(string $host, ?LoggerInterface $logger = null) { if (!str_contains($host, '://')) { $host = 'tcp://'.$host; } $this->host = $host; $this->logger = $logger; } public function start(): void { if (!$this->socket = stream_socket_server($this->host, $errno, $errstr)) { throw new \RuntimeException(sprintf('Server start failed on "%s": ', $this->host).$errstr.' '.$errno); } } public function listen(callable $callback): void { if (null === $this->socket) { $this->start(); } foreach ($this->getMessages() as $clientId => $message) { $this->logger?->info('Received a payload from client {clientId}', ['clientId' => $clientId]); $payload = @unserialize(base64_decode($message), ['allowed_classes' => [Data::class, Stub::class]]); // Impossible to decode the message, give up. if (false === $payload) { $this->logger?->warning('Unable to decode a message from {clientId} client.', ['clientId' => $clientId]); continue; } if (!\is_array($payload) || \count($payload) < 2 || !$payload[0] instanceof Data || !\is_array($payload[1])) { $this->logger?->warning('Invalid payload from {clientId} client. Expected an array of two elements (Data $data, array $context)', ['clientId' => $clientId]); continue; } [$data, $context] = $payload; $callback($data, $context, $clientId); } } public function getHost(): string { return $this->host; } private function getMessages(): iterable { $sockets = [(int) $this->socket => $this->socket]; $write = []; while (true) { $read = $sockets; stream_select($read, $write, $write, null); foreach ($read as $stream) { if ($this->socket === $stream) { $stream = stream_socket_accept($this->socket); $sockets[(int) $stream] = $stream; } elseif (feof($stream)) { unset($sockets[(int) $stream]); fclose($stream); } else { yield (int) $stream => fgets($stream); } } } } } var-dumper/Server/Connection.php 0000644 00000005153 15060133305 0012704 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Server; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; /** * Forwards serialized Data clones to a server. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ class Connection { private string $host; private array $contextProviders; /** * @var resource|null */ private $socket; /** * @param string $host The server host * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name */ public function __construct(string $host, array $contextProviders = []) { if (!str_contains($host, '://')) { $host = 'tcp://'.$host; } $this->host = $host; $this->contextProviders = $contextProviders; } public function getContextProviders(): array { return $this->contextProviders; } public function write(Data $data): bool { $socketIsFresh = !$this->socket; if (!$this->socket = $this->socket ?: $this->createSocket()) { return false; } $context = ['timestamp' => microtime(true)]; foreach ($this->contextProviders as $name => $provider) { $context[$name] = $provider->getContext(); } $context = array_filter($context); $encodedPayload = base64_encode(serialize([$data, $context]))."\n"; set_error_handler(static fn () => null); try { if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { return true; } if (!$socketIsFresh) { stream_socket_shutdown($this->socket, \STREAM_SHUT_RDWR); fclose($this->socket); $this->socket = $this->createSocket(); } if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { return true; } } finally { restore_error_handler(); } return false; } /** * @return resource|null */ private function createSocket() { set_error_handler(static fn () => null); try { return stream_socket_client($this->host, $errno, $errstr, 3) ?: null; } finally { restore_error_handler(); } } } var-dumper/Cloner/DumperInterface.php 0000644 00000003420 15060133305 0013631 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * DumperInterface used by Data objects. * * @author Nicolas Grekas <p@tchwork.com> */ interface DumperInterface { /** * Dumps a scalar value. */ public function dumpScalar(Cursor $cursor, string $type, string|int|float|bool|null $value): void; /** * Dumps a string. * * @param string $str The string being dumped * @param bool $bin Whether $str is UTF-8 or binary encoded * @param int $cut The number of characters $str has been cut by */ public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut): void; /** * Dumps while entering an hash. * * @param int $type A Cursor::HASH_* const for the type of hash * @param string|int|null $class The object class, resource type or array count * @param bool $hasChild When the dump of the hash has child item */ public function enterHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild): void; /** * Dumps while leaving an hash. * * @param int $type A Cursor::HASH_* const for the type of hash * @param string|int|null $class The object class, resource type or array count * @param bool $hasChild When the dump of the hash has child item * @param int $cut The number of items the hash has been cut by */ public function leaveHash(Cursor $cursor, int $type, string|int|null $class, bool $hasChild, int $cut): void; } var-dumper/Cloner/Internal/NoDefault.php 0000644 00000001046 15060133305 0014213 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner\Internal; /** * Flags a typed property that has no default value. * * This dummy object is used to distinguish a property with a default value of null * from a property that is uninitialized by default. * * @internal */ enum NoDefault { case NoDefault; } var-dumper/Cloner/AbstractCloner.php 0000644 00000054245 15060133305 0013475 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Exception\ThrowingCasterException; /** * AbstractCloner implements a generic caster mechanism for objects and resources. * * @author Nicolas Grekas <p@tchwork.com> */ abstract class AbstractCloner implements ClonerInterface { public static array $defaultCasters = [ '__PHP_Incomplete_Class' => ['Symfony\Component\VarDumper\Caster\Caster', 'castPhpIncompleteClass'], 'Symfony\Component\VarDumper\Caster\CutStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'], 'Symfony\Component\VarDumper\Caster\CutArrayStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castCutArray'], 'Symfony\Component\VarDumper\Caster\ConstStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'], 'Symfony\Component\VarDumper\Caster\EnumStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castEnum'], 'Symfony\Component\VarDumper\Caster\ScalarStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castScalar'], 'Fiber' => ['Symfony\Component\VarDumper\Caster\FiberCaster', 'castFiber'], 'Closure' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'], 'Generator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'], 'ReflectionType' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'], 'ReflectionAttribute' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castAttribute'], 'ReflectionGenerator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'], 'ReflectionClass' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'], 'ReflectionClassConstant' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClassConstant'], 'ReflectionFunctionAbstract' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'], 'ReflectionMethod' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'], 'ReflectionParameter' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'], 'ReflectionProperty' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castProperty'], 'ReflectionReference' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReference'], 'ReflectionExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castExtension'], 'ReflectionZendExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castZendExtension'], 'Doctrine\Common\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Doctrine\Common\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castCommonProxy'], 'Doctrine\ORM\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castOrmProxy'], 'Doctrine\ORM\PersistentCollection' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castPersistentCollection'], 'Doctrine\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'DOMException' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'], 'Dom\Exception' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'], 'DOMStringList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], 'DOMNameList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], 'DOMImplementation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'], 'Dom\Implementation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'], 'DOMImplementationList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], 'DOMNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNode'], 'Dom\Node' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNode'], 'DOMNameSpaceNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNameSpaceNode'], 'DOMDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocument'], 'Dom\XMLDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castXMLDocument'], 'Dom\HTMLDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castHTMLDocument'], 'DOMNodeList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], 'Dom\NodeList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], 'DOMNamedNodeMap' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], 'Dom\DTDNamedNodeMap' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], 'DOMCharacterData' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castCharacterData'], 'Dom\CharacterData' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castCharacterData'], 'DOMAttr' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castAttr'], 'Dom\Attr' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castAttr'], 'DOMElement' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castElement'], 'Dom\Element' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castElement'], 'DOMText' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castText'], 'Dom\Text' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castText'], 'DOMDocumentType' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocumentType'], 'Dom\DocumentType' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocumentType'], 'DOMNotation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNotation'], 'Dom\Notation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNotation'], 'DOMEntity' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castEntity'], 'Dom\Entity' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castEntity'], 'DOMProcessingInstruction' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castProcessingInstruction'], 'Dom\ProcessingInstruction' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castProcessingInstruction'], 'DOMXPath' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castXPath'], 'XMLReader' => ['Symfony\Component\VarDumper\Caster\XmlReaderCaster', 'castXmlReader'], 'ErrorException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castErrorException'], 'Exception' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castException'], 'Error' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castError'], 'Symfony\Bridge\Monolog\Logger' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\DependencyInjection\ContainerInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\EventDispatcher\EventDispatcherInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\HttpClient\AmpHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], 'Symfony\Component\HttpClient\CurlHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], 'Symfony\Component\HttpClient\NativeHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], 'Symfony\Component\HttpClient\Response\AmpResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], 'Symfony\Component\HttpClient\Response\CurlResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], 'Symfony\Component\HttpClient\Response\NativeResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], 'Symfony\Component\HttpFoundation\Request' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'], 'Symfony\Component\Uid\Ulid' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castUlid'], 'Symfony\Component\Uid\Uuid' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castUuid'], 'Symfony\Component\VarExporter\Internal\LazyObjectState' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castLazyObjectState'], 'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'], 'Symfony\Component\VarDumper\Caster\TraceStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'], 'Symfony\Component\VarDumper\Caster\FrameStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFrameStub'], 'Symfony\Component\VarDumper\Cloner\AbstractCloner' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Symfony\Component\ErrorHandler\Exception\FlattenException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFlattenException'], 'Symfony\Component\ErrorHandler\Exception\SilencedErrorContext' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'], 'Imagine\Image\ImageInterface' => ['Symfony\Component\VarDumper\Caster\ImagineCaster', 'castImage'], 'Ramsey\Uuid\UuidInterface' => ['Symfony\Component\VarDumper\Caster\UuidCaster', 'castRamseyUuid'], 'ProxyManager\Proxy\ProxyInterface' => ['Symfony\Component\VarDumper\Caster\ProxyManagerCaster', 'castProxy'], 'PHPUnit_Framework_MockObject_MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'PHPUnit\Framework\MockObject\MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'PHPUnit\Framework\MockObject\Stub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Prophecy\Prophecy\ProphecySubjectInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'Mockery\MockInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], 'PDO' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdo'], 'PDOStatement' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdoStatement'], 'AMQPConnection' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castConnection'], 'AMQPChannel' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castChannel'], 'AMQPQueue' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castQueue'], 'AMQPExchange' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castExchange'], 'AMQPEnvelope' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'], 'ArrayObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'], 'ArrayIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'], 'SplDoublyLinkedList' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'], 'SplFileInfo' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'], 'SplFileObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'], 'SplHeap' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], 'SplObjectStorage' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castObjectStorage'], 'SplPriorityQueue' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], 'OuterIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'], 'WeakMap' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castWeakMap'], 'WeakReference' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castWeakReference'], 'Redis' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'], 'Relay\Relay' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'], 'RedisArray' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'], 'RedisCluster' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisCluster'], 'DateTimeInterface' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castDateTime'], 'DateInterval' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castInterval'], 'DateTimeZone' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'], 'DatePeriod' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'], 'GMP' => ['Symfony\Component\VarDumper\Caster\GmpCaster', 'castGmp'], 'MessageFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castMessageFormatter'], 'NumberFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castNumberFormatter'], 'IntlTimeZone' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlTimeZone'], 'IntlCalendar' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlCalendar'], 'IntlDateFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlDateFormatter'], 'Memcached' => ['Symfony\Component\VarDumper\Caster\MemcachedCaster', 'castMemcached'], 'Ds\Collection' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castCollection'], 'Ds\Map' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castMap'], 'Ds\Pair' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPair'], 'Symfony\Component\VarDumper\Caster\DsPairStub' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPairStub'], 'mysqli_driver' => ['Symfony\Component\VarDumper\Caster\MysqliCaster', 'castMysqliDriver'], 'CurlHandle' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'], ':dba' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], ':dba persistent' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], 'GdImage' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'], ':gd' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'], ':pgsql large object' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'], ':pgsql link' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], ':pgsql link persistent' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], ':pgsql result' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'], ':process' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castProcess'], ':stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], 'OpenSSLCertificate' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'], ':OpenSSL X.509' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'], ':persistent stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], ':stream-context' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStreamContext'], 'XmlParser' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], ':xml' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], 'RdKafka' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castRdKafka'], 'RdKafka\Conf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castConf'], 'RdKafka\KafkaConsumer' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castKafkaConsumer'], 'RdKafka\Metadata\Broker' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castBrokerMetadata'], 'RdKafka\Metadata\Collection' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castCollectionMetadata'], 'RdKafka\Metadata\Partition' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castPartitionMetadata'], 'RdKafka\Metadata\Topic' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicMetadata'], 'RdKafka\Message' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castMessage'], 'RdKafka\Topic' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopic'], 'RdKafka\TopicPartition' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicPartition'], 'RdKafka\TopicConf' => ['Symfony\Component\VarDumper\Caster\RdKafkaCaster', 'castTopicConf'], 'FFI\CData' => ['Symfony\Component\VarDumper\Caster\FFICaster', 'castCTypeOrCData'], 'FFI\CType' => ['Symfony\Component\VarDumper\Caster\FFICaster', 'castCTypeOrCData'], ]; protected int $maxItems = 2500; protected int $maxString = -1; protected int $minDepth = 1; /** * @var array<string, list<callable>> */ private array $casters = []; /** * @var callable|null */ private $prevErrorHandler; private array $classInfo = []; private int $filter = 0; /** * @param callable[]|null $casters A map of casters * * @see addCasters */ public function __construct(?array $casters = null) { $this->addCasters($casters ?? static::$defaultCasters); } /** * Adds casters for resources and objects. * * Maps resources or objects types to a callback. * Types are in the key, with a callable caster for value. * Resource types are to be prefixed with a `:`, * see e.g. static::$defaultCasters. * * @param callable[] $casters A map of casters */ public function addCasters(array $casters): void { foreach ($casters as $type => $callback) { $this->casters[$type][] = $callback; } } /** * Sets the maximum number of items to clone past the minimum depth in nested structures. */ public function setMaxItems(int $maxItems): void { $this->maxItems = $maxItems; } /** * Sets the maximum cloned length for strings. */ public function setMaxString(int $maxString): void { $this->maxString = $maxString; } /** * Sets the minimum tree depth where we are guaranteed to clone all the items. After this * depth is reached, only setMaxItems items will be cloned. */ public function setMinDepth(int $minDepth): void { $this->minDepth = $minDepth; } /** * Clones a PHP variable. * * @param int $filter A bit field of Caster::EXCLUDE_* constants */ public function cloneVar(mixed $var, int $filter = 0): Data { $this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) { if (\E_RECOVERABLE_ERROR === $type || \E_USER_ERROR === $type) { // Cloner never dies throw new \ErrorException($msg, 0, $type, $file, $line); } if ($this->prevErrorHandler) { return ($this->prevErrorHandler)($type, $msg, $file, $line, $context); } return false; }); $this->filter = $filter; if ($gc = gc_enabled()) { gc_disable(); } try { return new Data($this->doClone($var)); } finally { if ($gc) { gc_enable(); } restore_error_handler(); $this->prevErrorHandler = null; } } /** * Effectively clones the PHP variable. */ abstract protected function doClone(mixed $var): array; /** * Casts an object to an array representation. * * @param bool $isNested True if the object is nested in the dumped structure */ protected function castObject(Stub $stub, bool $isNested): array { $obj = $stub->value; $class = $stub->class; if (str_contains($class, "@anonymous\0")) { $stub->class = get_debug_type($obj); } if (isset($this->classInfo[$class])) { [$i, $parents, $hasDebugInfo, $fileInfo] = $this->classInfo[$class]; } else { $i = 2; $parents = [$class]; $hasDebugInfo = method_exists($class, '__debugInfo'); foreach (class_parents($class) as $p) { $parents[] = $p; ++$i; } foreach (class_implements($class) as $p) { $parents[] = $p; ++$i; } $parents[] = '*'; $r = new \ReflectionClass($class); $fileInfo = $r->isInternal() || $r->isSubclassOf(Stub::class) ? [] : [ 'file' => $r->getFileName(), 'line' => $r->getStartLine(), ]; $this->classInfo[$class] = [$i, $parents, $hasDebugInfo, $fileInfo]; } $stub->attr += $fileInfo; $a = Caster::castObject($obj, $class, $hasDebugInfo, $stub->class); try { while ($i--) { if (!empty($this->casters[$p = $parents[$i]])) { foreach ($this->casters[$p] as $callback) { $a = $callback($obj, $a, $stub, $isNested, $this->filter); } } } } catch (\Exception $e) { $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; } return $a; } /** * Casts a resource to an array representation. * * @param bool $isNested True if the object is nested in the dumped structure */ protected function castResource(Stub $stub, bool $isNested): array { $a = []; $res = $stub->value; $type = $stub->class; try { if (!empty($this->casters[':'.$type])) { foreach ($this->casters[':'.$type] as $callback) { $a = $callback($res, $a, $stub, $isNested, $this->filter); } } } catch (\Exception $e) { $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; } return $a; } } var-dumper/Cloner/Stub.php 0000644 00000003615 15060133305 0011477 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; use Symfony\Component\VarDumper\Cloner\Internal\NoDefault; /** * Represents the main properties of a PHP variable. * * @author Nicolas Grekas <p@tchwork.com> */ class Stub { public const TYPE_REF = 1; public const TYPE_STRING = 2; public const TYPE_ARRAY = 3; public const TYPE_OBJECT = 4; public const TYPE_RESOURCE = 5; public const TYPE_SCALAR = 6; public const STRING_BINARY = 1; public const STRING_UTF8 = 2; public const ARRAY_ASSOC = 1; public const ARRAY_INDEXED = 2; public int $type = self::TYPE_REF; public string|int|null $class = ''; public mixed $value = null; public int $cut = 0; public int $handle = 0; public int $refCount = 0; public int $position = 0; public array $attr = []; private static array $defaultProperties = []; /** * @internal */ public function __sleep(): array { $properties = []; if (!isset(self::$defaultProperties[$c = static::class])) { $reflection = new \ReflectionClass($c); self::$defaultProperties[$c] = []; foreach ($reflection->getProperties() as $p) { if ($p->isStatic()) { continue; } self::$defaultProperties[$c][$p->name] = $p->hasDefaultValue() ? $p->getDefaultValue() : ($p->hasType() ? NoDefault::NoDefault : null); } } foreach (self::$defaultProperties[$c] as $k => $v) { if (NoDefault::NoDefault === $v || $this->$k !== $v) { $properties[] = $k; } } return $properties; } } var-dumper/Cloner/VarCloner.php 0000644 00000023547 15060133305 0012463 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * @author Nicolas Grekas <p@tchwork.com> */ class VarCloner extends AbstractCloner { private static array $arrayCache = []; protected function doClone(mixed $var): array { $len = 1; // Length of $queue $pos = 0; // Number of cloned items past the minimum depth $refsCounter = 0; // Hard references counter $queue = [[$var]]; // This breadth-first queue is the return value $hardRefs = []; // Map of original zval ids to stub objects $objRefs = []; // Map of original object handles to their stub object counterpart $objects = []; // Keep a ref to objects to ensure their handle cannot be reused while cloning $resRefs = []; // Map of original resource handles to their stub object counterpart $values = []; // Map of stub objects' ids to original values $maxItems = $this->maxItems; $maxString = $this->maxString; $minDepth = $this->minDepth; $currentDepth = 0; // Current tree depth $currentDepthFinalIndex = 0; // Final $queue index for current tree depth $minimumDepthReached = 0 === $minDepth; // Becomes true when minimum tree depth has been reached $cookie = (object) []; // Unique object used to detect hard references $a = null; // Array cast for nested structures $stub = null; // Stub capturing the main properties of an original item value // or null if the original value is used directly $arrayStub = new Stub(); $arrayStub->type = Stub::TYPE_ARRAY; for ($i = 0; $i < $len; ++$i) { // Detect when we move on to the next tree depth if ($i > $currentDepthFinalIndex) { ++$currentDepth; $currentDepthFinalIndex = $len - 1; if ($currentDepth >= $minDepth) { $minimumDepthReached = true; } } $refs = $vals = $queue[$i]; foreach ($vals as $k => $v) { // $v is the original value or a stub object in case of hard references $zvalRef = ($r = \ReflectionReference::fromArrayElement($vals, $k)) ? $r->getId() : null; if ($zvalRef) { $vals[$k] = &$stub; // Break hard references to make $queue completely unset($stub); // independent from the original structure if (null !== $vals[$k] = $hardRefs[$zvalRef] ?? null) { $v = $vals[$k]; if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) { ++$v->value->refCount; } ++$v->refCount; continue; } $vals[$k] = new Stub(); $vals[$k]->value = $v; $vals[$k]->handle = ++$refsCounter; $hardRefs[$zvalRef] = $vals[$k]; } // Create $stub when the original value $v cannot be used directly // If $v is a nested structure, put that structure in array $a switch (true) { case null === $v: case \is_bool($v): case \is_int($v): case \is_float($v): continue 2; case \is_string($v): if ('' === $v) { continue 2; } if (!preg_match('//u', $v)) { $stub = new Stub(); $stub->type = Stub::TYPE_STRING; $stub->class = Stub::STRING_BINARY; if (0 <= $maxString && 0 < $cut = \strlen($v) - $maxString) { $stub->cut = $cut; $stub->value = substr($v, 0, -$cut); } else { $stub->value = $v; } } elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = mb_strlen($v, 'UTF-8') - $maxString) { $stub = new Stub(); $stub->type = Stub::TYPE_STRING; $stub->class = Stub::STRING_UTF8; $stub->cut = $cut; $stub->value = mb_substr($v, 0, $maxString, 'UTF-8'); } else { continue 2; } $a = null; break; case \is_array($v): if (!$v) { continue 2; } $stub = $arrayStub; $stub->class = array_is_list($v) ? Stub::ARRAY_INDEXED : Stub::ARRAY_ASSOC; $a = $v; break; case \is_object($v): if (empty($objRefs[$h = spl_object_id($v)])) { $stub = new Stub(); $stub->type = Stub::TYPE_OBJECT; $stub->class = $v::class; $stub->value = $v; $stub->handle = $h; $a = $this->castObject($stub, 0 < $i); if ($v !== $stub->value) { if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) { break; } $stub->handle = $h = spl_object_id($stub->value); } $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { $stub->cut = \count($a); $a = null; } } if (empty($objRefs[$h])) { $objRefs[$h] = $stub; $objects[] = $v; } else { $stub = $objRefs[$h]; ++$stub->refCount; $a = null; } break; default: // resource if (empty($resRefs[$h = (int) $v])) { $stub = new Stub(); $stub->type = Stub::TYPE_RESOURCE; if ('Unknown' === $stub->class = @get_resource_type($v)) { $stub->class = 'Closed'; } $stub->value = $v; $stub->handle = $h; $a = $this->castResource($stub, 0 < $i); $stub->value = null; if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { $stub->cut = \count($a); $a = null; } } if (empty($resRefs[$h])) { $resRefs[$h] = $stub; } else { $stub = $resRefs[$h]; ++$stub->refCount; $a = null; } break; } if ($a) { if (!$minimumDepthReached || 0 > $maxItems) { $queue[$len] = $a; $stub->position = $len++; } elseif ($pos < $maxItems) { if ($maxItems < $pos += \count($a)) { $a = \array_slice($a, 0, $maxItems - $pos, true); if ($stub->cut >= 0) { $stub->cut += $pos - $maxItems; } } $queue[$len] = $a; $stub->position = $len++; } elseif ($stub->cut >= 0) { $stub->cut += \count($a); $stub->position = 0; } } if ($arrayStub === $stub) { if ($arrayStub->cut) { $stub = [$arrayStub->cut, $arrayStub->class => $arrayStub->position]; $arrayStub->cut = 0; } elseif (isset(self::$arrayCache[$arrayStub->class][$arrayStub->position])) { $stub = self::$arrayCache[$arrayStub->class][$arrayStub->position]; } else { self::$arrayCache[$arrayStub->class][$arrayStub->position] = $stub = [$arrayStub->class => $arrayStub->position]; } } if (!$zvalRef) { $vals[$k] = $stub; } else { $hardRefs[$zvalRef]->value = $stub; } } $queue[$i] = $vals; } foreach ($values as $h => $v) { $hardRefs[$h] = $v; } return $queue; } } var-dumper/Cloner/Cursor.php 0000644 00000002225 15060133305 0012033 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * Represents the current state of a dumper while dumping. * * @author Nicolas Grekas <p@tchwork.com> */ class Cursor { public const HASH_INDEXED = Stub::ARRAY_INDEXED; public const HASH_ASSOC = Stub::ARRAY_ASSOC; public const HASH_OBJECT = Stub::TYPE_OBJECT; public const HASH_RESOURCE = Stub::TYPE_RESOURCE; public int $depth = 0; public int $refIndex = 0; public int $softRefTo = 0; public int $softRefCount = 0; public int $softRefHandle = 0; public int $hardRefTo = 0; public int $hardRefCount = 0; public int $hardRefHandle = 0; public int $hashType; public string|int|null $hashKey = null; public bool $hashKeyIsBinary; public int $hashIndex = 0; public int $hashLength = 0; public int $hashCut = 0; public bool $stop = false; public array $attr = []; public bool $skipChildren = false; } var-dumper/Cloner/Data.php 0000644 00000032527 15060133305 0011437 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; /** * @author Nicolas Grekas <p@tchwork.com> */ class Data implements \ArrayAccess, \Countable, \IteratorAggregate, \Stringable { private array $data; private int $position = 0; private int|string $key = 0; private int $maxDepth = 20; private int $maxItemsPerDepth = -1; private int $useRefHandles = -1; private array $context = []; /** * @param array $data An array as returned by ClonerInterface::cloneVar() */ public function __construct(array $data) { $this->data = $data; } public function getType(): ?string { $item = $this->data[$this->position][$this->key]; if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { $item = $item->value; } if (!$item instanceof Stub) { return \gettype($item); } if (Stub::TYPE_STRING === $item->type) { return 'string'; } if (Stub::TYPE_ARRAY === $item->type) { return 'array'; } if (Stub::TYPE_OBJECT === $item->type) { return $item->class; } if (Stub::TYPE_RESOURCE === $item->type) { return $item->class.' resource'; } return null; } /** * Returns a native representation of the original value. * * @param array|bool $recursive Whether values should be resolved recursively or not * * @return string|int|float|bool|array|Data[]|null */ public function getValue(array|bool $recursive = false): string|int|float|bool|array|null { $item = $this->data[$this->position][$this->key]; if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { $item = $item->value; } if (!($item = $this->getStub($item)) instanceof Stub) { return $item; } if (Stub::TYPE_STRING === $item->type) { return $item->value; } $children = $item->position ? $this->data[$item->position] : []; foreach ($children as $k => $v) { if ($recursive && !($v = $this->getStub($v)) instanceof Stub) { continue; } $children[$k] = clone $this; $children[$k]->key = $k; $children[$k]->position = $item->position; if ($recursive) { if (Stub::TYPE_REF === $v->type && ($v = $this->getStub($v->value)) instanceof Stub) { $recursive = (array) $recursive; if (isset($recursive[$v->position])) { continue; } $recursive[$v->position] = true; } $children[$k] = $children[$k]->getValue($recursive); } } return $children; } public function count(): int { return \count($this->getValue()); } public function getIterator(): \Traversable { if (!\is_array($value = $this->getValue())) { throw new \LogicException(sprintf('"%s" object holds non-iterable type "%s".', self::class, get_debug_type($value))); } yield from $value; } public function __get(string $key): mixed { if (null !== $data = $this->seek($key)) { $item = $this->getStub($data->data[$data->position][$data->key]); return $item instanceof Stub || [] === $item ? $data : $item; } return null; } public function __isset(string $key): bool { return null !== $this->seek($key); } public function offsetExists(mixed $key): bool { return $this->__isset($key); } public function offsetGet(mixed $key): mixed { return $this->__get($key); } public function offsetSet(mixed $key, mixed $value): void { throw new \BadMethodCallException(self::class.' objects are immutable.'); } public function offsetUnset(mixed $key): void { throw new \BadMethodCallException(self::class.' objects are immutable.'); } public function __toString(): string { $value = $this->getValue(); if (!\is_array($value)) { return (string) $value; } return sprintf('%s (count=%d)', $this->getType(), \count($value)); } /** * Returns a depth limited clone of $this. */ public function withMaxDepth(int $maxDepth): static { $data = clone $this; $data->maxDepth = $maxDepth; return $data; } /** * Limits the number of elements per depth level. */ public function withMaxItemsPerDepth(int $maxItemsPerDepth): static { $data = clone $this; $data->maxItemsPerDepth = $maxItemsPerDepth; return $data; } /** * Enables/disables objects' identifiers tracking. * * @param bool $useRefHandles False to hide global ref. handles */ public function withRefHandles(bool $useRefHandles): static { $data = clone $this; $data->useRefHandles = $useRefHandles ? -1 : 0; return $data; } public function withContext(array $context): static { $data = clone $this; $data->context = $context; return $data; } public function getContext(): array { return $this->context; } /** * Seeks to a specific key in nested data structures. */ public function seek(string|int $key): ?static { $item = $this->data[$this->position][$this->key]; if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { $item = $item->value; } if (!($item = $this->getStub($item)) instanceof Stub || !$item->position) { return null; } $keys = [$key]; switch ($item->type) { case Stub::TYPE_OBJECT: $keys[] = Caster::PREFIX_DYNAMIC.$key; $keys[] = Caster::PREFIX_PROTECTED.$key; $keys[] = Caster::PREFIX_VIRTUAL.$key; $keys[] = "\0$item->class\0$key"; // no break case Stub::TYPE_ARRAY: case Stub::TYPE_RESOURCE: break; default: return null; } $data = null; $children = $this->data[$item->position]; foreach ($keys as $key) { if (isset($children[$key]) || \array_key_exists($key, $children)) { $data = clone $this; $data->key = $key; $data->position = $item->position; break; } } return $data; } /** * Dumps data with a DumperInterface dumper. */ public function dump(DumperInterface $dumper): void { $refs = [0]; $cursor = new Cursor(); $cursor->hashType = -1; $cursor->attr = $this->context[SourceContextProvider::class] ?? []; $label = $this->context['label'] ?? ''; if ($cursor->attr || '' !== $label) { $dumper->dumpScalar($cursor, 'label', $label); } $cursor->hashType = 0; $this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]); } /** * Depth-first dumping of items. * * @param mixed $item A Stub object or the original value being dumped */ private function dumpItem(DumperInterface $dumper, Cursor $cursor, array &$refs, mixed $item): void { $cursor->refIndex = 0; $cursor->softRefTo = $cursor->softRefHandle = $cursor->softRefCount = 0; $cursor->hardRefTo = $cursor->hardRefHandle = $cursor->hardRefCount = 0; $firstSeen = true; if (!$item instanceof Stub) { $cursor->attr = []; $type = \gettype($item); if ($item && 'array' === $type) { $item = $this->getStub($item); } } elseif (Stub::TYPE_REF === $item->type) { if ($item->handle) { if (!isset($refs[$r = $item->handle - (\PHP_INT_MAX >> 1)])) { $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; } else { $firstSeen = false; } $cursor->hardRefTo = $refs[$r]; $cursor->hardRefHandle = $this->useRefHandles & $item->handle; $cursor->hardRefCount = 0 < $item->handle ? $item->refCount : 0; } $cursor->attr = $item->attr; $type = $item->class ?: \gettype($item->value); $item = $this->getStub($item->value); } if ($item instanceof Stub) { if ($item->refCount) { if (!isset($refs[$r = $item->handle])) { $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; } else { $firstSeen = false; } $cursor->softRefTo = $refs[$r]; } $cursor->softRefHandle = $this->useRefHandles & $item->handle; $cursor->softRefCount = $item->refCount; $cursor->attr = $item->attr; $cut = $item->cut; if ($item->position && $firstSeen) { $children = $this->data[$item->position]; if ($cursor->stop) { if ($cut >= 0) { $cut += \count($children); } $children = []; } } else { $children = []; } switch ($item->type) { case Stub::TYPE_STRING: $dumper->dumpString($cursor, $item->value, Stub::STRING_BINARY === $item->class, $cut); break; case Stub::TYPE_ARRAY: $item = clone $item; $item->type = $item->class; $item->class = $item->value; // no break case Stub::TYPE_OBJECT: case Stub::TYPE_RESOURCE: $withChildren = $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth; $dumper->enterHash($cursor, $item->type, $item->class, $withChildren); if ($withChildren) { if ($cursor->skipChildren) { $withChildren = false; $cut = -1; } else { $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type, null !== $item->class); } } elseif ($children && 0 <= $cut) { $cut += \count($children); } $cursor->skipChildren = false; $dumper->leaveHash($cursor, $item->type, $item->class, $withChildren, $cut); break; case Stub::TYPE_SCALAR: $dumper->dumpScalar($cursor, 'default', $item->attr['value']); break; default: throw new \RuntimeException(sprintf('Unexpected Stub type: "%s".', $item->type)); } } elseif ('array' === $type) { $dumper->enterHash($cursor, Cursor::HASH_INDEXED, 0, false); $dumper->leaveHash($cursor, Cursor::HASH_INDEXED, 0, false, 0); } elseif ('string' === $type) { $dumper->dumpString($cursor, $item, false, 0); } else { $dumper->dumpScalar($cursor, $type, $item); } } /** * Dumps children of hash structures. * * @return int The final number of removed items */ private function dumpChildren(DumperInterface $dumper, Cursor $parentCursor, array &$refs, array $children, int $hashCut, int $hashType, bool $dumpKeys): int { $cursor = clone $parentCursor; ++$cursor->depth; $cursor->hashType = $hashType; $cursor->hashIndex = 0; $cursor->hashLength = \count($children); $cursor->hashCut = $hashCut; foreach ($children as $key => $child) { $cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u', $key); $cursor->hashKey = $dumpKeys ? $key : null; $this->dumpItem($dumper, $cursor, $refs, $child); if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) { $parentCursor->stop = true; return $hashCut >= 0 ? $hashCut + $cursor->hashLength - $cursor->hashIndex : $hashCut; } } return $hashCut; } private function getStub(mixed $item): mixed { if (!$item || !\is_array($item)) { return $item; } $stub = new Stub(); $stub->type = Stub::TYPE_ARRAY; foreach ($item as $stub->class => $stub->position) { } if (isset($item[0])) { $stub->cut = $item[0]; } $stub->value = $stub->cut + ($stub->position ? \count($this->data[$stub->position]) : 0); return $stub; } } var-dumper/Cloner/ClonerInterface.php 0000644 00000000713 15060133305 0013621 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Cloner; /** * @author Nicolas Grekas <p@tchwork.com> */ interface ClonerInterface { /** * Clones a PHP variable. */ public function cloneVar(mixed $var): Data; } var-dumper/CHANGELOG.md 0000644 00000005427 15060133305 0010443 0 ustar 00 CHANGELOG ========= 7.1 --- * Add support for new DOM extension classes in `DOMCaster` 7.0 --- * Add argument `$label` to `VarDumper::dump()` * Require explicit argument when calling `VarDumper::setHandler()` * Remove display of backtrace in `Twig_Template`, only `Twig\Template` is supported 6.4 --- * Dump uninitialized properties 6.3 --- * Add caster for `WeakMap` * Add support of named arguments to `dd()` and `dump()` to display the argument name * Add support for `Relay\Relay` * Add display of invisible characters 6.2 --- * Add support for `FFI\CData` and `FFI\CType` * Deprecate calling `VarDumper::setHandler()` without arguments 5.4 --- * Add ability to style integer and double values independently * Add casters for Symfony's UUIDs and ULIDs * Add support for `Fiber` 5.2.0 ----- * added support for PHPUnit `--colors` option * added `VAR_DUMPER_FORMAT=server` env var value support * prevent replacing the handler when the `VAR_DUMPER_FORMAT` env var is set 5.1.0 ----- * added `RdKafka` support 4.4.0 ----- * added `VarDumperTestTrait::setUpVarDumper()` and `VarDumperTestTrait::tearDownVarDumper()` to configure casters & flags to use in tests * added `ImagineCaster` and infrastructure to dump images * added the stamps of a message after it is dispatched in `TraceableMessageBus` and `MessengerDataCollector` collected data * added `UuidCaster` * made all casters final * added support for the `NO_COLOR` env var (https://no-color.org/) 4.3.0 ----- * added `DsCaster` to support dumping the contents of data structures from the Ds extension 4.2.0 ----- * support selecting the format to use by setting the environment variable `VAR_DUMPER_FORMAT` to `html` or `cli` 4.1.0 ----- * added a `ServerDumper` to send serialized Data clones to a server * added a `ServerDumpCommand` and `DumpServer` to run a server collecting and displaying dumps on a single place with multiple formats support * added `CliDescriptor` and `HtmlDescriptor` descriptors for `server:dump` CLI and HTML formats support 4.0.0 ----- * support for passing `\ReflectionClass` instances to the `Caster::castObject()` method has been dropped, pass class names as strings instead * the `Data::getRawData()` method has been removed * the `VarDumperTestTrait::assertDumpEquals()` method expects a 3rd `$filter = 0` argument and moves `$message = ''` argument at 4th position. * the `VarDumperTestTrait::assertDumpMatchesFormat()` method expects a 3rd `$filter = 0` argument and moves `$message = ''` argument at 4th position. 3.4.0 ----- * added `AbstractCloner::setMinDepth()` function to ensure minimum tree depth * deprecated `MongoCaster` 2.7.0 ----- * deprecated `Cloner\Data::getLimitedClone()`. Use `withMaxDepth`, `withMaxItemsPerDepth` or `withRefHandles` instead. var-dumper/composer.json 0000644 00000002231 15060133305 0011342 0 ustar 00 { "name": "symfony/var-dumper", "type": "library", "description": "Provides mechanisms for walking through any arbitrary PHP variable", "keywords": ["dump", "debug"], "homepage": "https://symfony.com", "license": "MIT", "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "require": { "php": ">=8.2", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { "ext-iconv": "*", "symfony/console": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/process": "^6.4|^7.0", "symfony/uid": "^6.4|^7.0", "twig/twig": "^3.0.4" }, "conflict": { "symfony/console": "<6.4" }, "autoload": { "files": [ "Resources/functions/dump.php" ], "psr-4": { "Symfony\\Component\\VarDumper\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "bin": [ "Resources/bin/var-dump-server" ], "minimum-stability": "dev" } var-dumper/Caster/GmpCaster.php 0000644 00000001355 15060133305 0012445 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts GMP objects to array representation. * * @author Hamza Amrouche <hamza.simperfit@gmail.com> * @author Nicolas Grekas <p@tchwork.com> * * @final */ class GmpCaster { public static function castGmp(\GMP $gmp, array $a, Stub $stub, bool $isNested, int $filter): array { $a[Caster::PREFIX_VIRTUAL.'value'] = new ConstStub(gmp_strval($gmp), gmp_strval($gmp)); return $a; } } var-dumper/Caster/SplCaster.php 0000644 00000017525 15060133305 0012466 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts SPL related classes to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class SplCaster { private const SPL_FILE_OBJECT_FLAGS = [ \SplFileObject::DROP_NEW_LINE => 'DROP_NEW_LINE', \SplFileObject::READ_AHEAD => 'READ_AHEAD', \SplFileObject::SKIP_EMPTY => 'SKIP_EMPTY', \SplFileObject::READ_CSV => 'READ_CSV', ]; public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, bool $isNested): array { return self::castSplArray($c, $a, $stub, $isNested); } public static function castArrayIterator(\ArrayIterator $c, array $a, Stub $stub, bool $isNested): array { return self::castSplArray($c, $a, $stub, $isNested); } public static function castHeap(\Iterator $c, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'heap' => iterator_to_array(clone $c), ]; return $a; } public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $mode = $c->getIteratorMode(); $c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE); $a += [ $prefix.'mode' => new ConstStub((($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_DELETE) ? 'IT_MODE_DELETE' : 'IT_MODE_KEEP'), $mode), $prefix.'dllist' => iterator_to_array($c), ]; $c->setIteratorMode($mode); return $a; } public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, bool $isNested): array { static $map = [ 'path' => 'getPath', 'filename' => 'getFilename', 'basename' => 'getBasename', 'pathname' => 'getPathname', 'extension' => 'getExtension', 'realPath' => 'getRealPath', 'aTime' => 'getATime', 'mTime' => 'getMTime', 'cTime' => 'getCTime', 'inode' => 'getInode', 'size' => 'getSize', 'perms' => 'getPerms', 'owner' => 'getOwner', 'group' => 'getGroup', 'type' => 'getType', 'writable' => 'isWritable', 'readable' => 'isReadable', 'executable' => 'isExecutable', 'file' => 'isFile', 'dir' => 'isDir', 'link' => 'isLink', 'linkTarget' => 'getLinkTarget', ]; $prefix = Caster::PREFIX_VIRTUAL; unset($a["\0SplFileInfo\0fileName"]); unset($a["\0SplFileInfo\0pathName"]); try { $c->isReadable(); } catch (\RuntimeException $e) { if ('Object not initialized' !== $e->getMessage()) { throw $e; } $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; return $a; } catch (\Error $e) { if ('Object not initialized' !== $e->getMessage()) { throw $e; } $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; return $a; } foreach ($map as $key => $accessor) { try { $a[$prefix.$key] = $c->$accessor(); } catch (\Exception) { } } if ($a[$prefix.'realPath'] ?? false) { $a[$prefix.'realPath'] = new LinkStub($a[$prefix.'realPath']); } if (isset($a[$prefix.'perms'])) { $a[$prefix.'perms'] = new ConstStub(sprintf('0%o', $a[$prefix.'perms']), $a[$prefix.'perms']); } static $mapDate = ['aTime', 'mTime', 'cTime']; foreach ($mapDate as $key) { if (isset($a[$prefix.$key])) { $a[$prefix.$key] = new ConstStub(date('Y-m-d H:i:s', $a[$prefix.$key]), $a[$prefix.$key]); } } return $a; } public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, bool $isNested): array { static $map = [ 'csvControl' => 'getCsvControl', 'flags' => 'getFlags', 'maxLineLen' => 'getMaxLineLen', 'fstat' => 'fstat', 'eof' => 'eof', 'key' => 'key', ]; $prefix = Caster::PREFIX_VIRTUAL; foreach ($map as $key => $accessor) { try { $a[$prefix.$key] = $c->$accessor(); } catch (\Exception) { } } if (isset($a[$prefix.'flags'])) { $flagsArray = []; foreach (self::SPL_FILE_OBJECT_FLAGS as $value => $name) { if ($a[$prefix.'flags'] & $value) { $flagsArray[] = $name; } } $a[$prefix.'flags'] = new ConstStub(implode('|', $flagsArray), $a[$prefix.'flags']); } if (isset($a[$prefix.'fstat'])) { $a[$prefix.'fstat'] = new CutArrayStub($a[$prefix.'fstat'], ['dev', 'ino', 'nlink', 'rdev', 'blksize', 'blocks']); } return $a; } public static function castObjectStorage(\SplObjectStorage $c, array $a, Stub $stub, bool $isNested): array { $storage = []; unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967 unset($a["\0SplObjectStorage\0storage"]); $clone = clone $c; foreach ($clone as $obj) { $storage[] = new EnumStub([ 'object' => $obj, 'info' => $clone->getInfo(), ]); } $a += [ Caster::PREFIX_VIRTUAL.'storage' => $storage, ]; return $a; } public static function castOuterIterator(\OuterIterator $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'innerIterator'] = $c->getInnerIterator(); return $a; } public static function castWeakReference(\WeakReference $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'object'] = $c->get(); return $a; } public static function castWeakMap(\WeakMap $c, array $a, Stub $stub, bool $isNested): array { $map = []; foreach (clone $c as $obj => $data) { $map[] = new EnumStub([ 'object' => $obj, 'data' => $data, ]); } $a += [ Caster::PREFIX_VIRTUAL.'map' => $map, ]; return $a; } private static function castSplArray(\ArrayObject|\ArrayIterator $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $flags = $c->getFlags(); if (!($flags & \ArrayObject::STD_PROP_LIST)) { $c->setFlags(\ArrayObject::STD_PROP_LIST); $a = Caster::castObject($c, $c::class, method_exists($c, '__debugInfo'), $stub->class); $c->setFlags($flags); } unset($a["\0ArrayObject\0storage"], $a["\0ArrayIterator\0storage"]); $a += [ $prefix.'storage' => $c->getArrayCopy(), $prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST), $prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS), ]; if ($c instanceof \ArrayObject) { $a[$prefix.'iteratorClass'] = new ClassStub($c->getIteratorClass()); } return $a; } } var-dumper/Caster/ExceptionCaster.php 0000644 00000040070 15060133305 0013655 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Exception\ThrowingCasterException; /** * Casts common Exception classes to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class ExceptionCaster { public static int $srcContext = 1; public static bool $traceArgs = true; public static array $errorTypes = [ \E_DEPRECATED => 'E_DEPRECATED', \E_USER_DEPRECATED => 'E_USER_DEPRECATED', \E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', \E_ERROR => 'E_ERROR', \E_WARNING => 'E_WARNING', \E_PARSE => 'E_PARSE', \E_NOTICE => 'E_NOTICE', \E_CORE_ERROR => 'E_CORE_ERROR', \E_CORE_WARNING => 'E_CORE_WARNING', \E_COMPILE_ERROR => 'E_COMPILE_ERROR', \E_COMPILE_WARNING => 'E_COMPILE_WARNING', \E_USER_ERROR => 'E_USER_ERROR', \E_USER_WARNING => 'E_USER_WARNING', \E_USER_NOTICE => 'E_USER_NOTICE', \E_STRICT => 'E_STRICT', ]; private static array $framesCache = []; public static function castError(\Error $e, array $a, Stub $stub, bool $isNested, int $filter = 0): array { return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter); } public static function castException(\Exception $e, array $a, Stub $stub, bool $isNested, int $filter = 0): array { return self::filterExceptionArray($stub->class, $a, "\0Exception\0", $filter); } public static function castErrorException(\ErrorException $e, array $a, Stub $stub, bool $isNested): array { if (isset($a[$s = Caster::PREFIX_PROTECTED.'severity'], self::$errorTypes[$a[$s]])) { $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); } return $a; } public static function castThrowingCasterException(ThrowingCasterException $e, array $a, Stub $stub, bool $isNested): array { $trace = Caster::PREFIX_VIRTUAL.'trace'; $prefix = Caster::PREFIX_PROTECTED; $xPrefix = "\0Exception\0"; if (isset($a[$xPrefix.'previous'], $a[$trace]) && $a[$xPrefix.'previous'] instanceof \Exception) { $b = (array) $a[$xPrefix.'previous']; $class = get_debug_type($a[$xPrefix.'previous']); self::traceUnshift($b[$xPrefix.'trace'], $class, $b[$prefix.'file'], $b[$prefix.'line']); $a[$trace] = new TraceStub($b[$xPrefix.'trace'], false, 0, -\count($a[$trace]->value)); } unset($a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']); return $a; } public static function castSilencedErrorContext(SilencedErrorContext $e, array $a, Stub $stub, bool $isNested): array { $sPrefix = "\0".SilencedErrorContext::class."\0"; if (!isset($a[$s = $sPrefix.'severity'])) { return $a; } if (isset(self::$errorTypes[$a[$s]])) { $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); } $trace = [[ 'file' => $a[$sPrefix.'file'], 'line' => $a[$sPrefix.'line'], ]]; if (isset($a[$sPrefix.'trace'])) { $trace = array_merge($trace, $a[$sPrefix.'trace']); } unset($a[$sPrefix.'file'], $a[$sPrefix.'line'], $a[$sPrefix.'trace']); $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs); return $a; } public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, bool $isNested): array { if (!$isNested) { return $a; } $stub->class = ''; $stub->handle = 0; $frames = $trace->value; $prefix = Caster::PREFIX_VIRTUAL; $a = []; $j = \count($frames); if (0 > $i = $trace->sliceOffset) { $i = max(0, $j + $i); } if (!isset($trace->value[$i])) { return []; } $lastCall = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : ''; $frames[] = ['function' => '']; $collapse = false; for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) { $f = $frames[$i]; $call = isset($f['function']) ? (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'] : '???'; $frame = new FrameStub( [ 'object' => $f['object'] ?? null, 'class' => $f['class'] ?? null, 'type' => $f['type'] ?? null, 'function' => $f['function'] ?? null, ] + $frames[$i - 1], false, true ); $f = self::castFrameStub($frame, [], $frame, true); if (isset($f[$prefix.'src'])) { foreach ($f[$prefix.'src']->value as $label => $frame) { if (str_starts_with($label, "\0~collapse=0")) { if ($collapse) { $label = substr_replace($label, '1', 11, 1); } else { $collapse = true; } } $label = substr_replace($label, "title=Stack level $j.&", 2, 0); } $f = $frames[$i - 1]; if ($trace->keepArgs && !empty($f['args']) && $frame instanceof EnumStub) { $frame->value['arguments'] = new ArgsStub($f['args'], $f['function'] ?? null, $f['class'] ?? null); } } elseif ('???' !== $lastCall) { $label = new ClassStub($lastCall); if (isset($label->attr['ellipsis'])) { $label->attr['ellipsis'] += 2; $label = substr_replace($prefix, "ellipsis-type=class&ellipsis={$label->attr['ellipsis']}&ellipsis-tail=1&title=Stack level $j.", 2, 0).$label->value.'()'; } else { $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$label->value.'()'; } } else { $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$lastCall; } $a[substr_replace($label, sprintf('separator=%s&', $frame instanceof EnumStub ? ' ' : ':'), 2, 0)] = $frame; $lastCall = $call; } if (null !== $trace->sliceLength) { $a = \array_slice($a, 0, $trace->sliceLength, true); } return $a; } public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, bool $isNested): array { if (!$isNested) { return $a; } $f = $frame->value; $prefix = Caster::PREFIX_VIRTUAL; if (isset($f['file'], $f['line'])) { $cacheKey = $f; unset($cacheKey['object'], $cacheKey['args']); $cacheKey[] = self::$srcContext; $cacheKey = implode('-', $cacheKey); if (isset(self::$framesCache[$cacheKey])) { $a[$prefix.'src'] = self::$framesCache[$cacheKey]; } else { if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) { $f['file'] = substr($f['file'], 0, -\strlen($match[0])); $f['line'] = (int) $match[1]; } $src = $f['line']; $srcKey = $f['file']; $ellipsis = new LinkStub($srcKey, 0); $srcAttr = 'collapse='.(int) $ellipsis->inVendor; $ellipsisTail = $ellipsis->attr['ellipsis-tail'] ?? 0; $ellipsis = $ellipsis->attr['ellipsis'] ?? 0; if (is_file($f['file']) && 0 <= self::$srcContext) { if (!empty($f['class']) && is_subclass_of($f['class'], 'Twig\Template')) { $template = null; if (isset($f['object'])) { $template = $f['object']; } elseif ((new \ReflectionClass($f['class']))->isInstantiable()) { $template = unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class'])); } if (null !== $template) { $ellipsis = 0; $templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : ''); $templateInfo = $template->getDebugInfo(); if (isset($templateInfo[$f['line']])) { if (!method_exists($template, 'getSourceContext') || !is_file($templatePath = $template->getSourceContext()->getPath())) { $templatePath = null; } if ($templateSrc) { $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, 'twig', $templatePath, $f); $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']]; } } } } if ($srcKey == $f['file']) { $src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, 'php', $f['file'], $f); $srcKey .= ':'.$f['line']; if ($ellipsis) { $ellipsis += 1 + \strlen($f['line']); } } $srcAttr .= sprintf('&separator= &file=%s&line=%d', rawurlencode($f['file']), $f['line']); } else { $srcAttr .= '&separator=:'; } $srcAttr .= $ellipsis ? '&ellipsis-type=path&ellipsis='.$ellipsis.'&ellipsis-tail='.$ellipsisTail : ''; self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(["\0~$srcAttr\0$srcKey" => $src]); } } unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']); if ($frame->inTraceStub) { unset($a[$prefix.'class'], $a[$prefix.'type'], $a[$prefix.'function']); } foreach ($a as $k => $v) { if (!$v) { unset($a[$k]); } } if ($frame->keepArgs && !empty($f['args'])) { $a[$prefix.'arguments'] = new ArgsStub($f['args'], $f['function'], $f['class']); } return $a; } public static function castFlattenException(FlattenException $e, array $a, Stub $stub, bool $isNested): array { if ($isNested) { $k = sprintf(Caster::PATTERN_PRIVATE, FlattenException::class, 'traceAsString'); $a[$k] = new CutStub($a[$k]); } return $a; } private static function filterExceptionArray(string $xClass, array $a, string $xPrefix, int $filter): array { if (isset($a[$xPrefix.'trace'])) { $trace = $a[$xPrefix.'trace']; unset($a[$xPrefix.'trace']); // Ensures the trace is always last } else { $trace = []; } if (!($filter & Caster::EXCLUDE_VERBOSE) && $trace) { if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { self::traceUnshift($trace, $xClass, $a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); } $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs); } if (empty($a[$xPrefix.'previous'])) { unset($a[$xPrefix.'previous']); } unset($a[$xPrefix.'string'], $a[Caster::PREFIX_DYNAMIC.'xdebug_message']); if (isset($a[Caster::PREFIX_PROTECTED.'message']) && str_contains($a[Caster::PREFIX_PROTECTED.'message'], "@anonymous\0")) { $a[Caster::PREFIX_PROTECTED.'message'] = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $a[Caster::PREFIX_PROTECTED.'message']); } if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { $a[Caster::PREFIX_PROTECTED.'file'] = new LinkStub($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); } return $a; } private static function traceUnshift(array &$trace, ?string $class, string $file, int $line): void { if (isset($trace[0]['file'], $trace[0]['line']) && $trace[0]['file'] === $file && $trace[0]['line'] === $line) { return; } array_unshift($trace, [ 'function' => $class ? 'new '.$class : null, 'file' => $file, 'line' => $line, ]); } private static function extractSource(string $srcLines, int $line, int $srcContext, string $lang, ?string $file, array $frame): EnumStub { $srcLines = explode("\n", $srcLines); $src = []; for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) { $src[] = ($srcLines[$i] ?? '')."\n"; } if ($frame['function'] ?? false) { $stub = new CutStub(new \stdClass()); $stub->class = (isset($frame['class']) ? $frame['class'].$frame['type'] : '').$frame['function']; $stub->type = Stub::TYPE_OBJECT; $stub->attr['cut_hash'] = true; $stub->attr['file'] = $frame['file']; $stub->attr['line'] = $frame['line']; try { $caller = isset($frame['class']) ? new \ReflectionMethod($frame['class'], $frame['function']) : new \ReflectionFunction($frame['function']); $stub->class .= ReflectionCaster::getSignature(ReflectionCaster::castFunctionAbstract($caller, [], $stub, true, Caster::EXCLUDE_VERBOSE)); if ($f = $caller->getFileName()) { $stub->attr['file'] = $f; $stub->attr['line'] = $caller->getStartLine(); } } catch (\ReflectionException) { // ignore fake class/function } $srcLines = ["\0~separator=\0" => $stub]; } else { $stub = null; $srcLines = []; } $ltrim = 0; do { $pad = null; for ($i = $srcContext << 1; $i >= 0; --$i) { if (isset($src[$i][$ltrim]) && "\r" !== ($c = $src[$i][$ltrim]) && "\n" !== $c) { $pad ??= $c; if ((' ' !== $c && "\t" !== $c) || $pad !== $c) { break; } } } ++$ltrim; } while (0 > $i && null !== $pad); --$ltrim; foreach ($src as $i => $c) { if ($ltrim) { $c = isset($c[$ltrim]) && "\r" !== $c[$ltrim] ? substr($c, $ltrim) : ltrim($c, " \t"); } $c = substr($c, 0, -1); if ($i !== $srcContext) { $c = new ConstStub('default', $c); } else { $c = new ConstStub($c, $stub ? 'in '.$stub->class : ''); if (null !== $file) { $c->attr['file'] = $file; $c->attr['line'] = $line; } } $c->attr['lang'] = $lang; $srcLines[sprintf("\0~separator=› &%d\0", $i + $line - $srcContext)] = $c; } return new EnumStub($srcLines); } } var-dumper/Caster/DoctrineCaster.php 0000644 00000003235 15060133305 0013470 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Doctrine\Common\Proxy\Proxy as CommonProxy; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Proxy\Proxy as OrmProxy; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Doctrine related classes to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class DoctrineCaster { public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, bool $isNested): array { foreach (['__cloner__', '__initializer__'] as $k) { if (\array_key_exists($k, $a)) { unset($a[$k]); ++$stub->cut; } } return $a; } public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, bool $isNested): array { foreach (['_entityPersister', '_identifier'] as $k) { if (\array_key_exists($k = "\0Doctrine\\ORM\\Proxy\\Proxy\0".$k, $a)) { unset($a[$k]); ++$stub->cut; } } return $a; } public static function castPersistentCollection(PersistentCollection $coll, array $a, Stub $stub, bool $isNested): array { foreach (['snapshot', 'association', 'typeClass'] as $k) { if (\array_key_exists($k = "\0Doctrine\\ORM\\PersistentCollection\0".$k, $a)) { $a[$k] = new CutStub($a[$k]); } } return $a; } } var-dumper/Caster/ConstStub.php 0000644 00000001344 15060133305 0012502 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents a PHP constant and its value. * * @author Nicolas Grekas <p@tchwork.com> */ class ConstStub extends Stub { public function __construct(string $name, string|int|float|null $value = null) { $this->class = $name; $this->value = 1 < \func_num_args() ? $value : $name; } public function __toString(): string { return (string) $this->value; } } var-dumper/Caster/ScalarStub.php 0000644 00000001052 15060133305 0012615 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents any arbitrary value. * * @author Alexandre Daubois <alex.daubois@gmail.com> */ class ScalarStub extends Stub { public function __construct(mixed $value) { $this->value = $value; } } var-dumper/Caster/ArgsStub.php 0000644 00000004372 15060133305 0012314 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents a list of function arguments. * * @author Nicolas Grekas <p@tchwork.com> */ class ArgsStub extends EnumStub { private static array $parameters = []; public function __construct(array $args, string $function, ?string $class) { [$variadic, $params] = self::getParameters($function, $class); $values = []; foreach ($args as $k => $v) { $values[$k] = !\is_scalar($v) && !$v instanceof Stub ? new CutStub($v) : $v; } if (null === $params) { parent::__construct($values, false); return; } if (\count($values) < \count($params)) { $params = \array_slice($params, 0, \count($values)); } elseif (\count($values) > \count($params)) { $values[] = new EnumStub(array_splice($values, \count($params)), false); $params[] = $variadic; } if (['...'] === $params) { $this->dumpKeys = false; $this->value = $values[0]->value; } else { $this->value = array_combine($params, $values); } } private static function getParameters(string $function, ?string $class): array { if (isset(self::$parameters[$k = $class.'::'.$function])) { return self::$parameters[$k]; } try { $r = null !== $class ? new \ReflectionMethod($class, $function) : new \ReflectionFunction($function); } catch (\ReflectionException) { return [null, null]; } $variadic = '...'; $params = []; foreach ($r->getParameters() as $v) { $k = '$'.$v->name; if ($v->isPassedByReference()) { $k = '&'.$k; } if ($v->isVariadic()) { $variadic .= $k; } else { $params[] = $k; } } return self::$parameters[$k] = [$variadic, $params]; } } var-dumper/Caster/FiberCaster.php 0000644 00000002055 15060133305 0012747 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Fiber related classes to array representation. * * @author Grégoire Pineau <lyrixx@lyrixx.info> */ final class FiberCaster { public static function castFiber(\Fiber $fiber, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $prefix = Caster::PREFIX_VIRTUAL; if ($fiber->isTerminated()) { $status = 'terminated'; } elseif ($fiber->isRunning()) { $status = 'running'; } elseif ($fiber->isSuspended()) { $status = 'suspended'; } elseif ($fiber->isStarted()) { $status = 'started'; } else { $status = 'not started'; } $a[$prefix.'status'] = $status; return $a; } } var-dumper/Caster/XmlResourceCaster.php 0000644 00000004770 15060133305 0014176 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts XML resources to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class XmlResourceCaster { private const XML_ERRORS = [ \XML_ERROR_NONE => 'XML_ERROR_NONE', \XML_ERROR_NO_MEMORY => 'XML_ERROR_NO_MEMORY', \XML_ERROR_SYNTAX => 'XML_ERROR_SYNTAX', \XML_ERROR_NO_ELEMENTS => 'XML_ERROR_NO_ELEMENTS', \XML_ERROR_INVALID_TOKEN => 'XML_ERROR_INVALID_TOKEN', \XML_ERROR_UNCLOSED_TOKEN => 'XML_ERROR_UNCLOSED_TOKEN', \XML_ERROR_PARTIAL_CHAR => 'XML_ERROR_PARTIAL_CHAR', \XML_ERROR_TAG_MISMATCH => 'XML_ERROR_TAG_MISMATCH', \XML_ERROR_DUPLICATE_ATTRIBUTE => 'XML_ERROR_DUPLICATE_ATTRIBUTE', \XML_ERROR_JUNK_AFTER_DOC_ELEMENT => 'XML_ERROR_JUNK_AFTER_DOC_ELEMENT', \XML_ERROR_PARAM_ENTITY_REF => 'XML_ERROR_PARAM_ENTITY_REF', \XML_ERROR_UNDEFINED_ENTITY => 'XML_ERROR_UNDEFINED_ENTITY', \XML_ERROR_RECURSIVE_ENTITY_REF => 'XML_ERROR_RECURSIVE_ENTITY_REF', \XML_ERROR_ASYNC_ENTITY => 'XML_ERROR_ASYNC_ENTITY', \XML_ERROR_BAD_CHAR_REF => 'XML_ERROR_BAD_CHAR_REF', \XML_ERROR_BINARY_ENTITY_REF => 'XML_ERROR_BINARY_ENTITY_REF', \XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF => 'XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF', \XML_ERROR_MISPLACED_XML_PI => 'XML_ERROR_MISPLACED_XML_PI', \XML_ERROR_UNKNOWN_ENCODING => 'XML_ERROR_UNKNOWN_ENCODING', \XML_ERROR_INCORRECT_ENCODING => 'XML_ERROR_INCORRECT_ENCODING', \XML_ERROR_UNCLOSED_CDATA_SECTION => 'XML_ERROR_UNCLOSED_CDATA_SECTION', \XML_ERROR_EXTERNAL_ENTITY_HANDLING => 'XML_ERROR_EXTERNAL_ENTITY_HANDLING', ]; public static function castXml($h, array $a, Stub $stub, bool $isNested): array { $a['current_byte_index'] = xml_get_current_byte_index($h); $a['current_column_number'] = xml_get_current_column_number($h); $a['current_line_number'] = xml_get_current_line_number($h); $a['error_code'] = xml_get_error_code($h); if (isset(self::XML_ERRORS[$a['error_code']])) { $a['error_code'] = new ConstStub(self::XML_ERRORS[$a['error_code']], $a['error_code']); } return $a; } } var-dumper/Caster/ResourceCaster.php 0000644 00000005663 15060133305 0013517 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts common resource types to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class ResourceCaster { public static function castCurl(\CurlHandle $h, array $a, Stub $stub, bool $isNested): array { return curl_getinfo($h); } public static function castDba($dba, array $a, Stub $stub, bool $isNested): array { $list = dba_list(); $a['file'] = $list[(int) $dba]; return $a; } public static function castProcess($process, array $a, Stub $stub, bool $isNested): array { return proc_get_status($process); } public static function castStream($stream, array $a, Stub $stub, bool $isNested): array { $a = stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested); if ($a['uri'] ?? false) { $a['uri'] = new LinkStub($a['uri']); } return $a; } public static function castStreamContext($stream, array $a, Stub $stub, bool $isNested): array { return @stream_context_get_params($stream) ?: $a; } public static function castGd($gd, array $a, Stub $stub, bool $isNested): array { $a['size'] = imagesx($gd).'x'.imagesy($gd); $a['trueColor'] = imageistruecolor($gd); return $a; } public static function castOpensslX509($h, array $a, Stub $stub, bool $isNested): array { $stub->cut = -1; $info = openssl_x509_parse($h, false); $pin = openssl_pkey_get_public($h); $pin = openssl_pkey_get_details($pin)['key']; $pin = \array_slice(explode("\n", $pin), 1, -2); $pin = base64_decode(implode('', $pin)); $pin = base64_encode(hash('sha256', $pin, true)); $a += [ 'subject' => new EnumStub(array_intersect_key($info['subject'], ['organizationName' => true, 'commonName' => true])), 'issuer' => new EnumStub(array_intersect_key($info['issuer'], ['organizationName' => true, 'commonName' => true])), 'expiry' => new ConstStub(date(\DateTimeInterface::ISO8601, $info['validTo_time_t']), $info['validTo_time_t']), 'fingerprint' => new EnumStub([ 'md5' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'md5')), 2, ':', true)), 'sha1' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha1')), 2, ':', true)), 'sha256' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha256')), 2, ':', true)), 'pin-sha256' => new ConstStub($pin), ]), ]; return $a; } } var-dumper/Caster/RdKafkaCaster.php 0000644 00000011171 15060133305 0013222 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use RdKafka\Conf; use RdKafka\Exception as RdKafkaException; use RdKafka\KafkaConsumer; use RdKafka\Message; use RdKafka\Metadata\Broker as BrokerMetadata; use RdKafka\Metadata\Collection as CollectionMetadata; use RdKafka\Metadata\Partition as PartitionMetadata; use RdKafka\Metadata\Topic as TopicMetadata; use RdKafka\Topic; use RdKafka\TopicConf; use RdKafka\TopicPartition; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts RdKafka related classes to array representation. * * @author Romain Neutron <imprec@gmail.com> */ class RdKafkaCaster { public static function castKafkaConsumer(KafkaConsumer $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; try { $assignment = $c->getAssignment(); } catch (RdKafkaException) { $assignment = []; } $a += [ $prefix.'subscription' => $c->getSubscription(), $prefix.'assignment' => $assignment, ]; $a += self::extractMetadata($c); return $a; } public static function castTopic(Topic $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'name' => $c->getName(), ]; return $a; } public static function castTopicPartition(TopicPartition $c, array $a): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'offset' => $c->getOffset(), $prefix.'partition' => $c->getPartition(), $prefix.'topic' => $c->getTopic(), ]; return $a; } public static function castMessage(Message $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'errstr' => $c->errstr(), ]; return $a; } public static function castConf(Conf $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; foreach ($c->dump() as $key => $value) { $a[$prefix.$key] = $value; } return $a; } public static function castTopicConf(TopicConf $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; foreach ($c->dump() as $key => $value) { $a[$prefix.$key] = $value; } return $a; } public static function castRdKafka(\RdKafka $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'out_q_len' => $c->getOutQLen(), ]; $a += self::extractMetadata($c); return $a; } public static function castCollectionMetadata(CollectionMetadata $c, array $a, Stub $stub, bool $isNested): array { $a += iterator_to_array($c); return $a; } public static function castTopicMetadata(TopicMetadata $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'name' => $c->getTopic(), $prefix.'partitions' => $c->getPartitions(), ]; return $a; } public static function castPartitionMetadata(PartitionMetadata $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'id' => $c->getId(), $prefix.'err' => $c->getErr(), $prefix.'leader' => $c->getLeader(), ]; return $a; } public static function castBrokerMetadata(BrokerMetadata $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'id' => $c->getId(), $prefix.'host' => $c->getHost(), $prefix.'port' => $c->getPort(), ]; return $a; } private static function extractMetadata(KafkaConsumer|\RdKafka $c): array { $prefix = Caster::PREFIX_VIRTUAL; try { $m = $c->getMetadata(true, null, 500); } catch (RdKafkaException) { return []; } return [ $prefix.'orig_broker_id' => $m->getOrigBrokerId(), $prefix.'orig_broker_name' => $m->getOrigBrokerName(), $prefix.'brokers' => $m->getBrokers(), $prefix.'topics' => $m->getTopics(), ]; } } var-dumper/Caster/PgSqlCaster.php 0000644 00000012601 15060133305 0012744 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts pqsql resources to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class PgSqlCaster { private const PARAM_CODES = [ 'server_encoding', 'client_encoding', 'is_superuser', 'session_authorization', 'DateStyle', 'TimeZone', 'IntervalStyle', 'integer_datetimes', 'application_name', 'standard_conforming_strings', ]; private const TRANSACTION_STATUS = [ \PGSQL_TRANSACTION_IDLE => 'PGSQL_TRANSACTION_IDLE', \PGSQL_TRANSACTION_ACTIVE => 'PGSQL_TRANSACTION_ACTIVE', \PGSQL_TRANSACTION_INTRANS => 'PGSQL_TRANSACTION_INTRANS', \PGSQL_TRANSACTION_INERROR => 'PGSQL_TRANSACTION_INERROR', \PGSQL_TRANSACTION_UNKNOWN => 'PGSQL_TRANSACTION_UNKNOWN', ]; private const RESULT_STATUS = [ \PGSQL_EMPTY_QUERY => 'PGSQL_EMPTY_QUERY', \PGSQL_COMMAND_OK => 'PGSQL_COMMAND_OK', \PGSQL_TUPLES_OK => 'PGSQL_TUPLES_OK', \PGSQL_COPY_OUT => 'PGSQL_COPY_OUT', \PGSQL_COPY_IN => 'PGSQL_COPY_IN', \PGSQL_BAD_RESPONSE => 'PGSQL_BAD_RESPONSE', \PGSQL_NONFATAL_ERROR => 'PGSQL_NONFATAL_ERROR', \PGSQL_FATAL_ERROR => 'PGSQL_FATAL_ERROR', ]; private const DIAG_CODES = [ 'severity' => \PGSQL_DIAG_SEVERITY, 'sqlstate' => \PGSQL_DIAG_SQLSTATE, 'message' => \PGSQL_DIAG_MESSAGE_PRIMARY, 'detail' => \PGSQL_DIAG_MESSAGE_DETAIL, 'hint' => \PGSQL_DIAG_MESSAGE_HINT, 'statement position' => \PGSQL_DIAG_STATEMENT_POSITION, 'internal position' => \PGSQL_DIAG_INTERNAL_POSITION, 'internal query' => \PGSQL_DIAG_INTERNAL_QUERY, 'context' => \PGSQL_DIAG_CONTEXT, 'file' => \PGSQL_DIAG_SOURCE_FILE, 'line' => \PGSQL_DIAG_SOURCE_LINE, 'function' => \PGSQL_DIAG_SOURCE_FUNCTION, ]; public static function castLargeObject($lo, array $a, Stub $stub, bool $isNested): array { $a['seek position'] = pg_lo_tell($lo); return $a; } public static function castLink($link, array $a, Stub $stub, bool $isNested): array { $a['status'] = pg_connection_status($link); $a['status'] = new ConstStub(\PGSQL_CONNECTION_OK === $a['status'] ? 'PGSQL_CONNECTION_OK' : 'PGSQL_CONNECTION_BAD', $a['status']); $a['busy'] = pg_connection_busy($link); $a['transaction'] = pg_transaction_status($link); if (isset(self::TRANSACTION_STATUS[$a['transaction']])) { $a['transaction'] = new ConstStub(self::TRANSACTION_STATUS[$a['transaction']], $a['transaction']); } $a['pid'] = pg_get_pid($link); $a['last error'] = pg_last_error($link); $a['last notice'] = pg_last_notice($link); $a['host'] = pg_host($link); $a['port'] = pg_port($link); $a['dbname'] = pg_dbname($link); $a['options'] = pg_options($link); $a['version'] = pg_version($link); foreach (self::PARAM_CODES as $v) { if (false !== $s = pg_parameter_status($link, $v)) { $a['param'][$v] = $s; } } $a['param']['client_encoding'] = pg_client_encoding($link); $a['param'] = new EnumStub($a['param']); return $a; } public static function castResult($result, array $a, Stub $stub, bool $isNested): array { $a['num rows'] = pg_num_rows($result); $a['status'] = pg_result_status($result); if (isset(self::RESULT_STATUS[$a['status']])) { $a['status'] = new ConstStub(self::RESULT_STATUS[$a['status']], $a['status']); } $a['command-completion tag'] = pg_result_status($result, \PGSQL_STATUS_STRING); if (-1 === $a['num rows']) { foreach (self::DIAG_CODES as $k => $v) { $a['error'][$k] = pg_result_error_field($result, $v); } } $a['affected rows'] = pg_affected_rows($result); $a['last OID'] = pg_last_oid($result); $fields = pg_num_fields($result); for ($i = 0; $i < $fields; ++$i) { $field = [ 'name' => pg_field_name($result, $i), 'table' => sprintf('%s (OID: %s)', pg_field_table($result, $i), pg_field_table($result, $i, true)), 'type' => sprintf('%s (OID: %s)', pg_field_type($result, $i), pg_field_type_oid($result, $i)), 'nullable' => (bool) pg_field_is_null($result, $i), 'storage' => pg_field_size($result, $i).' bytes', 'display' => pg_field_prtlen($result, $i).' chars', ]; if (' (OID: )' === $field['table']) { $field['table'] = null; } if ('-1 bytes' === $field['storage']) { $field['storage'] = 'variable size'; } elseif ('1 bytes' === $field['storage']) { $field['storage'] = '1 byte'; } if ('1 chars' === $field['display']) { $field['display'] = '1 char'; } $a['fields'][] = new EnumStub($field); } return $a; } } var-dumper/Caster/CutArrayStub.php 0000644 00000001276 15060133305 0013152 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * Represents a cut array. * * @author Nicolas Grekas <p@tchwork.com> */ class CutArrayStub extends CutStub { public array $preservedSubset; public function __construct(array $value, array $preservedKeys) { parent::__construct($value); $this->preservedSubset = array_intersect_key($value, array_flip($preservedKeys)); $this->cut -= \count($this->preservedSubset); } } var-dumper/Caster/FrameStub.php 0000644 00000001356 15060133305 0012451 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * Represents a single backtrace frame as returned by debug_backtrace() or Exception->getTrace(). * * @author Nicolas Grekas <p@tchwork.com> */ class FrameStub extends EnumStub { public bool $keepArgs; public bool $inTraceStub; public function __construct(array $frame, bool $keepArgs = true, bool $inTraceStub = false) { $this->value = $frame; $this->keepArgs = $keepArgs; $this->inTraceStub = $inTraceStub; } } var-dumper/Caster/ProxyManagerCaster.php 0000644 00000001330 15060133305 0014327 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use ProxyManager\Proxy\ProxyInterface; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Nicolas Grekas <p@tchwork.com> * * @final */ class ProxyManagerCaster { public static function castProxy(ProxyInterface $c, array $a, Stub $stub, bool $isNested): array { if ($parent = get_parent_class($c)) { $stub->class .= ' - '.$parent; } $stub->class .= '@proxy'; return $a; } } var-dumper/Caster/DsCaster.php 0000644 00000003066 15060133305 0012271 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Ds\Collection; use Ds\Map; use Ds\Pair; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Ds extension classes to array representation. * * @author Jáchym Toušek <enumag@gmail.com> * * @final */ class DsCaster { public static function castCollection(Collection $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'count'] = $c->count(); $a[Caster::PREFIX_VIRTUAL.'capacity'] = $c->capacity(); if (!$c instanceof Map) { $a += $c->toArray(); } return $a; } public static function castMap(Map $c, array $a, Stub $stub, bool $isNested): array { foreach ($c as $k => $v) { $a[] = new DsPairStub($k, $v); } return $a; } public static function castPair(Pair $c, array $a, Stub $stub, bool $isNested): array { foreach ($c->toArray() as $k => $v) { $a[Caster::PREFIX_VIRTUAL.$k] = $v; } return $a; } public static function castPairStub(DsPairStub $c, array $a, Stub $stub, bool $isNested): array { if ($isNested) { $stub->class = Pair::class; $stub->value = null; $stub->handle = 0; $a = $c->value; } return $a; } } var-dumper/Caster/UuidCaster.php 0000644 00000001234 15060133305 0012624 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Ramsey\Uuid\UuidInterface; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Grégoire Pineau <lyrixx@lyrixx.info> */ final class UuidCaster { public static function castRamseyUuid(UuidInterface $c, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'uuid' => (string) $c, ]; return $a; } } var-dumper/Caster/UninitializedStub.php 0000644 00000001147 15060133305 0014225 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * Represents an uninitialized property. * * @author Nicolas Grekas <p@tchwork.com> */ class UninitializedStub extends ConstStub { public function __construct(\ReflectionProperty $property) { parent::__construct('?'.($property->hasType() ? ' '.$property->getType() : ''), 'Uninitialized property'); } } var-dumper/Caster/Caster.php 0000644 00000015350 15060133305 0012001 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Helper for filtering out properties in casters. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class Caster { public const EXCLUDE_VERBOSE = 1; public const EXCLUDE_VIRTUAL = 2; public const EXCLUDE_DYNAMIC = 4; public const EXCLUDE_PUBLIC = 8; public const EXCLUDE_PROTECTED = 16; public const EXCLUDE_PRIVATE = 32; public const EXCLUDE_NULL = 64; public const EXCLUDE_EMPTY = 128; public const EXCLUDE_NOT_IMPORTANT = 256; public const EXCLUDE_STRICT = 512; public const EXCLUDE_UNINITIALIZED = 1024; public const PREFIX_VIRTUAL = "\0~\0"; public const PREFIX_DYNAMIC = "\0+\0"; public const PREFIX_PROTECTED = "\0*\0"; // usage: sprintf(Caster::PATTERN_PRIVATE, $class, $property) public const PATTERN_PRIVATE = "\0%s\0%s"; private static array $classProperties = []; /** * Casts objects to arrays and adds the dynamic property prefix. * * @param bool $hasDebugInfo Whether the __debugInfo method exists on $obj or not */ public static function castObject(object $obj, string $class, bool $hasDebugInfo = false, ?string $debugClass = null): array { if ($hasDebugInfo) { try { $debugInfo = $obj->__debugInfo(); } catch (\Throwable) { // ignore failing __debugInfo() $hasDebugInfo = false; } } $a = $obj instanceof \Closure ? [] : (array) $obj; if ($obj instanceof \__PHP_Incomplete_Class) { return $a; } $classProperties = self::$classProperties[$class] ??= self::getClassProperties(new \ReflectionClass($class)); $a = array_replace($classProperties, $a); if ($a) { $debugClass ??= get_debug_type($obj); $i = 0; $prefixedKeys = []; foreach ($a as $k => $v) { if ("\0" !== ($k[0] ?? '')) { if (!isset($classProperties[$k])) { $prefixedKeys[$i] = self::PREFIX_DYNAMIC.$k; } } elseif ($debugClass !== $class && 1 === strpos($k, $class)) { $prefixedKeys[$i] = "\0".$debugClass.strrchr($k, "\0"); } ++$i; } if ($prefixedKeys) { $keys = array_keys($a); foreach ($prefixedKeys as $i => $k) { $keys[$i] = $k; } $a = array_combine($keys, $a); } } if ($hasDebugInfo && \is_array($debugInfo)) { foreach ($debugInfo as $k => $v) { if (!isset($k[0]) || "\0" !== $k[0]) { if (\array_key_exists(self::PREFIX_DYNAMIC.$k, $a)) { continue; } $k = self::PREFIX_VIRTUAL.$k; } unset($a[$k]); $a[$k] = $v; } } return $a; } /** * Filters out the specified properties. * * By default, a single match in the $filter bit field filters properties out, following an "or" logic. * When EXCLUDE_STRICT is set, an "and" logic is applied: all bits must match for a property to be removed. * * @param array $a The array containing the properties to filter * @param int $filter A bit field of Caster::EXCLUDE_* constants specifying which properties to filter out * @param string[] $listedProperties List of properties to exclude when Caster::EXCLUDE_VERBOSE is set, and to preserve when Caster::EXCLUDE_NOT_IMPORTANT is set * @param int|null &$count Set to the number of removed properties */ public static function filter(array $a, int $filter, array $listedProperties = [], ?int &$count = 0): array { $count = 0; foreach ($a as $k => $v) { $type = self::EXCLUDE_STRICT & $filter; if (null === $v) { $type |= self::EXCLUDE_NULL & $filter; $type |= self::EXCLUDE_EMPTY & $filter; } elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || [] === $v) { $type |= self::EXCLUDE_EMPTY & $filter; } elseif ($v instanceof UninitializedStub) { $type |= self::EXCLUDE_UNINITIALIZED & $filter; } if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !\in_array($k, $listedProperties, true)) { $type |= self::EXCLUDE_NOT_IMPORTANT; } if ((self::EXCLUDE_VERBOSE & $filter) && \in_array($k, $listedProperties, true)) { $type |= self::EXCLUDE_VERBOSE; } if (!isset($k[1]) || "\0" !== $k[0]) { $type |= self::EXCLUDE_PUBLIC & $filter; } elseif ('~' === $k[1]) { $type |= self::EXCLUDE_VIRTUAL & $filter; } elseif ('+' === $k[1]) { $type |= self::EXCLUDE_DYNAMIC & $filter; } elseif ('*' === $k[1]) { $type |= self::EXCLUDE_PROTECTED & $filter; } else { $type |= self::EXCLUDE_PRIVATE & $filter; } if ((self::EXCLUDE_STRICT & $filter) ? $type === $filter : $type) { unset($a[$k]); ++$count; } } return $a; } public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array $a, Stub $stub, bool $isNested): array { if (isset($a['__PHP_Incomplete_Class_Name'])) { $stub->class .= '('.$a['__PHP_Incomplete_Class_Name'].')'; unset($a['__PHP_Incomplete_Class_Name']); } return $a; } private static function getClassProperties(\ReflectionClass $class): array { $classProperties = []; $className = $class->name; if ($parent = $class->getParentClass()) { $classProperties += self::$classProperties[$parent->name] ??= self::getClassProperties($parent); } foreach ($class->getProperties() as $p) { if ($p->isStatic()) { continue; } $classProperties[match (true) { $p->isPublic() => $p->name, $p->isProtected() => self::PREFIX_PROTECTED.$p->name, default => "\0".$className."\0".$p->name, }] = new UninitializedStub($p); } return $classProperties; } } var-dumper/Caster/PdoCaster.php 0000644 00000006761 15060133305 0012452 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts PDO related classes to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class PdoCaster { private const PDO_ATTRIBUTES = [ 'CASE' => [ \PDO::CASE_LOWER => 'LOWER', \PDO::CASE_NATURAL => 'NATURAL', \PDO::CASE_UPPER => 'UPPER', ], 'ERRMODE' => [ \PDO::ERRMODE_SILENT => 'SILENT', \PDO::ERRMODE_WARNING => 'WARNING', \PDO::ERRMODE_EXCEPTION => 'EXCEPTION', ], 'TIMEOUT', 'PREFETCH', 'AUTOCOMMIT', 'PERSISTENT', 'DRIVER_NAME', 'SERVER_INFO', 'ORACLE_NULLS' => [ \PDO::NULL_NATURAL => 'NATURAL', \PDO::NULL_EMPTY_STRING => 'EMPTY_STRING', \PDO::NULL_TO_STRING => 'TO_STRING', ], 'CLIENT_VERSION', 'SERVER_VERSION', 'STATEMENT_CLASS', 'EMULATE_PREPARES', 'CONNECTION_STATUS', 'STRINGIFY_FETCHES', 'DEFAULT_FETCH_MODE' => [ \PDO::FETCH_ASSOC => 'ASSOC', \PDO::FETCH_BOTH => 'BOTH', \PDO::FETCH_LAZY => 'LAZY', \PDO::FETCH_NUM => 'NUM', \PDO::FETCH_OBJ => 'OBJ', ], ]; public static function castPdo(\PDO $c, array $a, Stub $stub, bool $isNested): array { $attr = []; $errmode = $c->getAttribute(\PDO::ATTR_ERRMODE); $c->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); foreach (self::PDO_ATTRIBUTES as $k => $v) { if (!isset($k[0])) { $k = $v; $v = []; } try { $attr[$k] = 'ERRMODE' === $k ? $errmode : $c->getAttribute(\constant('PDO::ATTR_'.$k)); if ($v && isset($v[$attr[$k]])) { $attr[$k] = new ConstStub($v[$attr[$k]], $attr[$k]); } } catch (\Exception) { } } if (isset($attr[$k = 'STATEMENT_CLASS'][1])) { if ($attr[$k][1]) { $attr[$k][1] = new ArgsStub($attr[$k][1], '__construct', $attr[$k][0]); } $attr[$k][0] = new ClassStub($attr[$k][0]); } $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'inTransaction' => method_exists($c, 'inTransaction'), $prefix.'errorInfo' => $c->errorInfo(), $prefix.'attributes' => new EnumStub($attr), ]; if ($a[$prefix.'inTransaction']) { $a[$prefix.'inTransaction'] = $c->inTransaction(); } else { unset($a[$prefix.'inTransaction']); } if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) { unset($a[$prefix.'errorInfo']); } $c->setAttribute(\PDO::ATTR_ERRMODE, $errmode); return $a; } public static function castPdoStatement(\PDOStatement $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a[$prefix.'errorInfo'] = $c->errorInfo(); if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) { unset($a[$prefix.'errorInfo']); } return $a; } } var-dumper/Caster/SymfonyCaster.php 0000644 00000007012 15060133305 0013362 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Uid\Ulid; use Symfony\Component\Uid\Uuid; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarExporter\Internal\LazyObjectState; /** * @final */ class SymfonyCaster { private const REQUEST_GETTERS = [ 'pathInfo' => 'getPathInfo', 'requestUri' => 'getRequestUri', 'baseUrl' => 'getBaseUrl', 'basePath' => 'getBasePath', 'method' => 'getMethod', 'format' => 'getRequestFormat', ]; public static function castRequest(Request $request, array $a, Stub $stub, bool $isNested): array { $clone = null; foreach (self::REQUEST_GETTERS as $prop => $getter) { $key = Caster::PREFIX_PROTECTED.$prop; if (\array_key_exists($key, $a) && null === $a[$key]) { $clone ??= clone $request; $a[Caster::PREFIX_VIRTUAL.$prop] = $clone->{$getter}(); } } return $a; } public static function castHttpClient($client, array $a, Stub $stub, bool $isNested): array { $multiKey = sprintf("\0%s\0multi", $client::class); if (isset($a[$multiKey])) { $a[$multiKey] = new CutStub($a[$multiKey]); } return $a; } public static function castHttpClientResponse($response, array $a, Stub $stub, bool $isNested): array { $stub->cut += \count($a); $a = []; foreach ($response->getInfo() as $k => $v) { $a[Caster::PREFIX_VIRTUAL.$k] = $v; } return $a; } public static function castLazyObjectState($state, array $a, Stub $stub, bool $isNested): array { if (!$isNested) { return $a; } $stub->cut += \count($a) - 1; $instance = $a['realInstance'] ?? null; $a = ['status' => new ConstStub(match ($a['status']) { LazyObjectState::STATUS_INITIALIZED_FULL => 'INITIALIZED_FULL', LazyObjectState::STATUS_INITIALIZED_PARTIAL => 'INITIALIZED_PARTIAL', LazyObjectState::STATUS_UNINITIALIZED_FULL => 'UNINITIALIZED_FULL', LazyObjectState::STATUS_UNINITIALIZED_PARTIAL => 'UNINITIALIZED_PARTIAL', }, $a['status'])]; if ($instance) { $a['realInstance'] = $instance; --$stub->cut; } return $a; } public static function castUuid(Uuid $uuid, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'toBase58'] = $uuid->toBase58(); $a[Caster::PREFIX_VIRTUAL.'toBase32'] = $uuid->toBase32(); // symfony/uid >= 5.3 if (method_exists($uuid, 'getDateTime')) { $a[Caster::PREFIX_VIRTUAL.'time'] = $uuid->getDateTime()->format('Y-m-d H:i:s.u \U\T\C'); } return $a; } public static function castUlid(Ulid $ulid, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'toBase58'] = $ulid->toBase58(); $a[Caster::PREFIX_VIRTUAL.'toRfc4122'] = $ulid->toRfc4122(); // symfony/uid >= 5.3 if (method_exists($ulid, 'getDateTime')) { $a[Caster::PREFIX_VIRTUAL.'time'] = $ulid->getDateTime()->format('Y-m-d H:i:s.v \U\T\C'); } return $a; } } var-dumper/Caster/DateCaster.php 0000644 00000011454 15060133305 0012600 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts DateTimeInterface related classes to array representation. * * @author Dany Maillard <danymaillard93b@gmail.com> * * @final */ class DateCaster { private const PERIOD_LIMIT = 3; public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, bool $isNested, int $filter): array { $prefix = Caster::PREFIX_VIRTUAL; $location = $d->getTimezone() ? $d->getTimezone()->getLocation() : null; $fromNow = (new \DateTimeImmutable())->diff($d); $title = $d->format('l, F j, Y') ."\n".self::formatInterval($fromNow).' from now' .($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '') ; unset( $a[Caster::PREFIX_DYNAMIC.'date'], $a[Caster::PREFIX_DYNAMIC.'timezone'], $a[Caster::PREFIX_DYNAMIC.'timezone_type'] ); $a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title); $stub->class .= $d->format(' @U'); return $a; } public static function castInterval(\DateInterval $interval, array $a, Stub $stub, bool $isNested, int $filter): array { $now = new \DateTimeImmutable('@0', new \DateTimeZone('UTC')); $numberOfSeconds = $now->add($interval)->getTimestamp() - $now->getTimestamp(); $title = number_format($numberOfSeconds, 0, '.', ' ').'s'; $i = [Caster::PREFIX_VIRTUAL.'interval' => new ConstStub(self::formatInterval($interval), $title)]; return $filter & Caster::EXCLUDE_VERBOSE ? $i : $i + $a; } private static function formatInterval(\DateInterval $i): string { $format = '%R '; if (0 === $i->y && 0 === $i->m && ($i->h >= 24 || $i->i >= 60 || $i->s >= 60)) { $d = new \DateTimeImmutable('@0', new \DateTimeZone('UTC')); $i = $d->diff($d->add($i)); // recalculate carry over points $format .= 0 < $i->days ? '%ad ' : ''; } else { $format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : ''); } $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : ''; $format = '%R ' === $format ? '0s' : $format; return $i->format(rtrim($format)); } public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stub, bool $isNested, int $filter): array { $location = $timeZone->getLocation(); $formatted = (new \DateTimeImmutable('now', $timeZone))->format($location ? 'e (P)' : 'P'); $title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code']) : ''; $z = [Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title)]; return $filter & Caster::EXCLUDE_VERBOSE ? $z : $z + $a; } public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, bool $isNested, int $filter): array { $dates = []; foreach (clone $p as $i => $d) { if (self::PERIOD_LIMIT === $i) { $now = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); $dates[] = sprintf('%s more', ($end = $p->getEndDate()) ? ceil(($end->format('U.u') - $d->format('U.u')) / ((int) $now->add($p->getDateInterval())->format('U.u') - (int) $now->format('U.u'))) : $p->recurrences - $i ); break; } $dates[] = sprintf('%s) %s', $i + 1, self::formatDateTime($d)); } $period = sprintf( 'every %s, from %s%s %s', self::formatInterval($p->getDateInterval()), $p->include_start_date ? '[' : ']', self::formatDateTime($p->getStartDate()), ($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end).($p->include_end_date ? ']' : '[') : 'recurring '.$p->recurrences.' time/s' ); $p = [Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates))]; return $filter & Caster::EXCLUDE_VERBOSE ? $p : $p + $a; } private static function formatDateTime(\DateTimeInterface $d, string $extra = ''): string { return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra); } private static function formatSeconds(string $s, string $us): string { return sprintf('%02d.%s', $s, 0 === ($len = \strlen($t = rtrim($us, '0'))) ? '0' : ($len <= 3 ? str_pad($t, 3, '0') : $us)); } } var-dumper/Caster/ImgStub.php 0000644 00000001175 15060133305 0012132 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * @author Grégoire Pineau <lyrixx@lyrixx.info> */ class ImgStub extends ConstStub { public function __construct(string $data, string $contentType, string $size = '') { $this->value = ''; $this->attr['img-data'] = $data; $this->attr['img-size'] = $size; $this->attr['content-type'] = $contentType; } } var-dumper/Caster/ImagineCaster.php 0000644 00000001665 15060133305 0013277 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Imagine\Image\ImageInterface; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Grégoire Pineau <lyrixx@lyrixx.info> */ final class ImagineCaster { public static function castImage(ImageInterface $c, array $a, Stub $stub, bool $isNested): array { $imgData = $c->get('png'); if (\strlen($imgData) > 1 * 1000 * 1000) { $a += [ Caster::PREFIX_VIRTUAL.'image' => new ConstStub($c->getSize()), ]; } else { $a += [ Caster::PREFIX_VIRTUAL.'image' => new ImgStub($imgData, 'image/png', $c->getSize()), ]; } return $a; } } var-dumper/Caster/MysqliCaster.php 0000644 00000001263 15060133305 0013176 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Nicolas Grekas <p@tchwork.com> * * @internal */ final class MysqliCaster { public static function castMysqliDriver(\mysqli_driver $c, array $a, Stub $stub, bool $isNested): array { foreach ($a as $k => $v) { if (isset($c->$k)) { $a[$k] = $c->$k; } } return $a; } } var-dumper/Caster/IntlCaster.php 0000644 00000021407 15060133305 0012630 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Nicolas Grekas <p@tchwork.com> * @author Jan Schädlich <jan.schaedlich@sensiolabs.de> * * @final */ class IntlCaster { public static function castMessageFormatter(\MessageFormatter $c, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), ]; return self::castError($c, $a); } public static function castNumberFormatter(\NumberFormatter $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $a += [ Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), ]; if ($filter & Caster::EXCLUDE_VERBOSE) { $stub->cut += 3; return self::castError($c, $a); } $a += [ Caster::PREFIX_VIRTUAL.'attributes' => new EnumStub( [ 'PARSE_INT_ONLY' => $c->getAttribute(\NumberFormatter::PARSE_INT_ONLY), 'GROUPING_USED' => $c->getAttribute(\NumberFormatter::GROUPING_USED), 'DECIMAL_ALWAYS_SHOWN' => $c->getAttribute(\NumberFormatter::DECIMAL_ALWAYS_SHOWN), 'MAX_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_INTEGER_DIGITS), 'MIN_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_INTEGER_DIGITS), 'INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::INTEGER_DIGITS), 'MAX_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_FRACTION_DIGITS), 'MIN_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_FRACTION_DIGITS), 'FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::FRACTION_DIGITS), 'MULTIPLIER' => $c->getAttribute(\NumberFormatter::MULTIPLIER), 'GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::GROUPING_SIZE), 'ROUNDING_MODE' => $c->getAttribute(\NumberFormatter::ROUNDING_MODE), 'ROUNDING_INCREMENT' => $c->getAttribute(\NumberFormatter::ROUNDING_INCREMENT), 'FORMAT_WIDTH' => $c->getAttribute(\NumberFormatter::FORMAT_WIDTH), 'PADDING_POSITION' => $c->getAttribute(\NumberFormatter::PADDING_POSITION), 'SECONDARY_GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::SECONDARY_GROUPING_SIZE), 'SIGNIFICANT_DIGITS_USED' => $c->getAttribute(\NumberFormatter::SIGNIFICANT_DIGITS_USED), 'MIN_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_SIGNIFICANT_DIGITS), 'MAX_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_SIGNIFICANT_DIGITS), 'LENIENT_PARSE' => $c->getAttribute(\NumberFormatter::LENIENT_PARSE), ] ), Caster::PREFIX_VIRTUAL.'text_attributes' => new EnumStub( [ 'POSITIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_PREFIX), 'POSITIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_SUFFIX), 'NEGATIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_PREFIX), 'NEGATIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_SUFFIX), 'PADDING_CHARACTER' => $c->getTextAttribute(\NumberFormatter::PADDING_CHARACTER), 'CURRENCY_CODE' => $c->getTextAttribute(\NumberFormatter::CURRENCY_CODE), 'DEFAULT_RULESET' => $c->getTextAttribute(\NumberFormatter::DEFAULT_RULESET), 'PUBLIC_RULESETS' => $c->getTextAttribute(\NumberFormatter::PUBLIC_RULESETS), ] ), Caster::PREFIX_VIRTUAL.'symbols' => new EnumStub( [ 'DECIMAL_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL), 'GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL), 'PATTERN_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::PATTERN_SEPARATOR_SYMBOL), 'PERCENT_SYMBOL' => $c->getSymbol(\NumberFormatter::PERCENT_SYMBOL), 'ZERO_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::ZERO_DIGIT_SYMBOL), 'DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::DIGIT_SYMBOL), 'MINUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::MINUS_SIGN_SYMBOL), 'PLUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::PLUS_SIGN_SYMBOL), 'CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::CURRENCY_SYMBOL), 'INTL_CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::INTL_CURRENCY_SYMBOL), 'MONETARY_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL), 'EXPONENTIAL_SYMBOL' => $c->getSymbol(\NumberFormatter::EXPONENTIAL_SYMBOL), 'PERMILL_SYMBOL' => $c->getSymbol(\NumberFormatter::PERMILL_SYMBOL), 'PAD_ESCAPE_SYMBOL' => $c->getSymbol(\NumberFormatter::PAD_ESCAPE_SYMBOL), 'INFINITY_SYMBOL' => $c->getSymbol(\NumberFormatter::INFINITY_SYMBOL), 'NAN_SYMBOL' => $c->getSymbol(\NumberFormatter::NAN_SYMBOL), 'SIGNIFICANT_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL), 'MONETARY_GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL), ] ), ]; return self::castError($c, $a); } public static function castIntlTimeZone(\IntlTimeZone $c, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'display_name' => $c->getDisplayName(), Caster::PREFIX_VIRTUAL.'id' => $c->getID(), Caster::PREFIX_VIRTUAL.'raw_offset' => $c->getRawOffset(), ]; if ($c->useDaylightTime()) { $a += [ Caster::PREFIX_VIRTUAL.'dst_savings' => $c->getDSTSavings(), ]; } return self::castError($c, $a); } public static function castIntlCalendar(\IntlCalendar $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $a += [ Caster::PREFIX_VIRTUAL.'type' => $c->getType(), Caster::PREFIX_VIRTUAL.'first_day_of_week' => $c->getFirstDayOfWeek(), Caster::PREFIX_VIRTUAL.'minimal_days_in_first_week' => $c->getMinimalDaysInFirstWeek(), Caster::PREFIX_VIRTUAL.'repeated_wall_time_option' => $c->getRepeatedWallTimeOption(), Caster::PREFIX_VIRTUAL.'skipped_wall_time_option' => $c->getSkippedWallTimeOption(), Caster::PREFIX_VIRTUAL.'time' => $c->getTime(), Caster::PREFIX_VIRTUAL.'in_daylight_time' => $c->inDaylightTime(), Caster::PREFIX_VIRTUAL.'is_lenient' => $c->isLenient(), Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(), ]; return self::castError($c, $a); } public static function castIntlDateFormatter(\IntlDateFormatter $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $a += [ Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), Caster::PREFIX_VIRTUAL.'calendar' => $c->getCalendar(), Caster::PREFIX_VIRTUAL.'time_zone_id' => $c->getTimeZoneId(), Caster::PREFIX_VIRTUAL.'time_type' => $c->getTimeType(), Caster::PREFIX_VIRTUAL.'date_type' => $c->getDateType(), Caster::PREFIX_VIRTUAL.'calendar_object' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getCalendarObject()) : $c->getCalendarObject(), Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(), ]; return self::castError($c, $a); } private static function castError(object $c, array $a): array { if ($errorCode = $c->getErrorCode()) { $a += [ Caster::PREFIX_VIRTUAL.'error_code' => $errorCode, Caster::PREFIX_VIRTUAL.'error_message' => $c->getErrorMessage(), ]; } return $a; } } var-dumper/Caster/MemcachedCaster.php 0000644 00000004365 15060133305 0013574 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Jan Schädlich <jan.schaedlich@sensiolabs.de> * * @final */ class MemcachedCaster { private static array $optionConstants; private static array $defaultOptions; public static function castMemcached(\Memcached $c, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'servers' => $c->getServerList(), Caster::PREFIX_VIRTUAL.'options' => new EnumStub( self::getNonDefaultOptions($c) ), ]; return $a; } private static function getNonDefaultOptions(\Memcached $c): array { self::$defaultOptions ??= self::discoverDefaultOptions(); self::$optionConstants ??= self::getOptionConstants(); $nonDefaultOptions = []; foreach (self::$optionConstants as $constantKey => $value) { if (self::$defaultOptions[$constantKey] !== $option = $c->getOption($value)) { $nonDefaultOptions[$constantKey] = $option; } } return $nonDefaultOptions; } private static function discoverDefaultOptions(): array { $defaultMemcached = new \Memcached(); $defaultMemcached->addServer('127.0.0.1', 11211); $defaultOptions = []; self::$optionConstants ??= self::getOptionConstants(); foreach (self::$optionConstants as $constantKey => $value) { $defaultOptions[$constantKey] = $defaultMemcached->getOption($value); } return $defaultOptions; } private static function getOptionConstants(): array { $reflectedMemcached = new \ReflectionClass(\Memcached::class); $optionConstants = []; foreach ($reflectedMemcached->getConstants() as $constantKey => $value) { if (str_starts_with($constantKey, 'OPT_')) { $optionConstants[$constantKey] = $value; } } return $optionConstants; } } var-dumper/Caster/LinkStub.php 0000644 00000006453 15060133305 0012317 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; /** * Represents a file or a URL. * * @author Nicolas Grekas <p@tchwork.com> */ class LinkStub extends ConstStub { public bool $inVendor = false; private static array $vendorRoots; private static array $composerRoots = []; public function __construct(string $label, int $line = 0, ?string $href = null) { $this->value = $label; if (!\is_string($href ??= $label)) { return; } if (str_starts_with($href, 'file://')) { if ($href === $label) { $label = substr($label, 7); } $href = substr($href, 7); } elseif (str_contains($href, '://')) { $this->attr['href'] = $href; return; } if (!is_file($href)) { return; } if ($line) { $this->attr['line'] = $line; } if ($label !== $this->attr['file'] = realpath($href) ?: $href) { return; } if ($composerRoot = $this->getComposerRoot($href, $this->inVendor)) { $this->attr['ellipsis'] = \strlen($href) - \strlen($composerRoot) + 1; $this->attr['ellipsis-type'] = 'path'; $this->attr['ellipsis-tail'] = 1 + ($this->inVendor ? 2 + \strlen(implode('', \array_slice(explode(\DIRECTORY_SEPARATOR, substr($href, 1 - $this->attr['ellipsis'])), 0, 2))) : 0); } elseif (3 < \count($ellipsis = explode(\DIRECTORY_SEPARATOR, $href))) { $this->attr['ellipsis'] = 2 + \strlen(implode('', \array_slice($ellipsis, -2))); $this->attr['ellipsis-type'] = 'path'; $this->attr['ellipsis-tail'] = 1; } } private function getComposerRoot(string $file, bool &$inVendor): string|false { if (!isset(self::$vendorRoots)) { self::$vendorRoots = []; foreach (get_declared_classes() as $class) { if ('C' === $class[0] && str_starts_with($class, 'ComposerAutoloaderInit')) { $r = new \ReflectionClass($class); $v = \dirname($r->getFileName(), 2); if (is_file($v.'/composer/installed.json')) { self::$vendorRoots[] = $v.\DIRECTORY_SEPARATOR; } } } } $inVendor = false; if (isset(self::$composerRoots[$dir = \dirname($file)])) { return self::$composerRoots[$dir]; } foreach (self::$vendorRoots as $root) { if ($inVendor = str_starts_with($file, $root)) { return $root; } } $parent = $dir; while (!@is_file($parent.'/composer.json')) { if (!@file_exists($parent)) { // open_basedir restriction in effect break; } if ($parent === \dirname($parent)) { return self::$composerRoots[$dir] = false; } $parent = \dirname($parent); } return self::$composerRoots[$dir] = $parent.\DIRECTORY_SEPARATOR; } } var-dumper/Caster/AmqpCaster.php 0000644 00000015111 15060133305 0012613 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Amqp related classes to array representation. * * @author Grégoire Pineau <lyrixx@lyrixx.info> * * @final */ class AmqpCaster { private const FLAGS = [ \AMQP_DURABLE => 'AMQP_DURABLE', \AMQP_PASSIVE => 'AMQP_PASSIVE', \AMQP_EXCLUSIVE => 'AMQP_EXCLUSIVE', \AMQP_AUTODELETE => 'AMQP_AUTODELETE', \AMQP_INTERNAL => 'AMQP_INTERNAL', \AMQP_NOLOCAL => 'AMQP_NOLOCAL', \AMQP_AUTOACK => 'AMQP_AUTOACK', \AMQP_IFEMPTY => 'AMQP_IFEMPTY', \AMQP_IFUNUSED => 'AMQP_IFUNUSED', \AMQP_MANDATORY => 'AMQP_MANDATORY', \AMQP_IMMEDIATE => 'AMQP_IMMEDIATE', \AMQP_MULTIPLE => 'AMQP_MULTIPLE', \AMQP_NOWAIT => 'AMQP_NOWAIT', \AMQP_REQUEUE => 'AMQP_REQUEUE', ]; private const EXCHANGE_TYPES = [ \AMQP_EX_TYPE_DIRECT => 'AMQP_EX_TYPE_DIRECT', \AMQP_EX_TYPE_FANOUT => 'AMQP_EX_TYPE_FANOUT', \AMQP_EX_TYPE_TOPIC => 'AMQP_EX_TYPE_TOPIC', \AMQP_EX_TYPE_HEADERS => 'AMQP_EX_TYPE_HEADERS', ]; public static function castConnection(\AMQPConnection $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'is_connected' => $c->isConnected(), ]; // Recent version of the extension already expose private properties if (isset($a["\x00AMQPConnection\x00login"])) { return $a; } // BC layer in the amqp lib if (method_exists($c, 'getReadTimeout')) { $timeout = $c->getReadTimeout(); } else { $timeout = $c->getTimeout(); } $a += [ $prefix.'is_connected' => $c->isConnected(), $prefix.'login' => $c->getLogin(), $prefix.'password' => $c->getPassword(), $prefix.'host' => $c->getHost(), $prefix.'vhost' => $c->getVhost(), $prefix.'port' => $c->getPort(), $prefix.'read_timeout' => $timeout, ]; return $a; } public static function castChannel(\AMQPChannel $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'is_connected' => $c->isConnected(), $prefix.'channel_id' => $c->getChannelId(), ]; // Recent version of the extension already expose private properties if (isset($a["\x00AMQPChannel\x00connection"])) { return $a; } $a += [ $prefix.'connection' => $c->getConnection(), $prefix.'prefetch_size' => $c->getPrefetchSize(), $prefix.'prefetch_count' => $c->getPrefetchCount(), ]; return $a; } public static function castQueue(\AMQPQueue $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'flags' => self::extractFlags($c->getFlags()), ]; // Recent version of the extension already expose private properties if (isset($a["\x00AMQPQueue\x00name"])) { return $a; } $a += [ $prefix.'connection' => $c->getConnection(), $prefix.'channel' => $c->getChannel(), $prefix.'name' => $c->getName(), $prefix.'arguments' => $c->getArguments(), ]; return $a; } public static function castExchange(\AMQPExchange $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $a += [ $prefix.'flags' => self::extractFlags($c->getFlags()), ]; $type = isset(self::EXCHANGE_TYPES[$c->getType()]) ? new ConstStub(self::EXCHANGE_TYPES[$c->getType()], $c->getType()) : $c->getType(); // Recent version of the extension already expose private properties if (isset($a["\x00AMQPExchange\x00name"])) { $a["\x00AMQPExchange\x00type"] = $type; return $a; } $a += [ $prefix.'connection' => $c->getConnection(), $prefix.'channel' => $c->getChannel(), $prefix.'name' => $c->getName(), $prefix.'type' => $type, $prefix.'arguments' => $c->getArguments(), ]; return $a; } public static function castEnvelope(\AMQPEnvelope $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $prefix = Caster::PREFIX_VIRTUAL; $deliveryMode = new ConstStub($c->getDeliveryMode().(2 === $c->getDeliveryMode() ? ' (persistent)' : ' (non-persistent)'), $c->getDeliveryMode()); // Recent version of the extension already expose private properties if (isset($a["\x00AMQPEnvelope\x00body"])) { $a["\0AMQPEnvelope\0delivery_mode"] = $deliveryMode; return $a; } if (!($filter & Caster::EXCLUDE_VERBOSE)) { $a += [$prefix.'body' => $c->getBody()]; } $a += [ $prefix.'delivery_tag' => $c->getDeliveryTag(), $prefix.'is_redelivery' => $c->isRedelivery(), $prefix.'exchange_name' => $c->getExchangeName(), $prefix.'routing_key' => $c->getRoutingKey(), $prefix.'content_type' => $c->getContentType(), $prefix.'content_encoding' => $c->getContentEncoding(), $prefix.'headers' => $c->getHeaders(), $prefix.'delivery_mode' => $deliveryMode, $prefix.'priority' => $c->getPriority(), $prefix.'correlation_id' => $c->getCorrelationId(), $prefix.'reply_to' => $c->getReplyTo(), $prefix.'expiration' => $c->getExpiration(), $prefix.'message_id' => $c->getMessageId(), $prefix.'timestamp' => $c->getTimeStamp(), $prefix.'type' => $c->getType(), $prefix.'user_id' => $c->getUserId(), $prefix.'app_id' => $c->getAppId(), ]; return $a; } private static function extractFlags(int $flags): ConstStub { $flagsArray = []; foreach (self::FLAGS as $value => $name) { if ($flags & $value) { $flagsArray[] = $name; } } if (!$flagsArray) { $flagsArray = ['AMQP_NOPARAM']; } return new ConstStub(implode('|', $flagsArray), $flags); } } var-dumper/Caster/RedisCaster.php 0000644 00000012543 15060133305 0012771 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Relay\Relay; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Redis class from ext-redis to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class RedisCaster { private const SERIALIZERS = [ 0 => 'NONE', // Redis::SERIALIZER_NONE 1 => 'PHP', // Redis::SERIALIZER_PHP 2 => 'IGBINARY', // Optional Redis::SERIALIZER_IGBINARY ]; private const MODES = [ 0 => 'ATOMIC', // Redis::ATOMIC 1 => 'MULTI', // Redis::MULTI 2 => 'PIPELINE', // Redis::PIPELINE ]; private const COMPRESSION_MODES = [ 0 => 'NONE', // Redis::COMPRESSION_NONE 1 => 'LZF', // Redis::COMPRESSION_LZF ]; private const FAILOVER_OPTIONS = [ \RedisCluster::FAILOVER_NONE => 'NONE', \RedisCluster::FAILOVER_ERROR => 'ERROR', \RedisCluster::FAILOVER_DISTRIBUTE => 'DISTRIBUTE', \RedisCluster::FAILOVER_DISTRIBUTE_SLAVES => 'DISTRIBUTE_SLAVES', ]; public static function castRedis(\Redis|Relay $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; if (!$connected = $c->isConnected()) { return $a + [ $prefix.'isConnected' => $connected, ]; } $mode = $c->getMode(); return $a + [ $prefix.'isConnected' => $connected, $prefix.'host' => $c->getHost(), $prefix.'port' => $c->getPort(), $prefix.'auth' => $c->getAuth(), $prefix.'mode' => isset(self::MODES[$mode]) ? new ConstStub(self::MODES[$mode], $mode) : $mode, $prefix.'dbNum' => $c->getDbNum(), $prefix.'timeout' => $c->getTimeout(), $prefix.'lastError' => $c->getLastError(), $prefix.'persistentId' => $c->getPersistentID(), $prefix.'options' => self::getRedisOptions($c), ]; } public static function castRedisArray(\RedisArray $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; return $a + [ $prefix.'hosts' => $c->_hosts(), $prefix.'function' => ClassStub::wrapCallable($c->_function()), $prefix.'lastError' => $c->getLastError(), $prefix.'options' => self::getRedisOptions($c), ]; } public static function castRedisCluster(\RedisCluster $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; $failover = $c->getOption(\RedisCluster::OPT_SLAVE_FAILOVER); $a += [ $prefix.'_masters' => $c->_masters(), $prefix.'_redir' => $c->_redir(), $prefix.'mode' => new ConstStub($c->getMode() ? 'MULTI' : 'ATOMIC', $c->getMode()), $prefix.'lastError' => $c->getLastError(), $prefix.'options' => self::getRedisOptions($c, [ 'SLAVE_FAILOVER' => isset(self::FAILOVER_OPTIONS[$failover]) ? new ConstStub(self::FAILOVER_OPTIONS[$failover], $failover) : $failover, ]), ]; return $a; } private static function getRedisOptions(\Redis|Relay|\RedisArray|\RedisCluster $redis, array $options = []): EnumStub { $serializer = $redis->getOption(\defined('Redis::OPT_SERIALIZER') ? \Redis::OPT_SERIALIZER : 1); if (\is_array($serializer)) { foreach ($serializer as &$v) { if (isset(self::SERIALIZERS[$v])) { $v = new ConstStub(self::SERIALIZERS[$v], $v); } } } elseif (isset(self::SERIALIZERS[$serializer])) { $serializer = new ConstStub(self::SERIALIZERS[$serializer], $serializer); } $compression = \defined('Redis::OPT_COMPRESSION') ? $redis->getOption(\Redis::OPT_COMPRESSION) : 0; if (\is_array($compression)) { foreach ($compression as &$v) { if (isset(self::COMPRESSION_MODES[$v])) { $v = new ConstStub(self::COMPRESSION_MODES[$v], $v); } } } elseif (isset(self::COMPRESSION_MODES[$compression])) { $compression = new ConstStub(self::COMPRESSION_MODES[$compression], $compression); } $retry = \defined('Redis::OPT_SCAN') ? $redis->getOption(\Redis::OPT_SCAN) : 0; if (\is_array($retry)) { foreach ($retry as &$v) { $v = new ConstStub($v ? 'RETRY' : 'NORETRY', $v); } } else { $retry = new ConstStub($retry ? 'RETRY' : 'NORETRY', $retry); } $options += [ 'TCP_KEEPALIVE' => \defined('Redis::OPT_TCP_KEEPALIVE') ? $redis->getOption(\Redis::OPT_TCP_KEEPALIVE) : Relay::OPT_TCP_KEEPALIVE, 'READ_TIMEOUT' => $redis->getOption(\defined('Redis::OPT_READ_TIMEOUT') ? \Redis::OPT_READ_TIMEOUT : Relay::OPT_READ_TIMEOUT), 'COMPRESSION' => $compression, 'SERIALIZER' => $serializer, 'PREFIX' => $redis->getOption(\defined('Redis::OPT_PREFIX') ? \Redis::OPT_PREFIX : Relay::OPT_PREFIX), 'SCAN' => $retry, ]; return new EnumStub($options); } } var-dumper/Caster/XmlReaderCaster.php 0000644 00000006471 15060133305 0013611 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts XmlReader class to array representation. * * @author Baptiste Clavié <clavie.b@gmail.com> * * @final */ class XmlReaderCaster { private const NODE_TYPES = [ \XMLReader::NONE => 'NONE', \XMLReader::ELEMENT => 'ELEMENT', \XMLReader::ATTRIBUTE => 'ATTRIBUTE', \XMLReader::TEXT => 'TEXT', \XMLReader::CDATA => 'CDATA', \XMLReader::ENTITY_REF => 'ENTITY_REF', \XMLReader::ENTITY => 'ENTITY', \XMLReader::PI => 'PI (Processing Instruction)', \XMLReader::COMMENT => 'COMMENT', \XMLReader::DOC => 'DOC', \XMLReader::DOC_TYPE => 'DOC_TYPE', \XMLReader::DOC_FRAGMENT => 'DOC_FRAGMENT', \XMLReader::NOTATION => 'NOTATION', \XMLReader::WHITESPACE => 'WHITESPACE', \XMLReader::SIGNIFICANT_WHITESPACE => 'SIGNIFICANT_WHITESPACE', \XMLReader::END_ELEMENT => 'END_ELEMENT', \XMLReader::END_ENTITY => 'END_ENTITY', \XMLReader::XML_DECLARATION => 'XML_DECLARATION', ]; public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, bool $isNested): array { try { $properties = [ 'LOADDTD' => @$reader->getParserProperty(\XMLReader::LOADDTD), 'DEFAULTATTRS' => @$reader->getParserProperty(\XMLReader::DEFAULTATTRS), 'VALIDATE' => @$reader->getParserProperty(\XMLReader::VALIDATE), 'SUBST_ENTITIES' => @$reader->getParserProperty(\XMLReader::SUBST_ENTITIES), ]; } catch (\Error) { $properties = [ 'LOADDTD' => false, 'DEFAULTATTRS' => false, 'VALIDATE' => false, 'SUBST_ENTITIES' => false, ]; } $props = Caster::PREFIX_VIRTUAL.'parserProperties'; $info = [ 'localName' => $reader->localName, 'prefix' => $reader->prefix, 'nodeType' => new ConstStub(self::NODE_TYPES[$reader->nodeType], $reader->nodeType), 'depth' => $reader->depth, 'isDefault' => $reader->isDefault, 'isEmptyElement' => \XMLReader::NONE === $reader->nodeType ? null : $reader->isEmptyElement, 'xmlLang' => $reader->xmlLang, 'attributeCount' => $reader->attributeCount, 'value' => $reader->value, 'namespaceURI' => $reader->namespaceURI, 'baseURI' => $reader->baseURI ? new LinkStub($reader->baseURI) : $reader->baseURI, $props => $properties, ]; if ($info[$props] = Caster::filter($info[$props], Caster::EXCLUDE_EMPTY, [], $count)) { $info[$props] = new EnumStub($info[$props]); $info[$props]->cut = $count; } $a = Caster::filter($a, Caster::EXCLUDE_UNINITIALIZED, [], $count); $info = Caster::filter($info, Caster::EXCLUDE_EMPTY, [], $count); // +2 because hasValue and hasAttributes are always filtered $stub->cut += $count + 2; return $a + $info; } } var-dumper/Caster/ReflectionCaster.php 0000644 00000035452 15060133305 0014021 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts Reflector related classes to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class ReflectionCaster { public const UNSET_CLOSURE_FILE_INFO = ['Closure' => __CLASS__.'::unsetClosureFileInfo']; private const EXTRA_MAP = [ 'docComment' => 'getDocComment', 'extension' => 'getExtensionName', 'isDisabled' => 'isDisabled', 'isDeprecated' => 'isDeprecated', 'isInternal' => 'isInternal', 'isUserDefined' => 'isUserDefined', 'isGenerator' => 'isGenerator', 'isVariadic' => 'isVariadic', ]; public static function castClosure(\Closure $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $prefix = Caster::PREFIX_VIRTUAL; $c = new \ReflectionFunction($c); $a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter); if (!$c->isAnonymous()) { $stub->class = isset($a[$prefix.'class']) ? $a[$prefix.'class']->value.'::'.$c->name : $c->name; unset($a[$prefix.'class']); } unset($a[$prefix.'extra']); $stub->class .= self::getSignature($a); if ($f = $c->getFileName()) { $stub->attr['file'] = $f; $stub->attr['line'] = $c->getStartLine(); } unset($a[$prefix.'parameters']); if ($filter & Caster::EXCLUDE_VERBOSE) { $stub->cut += ($c->getFileName() ? 2 : 0) + \count($a); return []; } if ($f) { $a[$prefix.'file'] = new LinkStub($f, $c->getStartLine()); $a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine(); } return $a; } public static function unsetClosureFileInfo(\Closure $c, array $a): array { unset($a[Caster::PREFIX_VIRTUAL.'file'], $a[Caster::PREFIX_VIRTUAL.'line']); return $a; } public static function castGenerator(\Generator $c, array $a, Stub $stub, bool $isNested): array { // Cannot create ReflectionGenerator based on a terminated Generator try { $reflectionGenerator = new \ReflectionGenerator($c); return self::castReflectionGenerator($reflectionGenerator, $a, $stub, $isNested); } catch (\Exception) { $a[Caster::PREFIX_VIRTUAL.'closed'] = true; return $a; } } public static function castType(\ReflectionType $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; if ($c instanceof \ReflectionNamedType) { $a += [ $prefix.'name' => $c->getName(), $prefix.'allowsNull' => $c->allowsNull(), $prefix.'isBuiltin' => $c->isBuiltin(), ]; } elseif ($c instanceof \ReflectionUnionType || $c instanceof \ReflectionIntersectionType) { $a[$prefix.'allowsNull'] = $c->allowsNull(); self::addMap($a, $c, [ 'types' => 'getTypes', ]); } else { $a[$prefix.'allowsNull'] = $c->allowsNull(); } return $a; } public static function castAttribute(\ReflectionAttribute $c, array $a, Stub $stub, bool $isNested): array { $map = [ 'name' => 'getName', 'arguments' => 'getArguments', ]; if (\PHP_VERSION_ID >= 80400) { unset($map['name']); } self::addMap($a, $c, $map); return $a; } public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; if ($c->getThis()) { $a[$prefix.'this'] = new CutStub($c->getThis()); } $function = $c->getFunction(); $frame = [ 'class' => $function->class ?? null, 'type' => isset($function->class) ? ($function->isStatic() ? '::' : '->') : null, 'function' => $function->name, 'file' => $c->getExecutingFile(), 'line' => $c->getExecutingLine(), ]; if ($trace = $c->getTrace(\DEBUG_BACKTRACE_IGNORE_ARGS)) { $function = new \ReflectionGenerator($c->getExecutingGenerator()); array_unshift($trace, [ 'function' => 'yield', 'file' => $function->getExecutingFile(), 'line' => $function->getExecutingLine(), ]); $trace[] = $frame; $a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1); } else { $function = new FrameStub($frame, false, true); $function = ExceptionCaster::castFrameStub($function, [], $function, true); $a[$prefix.'executing'] = $function[$prefix.'src']; } $a[Caster::PREFIX_VIRTUAL.'closed'] = false; return $a; } public static function castClass(\ReflectionClass $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $prefix = Caster::PREFIX_VIRTUAL; if ($n = \Reflection::getModifierNames($c->getModifiers())) { $a[$prefix.'modifiers'] = implode(' ', $n); } self::addMap($a, $c, [ 'extends' => 'getParentClass', 'implements' => 'getInterfaceNames', 'constants' => 'getReflectionConstants', ]); foreach ($c->getProperties() as $n) { $a[$prefix.'properties'][$n->name] = $n; } foreach ($c->getMethods() as $n) { $a[$prefix.'methods'][$n->name] = $n; } self::addAttributes($a, $c, $prefix); if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { self::addExtra($a, $c); } return $a; } public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $prefix = Caster::PREFIX_VIRTUAL; self::addMap($a, $c, [ 'returnsReference' => 'returnsReference', 'returnType' => 'getReturnType', 'class' => 'getClosureCalledClass', 'this' => 'getClosureThis', ]); if (isset($a[$prefix.'returnType'])) { $v = $a[$prefix.'returnType']; $v = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType'] instanceof \ReflectionNamedType && $a[$prefix.'returnType']->allowsNull() && !\in_array($v, ['mixed', 'null'], true) ? '?'.$v : $v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); } if (isset($a[$prefix.'class'])) { $a[$prefix.'class'] = new ClassStub($a[$prefix.'class']); } if (isset($a[$prefix.'this'])) { $a[$prefix.'this'] = new CutStub($a[$prefix.'this']); } foreach ($c->getParameters() as $v) { $k = '$'.$v->name; if ($v->isVariadic()) { $k = '...'.$k; } if ($v->isPassedByReference()) { $k = '&'.$k; } $a[$prefix.'parameters'][$k] = $v; } if (isset($a[$prefix.'parameters'])) { $a[$prefix.'parameters'] = new EnumStub($a[$prefix.'parameters']); } self::addAttributes($a, $c, $prefix); if (!($filter & Caster::EXCLUDE_VERBOSE) && $v = $c->getStaticVariables()) { foreach ($v as $k => &$v) { if (\is_object($v)) { $a[$prefix.'use']['$'.$k] = new CutStub($v); } else { $a[$prefix.'use']['$'.$k] = &$v; } } unset($v); $a[$prefix.'use'] = new EnumStub($a[$prefix.'use']); } if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { self::addExtra($a, $c); } return $a; } public static function castClassConstant(\ReflectionClassConstant $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); $a[Caster::PREFIX_VIRTUAL.'value'] = $c->getValue(); self::addAttributes($a, $c); return $a; } public static function castMethod(\ReflectionMethod $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); return $a; } public static function castParameter(\ReflectionParameter $c, array $a, Stub $stub, bool $isNested): array { $prefix = Caster::PREFIX_VIRTUAL; self::addMap($a, $c, [ 'position' => 'getPosition', 'isVariadic' => 'isVariadic', 'byReference' => 'isPassedByReference', 'allowsNull' => 'allowsNull', ]); self::addAttributes($a, $c, $prefix); if ($v = $c->getType()) { $a[$prefix.'typeHint'] = $v instanceof \ReflectionNamedType ? $v->getName() : (string) $v; } if (isset($a[$prefix.'typeHint'])) { $v = $a[$prefix.'typeHint']; $a[$prefix.'typeHint'] = new ClassStub($v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); } else { unset($a[$prefix.'allowsNull']); } if ($c->isOptional()) { try { $a[$prefix.'default'] = $v = $c->getDefaultValue(); if ($c->isDefaultValueConstant() && !\is_object($v)) { $a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v); } if (null === $v) { unset($a[$prefix.'allowsNull']); } } catch (\ReflectionException) { } } return $a; } public static function castProperty(\ReflectionProperty $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); self::addAttributes($a, $c); self::addExtra($a, $c); return $a; } public static function castReference(\ReflectionReference $c, array $a, Stub $stub, bool $isNested): array { $a[Caster::PREFIX_VIRTUAL.'id'] = $c->getId(); return $a; } public static function castExtension(\ReflectionExtension $c, array $a, Stub $stub, bool $isNested): array { self::addMap($a, $c, [ 'version' => 'getVersion', 'dependencies' => 'getDependencies', 'iniEntries' => 'getIniEntries', 'isPersistent' => 'isPersistent', 'isTemporary' => 'isTemporary', 'constants' => 'getConstants', 'functions' => 'getFunctions', 'classes' => 'getClasses', ]); return $a; } public static function castZendExtension(\ReflectionZendExtension $c, array $a, Stub $stub, bool $isNested): array { self::addMap($a, $c, [ 'version' => 'getVersion', 'author' => 'getAuthor', 'copyright' => 'getCopyright', 'url' => 'getURL', ]); return $a; } public static function getSignature(array $a): string { $prefix = Caster::PREFIX_VIRTUAL; $signature = ''; if (isset($a[$prefix.'parameters'])) { foreach ($a[$prefix.'parameters']->value as $k => $param) { $signature .= ', '; if ($type = $param->getType()) { if (!$type instanceof \ReflectionNamedType) { $signature .= $type.' '; } else { if ($param->allowsNull() && !\in_array($type->getName(), ['mixed', 'null'], true)) { $signature .= '?'; } $signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1).' '; } } $signature .= $k; if (!$param->isDefaultValueAvailable()) { continue; } $v = $param->getDefaultValue(); $signature .= ' = '; if ($param->isDefaultValueConstant()) { $signature .= substr(strrchr('\\'.$param->getDefaultValueConstantName(), '\\'), 1); } elseif (null === $v) { $signature .= 'null'; } elseif (\is_array($v)) { $signature .= $v ? '[…'.\count($v).']' : '[]'; } elseif (\is_string($v)) { $signature .= 10 > \strlen($v) && !str_contains($v, '\\') ? "'{$v}'" : "'…".\strlen($v)."'"; } elseif (\is_bool($v)) { $signature .= $v ? 'true' : 'false'; } elseif (\is_object($v)) { $signature .= 'new '.substr(strrchr('\\'.get_debug_type($v), '\\'), 1); } else { $signature .= $v; } } } $signature = (empty($a[$prefix.'returnsReference']) ? '' : '&').'('.substr($signature, 2).')'; if (isset($a[$prefix.'returnType'])) { $signature .= ': '.substr(strrchr('\\'.$a[$prefix.'returnType'], '\\'), 1); } return $signature; } private static function addExtra(array &$a, \Reflector $c): void { $x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : []; if (method_exists($c, 'getFileName') && $m = $c->getFileName()) { $x['file'] = new LinkStub($m, $c->getStartLine()); $x['line'] = $c->getStartLine().' to '.$c->getEndLine(); } self::addMap($x, $c, self::EXTRA_MAP, ''); if ($x) { $a[Caster::PREFIX_VIRTUAL.'extra'] = new EnumStub($x); } } private static function addMap(array &$a, object $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL): void { foreach ($map as $k => $m) { if ('isDisabled' === $k) { continue; } if (method_exists($c, $m) && false !== ($m = $c->$m()) && null !== $m) { $a[$prefix.$k] = $m instanceof \Reflector ? $m->name : $m; } } } private static function addAttributes(array &$a, \Reflector $c, string $prefix = Caster::PREFIX_VIRTUAL): void { foreach ($c->getAttributes() as $n) { $a[$prefix.'attributes'][] = $n; } } } var-dumper/Caster/FFICaster.php 0000644 00000012254 15060133305 0012326 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use FFI\CData; use FFI\CType; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts FFI extension classes to array representation. * * @author Nesmeyanov Kirill <nesk@xakep.ru> */ final class FFICaster { /** * In case of "char*" contains a string, the length of which depends on * some other parameter, then during the generation of the string it is * possible to go beyond the allowable memory area. * * This restriction serves to ensure that processing does not take * up the entire allowable PHP memory limit. */ private const MAX_STRING_LENGTH = 255; public static function castCTypeOrCData(CData|CType $data, array $args, Stub $stub): array { if ($data instanceof CType) { $type = $data; $data = null; } else { $type = \FFI::typeof($data); } $stub->class = sprintf('%s<%s> size %d align %d', ($data ?? $type)::class, $type->getName(), $type->getSize(), $type->getAlignment()); return match ($type->getKind()) { CType::TYPE_FLOAT, CType::TYPE_DOUBLE, \defined('\FFI\CType::TYPE_LONGDOUBLE') ? CType::TYPE_LONGDOUBLE : -1, CType::TYPE_UINT8, CType::TYPE_SINT8, CType::TYPE_UINT16, CType::TYPE_SINT16, CType::TYPE_UINT32, CType::TYPE_SINT32, CType::TYPE_UINT64, CType::TYPE_SINT64, CType::TYPE_BOOL, CType::TYPE_CHAR, CType::TYPE_ENUM => null !== $data ? [Caster::PREFIX_VIRTUAL.'cdata' => $data->cdata] : [], CType::TYPE_POINTER => self::castFFIPointer($stub, $type, $data), CType::TYPE_STRUCT => self::castFFIStructLike($type, $data), CType::TYPE_FUNC => self::castFFIFunction($stub, $type), default => $args, }; } private static function castFFIFunction(Stub $stub, CType $type): array { $arguments = []; for ($i = 0, $count = $type->getFuncParameterCount(); $i < $count; ++$i) { $param = $type->getFuncParameterType($i); $arguments[] = $param->getName(); } $abi = match ($type->getFuncABI()) { CType::ABI_DEFAULT, CType::ABI_CDECL => '[cdecl]', CType::ABI_FASTCALL => '[fastcall]', CType::ABI_THISCALL => '[thiscall]', CType::ABI_STDCALL => '[stdcall]', CType::ABI_PASCAL => '[pascal]', CType::ABI_REGISTER => '[register]', CType::ABI_MS => '[ms]', CType::ABI_SYSV => '[sysv]', CType::ABI_VECTORCALL => '[vectorcall]', default => '[unknown abi]', }; $returnType = $type->getFuncReturnType(); $stub->class = $abi.' callable('.implode(', ', $arguments).'): ' .$returnType->getName(); return [Caster::PREFIX_VIRTUAL.'returnType' => $returnType]; } private static function castFFIPointer(Stub $stub, CType $type, ?CData $data = null): array { $ptr = $type->getPointerType(); if (null === $data) { return [Caster::PREFIX_VIRTUAL.'0' => $ptr]; } return match ($ptr->getKind()) { CType::TYPE_CHAR => [Caster::PREFIX_VIRTUAL.'cdata' => self::castFFIStringValue($data)], CType::TYPE_FUNC => self::castFFIFunction($stub, $ptr), default => [Caster::PREFIX_VIRTUAL.'cdata' => $data[0]], }; } private static function castFFIStringValue(CData $data): string|CutStub { $result = []; for ($i = 0; $i < self::MAX_STRING_LENGTH; ++$i) { $result[$i] = $data[$i]; if ("\0" === $result[$i]) { return implode('', $result); } } $string = implode('', $result); $stub = new CutStub($string); $stub->cut = -1; $stub->value = $string; return $stub; } private static function castFFIStructLike(CType $type, ?CData $data = null): array { $isUnion = ($type->getAttributes() & CType::ATTR_UNION) === CType::ATTR_UNION; $result = []; foreach ($type->getStructFieldNames() as $name) { $field = $type->getStructFieldType($name); // Retrieving the value of a field from a union containing // a pointer is not a safe operation, because may contain // incorrect data. $isUnsafe = $isUnion && CType::TYPE_POINTER === $field->getKind(); if ($isUnsafe) { $result[Caster::PREFIX_VIRTUAL.$name.'?'] = $field; } elseif (null === $data) { $result[Caster::PREFIX_VIRTUAL.$name] = $field; } else { $fieldName = $data->{$name} instanceof CData ? '' : $field->getName().' '; $result[Caster::PREFIX_VIRTUAL.$fieldName.$name] = $data->{$name}; } } return $result; } } var-dumper/Caster/CutStub.php 0000644 00000003612 15060133305 0012147 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents the main properties of a PHP variable, pre-casted by a caster. * * @author Nicolas Grekas <p@tchwork.com> */ class CutStub extends Stub { public function __construct(mixed $value) { $this->value = $value; switch (\gettype($value)) { case 'object': $this->type = self::TYPE_OBJECT; $this->class = $value::class; if ($value instanceof \Closure) { ReflectionCaster::castClosure($value, [], $this, true, Caster::EXCLUDE_VERBOSE); } $this->cut = -1; break; case 'array': $this->type = self::TYPE_ARRAY; $this->class = self::ARRAY_ASSOC; $this->cut = $this->value = \count($value); break; case 'resource': case 'unknown type': case 'resource (closed)': $this->type = self::TYPE_RESOURCE; $this->handle = (int) $value; if ('Unknown' === $this->class = @get_resource_type($value)) { $this->class = 'Closed'; } $this->cut = -1; break; case 'string': $this->type = self::TYPE_STRING; $this->class = preg_match('//u', $value) ? self::STRING_UTF8 : self::STRING_BINARY; $this->cut = self::STRING_BINARY === $this->class ? \strlen($value) : mb_strlen($value, 'UTF-8'); $this->value = ''; break; } } } var-dumper/Caster/DOMCaster.php 0000644 00000025735 15060133305 0012351 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts DOM related classes to array representation. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class DOMCaster { private const ERROR_CODES = [ \DOM_PHP_ERR => 'DOM_PHP_ERR', \DOM_INDEX_SIZE_ERR => 'DOM_INDEX_SIZE_ERR', \DOMSTRING_SIZE_ERR => 'DOMSTRING_SIZE_ERR', \DOM_HIERARCHY_REQUEST_ERR => 'DOM_HIERARCHY_REQUEST_ERR', \DOM_WRONG_DOCUMENT_ERR => 'DOM_WRONG_DOCUMENT_ERR', \DOM_INVALID_CHARACTER_ERR => 'DOM_INVALID_CHARACTER_ERR', \DOM_NO_DATA_ALLOWED_ERR => 'DOM_NO_DATA_ALLOWED_ERR', \DOM_NO_MODIFICATION_ALLOWED_ERR => 'DOM_NO_MODIFICATION_ALLOWED_ERR', \DOM_NOT_FOUND_ERR => 'DOM_NOT_FOUND_ERR', \DOM_NOT_SUPPORTED_ERR => 'DOM_NOT_SUPPORTED_ERR', \DOM_INUSE_ATTRIBUTE_ERR => 'DOM_INUSE_ATTRIBUTE_ERR', \DOM_INVALID_STATE_ERR => 'DOM_INVALID_STATE_ERR', \DOM_SYNTAX_ERR => 'DOM_SYNTAX_ERR', \DOM_INVALID_MODIFICATION_ERR => 'DOM_INVALID_MODIFICATION_ERR', \DOM_NAMESPACE_ERR => 'DOM_NAMESPACE_ERR', \DOM_INVALID_ACCESS_ERR => 'DOM_INVALID_ACCESS_ERR', \DOM_VALIDATION_ERR => 'DOM_VALIDATION_ERR', ]; private const NODE_TYPES = [ \XML_ELEMENT_NODE => 'XML_ELEMENT_NODE', \XML_ATTRIBUTE_NODE => 'XML_ATTRIBUTE_NODE', \XML_TEXT_NODE => 'XML_TEXT_NODE', \XML_CDATA_SECTION_NODE => 'XML_CDATA_SECTION_NODE', \XML_ENTITY_REF_NODE => 'XML_ENTITY_REF_NODE', \XML_ENTITY_NODE => 'XML_ENTITY_NODE', \XML_PI_NODE => 'XML_PI_NODE', \XML_COMMENT_NODE => 'XML_COMMENT_NODE', \XML_DOCUMENT_NODE => 'XML_DOCUMENT_NODE', \XML_DOCUMENT_TYPE_NODE => 'XML_DOCUMENT_TYPE_NODE', \XML_DOCUMENT_FRAG_NODE => 'XML_DOCUMENT_FRAG_NODE', \XML_NOTATION_NODE => 'XML_NOTATION_NODE', \XML_HTML_DOCUMENT_NODE => 'XML_HTML_DOCUMENT_NODE', \XML_DTD_NODE => 'XML_DTD_NODE', \XML_ELEMENT_DECL_NODE => 'XML_ELEMENT_DECL_NODE', \XML_ATTRIBUTE_DECL_NODE => 'XML_ATTRIBUTE_DECL_NODE', \XML_ENTITY_DECL_NODE => 'XML_ENTITY_DECL_NODE', \XML_NAMESPACE_DECL_NODE => 'XML_NAMESPACE_DECL_NODE', ]; public static function castException(\DOMException|\Dom\Exception $e, array $a, Stub $stub, bool $isNested): array { $k = Caster::PREFIX_PROTECTED.'code'; if (isset($a[$k], self::ERROR_CODES[$a[$k]])) { $a[$k] = new ConstStub(self::ERROR_CODES[$a[$k]], $a[$k]); } return $a; } public static function castLength($dom, array $a, Stub $stub, bool $isNested): array { $a += [ 'length' => $dom->length, ]; return $a; } public static function castImplementation(\DOMImplementation|\Dom\Implementation $dom, array $a, Stub $stub, bool $isNested): array { $a += [ Caster::PREFIX_VIRTUAL.'Core' => '1.0', Caster::PREFIX_VIRTUAL.'XML' => '2.0', ]; return $a; } public static function castNode(\DOMNode|\Dom\Node $dom, array $a, Stub $stub, bool $isNested): array { $a += [ 'nodeName' => $dom->nodeName, 'nodeValue' => new CutStub($dom->nodeValue), 'nodeType' => new ConstStub(self::NODE_TYPES[$dom->nodeType], $dom->nodeType), 'parentNode' => new CutStub($dom->parentNode), 'childNodes' => $dom->childNodes, 'firstChild' => new CutStub($dom->firstChild), 'lastChild' => new CutStub($dom->lastChild), 'previousSibling' => new CutStub($dom->previousSibling), 'nextSibling' => new CutStub($dom->nextSibling), 'ownerDocument' => new CutStub($dom->ownerDocument), 'baseURI' => $dom->baseURI ? new LinkStub($dom->baseURI) : $dom->baseURI, 'textContent' => new CutStub($dom->textContent), ]; if ($dom instanceof \DOMNode || $dom instanceof \Dom\Element) { $a += [ 'attributes' => $dom->attributes, 'namespaceURI' => $dom->namespaceURI, 'prefix' => $dom->prefix, 'localName' => $dom->localName, ]; } return $a; } public static function castNameSpaceNode(\DOMNameSpaceNode $dom, array $a, Stub $stub, bool $isNested): array { $a += [ 'nodeName' => $dom->nodeName, 'nodeValue' => new CutStub($dom->nodeValue), 'nodeType' => new ConstStub(self::NODE_TYPES[$dom->nodeType], $dom->nodeType), 'prefix' => $dom->prefix, 'localName' => $dom->localName, 'namespaceURI' => $dom->namespaceURI, 'ownerDocument' => new CutStub($dom->ownerDocument), 'parentNode' => new CutStub($dom->parentNode), ]; return $a; } public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $a += [ 'doctype' => $dom->doctype, 'implementation' => $dom->implementation, 'documentElement' => new CutStub($dom->documentElement), 'actualEncoding' => $dom->actualEncoding, 'encoding' => $dom->encoding, 'xmlEncoding' => $dom->xmlEncoding, 'standalone' => $dom->standalone, 'xmlStandalone' => $dom->xmlStandalone, 'version' => $dom->version, 'xmlVersion' => $dom->xmlVersion, 'strictErrorChecking' => $dom->strictErrorChecking, 'documentURI' => $dom->documentURI ? new LinkStub($dom->documentURI) : $dom->documentURI, 'config' => $dom->config, 'formatOutput' => $dom->formatOutput, 'validateOnParse' => $dom->validateOnParse, 'resolveExternals' => $dom->resolveExternals, 'preserveWhiteSpace' => $dom->preserveWhiteSpace, 'recover' => $dom->recover, 'substituteEntities' => $dom->substituteEntities, ]; if (!($filter & Caster::EXCLUDE_VERBOSE)) { $formatOutput = $dom->formatOutput; $dom->formatOutput = true; $a += [Caster::PREFIX_VIRTUAL.'xml' => $dom->saveXML()]; $dom->formatOutput = $formatOutput; } return $a; } public static function castXMLDocument(\Dom\XMLDocument $dom, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $a += [ 'doctype' => $dom->doctype, 'implementation' => $dom->implementation, 'documentElement' => new CutStub($dom->documentElement), 'inputEncoding' => $dom->inputEncoding, 'xmlEncoding' => $dom->xmlEncoding, 'xmlStandalone' => $dom->xmlStandalone, 'xmlVersion' => $dom->xmlVersion, 'documentURI' => $dom->documentURI ? new LinkStub($dom->documentURI) : $dom->documentURI, 'formatOutput' => $dom->formatOutput, ]; if (!($filter & Caster::EXCLUDE_VERBOSE)) { $formatOutput = $dom->formatOutput; $dom->formatOutput = true; $a += [Caster::PREFIX_VIRTUAL.'xml' => $dom->saveXML()]; $dom->formatOutput = $formatOutput; } return $a; } public static function castHTMLDocument(\Dom\HTMLDocument $dom, array $a, Stub $stub, bool $isNested, int $filter = 0): array { $a += [ 'doctype' => $dom->doctype, 'implementation' => $dom->implementation, 'documentElement' => new CutStub($dom->documentElement), 'inputEncoding' => $dom->inputEncoding, 'documentURI' => $dom->documentURI ? new LinkStub($dom->documentURI) : $dom->documentURI, ]; if (!($filter & Caster::EXCLUDE_VERBOSE)) { $a += [Caster::PREFIX_VIRTUAL.'html' => $dom->saveHTML()]; } return $a; } public static function castCharacterData(\DOMCharacterData|\Dom\CharacterData $dom, array $a, Stub $stub, bool $isNested): array { $a += [ 'data' => $dom->data, 'length' => $dom->length, ]; return $a; } public static function castAttr(\DOMAttr|\Dom\Attr $dom, array $a, Stub $stub, bool $isNested): array { $a += [ 'name' => $dom->name, 'specified' => $dom->specified, 'value' => $dom->value, 'ownerElement' => $dom->ownerElement, ]; if ($dom instanceof \DOMAttr) { $a += [ 'schemaTypeInfo' => $dom->schemaTypeInfo, ]; } return $a; } public static function castElement(\DOMElement|\Dom\Element $dom, array $a, Stub $stub, bool $isNested): array { $a += [ 'tagName' => $dom->tagName, ]; if ($dom instanceof \DOMElement) { $a += [ 'schemaTypeInfo' => $dom->schemaTypeInfo, ]; } return $a; } public static function castText(\DOMText|\Dom\Text $dom, array $a, Stub $stub, bool $isNested): array { $a += [ 'wholeText' => $dom->wholeText, ]; return $a; } public static function castDocumentType(\DOMDocumentType|\Dom\DocumentType $dom, array $a, Stub $stub, bool $isNested): array { $a += [ 'name' => $dom->name, 'entities' => $dom->entities, 'notations' => $dom->notations, 'publicId' => $dom->publicId, 'systemId' => $dom->systemId, 'internalSubset' => $dom->internalSubset, ]; return $a; } public static function castNotation(\DOMNotation|\Dom\Notation $dom, array $a, Stub $stub, bool $isNested): array { $a += [ 'publicId' => $dom->publicId, 'systemId' => $dom->systemId, ]; return $a; } public static function castEntity(\DOMEntity|\Dom\Entity $dom, array $a, Stub $stub, bool $isNested): array { $a += [ 'publicId' => $dom->publicId, 'systemId' => $dom->systemId, 'notationName' => $dom->notationName, 'actualEncoding' => $dom->actualEncoding, 'encoding' => $dom->encoding, 'version' => $dom->version, ]; return $a; } public static function castProcessingInstruction(\DOMProcessingInstruction|\Dom\ProcessingInstruction $dom, array $a, Stub $stub, bool $isNested): array { $a += [ 'target' => $dom->target, 'data' => $dom->data, ]; return $a; } public static function castXPath(\DOMXPath|\Dom\XPath $dom, array $a, Stub $stub, bool $isNested): array { $a += [ 'document' => $dom->document, ]; return $a; } } var-dumper/Caster/TraceStub.php 0000644 00000001726 15060133305 0012456 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents a backtrace as returned by debug_backtrace() or Exception->getTrace(). * * @author Nicolas Grekas <p@tchwork.com> */ class TraceStub extends Stub { public bool $keepArgs; public int $sliceOffset; public ?int $sliceLength; public int $numberingOffset; public function __construct(array $trace, bool $keepArgs = true, int $sliceOffset = 0, ?int $sliceLength = null, int $numberingOffset = 0) { $this->value = $trace; $this->keepArgs = $keepArgs; $this->sliceOffset = $sliceOffset; $this->sliceLength = $sliceLength; $this->numberingOffset = $numberingOffset; } } var-dumper/Caster/StubCaster.php 0000644 00000004520 15060133305 0012634 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Casts a caster's Stub. * * @author Nicolas Grekas <p@tchwork.com> * * @final */ class StubCaster { public static function castStub(Stub $c, array $a, Stub $stub, bool $isNested): array { if ($isNested) { $stub->type = $c->type; $stub->class = $c->class; $stub->value = $c->value; $stub->handle = $c->handle; $stub->cut = $c->cut; $stub->attr = $c->attr; if (Stub::TYPE_REF === $c->type && !$c->class && \is_string($c->value) && !preg_match('//u', $c->value)) { $stub->type = Stub::TYPE_STRING; $stub->class = Stub::STRING_BINARY; } $a = []; } return $a; } public static function castCutArray(CutArrayStub $c, array $a, Stub $stub, bool $isNested): array { return $isNested ? $c->preservedSubset : $a; } public static function cutInternals($obj, array $a, Stub $stub, bool $isNested): array { if ($isNested) { $stub->cut += \count($a); return []; } return $a; } public static function castEnum(EnumStub $c, array $a, Stub $stub, bool $isNested): array { if ($isNested) { $stub->class = $c->dumpKeys ? '' : null; $stub->handle = 0; $stub->value = null; $stub->cut = $c->cut; $stub->attr = $c->attr; $a = []; if ($c->value) { foreach (array_keys($c->value) as $k) { $keys[] = !isset($k[0]) || "\0" !== $k[0] ? Caster::PREFIX_VIRTUAL.$k : $k; } // Preserve references with array_combine() $a = array_combine($keys, $c->value); } } return $a; } public static function castScalar(ScalarStub $scalarStub, array $a, Stub $stub): array { $stub->type = Stub::TYPE_SCALAR; $stub->attr['value'] = $scalarStub->value; return $a; } } var-dumper/Caster/EnumStub.php 0000644 00000001202 15060133305 0012311 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents an enumeration of values. * * @author Nicolas Grekas <p@tchwork.com> */ class EnumStub extends Stub { public bool $dumpKeys = true; public function __construct(array $values, bool $dumpKeys = true) { $this->value = $values; $this->dumpKeys = $dumpKeys; } } var-dumper/Caster/ClassStub.php 0000644 00000007350 15060133305 0012464 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * Represents a PHP class identifier. * * @author Nicolas Grekas <p@tchwork.com> */ class ClassStub extends ConstStub { /** * @param string $identifier A PHP identifier, e.g. a class, method, interface, etc. name * @param callable $callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier */ public function __construct(string $identifier, callable|array|string|null $callable = null) { $this->value = $identifier; try { if (null !== $callable) { if ($callable instanceof \Closure) { $r = new \ReflectionFunction($callable); } elseif (\is_object($callable)) { $r = [$callable, '__invoke']; } elseif (\is_array($callable)) { $r = $callable; } elseif (false !== $i = strpos($callable, '::')) { $r = [substr($callable, 0, $i), substr($callable, 2 + $i)]; } else { $r = new \ReflectionFunction($callable); } } elseif (0 < $i = strpos($identifier, '::') ?: strpos($identifier, '->')) { $r = [substr($identifier, 0, $i), substr($identifier, 2 + $i)]; } else { $r = new \ReflectionClass($identifier); } if (\is_array($r)) { try { $r = new \ReflectionMethod($r[0], $r[1]); } catch (\ReflectionException) { $r = new \ReflectionClass($r[0]); } } if (str_contains($identifier, "@anonymous\0")) { $this->value = $identifier = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $identifier); } if (null !== $callable && $r instanceof \ReflectionFunctionAbstract) { $s = ReflectionCaster::castFunctionAbstract($r, [], new Stub(), true, Caster::EXCLUDE_VERBOSE); $s = ReflectionCaster::getSignature($s); if (str_ends_with($identifier, '()')) { $this->value = substr_replace($identifier, $s, -2); } else { $this->value .= $s; } } } catch (\ReflectionException) { return; } finally { if (0 < $i = strrpos($this->value, '\\')) { $this->attr['ellipsis'] = \strlen($this->value) - $i; $this->attr['ellipsis-type'] = 'class'; $this->attr['ellipsis-tail'] = 1; } } if ($f = $r->getFileName()) { $this->attr['file'] = $f; $this->attr['line'] = $r->getStartLine(); } } public static function wrapCallable(mixed $callable): mixed { if (\is_object($callable) || !\is_callable($callable)) { return $callable; } if (!\is_array($callable)) { $callable = new static($callable, $callable); } elseif (\is_string($callable[0])) { $callable[0] = new static($callable[0], $callable); } else { $callable[1] = new static($callable[1], $callable); } return $callable; } } var-dumper/Caster/DsPairStub.php 0000644 00000001161 15060133305 0012573 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Caster; use Symfony\Component\VarDumper\Cloner\Stub; /** * @author Nicolas Grekas <p@tchwork.com> */ class DsPairStub extends Stub { public function __construct(mixed $key, mixed $value) { $this->value = [ Caster::PREFIX_VIRTUAL.'key' => $key, Caster::PREFIX_VIRTUAL.'value' => $value, ]; } } var-dumper/LICENSE 0000644 00000002054 15060133305 0007630 0 ustar 00 Copyright (c) 2014-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. var-dumper/VarDumper.php 0000644 00000007725 15060133305 0011253 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper; use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\VarDumper\Caster\ReflectionCaster; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Dumper\ContextProvider\CliContextProvider; use Symfony\Component\VarDumper\Dumper\ContextProvider\RequestContextProvider; use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; use Symfony\Component\VarDumper\Dumper\ContextualizedDumper; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\Dumper\ServerDumper; // Load the global dump() function require_once __DIR__.'/Resources/functions/dump.php'; /** * @author Nicolas Grekas <p@tchwork.com> */ class VarDumper { /** * @var callable|null */ private static $handler; public static function dump(mixed $var, ?string $label = null): mixed { if (null === self::$handler) { self::register(); } return (self::$handler)($var, $label); } public static function setHandler(?callable $callable): ?callable { $prevHandler = self::$handler; // Prevent replacing the handler with expected format as soon as the env var was set: if (isset($_SERVER['VAR_DUMPER_FORMAT'])) { return $prevHandler; } self::$handler = $callable; return $prevHandler; } private static function register(): void { $cloner = new VarCloner(); $cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); $format = $_SERVER['VAR_DUMPER_FORMAT'] ?? null; switch (true) { case 'html' === $format: $dumper = new HtmlDumper(); break; case 'cli' === $format: $dumper = new CliDumper(); break; case 'server' === $format: case $format && 'tcp' === parse_url($format, \PHP_URL_SCHEME): $host = 'server' === $format ? $_SERVER['VAR_DUMPER_SERVER'] ?? '127.0.0.1:9912' : $format; $dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) ? new CliDumper() : new HtmlDumper(); $dumper = new ServerDumper($host, $dumper, self::getDefaultContextProviders()); break; default: $dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) ? new CliDumper() : new HtmlDumper(); } if (!$dumper instanceof ServerDumper) { $dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]); } self::$handler = function ($var, ?string $label = null) use ($cloner, $dumper) { $var = $cloner->cloneVar($var); if (null !== $label) { $var = $var->withContext(['label' => $label]); } $dumper->dump($var); }; } private static function getDefaultContextProviders(): array { $contextProviders = []; if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) && class_exists(Request::class)) { $requestStack = new RequestStack(); $requestStack->push(Request::createFromGlobals()); $contextProviders['request'] = new RequestContextProvider($requestStack); } $fileLinkFormatter = class_exists(FileLinkFormatter::class) ? new FileLinkFormatter(null, $requestStack ?? null) : null; return $contextProviders + [ 'cli' => new CliContextProvider(), 'source' => new SourceContextProvider(null, null, $fileLinkFormatter), ]; } } var-dumper/Resources/css/htmlDescriptor.css 0000644 00000005702 15060133305 0015105 0 ustar 00 body { display: flex; flex-direction: column-reverse; justify-content: flex-end; max-width: 1140px; margin: auto; padding: 15px; word-wrap: break-word; background-color: #F9F9F9; color: #222; font-family: Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.4; } p { margin: 0; } a { color: #218BC3; text-decoration: none; } a:hover { text-decoration: underline; } .text-small { font-size: 12px !important; } article { margin: 5px; margin-bottom: 10px; } article > header > .row { display: flex; flex-direction: row; align-items: baseline; margin-bottom: 10px; } article > header > .row > .col { flex: 1; display: flex; align-items: baseline; } article > header > .row > h2 { font-size: 14px; color: #222; font-weight: normal; font-family: "Lucida Console", monospace, sans-serif; word-break: break-all; margin: 20px 5px 0 0; user-select: all; } article > header > .row > h2 > code { white-space: nowrap; user-select: none; color: #cc2255; background-color: #f7f7f9; border: 1px solid #e1e1e8; border-radius: 3px; margin-right: 5px; padding: 0 3px; } article > header > .row > time.col { flex: 0; text-align: right; white-space: nowrap; color: #999; font-style: italic; } article > header ul.tags { list-style: none; padding: 0; margin: 0; font-size: 12px; } article > header ul.tags > li { user-select: all; margin-bottom: 2px; } article > header ul.tags > li > span.badge { display: inline-block; padding: .25em .4em; margin-right: 5px; border-radius: 4px; background-color: #6c757d3b; color: #524d4d; font-size: 12px; text-align: center; font-weight: 700; line-height: 1; white-space: nowrap; vertical-align: baseline; user-select: none; } article > section.body { border: 1px solid #d8d8d8; background: #FFF; padding: 10px; border-radius: 3px; } pre.sf-dump { border-radius: 3px; margin-bottom: 0; } .hidden { display: none !important; } .dumped-tag > .sf-dump { display: inline-block; margin: 0; padding: 1px 5px; line-height: 1.4; vertical-align: top; background-color: transparent; user-select: auto; } .dumped-tag > pre.sf-dump, .dumped-tag > .sf-dump-default { color: #CC7832; background: none; } .dumped-tag > .sf-dump .sf-dump-str { color: #629755; } .dumped-tag > .sf-dump .sf-dump-private, .dumped-tag > .sf-dump .sf-dump-protected, .dumped-tag > .sf-dump .sf-dump-public { color: #262626; } .dumped-tag > .sf-dump .sf-dump-note { color: #6897BB; } .dumped-tag > .sf-dump .sf-dump-key { color: #789339; } .dumped-tag > .sf-dump .sf-dump-ref { color: #6E6E6E; } .dumped-tag > .sf-dump .sf-dump-ellipsis { color: #CC7832; max-width: 100em; } .dumped-tag > .sf-dump .sf-dump-ellipsis-path { max-width: 5em; } .dumped-tag > .sf-dump .sf-dump-ns { user-select: none; } var-dumper/Resources/bin/var-dump-server 0000755 00000004136 15060133305 0014335 0 ustar 00 #!/usr/bin/env php <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if ('cli' !== PHP_SAPI) { throw new Exception('This script must be run from the command line.'); } /** * Starts a dump server to collect and output dumps on a single place with multiple formats support. * * @author Maxime Steinhausser <maxime.steinhausser@gmail.com> */ use Psr\Log\LoggerInterface; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Logger\ConsoleLogger; use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\VarDumper\Command\ServerDumpCommand; use Symfony\Component\VarDumper\Server\DumpServer; function includeIfExists(string $file): bool { return file_exists($file) && include $file; } if ( !includeIfExists(__DIR__ . '/../../../../autoload.php') && !includeIfExists(__DIR__ . '/../../vendor/autoload.php') && !includeIfExists(__DIR__ . '/../../../../../../vendor/autoload.php') ) { fwrite(STDERR, 'Install dependencies using Composer.'.PHP_EOL); exit(1); } if (!class_exists(Application::class)) { fwrite(STDERR, 'You need the "symfony/console" component in order to run the VarDumper server.'.PHP_EOL); exit(1); } $input = new ArgvInput(); $output = new ConsoleOutput(); $defaultHost = '127.0.0.1:9912'; $host = $input->getParameterOption(['--host'], $_SERVER['VAR_DUMPER_SERVER'] ?? $defaultHost, true); $logger = interface_exists(LoggerInterface::class) ? new ConsoleLogger($output->getErrorOutput()) : null; $app = new Application(); $app->getDefinition()->addOption( new InputOption('--host', null, InputOption::VALUE_REQUIRED, 'The address the server should listen to', $defaultHost) ); $app->add($command = new ServerDumpCommand(new DumpServer($host, $logger))) ->getApplication() ->setDefaultCommand($command->getName(), true) ->run($input, $output) ; var-dumper/Resources/functions/dump.php 0000644 00000003143 15060133305 0014263 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Component\VarDumper\Caster\ScalarStub; use Symfony\Component\VarDumper\VarDumper; if (!function_exists('dump')) { /** * @author Nicolas Grekas <p@tchwork.com> * @author Alexandre Daubois <alex.daubois@gmail.com> */ function dump(mixed ...$vars): mixed { if (!$vars) { VarDumper::dump(new ScalarStub('🐛')); return null; } if (array_key_exists(0, $vars) && 1 === count($vars)) { VarDumper::dump($vars[0]); $k = 0; } else { foreach ($vars as $k => $v) { VarDumper::dump($v, is_int($k) ? 1 + $k : $k); } } if (1 < count($vars)) { return $vars; } return $vars[$k]; } } if (!function_exists('dd')) { function dd(mixed ...$vars): never { if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) && !headers_sent()) { header('HTTP/1.1 500 Internal Server Error'); } if (!$vars) { VarDumper::dump(new ScalarStub('🐛')); exit(1); } if (array_key_exists(0, $vars) && 1 === count($vars)) { VarDumper::dump($vars[0]); } else { foreach ($vars as $k => $v) { VarDumper::dump($v, is_int($k) ? 1 + $k : $k); } } exit(1); } } var-dumper/Resources/js/htmlDescriptor.js 0000644 00000000542 15060133305 0014552 0 ustar 00 document.addEventListener('DOMContentLoaded', function() { let prev = null; Array.from(document.getElementsByTagName('article')).reverse().forEach(function (article) { const dedupId = article.dataset.dedupId; if (dedupId === prev) { article.getElementsByTagName('header')[0].classList.add('hidden'); } prev = dedupId; }); }); var-dumper/README.md 0000644 00000001137 15060133305 0010103 0 ustar 00 VarDumper Component =================== The VarDumper component provides mechanisms for walking through any arbitrary PHP variable. It provides a better `dump()` function that you can use instead of `var_dump()`. Resources --------- * [Documentation](https://symfony.com/doc/current/components/var_dumper/introduction.html) * [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) polyfill-mbstring/bootstrap80.php 0000644 00000021541 15060133305 0013116 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Mbstring as p; if (!function_exists('mb_convert_encoding')) { function mb_convert_encoding(array|string|null $string, ?string $to_encoding, array|string|null $from_encoding = null): array|string|false { return p\Mbstring::mb_convert_encoding($string ?? '', (string) $to_encoding, $from_encoding); } } if (!function_exists('mb_decode_mimeheader')) { function mb_decode_mimeheader(?string $string): string { return p\Mbstring::mb_decode_mimeheader((string) $string); } } if (!function_exists('mb_encode_mimeheader')) { function mb_encode_mimeheader(?string $string, ?string $charset = null, ?string $transfer_encoding = null, ?string $newline = "\r\n", ?int $indent = 0): string { return p\Mbstring::mb_encode_mimeheader((string) $string, $charset, $transfer_encoding, (string) $newline, (int) $indent); } } if (!function_exists('mb_decode_numericentity')) { function mb_decode_numericentity(?string $string, array $map, ?string $encoding = null): string { return p\Mbstring::mb_decode_numericentity((string) $string, $map, $encoding); } } if (!function_exists('mb_encode_numericentity')) { function mb_encode_numericentity(?string $string, array $map, ?string $encoding = null, ?bool $hex = false): string { return p\Mbstring::mb_encode_numericentity((string) $string, $map, $encoding, (bool) $hex); } } if (!function_exists('mb_convert_case')) { function mb_convert_case(?string $string, ?int $mode, ?string $encoding = null): string { return p\Mbstring::mb_convert_case((string) $string, (int) $mode, $encoding); } } if (!function_exists('mb_internal_encoding')) { function mb_internal_encoding(?string $encoding = null): string|bool { return p\Mbstring::mb_internal_encoding($encoding); } } if (!function_exists('mb_language')) { function mb_language(?string $language = null): string|bool { return p\Mbstring::mb_language($language); } } if (!function_exists('mb_list_encodings')) { function mb_list_encodings(): array { return p\Mbstring::mb_list_encodings(); } } if (!function_exists('mb_encoding_aliases')) { function mb_encoding_aliases(?string $encoding): array { return p\Mbstring::mb_encoding_aliases((string) $encoding); } } if (!function_exists('mb_check_encoding')) { function mb_check_encoding(array|string|null $value = null, ?string $encoding = null): bool { return p\Mbstring::mb_check_encoding($value, $encoding); } } if (!function_exists('mb_detect_encoding')) { function mb_detect_encoding(?string $string, array|string|null $encodings = null, ?bool $strict = false): string|false { return p\Mbstring::mb_detect_encoding((string) $string, $encodings, (bool) $strict); } } if (!function_exists('mb_detect_order')) { function mb_detect_order(array|string|null $encoding = null): array|bool { return p\Mbstring::mb_detect_order($encoding); } } if (!function_exists('mb_parse_str')) { function mb_parse_str(?string $string, &$result = []): bool { parse_str((string) $string, $result); return (bool) $result; } } if (!function_exists('mb_strlen')) { function mb_strlen(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strlen((string) $string, $encoding); } } if (!function_exists('mb_strpos')) { function mb_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); } } if (!function_exists('mb_strtolower')) { function mb_strtolower(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtolower((string) $string, $encoding); } } if (!function_exists('mb_strtoupper')) { function mb_strtoupper(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtoupper((string) $string, $encoding); } } if (!function_exists('mb_substitute_character')) { function mb_substitute_character(string|int|null $substitute_character = null): string|int|bool { return p\Mbstring::mb_substitute_character($substitute_character); } } if (!function_exists('mb_substr')) { function mb_substr(?string $string, ?int $start, ?int $length = null, ?string $encoding = null): string { return p\Mbstring::mb_substr((string) $string, (int) $start, $length, $encoding); } } if (!function_exists('mb_stripos')) { function mb_stripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_stripos((string) $haystack, (string) $needle, (int) $offset, $encoding); } } if (!function_exists('mb_stristr')) { function mb_stristr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_stristr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } } if (!function_exists('mb_strrchr')) { function mb_strrchr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrchr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } } if (!function_exists('mb_strrichr')) { function mb_strrichr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrichr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } } if (!function_exists('mb_strripos')) { function mb_strripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strripos((string) $haystack, (string) $needle, (int) $offset, $encoding); } } if (!function_exists('mb_strrpos')) { function mb_strrpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strrpos((string) $haystack, (string) $needle, (int) $offset, $encoding); } } if (!function_exists('mb_strstr')) { function mb_strstr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strstr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } } if (!function_exists('mb_get_info')) { function mb_get_info(?string $type = 'all'): array|string|int|false { return p\Mbstring::mb_get_info((string) $type); } } if (!function_exists('mb_http_output')) { function mb_http_output(?string $encoding = null): string|bool { return p\Mbstring::mb_http_output($encoding); } } if (!function_exists('mb_strwidth')) { function mb_strwidth(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strwidth((string) $string, $encoding); } } if (!function_exists('mb_substr_count')) { function mb_substr_count(?string $haystack, ?string $needle, ?string $encoding = null): int { return p\Mbstring::mb_substr_count((string) $haystack, (string) $needle, $encoding); } } if (!function_exists('mb_output_handler')) { function mb_output_handler(?string $string, ?int $status): string { return p\Mbstring::mb_output_handler((string) $string, (int) $status); } } if (!function_exists('mb_http_input')) { function mb_http_input(?string $type = null): array|string|false { return p\Mbstring::mb_http_input($type); } } if (!function_exists('mb_convert_variables')) { function mb_convert_variables(?string $to_encoding, array|string|null $from_encoding, mixed &$var, mixed &...$vars): string|false { return p\Mbstring::mb_convert_variables((string) $to_encoding, $from_encoding ?? '', $var, ...$vars); } } if (!function_exists('mb_ord')) { function mb_ord(?string $string, ?string $encoding = null): int|false { return p\Mbstring::mb_ord((string) $string, $encoding); } } if (!function_exists('mb_chr')) { function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); } } if (!function_exists('mb_scrub')) { function mb_scrub(?string $string, ?string $encoding = null): string { $encoding ??= mb_internal_encoding(); return mb_convert_encoding((string) $string, $encoding, $encoding); } } if (!function_exists('mb_str_split')) { function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); } } if (!function_exists('mb_str_pad')) { function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); } } if (extension_loaded('mbstring')) { return; } if (!defined('MB_CASE_UPPER')) { define('MB_CASE_UPPER', 0); } if (!defined('MB_CASE_LOWER')) { define('MB_CASE_LOWER', 1); } if (!defined('MB_CASE_TITLE')) { define('MB_CASE_TITLE', 2); } polyfill-mbstring/bootstrap.php 0000644 00000016526 15060133305 0012755 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ use Symfony\Polyfill\Mbstring as p; if (\PHP_VERSION_ID >= 80000) { return require __DIR__.'/bootstrap80.php'; } if (!function_exists('mb_convert_encoding')) { function mb_convert_encoding($string, $to_encoding, $from_encoding = null) { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); } } if (!function_exists('mb_decode_mimeheader')) { function mb_decode_mimeheader($string) { return p\Mbstring::mb_decode_mimeheader($string); } } if (!function_exists('mb_encode_mimeheader')) { function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); } } if (!function_exists('mb_decode_numericentity')) { function mb_decode_numericentity($string, $map, $encoding = null) { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); } } if (!function_exists('mb_encode_numericentity')) { function mb_encode_numericentity($string, $map, $encoding = null, $hex = false) { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); } } if (!function_exists('mb_convert_case')) { function mb_convert_case($string, $mode, $encoding = null) { return p\Mbstring::mb_convert_case($string, $mode, $encoding); } } if (!function_exists('mb_internal_encoding')) { function mb_internal_encoding($encoding = null) { return p\Mbstring::mb_internal_encoding($encoding); } } if (!function_exists('mb_language')) { function mb_language($language = null) { return p\Mbstring::mb_language($language); } } if (!function_exists('mb_list_encodings')) { function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } } if (!function_exists('mb_encoding_aliases')) { function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } } if (!function_exists('mb_check_encoding')) { function mb_check_encoding($value = null, $encoding = null) { return p\Mbstring::mb_check_encoding($value, $encoding); } } if (!function_exists('mb_detect_encoding')) { function mb_detect_encoding($string, $encodings = null, $strict = false) { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); } } if (!function_exists('mb_detect_order')) { function mb_detect_order($encoding = null) { return p\Mbstring::mb_detect_order($encoding); } } if (!function_exists('mb_parse_str')) { function mb_parse_str($string, &$result = []) { parse_str($string, $result); return (bool) $result; } } if (!function_exists('mb_strlen')) { function mb_strlen($string, $encoding = null) { return p\Mbstring::mb_strlen($string, $encoding); } } if (!function_exists('mb_strpos')) { function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); } } if (!function_exists('mb_strtolower')) { function mb_strtolower($string, $encoding = null) { return p\Mbstring::mb_strtolower($string, $encoding); } } if (!function_exists('mb_strtoupper')) { function mb_strtoupper($string, $encoding = null) { return p\Mbstring::mb_strtoupper($string, $encoding); } } if (!function_exists('mb_substitute_character')) { function mb_substitute_character($substitute_character = null) { return p\Mbstring::mb_substitute_character($substitute_character); } } if (!function_exists('mb_substr')) { function mb_substr($string, $start, $length = 2147483647, $encoding = null) { return p\Mbstring::mb_substr($string, $start, $length, $encoding); } } if (!function_exists('mb_stripos')) { function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); } } if (!function_exists('mb_stristr')) { function mb_stristr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); } } if (!function_exists('mb_strrchr')) { function mb_strrchr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); } } if (!function_exists('mb_strrichr')) { function mb_strrichr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); } } if (!function_exists('mb_strripos')) { function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); } } if (!function_exists('mb_strrpos')) { function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); } } if (!function_exists('mb_strstr')) { function mb_strstr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); } } if (!function_exists('mb_get_info')) { function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } } if (!function_exists('mb_http_output')) { function mb_http_output($encoding = null) { return p\Mbstring::mb_http_output($encoding); } } if (!function_exists('mb_strwidth')) { function mb_strwidth($string, $encoding = null) { return p\Mbstring::mb_strwidth($string, $encoding); } } if (!function_exists('mb_substr_count')) { function mb_substr_count($haystack, $needle, $encoding = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); } } if (!function_exists('mb_output_handler')) { function mb_output_handler($string, $status) { return p\Mbstring::mb_output_handler($string, $status); } } if (!function_exists('mb_http_input')) { function mb_http_input($type = null) { return p\Mbstring::mb_http_input($type); } } if (!function_exists('mb_convert_variables')) { function mb_convert_variables($to_encoding, $from_encoding, &...$vars) { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); } } if (!function_exists('mb_ord')) { function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); } } if (!function_exists('mb_chr')) { function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); } } if (!function_exists('mb_scrub')) { function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); } } if (!function_exists('mb_str_split')) { function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); } } if (!function_exists('mb_str_pad')) { function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); } } if (extension_loaded('mbstring')) { return; } if (!defined('MB_CASE_UPPER')) { define('MB_CASE_UPPER', 0); } if (!defined('MB_CASE_LOWER')) { define('MB_CASE_LOWER', 1); } if (!defined('MB_CASE_TITLE')) { define('MB_CASE_TITLE', 2); } polyfill-mbstring/composer.json 0000644 00000001730 15060133305 0012740 0 ustar 00 { "name": "symfony/polyfill-mbstring", "type": "library", "description": "Symfony polyfill for the Mbstring extension", "keywords": ["polyfill", "shim", "compatibility", "portable", "mbstring"], "homepage": "https://symfony.com", "license": "MIT", "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "require": { "php": ">=7.1" }, "provide": { "ext-mbstring": "*" }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, "files": [ "bootstrap.php" ] }, "suggest": { "ext-mbstring": "For best performance" }, "minimum-stability": "dev", "extra": { "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" } } } polyfill-mbstring/LICENSE 0000644 00000002054 15060133305 0011223 0 ustar 00 Copyright (c) 2015-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. polyfill-mbstring/Mbstring.php 0000644 00000076650 15060133305 0012531 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Polyfill\Mbstring; /** * Partial mbstring implementation in PHP, iconv based, UTF-8 centric. * * Implemented: * - mb_chr - Returns a specific character from its Unicode code point * - mb_convert_encoding - Convert character encoding * - mb_convert_variables - Convert character code in variable(s) * - mb_decode_mimeheader - Decode string in MIME header field * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED * - mb_decode_numericentity - Decode HTML numeric string reference to character * - mb_encode_numericentity - Encode character to HTML numeric string reference * - mb_convert_case - Perform case folding on a string * - mb_detect_encoding - Detect character encoding * - mb_get_info - Get internal settings of mbstring * - mb_http_input - Detect HTTP input character encoding * - mb_http_output - Set/Get HTTP output character encoding * - mb_internal_encoding - Set/Get internal character encoding * - mb_list_encodings - Returns an array of all supported encodings * - mb_ord - Returns the Unicode code point of a character * - mb_output_handler - Callback function converts character encoding in output buffer * - mb_scrub - Replaces ill-formed byte sequences with substitute characters * - mb_strlen - Get string length * - mb_strpos - Find position of first occurrence of string in a string * - mb_strrpos - Find position of last occurrence of a string in a string * - mb_str_split - Convert a string to an array * - mb_strtolower - Make a string lowercase * - mb_strtoupper - Make a string uppercase * - mb_substitute_character - Set/Get substitution character * - mb_substr - Get part of string * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive * - mb_stristr - Finds first occurrence of a string within another, case insensitive * - mb_strrchr - Finds the last occurrence of a character in a string within another * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive * - mb_strstr - Finds first occurrence of a string within another * - mb_strwidth - Return width of string * - mb_substr_count - Count the number of substring occurrences * * Not implemented: * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) * - mb_ereg_* - Regular expression with multibyte support * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable * - mb_preferred_mime_name - Get MIME charset string * - mb_regex_encoding - Returns current encoding for multibyte regex as string * - mb_regex_set_options - Set/Get the default options for mbregex functions * - mb_send_mail - Send encoded mail * - mb_split - Split multibyte string using regular expression * - mb_strcut - Get part of string * - mb_strimwidth - Get truncated string with specified width * * @author Nicolas Grekas <p@tchwork.com> * * @internal */ final class Mbstring { public const MB_CASE_FOLD = \PHP_INT_MAX; private const SIMPLE_CASE_FOLD = [ ['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"], ['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'], ]; private static $encodingList = ['ASCII', 'UTF-8']; private static $language = 'neutral'; private static $internalEncoding = 'UTF-8'; public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) { if (\is_array($fromEncoding) || (null !== $fromEncoding && false !== strpos($fromEncoding, ','))) { $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); } else { $fromEncoding = self::getEncoding($fromEncoding); } $toEncoding = self::getEncoding($toEncoding); if ('BASE64' === $fromEncoding) { $s = base64_decode($s); $fromEncoding = $toEncoding; } if ('BASE64' === $toEncoding) { return base64_encode($s); } if ('HTML-ENTITIES' === $toEncoding || 'HTML' === $toEncoding) { if ('HTML-ENTITIES' === $fromEncoding || 'HTML' === $fromEncoding) { $fromEncoding = 'Windows-1252'; } if ('UTF-8' !== $fromEncoding) { $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); } return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s); } if ('HTML-ENTITIES' === $fromEncoding) { $s = html_entity_decode($s, \ENT_COMPAT, 'UTF-8'); $fromEncoding = 'UTF-8'; } return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); } public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars) { $ok = true; array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { $ok = false; } }); return $ok ? $fromEncoding : false; } public static function mb_decode_mimeheader($s) { return iconv_mime_decode($s, 2, self::$internalEncoding); } public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) { trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', \E_USER_WARNING); } public static function mb_decode_numericentity($s, $convmap, $encoding = null) { if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING); return null; } if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) { return false; } if (null !== $encoding && !\is_scalar($encoding)) { trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING); return ''; // Instead of null (cf. mb_encode_numericentity). } $s = (string) $s; if ('' === $s) { return ''; } $encoding = self::getEncoding($encoding); if ('UTF-8' === $encoding) { $encoding = null; if (!preg_match('//u', $s)) { $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); } } else { $s = iconv($encoding, 'UTF-8//IGNORE', $s); } $cnt = floor(\count($convmap) / 4) * 4; for ($i = 0; $i < $cnt; $i += 4) { // collector_decode_htmlnumericentity ignores $convmap[$i + 3] $convmap[$i] += $convmap[$i + 2]; $convmap[$i + 1] += $convmap[$i + 2]; } $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) { $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1]; for ($i = 0; $i < $cnt; $i += 4) { if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) { return self::mb_chr($c - $convmap[$i + 2]); } } return $m[0]; }, $s); if (null === $encoding) { return $s; } return iconv('UTF-8', $encoding.'//IGNORE', $s); } public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false) { if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING); return null; } if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) { return false; } if (null !== $encoding && !\is_scalar($encoding)) { trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING); return null; // Instead of '' (cf. mb_decode_numericentity). } if (null !== $is_hex && !\is_scalar($is_hex)) { trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', \E_USER_WARNING); return null; } $s = (string) $s; if ('' === $s) { return ''; } $encoding = self::getEncoding($encoding); if ('UTF-8' === $encoding) { $encoding = null; if (!preg_match('//u', $s)) { $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); } } else { $s = iconv($encoding, 'UTF-8//IGNORE', $s); } static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; $cnt = floor(\count($convmap) / 4) * 4; $i = 0; $len = \strlen($s); $result = ''; while ($i < $len) { $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; $uchr = substr($s, $i, $ulen); $i += $ulen; $c = self::mb_ord($uchr); for ($j = 0; $j < $cnt; $j += 4) { if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) { $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3]; $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';'; continue 2; } } $result .= $uchr; } if (null === $encoding) { return $result; } return iconv('UTF-8', $encoding.'//IGNORE', $result); } public static function mb_convert_case($s, $mode, $encoding = null) { $s = (string) $s; if ('' === $s) { return ''; } $encoding = self::getEncoding($encoding); if ('UTF-8' === $encoding) { $encoding = null; if (!preg_match('//u', $s)) { $s = @iconv('UTF-8', 'UTF-8//IGNORE', $s); } } else { $s = iconv($encoding, 'UTF-8//IGNORE', $s); } if (\MB_CASE_TITLE == $mode) { static $titleRegexp = null; if (null === $titleRegexp) { $titleRegexp = self::getData('titleCaseRegexp'); } $s = preg_replace_callback($titleRegexp, [__CLASS__, 'title_case'], $s); } else { if (\MB_CASE_UPPER == $mode) { static $upper = null; if (null === $upper) { $upper = self::getData('upperCase'); } $map = $upper; } else { if (self::MB_CASE_FOLD === $mode) { static $caseFolding = null; if (null === $caseFolding) { $caseFolding = self::getData('caseFolding'); } $s = strtr($s, $caseFolding); } static $lower = null; if (null === $lower) { $lower = self::getData('lowerCase'); } $map = $lower; } static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; $i = 0; $len = \strlen($s); while ($i < $len) { $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; $uchr = substr($s, $i, $ulen); $i += $ulen; if (isset($map[$uchr])) { $uchr = $map[$uchr]; $nlen = \strlen($uchr); if ($nlen == $ulen) { $nlen = $i; do { $s[--$nlen] = $uchr[--$ulen]; } while ($ulen); } else { $s = substr_replace($s, $uchr, $i - $ulen, $ulen); $len += $nlen - $ulen; $i += $nlen - $ulen; } } } } if (null === $encoding) { return $s; } return iconv('UTF-8', $encoding.'//IGNORE', $s); } public static function mb_internal_encoding($encoding = null) { if (null === $encoding) { return self::$internalEncoding; } $normalizedEncoding = self::getEncoding($encoding); if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, ' ')) { self::$internalEncoding = $normalizedEncoding; return true; } if (80000 > \PHP_VERSION_ID) { return false; } throw new \ValueError(sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding)); } public static function mb_language($lang = null) { if (null === $lang) { return self::$language; } switch ($normalizedLang = strtolower($lang)) { case 'uni': case 'neutral': self::$language = $normalizedLang; return true; } if (80000 > \PHP_VERSION_ID) { return false; } throw new \ValueError(sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang)); } public static function mb_list_encodings() { return ['UTF-8']; } public static function mb_encoding_aliases($encoding) { switch (strtoupper($encoding)) { case 'UTF8': case 'UTF-8': return ['utf8']; } return false; } public static function mb_check_encoding($var = null, $encoding = null) { if (PHP_VERSION_ID < 70200 && \is_array($var)) { trigger_error('mb_check_encoding() expects parameter 1 to be string, array given', \E_USER_WARNING); return null; } if (null === $encoding) { if (null === $var) { return false; } $encoding = self::$internalEncoding; } if (!\is_array($var)) { return self::mb_detect_encoding($var, [$encoding]) || false !== @iconv($encoding, $encoding, $var); } foreach ($var as $key => $value) { if (!self::mb_check_encoding($key, $encoding)) { return false; } if (!self::mb_check_encoding($value, $encoding)) { return false; } } return true; } public static function mb_detect_encoding($str, $encodingList = null, $strict = false) { if (null === $encodingList) { $encodingList = self::$encodingList; } else { if (!\is_array($encodingList)) { $encodingList = array_map('trim', explode(',', $encodingList)); } $encodingList = array_map('strtoupper', $encodingList); } foreach ($encodingList as $enc) { switch ($enc) { case 'ASCII': if (!preg_match('/[\x80-\xFF]/', $str)) { return $enc; } break; case 'UTF8': case 'UTF-8': if (preg_match('//u', $str)) { return 'UTF-8'; } break; default: if (0 === strncmp($enc, 'ISO-8859-', 9)) { return $enc; } } } return false; } public static function mb_detect_order($encodingList = null) { if (null === $encodingList) { return self::$encodingList; } if (!\is_array($encodingList)) { $encodingList = array_map('trim', explode(',', $encodingList)); } $encodingList = array_map('strtoupper', $encodingList); foreach ($encodingList as $enc) { switch ($enc) { default: if (strncmp($enc, 'ISO-8859-', 9)) { return false; } // no break case 'ASCII': case 'UTF8': case 'UTF-8': } } self::$encodingList = $encodingList; return true; } public static function mb_strlen($s, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { return \strlen($s); } return @iconv_strlen($s, $encoding); } public static function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { return strpos($haystack, $needle, $offset); } $needle = (string) $needle; if ('' === $needle) { if (80000 > \PHP_VERSION_ID) { trigger_error(__METHOD__.': Empty delimiter', \E_USER_WARNING); return false; } return 0; } return iconv_strpos($haystack, $needle, $offset, $encoding); } public static function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { return strrpos($haystack, $needle, $offset); } if ($offset != (int) $offset) { $offset = 0; } elseif ($offset = (int) $offset) { if ($offset < 0) { if (0 > $offset += self::mb_strlen($needle)) { $haystack = self::mb_substr($haystack, 0, $offset, $encoding); } $offset = 0; } else { $haystack = self::mb_substr($haystack, $offset, 2147483647, $encoding); } } $pos = '' !== $needle || 80000 > \PHP_VERSION_ID ? iconv_strrpos($haystack, $needle, $encoding) : self::mb_strlen($haystack, $encoding); return false !== $pos ? $offset + $pos : false; } public static function mb_str_split($string, $split_length = 1, $encoding = null) { if (null !== $string && !\is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) { trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING); return null; } if (1 > $split_length = (int) $split_length) { if (80000 > \PHP_VERSION_ID) { trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING); return false; } throw new \ValueError('Argument #2 ($length) must be greater than 0'); } if (null === $encoding) { $encoding = mb_internal_encoding(); } if ('UTF-8' === $encoding = self::getEncoding($encoding)) { $rx = '/('; while (65535 < $split_length) { $rx .= '.{65535}'; $split_length -= 65535; } $rx .= '.{'.$split_length.'})/us'; return preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); } $result = []; $length = mb_strlen($string, $encoding); for ($i = 0; $i < $length; $i += $split_length) { $result[] = mb_substr($string, $i, $split_length, $encoding); } return $result; } public static function mb_strtolower($s, $encoding = null) { return self::mb_convert_case($s, \MB_CASE_LOWER, $encoding); } public static function mb_strtoupper($s, $encoding = null) { return self::mb_convert_case($s, \MB_CASE_UPPER, $encoding); } public static function mb_substitute_character($c = null) { if (null === $c) { return 'none'; } if (0 === strcasecmp($c, 'none')) { return true; } if (80000 > \PHP_VERSION_ID) { return false; } if (\is_int($c) || 'long' === $c || 'entity' === $c) { return false; } throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint'); } public static function mb_substr($s, $start, $length = null, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { return (string) substr($s, $start, null === $length ? 2147483647 : $length); } if ($start < 0) { $start = iconv_strlen($s, $encoding) + $start; if ($start < 0) { $start = 0; } } if (null === $length) { $length = 2147483647; } elseif ($length < 0) { $length = iconv_strlen($s, $encoding) + $length - $start; if ($length < 0) { return ''; } } return (string) iconv_substr($s, $start, $length, $encoding); } public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { [$haystack, $needle] = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], [ self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding), self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding), ]); return self::mb_strpos($haystack, $needle, $offset, $encoding); } public static function mb_stristr($haystack, $needle, $part = false, $encoding = null) { $pos = self::mb_stripos($haystack, $needle, 0, $encoding); return self::getSubpart($pos, $part, $haystack, $encoding); } public static function mb_strrchr($haystack, $needle, $part = false, $encoding = null) { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { $pos = strrpos($haystack, $needle); } else { $needle = self::mb_substr($needle, 0, 1, $encoding); $pos = iconv_strrpos($haystack, $needle, $encoding); } return self::getSubpart($pos, $part, $haystack, $encoding); } public static function mb_strrichr($haystack, $needle, $part = false, $encoding = null) { $needle = self::mb_substr($needle, 0, 1, $encoding); $pos = self::mb_strripos($haystack, $needle, $encoding); return self::getSubpart($pos, $part, $haystack, $encoding); } public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { $haystack = self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding); $needle = self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding); $haystack = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $haystack); $needle = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $needle); return self::mb_strrpos($haystack, $needle, $offset, $encoding); } public static function mb_strstr($haystack, $needle, $part = false, $encoding = null) { $pos = strpos($haystack, $needle); if (false === $pos) { return false; } if ($part) { return substr($haystack, 0, $pos); } return substr($haystack, $pos); } public static function mb_get_info($type = 'all') { $info = [ 'internal_encoding' => self::$internalEncoding, 'http_output' => 'pass', 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', 'func_overload' => 0, 'func_overload_list' => 'no overload', 'mail_charset' => 'UTF-8', 'mail_header_encoding' => 'BASE64', 'mail_body_encoding' => 'BASE64', 'illegal_chars' => 0, 'encoding_translation' => 'Off', 'language' => self::$language, 'detect_order' => self::$encodingList, 'substitute_character' => 'none', 'strict_detection' => 'Off', ]; if ('all' === $type) { return $info; } if (isset($info[$type])) { return $info[$type]; } return false; } public static function mb_http_input($type = '') { return false; } public static function mb_http_output($encoding = null) { return null !== $encoding ? 'pass' === $encoding : 'pass'; } public static function mb_strwidth($s, $encoding = null) { $encoding = self::getEncoding($encoding); if ('UTF-8' !== $encoding) { $s = iconv($encoding, 'UTF-8//IGNORE', $s); } $s = preg_replace('/[\x{1100}-\x{115F}\x{2329}\x{232A}\x{2E80}-\x{303E}\x{3040}-\x{A4CF}\x{AC00}-\x{D7A3}\x{F900}-\x{FAFF}\x{FE10}-\x{FE19}\x{FE30}-\x{FE6F}\x{FF00}-\x{FF60}\x{FFE0}-\x{FFE6}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}]/u', '', $s, -1, $wide); return ($wide << 1) + iconv_strlen($s, 'UTF-8'); } public static function mb_substr_count($haystack, $needle, $encoding = null) { return substr_count($haystack, $needle); } public static function mb_output_handler($contents, $status) { return $contents; } public static function mb_chr($code, $encoding = null) { if (0x80 > $code %= 0x200000) { $s = \chr($code); } elseif (0x800 > $code) { $s = \chr(0xC0 | $code >> 6).\chr(0x80 | $code & 0x3F); } elseif (0x10000 > $code) { $s = \chr(0xE0 | $code >> 12).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); } else { $s = \chr(0xF0 | $code >> 18).\chr(0x80 | $code >> 12 & 0x3F).\chr(0x80 | $code >> 6 & 0x3F).\chr(0x80 | $code & 0x3F); } if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { $s = mb_convert_encoding($s, $encoding, 'UTF-8'); } return $s; } public static function mb_ord($s, $encoding = null) { if ('UTF-8' !== $encoding = self::getEncoding($encoding)) { $s = mb_convert_encoding($s, 'UTF-8', $encoding); } if (1 === \strlen($s)) { return \ord($s); } $code = ($s = unpack('C*', substr($s, 0, 4))) ? $s[1] : 0; if (0xF0 <= $code) { return (($code - 0xF0) << 18) + (($s[2] - 0x80) << 12) + (($s[3] - 0x80) << 6) + $s[4] - 0x80; } if (0xE0 <= $code) { return (($code - 0xE0) << 12) + (($s[2] - 0x80) << 6) + $s[3] - 0x80; } if (0xC0 <= $code) { return (($code - 0xC0) << 6) + $s[2] - 0x80; } return $code; } public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, string $encoding = null): string { if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) { throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH'); } if (null === $encoding) { $encoding = self::mb_internal_encoding(); } try { $validEncoding = @self::mb_check_encoding('', $encoding); } catch (\ValueError $e) { throw new \ValueError(sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding)); } // BC for PHP 7.3 and lower if (!$validEncoding) { throw new \ValueError(sprintf('mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given', $encoding)); } if (self::mb_strlen($pad_string, $encoding) <= 0) { throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string'); } $paddingRequired = $length - self::mb_strlen($string, $encoding); if ($paddingRequired < 1) { return $string; } switch ($pad_type) { case \STR_PAD_LEFT: return self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding).$string; case \STR_PAD_RIGHT: return $string.self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding); default: $leftPaddingLength = floor($paddingRequired / 2); $rightPaddingLength = $paddingRequired - $leftPaddingLength; return self::mb_substr(str_repeat($pad_string, $leftPaddingLength), 0, $leftPaddingLength, $encoding).$string.self::mb_substr(str_repeat($pad_string, $rightPaddingLength), 0, $rightPaddingLength, $encoding); } } private static function getSubpart($pos, $part, $haystack, $encoding) { if (false === $pos) { return false; } if ($part) { return self::mb_substr($haystack, 0, $pos, $encoding); } return self::mb_substr($haystack, $pos, null, $encoding); } private static function html_encoding_callback(array $m) { $i = 1; $entities = ''; $m = unpack('C*', htmlentities($m[0], \ENT_COMPAT, 'UTF-8')); while (isset($m[$i])) { if (0x80 > $m[$i]) { $entities .= \chr($m[$i++]); continue; } if (0xF0 <= $m[$i]) { $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; } elseif (0xE0 <= $m[$i]) { $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; } else { $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80; } $entities .= '&#'.$c.';'; } return $entities; } private static function title_case(array $s) { return self::mb_convert_case($s[1], \MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], \MB_CASE_LOWER, 'UTF-8'); } private static function getData($file) { if (file_exists($file = __DIR__.'/Resources/unidata/'.$file.'.php')) { return require $file; } return false; } private static function getEncoding($encoding) { if (null === $encoding) { return self::$internalEncoding; } if ('UTF-8' === $encoding) { return 'UTF-8'; } $encoding = strtoupper($encoding); if ('8BIT' === $encoding || 'BINARY' === $encoding) { return 'CP850'; } if ('UTF8' === $encoding) { return 'UTF-8'; } return $encoding; } } polyfill-mbstring/Resources/unidata/lowerCase.php 0000644 00000057733 15060133305 0016270 0 ustar 00 <?php return array ( 'A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e', 'F' => 'f', 'G' => 'g', 'H' => 'h', 'I' => 'i', 'J' => 'j', 'K' => 'k', 'L' => 'l', 'M' => 'm', 'N' => 'n', 'O' => 'o', 'P' => 'p', 'Q' => 'q', 'R' => 'r', 'S' => 's', 'T' => 't', 'U' => 'u', 'V' => 'v', 'W' => 'w', 'X' => 'x', 'Y' => 'y', 'Z' => 'z', 'À' => 'à', 'Á' => 'á', 'Â' => 'â', 'Ã' => 'ã', 'Ä' => 'ä', 'Å' => 'å', 'Æ' => 'æ', 'Ç' => 'ç', 'È' => 'è', 'É' => 'é', 'Ê' => 'ê', 'Ë' => 'ë', 'Ì' => 'ì', 'Í' => 'í', 'Î' => 'î', 'Ï' => 'ï', 'Ð' => 'ð', 'Ñ' => 'ñ', 'Ò' => 'ò', 'Ó' => 'ó', 'Ô' => 'ô', 'Õ' => 'õ', 'Ö' => 'ö', 'Ø' => 'ø', 'Ù' => 'ù', 'Ú' => 'ú', 'Û' => 'û', 'Ü' => 'ü', 'Ý' => 'ý', 'Þ' => 'þ', 'Ā' => 'ā', 'Ă' => 'ă', 'Ą' => 'ą', 'Ć' => 'ć', 'Ĉ' => 'ĉ', 'Ċ' => 'ċ', 'Č' => 'č', 'Ď' => 'ď', 'Đ' => 'đ', 'Ē' => 'ē', 'Ĕ' => 'ĕ', 'Ė' => 'ė', 'Ę' => 'ę', 'Ě' => 'ě', 'Ĝ' => 'ĝ', 'Ğ' => 'ğ', 'Ġ' => 'ġ', 'Ģ' => 'ģ', 'Ĥ' => 'ĥ', 'Ħ' => 'ħ', 'Ĩ' => 'ĩ', 'Ī' => 'ī', 'Ĭ' => 'ĭ', 'Į' => 'į', 'İ' => 'i̇', 'IJ' => 'ij', 'Ĵ' => 'ĵ', 'Ķ' => 'ķ', 'Ĺ' => 'ĺ', 'Ļ' => 'ļ', 'Ľ' => 'ľ', 'Ŀ' => 'ŀ', 'Ł' => 'ł', 'Ń' => 'ń', 'Ņ' => 'ņ', 'Ň' => 'ň', 'Ŋ' => 'ŋ', 'Ō' => 'ō', 'Ŏ' => 'ŏ', 'Ő' => 'ő', 'Œ' => 'œ', 'Ŕ' => 'ŕ', 'Ŗ' => 'ŗ', 'Ř' => 'ř', 'Ś' => 'ś', 'Ŝ' => 'ŝ', 'Ş' => 'ş', 'Š' => 'š', 'Ţ' => 'ţ', 'Ť' => 'ť', 'Ŧ' => 'ŧ', 'Ũ' => 'ũ', 'Ū' => 'ū', 'Ŭ' => 'ŭ', 'Ů' => 'ů', 'Ű' => 'ű', 'Ų' => 'ų', 'Ŵ' => 'ŵ', 'Ŷ' => 'ŷ', 'Ÿ' => 'ÿ', 'Ź' => 'ź', 'Ż' => 'ż', 'Ž' => 'ž', 'Ɓ' => 'ɓ', 'Ƃ' => 'ƃ', 'Ƅ' => 'ƅ', 'Ɔ' => 'ɔ', 'Ƈ' => 'ƈ', 'Ɖ' => 'ɖ', 'Ɗ' => 'ɗ', 'Ƌ' => 'ƌ', 'Ǝ' => 'ǝ', 'Ə' => 'ə', 'Ɛ' => 'ɛ', 'Ƒ' => 'ƒ', 'Ɠ' => 'ɠ', 'Ɣ' => 'ɣ', 'Ɩ' => 'ɩ', 'Ɨ' => 'ɨ', 'Ƙ' => 'ƙ', 'Ɯ' => 'ɯ', 'Ɲ' => 'ɲ', 'Ɵ' => 'ɵ', 'Ơ' => 'ơ', 'Ƣ' => 'ƣ', 'Ƥ' => 'ƥ', 'Ʀ' => 'ʀ', 'Ƨ' => 'ƨ', 'Ʃ' => 'ʃ', 'Ƭ' => 'ƭ', 'Ʈ' => 'ʈ', 'Ư' => 'ư', 'Ʊ' => 'ʊ', 'Ʋ' => 'ʋ', 'Ƴ' => 'ƴ', 'Ƶ' => 'ƶ', 'Ʒ' => 'ʒ', 'Ƹ' => 'ƹ', 'Ƽ' => 'ƽ', 'DŽ' => 'dž', 'Dž' => 'dž', 'LJ' => 'lj', 'Lj' => 'lj', 'NJ' => 'nj', 'Nj' => 'nj', 'Ǎ' => 'ǎ', 'Ǐ' => 'ǐ', 'Ǒ' => 'ǒ', 'Ǔ' => 'ǔ', 'Ǖ' => 'ǖ', 'Ǘ' => 'ǘ', 'Ǚ' => 'ǚ', 'Ǜ' => 'ǜ', 'Ǟ' => 'ǟ', 'Ǡ' => 'ǡ', 'Ǣ' => 'ǣ', 'Ǥ' => 'ǥ', 'Ǧ' => 'ǧ', 'Ǩ' => 'ǩ', 'Ǫ' => 'ǫ', 'Ǭ' => 'ǭ', 'Ǯ' => 'ǯ', 'DZ' => 'dz', 'Dz' => 'dz', 'Ǵ' => 'ǵ', 'Ƕ' => 'ƕ', 'Ƿ' => 'ƿ', 'Ǹ' => 'ǹ', 'Ǻ' => 'ǻ', 'Ǽ' => 'ǽ', 'Ǿ' => 'ǿ', 'Ȁ' => 'ȁ', 'Ȃ' => 'ȃ', 'Ȅ' => 'ȅ', 'Ȇ' => 'ȇ', 'Ȉ' => 'ȉ', 'Ȋ' => 'ȋ', 'Ȍ' => 'ȍ', 'Ȏ' => 'ȏ', 'Ȑ' => 'ȑ', 'Ȓ' => 'ȓ', 'Ȕ' => 'ȕ', 'Ȗ' => 'ȗ', 'Ș' => 'ș', 'Ț' => 'ț', 'Ȝ' => 'ȝ', 'Ȟ' => 'ȟ', 'Ƞ' => 'ƞ', 'Ȣ' => 'ȣ', 'Ȥ' => 'ȥ', 'Ȧ' => 'ȧ', 'Ȩ' => 'ȩ', 'Ȫ' => 'ȫ', 'Ȭ' => 'ȭ', 'Ȯ' => 'ȯ', 'Ȱ' => 'ȱ', 'Ȳ' => 'ȳ', 'Ⱥ' => 'ⱥ', 'Ȼ' => 'ȼ', 'Ƚ' => 'ƚ', 'Ⱦ' => 'ⱦ', 'Ɂ' => 'ɂ', 'Ƀ' => 'ƀ', 'Ʉ' => 'ʉ', 'Ʌ' => 'ʌ', 'Ɇ' => 'ɇ', 'Ɉ' => 'ɉ', 'Ɋ' => 'ɋ', 'Ɍ' => 'ɍ', 'Ɏ' => 'ɏ', 'Ͱ' => 'ͱ', 'Ͳ' => 'ͳ', 'Ͷ' => 'ͷ', 'Ϳ' => 'ϳ', 'Ά' => 'ά', 'Έ' => 'έ', 'Ή' => 'ή', 'Ί' => 'ί', 'Ό' => 'ό', 'Ύ' => 'ύ', 'Ώ' => 'ώ', 'Α' => 'α', 'Β' => 'β', 'Γ' => 'γ', 'Δ' => 'δ', 'Ε' => 'ε', 'Ζ' => 'ζ', 'Η' => 'η', 'Θ' => 'θ', 'Ι' => 'ι', 'Κ' => 'κ', 'Λ' => 'λ', 'Μ' => 'μ', 'Ν' => 'ν', 'Ξ' => 'ξ', 'Ο' => 'ο', 'Π' => 'π', 'Ρ' => 'ρ', 'Σ' => 'σ', 'Τ' => 'τ', 'Υ' => 'υ', 'Φ' => 'φ', 'Χ' => 'χ', 'Ψ' => 'ψ', 'Ω' => 'ω', 'Ϊ' => 'ϊ', 'Ϋ' => 'ϋ', 'Ϗ' => 'ϗ', 'Ϙ' => 'ϙ', 'Ϛ' => 'ϛ', 'Ϝ' => 'ϝ', 'Ϟ' => 'ϟ', 'Ϡ' => 'ϡ', 'Ϣ' => 'ϣ', 'Ϥ' => 'ϥ', 'Ϧ' => 'ϧ', 'Ϩ' => 'ϩ', 'Ϫ' => 'ϫ', 'Ϭ' => 'ϭ', 'Ϯ' => 'ϯ', 'ϴ' => 'θ', 'Ϸ' => 'ϸ', 'Ϲ' => 'ϲ', 'Ϻ' => 'ϻ', 'Ͻ' => 'ͻ', 'Ͼ' => 'ͼ', 'Ͽ' => 'ͽ', 'Ѐ' => 'ѐ', 'Ё' => 'ё', 'Ђ' => 'ђ', 'Ѓ' => 'ѓ', 'Є' => 'є', 'Ѕ' => 'ѕ', 'І' => 'і', 'Ї' => 'ї', 'Ј' => 'ј', 'Љ' => 'љ', 'Њ' => 'њ', 'Ћ' => 'ћ', 'Ќ' => 'ќ', 'Ѝ' => 'ѝ', 'Ў' => 'ў', 'Џ' => 'џ', 'А' => 'а', 'Б' => 'б', 'В' => 'в', 'Г' => 'г', 'Д' => 'д', 'Е' => 'е', 'Ж' => 'ж', 'З' => 'з', 'И' => 'и', 'Й' => 'й', 'К' => 'к', 'Л' => 'л', 'М' => 'м', 'Н' => 'н', 'О' => 'о', 'П' => 'п', 'Р' => 'р', 'С' => 'с', 'Т' => 'т', 'У' => 'у', 'Ф' => 'ф', 'Х' => 'х', 'Ц' => 'ц', 'Ч' => 'ч', 'Ш' => 'ш', 'Щ' => 'щ', 'Ъ' => 'ъ', 'Ы' => 'ы', 'Ь' => 'ь', 'Э' => 'э', 'Ю' => 'ю', 'Я' => 'я', 'Ѡ' => 'ѡ', 'Ѣ' => 'ѣ', 'Ѥ' => 'ѥ', 'Ѧ' => 'ѧ', 'Ѩ' => 'ѩ', 'Ѫ' => 'ѫ', 'Ѭ' => 'ѭ', 'Ѯ' => 'ѯ', 'Ѱ' => 'ѱ', 'Ѳ' => 'ѳ', 'Ѵ' => 'ѵ', 'Ѷ' => 'ѷ', 'Ѹ' => 'ѹ', 'Ѻ' => 'ѻ', 'Ѽ' => 'ѽ', 'Ѿ' => 'ѿ', 'Ҁ' => 'ҁ', 'Ҋ' => 'ҋ', 'Ҍ' => 'ҍ', 'Ҏ' => 'ҏ', 'Ґ' => 'ґ', 'Ғ' => 'ғ', 'Ҕ' => 'ҕ', 'Җ' => 'җ', 'Ҙ' => 'ҙ', 'Қ' => 'қ', 'Ҝ' => 'ҝ', 'Ҟ' => 'ҟ', 'Ҡ' => 'ҡ', 'Ң' => 'ң', 'Ҥ' => 'ҥ', 'Ҧ' => 'ҧ', 'Ҩ' => 'ҩ', 'Ҫ' => 'ҫ', 'Ҭ' => 'ҭ', 'Ү' => 'ү', 'Ұ' => 'ұ', 'Ҳ' => 'ҳ', 'Ҵ' => 'ҵ', 'Ҷ' => 'ҷ', 'Ҹ' => 'ҹ', 'Һ' => 'һ', 'Ҽ' => 'ҽ', 'Ҿ' => 'ҿ', 'Ӏ' => 'ӏ', 'Ӂ' => 'ӂ', 'Ӄ' => 'ӄ', 'Ӆ' => 'ӆ', 'Ӈ' => 'ӈ', 'Ӊ' => 'ӊ', 'Ӌ' => 'ӌ', 'Ӎ' => 'ӎ', 'Ӑ' => 'ӑ', 'Ӓ' => 'ӓ', 'Ӕ' => 'ӕ', 'Ӗ' => 'ӗ', 'Ә' => 'ә', 'Ӛ' => 'ӛ', 'Ӝ' => 'ӝ', 'Ӟ' => 'ӟ', 'Ӡ' => 'ӡ', 'Ӣ' => 'ӣ', 'Ӥ' => 'ӥ', 'Ӧ' => 'ӧ', 'Ө' => 'ө', 'Ӫ' => 'ӫ', 'Ӭ' => 'ӭ', 'Ӯ' => 'ӯ', 'Ӱ' => 'ӱ', 'Ӳ' => 'ӳ', 'Ӵ' => 'ӵ', 'Ӷ' => 'ӷ', 'Ӹ' => 'ӹ', 'Ӻ' => 'ӻ', 'Ӽ' => 'ӽ', 'Ӿ' => 'ӿ', 'Ԁ' => 'ԁ', 'Ԃ' => 'ԃ', 'Ԅ' => 'ԅ', 'Ԇ' => 'ԇ', 'Ԉ' => 'ԉ', 'Ԋ' => 'ԋ', 'Ԍ' => 'ԍ', 'Ԏ' => 'ԏ', 'Ԑ' => 'ԑ', 'Ԓ' => 'ԓ', 'Ԕ' => 'ԕ', 'Ԗ' => 'ԗ', 'Ԙ' => 'ԙ', 'Ԛ' => 'ԛ', 'Ԝ' => 'ԝ', 'Ԟ' => 'ԟ', 'Ԡ' => 'ԡ', 'Ԣ' => 'ԣ', 'Ԥ' => 'ԥ', 'Ԧ' => 'ԧ', 'Ԩ' => 'ԩ', 'Ԫ' => 'ԫ', 'Ԭ' => 'ԭ', 'Ԯ' => 'ԯ', 'Ա' => 'ա', 'Բ' => 'բ', 'Գ' => 'գ', 'Դ' => 'դ', 'Ե' => 'ե', 'Զ' => 'զ', 'Է' => 'է', 'Ը' => 'ը', 'Թ' => 'թ', 'Ժ' => 'ժ', 'Ի' => 'ի', 'Լ' => 'լ', 'Խ' => 'խ', 'Ծ' => 'ծ', 'Կ' => 'կ', 'Հ' => 'հ', 'Ձ' => 'ձ', 'Ղ' => 'ղ', 'Ճ' => 'ճ', 'Մ' => 'մ', 'Յ' => 'յ', 'Ն' => 'ն', 'Շ' => 'շ', 'Ո' => 'ո', 'Չ' => 'չ', 'Պ' => 'պ', 'Ջ' => 'ջ', 'Ռ' => 'ռ', 'Ս' => 'ս', 'Վ' => 'վ', 'Տ' => 'տ', 'Ր' => 'ր', 'Ց' => 'ց', 'Ւ' => 'ւ', 'Փ' => 'փ', 'Ք' => 'ք', 'Օ' => 'օ', 'Ֆ' => 'ֆ', 'Ⴀ' => 'ⴀ', 'Ⴁ' => 'ⴁ', 'Ⴂ' => 'ⴂ', 'Ⴃ' => 'ⴃ', 'Ⴄ' => 'ⴄ', 'Ⴅ' => 'ⴅ', 'Ⴆ' => 'ⴆ', 'Ⴇ' => 'ⴇ', 'Ⴈ' => 'ⴈ', 'Ⴉ' => 'ⴉ', 'Ⴊ' => 'ⴊ', 'Ⴋ' => 'ⴋ', 'Ⴌ' => 'ⴌ', 'Ⴍ' => 'ⴍ', 'Ⴎ' => 'ⴎ', 'Ⴏ' => 'ⴏ', 'Ⴐ' => 'ⴐ', 'Ⴑ' => 'ⴑ', 'Ⴒ' => 'ⴒ', 'Ⴓ' => 'ⴓ', 'Ⴔ' => 'ⴔ', 'Ⴕ' => 'ⴕ', 'Ⴖ' => 'ⴖ', 'Ⴗ' => 'ⴗ', 'Ⴘ' => 'ⴘ', 'Ⴙ' => 'ⴙ', 'Ⴚ' => 'ⴚ', 'Ⴛ' => 'ⴛ', 'Ⴜ' => 'ⴜ', 'Ⴝ' => 'ⴝ', 'Ⴞ' => 'ⴞ', 'Ⴟ' => 'ⴟ', 'Ⴠ' => 'ⴠ', 'Ⴡ' => 'ⴡ', 'Ⴢ' => 'ⴢ', 'Ⴣ' => 'ⴣ', 'Ⴤ' => 'ⴤ', 'Ⴥ' => 'ⴥ', 'Ⴧ' => 'ⴧ', 'Ⴭ' => 'ⴭ', 'Ꭰ' => 'ꭰ', 'Ꭱ' => 'ꭱ', 'Ꭲ' => 'ꭲ', 'Ꭳ' => 'ꭳ', 'Ꭴ' => 'ꭴ', 'Ꭵ' => 'ꭵ', 'Ꭶ' => 'ꭶ', 'Ꭷ' => 'ꭷ', 'Ꭸ' => 'ꭸ', 'Ꭹ' => 'ꭹ', 'Ꭺ' => 'ꭺ', 'Ꭻ' => 'ꭻ', 'Ꭼ' => 'ꭼ', 'Ꭽ' => 'ꭽ', 'Ꭾ' => 'ꭾ', 'Ꭿ' => 'ꭿ', 'Ꮀ' => 'ꮀ', 'Ꮁ' => 'ꮁ', 'Ꮂ' => 'ꮂ', 'Ꮃ' => 'ꮃ', 'Ꮄ' => 'ꮄ', 'Ꮅ' => 'ꮅ', 'Ꮆ' => 'ꮆ', 'Ꮇ' => 'ꮇ', 'Ꮈ' => 'ꮈ', 'Ꮉ' => 'ꮉ', 'Ꮊ' => 'ꮊ', 'Ꮋ' => 'ꮋ', 'Ꮌ' => 'ꮌ', 'Ꮍ' => 'ꮍ', 'Ꮎ' => 'ꮎ', 'Ꮏ' => 'ꮏ', 'Ꮐ' => 'ꮐ', 'Ꮑ' => 'ꮑ', 'Ꮒ' => 'ꮒ', 'Ꮓ' => 'ꮓ', 'Ꮔ' => 'ꮔ', 'Ꮕ' => 'ꮕ', 'Ꮖ' => 'ꮖ', 'Ꮗ' => 'ꮗ', 'Ꮘ' => 'ꮘ', 'Ꮙ' => 'ꮙ', 'Ꮚ' => 'ꮚ', 'Ꮛ' => 'ꮛ', 'Ꮜ' => 'ꮜ', 'Ꮝ' => 'ꮝ', 'Ꮞ' => 'ꮞ', 'Ꮟ' => 'ꮟ', 'Ꮠ' => 'ꮠ', 'Ꮡ' => 'ꮡ', 'Ꮢ' => 'ꮢ', 'Ꮣ' => 'ꮣ', 'Ꮤ' => 'ꮤ', 'Ꮥ' => 'ꮥ', 'Ꮦ' => 'ꮦ', 'Ꮧ' => 'ꮧ', 'Ꮨ' => 'ꮨ', 'Ꮩ' => 'ꮩ', 'Ꮪ' => 'ꮪ', 'Ꮫ' => 'ꮫ', 'Ꮬ' => 'ꮬ', 'Ꮭ' => 'ꮭ', 'Ꮮ' => 'ꮮ', 'Ꮯ' => 'ꮯ', 'Ꮰ' => 'ꮰ', 'Ꮱ' => 'ꮱ', 'Ꮲ' => 'ꮲ', 'Ꮳ' => 'ꮳ', 'Ꮴ' => 'ꮴ', 'Ꮵ' => 'ꮵ', 'Ꮶ' => 'ꮶ', 'Ꮷ' => 'ꮷ', 'Ꮸ' => 'ꮸ', 'Ꮹ' => 'ꮹ', 'Ꮺ' => 'ꮺ', 'Ꮻ' => 'ꮻ', 'Ꮼ' => 'ꮼ', 'Ꮽ' => 'ꮽ', 'Ꮾ' => 'ꮾ', 'Ꮿ' => 'ꮿ', 'Ᏸ' => 'ᏸ', 'Ᏹ' => 'ᏹ', 'Ᏺ' => 'ᏺ', 'Ᏻ' => 'ᏻ', 'Ᏼ' => 'ᏼ', 'Ᏽ' => 'ᏽ', 'Ა' => 'ა', 'Ბ' => 'ბ', 'Გ' => 'გ', 'Დ' => 'დ', 'Ე' => 'ე', 'Ვ' => 'ვ', 'Ზ' => 'ზ', 'Თ' => 'თ', 'Ი' => 'ი', 'Კ' => 'კ', 'Ლ' => 'ლ', 'Მ' => 'მ', 'Ნ' => 'ნ', 'Ო' => 'ო', 'Პ' => 'პ', 'Ჟ' => 'ჟ', 'Რ' => 'რ', 'Ს' => 'ს', 'Ტ' => 'ტ', 'Უ' => 'უ', 'Ფ' => 'ფ', 'Ქ' => 'ქ', 'Ღ' => 'ღ', 'Ყ' => 'ყ', 'Შ' => 'შ', 'Ჩ' => 'ჩ', 'Ც' => 'ც', 'Ძ' => 'ძ', 'Წ' => 'წ', 'Ჭ' => 'ჭ', 'Ხ' => 'ხ', 'Ჯ' => 'ჯ', 'Ჰ' => 'ჰ', 'Ჱ' => 'ჱ', 'Ჲ' => 'ჲ', 'Ჳ' => 'ჳ', 'Ჴ' => 'ჴ', 'Ჵ' => 'ჵ', 'Ჶ' => 'ჶ', 'Ჷ' => 'ჷ', 'Ჸ' => 'ჸ', 'Ჹ' => 'ჹ', 'Ჺ' => 'ჺ', 'Ჽ' => 'ჽ', 'Ჾ' => 'ჾ', 'Ჿ' => 'ჿ', 'Ḁ' => 'ḁ', 'Ḃ' => 'ḃ', 'Ḅ' => 'ḅ', 'Ḇ' => 'ḇ', 'Ḉ' => 'ḉ', 'Ḋ' => 'ḋ', 'Ḍ' => 'ḍ', 'Ḏ' => 'ḏ', 'Ḑ' => 'ḑ', 'Ḓ' => 'ḓ', 'Ḕ' => 'ḕ', 'Ḗ' => 'ḗ', 'Ḙ' => 'ḙ', 'Ḛ' => 'ḛ', 'Ḝ' => 'ḝ', 'Ḟ' => 'ḟ', 'Ḡ' => 'ḡ', 'Ḣ' => 'ḣ', 'Ḥ' => 'ḥ', 'Ḧ' => 'ḧ', 'Ḩ' => 'ḩ', 'Ḫ' => 'ḫ', 'Ḭ' => 'ḭ', 'Ḯ' => 'ḯ', 'Ḱ' => 'ḱ', 'Ḳ' => 'ḳ', 'Ḵ' => 'ḵ', 'Ḷ' => 'ḷ', 'Ḹ' => 'ḹ', 'Ḻ' => 'ḻ', 'Ḽ' => 'ḽ', 'Ḿ' => 'ḿ', 'Ṁ' => 'ṁ', 'Ṃ' => 'ṃ', 'Ṅ' => 'ṅ', 'Ṇ' => 'ṇ', 'Ṉ' => 'ṉ', 'Ṋ' => 'ṋ', 'Ṍ' => 'ṍ', 'Ṏ' => 'ṏ', 'Ṑ' => 'ṑ', 'Ṓ' => 'ṓ', 'Ṕ' => 'ṕ', 'Ṗ' => 'ṗ', 'Ṙ' => 'ṙ', 'Ṛ' => 'ṛ', 'Ṝ' => 'ṝ', 'Ṟ' => 'ṟ', 'Ṡ' => 'ṡ', 'Ṣ' => 'ṣ', 'Ṥ' => 'ṥ', 'Ṧ' => 'ṧ', 'Ṩ' => 'ṩ', 'Ṫ' => 'ṫ', 'Ṭ' => 'ṭ', 'Ṯ' => 'ṯ', 'Ṱ' => 'ṱ', 'Ṳ' => 'ṳ', 'Ṵ' => 'ṵ', 'Ṷ' => 'ṷ', 'Ṹ' => 'ṹ', 'Ṻ' => 'ṻ', 'Ṽ' => 'ṽ', 'Ṿ' => 'ṿ', 'Ẁ' => 'ẁ', 'Ẃ' => 'ẃ', 'Ẅ' => 'ẅ', 'Ẇ' => 'ẇ', 'Ẉ' => 'ẉ', 'Ẋ' => 'ẋ', 'Ẍ' => 'ẍ', 'Ẏ' => 'ẏ', 'Ẑ' => 'ẑ', 'Ẓ' => 'ẓ', 'Ẕ' => 'ẕ', 'ẞ' => 'ß', 'Ạ' => 'ạ', 'Ả' => 'ả', 'Ấ' => 'ấ', 'Ầ' => 'ầ', 'Ẩ' => 'ẩ', 'Ẫ' => 'ẫ', 'Ậ' => 'ậ', 'Ắ' => 'ắ', 'Ằ' => 'ằ', 'Ẳ' => 'ẳ', 'Ẵ' => 'ẵ', 'Ặ' => 'ặ', 'Ẹ' => 'ẹ', 'Ẻ' => 'ẻ', 'Ẽ' => 'ẽ', 'Ế' => 'ế', 'Ề' => 'ề', 'Ể' => 'ể', 'Ễ' => 'ễ', 'Ệ' => 'ệ', 'Ỉ' => 'ỉ', 'Ị' => 'ị', 'Ọ' => 'ọ', 'Ỏ' => 'ỏ', 'Ố' => 'ố', 'Ồ' => 'ồ', 'Ổ' => 'ổ', 'Ỗ' => 'ỗ', 'Ộ' => 'ộ', 'Ớ' => 'ớ', 'Ờ' => 'ờ', 'Ở' => 'ở', 'Ỡ' => 'ỡ', 'Ợ' => 'ợ', 'Ụ' => 'ụ', 'Ủ' => 'ủ', 'Ứ' => 'ứ', 'Ừ' => 'ừ', 'Ử' => 'ử', 'Ữ' => 'ữ', 'Ự' => 'ự', 'Ỳ' => 'ỳ', 'Ỵ' => 'ỵ', 'Ỷ' => 'ỷ', 'Ỹ' => 'ỹ', 'Ỻ' => 'ỻ', 'Ỽ' => 'ỽ', 'Ỿ' => 'ỿ', 'Ἀ' => 'ἀ', 'Ἁ' => 'ἁ', 'Ἂ' => 'ἂ', 'Ἃ' => 'ἃ', 'Ἄ' => 'ἄ', 'Ἅ' => 'ἅ', 'Ἆ' => 'ἆ', 'Ἇ' => 'ἇ', 'Ἐ' => 'ἐ', 'Ἑ' => 'ἑ', 'Ἒ' => 'ἒ', 'Ἓ' => 'ἓ', 'Ἔ' => 'ἔ', 'Ἕ' => 'ἕ', 'Ἠ' => 'ἠ', 'Ἡ' => 'ἡ', 'Ἢ' => 'ἢ', 'Ἣ' => 'ἣ', 'Ἤ' => 'ἤ', 'Ἥ' => 'ἥ', 'Ἦ' => 'ἦ', 'Ἧ' => 'ἧ', 'Ἰ' => 'ἰ', 'Ἱ' => 'ἱ', 'Ἲ' => 'ἲ', 'Ἳ' => 'ἳ', 'Ἴ' => 'ἴ', 'Ἵ' => 'ἵ', 'Ἶ' => 'ἶ', 'Ἷ' => 'ἷ', 'Ὀ' => 'ὀ', 'Ὁ' => 'ὁ', 'Ὂ' => 'ὂ', 'Ὃ' => 'ὃ', 'Ὄ' => 'ὄ', 'Ὅ' => 'ὅ', 'Ὑ' => 'ὑ', 'Ὓ' => 'ὓ', 'Ὕ' => 'ὕ', 'Ὗ' => 'ὗ', 'Ὠ' => 'ὠ', 'Ὡ' => 'ὡ', 'Ὢ' => 'ὢ', 'Ὣ' => 'ὣ', 'Ὤ' => 'ὤ', 'Ὥ' => 'ὥ', 'Ὦ' => 'ὦ', 'Ὧ' => 'ὧ', 'ᾈ' => 'ᾀ', 'ᾉ' => 'ᾁ', 'ᾊ' => 'ᾂ', 'ᾋ' => 'ᾃ', 'ᾌ' => 'ᾄ', 'ᾍ' => 'ᾅ', 'ᾎ' => 'ᾆ', 'ᾏ' => 'ᾇ', 'ᾘ' => 'ᾐ', 'ᾙ' => 'ᾑ', 'ᾚ' => 'ᾒ', 'ᾛ' => 'ᾓ', 'ᾜ' => 'ᾔ', 'ᾝ' => 'ᾕ', 'ᾞ' => 'ᾖ', 'ᾟ' => 'ᾗ', 'ᾨ' => 'ᾠ', 'ᾩ' => 'ᾡ', 'ᾪ' => 'ᾢ', 'ᾫ' => 'ᾣ', 'ᾬ' => 'ᾤ', 'ᾭ' => 'ᾥ', 'ᾮ' => 'ᾦ', 'ᾯ' => 'ᾧ', 'Ᾰ' => 'ᾰ', 'Ᾱ' => 'ᾱ', 'Ὰ' => 'ὰ', 'Ά' => 'ά', 'ᾼ' => 'ᾳ', 'Ὲ' => 'ὲ', 'Έ' => 'έ', 'Ὴ' => 'ὴ', 'Ή' => 'ή', 'ῌ' => 'ῃ', 'Ῐ' => 'ῐ', 'Ῑ' => 'ῑ', 'Ὶ' => 'ὶ', 'Ί' => 'ί', 'Ῠ' => 'ῠ', 'Ῡ' => 'ῡ', 'Ὺ' => 'ὺ', 'Ύ' => 'ύ', 'Ῥ' => 'ῥ', 'Ὸ' => 'ὸ', 'Ό' => 'ό', 'Ὼ' => 'ὼ', 'Ώ' => 'ώ', 'ῼ' => 'ῳ', 'Ω' => 'ω', 'K' => 'k', 'Å' => 'å', 'Ⅎ' => 'ⅎ', 'Ⅰ' => 'ⅰ', 'Ⅱ' => 'ⅱ', 'Ⅲ' => 'ⅲ', 'Ⅳ' => 'ⅳ', 'Ⅴ' => 'ⅴ', 'Ⅵ' => 'ⅵ', 'Ⅶ' => 'ⅶ', 'Ⅷ' => 'ⅷ', 'Ⅸ' => 'ⅸ', 'Ⅹ' => 'ⅹ', 'Ⅺ' => 'ⅺ', 'Ⅻ' => 'ⅻ', 'Ⅼ' => 'ⅼ', 'Ⅽ' => 'ⅽ', 'Ⅾ' => 'ⅾ', 'Ⅿ' => 'ⅿ', 'Ↄ' => 'ↄ', 'Ⓐ' => 'ⓐ', 'Ⓑ' => 'ⓑ', 'Ⓒ' => 'ⓒ', 'Ⓓ' => 'ⓓ', 'Ⓔ' => 'ⓔ', 'Ⓕ' => 'ⓕ', 'Ⓖ' => 'ⓖ', 'Ⓗ' => 'ⓗ', 'Ⓘ' => 'ⓘ', 'Ⓙ' => 'ⓙ', 'Ⓚ' => 'ⓚ', 'Ⓛ' => 'ⓛ', 'Ⓜ' => 'ⓜ', 'Ⓝ' => 'ⓝ', 'Ⓞ' => 'ⓞ', 'Ⓟ' => 'ⓟ', 'Ⓠ' => 'ⓠ', 'Ⓡ' => 'ⓡ', 'Ⓢ' => 'ⓢ', 'Ⓣ' => 'ⓣ', 'Ⓤ' => 'ⓤ', 'Ⓥ' => 'ⓥ', 'Ⓦ' => 'ⓦ', 'Ⓧ' => 'ⓧ', 'Ⓨ' => 'ⓨ', 'Ⓩ' => 'ⓩ', 'Ⰰ' => 'ⰰ', 'Ⰱ' => 'ⰱ', 'Ⰲ' => 'ⰲ', 'Ⰳ' => 'ⰳ', 'Ⰴ' => 'ⰴ', 'Ⰵ' => 'ⰵ', 'Ⰶ' => 'ⰶ', 'Ⰷ' => 'ⰷ', 'Ⰸ' => 'ⰸ', 'Ⰹ' => 'ⰹ', 'Ⰺ' => 'ⰺ', 'Ⰻ' => 'ⰻ', 'Ⰼ' => 'ⰼ', 'Ⰽ' => 'ⰽ', 'Ⰾ' => 'ⰾ', 'Ⰿ' => 'ⰿ', 'Ⱀ' => 'ⱀ', 'Ⱁ' => 'ⱁ', 'Ⱂ' => 'ⱂ', 'Ⱃ' => 'ⱃ', 'Ⱄ' => 'ⱄ', 'Ⱅ' => 'ⱅ', 'Ⱆ' => 'ⱆ', 'Ⱇ' => 'ⱇ', 'Ⱈ' => 'ⱈ', 'Ⱉ' => 'ⱉ', 'Ⱊ' => 'ⱊ', 'Ⱋ' => 'ⱋ', 'Ⱌ' => 'ⱌ', 'Ⱍ' => 'ⱍ', 'Ⱎ' => 'ⱎ', 'Ⱏ' => 'ⱏ', 'Ⱐ' => 'ⱐ', 'Ⱑ' => 'ⱑ', 'Ⱒ' => 'ⱒ', 'Ⱓ' => 'ⱓ', 'Ⱔ' => 'ⱔ', 'Ⱕ' => 'ⱕ', 'Ⱖ' => 'ⱖ', 'Ⱗ' => 'ⱗ', 'Ⱘ' => 'ⱘ', 'Ⱙ' => 'ⱙ', 'Ⱚ' => 'ⱚ', 'Ⱛ' => 'ⱛ', 'Ⱜ' => 'ⱜ', 'Ⱝ' => 'ⱝ', 'Ⱞ' => 'ⱞ', 'Ⱡ' => 'ⱡ', 'Ɫ' => 'ɫ', 'Ᵽ' => 'ᵽ', 'Ɽ' => 'ɽ', 'Ⱨ' => 'ⱨ', 'Ⱪ' => 'ⱪ', 'Ⱬ' => 'ⱬ', 'Ɑ' => 'ɑ', 'Ɱ' => 'ɱ', 'Ɐ' => 'ɐ', 'Ɒ' => 'ɒ', 'Ⱳ' => 'ⱳ', 'Ⱶ' => 'ⱶ', 'Ȿ' => 'ȿ', 'Ɀ' => 'ɀ', 'Ⲁ' => 'ⲁ', 'Ⲃ' => 'ⲃ', 'Ⲅ' => 'ⲅ', 'Ⲇ' => 'ⲇ', 'Ⲉ' => 'ⲉ', 'Ⲋ' => 'ⲋ', 'Ⲍ' => 'ⲍ', 'Ⲏ' => 'ⲏ', 'Ⲑ' => 'ⲑ', 'Ⲓ' => 'ⲓ', 'Ⲕ' => 'ⲕ', 'Ⲗ' => 'ⲗ', 'Ⲙ' => 'ⲙ', 'Ⲛ' => 'ⲛ', 'Ⲝ' => 'ⲝ', 'Ⲟ' => 'ⲟ', 'Ⲡ' => 'ⲡ', 'Ⲣ' => 'ⲣ', 'Ⲥ' => 'ⲥ', 'Ⲧ' => 'ⲧ', 'Ⲩ' => 'ⲩ', 'Ⲫ' => 'ⲫ', 'Ⲭ' => 'ⲭ', 'Ⲯ' => 'ⲯ', 'Ⲱ' => 'ⲱ', 'Ⲳ' => 'ⲳ', 'Ⲵ' => 'ⲵ', 'Ⲷ' => 'ⲷ', 'Ⲹ' => 'ⲹ', 'Ⲻ' => 'ⲻ', 'Ⲽ' => 'ⲽ', 'Ⲿ' => 'ⲿ', 'Ⳁ' => 'ⳁ', 'Ⳃ' => 'ⳃ', 'Ⳅ' => 'ⳅ', 'Ⳇ' => 'ⳇ', 'Ⳉ' => 'ⳉ', 'Ⳋ' => 'ⳋ', 'Ⳍ' => 'ⳍ', 'Ⳏ' => 'ⳏ', 'Ⳑ' => 'ⳑ', 'Ⳓ' => 'ⳓ', 'Ⳕ' => 'ⳕ', 'Ⳗ' => 'ⳗ', 'Ⳙ' => 'ⳙ', 'Ⳛ' => 'ⳛ', 'Ⳝ' => 'ⳝ', 'Ⳟ' => 'ⳟ', 'Ⳡ' => 'ⳡ', 'Ⳣ' => 'ⳣ', 'Ⳬ' => 'ⳬ', 'Ⳮ' => 'ⳮ', 'Ⳳ' => 'ⳳ', 'Ꙁ' => 'ꙁ', 'Ꙃ' => 'ꙃ', 'Ꙅ' => 'ꙅ', 'Ꙇ' => 'ꙇ', 'Ꙉ' => 'ꙉ', 'Ꙋ' => 'ꙋ', 'Ꙍ' => 'ꙍ', 'Ꙏ' => 'ꙏ', 'Ꙑ' => 'ꙑ', 'Ꙓ' => 'ꙓ', 'Ꙕ' => 'ꙕ', 'Ꙗ' => 'ꙗ', 'Ꙙ' => 'ꙙ', 'Ꙛ' => 'ꙛ', 'Ꙝ' => 'ꙝ', 'Ꙟ' => 'ꙟ', 'Ꙡ' => 'ꙡ', 'Ꙣ' => 'ꙣ', 'Ꙥ' => 'ꙥ', 'Ꙧ' => 'ꙧ', 'Ꙩ' => 'ꙩ', 'Ꙫ' => 'ꙫ', 'Ꙭ' => 'ꙭ', 'Ꚁ' => 'ꚁ', 'Ꚃ' => 'ꚃ', 'Ꚅ' => 'ꚅ', 'Ꚇ' => 'ꚇ', 'Ꚉ' => 'ꚉ', 'Ꚋ' => 'ꚋ', 'Ꚍ' => 'ꚍ', 'Ꚏ' => 'ꚏ', 'Ꚑ' => 'ꚑ', 'Ꚓ' => 'ꚓ', 'Ꚕ' => 'ꚕ', 'Ꚗ' => 'ꚗ', 'Ꚙ' => 'ꚙ', 'Ꚛ' => 'ꚛ', 'Ꜣ' => 'ꜣ', 'Ꜥ' => 'ꜥ', 'Ꜧ' => 'ꜧ', 'Ꜩ' => 'ꜩ', 'Ꜫ' => 'ꜫ', 'Ꜭ' => 'ꜭ', 'Ꜯ' => 'ꜯ', 'Ꜳ' => 'ꜳ', 'Ꜵ' => 'ꜵ', 'Ꜷ' => 'ꜷ', 'Ꜹ' => 'ꜹ', 'Ꜻ' => 'ꜻ', 'Ꜽ' => 'ꜽ', 'Ꜿ' => 'ꜿ', 'Ꝁ' => 'ꝁ', 'Ꝃ' => 'ꝃ', 'Ꝅ' => 'ꝅ', 'Ꝇ' => 'ꝇ', 'Ꝉ' => 'ꝉ', 'Ꝋ' => 'ꝋ', 'Ꝍ' => 'ꝍ', 'Ꝏ' => 'ꝏ', 'Ꝑ' => 'ꝑ', 'Ꝓ' => 'ꝓ', 'Ꝕ' => 'ꝕ', 'Ꝗ' => 'ꝗ', 'Ꝙ' => 'ꝙ', 'Ꝛ' => 'ꝛ', 'Ꝝ' => 'ꝝ', 'Ꝟ' => 'ꝟ', 'Ꝡ' => 'ꝡ', 'Ꝣ' => 'ꝣ', 'Ꝥ' => 'ꝥ', 'Ꝧ' => 'ꝧ', 'Ꝩ' => 'ꝩ', 'Ꝫ' => 'ꝫ', 'Ꝭ' => 'ꝭ', 'Ꝯ' => 'ꝯ', 'Ꝺ' => 'ꝺ', 'Ꝼ' => 'ꝼ', 'Ᵹ' => 'ᵹ', 'Ꝿ' => 'ꝿ', 'Ꞁ' => 'ꞁ', 'Ꞃ' => 'ꞃ', 'Ꞅ' => 'ꞅ', 'Ꞇ' => 'ꞇ', 'Ꞌ' => 'ꞌ', 'Ɥ' => 'ɥ', 'Ꞑ' => 'ꞑ', 'Ꞓ' => 'ꞓ', 'Ꞗ' => 'ꞗ', 'Ꞙ' => 'ꞙ', 'Ꞛ' => 'ꞛ', 'Ꞝ' => 'ꞝ', 'Ꞟ' => 'ꞟ', 'Ꞡ' => 'ꞡ', 'Ꞣ' => 'ꞣ', 'Ꞥ' => 'ꞥ', 'Ꞧ' => 'ꞧ', 'Ꞩ' => 'ꞩ', 'Ɦ' => 'ɦ', 'Ɜ' => 'ɜ', 'Ɡ' => 'ɡ', 'Ɬ' => 'ɬ', 'Ɪ' => 'ɪ', 'Ʞ' => 'ʞ', 'Ʇ' => 'ʇ', 'Ʝ' => 'ʝ', 'Ꭓ' => 'ꭓ', 'Ꞵ' => 'ꞵ', 'Ꞷ' => 'ꞷ', 'Ꞹ' => 'ꞹ', 'Ꞻ' => 'ꞻ', 'Ꞽ' => 'ꞽ', 'Ꞿ' => 'ꞿ', 'Ꟃ' => 'ꟃ', 'Ꞔ' => 'ꞔ', 'Ʂ' => 'ʂ', 'Ᶎ' => 'ᶎ', 'Ꟈ' => 'ꟈ', 'Ꟊ' => 'ꟊ', 'Ꟶ' => 'ꟶ', 'A' => 'a', 'B' => 'b', 'C' => 'c', 'D' => 'd', 'E' => 'e', 'F' => 'f', 'G' => 'g', 'H' => 'h', 'I' => 'i', 'J' => 'j', 'K' => 'k', 'L' => 'l', 'M' => 'm', 'N' => 'n', 'O' => 'o', 'P' => 'p', 'Q' => 'q', 'R' => 'r', 'S' => 's', 'T' => 't', 'U' => 'u', 'V' => 'v', 'W' => 'w', 'X' => 'x', 'Y' => 'y', 'Z' => 'z', '𐐀' => '𐐨', '𐐁' => '𐐩', '𐐂' => '𐐪', '𐐃' => '𐐫', '𐐄' => '𐐬', '𐐅' => '𐐭', '𐐆' => '𐐮', '𐐇' => '𐐯', '𐐈' => '𐐰', '𐐉' => '𐐱', '𐐊' => '𐐲', '𐐋' => '𐐳', '𐐌' => '𐐴', '𐐍' => '𐐵', '𐐎' => '𐐶', '𐐏' => '𐐷', '𐐐' => '𐐸', '𐐑' => '𐐹', '𐐒' => '𐐺', '𐐓' => '𐐻', '𐐔' => '𐐼', '𐐕' => '𐐽', '𐐖' => '𐐾', '𐐗' => '𐐿', '𐐘' => '𐑀', '𐐙' => '𐑁', '𐐚' => '𐑂', '𐐛' => '𐑃', '𐐜' => '𐑄', '𐐝' => '𐑅', '𐐞' => '𐑆', '𐐟' => '𐑇', '𐐠' => '𐑈', '𐐡' => '𐑉', '𐐢' => '𐑊', '𐐣' => '𐑋', '𐐤' => '𐑌', '𐐥' => '𐑍', '𐐦' => '𐑎', '𐐧' => '𐑏', '𐒰' => '𐓘', '𐒱' => '𐓙', '𐒲' => '𐓚', '𐒳' => '𐓛', '𐒴' => '𐓜', '𐒵' => '𐓝', '𐒶' => '𐓞', '𐒷' => '𐓟', '𐒸' => '𐓠', '𐒹' => '𐓡', '𐒺' => '𐓢', '𐒻' => '𐓣', '𐒼' => '𐓤', '𐒽' => '𐓥', '𐒾' => '𐓦', '𐒿' => '𐓧', '𐓀' => '𐓨', '𐓁' => '𐓩', '𐓂' => '𐓪', '𐓃' => '𐓫', '𐓄' => '𐓬', '𐓅' => '𐓭', '𐓆' => '𐓮', '𐓇' => '𐓯', '𐓈' => '𐓰', '𐓉' => '𐓱', '𐓊' => '𐓲', '𐓋' => '𐓳', '𐓌' => '𐓴', '𐓍' => '𐓵', '𐓎' => '𐓶', '𐓏' => '𐓷', '𐓐' => '𐓸', '𐓑' => '𐓹', '𐓒' => '𐓺', '𐓓' => '𐓻', '𐲀' => '𐳀', '𐲁' => '𐳁', '𐲂' => '𐳂', '𐲃' => '𐳃', '𐲄' => '𐳄', '𐲅' => '𐳅', '𐲆' => '𐳆', '𐲇' => '𐳇', '𐲈' => '𐳈', '𐲉' => '𐳉', '𐲊' => '𐳊', '𐲋' => '𐳋', '𐲌' => '𐳌', '𐲍' => '𐳍', '𐲎' => '𐳎', '𐲏' => '𐳏', '𐲐' => '𐳐', '𐲑' => '𐳑', '𐲒' => '𐳒', '𐲓' => '𐳓', '𐲔' => '𐳔', '𐲕' => '𐳕', '𐲖' => '𐳖', '𐲗' => '𐳗', '𐲘' => '𐳘', '𐲙' => '𐳙', '𐲚' => '𐳚', '𐲛' => '𐳛', '𐲜' => '𐳜', '𐲝' => '𐳝', '𐲞' => '𐳞', '𐲟' => '𐳟', '𐲠' => '𐳠', '𐲡' => '𐳡', '𐲢' => '𐳢', '𐲣' => '𐳣', '𐲤' => '𐳤', '𐲥' => '𐳥', '𐲦' => '𐳦', '𐲧' => '𐳧', '𐲨' => '𐳨', '𐲩' => '𐳩', '𐲪' => '𐳪', '𐲫' => '𐳫', '𐲬' => '𐳬', '𐲭' => '𐳭', '𐲮' => '𐳮', '𐲯' => '𐳯', '𐲰' => '𐳰', '𐲱' => '𐳱', '𐲲' => '𐳲', '𑢠' => '𑣀', '𑢡' => '𑣁', '𑢢' => '𑣂', '𑢣' => '𑣃', '𑢤' => '𑣄', '𑢥' => '𑣅', '𑢦' => '𑣆', '𑢧' => '𑣇', '𑢨' => '𑣈', '𑢩' => '𑣉', '𑢪' => '𑣊', '𑢫' => '𑣋', '𑢬' => '𑣌', '𑢭' => '𑣍', '𑢮' => '𑣎', '𑢯' => '𑣏', '𑢰' => '𑣐', '𑢱' => '𑣑', '𑢲' => '𑣒', '𑢳' => '𑣓', '𑢴' => '𑣔', '𑢵' => '𑣕', '𑢶' => '𑣖', '𑢷' => '𑣗', '𑢸' => '𑣘', '𑢹' => '𑣙', '𑢺' => '𑣚', '𑢻' => '𑣛', '𑢼' => '𑣜', '𑢽' => '𑣝', '𑢾' => '𑣞', '𑢿' => '𑣟', '𖹀' => '𖹠', '𖹁' => '𖹡', '𖹂' => '𖹢', '𖹃' => '𖹣', '𖹄' => '𖹤', '𖹅' => '𖹥', '𖹆' => '𖹦', '𖹇' => '𖹧', '𖹈' => '𖹨', '𖹉' => '𖹩', '𖹊' => '𖹪', '𖹋' => '𖹫', '𖹌' => '𖹬', '𖹍' => '𖹭', '𖹎' => '𖹮', '𖹏' => '𖹯', '𖹐' => '𖹰', '𖹑' => '𖹱', '𖹒' => '𖹲', '𖹓' => '𖹳', '𖹔' => '𖹴', '𖹕' => '𖹵', '𖹖' => '𖹶', '𖹗' => '𖹷', '𖹘' => '𖹸', '𖹙' => '𖹹', '𖹚' => '𖹺', '𖹛' => '𖹻', '𖹜' => '𖹼', '𖹝' => '𖹽', '𖹞' => '𖹾', '𖹟' => '𖹿', '𞤀' => '𞤢', '𞤁' => '𞤣', '𞤂' => '𞤤', '𞤃' => '𞤥', '𞤄' => '𞤦', '𞤅' => '𞤧', '𞤆' => '𞤨', '𞤇' => '𞤩', '𞤈' => '𞤪', '𞤉' => '𞤫', '𞤊' => '𞤬', '𞤋' => '𞤭', '𞤌' => '𞤮', '𞤍' => '𞤯', '𞤎' => '𞤰', '𞤏' => '𞤱', '𞤐' => '𞤲', '𞤑' => '𞤳', '𞤒' => '𞤴', '𞤓' => '𞤵', '𞤔' => '𞤶', '𞤕' => '𞤷', '𞤖' => '𞤸', '𞤗' => '𞤹', '𞤘' => '𞤺', '𞤙' => '𞤻', '𞤚' => '𞤼', '𞤛' => '𞤽', '𞤜' => '𞤾', '𞤝' => '𞤿', '𞤞' => '𞥀', '𞤟' => '𞥁', '𞤠' => '𞥂', '𞤡' => '𞥃', ); polyfill-mbstring/Resources/unidata/caseFolding.php 0000644 00000004541 15060133305 0016547 0 ustar 00 <?php return [ 'İ' => 'i̇', 'µ' => 'μ', 'ſ' => 's', 'ͅ' => 'ι', 'ς' => 'σ', 'ϐ' => 'β', 'ϑ' => 'θ', 'ϕ' => 'φ', 'ϖ' => 'π', 'ϰ' => 'κ', 'ϱ' => 'ρ', 'ϵ' => 'ε', 'ẛ' => 'ṡ', 'ι' => 'ι', 'ß' => 'ss', 'ʼn' => 'ʼn', 'ǰ' => 'ǰ', 'ΐ' => 'ΐ', 'ΰ' => 'ΰ', 'և' => 'եւ', 'ẖ' => 'ẖ', 'ẗ' => 'ẗ', 'ẘ' => 'ẘ', 'ẙ' => 'ẙ', 'ẚ' => 'aʾ', 'ẞ' => 'ss', 'ὐ' => 'ὐ', 'ὒ' => 'ὒ', 'ὔ' => 'ὔ', 'ὖ' => 'ὖ', 'ᾀ' => 'ἀι', 'ᾁ' => 'ἁι', 'ᾂ' => 'ἂι', 'ᾃ' => 'ἃι', 'ᾄ' => 'ἄι', 'ᾅ' => 'ἅι', 'ᾆ' => 'ἆι', 'ᾇ' => 'ἇι', 'ᾈ' => 'ἀι', 'ᾉ' => 'ἁι', 'ᾊ' => 'ἂι', 'ᾋ' => 'ἃι', 'ᾌ' => 'ἄι', 'ᾍ' => 'ἅι', 'ᾎ' => 'ἆι', 'ᾏ' => 'ἇι', 'ᾐ' => 'ἠι', 'ᾑ' => 'ἡι', 'ᾒ' => 'ἢι', 'ᾓ' => 'ἣι', 'ᾔ' => 'ἤι', 'ᾕ' => 'ἥι', 'ᾖ' => 'ἦι', 'ᾗ' => 'ἧι', 'ᾘ' => 'ἠι', 'ᾙ' => 'ἡι', 'ᾚ' => 'ἢι', 'ᾛ' => 'ἣι', 'ᾜ' => 'ἤι', 'ᾝ' => 'ἥι', 'ᾞ' => 'ἦι', 'ᾟ' => 'ἧι', 'ᾠ' => 'ὠι', 'ᾡ' => 'ὡι', 'ᾢ' => 'ὢι', 'ᾣ' => 'ὣι', 'ᾤ' => 'ὤι', 'ᾥ' => 'ὥι', 'ᾦ' => 'ὦι', 'ᾧ' => 'ὧι', 'ᾨ' => 'ὠι', 'ᾩ' => 'ὡι', 'ᾪ' => 'ὢι', 'ᾫ' => 'ὣι', 'ᾬ' => 'ὤι', 'ᾭ' => 'ὥι', 'ᾮ' => 'ὦι', 'ᾯ' => 'ὧι', 'ᾲ' => 'ὰι', 'ᾳ' => 'αι', 'ᾴ' => 'άι', 'ᾶ' => 'ᾶ', 'ᾷ' => 'ᾶι', 'ᾼ' => 'αι', 'ῂ' => 'ὴι', 'ῃ' => 'ηι', 'ῄ' => 'ήι', 'ῆ' => 'ῆ', 'ῇ' => 'ῆι', 'ῌ' => 'ηι', 'ῒ' => 'ῒ', 'ῖ' => 'ῖ', 'ῗ' => 'ῗ', 'ῢ' => 'ῢ', 'ῤ' => 'ῤ', 'ῦ' => 'ῦ', 'ῧ' => 'ῧ', 'ῲ' => 'ὼι', 'ῳ' => 'ωι', 'ῴ' => 'ώι', 'ῶ' => 'ῶ', 'ῷ' => 'ῶι', 'ῼ' => 'ωι', 'ff' => 'ff', 'fi' => 'fi', 'fl' => 'fl', 'ffi' => 'ffi', 'ffl' => 'ffl', 'ſt' => 'st', 'st' => 'st', 'ﬓ' => 'մն', 'ﬔ' => 'մե', 'ﬕ' => 'մի', 'ﬖ' => 'վն', 'ﬗ' => 'մխ', ]; polyfill-mbstring/Resources/unidata/titleCaseRegexp.php 0000644 00000014071 15060133305 0017420 0 ustar 00 <?php // from Case_Ignorable in https://unicode.org/Public/UNIDATA/DerivedCoreProperties.txt return '/(?<![\x{0027}\x{002E}\x{003A}\x{005E}\x{0060}\x{00A8}\x{00AD}\x{00AF}\x{00B4}\x{00B7}\x{00B8}\x{02B0}-\x{02C1}\x{02C2}-\x{02C5}\x{02C6}-\x{02D1}\x{02D2}-\x{02DF}\x{02E0}-\x{02E4}\x{02E5}-\x{02EB}\x{02EC}\x{02ED}\x{02EE}\x{02EF}-\x{02FF}\x{0300}-\x{036F}\x{0374}\x{0375}\x{037A}\x{0384}-\x{0385}\x{0387}\x{0483}-\x{0487}\x{0488}-\x{0489}\x{0559}\x{0591}-\x{05BD}\x{05BF}\x{05C1}-\x{05C2}\x{05C4}-\x{05C5}\x{05C7}\x{05F4}\x{0600}-\x{0605}\x{0610}-\x{061A}\x{061C}\x{0640}\x{064B}-\x{065F}\x{0670}\x{06D6}-\x{06DC}\x{06DD}\x{06DF}-\x{06E4}\x{06E5}-\x{06E6}\x{06E7}-\x{06E8}\x{06EA}-\x{06ED}\x{070F}\x{0711}\x{0730}-\x{074A}\x{07A6}-\x{07B0}\x{07EB}-\x{07F3}\x{07F4}-\x{07F5}\x{07FA}\x{07FD}\x{0816}-\x{0819}\x{081A}\x{081B}-\x{0823}\x{0824}\x{0825}-\x{0827}\x{0828}\x{0829}-\x{082D}\x{0859}-\x{085B}\x{08D3}-\x{08E1}\x{08E2}\x{08E3}-\x{0902}\x{093A}\x{093C}\x{0941}-\x{0948}\x{094D}\x{0951}-\x{0957}\x{0962}-\x{0963}\x{0971}\x{0981}\x{09BC}\x{09C1}-\x{09C4}\x{09CD}\x{09E2}-\x{09E3}\x{09FE}\x{0A01}-\x{0A02}\x{0A3C}\x{0A41}-\x{0A42}\x{0A47}-\x{0A48}\x{0A4B}-\x{0A4D}\x{0A51}\x{0A70}-\x{0A71}\x{0A75}\x{0A81}-\x{0A82}\x{0ABC}\x{0AC1}-\x{0AC5}\x{0AC7}-\x{0AC8}\x{0ACD}\x{0AE2}-\x{0AE3}\x{0AFA}-\x{0AFF}\x{0B01}\x{0B3C}\x{0B3F}\x{0B41}-\x{0B44}\x{0B4D}\x{0B56}\x{0B62}-\x{0B63}\x{0B82}\x{0BC0}\x{0BCD}\x{0C00}\x{0C04}\x{0C3E}-\x{0C40}\x{0C46}-\x{0C48}\x{0C4A}-\x{0C4D}\x{0C55}-\x{0C56}\x{0C62}-\x{0C63}\x{0C81}\x{0CBC}\x{0CBF}\x{0CC6}\x{0CCC}-\x{0CCD}\x{0CE2}-\x{0CE3}\x{0D00}-\x{0D01}\x{0D3B}-\x{0D3C}\x{0D41}-\x{0D44}\x{0D4D}\x{0D62}-\x{0D63}\x{0DCA}\x{0DD2}-\x{0DD4}\x{0DD6}\x{0E31}\x{0E34}-\x{0E3A}\x{0E46}\x{0E47}-\x{0E4E}\x{0EB1}\x{0EB4}-\x{0EB9}\x{0EBB}-\x{0EBC}\x{0EC6}\x{0EC8}-\x{0ECD}\x{0F18}-\x{0F19}\x{0F35}\x{0F37}\x{0F39}\x{0F71}-\x{0F7E}\x{0F80}-\x{0F84}\x{0F86}-\x{0F87}\x{0F8D}-\x{0F97}\x{0F99}-\x{0FBC}\x{0FC6}\x{102D}-\x{1030}\x{1032}-\x{1037}\x{1039}-\x{103A}\x{103D}-\x{103E}\x{1058}-\x{1059}\x{105E}-\x{1060}\x{1071}-\x{1074}\x{1082}\x{1085}-\x{1086}\x{108D}\x{109D}\x{10FC}\x{135D}-\x{135F}\x{1712}-\x{1714}\x{1732}-\x{1734}\x{1752}-\x{1753}\x{1772}-\x{1773}\x{17B4}-\x{17B5}\x{17B7}-\x{17BD}\x{17C6}\x{17C9}-\x{17D3}\x{17D7}\x{17DD}\x{180B}-\x{180D}\x{180E}\x{1843}\x{1885}-\x{1886}\x{18A9}\x{1920}-\x{1922}\x{1927}-\x{1928}\x{1932}\x{1939}-\x{193B}\x{1A17}-\x{1A18}\x{1A1B}\x{1A56}\x{1A58}-\x{1A5E}\x{1A60}\x{1A62}\x{1A65}-\x{1A6C}\x{1A73}-\x{1A7C}\x{1A7F}\x{1AA7}\x{1AB0}-\x{1ABD}\x{1ABE}\x{1B00}-\x{1B03}\x{1B34}\x{1B36}-\x{1B3A}\x{1B3C}\x{1B42}\x{1B6B}-\x{1B73}\x{1B80}-\x{1B81}\x{1BA2}-\x{1BA5}\x{1BA8}-\x{1BA9}\x{1BAB}-\x{1BAD}\x{1BE6}\x{1BE8}-\x{1BE9}\x{1BED}\x{1BEF}-\x{1BF1}\x{1C2C}-\x{1C33}\x{1C36}-\x{1C37}\x{1C78}-\x{1C7D}\x{1CD0}-\x{1CD2}\x{1CD4}-\x{1CE0}\x{1CE2}-\x{1CE8}\x{1CED}\x{1CF4}\x{1CF8}-\x{1CF9}\x{1D2C}-\x{1D6A}\x{1D78}\x{1D9B}-\x{1DBF}\x{1DC0}-\x{1DF9}\x{1DFB}-\x{1DFF}\x{1FBD}\x{1FBF}-\x{1FC1}\x{1FCD}-\x{1FCF}\x{1FDD}-\x{1FDF}\x{1FED}-\x{1FEF}\x{1FFD}-\x{1FFE}\x{200B}-\x{200F}\x{2018}\x{2019}\x{2024}\x{2027}\x{202A}-\x{202E}\x{2060}-\x{2064}\x{2066}-\x{206F}\x{2071}\x{207F}\x{2090}-\x{209C}\x{20D0}-\x{20DC}\x{20DD}-\x{20E0}\x{20E1}\x{20E2}-\x{20E4}\x{20E5}-\x{20F0}\x{2C7C}-\x{2C7D}\x{2CEF}-\x{2CF1}\x{2D6F}\x{2D7F}\x{2DE0}-\x{2DFF}\x{2E2F}\x{3005}\x{302A}-\x{302D}\x{3031}-\x{3035}\x{303B}\x{3099}-\x{309A}\x{309B}-\x{309C}\x{309D}-\x{309E}\x{30FC}-\x{30FE}\x{A015}\x{A4F8}-\x{A4FD}\x{A60C}\x{A66F}\x{A670}-\x{A672}\x{A674}-\x{A67D}\x{A67F}\x{A69C}-\x{A69D}\x{A69E}-\x{A69F}\x{A6F0}-\x{A6F1}\x{A700}-\x{A716}\x{A717}-\x{A71F}\x{A720}-\x{A721}\x{A770}\x{A788}\x{A789}-\x{A78A}\x{A7F8}-\x{A7F9}\x{A802}\x{A806}\x{A80B}\x{A825}-\x{A826}\x{A8C4}-\x{A8C5}\x{A8E0}-\x{A8F1}\x{A8FF}\x{A926}-\x{A92D}\x{A947}-\x{A951}\x{A980}-\x{A982}\x{A9B3}\x{A9B6}-\x{A9B9}\x{A9BC}\x{A9CF}\x{A9E5}\x{A9E6}\x{AA29}-\x{AA2E}\x{AA31}-\x{AA32}\x{AA35}-\x{AA36}\x{AA43}\x{AA4C}\x{AA70}\x{AA7C}\x{AAB0}\x{AAB2}-\x{AAB4}\x{AAB7}-\x{AAB8}\x{AABE}-\x{AABF}\x{AAC1}\x{AADD}\x{AAEC}-\x{AAED}\x{AAF3}-\x{AAF4}\x{AAF6}\x{AB5B}\x{AB5C}-\x{AB5F}\x{ABE5}\x{ABE8}\x{ABED}\x{FB1E}\x{FBB2}-\x{FBC1}\x{FE00}-\x{FE0F}\x{FE13}\x{FE20}-\x{FE2F}\x{FE52}\x{FE55}\x{FEFF}\x{FF07}\x{FF0E}\x{FF1A}\x{FF3E}\x{FF40}\x{FF70}\x{FF9E}-\x{FF9F}\x{FFE3}\x{FFF9}-\x{FFFB}\x{101FD}\x{102E0}\x{10376}-\x{1037A}\x{10A01}-\x{10A03}\x{10A05}-\x{10A06}\x{10A0C}-\x{10A0F}\x{10A38}-\x{10A3A}\x{10A3F}\x{10AE5}-\x{10AE6}\x{10D24}-\x{10D27}\x{10F46}-\x{10F50}\x{11001}\x{11038}-\x{11046}\x{1107F}-\x{11081}\x{110B3}-\x{110B6}\x{110B9}-\x{110BA}\x{110BD}\x{110CD}\x{11100}-\x{11102}\x{11127}-\x{1112B}\x{1112D}-\x{11134}\x{11173}\x{11180}-\x{11181}\x{111B6}-\x{111BE}\x{111C9}-\x{111CC}\x{1122F}-\x{11231}\x{11234}\x{11236}-\x{11237}\x{1123E}\x{112DF}\x{112E3}-\x{112EA}\x{11300}-\x{11301}\x{1133B}-\x{1133C}\x{11340}\x{11366}-\x{1136C}\x{11370}-\x{11374}\x{11438}-\x{1143F}\x{11442}-\x{11444}\x{11446}\x{1145E}\x{114B3}-\x{114B8}\x{114BA}\x{114BF}-\x{114C0}\x{114C2}-\x{114C3}\x{115B2}-\x{115B5}\x{115BC}-\x{115BD}\x{115BF}-\x{115C0}\x{115DC}-\x{115DD}\x{11633}-\x{1163A}\x{1163D}\x{1163F}-\x{11640}\x{116AB}\x{116AD}\x{116B0}-\x{116B5}\x{116B7}\x{1171D}-\x{1171F}\x{11722}-\x{11725}\x{11727}-\x{1172B}\x{1182F}-\x{11837}\x{11839}-\x{1183A}\x{11A01}-\x{11A0A}\x{11A33}-\x{11A38}\x{11A3B}-\x{11A3E}\x{11A47}\x{11A51}-\x{11A56}\x{11A59}-\x{11A5B}\x{11A8A}-\x{11A96}\x{11A98}-\x{11A99}\x{11C30}-\x{11C36}\x{11C38}-\x{11C3D}\x{11C3F}\x{11C92}-\x{11CA7}\x{11CAA}-\x{11CB0}\x{11CB2}-\x{11CB3}\x{11CB5}-\x{11CB6}\x{11D31}-\x{11D36}\x{11D3A}\x{11D3C}-\x{11D3D}\x{11D3F}-\x{11D45}\x{11D47}\x{11D90}-\x{11D91}\x{11D95}\x{11D97}\x{11EF3}-\x{11EF4}\x{16AF0}-\x{16AF4}\x{16B30}-\x{16B36}\x{16B40}-\x{16B43}\x{16F8F}-\x{16F92}\x{16F93}-\x{16F9F}\x{16FE0}-\x{16FE1}\x{1BC9D}-\x{1BC9E}\x{1BCA0}-\x{1BCA3}\x{1D167}-\x{1D169}\x{1D173}-\x{1D17A}\x{1D17B}-\x{1D182}\x{1D185}-\x{1D18B}\x{1D1AA}-\x{1D1AD}\x{1D242}-\x{1D244}\x{1DA00}-\x{1DA36}\x{1DA3B}-\x{1DA6C}\x{1DA75}\x{1DA84}\x{1DA9B}-\x{1DA9F}\x{1DAA1}-\x{1DAAF}\x{1E000}-\x{1E006}\x{1E008}-\x{1E018}\x{1E01B}-\x{1E021}\x{1E023}-\x{1E024}\x{1E026}-\x{1E02A}\x{1E8D0}-\x{1E8D6}\x{1E944}-\x{1E94A}\x{1F3FB}-\x{1F3FF}\x{E0001}\x{E0020}-\x{E007F}\x{E0100}-\x{E01EF}])(\pL)(\pL*+)/u'; polyfill-mbstring/Resources/unidata/upperCase.php 0000644 00000063322 15060133305 0016262 0 ustar 00 <?php return array ( 'a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D', 'e' => 'E', 'f' => 'F', 'g' => 'G', 'h' => 'H', 'i' => 'I', 'j' => 'J', 'k' => 'K', 'l' => 'L', 'm' => 'M', 'n' => 'N', 'o' => 'O', 'p' => 'P', 'q' => 'Q', 'r' => 'R', 's' => 'S', 't' => 'T', 'u' => 'U', 'v' => 'V', 'w' => 'W', 'x' => 'X', 'y' => 'Y', 'z' => 'Z', 'µ' => 'Μ', 'à' => 'À', 'á' => 'Á', 'â' => 'Â', 'ã' => 'Ã', 'ä' => 'Ä', 'å' => 'Å', 'æ' => 'Æ', 'ç' => 'Ç', 'è' => 'È', 'é' => 'É', 'ê' => 'Ê', 'ë' => 'Ë', 'ì' => 'Ì', 'í' => 'Í', 'î' => 'Î', 'ï' => 'Ï', 'ð' => 'Ð', 'ñ' => 'Ñ', 'ò' => 'Ò', 'ó' => 'Ó', 'ô' => 'Ô', 'õ' => 'Õ', 'ö' => 'Ö', 'ø' => 'Ø', 'ù' => 'Ù', 'ú' => 'Ú', 'û' => 'Û', 'ü' => 'Ü', 'ý' => 'Ý', 'þ' => 'Þ', 'ÿ' => 'Ÿ', 'ā' => 'Ā', 'ă' => 'Ă', 'ą' => 'Ą', 'ć' => 'Ć', 'ĉ' => 'Ĉ', 'ċ' => 'Ċ', 'č' => 'Č', 'ď' => 'Ď', 'đ' => 'Đ', 'ē' => 'Ē', 'ĕ' => 'Ĕ', 'ė' => 'Ė', 'ę' => 'Ę', 'ě' => 'Ě', 'ĝ' => 'Ĝ', 'ğ' => 'Ğ', 'ġ' => 'Ġ', 'ģ' => 'Ģ', 'ĥ' => 'Ĥ', 'ħ' => 'Ħ', 'ĩ' => 'Ĩ', 'ī' => 'Ī', 'ĭ' => 'Ĭ', 'į' => 'Į', 'ı' => 'I', 'ij' => 'IJ', 'ĵ' => 'Ĵ', 'ķ' => 'Ķ', 'ĺ' => 'Ĺ', 'ļ' => 'Ļ', 'ľ' => 'Ľ', 'ŀ' => 'Ŀ', 'ł' => 'Ł', 'ń' => 'Ń', 'ņ' => 'Ņ', 'ň' => 'Ň', 'ŋ' => 'Ŋ', 'ō' => 'Ō', 'ŏ' => 'Ŏ', 'ő' => 'Ő', 'œ' => 'Œ', 'ŕ' => 'Ŕ', 'ŗ' => 'Ŗ', 'ř' => 'Ř', 'ś' => 'Ś', 'ŝ' => 'Ŝ', 'ş' => 'Ş', 'š' => 'Š', 'ţ' => 'Ţ', 'ť' => 'Ť', 'ŧ' => 'Ŧ', 'ũ' => 'Ũ', 'ū' => 'Ū', 'ŭ' => 'Ŭ', 'ů' => 'Ů', 'ű' => 'Ű', 'ų' => 'Ų', 'ŵ' => 'Ŵ', 'ŷ' => 'Ŷ', 'ź' => 'Ź', 'ż' => 'Ż', 'ž' => 'Ž', 'ſ' => 'S', 'ƀ' => 'Ƀ', 'ƃ' => 'Ƃ', 'ƅ' => 'Ƅ', 'ƈ' => 'Ƈ', 'ƌ' => 'Ƌ', 'ƒ' => 'Ƒ', 'ƕ' => 'Ƕ', 'ƙ' => 'Ƙ', 'ƚ' => 'Ƚ', 'ƞ' => 'Ƞ', 'ơ' => 'Ơ', 'ƣ' => 'Ƣ', 'ƥ' => 'Ƥ', 'ƨ' => 'Ƨ', 'ƭ' => 'Ƭ', 'ư' => 'Ư', 'ƴ' => 'Ƴ', 'ƶ' => 'Ƶ', 'ƹ' => 'Ƹ', 'ƽ' => 'Ƽ', 'ƿ' => 'Ƿ', 'Dž' => 'DŽ', 'dž' => 'DŽ', 'Lj' => 'LJ', 'lj' => 'LJ', 'Nj' => 'NJ', 'nj' => 'NJ', 'ǎ' => 'Ǎ', 'ǐ' => 'Ǐ', 'ǒ' => 'Ǒ', 'ǔ' => 'Ǔ', 'ǖ' => 'Ǖ', 'ǘ' => 'Ǘ', 'ǚ' => 'Ǚ', 'ǜ' => 'Ǜ', 'ǝ' => 'Ǝ', 'ǟ' => 'Ǟ', 'ǡ' => 'Ǡ', 'ǣ' => 'Ǣ', 'ǥ' => 'Ǥ', 'ǧ' => 'Ǧ', 'ǩ' => 'Ǩ', 'ǫ' => 'Ǫ', 'ǭ' => 'Ǭ', 'ǯ' => 'Ǯ', 'Dz' => 'DZ', 'dz' => 'DZ', 'ǵ' => 'Ǵ', 'ǹ' => 'Ǹ', 'ǻ' => 'Ǻ', 'ǽ' => 'Ǽ', 'ǿ' => 'Ǿ', 'ȁ' => 'Ȁ', 'ȃ' => 'Ȃ', 'ȅ' => 'Ȅ', 'ȇ' => 'Ȇ', 'ȉ' => 'Ȉ', 'ȋ' => 'Ȋ', 'ȍ' => 'Ȍ', 'ȏ' => 'Ȏ', 'ȑ' => 'Ȑ', 'ȓ' => 'Ȓ', 'ȕ' => 'Ȕ', 'ȗ' => 'Ȗ', 'ș' => 'Ș', 'ț' => 'Ț', 'ȝ' => 'Ȝ', 'ȟ' => 'Ȟ', 'ȣ' => 'Ȣ', 'ȥ' => 'Ȥ', 'ȧ' => 'Ȧ', 'ȩ' => 'Ȩ', 'ȫ' => 'Ȫ', 'ȭ' => 'Ȭ', 'ȯ' => 'Ȯ', 'ȱ' => 'Ȱ', 'ȳ' => 'Ȳ', 'ȼ' => 'Ȼ', 'ȿ' => 'Ȿ', 'ɀ' => 'Ɀ', 'ɂ' => 'Ɂ', 'ɇ' => 'Ɇ', 'ɉ' => 'Ɉ', 'ɋ' => 'Ɋ', 'ɍ' => 'Ɍ', 'ɏ' => 'Ɏ', 'ɐ' => 'Ɐ', 'ɑ' => 'Ɑ', 'ɒ' => 'Ɒ', 'ɓ' => 'Ɓ', 'ɔ' => 'Ɔ', 'ɖ' => 'Ɖ', 'ɗ' => 'Ɗ', 'ə' => 'Ə', 'ɛ' => 'Ɛ', 'ɜ' => 'Ɜ', 'ɠ' => 'Ɠ', 'ɡ' => 'Ɡ', 'ɣ' => 'Ɣ', 'ɥ' => 'Ɥ', 'ɦ' => 'Ɦ', 'ɨ' => 'Ɨ', 'ɩ' => 'Ɩ', 'ɪ' => 'Ɪ', 'ɫ' => 'Ɫ', 'ɬ' => 'Ɬ', 'ɯ' => 'Ɯ', 'ɱ' => 'Ɱ', 'ɲ' => 'Ɲ', 'ɵ' => 'Ɵ', 'ɽ' => 'Ɽ', 'ʀ' => 'Ʀ', 'ʂ' => 'Ʂ', 'ʃ' => 'Ʃ', 'ʇ' => 'Ʇ', 'ʈ' => 'Ʈ', 'ʉ' => 'Ʉ', 'ʊ' => 'Ʊ', 'ʋ' => 'Ʋ', 'ʌ' => 'Ʌ', 'ʒ' => 'Ʒ', 'ʝ' => 'Ʝ', 'ʞ' => 'Ʞ', 'ͅ' => 'Ι', 'ͱ' => 'Ͱ', 'ͳ' => 'Ͳ', 'ͷ' => 'Ͷ', 'ͻ' => 'Ͻ', 'ͼ' => 'Ͼ', 'ͽ' => 'Ͽ', 'ά' => 'Ά', 'έ' => 'Έ', 'ή' => 'Ή', 'ί' => 'Ί', 'α' => 'Α', 'β' => 'Β', 'γ' => 'Γ', 'δ' => 'Δ', 'ε' => 'Ε', 'ζ' => 'Ζ', 'η' => 'Η', 'θ' => 'Θ', 'ι' => 'Ι', 'κ' => 'Κ', 'λ' => 'Λ', 'μ' => 'Μ', 'ν' => 'Ν', 'ξ' => 'Ξ', 'ο' => 'Ο', 'π' => 'Π', 'ρ' => 'Ρ', 'ς' => 'Σ', 'σ' => 'Σ', 'τ' => 'Τ', 'υ' => 'Υ', 'φ' => 'Φ', 'χ' => 'Χ', 'ψ' => 'Ψ', 'ω' => 'Ω', 'ϊ' => 'Ϊ', 'ϋ' => 'Ϋ', 'ό' => 'Ό', 'ύ' => 'Ύ', 'ώ' => 'Ώ', 'ϐ' => 'Β', 'ϑ' => 'Θ', 'ϕ' => 'Φ', 'ϖ' => 'Π', 'ϗ' => 'Ϗ', 'ϙ' => 'Ϙ', 'ϛ' => 'Ϛ', 'ϝ' => 'Ϝ', 'ϟ' => 'Ϟ', 'ϡ' => 'Ϡ', 'ϣ' => 'Ϣ', 'ϥ' => 'Ϥ', 'ϧ' => 'Ϧ', 'ϩ' => 'Ϩ', 'ϫ' => 'Ϫ', 'ϭ' => 'Ϭ', 'ϯ' => 'Ϯ', 'ϰ' => 'Κ', 'ϱ' => 'Ρ', 'ϲ' => 'Ϲ', 'ϳ' => 'Ϳ', 'ϵ' => 'Ε', 'ϸ' => 'Ϸ', 'ϻ' => 'Ϻ', 'а' => 'А', 'б' => 'Б', 'в' => 'В', 'г' => 'Г', 'д' => 'Д', 'е' => 'Е', 'ж' => 'Ж', 'з' => 'З', 'и' => 'И', 'й' => 'Й', 'к' => 'К', 'л' => 'Л', 'м' => 'М', 'н' => 'Н', 'о' => 'О', 'п' => 'П', 'р' => 'Р', 'с' => 'С', 'т' => 'Т', 'у' => 'У', 'ф' => 'Ф', 'х' => 'Х', 'ц' => 'Ц', 'ч' => 'Ч', 'ш' => 'Ш', 'щ' => 'Щ', 'ъ' => 'Ъ', 'ы' => 'Ы', 'ь' => 'Ь', 'э' => 'Э', 'ю' => 'Ю', 'я' => 'Я', 'ѐ' => 'Ѐ', 'ё' => 'Ё', 'ђ' => 'Ђ', 'ѓ' => 'Ѓ', 'є' => 'Є', 'ѕ' => 'Ѕ', 'і' => 'І', 'ї' => 'Ї', 'ј' => 'Ј', 'љ' => 'Љ', 'њ' => 'Њ', 'ћ' => 'Ћ', 'ќ' => 'Ќ', 'ѝ' => 'Ѝ', 'ў' => 'Ў', 'џ' => 'Џ', 'ѡ' => 'Ѡ', 'ѣ' => 'Ѣ', 'ѥ' => 'Ѥ', 'ѧ' => 'Ѧ', 'ѩ' => 'Ѩ', 'ѫ' => 'Ѫ', 'ѭ' => 'Ѭ', 'ѯ' => 'Ѯ', 'ѱ' => 'Ѱ', 'ѳ' => 'Ѳ', 'ѵ' => 'Ѵ', 'ѷ' => 'Ѷ', 'ѹ' => 'Ѹ', 'ѻ' => 'Ѻ', 'ѽ' => 'Ѽ', 'ѿ' => 'Ѿ', 'ҁ' => 'Ҁ', 'ҋ' => 'Ҋ', 'ҍ' => 'Ҍ', 'ҏ' => 'Ҏ', 'ґ' => 'Ґ', 'ғ' => 'Ғ', 'ҕ' => 'Ҕ', 'җ' => 'Җ', 'ҙ' => 'Ҙ', 'қ' => 'Қ', 'ҝ' => 'Ҝ', 'ҟ' => 'Ҟ', 'ҡ' => 'Ҡ', 'ң' => 'Ң', 'ҥ' => 'Ҥ', 'ҧ' => 'Ҧ', 'ҩ' => 'Ҩ', 'ҫ' => 'Ҫ', 'ҭ' => 'Ҭ', 'ү' => 'Ү', 'ұ' => 'Ұ', 'ҳ' => 'Ҳ', 'ҵ' => 'Ҵ', 'ҷ' => 'Ҷ', 'ҹ' => 'Ҹ', 'һ' => 'Һ', 'ҽ' => 'Ҽ', 'ҿ' => 'Ҿ', 'ӂ' => 'Ӂ', 'ӄ' => 'Ӄ', 'ӆ' => 'Ӆ', 'ӈ' => 'Ӈ', 'ӊ' => 'Ӊ', 'ӌ' => 'Ӌ', 'ӎ' => 'Ӎ', 'ӏ' => 'Ӏ', 'ӑ' => 'Ӑ', 'ӓ' => 'Ӓ', 'ӕ' => 'Ӕ', 'ӗ' => 'Ӗ', 'ә' => 'Ә', 'ӛ' => 'Ӛ', 'ӝ' => 'Ӝ', 'ӟ' => 'Ӟ', 'ӡ' => 'Ӡ', 'ӣ' => 'Ӣ', 'ӥ' => 'Ӥ', 'ӧ' => 'Ӧ', 'ө' => 'Ө', 'ӫ' => 'Ӫ', 'ӭ' => 'Ӭ', 'ӯ' => 'Ӯ', 'ӱ' => 'Ӱ', 'ӳ' => 'Ӳ', 'ӵ' => 'Ӵ', 'ӷ' => 'Ӷ', 'ӹ' => 'Ӹ', 'ӻ' => 'Ӻ', 'ӽ' => 'Ӽ', 'ӿ' => 'Ӿ', 'ԁ' => 'Ԁ', 'ԃ' => 'Ԃ', 'ԅ' => 'Ԅ', 'ԇ' => 'Ԇ', 'ԉ' => 'Ԉ', 'ԋ' => 'Ԋ', 'ԍ' => 'Ԍ', 'ԏ' => 'Ԏ', 'ԑ' => 'Ԑ', 'ԓ' => 'Ԓ', 'ԕ' => 'Ԕ', 'ԗ' => 'Ԗ', 'ԙ' => 'Ԙ', 'ԛ' => 'Ԛ', 'ԝ' => 'Ԝ', 'ԟ' => 'Ԟ', 'ԡ' => 'Ԡ', 'ԣ' => 'Ԣ', 'ԥ' => 'Ԥ', 'ԧ' => 'Ԧ', 'ԩ' => 'Ԩ', 'ԫ' => 'Ԫ', 'ԭ' => 'Ԭ', 'ԯ' => 'Ԯ', 'ա' => 'Ա', 'բ' => 'Բ', 'գ' => 'Գ', 'դ' => 'Դ', 'ե' => 'Ե', 'զ' => 'Զ', 'է' => 'Է', 'ը' => 'Ը', 'թ' => 'Թ', 'ժ' => 'Ժ', 'ի' => 'Ի', 'լ' => 'Լ', 'խ' => 'Խ', 'ծ' => 'Ծ', 'կ' => 'Կ', 'հ' => 'Հ', 'ձ' => 'Ձ', 'ղ' => 'Ղ', 'ճ' => 'Ճ', 'մ' => 'Մ', 'յ' => 'Յ', 'ն' => 'Ն', 'շ' => 'Շ', 'ո' => 'Ո', 'չ' => 'Չ', 'պ' => 'Պ', 'ջ' => 'Ջ', 'ռ' => 'Ռ', 'ս' => 'Ս', 'վ' => 'Վ', 'տ' => 'Տ', 'ր' => 'Ր', 'ց' => 'Ց', 'ւ' => 'Ւ', 'փ' => 'Փ', 'ք' => 'Ք', 'օ' => 'Օ', 'ֆ' => 'Ֆ', 'ა' => 'Ა', 'ბ' => 'Ბ', 'გ' => 'Გ', 'დ' => 'Დ', 'ე' => 'Ე', 'ვ' => 'Ვ', 'ზ' => 'Ზ', 'თ' => 'Თ', 'ი' => 'Ი', 'კ' => 'Კ', 'ლ' => 'Ლ', 'მ' => 'Მ', 'ნ' => 'Ნ', 'ო' => 'Ო', 'პ' => 'Პ', 'ჟ' => 'Ჟ', 'რ' => 'Რ', 'ს' => 'Ს', 'ტ' => 'Ტ', 'უ' => 'Უ', 'ფ' => 'Ფ', 'ქ' => 'Ქ', 'ღ' => 'Ღ', 'ყ' => 'Ყ', 'შ' => 'Შ', 'ჩ' => 'Ჩ', 'ც' => 'Ც', 'ძ' => 'Ძ', 'წ' => 'Წ', 'ჭ' => 'Ჭ', 'ხ' => 'Ხ', 'ჯ' => 'Ჯ', 'ჰ' => 'Ჰ', 'ჱ' => 'Ჱ', 'ჲ' => 'Ჲ', 'ჳ' => 'Ჳ', 'ჴ' => 'Ჴ', 'ჵ' => 'Ჵ', 'ჶ' => 'Ჶ', 'ჷ' => 'Ჷ', 'ჸ' => 'Ჸ', 'ჹ' => 'Ჹ', 'ჺ' => 'Ჺ', 'ჽ' => 'Ჽ', 'ჾ' => 'Ჾ', 'ჿ' => 'Ჿ', 'ᏸ' => 'Ᏸ', 'ᏹ' => 'Ᏹ', 'ᏺ' => 'Ᏺ', 'ᏻ' => 'Ᏻ', 'ᏼ' => 'Ᏼ', 'ᏽ' => 'Ᏽ', 'ᲀ' => 'В', 'ᲁ' => 'Д', 'ᲂ' => 'О', 'ᲃ' => 'С', 'ᲄ' => 'Т', 'ᲅ' => 'Т', 'ᲆ' => 'Ъ', 'ᲇ' => 'Ѣ', 'ᲈ' => 'Ꙋ', 'ᵹ' => 'Ᵹ', 'ᵽ' => 'Ᵽ', 'ᶎ' => 'Ᶎ', 'ḁ' => 'Ḁ', 'ḃ' => 'Ḃ', 'ḅ' => 'Ḅ', 'ḇ' => 'Ḇ', 'ḉ' => 'Ḉ', 'ḋ' => 'Ḋ', 'ḍ' => 'Ḍ', 'ḏ' => 'Ḏ', 'ḑ' => 'Ḑ', 'ḓ' => 'Ḓ', 'ḕ' => 'Ḕ', 'ḗ' => 'Ḗ', 'ḙ' => 'Ḙ', 'ḛ' => 'Ḛ', 'ḝ' => 'Ḝ', 'ḟ' => 'Ḟ', 'ḡ' => 'Ḡ', 'ḣ' => 'Ḣ', 'ḥ' => 'Ḥ', 'ḧ' => 'Ḧ', 'ḩ' => 'Ḩ', 'ḫ' => 'Ḫ', 'ḭ' => 'Ḭ', 'ḯ' => 'Ḯ', 'ḱ' => 'Ḱ', 'ḳ' => 'Ḳ', 'ḵ' => 'Ḵ', 'ḷ' => 'Ḷ', 'ḹ' => 'Ḹ', 'ḻ' => 'Ḻ', 'ḽ' => 'Ḽ', 'ḿ' => 'Ḿ', 'ṁ' => 'Ṁ', 'ṃ' => 'Ṃ', 'ṅ' => 'Ṅ', 'ṇ' => 'Ṇ', 'ṉ' => 'Ṉ', 'ṋ' => 'Ṋ', 'ṍ' => 'Ṍ', 'ṏ' => 'Ṏ', 'ṑ' => 'Ṑ', 'ṓ' => 'Ṓ', 'ṕ' => 'Ṕ', 'ṗ' => 'Ṗ', 'ṙ' => 'Ṙ', 'ṛ' => 'Ṛ', 'ṝ' => 'Ṝ', 'ṟ' => 'Ṟ', 'ṡ' => 'Ṡ', 'ṣ' => 'Ṣ', 'ṥ' => 'Ṥ', 'ṧ' => 'Ṧ', 'ṩ' => 'Ṩ', 'ṫ' => 'Ṫ', 'ṭ' => 'Ṭ', 'ṯ' => 'Ṯ', 'ṱ' => 'Ṱ', 'ṳ' => 'Ṳ', 'ṵ' => 'Ṵ', 'ṷ' => 'Ṷ', 'ṹ' => 'Ṹ', 'ṻ' => 'Ṻ', 'ṽ' => 'Ṽ', 'ṿ' => 'Ṿ', 'ẁ' => 'Ẁ', 'ẃ' => 'Ẃ', 'ẅ' => 'Ẅ', 'ẇ' => 'Ẇ', 'ẉ' => 'Ẉ', 'ẋ' => 'Ẋ', 'ẍ' => 'Ẍ', 'ẏ' => 'Ẏ', 'ẑ' => 'Ẑ', 'ẓ' => 'Ẓ', 'ẕ' => 'Ẕ', 'ẛ' => 'Ṡ', 'ạ' => 'Ạ', 'ả' => 'Ả', 'ấ' => 'Ấ', 'ầ' => 'Ầ', 'ẩ' => 'Ẩ', 'ẫ' => 'Ẫ', 'ậ' => 'Ậ', 'ắ' => 'Ắ', 'ằ' => 'Ằ', 'ẳ' => 'Ẳ', 'ẵ' => 'Ẵ', 'ặ' => 'Ặ', 'ẹ' => 'Ẹ', 'ẻ' => 'Ẻ', 'ẽ' => 'Ẽ', 'ế' => 'Ế', 'ề' => 'Ề', 'ể' => 'Ể', 'ễ' => 'Ễ', 'ệ' => 'Ệ', 'ỉ' => 'Ỉ', 'ị' => 'Ị', 'ọ' => 'Ọ', 'ỏ' => 'Ỏ', 'ố' => 'Ố', 'ồ' => 'Ồ', 'ổ' => 'Ổ', 'ỗ' => 'Ỗ', 'ộ' => 'Ộ', 'ớ' => 'Ớ', 'ờ' => 'Ờ', 'ở' => 'Ở', 'ỡ' => 'Ỡ', 'ợ' => 'Ợ', 'ụ' => 'Ụ', 'ủ' => 'Ủ', 'ứ' => 'Ứ', 'ừ' => 'Ừ', 'ử' => 'Ử', 'ữ' => 'Ữ', 'ự' => 'Ự', 'ỳ' => 'Ỳ', 'ỵ' => 'Ỵ', 'ỷ' => 'Ỷ', 'ỹ' => 'Ỹ', 'ỻ' => 'Ỻ', 'ỽ' => 'Ỽ', 'ỿ' => 'Ỿ', 'ἀ' => 'Ἀ', 'ἁ' => 'Ἁ', 'ἂ' => 'Ἂ', 'ἃ' => 'Ἃ', 'ἄ' => 'Ἄ', 'ἅ' => 'Ἅ', 'ἆ' => 'Ἆ', 'ἇ' => 'Ἇ', 'ἐ' => 'Ἐ', 'ἑ' => 'Ἑ', 'ἒ' => 'Ἒ', 'ἓ' => 'Ἓ', 'ἔ' => 'Ἔ', 'ἕ' => 'Ἕ', 'ἠ' => 'Ἠ', 'ἡ' => 'Ἡ', 'ἢ' => 'Ἢ', 'ἣ' => 'Ἣ', 'ἤ' => 'Ἤ', 'ἥ' => 'Ἥ', 'ἦ' => 'Ἦ', 'ἧ' => 'Ἧ', 'ἰ' => 'Ἰ', 'ἱ' => 'Ἱ', 'ἲ' => 'Ἲ', 'ἳ' => 'Ἳ', 'ἴ' => 'Ἴ', 'ἵ' => 'Ἵ', 'ἶ' => 'Ἶ', 'ἷ' => 'Ἷ', 'ὀ' => 'Ὀ', 'ὁ' => 'Ὁ', 'ὂ' => 'Ὂ', 'ὃ' => 'Ὃ', 'ὄ' => 'Ὄ', 'ὅ' => 'Ὅ', 'ὑ' => 'Ὑ', 'ὓ' => 'Ὓ', 'ὕ' => 'Ὕ', 'ὗ' => 'Ὗ', 'ὠ' => 'Ὠ', 'ὡ' => 'Ὡ', 'ὢ' => 'Ὢ', 'ὣ' => 'Ὣ', 'ὤ' => 'Ὤ', 'ὥ' => 'Ὥ', 'ὦ' => 'Ὦ', 'ὧ' => 'Ὧ', 'ὰ' => 'Ὰ', 'ά' => 'Ά', 'ὲ' => 'Ὲ', 'έ' => 'Έ', 'ὴ' => 'Ὴ', 'ή' => 'Ή', 'ὶ' => 'Ὶ', 'ί' => 'Ί', 'ὸ' => 'Ὸ', 'ό' => 'Ό', 'ὺ' => 'Ὺ', 'ύ' => 'Ύ', 'ὼ' => 'Ὼ', 'ώ' => 'Ώ', 'ᾀ' => 'ἈΙ', 'ᾁ' => 'ἉΙ', 'ᾂ' => 'ἊΙ', 'ᾃ' => 'ἋΙ', 'ᾄ' => 'ἌΙ', 'ᾅ' => 'ἍΙ', 'ᾆ' => 'ἎΙ', 'ᾇ' => 'ἏΙ', 'ᾐ' => 'ἨΙ', 'ᾑ' => 'ἩΙ', 'ᾒ' => 'ἪΙ', 'ᾓ' => 'ἫΙ', 'ᾔ' => 'ἬΙ', 'ᾕ' => 'ἭΙ', 'ᾖ' => 'ἮΙ', 'ᾗ' => 'ἯΙ', 'ᾠ' => 'ὨΙ', 'ᾡ' => 'ὩΙ', 'ᾢ' => 'ὪΙ', 'ᾣ' => 'ὫΙ', 'ᾤ' => 'ὬΙ', 'ᾥ' => 'ὭΙ', 'ᾦ' => 'ὮΙ', 'ᾧ' => 'ὯΙ', 'ᾰ' => 'Ᾰ', 'ᾱ' => 'Ᾱ', 'ᾳ' => 'ΑΙ', 'ι' => 'Ι', 'ῃ' => 'ΗΙ', 'ῐ' => 'Ῐ', 'ῑ' => 'Ῑ', 'ῠ' => 'Ῠ', 'ῡ' => 'Ῡ', 'ῥ' => 'Ῥ', 'ῳ' => 'ΩΙ', 'ⅎ' => 'Ⅎ', 'ⅰ' => 'Ⅰ', 'ⅱ' => 'Ⅱ', 'ⅲ' => 'Ⅲ', 'ⅳ' => 'Ⅳ', 'ⅴ' => 'Ⅴ', 'ⅵ' => 'Ⅵ', 'ⅶ' => 'Ⅶ', 'ⅷ' => 'Ⅷ', 'ⅸ' => 'Ⅸ', 'ⅹ' => 'Ⅹ', 'ⅺ' => 'Ⅺ', 'ⅻ' => 'Ⅻ', 'ⅼ' => 'Ⅼ', 'ⅽ' => 'Ⅽ', 'ⅾ' => 'Ⅾ', 'ⅿ' => 'Ⅿ', 'ↄ' => 'Ↄ', 'ⓐ' => 'Ⓐ', 'ⓑ' => 'Ⓑ', 'ⓒ' => 'Ⓒ', 'ⓓ' => 'Ⓓ', 'ⓔ' => 'Ⓔ', 'ⓕ' => 'Ⓕ', 'ⓖ' => 'Ⓖ', 'ⓗ' => 'Ⓗ', 'ⓘ' => 'Ⓘ', 'ⓙ' => 'Ⓙ', 'ⓚ' => 'Ⓚ', 'ⓛ' => 'Ⓛ', 'ⓜ' => 'Ⓜ', 'ⓝ' => 'Ⓝ', 'ⓞ' => 'Ⓞ', 'ⓟ' => 'Ⓟ', 'ⓠ' => 'Ⓠ', 'ⓡ' => 'Ⓡ', 'ⓢ' => 'Ⓢ', 'ⓣ' => 'Ⓣ', 'ⓤ' => 'Ⓤ', 'ⓥ' => 'Ⓥ', 'ⓦ' => 'Ⓦ', 'ⓧ' => 'Ⓧ', 'ⓨ' => 'Ⓨ', 'ⓩ' => 'Ⓩ', 'ⰰ' => 'Ⰰ', 'ⰱ' => 'Ⰱ', 'ⰲ' => 'Ⰲ', 'ⰳ' => 'Ⰳ', 'ⰴ' => 'Ⰴ', 'ⰵ' => 'Ⰵ', 'ⰶ' => 'Ⰶ', 'ⰷ' => 'Ⰷ', 'ⰸ' => 'Ⰸ', 'ⰹ' => 'Ⰹ', 'ⰺ' => 'Ⰺ', 'ⰻ' => 'Ⰻ', 'ⰼ' => 'Ⰼ', 'ⰽ' => 'Ⰽ', 'ⰾ' => 'Ⰾ', 'ⰿ' => 'Ⰿ', 'ⱀ' => 'Ⱀ', 'ⱁ' => 'Ⱁ', 'ⱂ' => 'Ⱂ', 'ⱃ' => 'Ⱃ', 'ⱄ' => 'Ⱄ', 'ⱅ' => 'Ⱅ', 'ⱆ' => 'Ⱆ', 'ⱇ' => 'Ⱇ', 'ⱈ' => 'Ⱈ', 'ⱉ' => 'Ⱉ', 'ⱊ' => 'Ⱊ', 'ⱋ' => 'Ⱋ', 'ⱌ' => 'Ⱌ', 'ⱍ' => 'Ⱍ', 'ⱎ' => 'Ⱎ', 'ⱏ' => 'Ⱏ', 'ⱐ' => 'Ⱐ', 'ⱑ' => 'Ⱑ', 'ⱒ' => 'Ⱒ', 'ⱓ' => 'Ⱓ', 'ⱔ' => 'Ⱔ', 'ⱕ' => 'Ⱕ', 'ⱖ' => 'Ⱖ', 'ⱗ' => 'Ⱗ', 'ⱘ' => 'Ⱘ', 'ⱙ' => 'Ⱙ', 'ⱚ' => 'Ⱚ', 'ⱛ' => 'Ⱛ', 'ⱜ' => 'Ⱜ', 'ⱝ' => 'Ⱝ', 'ⱞ' => 'Ⱞ', 'ⱡ' => 'Ⱡ', 'ⱥ' => 'Ⱥ', 'ⱦ' => 'Ⱦ', 'ⱨ' => 'Ⱨ', 'ⱪ' => 'Ⱪ', 'ⱬ' => 'Ⱬ', 'ⱳ' => 'Ⱳ', 'ⱶ' => 'Ⱶ', 'ⲁ' => 'Ⲁ', 'ⲃ' => 'Ⲃ', 'ⲅ' => 'Ⲅ', 'ⲇ' => 'Ⲇ', 'ⲉ' => 'Ⲉ', 'ⲋ' => 'Ⲋ', 'ⲍ' => 'Ⲍ', 'ⲏ' => 'Ⲏ', 'ⲑ' => 'Ⲑ', 'ⲓ' => 'Ⲓ', 'ⲕ' => 'Ⲕ', 'ⲗ' => 'Ⲗ', 'ⲙ' => 'Ⲙ', 'ⲛ' => 'Ⲛ', 'ⲝ' => 'Ⲝ', 'ⲟ' => 'Ⲟ', 'ⲡ' => 'Ⲡ', 'ⲣ' => 'Ⲣ', 'ⲥ' => 'Ⲥ', 'ⲧ' => 'Ⲧ', 'ⲩ' => 'Ⲩ', 'ⲫ' => 'Ⲫ', 'ⲭ' => 'Ⲭ', 'ⲯ' => 'Ⲯ', 'ⲱ' => 'Ⲱ', 'ⲳ' => 'Ⲳ', 'ⲵ' => 'Ⲵ', 'ⲷ' => 'Ⲷ', 'ⲹ' => 'Ⲹ', 'ⲻ' => 'Ⲻ', 'ⲽ' => 'Ⲽ', 'ⲿ' => 'Ⲿ', 'ⳁ' => 'Ⳁ', 'ⳃ' => 'Ⳃ', 'ⳅ' => 'Ⳅ', 'ⳇ' => 'Ⳇ', 'ⳉ' => 'Ⳉ', 'ⳋ' => 'Ⳋ', 'ⳍ' => 'Ⳍ', 'ⳏ' => 'Ⳏ', 'ⳑ' => 'Ⳑ', 'ⳓ' => 'Ⳓ', 'ⳕ' => 'Ⳕ', 'ⳗ' => 'Ⳗ', 'ⳙ' => 'Ⳙ', 'ⳛ' => 'Ⳛ', 'ⳝ' => 'Ⳝ', 'ⳟ' => 'Ⳟ', 'ⳡ' => 'Ⳡ', 'ⳣ' => 'Ⳣ', 'ⳬ' => 'Ⳬ', 'ⳮ' => 'Ⳮ', 'ⳳ' => 'Ⳳ', 'ⴀ' => 'Ⴀ', 'ⴁ' => 'Ⴁ', 'ⴂ' => 'Ⴂ', 'ⴃ' => 'Ⴃ', 'ⴄ' => 'Ⴄ', 'ⴅ' => 'Ⴅ', 'ⴆ' => 'Ⴆ', 'ⴇ' => 'Ⴇ', 'ⴈ' => 'Ⴈ', 'ⴉ' => 'Ⴉ', 'ⴊ' => 'Ⴊ', 'ⴋ' => 'Ⴋ', 'ⴌ' => 'Ⴌ', 'ⴍ' => 'Ⴍ', 'ⴎ' => 'Ⴎ', 'ⴏ' => 'Ⴏ', 'ⴐ' => 'Ⴐ', 'ⴑ' => 'Ⴑ', 'ⴒ' => 'Ⴒ', 'ⴓ' => 'Ⴓ', 'ⴔ' => 'Ⴔ', 'ⴕ' => 'Ⴕ', 'ⴖ' => 'Ⴖ', 'ⴗ' => 'Ⴗ', 'ⴘ' => 'Ⴘ', 'ⴙ' => 'Ⴙ', 'ⴚ' => 'Ⴚ', 'ⴛ' => 'Ⴛ', 'ⴜ' => 'Ⴜ', 'ⴝ' => 'Ⴝ', 'ⴞ' => 'Ⴞ', 'ⴟ' => 'Ⴟ', 'ⴠ' => 'Ⴠ', 'ⴡ' => 'Ⴡ', 'ⴢ' => 'Ⴢ', 'ⴣ' => 'Ⴣ', 'ⴤ' => 'Ⴤ', 'ⴥ' => 'Ⴥ', 'ⴧ' => 'Ⴧ', 'ⴭ' => 'Ⴭ', 'ꙁ' => 'Ꙁ', 'ꙃ' => 'Ꙃ', 'ꙅ' => 'Ꙅ', 'ꙇ' => 'Ꙇ', 'ꙉ' => 'Ꙉ', 'ꙋ' => 'Ꙋ', 'ꙍ' => 'Ꙍ', 'ꙏ' => 'Ꙏ', 'ꙑ' => 'Ꙑ', 'ꙓ' => 'Ꙓ', 'ꙕ' => 'Ꙕ', 'ꙗ' => 'Ꙗ', 'ꙙ' => 'Ꙙ', 'ꙛ' => 'Ꙛ', 'ꙝ' => 'Ꙝ', 'ꙟ' => 'Ꙟ', 'ꙡ' => 'Ꙡ', 'ꙣ' => 'Ꙣ', 'ꙥ' => 'Ꙥ', 'ꙧ' => 'Ꙧ', 'ꙩ' => 'Ꙩ', 'ꙫ' => 'Ꙫ', 'ꙭ' => 'Ꙭ', 'ꚁ' => 'Ꚁ', 'ꚃ' => 'Ꚃ', 'ꚅ' => 'Ꚅ', 'ꚇ' => 'Ꚇ', 'ꚉ' => 'Ꚉ', 'ꚋ' => 'Ꚋ', 'ꚍ' => 'Ꚍ', 'ꚏ' => 'Ꚏ', 'ꚑ' => 'Ꚑ', 'ꚓ' => 'Ꚓ', 'ꚕ' => 'Ꚕ', 'ꚗ' => 'Ꚗ', 'ꚙ' => 'Ꚙ', 'ꚛ' => 'Ꚛ', 'ꜣ' => 'Ꜣ', 'ꜥ' => 'Ꜥ', 'ꜧ' => 'Ꜧ', 'ꜩ' => 'Ꜩ', 'ꜫ' => 'Ꜫ', 'ꜭ' => 'Ꜭ', 'ꜯ' => 'Ꜯ', 'ꜳ' => 'Ꜳ', 'ꜵ' => 'Ꜵ', 'ꜷ' => 'Ꜷ', 'ꜹ' => 'Ꜹ', 'ꜻ' => 'Ꜻ', 'ꜽ' => 'Ꜽ', 'ꜿ' => 'Ꜿ', 'ꝁ' => 'Ꝁ', 'ꝃ' => 'Ꝃ', 'ꝅ' => 'Ꝅ', 'ꝇ' => 'Ꝇ', 'ꝉ' => 'Ꝉ', 'ꝋ' => 'Ꝋ', 'ꝍ' => 'Ꝍ', 'ꝏ' => 'Ꝏ', 'ꝑ' => 'Ꝑ', 'ꝓ' => 'Ꝓ', 'ꝕ' => 'Ꝕ', 'ꝗ' => 'Ꝗ', 'ꝙ' => 'Ꝙ', 'ꝛ' => 'Ꝛ', 'ꝝ' => 'Ꝝ', 'ꝟ' => 'Ꝟ', 'ꝡ' => 'Ꝡ', 'ꝣ' => 'Ꝣ', 'ꝥ' => 'Ꝥ', 'ꝧ' => 'Ꝧ', 'ꝩ' => 'Ꝩ', 'ꝫ' => 'Ꝫ', 'ꝭ' => 'Ꝭ', 'ꝯ' => 'Ꝯ', 'ꝺ' => 'Ꝺ', 'ꝼ' => 'Ꝼ', 'ꝿ' => 'Ꝿ', 'ꞁ' => 'Ꞁ', 'ꞃ' => 'Ꞃ', 'ꞅ' => 'Ꞅ', 'ꞇ' => 'Ꞇ', 'ꞌ' => 'Ꞌ', 'ꞑ' => 'Ꞑ', 'ꞓ' => 'Ꞓ', 'ꞔ' => 'Ꞔ', 'ꞗ' => 'Ꞗ', 'ꞙ' => 'Ꞙ', 'ꞛ' => 'Ꞛ', 'ꞝ' => 'Ꞝ', 'ꞟ' => 'Ꞟ', 'ꞡ' => 'Ꞡ', 'ꞣ' => 'Ꞣ', 'ꞥ' => 'Ꞥ', 'ꞧ' => 'Ꞧ', 'ꞩ' => 'Ꞩ', 'ꞵ' => 'Ꞵ', 'ꞷ' => 'Ꞷ', 'ꞹ' => 'Ꞹ', 'ꞻ' => 'Ꞻ', 'ꞽ' => 'Ꞽ', 'ꞿ' => 'Ꞿ', 'ꟃ' => 'Ꟃ', 'ꟈ' => 'Ꟈ', 'ꟊ' => 'Ꟊ', 'ꟶ' => 'Ꟶ', 'ꭓ' => 'Ꭓ', 'ꭰ' => 'Ꭰ', 'ꭱ' => 'Ꭱ', 'ꭲ' => 'Ꭲ', 'ꭳ' => 'Ꭳ', 'ꭴ' => 'Ꭴ', 'ꭵ' => 'Ꭵ', 'ꭶ' => 'Ꭶ', 'ꭷ' => 'Ꭷ', 'ꭸ' => 'Ꭸ', 'ꭹ' => 'Ꭹ', 'ꭺ' => 'Ꭺ', 'ꭻ' => 'Ꭻ', 'ꭼ' => 'Ꭼ', 'ꭽ' => 'Ꭽ', 'ꭾ' => 'Ꭾ', 'ꭿ' => 'Ꭿ', 'ꮀ' => 'Ꮀ', 'ꮁ' => 'Ꮁ', 'ꮂ' => 'Ꮂ', 'ꮃ' => 'Ꮃ', 'ꮄ' => 'Ꮄ', 'ꮅ' => 'Ꮅ', 'ꮆ' => 'Ꮆ', 'ꮇ' => 'Ꮇ', 'ꮈ' => 'Ꮈ', 'ꮉ' => 'Ꮉ', 'ꮊ' => 'Ꮊ', 'ꮋ' => 'Ꮋ', 'ꮌ' => 'Ꮌ', 'ꮍ' => 'Ꮍ', 'ꮎ' => 'Ꮎ', 'ꮏ' => 'Ꮏ', 'ꮐ' => 'Ꮐ', 'ꮑ' => 'Ꮑ', 'ꮒ' => 'Ꮒ', 'ꮓ' => 'Ꮓ', 'ꮔ' => 'Ꮔ', 'ꮕ' => 'Ꮕ', 'ꮖ' => 'Ꮖ', 'ꮗ' => 'Ꮗ', 'ꮘ' => 'Ꮘ', 'ꮙ' => 'Ꮙ', 'ꮚ' => 'Ꮚ', 'ꮛ' => 'Ꮛ', 'ꮜ' => 'Ꮜ', 'ꮝ' => 'Ꮝ', 'ꮞ' => 'Ꮞ', 'ꮟ' => 'Ꮟ', 'ꮠ' => 'Ꮠ', 'ꮡ' => 'Ꮡ', 'ꮢ' => 'Ꮢ', 'ꮣ' => 'Ꮣ', 'ꮤ' => 'Ꮤ', 'ꮥ' => 'Ꮥ', 'ꮦ' => 'Ꮦ', 'ꮧ' => 'Ꮧ', 'ꮨ' => 'Ꮨ', 'ꮩ' => 'Ꮩ', 'ꮪ' => 'Ꮪ', 'ꮫ' => 'Ꮫ', 'ꮬ' => 'Ꮬ', 'ꮭ' => 'Ꮭ', 'ꮮ' => 'Ꮮ', 'ꮯ' => 'Ꮯ', 'ꮰ' => 'Ꮰ', 'ꮱ' => 'Ꮱ', 'ꮲ' => 'Ꮲ', 'ꮳ' => 'Ꮳ', 'ꮴ' => 'Ꮴ', 'ꮵ' => 'Ꮵ', 'ꮶ' => 'Ꮶ', 'ꮷ' => 'Ꮷ', 'ꮸ' => 'Ꮸ', 'ꮹ' => 'Ꮹ', 'ꮺ' => 'Ꮺ', 'ꮻ' => 'Ꮻ', 'ꮼ' => 'Ꮼ', 'ꮽ' => 'Ꮽ', 'ꮾ' => 'Ꮾ', 'ꮿ' => 'Ꮿ', 'a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D', 'e' => 'E', 'f' => 'F', 'g' => 'G', 'h' => 'H', 'i' => 'I', 'j' => 'J', 'k' => 'K', 'l' => 'L', 'm' => 'M', 'n' => 'N', 'o' => 'O', 'p' => 'P', 'q' => 'Q', 'r' => 'R', 's' => 'S', 't' => 'T', 'u' => 'U', 'v' => 'V', 'w' => 'W', 'x' => 'X', 'y' => 'Y', 'z' => 'Z', '𐐨' => '𐐀', '𐐩' => '𐐁', '𐐪' => '𐐂', '𐐫' => '𐐃', '𐐬' => '𐐄', '𐐭' => '𐐅', '𐐮' => '𐐆', '𐐯' => '𐐇', '𐐰' => '𐐈', '𐐱' => '𐐉', '𐐲' => '𐐊', '𐐳' => '𐐋', '𐐴' => '𐐌', '𐐵' => '𐐍', '𐐶' => '𐐎', '𐐷' => '𐐏', '𐐸' => '𐐐', '𐐹' => '𐐑', '𐐺' => '𐐒', '𐐻' => '𐐓', '𐐼' => '𐐔', '𐐽' => '𐐕', '𐐾' => '𐐖', '𐐿' => '𐐗', '𐑀' => '𐐘', '𐑁' => '𐐙', '𐑂' => '𐐚', '𐑃' => '𐐛', '𐑄' => '𐐜', '𐑅' => '𐐝', '𐑆' => '𐐞', '𐑇' => '𐐟', '𐑈' => '𐐠', '𐑉' => '𐐡', '𐑊' => '𐐢', '𐑋' => '𐐣', '𐑌' => '𐐤', '𐑍' => '𐐥', '𐑎' => '𐐦', '𐑏' => '𐐧', '𐓘' => '𐒰', '𐓙' => '𐒱', '𐓚' => '𐒲', '𐓛' => '𐒳', '𐓜' => '𐒴', '𐓝' => '𐒵', '𐓞' => '𐒶', '𐓟' => '𐒷', '𐓠' => '𐒸', '𐓡' => '𐒹', '𐓢' => '𐒺', '𐓣' => '𐒻', '𐓤' => '𐒼', '𐓥' => '𐒽', '𐓦' => '𐒾', '𐓧' => '𐒿', '𐓨' => '𐓀', '𐓩' => '𐓁', '𐓪' => '𐓂', '𐓫' => '𐓃', '𐓬' => '𐓄', '𐓭' => '𐓅', '𐓮' => '𐓆', '𐓯' => '𐓇', '𐓰' => '𐓈', '𐓱' => '𐓉', '𐓲' => '𐓊', '𐓳' => '𐓋', '𐓴' => '𐓌', '𐓵' => '𐓍', '𐓶' => '𐓎', '𐓷' => '𐓏', '𐓸' => '𐓐', '𐓹' => '𐓑', '𐓺' => '𐓒', '𐓻' => '𐓓', '𐳀' => '𐲀', '𐳁' => '𐲁', '𐳂' => '𐲂', '𐳃' => '𐲃', '𐳄' => '𐲄', '𐳅' => '𐲅', '𐳆' => '𐲆', '𐳇' => '𐲇', '𐳈' => '𐲈', '𐳉' => '𐲉', '𐳊' => '𐲊', '𐳋' => '𐲋', '𐳌' => '𐲌', '𐳍' => '𐲍', '𐳎' => '𐲎', '𐳏' => '𐲏', '𐳐' => '𐲐', '𐳑' => '𐲑', '𐳒' => '𐲒', '𐳓' => '𐲓', '𐳔' => '𐲔', '𐳕' => '𐲕', '𐳖' => '𐲖', '𐳗' => '𐲗', '𐳘' => '𐲘', '𐳙' => '𐲙', '𐳚' => '𐲚', '𐳛' => '𐲛', '𐳜' => '𐲜', '𐳝' => '𐲝', '𐳞' => '𐲞', '𐳟' => '𐲟', '𐳠' => '𐲠', '𐳡' => '𐲡', '𐳢' => '𐲢', '𐳣' => '𐲣', '𐳤' => '𐲤', '𐳥' => '𐲥', '𐳦' => '𐲦', '𐳧' => '𐲧', '𐳨' => '𐲨', '𐳩' => '𐲩', '𐳪' => '𐲪', '𐳫' => '𐲫', '𐳬' => '𐲬', '𐳭' => '𐲭', '𐳮' => '𐲮', '𐳯' => '𐲯', '𐳰' => '𐲰', '𐳱' => '𐲱', '𐳲' => '𐲲', '𑣀' => '𑢠', '𑣁' => '𑢡', '𑣂' => '𑢢', '𑣃' => '𑢣', '𑣄' => '𑢤', '𑣅' => '𑢥', '𑣆' => '𑢦', '𑣇' => '𑢧', '𑣈' => '𑢨', '𑣉' => '𑢩', '𑣊' => '𑢪', '𑣋' => '𑢫', '𑣌' => '𑢬', '𑣍' => '𑢭', '𑣎' => '𑢮', '𑣏' => '𑢯', '𑣐' => '𑢰', '𑣑' => '𑢱', '𑣒' => '𑢲', '𑣓' => '𑢳', '𑣔' => '𑢴', '𑣕' => '𑢵', '𑣖' => '𑢶', '𑣗' => '𑢷', '𑣘' => '𑢸', '𑣙' => '𑢹', '𑣚' => '𑢺', '𑣛' => '𑢻', '𑣜' => '𑢼', '𑣝' => '𑢽', '𑣞' => '𑢾', '𑣟' => '𑢿', '𖹠' => '𖹀', '𖹡' => '𖹁', '𖹢' => '𖹂', '𖹣' => '𖹃', '𖹤' => '𖹄', '𖹥' => '𖹅', '𖹦' => '𖹆', '𖹧' => '𖹇', '𖹨' => '𖹈', '𖹩' => '𖹉', '𖹪' => '𖹊', '𖹫' => '𖹋', '𖹬' => '𖹌', '𖹭' => '𖹍', '𖹮' => '𖹎', '𖹯' => '𖹏', '𖹰' => '𖹐', '𖹱' => '𖹑', '𖹲' => '𖹒', '𖹳' => '𖹓', '𖹴' => '𖹔', '𖹵' => '𖹕', '𖹶' => '𖹖', '𖹷' => '𖹗', '𖹸' => '𖹘', '𖹹' => '𖹙', '𖹺' => '𖹚', '𖹻' => '𖹛', '𖹼' => '𖹜', '𖹽' => '𖹝', '𖹾' => '𖹞', '𖹿' => '𖹟', '𞤢' => '𞤀', '𞤣' => '𞤁', '𞤤' => '𞤂', '𞤥' => '𞤃', '𞤦' => '𞤄', '𞤧' => '𞤅', '𞤨' => '𞤆', '𞤩' => '𞤇', '𞤪' => '𞤈', '𞤫' => '𞤉', '𞤬' => '𞤊', '𞤭' => '𞤋', '𞤮' => '𞤌', '𞤯' => '𞤍', '𞤰' => '𞤎', '𞤱' => '𞤏', '𞤲' => '𞤐', '𞤳' => '𞤑', '𞤴' => '𞤒', '𞤵' => '𞤓', '𞤶' => '𞤔', '𞤷' => '𞤕', '𞤸' => '𞤖', '𞤹' => '𞤗', '𞤺' => '𞤘', '𞤻' => '𞤙', '𞤼' => '𞤚', '𞤽' => '𞤛', '𞤾' => '𞤜', '𞤿' => '𞤝', '𞥀' => '𞤞', '𞥁' => '𞤟', '𞥂' => '𞤠', '𞥃' => '𞤡', 'ß' => 'SS', 'ff' => 'FF', 'fi' => 'FI', 'fl' => 'FL', 'ffi' => 'FFI', 'ffl' => 'FFL', 'ſt' => 'ST', 'st' => 'ST', 'և' => 'ԵՒ', 'ﬓ' => 'ՄՆ', 'ﬔ' => 'ՄԵ', 'ﬕ' => 'ՄԻ', 'ﬖ' => 'ՎՆ', 'ﬗ' => 'ՄԽ', 'ʼn' => 'ʼN', 'ΐ' => 'Ϊ́', 'ΰ' => 'Ϋ́', 'ǰ' => 'J̌', 'ẖ' => 'H̱', 'ẗ' => 'T̈', 'ẘ' => 'W̊', 'ẙ' => 'Y̊', 'ẚ' => 'Aʾ', 'ὐ' => 'Υ̓', 'ὒ' => 'Υ̓̀', 'ὔ' => 'Υ̓́', 'ὖ' => 'Υ̓͂', 'ᾶ' => 'Α͂', 'ῆ' => 'Η͂', 'ῒ' => 'Ϊ̀', 'ΐ' => 'Ϊ́', 'ῖ' => 'Ι͂', 'ῗ' => 'Ϊ͂', 'ῢ' => 'Ϋ̀', 'ΰ' => 'Ϋ́', 'ῤ' => 'Ρ̓', 'ῦ' => 'Υ͂', 'ῧ' => 'Ϋ͂', 'ῶ' => 'Ω͂', 'ᾈ' => 'ἈΙ', 'ᾉ' => 'ἉΙ', 'ᾊ' => 'ἊΙ', 'ᾋ' => 'ἋΙ', 'ᾌ' => 'ἌΙ', 'ᾍ' => 'ἍΙ', 'ᾎ' => 'ἎΙ', 'ᾏ' => 'ἏΙ', 'ᾘ' => 'ἨΙ', 'ᾙ' => 'ἩΙ', 'ᾚ' => 'ἪΙ', 'ᾛ' => 'ἫΙ', 'ᾜ' => 'ἬΙ', 'ᾝ' => 'ἭΙ', 'ᾞ' => 'ἮΙ', 'ᾟ' => 'ἯΙ', 'ᾨ' => 'ὨΙ', 'ᾩ' => 'ὩΙ', 'ᾪ' => 'ὪΙ', 'ᾫ' => 'ὫΙ', 'ᾬ' => 'ὬΙ', 'ᾭ' => 'ὭΙ', 'ᾮ' => 'ὮΙ', 'ᾯ' => 'ὯΙ', 'ᾼ' => 'ΑΙ', 'ῌ' => 'ΗΙ', 'ῼ' => 'ΩΙ', 'ᾲ' => 'ᾺΙ', 'ᾴ' => 'ΆΙ', 'ῂ' => 'ῊΙ', 'ῄ' => 'ΉΙ', 'ῲ' => 'ῺΙ', 'ῴ' => 'ΏΙ', 'ᾷ' => 'Α͂Ι', 'ῇ' => 'Η͂Ι', 'ῷ' => 'Ω͂Ι', ); polyfill-mbstring/README.md 0000644 00000000562 15060133305 0011477 0 ustar 00 Symfony Polyfill / Mbstring =========================== This component provides a partial, native PHP implementation for the [Mbstring](https://php.net/mbstring) extension. More information can be found in the [main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). License ======= This library is released under the [MIT license](LICENSE). error-handler/Exception/FlattenException.php 0000644 00000027476 15060133305 0015247 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Exception; use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\VarDumper\Caster\Caster; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\Stub; use Symfony\Component\VarDumper\Cloner\VarCloner; /** * FlattenException wraps a PHP Error or Exception to be able to serialize it. * * Basically, this class removes all objects from the trace. * * @author Fabien Potencier <fabien@symfony.com> */ class FlattenException { private string $message; private string|int $code; private ?self $previous = null; private array $trace; private string $traceAsString; private string $class; private int $statusCode; private string $statusText; private array $headers; private string $file; private int $line; private ?string $asString = null; private Data $dataRepresentation; public static function create(\Exception $exception, ?int $statusCode = null, array $headers = []): static { return static::createFromThrowable($exception, $statusCode, $headers); } public static function createFromThrowable(\Throwable $exception, ?int $statusCode = null, array $headers = []): static { $e = new static(); $e->setMessage($exception->getMessage()); $e->setCode($exception->getCode()); if ($exception instanceof HttpExceptionInterface) { $statusCode = $exception->getStatusCode(); $headers = array_merge($headers, $exception->getHeaders()); } elseif ($exception instanceof RequestExceptionInterface) { $statusCode = 400; } $statusCode ??= 500; if (class_exists(Response::class) && isset(Response::$statusTexts[$statusCode])) { $statusText = Response::$statusTexts[$statusCode]; } else { $statusText = 'Whoops, looks like something went wrong.'; } $e->setStatusText($statusText); $e->setStatusCode($statusCode); $e->setHeaders($headers); $e->setTraceFromThrowable($exception); $e->setClass(get_debug_type($exception)); $e->setFile($exception->getFile()); $e->setLine($exception->getLine()); $previous = $exception->getPrevious(); if ($previous instanceof \Throwable) { $e->setPrevious(static::createFromThrowable($previous)); } return $e; } public static function createWithDataRepresentation(\Throwable $throwable, ?int $statusCode = null, array $headers = [], ?VarCloner $cloner = null): static { $e = static::createFromThrowable($throwable, $statusCode, $headers); static $defaultCloner; if (!$cloner ??= $defaultCloner) { $cloner = $defaultCloner = new VarCloner(); $cloner->addCasters([ \Throwable::class => function (\Throwable $e, array $a, Stub $s, bool $isNested): array { if (!$isNested) { unset($a[Caster::PREFIX_PROTECTED.'message']); unset($a[Caster::PREFIX_PROTECTED.'code']); unset($a[Caster::PREFIX_PROTECTED.'file']); unset($a[Caster::PREFIX_PROTECTED.'line']); unset($a["\0Error\0trace"], $a["\0Exception\0trace"]); unset($a["\0Error\0previous"], $a["\0Exception\0previous"]); } return $a; }, ]); } return $e->setDataRepresentation($cloner->cloneVar($throwable)); } public function toArray(): array { $exceptions = []; foreach (array_merge([$this], $this->getAllPrevious()) as $exception) { $exceptions[] = [ 'message' => $exception->getMessage(), 'class' => $exception->getClass(), 'trace' => $exception->getTrace(), 'data' => $exception->getDataRepresentation(), ]; } return $exceptions; } public function getStatusCode(): int { return $this->statusCode; } /** * @return $this */ public function setStatusCode(int $code): static { $this->statusCode = $code; return $this; } public function getHeaders(): array { return $this->headers; } /** * @return $this */ public function setHeaders(array $headers): static { $this->headers = $headers; return $this; } public function getClass(): string { return $this->class; } /** * @return $this */ public function setClass(string $class): static { $this->class = str_contains($class, "@anonymous\0") ? (get_parent_class($class) ?: key(class_implements($class)) ?: 'class').'@anonymous' : $class; return $this; } public function getFile(): string { return $this->file; } /** * @return $this */ public function setFile(string $file): static { $this->file = $file; return $this; } public function getLine(): int { return $this->line; } /** * @return $this */ public function setLine(int $line): static { $this->line = $line; return $this; } public function getStatusText(): string { return $this->statusText; } /** * @return $this */ public function setStatusText(string $statusText): static { $this->statusText = $statusText; return $this; } public function getMessage(): string { return $this->message; } /** * @return $this */ public function setMessage(string $message): static { if (str_contains($message, "@anonymous\0")) { $message = preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $message); } $this->message = $message; return $this; } /** * @return int|string int most of the time (might be a string with PDOException) */ public function getCode(): int|string { return $this->code; } /** * @return $this */ public function setCode(int|string $code): static { $this->code = $code; return $this; } public function getPrevious(): ?self { return $this->previous; } /** * @return $this */ public function setPrevious(?self $previous): static { $this->previous = $previous; return $this; } /** * @return self[] */ public function getAllPrevious(): array { $exceptions = []; $e = $this; while ($e = $e->getPrevious()) { $exceptions[] = $e; } return $exceptions; } public function getTrace(): array { return $this->trace; } /** * @return $this */ public function setTraceFromThrowable(\Throwable $throwable): static { $this->traceAsString = $throwable->getTraceAsString(); return $this->setTrace($throwable->getTrace(), $throwable->getFile(), $throwable->getLine()); } /** * @return $this */ public function setTrace(array $trace, ?string $file, ?int $line): static { $this->trace = []; $this->trace[] = [ 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => $file, 'line' => $line, 'args' => [], ]; foreach ($trace as $entry) { $class = ''; $namespace = ''; if (isset($entry['class'])) { $parts = explode('\\', $entry['class']); $class = array_pop($parts); $namespace = implode('\\', $parts); } $this->trace[] = [ 'namespace' => $namespace, 'short_class' => $class, 'class' => $entry['class'] ?? '', 'type' => $entry['type'] ?? '', 'function' => $entry['function'] ?? null, 'file' => $entry['file'] ?? null, 'line' => $entry['line'] ?? null, 'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : [], ]; } return $this; } public function getDataRepresentation(): ?Data { return $this->dataRepresentation ?? null; } /** * @return $this */ public function setDataRepresentation(Data $data): static { $this->dataRepresentation = $data; return $this; } private function flattenArgs(array $args, int $level = 0, int &$count = 0): array { $result = []; foreach ($args as $key => $value) { if (++$count > 1e4) { return ['array', '*SKIPPED over 10000 entries*']; } if ($value instanceof \__PHP_Incomplete_Class) { $result[$key] = ['incomplete-object', $this->getClassNameFromIncomplete($value)]; } elseif (\is_object($value)) { $result[$key] = ['object', get_debug_type($value)]; } elseif (\is_array($value)) { if ($level > 10) { $result[$key] = ['array', '*DEEP NESTED ARRAY*']; } else { $result[$key] = ['array', $this->flattenArgs($value, $level + 1, $count)]; } } elseif (null === $value) { $result[$key] = ['null', null]; } elseif (\is_bool($value)) { $result[$key] = ['boolean', $value]; } elseif (\is_int($value)) { $result[$key] = ['integer', $value]; } elseif (\is_float($value)) { $result[$key] = ['float', $value]; } elseif (\is_resource($value)) { $result[$key] = ['resource', get_resource_type($value)]; } else { $result[$key] = ['string', (string) $value]; } } return $result; } private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value): string { $array = new \ArrayObject($value); return $array['__PHP_Incomplete_Class_Name']; } public function getTraceAsString(): string { return $this->traceAsString; } /** * @return $this */ public function setAsString(?string $asString): static { $this->asString = $asString; return $this; } public function getAsString(): string { if (null !== $this->asString) { return $this->asString; } $message = ''; $next = false; foreach (array_reverse(array_merge([$this], $this->getAllPrevious())) as $exception) { if ($next) { $message .= 'Next '; } else { $next = true; } $message .= $exception->getClass(); if ('' != $exception->getMessage()) { $message .= ': '.$exception->getMessage(); } $message .= ' in '.$exception->getFile().':'.$exception->getLine(). "\nStack trace:\n".$exception->getTraceAsString()."\n\n"; } return rtrim($message); } } error-handler/Exception/SilencedErrorContext.php 0000644 00000002427 15060133305 0016065 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Exception; /** * Data Object that represents a Silenced Error. * * @author Grégoire Pineau <lyrixx@lyrixx.info> */ class SilencedErrorContext implements \JsonSerializable { public int $count = 1; public function __construct( private int $severity, private string $file, private int $line, private array $trace = [], int $count = 1, ) { $this->count = $count; } public function getSeverity(): int { return $this->severity; } public function getFile(): string { return $this->file; } public function getLine(): int { return $this->line; } public function getTrace(): array { return $this->trace; } public function jsonSerialize(): array { return [ 'severity' => $this->severity, 'file' => $this->file, 'line' => $this->line, 'trace' => $this->trace, 'count' => $this->count, ]; } } error-handler/ErrorHandler.php 0000644 00000065403 15060133305 0012414 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; use Symfony\Component\ErrorHandler\Error\FatalError; use Symfony\Component\ErrorHandler\Error\OutOfMemoryError; use Symfony\Component\ErrorHandler\ErrorEnhancer\ClassNotFoundErrorEnhancer; use Symfony\Component\ErrorHandler\ErrorEnhancer\ErrorEnhancerInterface; use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedFunctionErrorEnhancer; use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedMethodErrorEnhancer; use Symfony\Component\ErrorHandler\ErrorRenderer\CliErrorRenderer; use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer; use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext; /** * A generic ErrorHandler for the PHP engine. * * Provides five bit fields that control how errors are handled: * - thrownErrors: errors thrown as \ErrorException * - loggedErrors: logged errors, when not @-silenced * - scopedErrors: errors thrown or logged with their local context * - tracedErrors: errors logged with their stack trace * - screamedErrors: never @-silenced errors * * Each error level can be logged by a dedicated PSR-3 logger object. * Screaming only applies to logging. * Throwing takes precedence over logging. * Uncaught exceptions are logged as E_ERROR. * E_DEPRECATED and E_USER_DEPRECATED levels never throw. * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw. * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so. * As errors have a performance cost, repeated errors are all logged, so that the developer * can see them and weight them as more important to fix than others of the same level. * * @author Nicolas Grekas <p@tchwork.com> * @author Grégoire Pineau <lyrixx@lyrixx.info> * * @final */ class ErrorHandler { private array $levels = [ \E_DEPRECATED => 'Deprecated', \E_USER_DEPRECATED => 'User Deprecated', \E_NOTICE => 'Notice', \E_USER_NOTICE => 'User Notice', \E_STRICT => 'Runtime Notice', \E_WARNING => 'Warning', \E_USER_WARNING => 'User Warning', \E_COMPILE_WARNING => 'Compile Warning', \E_CORE_WARNING => 'Core Warning', \E_USER_ERROR => 'User Error', \E_RECOVERABLE_ERROR => 'Catchable Fatal Error', \E_COMPILE_ERROR => 'Compile Error', \E_PARSE => 'Parse Error', \E_ERROR => 'Error', \E_CORE_ERROR => 'Core Error', ]; private array $loggers = [ \E_DEPRECATED => [null, LogLevel::INFO], \E_USER_DEPRECATED => [null, LogLevel::INFO], \E_NOTICE => [null, LogLevel::ERROR], \E_USER_NOTICE => [null, LogLevel::ERROR], \E_STRICT => [null, LogLevel::ERROR], \E_WARNING => [null, LogLevel::ERROR], \E_USER_WARNING => [null, LogLevel::ERROR], \E_COMPILE_WARNING => [null, LogLevel::ERROR], \E_CORE_WARNING => [null, LogLevel::ERROR], \E_USER_ERROR => [null, LogLevel::CRITICAL], \E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL], \E_COMPILE_ERROR => [null, LogLevel::CRITICAL], \E_PARSE => [null, LogLevel::CRITICAL], \E_ERROR => [null, LogLevel::CRITICAL], \E_CORE_ERROR => [null, LogLevel::CRITICAL], ]; private int $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED private int $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED private int $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE private int $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE private int $loggedErrors = 0; private \Closure $configureException; private bool $debug; private bool $isRecursive = false; private bool $isRoot = false; /** @var callable|null */ private $exceptionHandler; private ?BufferingLogger $bootstrappingLogger = null; private static ?string $reservedMemory = null; private static array $silencedErrorCache = []; private static int $silencedErrorCount = 0; private static int $exitCode = 0; /** * Registers the error handler. */ public static function register(?self $handler = null, bool $replace = true): self { if (null === self::$reservedMemory) { self::$reservedMemory = str_repeat('x', 32768); register_shutdown_function(self::handleFatalError(...)); } if ($handlerIsNew = null === $handler) { $handler = new static(); } if (null === $prev = set_error_handler([$handler, 'handleError'])) { restore_error_handler(); // Specifying the error types earlier would expose us to https://bugs.php.net/63206 set_error_handler([$handler, 'handleError'], $handler->thrownErrors | $handler->loggedErrors); $handler->isRoot = true; } if ($handlerIsNew && \is_array($prev) && $prev[0] instanceof self) { $handler = $prev[0]; $replace = false; } if (!$replace && $prev) { restore_error_handler(); $handlerIsRegistered = \is_array($prev) && $handler === $prev[0]; } else { $handlerIsRegistered = true; } if (\is_array($prev = set_exception_handler([$handler, 'handleException'])) && $prev[0] instanceof self) { restore_exception_handler(); if (!$handlerIsRegistered) { $handler = $prev[0]; } elseif ($handler !== $prev[0] && $replace) { set_exception_handler([$handler, 'handleException']); $p = $prev[0]->setExceptionHandler(null); $handler->setExceptionHandler($p); $prev[0]->setExceptionHandler($p); } } else { $handler->setExceptionHandler($prev ?? [$handler, 'renderException']); } $handler->throwAt(\E_ALL & $handler->thrownErrors, true); return $handler; } /** * Calls a function and turns any PHP error into \ErrorException. * * @throws \ErrorException When $function(...$arguments) triggers a PHP error */ public static function call(callable $function, mixed ...$arguments): mixed { set_error_handler(static function (int $type, string $message, string $file, int $line) { if (__FILE__ === $file) { $trace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 3); $file = $trace[2]['file'] ?? $file; $line = $trace[2]['line'] ?? $line; } throw new \ErrorException($message, 0, $type, $file, $line); }); try { return $function(...$arguments); } finally { restore_error_handler(); } } public function __construct(?BufferingLogger $bootstrappingLogger = null, bool $debug = false) { if ($bootstrappingLogger) { $this->bootstrappingLogger = $bootstrappingLogger; $this->setDefaultLogger($bootstrappingLogger); } $traceReflector = new \ReflectionProperty(\Exception::class, 'trace'); $this->configureException = \Closure::bind(static function ($e, $trace, $file = null, $line = null) use ($traceReflector) { $traceReflector->setValue($e, $trace); $e->file = $file ?? $e->file; $e->line = $line ?? $e->line; }, null, new class() extends \Exception { }); $this->debug = $debug; } /** * Sets a logger to non assigned errors levels. * * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels * @param array|int|null $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants * @param bool $replace Whether to replace or not any existing logger */ public function setDefaultLogger(LoggerInterface $logger, array|int|null $levels = \E_ALL, bool $replace = false): void { $loggers = []; if (\is_array($levels)) { foreach ($levels as $type => $logLevel) { if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) { $loggers[$type] = [$logger, $logLevel]; } } } else { $levels ??= \E_ALL; foreach ($this->loggers as $type => $log) { if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) { $log[0] = $logger; $loggers[$type] = $log; } } } $this->setLoggers($loggers); } /** * Sets a logger for each error level. * * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map * * @throws \InvalidArgumentException */ public function setLoggers(array $loggers): array { $prevLogged = $this->loggedErrors; $prev = $this->loggers; $flush = []; foreach ($loggers as $type => $log) { if (!isset($prev[$type])) { throw new \InvalidArgumentException('Unknown error type: '.$type); } if (!\is_array($log)) { $log = [$log]; } elseif (!\array_key_exists(0, $log)) { throw new \InvalidArgumentException('No logger provided.'); } if (null === $log[0]) { $this->loggedErrors &= ~$type; } elseif ($log[0] instanceof LoggerInterface) { $this->loggedErrors |= $type; } else { throw new \InvalidArgumentException('Invalid logger provided.'); } $this->loggers[$type] = $log + $prev[$type]; if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) { $flush[$type] = $type; } } $this->reRegister($prevLogged | $this->thrownErrors); if ($flush) { foreach ($this->bootstrappingLogger->cleanLogs() as $log) { $type = ThrowableUtils::getSeverity($log[2]['exception']); if (!isset($flush[$type])) { $this->bootstrappingLogger->log($log[0], $log[1], $log[2]); } elseif ($this->loggers[$type][0]) { $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]); } } } return $prev; } public function setExceptionHandler(?callable $handler): ?callable { $prev = $this->exceptionHandler; $this->exceptionHandler = $handler; return $prev; } /** * Sets the PHP error levels that throw an exception when a PHP error occurs. * * @param int $levels A bit field of E_* constants for thrown errors * @param bool $replace Replace or amend the previous value */ public function throwAt(int $levels, bool $replace = false): int { $prev = $this->thrownErrors; $this->thrownErrors = ($levels | \E_RECOVERABLE_ERROR | \E_USER_ERROR) & ~\E_USER_DEPRECATED & ~\E_DEPRECATED; if (!$replace) { $this->thrownErrors |= $prev; } $this->reRegister($prev | $this->loggedErrors); return $prev; } /** * Sets the PHP error levels for which local variables are preserved. * * @param int $levels A bit field of E_* constants for scoped errors * @param bool $replace Replace or amend the previous value */ public function scopeAt(int $levels, bool $replace = false): int { $prev = $this->scopedErrors; $this->scopedErrors = $levels; if (!$replace) { $this->scopedErrors |= $prev; } return $prev; } /** * Sets the PHP error levels for which the stack trace is preserved. * * @param int $levels A bit field of E_* constants for traced errors * @param bool $replace Replace or amend the previous value */ public function traceAt(int $levels, bool $replace = false): int { $prev = $this->tracedErrors; $this->tracedErrors = $levels; if (!$replace) { $this->tracedErrors |= $prev; } return $prev; } /** * Sets the error levels where the @-operator is ignored. * * @param int $levels A bit field of E_* constants for screamed errors * @param bool $replace Replace or amend the previous value */ public function screamAt(int $levels, bool $replace = false): int { $prev = $this->screamedErrors; $this->screamedErrors = $levels; if (!$replace) { $this->screamedErrors |= $prev; } return $prev; } /** * Re-registers as a PHP error handler if levels changed. */ private function reRegister(int $prev): void { if ($prev !== ($this->thrownErrors | $this->loggedErrors)) { $handler = set_error_handler(static fn () => null); $handler = \is_array($handler) ? $handler[0] : null; restore_error_handler(); if ($handler === $this) { restore_error_handler(); if ($this->isRoot) { set_error_handler([$this, 'handleError'], $this->thrownErrors | $this->loggedErrors); } else { set_error_handler([$this, 'handleError']); } } } } /** * Handles errors by filtering then logging them according to the configured bit fields. * * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself * * @throws \ErrorException When $this->thrownErrors requests so * * @internal */ public function handleError(int $type, string $message, string $file, int $line): bool { if (\E_WARNING === $type && '"' === $message[0] && str_contains($message, '" targeting switch is equivalent to "break')) { $type = \E_DEPRECATED; } // Level is the current error reporting level to manage silent error. $level = error_reporting(); $silenced = 0 === ($level & $type); // Strong errors are not authorized to be silenced. $level |= \E_RECOVERABLE_ERROR | \E_USER_ERROR | \E_DEPRECATED | \E_USER_DEPRECATED; $log = $this->loggedErrors & $type; $throw = $this->thrownErrors & $type & $level; $type &= $level | $this->screamedErrors; // Never throw on warnings triggered by assert() if (\E_WARNING === $type && 'a' === $message[0] && 0 === strncmp($message, 'assert(): ', 10)) { $throw = 0; } if (!$type || (!$log && !$throw)) { return false; } $logMessage = $this->levels[$type].': '.$message; if (!$throw && !($type & $level)) { if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) { $lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 5), $type, $file, $line, false) : []; $errorAsException = new SilencedErrorContext($type, $file, $line, isset($lightTrace[1]) ? [$lightTrace[0]] : $lightTrace); } elseif (isset(self::$silencedErrorCache[$id][$message])) { $lightTrace = null; $errorAsException = self::$silencedErrorCache[$id][$message]; ++$errorAsException->count; } else { $lightTrace = []; $errorAsException = null; } if (100 < ++self::$silencedErrorCount) { self::$silencedErrorCache = $lightTrace = []; self::$silencedErrorCount = 1; } if ($errorAsException) { self::$silencedErrorCache[$id][$message] = $errorAsException; } if (null === $lightTrace) { return true; } } else { if (\PHP_VERSION_ID < 80303 && str_contains($message, '@anonymous')) { $backtrace = debug_backtrace(false, 5); for ($i = 1; isset($backtrace[$i]); ++$i) { if (isset($backtrace[$i]['function'], $backtrace[$i]['args'][0]) && ('trigger_error' === $backtrace[$i]['function'] || 'user_error' === $backtrace[$i]['function']) ) { if ($backtrace[$i]['args'][0] !== $message) { $message = $backtrace[$i]['args'][0]; } break; } } } if (str_contains($message, "@anonymous\0")) { $message = $this->parseAnonymousClass($message); $logMessage = $this->levels[$type].': '.$message; } $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line); if ($throw || $this->tracedErrors & $type) { $backtrace = $errorAsException->getTrace(); $backtrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw); ($this->configureException)($errorAsException, $backtrace, $file, $line); } else { ($this->configureException)($errorAsException, []); } } if ($throw) { throw $errorAsException; } if ($this->isRecursive) { $log = 0; } else { try { $this->isRecursive = true; $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG; $this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? ['exception' => $errorAsException] : []); } finally { $this->isRecursive = false; } } return !$silenced && $type && $log; } /** * Handles an exception by logging then forwarding it to another handler. * * @internal */ public function handleException(\Throwable $exception): void { $handlerException = null; if (!$exception instanceof FatalError) { self::$exitCode = 255; $type = ThrowableUtils::getSeverity($exception); } else { $type = $exception->getError()['type']; } if ($this->loggedErrors & $type) { if (str_contains($message = $exception->getMessage(), "@anonymous\0")) { $message = $this->parseAnonymousClass($message); } if ($exception instanceof FatalError) { $message = 'Fatal '.$message; } elseif ($exception instanceof \Error) { $message = 'Uncaught Error: '.$message; } elseif ($exception instanceof \ErrorException) { $message = 'Uncaught '.$message; } else { $message = 'Uncaught Exception: '.$message; } try { $this->loggers[$type][0]->log($this->loggers[$type][1], $message, ['exception' => $exception]); } catch (\Throwable $handlerException) { } } $exception = $this->enhanceError($exception); $exceptionHandler = $this->exceptionHandler; $this->exceptionHandler = [$this, 'renderException']; if (null === $exceptionHandler || $exceptionHandler === $this->exceptionHandler) { $this->exceptionHandler = null; } try { if (null !== $exceptionHandler) { $exceptionHandler($exception); return; } $handlerException ??= $exception; } catch (\Throwable $handlerException) { } if ($exception === $handlerException && null === $this->exceptionHandler) { self::$reservedMemory = null; // Disable the fatal error handler throw $exception; // Give back $exception to the native handler } $loggedErrors = $this->loggedErrors; if ($exception === $handlerException) { $this->loggedErrors &= ~$type; } try { $this->handleException($handlerException); } finally { $this->loggedErrors = $loggedErrors; } } /** * Shutdown registered function for handling PHP fatal errors. * * @param array|null $error An array as returned by error_get_last() * * @internal */ public static function handleFatalError(?array $error = null): void { if (null === self::$reservedMemory) { return; } $handler = self::$reservedMemory = null; $handlers = []; $previousHandler = null; $sameHandlerLimit = 10; while (!\is_array($handler) || !$handler[0] instanceof self) { $handler = set_exception_handler('is_int'); restore_exception_handler(); if (!$handler) { break; } restore_exception_handler(); if ($handler !== $previousHandler) { array_unshift($handlers, $handler); $previousHandler = $handler; } elseif (0 === --$sameHandlerLimit) { $handler = null; break; } } foreach ($handlers as $h) { set_exception_handler($h); } if (!$handler) { if (null === $error && $exitCode = self::$exitCode) { register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); }); } return; } if ($handler !== $h) { $handler[0]->setExceptionHandler($h); } $handler = $handler[0]; $handlers = []; if ($exit = null === $error) { $error = error_get_last(); } if ($error && $error['type'] &= \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR) { // Let's not throw anymore but keep logging $handler->throwAt(0, true); $trace = $error['backtrace'] ?? null; if (str_starts_with($error['message'], 'Allowed memory') || str_starts_with($error['message'], 'Out of memory')) { $fatalError = new OutOfMemoryError($handler->levels[$error['type']].': '.$error['message'], 0, $error, 2, false, $trace); } else { $fatalError = new FatalError($handler->levels[$error['type']].': '.$error['message'], 0, $error, 2, true, $trace); } } else { $fatalError = null; } try { if (null !== $fatalError) { self::$exitCode = 255; $handler->handleException($fatalError); } } catch (FatalError) { // Ignore this re-throw } if ($exit && $exitCode = self::$exitCode) { register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); }); } } /** * Renders the given exception. * * As this method is mainly called during boot where nothing is yet available, * the output is always either HTML or CLI depending where PHP runs. */ private function renderException(\Throwable $exception): void { $renderer = \in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) ? new CliErrorRenderer() : new HtmlErrorRenderer($this->debug); $exception = $renderer->render($exception); if (!headers_sent()) { http_response_code($exception->getStatusCode()); foreach ($exception->getHeaders() as $name => $value) { header($name.': '.$value, false); } } echo $exception->getAsString(); } public function enhanceError(\Throwable $exception): \Throwable { if ($exception instanceof OutOfMemoryError) { return $exception; } foreach ($this->getErrorEnhancers() as $errorEnhancer) { if ($e = $errorEnhancer->enhance($exception)) { return $e; } } return $exception; } /** * Override this method if you want to define more error enhancers. * * @return ErrorEnhancerInterface[] */ protected function getErrorEnhancers(): iterable { return [ new UndefinedFunctionErrorEnhancer(), new UndefinedMethodErrorEnhancer(), new ClassNotFoundErrorEnhancer(), ]; } /** * Cleans the trace by removing function arguments and the frames added by the error handler and DebugClassLoader. */ private function cleanTrace(array $backtrace, int $type, string &$file, int &$line, bool $throw): array { $lightTrace = $backtrace; for ($i = 0; isset($backtrace[$i]); ++$i) { if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) { $lightTrace = \array_slice($lightTrace, 1 + $i); break; } } if (\E_USER_DEPRECATED === $type) { for ($i = 0; isset($lightTrace[$i]); ++$i) { if (!isset($lightTrace[$i]['file'], $lightTrace[$i]['line'], $lightTrace[$i]['function'])) { continue; } if (!isset($lightTrace[$i]['class']) && 'trigger_deprecation' === $lightTrace[$i]['function']) { $file = $lightTrace[$i]['file']; $line = $lightTrace[$i]['line']; $lightTrace = \array_slice($lightTrace, 1 + $i); break; } } } if (class_exists(DebugClassLoader::class, false)) { for ($i = \count($lightTrace) - 2; 0 < $i; --$i) { if (DebugClassLoader::class === ($lightTrace[$i]['class'] ?? null)) { array_splice($lightTrace, --$i, 2); } } } if (!($throw || $this->scopedErrors & $type)) { for ($i = 0; isset($lightTrace[$i]); ++$i) { unset($lightTrace[$i]['args'], $lightTrace[$i]['object']); } } return $lightTrace; } /** * Parse the error message by removing the anonymous class notation * and using the parent class instead if possible. */ private function parseAnonymousClass(string $message): string { return preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', static fn ($m) => class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0], $message); } } error-handler/ThrowableUtils.php 0000644 00000001541 15060133305 0012766 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler; use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext; /** * @internal */ class ThrowableUtils { public static function getSeverity(SilencedErrorContext|\Throwable $throwable): int { if ($throwable instanceof \ErrorException || $throwable instanceof SilencedErrorContext) { return $throwable->getSeverity(); } if ($throwable instanceof \ParseError) { return \E_PARSE; } if ($throwable instanceof \TypeError) { return \E_RECOVERABLE_ERROR; } return \E_ERROR; } } error-handler/ErrorEnhancer/ErrorEnhancerInterface.php 0000644 00000000772 15060133305 0017136 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorEnhancer; interface ErrorEnhancerInterface { /** * Returns an \Throwable instance if the class is able to improve the error, null otherwise. */ public function enhance(\Throwable $error): ?\Throwable; } error-handler/ErrorEnhancer/UndefinedMethodErrorEnhancer.php 0000644 00000004140 15060133305 0020271 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorEnhancer; use Symfony\Component\ErrorHandler\Error\FatalError; use Symfony\Component\ErrorHandler\Error\UndefinedMethodError; /** * @author Grégoire Pineau <lyrixx@lyrixx.info> */ class UndefinedMethodErrorEnhancer implements ErrorEnhancerInterface { public function enhance(\Throwable $error): ?\Throwable { if ($error instanceof FatalError) { return null; } $message = $error->getMessage(); preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $message, $matches); if (!$matches) { return null; } $className = $matches[1]; $methodName = $matches[2]; $message = sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className); if ('' === $methodName || !class_exists($className) || null === $methods = get_class_methods($className)) { // failed to get the class or its methods on which an unknown method was called (for example on an anonymous class) return new UndefinedMethodError($message, $error); } $candidates = []; foreach ($methods as $definedMethodName) { $lev = levenshtein($methodName, $definedMethodName); if ($lev <= \strlen($methodName) / 3 || str_contains($definedMethodName, $methodName)) { $candidates[] = $definedMethodName; } } if ($candidates) { sort($candidates); $last = array_pop($candidates).'"?'; if ($candidates) { $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; } else { $candidates = '"'.$last; } $message .= "\nDid you mean to call ".$candidates; } return new UndefinedMethodError($message, $error); } } error-handler/ErrorEnhancer/UndefinedFunctionErrorEnhancer.php 0000644 00000005744 15060133305 0020651 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorEnhancer; use Symfony\Component\ErrorHandler\Error\FatalError; use Symfony\Component\ErrorHandler\Error\UndefinedFunctionError; /** * @author Fabien Potencier <fabien@symfony.com> */ class UndefinedFunctionErrorEnhancer implements ErrorEnhancerInterface { public function enhance(\Throwable $error): ?\Throwable { if ($error instanceof FatalError) { return null; } $message = $error->getMessage(); $messageLen = \strlen($message); $notFoundSuffix = '()'; $notFoundSuffixLen = \strlen($notFoundSuffix); if ($notFoundSuffixLen > $messageLen) { return null; } if (0 !== substr_compare($message, $notFoundSuffix, -$notFoundSuffixLen)) { return null; } $prefix = 'Call to undefined function '; $prefixLen = \strlen($prefix); if (!str_starts_with($message, $prefix)) { return null; } $fullyQualifiedFunctionName = substr($message, $prefixLen, -$notFoundSuffixLen); if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) { $functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1); $namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex); $message = sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix); } else { $functionName = $fullyQualifiedFunctionName; $message = sprintf('Attempted to call function "%s" from the global namespace.', $functionName); } $candidates = []; foreach (get_defined_functions() as $type => $definedFunctionNames) { foreach ($definedFunctionNames as $definedFunctionName) { if (false !== $namespaceSeparatorIndex = strrpos($definedFunctionName, '\\')) { $definedFunctionNameBasename = substr($definedFunctionName, $namespaceSeparatorIndex + 1); } else { $definedFunctionNameBasename = $definedFunctionName; } if ($definedFunctionNameBasename === $functionName) { $candidates[] = '\\'.$definedFunctionName; } } } if ($candidates) { sort($candidates); $last = array_pop($candidates).'"?'; if ($candidates) { $candidates = 'e.g. "'.implode('", "', $candidates).'" or "'.$last; } else { $candidates = '"'.$last; } $message .= "\nDid you mean to call ".$candidates; } return new UndefinedFunctionError($message, $error); } } error-handler/ErrorEnhancer/ClassNotFoundErrorEnhancer.php 0000644 00000015337 15060133305 0017763 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorEnhancer; use Composer\Autoload\ClassLoader; use Symfony\Component\ErrorHandler\DebugClassLoader; use Symfony\Component\ErrorHandler\Error\ClassNotFoundError; use Symfony\Component\ErrorHandler\Error\FatalError; /** * @author Fabien Potencier <fabien@symfony.com> */ class ClassNotFoundErrorEnhancer implements ErrorEnhancerInterface { public function enhance(\Throwable $error): ?\Throwable { // Some specific versions of PHP produce a fatal error when extending a not found class. $message = !$error instanceof FatalError ? $error->getMessage() : $error->getError()['message']; if (!preg_match('/^(Class|Interface|Trait) [\'"]([^\'"]+)[\'"] not found$/', $message, $matches)) { return null; } $typeName = strtolower($matches[1]); $fullyQualifiedClassName = $matches[2]; if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) { $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1); $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex); $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix); $tail = ' for another namespace?'; } else { $className = $fullyQualifiedClassName; $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className); $tail = '?'; } if ($candidates = $this->getClassCandidates($className)) { $tail = array_pop($candidates).'"?'; if ($candidates) { $tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail; } else { $tail = ' for "'.$tail; } } $message .= "\nDid you forget a \"use\" statement".$tail; return new ClassNotFoundError($message, $error); } /** * Tries to guess the full namespace for a given class name. * * By default, it looks for PSR-0 and PSR-4 classes registered via a Symfony or a Composer * autoloader (that should cover all common cases). * * @param string $class A class name (without its namespace) * * Returns an array of possible fully qualified class names */ private function getClassCandidates(string $class): array { if (!\is_array($functions = spl_autoload_functions())) { return []; } // find Symfony and Composer autoloaders $classes = []; foreach ($functions as $function) { if (!\is_array($function)) { continue; } // get class loaders wrapped by DebugClassLoader if ($function[0] instanceof DebugClassLoader) { $function = $function[0]->getClassLoader(); if (!\is_array($function)) { continue; } } if ($function[0] instanceof ClassLoader) { foreach ($function[0]->getPrefixes() as $prefix => $paths) { foreach ($paths as $path) { $classes[] = $this->findClassInPath($path, $class, $prefix); } } foreach ($function[0]->getPrefixesPsr4() as $prefix => $paths) { foreach ($paths as $path) { $classes[] = $this->findClassInPath($path, $class, $prefix); } } } } return array_unique(array_merge([], ...$classes)); } private function findClassInPath(string $path, string $class, string $prefix): array { $path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.\dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path); if (!$path || !is_dir($path)) { return []; } $classes = []; $filename = $class.'.php'; foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) { $classes[] = $class; } } return $classes; } private function convertFileToClass(string $path, string $file, string $prefix): ?string { $candidates = [ // namespaced class $namespacedClass = str_replace([$path.\DIRECTORY_SEPARATOR, '.php', '/'], ['', '', '\\'], $file), // namespaced class (with target dir) $prefix.$namespacedClass, // namespaced class (with target dir and separator) $prefix.'\\'.$namespacedClass, // PEAR class str_replace('\\', '_', $namespacedClass), // PEAR class (with target dir) str_replace('\\', '_', $prefix.$namespacedClass), // PEAR class (with target dir and separator) str_replace('\\', '_', $prefix.'\\'.$namespacedClass), ]; if ($prefix) { $candidates = array_filter($candidates, fn ($candidate) => str_starts_with($candidate, $prefix)); } // We cannot use the autoloader here as most of them use require; but if the class // is not found, the new autoloader call will require the file again leading to a // "cannot redeclare class" error. foreach ($candidates as $candidate) { if ($this->classExists($candidate)) { return $candidate; } } // Symfony may ship some polyfills, like "Normalizer". But if the Intl // extension is already installed, the next require_once will fail with // a compile error because the class is already defined. And this one // does not throw a Throwable. So it's better to skip it here. if (str_contains($file, 'Resources/stubs')) { return null; } try { require_once $file; } catch (\Throwable) { return null; } foreach ($candidates as $candidate) { if ($this->classExists($candidate)) { return $candidate; } } return null; } private function classExists(string $class): bool { return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); } } error-handler/Internal/TentativeTypes.php 0000644 00000163377 15060133305 0014602 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Internal; /** * This class has been generated by extract-tentative-return-types.php. * * @internal */ class TentativeTypes { public const RETURN_TYPES = [ 'CURLFile' => [ 'getFilename' => 'string', 'getMimeType' => 'string', 'getPostFilename' => 'string', 'setMimeType' => 'void', 'setPostFilename' => 'void', ], 'DateTimeInterface' => [ 'format' => 'string', 'getTimezone' => 'DateTimeZone|false', 'getOffset' => 'int', 'getTimestamp' => 'int', 'diff' => 'DateInterval', '__wakeup' => 'void', ], 'DateTime' => [ '__wakeup' => 'void', '__set_state' => 'DateTime', 'createFromImmutable' => 'static', 'createFromFormat' => 'DateTime|false', 'getLastErrors' => 'array|false', 'format' => 'string', 'modify' => 'DateTime|false', 'add' => 'DateTime', 'sub' => 'DateTime', 'getTimezone' => 'DateTimeZone|false', 'setTimezone' => 'DateTime', 'getOffset' => 'int', 'setTime' => 'DateTime', 'setDate' => 'DateTime', 'setISODate' => 'DateTime', 'setTimestamp' => 'DateTime', 'getTimestamp' => 'int', 'diff' => 'DateInterval', ], 'DateTimeImmutable' => [ '__wakeup' => 'void', '__set_state' => 'DateTimeImmutable', 'createFromFormat' => 'DateTimeImmutable|false', 'getLastErrors' => 'array|false', 'format' => 'string', 'getTimezone' => 'DateTimeZone|false', 'getOffset' => 'int', 'getTimestamp' => 'int', 'diff' => 'DateInterval', 'modify' => 'DateTimeImmutable|false', 'add' => 'DateTimeImmutable', 'sub' => 'DateTimeImmutable', 'setTimezone' => 'DateTimeImmutable', 'setTime' => 'DateTimeImmutable', 'setDate' => 'DateTimeImmutable', 'setISODate' => 'DateTimeImmutable', 'setTimestamp' => 'DateTimeImmutable', 'createFromMutable' => 'static', ], 'DateTimeZone' => [ 'getName' => 'string', 'getOffset' => 'int', 'getTransitions' => 'array|false', 'getLocation' => 'array|false', 'listAbbreviations' => 'array', 'listIdentifiers' => 'array', '__wakeup' => 'void', '__set_state' => 'DateTimeZone', ], 'DateInterval' => [ 'createFromDateString' => 'DateInterval|false', 'format' => 'string', '__wakeup' => 'void', '__set_state' => 'DateInterval', ], 'DatePeriod' => [ 'getStartDate' => 'DateTimeInterface', 'getEndDate' => '?DateTimeInterface', 'getDateInterval' => 'DateInterval', 'getRecurrences' => '?int', '__wakeup' => 'void', '__set_state' => 'DatePeriod', ], 'DOMNode' => [ 'C14N' => 'string|false', 'C14NFile' => 'int|false', 'getLineNo' => 'int', 'getNodePath' => '?string', 'hasAttributes' => 'bool', 'hasChildNodes' => 'bool', 'isDefaultNamespace' => 'bool', 'isSameNode' => 'bool', 'isSupported' => 'bool', 'lookupNamespaceURI' => '?string', 'lookupPrefix' => '?string', 'normalize' => 'void', ], 'DOMImplementation' => [ 'getFeature' => 'never', 'hasFeature' => 'bool', ], 'DOMDocumentFragment' => [ 'appendXML' => 'bool', ], 'DOMNodeList' => [ 'count' => 'int', ], 'DOMCharacterData' => [ 'appendData' => 'bool', 'insertData' => 'bool', 'deleteData' => 'bool', 'replaceData' => 'bool', ], 'DOMAttr' => [ 'isId' => 'bool', ], 'DOMElement' => [ 'getAttribute' => 'string', 'getAttributeNS' => 'string', 'getElementsByTagName' => 'DOMNodeList', 'getElementsByTagNameNS' => 'DOMNodeList', 'hasAttribute' => 'bool', 'hasAttributeNS' => 'bool', 'removeAttribute' => 'bool', 'removeAttributeNS' => 'void', 'setAttributeNS' => 'void', 'setIdAttribute' => 'void', 'setIdAttributeNS' => 'void', 'setIdAttributeNode' => 'void', ], 'DOMDocument' => [ 'createComment' => 'DOMComment', 'createDocumentFragment' => 'DOMDocumentFragment', 'createTextNode' => 'DOMText', 'getElementById' => '?DOMElement', 'getElementsByTagName' => 'DOMNodeList', 'getElementsByTagNameNS' => 'DOMNodeList', 'normalizeDocument' => 'void', 'registerNodeClass' => 'bool', 'save' => 'int|false', 'saveHTML' => 'string|false', 'saveHTMLFile' => 'int|false', 'saveXML' => 'string|false', 'schemaValidate' => 'bool', 'schemaValidateSource' => 'bool', 'relaxNGValidate' => 'bool', 'relaxNGValidateSource' => 'bool', 'validate' => 'bool', 'xinclude' => 'int|false', ], 'DOMText' => [ 'isWhitespaceInElementContent' => 'bool', 'isElementContentWhitespace' => 'bool', ], 'DOMNamedNodeMap' => [ 'getNamedItem' => '?DOMNode', 'getNamedItemNS' => '?DOMNode', 'item' => '?DOMNode', 'count' => 'int', ], 'DOMXPath' => [ 'evaluate' => 'mixed', 'query' => 'mixed', 'registerNamespace' => 'bool', 'registerPhpFunctions' => 'void', ], 'finfo' => [ 'file' => 'string|false', 'buffer' => 'string|false', ], 'IntlPartsIterator' => [ 'getBreakIterator' => 'IntlBreakIterator', 'getRuleStatus' => 'int', ], 'IntlBreakIterator' => [ 'createCharacterInstance' => '?IntlBreakIterator', 'createCodePointInstance' => 'IntlCodePointBreakIterator', 'createLineInstance' => '?IntlBreakIterator', 'createSentenceInstance' => '?IntlBreakIterator', 'createTitleInstance' => '?IntlBreakIterator', 'createWordInstance' => '?IntlBreakIterator', 'current' => 'int', 'first' => 'int', 'following' => 'int', 'getErrorCode' => 'int', 'getErrorMessage' => 'string', 'getLocale' => 'string|false', 'getPartsIterator' => 'IntlPartsIterator', 'getText' => '?string', 'isBoundary' => 'bool', 'last' => 'int', 'next' => 'int', 'preceding' => 'int', 'previous' => 'int', 'setText' => '?bool', ], 'IntlRuleBasedBreakIterator' => [ 'getBinaryRules' => 'string|false', 'getRules' => 'string|false', 'getRuleStatus' => 'int', 'getRuleStatusVec' => 'array|false', ], 'IntlCodePointBreakIterator' => [ 'getLastCodePoint' => 'int', ], 'IntlCalendar' => [ 'createInstance' => '?IntlCalendar', 'equals' => 'bool', 'fieldDifference' => 'int|false', 'add' => 'bool', 'after' => 'bool', 'before' => 'bool', 'fromDateTime' => '?IntlCalendar', 'get' => 'int|false', 'getActualMaximum' => 'int|false', 'getActualMinimum' => 'int|false', 'getAvailableLocales' => 'array', 'getDayOfWeekType' => 'int|false', 'getErrorCode' => 'int|false', 'getErrorMessage' => 'string|false', 'getFirstDayOfWeek' => 'int|false', 'getGreatestMinimum' => 'int|false', 'getKeywordValuesForLocale' => 'IntlIterator|false', 'getLeastMaximum' => 'int|false', 'getLocale' => 'string|false', 'getMaximum' => 'int|false', 'getMinimalDaysInFirstWeek' => 'int|false', 'getMinimum' => 'int|false', 'getNow' => 'float', 'getRepeatedWallTimeOption' => 'int', 'getSkippedWallTimeOption' => 'int', 'getTime' => 'float|false', 'getTimeZone' => 'IntlTimeZone|false', 'getType' => 'string', 'getWeekendTransition' => 'int|false', 'inDaylightTime' => 'bool', 'isEquivalentTo' => 'bool', 'isLenient' => 'bool', 'isWeekend' => 'bool', 'roll' => 'bool', 'isSet' => 'bool', 'setTime' => 'bool', 'setTimeZone' => 'bool', 'toDateTime' => 'DateTime|false', ], 'IntlGregorianCalendar' => [ 'setGregorianChange' => 'bool', 'getGregorianChange' => 'float', 'isLeapYear' => 'bool', ], 'Collator' => [ 'create' => '?Collator', 'compare' => 'int|false', 'sort' => 'bool', 'sortWithSortKeys' => 'bool', 'asort' => 'bool', 'getAttribute' => 'int|false', 'setAttribute' => 'bool', 'getStrength' => 'int', 'getLocale' => 'string|false', 'getErrorCode' => 'int|false', 'getErrorMessage' => 'string|false', 'getSortKey' => 'string|false', ], 'IntlIterator' => [ 'current' => 'mixed', 'key' => 'mixed', 'next' => 'void', 'rewind' => 'void', 'valid' => 'bool', ], 'UConverter' => [ 'convert' => 'string|false', 'fromUCallback' => 'string|int|array|null', 'getAliases' => 'array|false|null', 'getAvailable' => 'array', 'getDestinationEncoding' => 'string|false|null', 'getDestinationType' => 'int|false|null', 'getErrorCode' => 'int', 'getErrorMessage' => '?string', 'getSourceEncoding' => 'string|false|null', 'getSourceType' => 'int|false|null', 'getStandards' => '?array', 'getSubstChars' => 'string|false|null', 'reasonText' => 'string', 'setDestinationEncoding' => 'bool', 'setSourceEncoding' => 'bool', 'setSubstChars' => 'bool', 'toUCallback' => 'string|int|array|null', 'transcode' => 'string|false', ], 'IntlDateFormatter' => [ 'create' => '?IntlDateFormatter', 'getDateType' => 'int|false', 'getTimeType' => 'int|false', 'getCalendar' => 'int|false', 'setCalendar' => 'bool', 'getTimeZoneId' => 'string|false', 'getCalendarObject' => 'IntlCalendar|false|null', 'getTimeZone' => 'IntlTimeZone|false', 'setTimeZone' => '?bool', 'setPattern' => 'bool', 'getPattern' => 'string|false', 'getLocale' => 'string|false', 'setLenient' => 'void', 'isLenient' => 'bool', 'format' => 'string|false', 'formatObject' => 'string|false', 'parse' => 'int|float|false', 'localtime' => 'array|false', 'getErrorCode' => 'int', 'getErrorMessage' => 'string', ], 'NumberFormatter' => [ 'create' => '?NumberFormatter', 'format' => 'string|false', 'parse' => 'int|float|false', 'formatCurrency' => 'string|false', 'parseCurrency' => 'float|false', 'setAttribute' => 'bool', 'getAttribute' => 'int|float|false', 'setTextAttribute' => 'bool', 'getTextAttribute' => 'string|false', 'setSymbol' => 'bool', 'getSymbol' => 'string|false', 'setPattern' => 'bool', 'getPattern' => 'string|false', 'getLocale' => 'string|false', 'getErrorCode' => 'int', 'getErrorMessage' => 'string', ], 'Locale' => [ 'getDefault' => 'string', 'getPrimaryLanguage' => '?string', 'getScript' => '?string', 'getRegion' => '?string', 'getKeywords' => 'array|false|null', 'getDisplayScript' => 'string|false', 'getDisplayRegion' => 'string|false', 'getDisplayName' => 'string|false', 'getDisplayLanguage' => 'string|false', 'getDisplayVariant' => 'string|false', 'composeLocale' => 'string|false', 'parseLocale' => '?array', 'getAllVariants' => '?array', 'filterMatches' => '?bool', 'lookup' => '?string', 'canonicalize' => '?string', 'acceptFromHttp' => 'string|false', ], 'MessageFormatter' => [ 'create' => '?MessageFormatter', 'format' => 'string|false', 'formatMessage' => 'string|false', 'parse' => 'array|false', 'parseMessage' => 'array|false', 'setPattern' => 'bool', 'getPattern' => 'string|false', 'getLocale' => 'string', 'getErrorCode' => 'int', 'getErrorMessage' => 'string', ], 'Normalizer' => [ 'normalize' => 'string|false', 'isNormalized' => 'bool', 'getRawDecomposition' => '?string', ], 'ResourceBundle' => [ 'create' => '?ResourceBundle', 'get' => 'mixed', 'count' => 'int', 'getLocales' => 'array|false', 'getErrorCode' => 'int', 'getErrorMessage' => 'string', ], 'Spoofchecker' => [ 'isSuspicious' => 'bool', 'areConfusable' => 'bool', 'setAllowedLocales' => 'void', 'setChecks' => 'void', 'setRestrictionLevel' => 'void', ], 'IntlTimeZone' => [ 'countEquivalentIDs' => 'int|false', 'createDefault' => 'IntlTimeZone', 'createEnumeration' => 'IntlIterator|false', 'createTimeZone' => '?IntlTimeZone', 'createTimeZoneIDEnumeration' => 'IntlIterator|false', 'fromDateTimeZone' => '?IntlTimeZone', 'getCanonicalID' => 'string|false', 'getDisplayName' => 'string|false', 'getDSTSavings' => 'int', 'getEquivalentID' => 'string|false', 'getErrorCode' => 'int|false', 'getErrorMessage' => 'string|false', 'getGMT' => 'IntlTimeZone', 'getID' => 'string|false', 'getOffset' => 'bool', 'getRawOffset' => 'int', 'getRegion' => 'string|false', 'getTZDataVersion' => 'string|false', 'getUnknown' => 'IntlTimeZone', 'getWindowsID' => 'string|false', 'getIDForWindowsID' => 'string|false', 'hasSameRules' => 'bool', 'toDateTimeZone' => 'DateTimeZone|false', 'useDaylightTime' => 'bool', ], 'Transliterator' => [ 'create' => '?Transliterator', 'createFromRules' => '?Transliterator', 'createInverse' => '?Transliterator', 'listIDs' => 'array|false', 'transliterate' => 'string|false', 'getErrorCode' => 'int|false', 'getErrorMessage' => 'string|false', ], 'IntlChar' => [ 'hasBinaryProperty' => '?bool', 'charAge' => '?array', 'charDigitValue' => '?int', 'charDirection' => '?int', 'charFromName' => '?int', 'charMirror' => 'int|string|null', 'charName' => '?string', 'charType' => '?int', 'chr' => '?string', 'digit' => 'int|false|null', 'enumCharNames' => '?bool', 'enumCharTypes' => 'void', 'foldCase' => 'int|string|null', 'forDigit' => 'int', 'getBidiPairedBracket' => 'int|string|null', 'getBlockCode' => '?int', 'getCombiningClass' => '?int', 'getFC_NFKC_Closure' => 'string|false|null', 'getIntPropertyMaxValue' => 'int', 'getIntPropertyMinValue' => 'int', 'getIntPropertyValue' => '?int', 'getNumericValue' => '?float', 'getPropertyEnum' => 'int', 'getPropertyName' => 'string|false', 'getPropertyValueEnum' => 'int', 'getPropertyValueName' => 'string|false', 'getUnicodeVersion' => 'array', 'isalnum' => '?bool', 'isalpha' => '?bool', 'isbase' => '?bool', 'isblank' => '?bool', 'iscntrl' => '?bool', 'isdefined' => '?bool', 'isdigit' => '?bool', 'isgraph' => '?bool', 'isIDIgnorable' => '?bool', 'isIDPart' => '?bool', 'isIDStart' => '?bool', 'isISOControl' => '?bool', 'isJavaIDPart' => '?bool', 'isJavaIDStart' => '?bool', 'isJavaSpaceChar' => '?bool', 'islower' => '?bool', 'isMirrored' => '?bool', 'isprint' => '?bool', 'ispunct' => '?bool', 'isspace' => '?bool', 'istitle' => '?bool', 'isUAlphabetic' => '?bool', 'isULowercase' => '?bool', 'isupper' => '?bool', 'isUUppercase' => '?bool', 'isUWhiteSpace' => '?bool', 'isWhitespace' => '?bool', 'isxdigit' => '?bool', 'ord' => '?int', 'tolower' => 'int|string|null', 'totitle' => 'int|string|null', 'toupper' => 'int|string|null', ], 'JsonSerializable' => [ 'jsonSerialize' => 'mixed', ], 'mysqli' => [ 'autocommit' => 'bool', 'begin_transaction' => 'bool', 'change_user' => 'bool', 'character_set_name' => 'string', 'commit' => 'bool', 'connect' => 'bool', 'dump_debug_info' => 'bool', 'get_charset' => '?object', 'get_client_info' => 'string', 'get_connection_stats' => 'array', 'get_server_info' => 'string', 'get_warnings' => 'mysqli_warning|false', 'kill' => 'bool', 'multi_query' => 'bool', 'more_results' => 'bool', 'next_result' => 'bool', 'ping' => 'bool', 'poll' => 'int|false', 'prepare' => 'mysqli_stmt|false', 'query' => 'mysqli_result|bool', 'real_connect' => 'bool', 'real_escape_string' => 'string', 'reap_async_query' => 'mysqli_result|bool', 'escape_string' => 'string', 'real_query' => 'bool', 'release_savepoint' => 'bool', 'rollback' => 'bool', 'savepoint' => 'bool', 'select_db' => 'bool', 'set_charset' => 'bool', 'options' => 'bool', 'set_opt' => 'bool', 'stat' => 'string|false', 'stmt_init' => 'mysqli_stmt|false', 'store_result' => 'mysqli_result|false', 'thread_safe' => 'bool', 'use_result' => 'mysqli_result|false', 'refresh' => 'bool', ], 'mysqli_result' => [ 'close' => 'void', 'free' => 'void', 'data_seek' => 'bool', 'fetch_field' => 'object|false', 'fetch_fields' => 'array', 'fetch_field_direct' => 'object|false', 'fetch_all' => 'array', 'fetch_array' => 'array|null|false', 'fetch_assoc' => 'array|null|false', 'fetch_object' => 'object|null|false', 'fetch_row' => 'array|null|false', 'field_seek' => 'bool', 'free_result' => 'void', ], 'mysqli_stmt' => [ 'attr_get' => 'int', 'attr_set' => 'bool', 'bind_param' => 'bool', 'bind_result' => 'bool', 'data_seek' => 'void', 'execute' => 'bool', 'fetch' => '?bool', 'get_warnings' => 'mysqli_warning|false', 'result_metadata' => 'mysqli_result|false', 'more_results' => 'bool', 'next_result' => 'bool', 'num_rows' => 'int|string', 'send_long_data' => 'bool', 'free_result' => 'void', 'reset' => 'bool', 'prepare' => 'bool', 'store_result' => 'bool', 'get_result' => 'mysqli_result|false', ], 'OCILob' => [ 'save' => 'bool', 'import' => 'bool', 'saveFile' => 'bool', 'load' => 'string|false', 'read' => 'string|false', 'eof' => 'bool', 'tell' => 'int|false', 'rewind' => 'bool', 'seek' => 'bool', 'size' => 'int|false', 'write' => 'int|false', 'append' => 'bool', 'truncate' => 'bool', 'erase' => 'int|false', 'flush' => 'bool', 'setBuffering' => 'bool', 'getBuffering' => 'bool', 'writeToFile' => 'bool', 'export' => 'bool', 'writeTemporary' => 'bool', 'close' => 'bool', 'free' => 'bool', ], 'OCICollection' => [ 'free' => 'bool', 'append' => 'bool', 'getElem' => 'string|float|null|false', 'assign' => 'bool', 'assignElem' => 'bool', 'size' => 'int|false', 'max' => 'int|false', 'trim' => 'bool', ], 'PDO' => [ 'beginTransaction' => 'bool', 'commit' => 'bool', 'errorCode' => '?string', 'errorInfo' => 'array', 'exec' => 'int|false', 'getAttribute' => 'mixed', 'getAvailableDrivers' => 'array', 'inTransaction' => 'bool', 'lastInsertId' => 'string|false', 'prepare' => 'PDOStatement|false', 'query' => 'PDOStatement|false', 'quote' => 'string|false', 'rollBack' => 'bool', 'setAttribute' => 'bool', ], 'PDOStatement' => [ 'bindColumn' => 'bool', 'bindParam' => 'bool', 'bindValue' => 'bool', 'closeCursor' => 'bool', 'columnCount' => 'int', 'debugDumpParams' => '?bool', 'errorCode' => '?string', 'errorInfo' => 'array', 'execute' => 'bool', 'fetch' => 'mixed', 'fetchAll' => 'array', 'fetchColumn' => 'mixed', 'fetchObject' => 'object|false', 'getAttribute' => 'mixed', 'getColumnMeta' => 'array|false', 'nextRowset' => 'bool', 'rowCount' => 'int', 'setAttribute' => 'bool', ], 'PDO_PGSql_Ext' => [ 'pgsqlCopyFromArray' => 'bool', 'pgsqlCopyFromFile' => 'bool', 'pgsqlCopyToArray' => 'array|false', 'pgsqlCopyToFile' => 'bool', 'pgsqlLOBCreate' => 'string|false', 'pgsqlLOBUnlink' => 'bool', 'pgsqlGetNotify' => 'array|false', 'pgsqlGetPid' => 'int', ], 'PDO_SQLite_Ext' => [ 'sqliteCreateFunction' => 'bool', 'sqliteCreateAggregate' => 'bool', 'sqliteCreateCollation' => 'bool', ], 'Phar' => [ 'addEmptyDir' => 'void', 'addFile' => 'void', 'addFromString' => 'void', 'buildFromDirectory' => 'array', 'buildFromIterator' => 'array', 'compressFiles' => 'void', 'compress' => '?Phar', 'decompress' => '?Phar', 'convertToExecutable' => '?Phar', 'convertToData' => '?PharData', 'count' => 'int', 'extractTo' => 'bool', 'getAlias' => '?string', 'getPath' => 'string', 'getMetadata' => 'mixed', 'getModified' => 'bool', 'getSignature' => 'array|false', 'getStub' => 'string', 'getVersion' => 'string', 'hasMetadata' => 'bool', 'isBuffering' => 'bool', 'isCompressed' => 'int|false', 'isFileFormat' => 'bool', 'isWritable' => 'bool', 'offsetExists' => 'bool', 'offsetGet' => 'SplFileInfo', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'setAlias' => 'bool', 'setDefaultStub' => 'bool', 'setMetadata' => 'void', 'setSignatureAlgorithm' => 'void', 'startBuffering' => 'void', 'stopBuffering' => 'void', ], 'PharData' => [ 'addEmptyDir' => 'void', 'addFile' => 'void', 'addFromString' => 'void', 'buildFromDirectory' => 'array', 'buildFromIterator' => 'array', 'compressFiles' => 'void', 'compress' => '?PharData', 'decompress' => '?PharData', 'convertToExecutable' => '?Phar', 'convertToData' => '?PharData', 'count' => 'int', 'extractTo' => 'bool', 'getAlias' => '?string', 'getPath' => 'string', 'getMetadata' => 'mixed', 'getModified' => 'bool', 'getSignature' => 'array|false', 'getStub' => 'string', 'getVersion' => 'string', 'hasMetadata' => 'bool', 'isBuffering' => 'bool', 'isCompressed' => 'int|false', 'isFileFormat' => 'bool', 'isWritable' => 'bool', 'offsetExists' => 'bool', 'offsetGet' => 'SplFileInfo', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'setAlias' => 'bool', 'setDefaultStub' => 'bool', 'setMetadata' => 'void', 'setSignatureAlgorithm' => 'void', 'startBuffering' => 'void', 'stopBuffering' => 'void', ], 'PharFileInfo' => [ 'chmod' => 'void', 'getCompressedSize' => 'int', 'getCRC32' => 'int', 'getContent' => 'string', 'getMetadata' => 'mixed', 'getPharFlags' => 'int', 'hasMetadata' => 'bool', 'isCompressed' => 'bool', 'isCRCChecked' => 'bool', 'setMetadata' => 'void', ], 'Reflection' => [ 'getModifierNames' => 'array', ], 'ReflectionFunctionAbstract' => [ 'inNamespace' => 'bool', 'isClosure' => 'bool', 'isDeprecated' => 'bool', 'isInternal' => 'bool', 'isUserDefined' => 'bool', 'isGenerator' => 'bool', 'isVariadic' => 'bool', 'isStatic' => 'bool', 'getClosureThis' => '?object', 'getClosureCalledClass' => '?ReflectionClass', 'getClosureScopeClass' => '?ReflectionClass', 'getDocComment' => 'string|false', 'getEndLine' => 'int|false', 'getExtension' => '?ReflectionExtension', 'getExtensionName' => 'string|false', 'getFileName' => 'string|false', 'getName' => 'string', 'getNamespaceName' => 'string', 'getNumberOfParameters' => 'int', 'getNumberOfRequiredParameters' => 'int', 'getParameters' => 'array', 'getShortName' => 'string', 'getStartLine' => 'int|false', 'getStaticVariables' => 'array', 'returnsReference' => 'bool', 'hasReturnType' => 'bool', 'getReturnType' => '?ReflectionType', ], 'ReflectionFunction' => [ 'isDisabled' => 'bool', 'invoke' => 'mixed', 'invokeArgs' => 'mixed', 'getClosure' => 'Closure', 'getExecutingLine' => 'int', 'getExecutingFile' => 'string', 'getTrace' => 'array', 'getFunction' => 'ReflectionFunctionAbstract', 'getThis' => '?object', 'getExecutingGenerator' => 'Generator', ], 'ReflectionMethod' => [ 'isPublic' => 'bool', 'isPrivate' => 'bool', 'isProtected' => 'bool', 'isAbstract' => 'bool', 'isFinal' => 'bool', 'isConstructor' => 'bool', 'isDestructor' => 'bool', 'getClosure' => 'Closure', 'getModifiers' => 'int', 'invoke' => 'mixed', 'invokeArgs' => 'mixed', 'getDeclaringClass' => 'ReflectionClass', 'getPrototype' => 'ReflectionMethod', 'setAccessible' => 'void', ], 'ReflectionClass' => [ 'getName' => 'string', 'isInternal' => 'bool', 'isUserDefined' => 'bool', 'isAnonymous' => 'bool', 'isInstantiable' => 'bool', 'isCloneable' => 'bool', 'getFileName' => 'string|false', 'getStartLine' => 'int|false', 'getEndLine' => 'int|false', 'getDocComment' => 'string|false', 'getConstructor' => '?ReflectionMethod', 'hasMethod' => 'bool', 'getMethod' => 'ReflectionMethod', 'getMethods' => 'array', 'hasProperty' => 'bool', 'getProperty' => 'ReflectionProperty', 'getProperties' => 'array', 'hasConstant' => 'bool', 'getConstants' => 'array', 'getReflectionConstants' => 'array', 'getConstant' => 'mixed', 'getReflectionConstant' => 'ReflectionClassConstant|false', 'getInterfaces' => 'array', 'getInterfaceNames' => 'array', 'isInterface' => 'bool', 'getTraits' => 'array', 'getTraitNames' => 'array', 'getTraitAliases' => 'array', 'isTrait' => 'bool', 'isAbstract' => 'bool', 'isFinal' => 'bool', 'getModifiers' => 'int', 'isInstance' => 'bool', 'newInstance' => 'object', 'newInstanceWithoutConstructor' => 'object', 'newInstanceArgs' => '?object', 'getParentClass' => 'ReflectionClass|false', 'isSubclassOf' => 'bool', 'getStaticProperties' => '?array', 'getStaticPropertyValue' => 'mixed', 'setStaticPropertyValue' => 'void', 'getDefaultProperties' => 'array', 'isIterable' => 'bool', 'isIterateable' => 'bool', 'implementsInterface' => 'bool', 'getExtension' => '?ReflectionExtension', 'getExtensionName' => 'string|false', 'inNamespace' => 'bool', 'getNamespaceName' => 'string', 'getShortName' => 'string', ], 'ReflectionProperty' => [ 'getName' => 'string', 'getValue' => 'mixed', 'setValue' => 'void', 'isInitialized' => 'bool', 'isPublic' => 'bool', 'isPrivate' => 'bool', 'isProtected' => 'bool', 'isStatic' => 'bool', 'isDefault' => 'bool', 'getModifiers' => 'int', 'getDeclaringClass' => 'ReflectionClass', 'getDocComment' => 'string|false', 'setAccessible' => 'void', 'getType' => '?ReflectionType', 'hasType' => 'bool', 'getDefaultValue' => 'mixed', ], 'ReflectionClassConstant' => [ 'getName' => 'string', 'getValue' => 'mixed', 'isPublic' => 'bool', 'isPrivate' => 'bool', 'isProtected' => 'bool', 'getModifiers' => 'int', 'getDeclaringClass' => 'ReflectionClass', 'getDocComment' => 'string|false', ], 'ReflectionParameter' => [ 'getName' => 'string', 'isPassedByReference' => 'bool', 'canBePassedByValue' => 'bool', 'getDeclaringFunction' => 'ReflectionFunctionAbstract', 'getDeclaringClass' => '?ReflectionClass', 'getClass' => '?ReflectionClass', 'hasType' => 'bool', 'getType' => '?ReflectionType', 'isArray' => 'bool', 'isCallable' => 'bool', 'allowsNull' => 'bool', 'getPosition' => 'int', 'isOptional' => 'bool', 'isDefaultValueAvailable' => 'bool', 'getDefaultValue' => 'mixed', 'isDefaultValueConstant' => 'bool', 'getDefaultValueConstantName' => '?string', 'isVariadic' => 'bool', ], 'ReflectionType' => [ 'allowsNull' => 'bool', ], 'ReflectionNamedType' => [ 'getName' => 'string', 'isBuiltin' => 'bool', ], 'ReflectionExtension' => [ 'getName' => 'string', 'getVersion' => '?string', 'getFunctions' => 'array', 'getConstants' => 'array', 'getINIEntries' => 'array', 'getClasses' => 'array', 'getClassNames' => 'array', 'getDependencies' => 'array', 'info' => 'void', 'isPersistent' => 'bool', 'isTemporary' => 'bool', ], 'ReflectionZendExtension' => [ 'getName' => 'string', 'getVersion' => 'string', 'getAuthor' => 'string', 'getURL' => 'string', 'getCopyright' => 'string', ], 'SessionHandlerInterface' => [ 'open' => 'bool', 'close' => 'bool', 'read' => 'string|false', 'write' => 'bool', 'destroy' => 'bool', 'gc' => 'int|false', ], 'SessionIdInterface' => [ 'create_sid' => 'string', ], 'SessionUpdateTimestampHandlerInterface' => [ 'validateId' => 'bool', 'updateTimestamp' => 'bool', ], 'SessionHandler' => [ 'open' => 'bool', 'close' => 'bool', 'read' => 'string|false', 'write' => 'bool', 'destroy' => 'bool', 'gc' => 'int|false', 'create_sid' => 'string', ], 'SimpleXMLElement' => [ 'xpath' => 'array|null|false', 'registerXPathNamespace' => 'bool', 'asXML' => 'string|bool', 'saveXML' => 'string|bool', 'getNamespaces' => 'array', 'getDocNamespaces' => 'array|false', 'children' => '?SimpleXMLElement', 'attributes' => '?SimpleXMLElement', 'addChild' => '?SimpleXMLElement', 'addAttribute' => 'void', 'getName' => 'string', 'count' => 'int', 'rewind' => 'void', 'valid' => 'bool', 'current' => 'SimpleXMLElement', 'key' => 'string', 'next' => 'void', 'hasChildren' => 'bool', 'getChildren' => '?SimpleXMLElement', ], 'SNMP' => [ 'close' => 'bool', 'setSecurity' => 'bool', 'get' => 'mixed', 'getnext' => 'mixed', 'walk' => 'array|false', 'set' => 'bool', 'getErrno' => 'int', 'getError' => 'string', ], 'SoapServer' => [ 'fault' => 'void', 'addSoapHeader' => 'void', 'setPersistence' => 'void', 'setClass' => 'void', 'setObject' => 'void', 'getFunctions' => 'array', 'addFunction' => 'void', 'handle' => 'void', ], 'SoapClient' => [ '__call' => 'mixed', '__soapCall' => 'mixed', '__getFunctions' => '?array', '__getTypes' => '?array', '__getLastRequest' => '?string', '__getLastResponse' => '?string', '__getLastRequestHeaders' => '?string', '__getLastResponseHeaders' => '?string', '__doRequest' => '?string', '__setCookie' => 'void', '__getCookies' => 'array', '__setSoapHeaders' => 'bool', '__setLocation' => '?string', ], 'ArrayObject' => [ 'offsetExists' => 'bool', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'append' => 'void', 'getArrayCopy' => 'array', 'count' => 'int', 'getFlags' => 'int', 'setFlags' => 'void', 'asort' => 'bool', 'ksort' => 'bool', 'uasort' => 'bool', 'uksort' => 'bool', 'natsort' => 'bool', 'natcasesort' => 'bool', 'unserialize' => 'void', 'serialize' => 'string', '__serialize' => 'array', '__unserialize' => 'void', 'getIterator' => 'Iterator', 'exchangeArray' => 'array', 'setIteratorClass' => 'void', 'getIteratorClass' => 'string', '__debugInfo' => 'array', ], 'ArrayIterator' => [ 'offsetExists' => 'bool', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'append' => 'void', 'getArrayCopy' => 'array', 'count' => 'int', 'getFlags' => 'int', 'setFlags' => 'void', 'asort' => 'bool', 'ksort' => 'bool', 'uasort' => 'bool', 'uksort' => 'bool', 'natsort' => 'bool', 'natcasesort' => 'bool', 'unserialize' => 'void', 'serialize' => 'string', '__serialize' => 'array', '__unserialize' => 'void', 'rewind' => 'void', 'current' => 'mixed', 'key' => 'string|int|null', 'next' => 'void', 'valid' => 'bool', 'seek' => 'void', '__debugInfo' => 'array', ], 'RecursiveArrayIterator' => [ 'hasChildren' => 'bool', 'getChildren' => '?RecursiveArrayIterator', ], 'SplFileInfo' => [ 'getPath' => 'string', 'getFilename' => 'string', 'getExtension' => 'string', 'getBasename' => 'string', 'getPathname' => 'string', 'getPerms' => 'int|false', 'getInode' => 'int|false', 'getSize' => 'int|false', 'getOwner' => 'int|false', 'getGroup' => 'int|false', 'getATime' => 'int|false', 'getMTime' => 'int|false', 'getCTime' => 'int|false', 'getType' => 'string|false', 'isWritable' => 'bool', 'isReadable' => 'bool', 'isExecutable' => 'bool', 'isFile' => 'bool', 'isDir' => 'bool', 'isLink' => 'bool', 'getLinkTarget' => 'string|false', 'getRealPath' => 'string|false', 'getFileInfo' => 'SplFileInfo', 'getPathInfo' => '?SplFileInfo', 'openFile' => 'SplFileObject', 'setFileClass' => 'void', 'setInfoClass' => 'void', '__debugInfo' => 'array', '_bad_state_ex' => 'void', ], 'DirectoryIterator' => [ 'getFilename' => 'string', 'getExtension' => 'string', 'getBasename' => 'string', 'isDot' => 'bool', 'rewind' => 'void', 'valid' => 'bool', 'key' => 'mixed', 'current' => 'mixed', 'next' => 'void', 'seek' => 'void', ], 'FilesystemIterator' => [ 'rewind' => 'void', 'key' => 'string', 'current' => 'string|SplFileInfo|FilesystemIterator', 'getFlags' => 'int', 'setFlags' => 'void', ], 'RecursiveDirectoryIterator' => [ 'hasChildren' => 'bool', 'getChildren' => 'RecursiveDirectoryIterator', 'getSubPath' => 'string', 'getSubPathname' => 'string', ], 'GlobIterator' => [ 'count' => 'int', ], 'SplFileObject' => [ 'rewind' => 'void', 'eof' => 'bool', 'valid' => 'bool', 'fgets' => 'string', 'fread' => 'string|false', 'fgetcsv' => 'array|false', 'fputcsv' => 'int|false', 'setCsvControl' => 'void', 'getCsvControl' => 'array', 'flock' => 'bool', 'fflush' => 'bool', 'ftell' => 'int|false', 'fseek' => 'int', 'fgetc' => 'string|false', 'fpassthru' => 'int', 'fscanf' => 'array|int|null', 'fwrite' => 'int|false', 'fstat' => 'array', 'ftruncate' => 'bool', 'current' => 'string|array|false', 'key' => 'int', 'next' => 'void', 'setFlags' => 'void', 'getFlags' => 'int', 'setMaxLineLen' => 'void', 'getMaxLineLen' => 'int', 'hasChildren' => 'false', 'getChildren' => 'null', 'seek' => 'void', 'getCurrentLine' => 'string', ], 'SplDoublyLinkedList' => [ 'add' => 'void', 'pop' => 'mixed', 'shift' => 'mixed', 'push' => 'void', 'unshift' => 'void', 'top' => 'mixed', 'bottom' => 'mixed', '__debugInfo' => 'array', 'count' => 'int', 'isEmpty' => 'bool', 'setIteratorMode' => 'int', 'getIteratorMode' => 'int', 'offsetExists' => 'bool', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'rewind' => 'void', 'current' => 'mixed', 'key' => 'int', 'prev' => 'void', 'next' => 'void', 'valid' => 'bool', 'unserialize' => 'void', 'serialize' => 'string', '__serialize' => 'array', '__unserialize' => 'void', ], 'SplQueue' => [ 'enqueue' => 'void', 'dequeue' => 'mixed', ], 'SplFixedArray' => [ '__wakeup' => 'void', 'count' => 'int', 'toArray' => 'array', 'fromArray' => 'SplFixedArray', 'getSize' => 'int', 'offsetExists' => 'bool', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', ], 'SplPriorityQueue' => [ 'compare' => 'int', 'setExtractFlags' => 'int', 'top' => 'mixed', 'extract' => 'mixed', 'count' => 'int', 'isEmpty' => 'bool', 'rewind' => 'void', 'current' => 'mixed', 'key' => 'int', 'next' => 'void', 'valid' => 'bool', 'isCorrupted' => 'bool', 'getExtractFlags' => 'int', '__debugInfo' => 'array', ], 'SplHeap' => [ 'extract' => 'mixed', 'insert' => 'bool', 'top' => 'mixed', 'count' => 'int', 'isEmpty' => 'bool', 'rewind' => 'void', 'current' => 'mixed', 'key' => 'int', 'next' => 'void', 'valid' => 'bool', 'recoverFromCorruption' => 'bool', 'compare' => 'int', 'isCorrupted' => 'bool', '__debugInfo' => 'array', ], 'SplMinHeap' => [ 'compare' => 'int', ], 'SplMaxHeap' => [ 'compare' => 'int', ], 'EmptyIterator' => [ 'current' => 'never', 'next' => 'void', 'key' => 'never', 'valid' => 'false', 'rewind' => 'void', ], 'CallbackFilterIterator' => [ 'accept' => 'bool', ], 'RecursiveCallbackFilterIterator' => [ 'hasChildren' => 'bool', 'getChildren' => 'RecursiveCallbackFilterIterator', ], 'RecursiveIterator' => [ 'hasChildren' => 'bool', 'getChildren' => '?RecursiveIterator', ], 'RecursiveIteratorIterator' => [ 'rewind' => 'void', 'valid' => 'bool', 'key' => 'mixed', 'current' => 'mixed', 'next' => 'void', 'getDepth' => 'int', 'getSubIterator' => '?RecursiveIterator', 'getInnerIterator' => 'RecursiveIterator', 'beginIteration' => 'void', 'endIteration' => 'void', 'callHasChildren' => 'bool', 'callGetChildren' => '?RecursiveIterator', 'beginChildren' => 'void', 'endChildren' => 'void', 'nextElement' => 'void', 'setMaxDepth' => 'void', 'getMaxDepth' => 'int|false', ], 'OuterIterator' => [ 'getInnerIterator' => '?Iterator', ], 'IteratorIterator' => [ 'getInnerIterator' => '?Iterator', 'rewind' => 'void', 'valid' => 'bool', 'key' => 'mixed', 'current' => 'mixed', 'next' => 'void', ], 'FilterIterator' => [ 'accept' => 'bool', 'rewind' => 'void', 'next' => 'void', ], 'RecursiveFilterIterator' => [ 'hasChildren' => 'bool', 'getChildren' => '?RecursiveFilterIterator', ], 'ParentIterator' => [ 'accept' => 'bool', ], 'SeekableIterator' => [ 'seek' => 'void', ], 'LimitIterator' => [ 'rewind' => 'void', 'valid' => 'bool', 'next' => 'void', 'seek' => 'int', 'getPosition' => 'int', ], 'CachingIterator' => [ 'rewind' => 'void', 'valid' => 'bool', 'next' => 'void', 'hasNext' => 'bool', 'getFlags' => 'int', 'setFlags' => 'void', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'offsetExists' => 'bool', 'getCache' => 'array', 'count' => 'int', ], 'RecursiveCachingIterator' => [ 'hasChildren' => 'bool', 'getChildren' => '?RecursiveCachingIterator', ], 'NoRewindIterator' => [ 'rewind' => 'void', 'valid' => 'bool', 'key' => 'mixed', 'current' => 'mixed', 'next' => 'void', ], 'AppendIterator' => [ 'append' => 'void', 'rewind' => 'void', 'valid' => 'bool', 'current' => 'mixed', 'next' => 'void', 'getIteratorIndex' => '?int', 'getArrayIterator' => 'ArrayIterator', ], 'InfiniteIterator' => [ 'next' => 'void', ], 'RegexIterator' => [ 'accept' => 'bool', 'getMode' => 'int', 'setMode' => 'void', 'getFlags' => 'int', 'setFlags' => 'void', 'getRegex' => 'string', 'getPregFlags' => 'int', 'setPregFlags' => 'void', ], 'RecursiveRegexIterator' => [ 'accept' => 'bool', 'hasChildren' => 'bool', 'getChildren' => 'RecursiveRegexIterator', ], 'RecursiveTreeIterator' => [ 'key' => 'mixed', 'current' => 'mixed', 'getPrefix' => 'string', 'setPostfix' => 'void', 'setPrefixPart' => 'void', 'getEntry' => 'string', 'getPostfix' => 'string', ], 'SplObserver' => [ 'update' => 'void', ], 'SplSubject' => [ 'attach' => 'void', 'detach' => 'void', 'notify' => 'void', ], 'SplObjectStorage' => [ 'attach' => 'void', 'detach' => 'void', 'contains' => 'bool', 'addAll' => 'int', 'removeAll' => 'int', 'removeAllExcept' => 'int', 'getInfo' => 'mixed', 'setInfo' => 'void', 'count' => 'int', 'rewind' => 'void', 'valid' => 'bool', 'key' => 'int', 'current' => 'object', 'next' => 'void', 'unserialize' => 'void', 'serialize' => 'string', 'offsetExists' => 'bool', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', 'getHash' => 'string', '__serialize' => 'array', '__unserialize' => 'void', '__debugInfo' => 'array', ], 'MultipleIterator' => [ 'getFlags' => 'int', 'setFlags' => 'void', 'attachIterator' => 'void', 'detachIterator' => 'void', 'containsIterator' => 'bool', 'countIterators' => 'int', 'rewind' => 'void', 'valid' => 'bool', 'key' => 'array', 'current' => 'array', 'next' => 'void', '__debugInfo' => 'array', ], 'SQLite3' => [ 'open' => 'void', 'version' => 'array', 'lastInsertRowID' => 'int', 'lastErrorCode' => 'int', 'lastExtendedErrorCode' => 'int', 'lastErrorMsg' => 'string', 'changes' => 'int', 'busyTimeout' => 'bool', 'loadExtension' => 'bool', 'backup' => 'bool', 'escapeString' => 'string', 'prepare' => 'SQLite3Stmt|false', 'exec' => 'bool', 'query' => 'SQLite3Result|false', 'querySingle' => 'mixed', 'createFunction' => 'bool', 'createAggregate' => 'bool', 'createCollation' => 'bool', 'enableExceptions' => 'bool', 'enableExtendedResultCodes' => 'bool', 'setAuthorizer' => 'bool', ], 'SQLite3Stmt' => [ 'bindParam' => 'bool', 'bindValue' => 'bool', 'clear' => 'bool', 'close' => 'bool', 'execute' => 'SQLite3Result|false', 'getSQL' => 'string|false', 'paramCount' => 'int', 'readOnly' => 'bool', 'reset' => 'bool', ], 'SQLite3Result' => [ 'numColumns' => 'int', 'columnName' => 'string|false', 'columnType' => 'int|false', 'fetchArray' => 'array|false', 'reset' => 'bool', ], 'Directory' => [ 'close' => 'void', 'rewind' => 'void', 'read' => 'string|false', ], 'php_user_filter' => [ 'filter' => 'int', 'onCreate' => 'bool', 'onClose' => 'void', ], 'tidy' => [ 'getOpt' => 'string|int|bool', 'cleanRepair' => 'bool', 'parseFile' => 'bool', 'parseString' => 'bool', 'repairString' => 'string|false', 'repairFile' => 'string|false', 'diagnose' => 'bool', 'getRelease' => 'string', 'getConfig' => 'array', 'getStatus' => 'int', 'getHtmlVer' => 'int', 'getOptDoc' => 'string|false', 'isXhtml' => 'bool', 'isXml' => 'bool', 'root' => '?tidyNode', 'head' => '?tidyNode', 'html' => '?tidyNode', 'body' => '?tidyNode', ], 'XMLReader' => [ 'getAttribute' => '?string', 'getAttributeNo' => '?string', 'getAttributeNs' => '?string', 'getParserProperty' => 'bool', 'isValid' => 'bool', 'lookupNamespace' => '?string', 'moveToAttribute' => 'bool', 'moveToAttributeNo' => 'bool', 'moveToAttributeNs' => 'bool', 'moveToElement' => 'bool', 'moveToFirstAttribute' => 'bool', 'moveToNextAttribute' => 'bool', 'read' => 'bool', 'next' => 'bool', 'readInnerXml' => 'string', 'readOuterXml' => 'string', 'readString' => 'string', 'setSchema' => 'bool', 'setParserProperty' => 'bool', 'setRelaxNGSchema' => 'bool', 'setRelaxNGSchemaSource' => 'bool', 'expand' => 'DOMNode|false', ], 'XMLWriter' => [ 'openUri' => 'bool', 'openMemory' => 'bool', 'setIndent' => 'bool', 'setIndentString' => 'bool', 'startComment' => 'bool', 'endComment' => 'bool', 'startAttribute' => 'bool', 'endAttribute' => 'bool', 'writeAttribute' => 'bool', 'startAttributeNs' => 'bool', 'writeAttributeNs' => 'bool', 'startElement' => 'bool', 'endElement' => 'bool', 'fullEndElement' => 'bool', 'startElementNs' => 'bool', 'writeElement' => 'bool', 'writeElementNs' => 'bool', 'startPi' => 'bool', 'endPi' => 'bool', 'writePi' => 'bool', 'startCdata' => 'bool', 'endCdata' => 'bool', 'writeCdata' => 'bool', 'text' => 'bool', 'writeRaw' => 'bool', 'startDocument' => 'bool', 'endDocument' => 'bool', 'writeComment' => 'bool', 'startDtd' => 'bool', 'endDtd' => 'bool', 'writeDtd' => 'bool', 'startDtdElement' => 'bool', 'endDtdElement' => 'bool', 'writeDtdElement' => 'bool', 'startDtdAttlist' => 'bool', 'endDtdAttlist' => 'bool', 'writeDtdAttlist' => 'bool', 'startDtdEntity' => 'bool', 'endDtdEntity' => 'bool', 'writeDtdEntity' => 'bool', 'outputMemory' => 'string', 'flush' => 'string|int', ], 'XSLTProcessor' => [ 'importStylesheet' => 'bool', 'transformToDoc' => 'DOMDocument|false', 'transformToUri' => 'int', 'transformToXml' => 'string|null|false', 'setParameter' => 'bool', 'getParameter' => 'string|false', 'removeParameter' => 'bool', 'hasExsltSupport' => 'bool', 'registerPHPFunctions' => 'void', 'setSecurityPrefs' => 'int', 'getSecurityPrefs' => 'int', ], 'ZipArchive' => [ 'open' => 'bool|int', 'setPassword' => 'bool', 'close' => 'bool', 'count' => 'int', 'getStatusString' => 'string', 'addEmptyDir' => 'bool', 'addFromString' => 'bool', 'addFile' => 'bool', 'replaceFile' => 'bool', 'addGlob' => 'array|false', 'addPattern' => 'array|false', 'renameIndex' => 'bool', 'renameName' => 'bool', 'setArchiveComment' => 'bool', 'getArchiveComment' => 'string|false', 'setCommentIndex' => 'bool', 'setCommentName' => 'bool', 'setMtimeIndex' => 'bool', 'setMtimeName' => 'bool', 'getCommentIndex' => 'string|false', 'getCommentName' => 'string|false', 'deleteIndex' => 'bool', 'deleteName' => 'bool', 'statName' => 'array|false', 'statIndex' => 'array|false', 'locateName' => 'int|false', 'getNameIndex' => 'string|false', 'unchangeArchive' => 'bool', 'unchangeAll' => 'bool', 'unchangeIndex' => 'bool', 'unchangeName' => 'bool', 'extractTo' => 'bool', 'getFromName' => 'string|false', 'getFromIndex' => 'string|false', 'setExternalAttributesName' => 'bool', 'setExternalAttributesIndex' => 'bool', 'getExternalAttributesName' => 'bool', 'getExternalAttributesIndex' => 'bool', 'setCompressionName' => 'bool', 'setCompressionIndex' => 'bool', 'setEncryptionName' => 'bool', 'setEncryptionIndex' => 'bool', 'registerProgressCallback' => 'bool', 'registerCancelCallback' => 'bool', ], 'Exception' => [ '__wakeup' => 'void', ], 'Error' => [ '__wakeup' => 'void', ], 'IteratorAggregate' => [ 'getIterator' => 'Traversable', ], 'Iterator' => [ 'current' => 'mixed', 'next' => 'void', 'key' => 'mixed', 'valid' => 'bool', 'rewind' => 'void', ], 'ArrayAccess' => [ 'offsetExists' => 'bool', 'offsetGet' => 'mixed', 'offsetSet' => 'void', 'offsetUnset' => 'void', ], 'Countable' => [ 'count' => 'int', ], ]; } error-handler/ErrorRenderer/ErrorRendererInterface.php 0000644 00000002076 15060133305 0017203 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorRenderer; use Symfony\Component\ErrorHandler\Exception\FlattenException; /** * Formats an exception to be used as response content. * * @author Yonel Ceruto <yonelceruto@gmail.com> */ interface ErrorRendererInterface { public const IDE_LINK_FORMATS = [ 'textmate' => 'txmt://open?url=file://%f&line=%l', 'macvim' => 'mvim://open?url=file://%f&line=%l', 'emacs' => 'emacs://open?url=file://%f&line=%l', 'sublime' => 'subl://open?url=file://%f&line=%l', 'phpstorm' => 'phpstorm://open?file=%f&line=%l', 'atom' => 'atom://core/open/file?filename=%f&line=%l', 'vscode' => 'vscode://file/%f:%l', ]; /** * Renders a Throwable as a FlattenException. */ public function render(\Throwable $exception): FlattenException; } error-handler/ErrorRenderer/HtmlErrorRenderer.php 0000644 00000056614 15060133305 0016216 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorRenderer; use Psr\Log\LoggerInterface; use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\HtmlDumper; /** * @author Yonel Ceruto <yonelceruto@gmail.com> */ class HtmlErrorRenderer implements ErrorRendererInterface { private const GHOST_ADDONS = [ '02-14' => self::GHOST_HEART, '02-29' => self::GHOST_PLUS, '10-18' => self::GHOST_GIFT, ]; private const GHOST_GIFT = 'M124.00534057617188,5.3606138080358505 C124.40059661865234,4.644828304648399 125.1237564086914,3.712414965033531 123.88127899169922,3.487462028861046 C123.53517150878906,3.3097832053899765 123.18894958496094,2.9953975528478622 122.8432846069336,3.345616325736046 C122.07421112060547,3.649444565176964 121.40750122070312,4.074306473135948 122.2164306640625,4.869479164481163 C122.57514953613281,5.3830065578222275 122.90142822265625,6.503447040915489 123.3077621459961,6.626829609274864 C123.55027770996094,6.210384353995323 123.7774658203125,5.785196766257286 124.00534057617188,5.3606138080358505 zM122.30630493164062,7.336987480521202 C121.60028076171875,6.076864704489708 121.03211975097656,4.72498320043087 120.16796875,3.562500938773155 C119.11695098876953,2.44033907353878 117.04605865478516,2.940566048026085 116.57544708251953,4.387995228171349 C115.95028686523438,5.819030746817589 117.2991714477539,7.527640804648399 118.826171875,7.348545059561729 C119.98493194580078,7.367936596274376 121.15027618408203,7.420116886496544 122.30630493164062,7.336987480521202 zM128.1732177734375,7.379541382193565 C129.67486572265625,7.17823551595211 130.53842163085938,5.287807449698448 129.68344116210938,4.032590612769127 C128.92578125,2.693056806921959 126.74605560302734,2.6463639587163925 125.98509216308594,4.007616028189659 C125.32617950439453,5.108129009604454 124.75428009033203,6.258124336600304 124.14962768554688,7.388818249106407 C125.48638916015625,7.465229496359825 126.8357162475586,7.447416767477989 128.1732177734375,7.379541382193565 zM130.6601104736328,8.991325363516808 C131.17202758789062,8.540884003043175 133.1543731689453,8.009847149252892 131.65304565429688,7.582054600119591 C131.2811279296875,7.476506695151329 130.84751892089844,6.99234913289547 130.5132598876953,7.124847874045372 C129.78744506835938,8.02728746831417 128.67140197753906,8.55669592320919 127.50616455078125,8.501235947012901 C127.27806091308594,8.576229080557823 126.11459350585938,8.38720129430294 126.428955078125,8.601900085806847 C127.25099182128906,9.070617660880089 128.0523223876953,9.579657539725304 128.902587890625,9.995706543326378 C129.49813842773438,9.678531631827354 130.0761260986328,9.329126343131065 130.6601104736328,8.991325363516808 zM118.96446990966797,9.246344551444054 C119.4022445678711,8.991325363516808 119.84001922607422,8.736305221915245 120.27779388427734,8.481284126639366 C118.93965911865234,8.414779648184776 117.40827941894531,8.607666000723839 116.39698791503906,7.531384453177452 C116.11186981201172,7.212117180228233 115.83845520019531,6.846597656607628 115.44329071044922,7.248530372977257 C114.96995544433594,7.574637398123741 113.5140609741211,7.908811077475548 114.63501739501953,8.306883797049522 C115.61112976074219,8.883499130606651 116.58037567138672,9.474181160330772 117.58061218261719,10.008124336600304 C118.05723571777344,9.784612640738487 118.50651550292969,9.5052699893713 118.96446990966797,9.246344551444054 zM125.38018035888672,12.091858848929405 C125.9474868774414,11.636047348380089 127.32159423828125,11.201767906546593 127.36749267578125,10.712632164359093 C126.08487701416016,9.974547371268272 124.83960723876953,9.152772888541222 123.49772644042969,8.528907760977745 C123.03594207763672,8.353693947196007 122.66152954101562,8.623294815421104 122.28982543945312,8.857431396842003 C121.19065856933594,9.51122473180294 120.06505584716797,10.12446115911007 119.00167083740234,10.835315689444542 C120.39238739013672,11.69529627263546 121.79983520507812,12.529837593436241 123.22095489501953,13.338589653372765 C123.94580841064453,12.932025894522667 124.66128540039062,12.508862480521202 125.38018035888672,12.091858848929405 zM131.07164001464844,13.514615997672081 C131.66018676757812,13.143282875418663 132.2487335205078,12.771927818655968 132.8372802734375,12.400571808218956 C132.8324737548828,11.156818374991417 132.8523406982422,9.912529930472374 132.81829833984375,8.669195160269737 C131.63046264648438,9.332009300589561 130.45948791503906,10.027913078665733 129.30828857421875,10.752535805106163 C129.182373046875,12.035354599356651 129.24623107910156,13.33940313756466 129.27359008789062,14.628684982657433 C129.88104248046875,14.27079389989376 130.4737548828125,13.888019546866417 131.07164001464844,13.514640793204308 zM117.26847839355469,12.731024727225304 C117.32825469970703,11.67083452641964 117.45709991455078,10.46224020421505 116.17853546142578,10.148179039359093 C115.37110900878906,9.77159021794796 114.25194549560547,8.806716904044151 113.62991333007812,8.81639002263546 C113.61052703857422,10.0110072940588 113.62078857421875,11.20585821568966 113.61869049072266,12.400571808218956 C114.81139373779297,13.144886955618858 115.98292541503906,13.925040230154991 117.20137023925781,14.626662239432335 C117.31951141357422,14.010867103934288 117.24227905273438,13.35805033147335 117.26847839355469,12.731024727225304 zM125.80937957763672,16.836034759879112 C126.51483917236328,16.390663132071495 127.22030639648438,15.945291504263878 127.92576599121094,15.49991987645626 C127.92250061035156,14.215868934988976 127.97560119628906,12.929980263113976 127.91757202148438,11.647302612662315 C127.14225769042969,11.869626984000206 126.25550079345703,12.556857094168663 125.43866729736328,12.983742699027061 C124.82704162597656,13.342005714774132 124.21542358398438,13.700271591544151 123.60379028320312,14.05853746831417 C123.61585235595703,15.429577812552452 123.57081604003906,16.803131088614464 123.64839172363281,18.172149643301964 C124.37957000732422,17.744937881827354 125.09130859375,17.284801468253136 125.80937957763672,16.836034759879112 zM122.8521499633789,16.115344032645226 C122.8521499633789,15.429741844534874 122.8521499633789,14.744139656424522 122.8521499633789,14.05853746831417 C121.43595123291016,13.230924591422081 120.02428436279297,12.395455345511436 118.60256958007812,11.577354416251183 C118.52394104003906,12.888403877615929 118.56887817382812,14.204405769705772 118.55702209472656,15.517732605338097 C119.97289276123047,16.4041957706213 121.37410736083984,17.314891800284386 122.80789947509766,18.172149643301964 C122.86368560791016,17.488990768790245 122.84332275390625,16.800363525748253 122.8521499633789,16.115344032645226 zM131.10684204101562,18.871450409293175 C131.68399047851562,18.48711584508419 132.2611541748047,18.10278509557247 132.8383026123047,17.718475326895714 C132.81423950195312,16.499977096915245 132.89776611328125,15.264989838004112 132.77627563476562,14.05993078649044 C131.5760040283203,14.744719490408897 130.41763305664062,15.524359688162804 129.23875427246094,16.255397781729698 C129.26707458496094,17.516149505972862 129.18060302734375,18.791316971182823 129.3108367919922,20.041303619742393 C129.91973876953125,19.667551025748253 130.51010131835938,19.264152511954308 131.10684204101562,18.871450409293175 zM117.2557373046875,18.188333496451378 C117.25104522705078,17.549470886588097 117.24633026123047,16.91058538854122 117.24163055419922,16.271720871329308 C116.04924774169922,15.525708183646202 114.87187957763672,14.75476549565792 113.66158294677734,14.038097366690636 C113.5858383178711,15.262084946036339 113.62901306152344,16.49083898961544 113.61761474609375,17.717010483145714 C114.82051086425781,18.513254150748253 116.00987243652344,19.330610260367393 117.22888946533203,20.101993545889854 C117.27559661865234,19.466014847159386 117.25241088867188,18.825733169913292 117.2557373046875,18.188333496451378 zM125.8398666381836,22.38675306737423 C126.54049682617188,21.921453461050987 127.24110412597656,21.456151947379112 127.94172668457031,20.99083136022091 C127.94009399414062,19.693386062979698 127.96646118164062,18.395381912589073 127.93160247802734,17.098379120230675 C126.50540924072266,17.97775076329708 125.08877563476562,18.873308166861534 123.68258666992188,19.78428266942501 C123.52366638183594,21.03710363805294 123.626708984375,22.32878302037716 123.62647247314453,23.595300659537315 C124.06291198730469,23.86113165318966 125.1788101196289,22.68297766149044 125.8398666381836,22.38675306737423 zM122.8521499633789,21.83134649693966 C122.76741790771484,20.936696991324425 123.21651458740234,19.67745779454708 122.0794677734375,19.330633148550987 C120.93280029296875,18.604360565543175 119.7907485961914,17.870157226920128 118.62899780273438,17.16818617284298 C118.45966339111328,18.396427139639854 118.63676452636719,19.675991043448448 118.50668334960938,20.919256195425987 C119.89984130859375,21.92635916173458 121.32942199707031,22.88914106786251 122.78502655029297,23.803510650992393 C122.90177917480469,23.1627406924963 122.82917022705078,22.48402212560177 122.8521499633789,21.83134649693966 zM117.9798355102539,21.59483526647091 C116.28416442871094,20.46288488805294 114.58848571777344,19.330957397818565 112.892822265625,18.199007019400597 C112.89473724365234,14.705654129385948 112.84647369384766,11.211485847830772 112.90847778320312,7.718807205557823 C113.7575912475586,7.194885239005089 114.66117858886719,6.765397056937218 115.5350341796875,6.284702762961388 C114.97061157226562,4.668964847922325 115.78496551513672,2.7054970115423203 117.42159271240234,2.1007001250982285 C118.79354095458984,1.537783369421959 120.44731903076172,2.0457767099142075 121.32200622558594,3.23083733022213 C121.95732116699219,2.9050118774175644 122.59264373779297,2.5791852325201035 123.22796630859375,2.253336176276207 C123.86669921875,2.5821153968572617 124.50543975830078,2.9108948558568954 125.1441650390625,3.23967407643795 C126.05941009521484,2.154020771384239 127.62747192382812,1.5344576686620712 128.986328125,2.1429056972265244 C130.61741638183594,2.716217741370201 131.50650024414062,4.675290569663048 130.9215545654297,6.2884936183691025 C131.8018341064453,6.78548763692379 132.7589111328125,7.1738648265600204 133.5660400390625,7.780336365103722 C133.60182189941406,11.252970680594444 133.56637573242188,14.726140961050987 133.5631103515625,18.199007019400597 C130.18914794921875,20.431867584586143 126.86984252929688,22.74994657933712 123.44108581542969,24.897907242178917 C122.44406127929688,24.897628769278526 121.5834732055664,23.815067276358604 120.65831756591797,23.37616156041622 C119.76387023925781,22.784828171133995 118.87168884277344,22.19007681310177 117.9798355102539,21.59483526647091 z'; private const GHOST_HEART = 'M125.91386369681868,8.305165958366445 C128.95033202169043,-0.40540639102854037 140.8469835342744,8.305165958366445 125.91386369681868,19.504526138305664 C110.98208663272044,8.305165958366445 122.87795231771452,-0.40540639102854037 125.91386369681868,8.305165958366445 z'; private const GHOST_PLUS = 'M111.36824226379395,8.969108581542969 L118.69175148010254,8.969108581542969 L118.69175148010254,1.6455793380737305 L126.20429420471191,1.6455793380737305 L126.20429420471191,8.969108581542969 L133.52781105041504,8.969108581542969 L133.52781105041504,16.481630325317383 L126.20429420471191,16.481630325317383 L126.20429420471191,23.805158615112305 L118.69175148010254,23.805158615112305 L118.69175148010254,16.481630325317383 L111.36824226379395,16.481630325317383 z'; private bool|\Closure $debug; private string $charset; private FileLinkFormatter $fileLinkFormat; private string|\Closure $outputBuffer; private static string $template = 'views/error.html.php'; /** * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it * @param string|callable $outputBuffer The output buffer as a string or a callable that should return it */ public function __construct( bool|callable $debug = false, ?string $charset = null, string|FileLinkFormatter|null $fileLinkFormat = null, private ?string $projectDir = null, string|callable $outputBuffer = '', private ?LoggerInterface $logger = null, ) { $this->debug = \is_bool($debug) ? $debug : $debug(...); $this->charset = $charset ?: (\ini_get('default_charset') ?: 'UTF-8'); $this->fileLinkFormat = $fileLinkFormat instanceof FileLinkFormatter ? $fileLinkFormat : new FileLinkFormatter($fileLinkFormat); $this->outputBuffer = \is_string($outputBuffer) ? $outputBuffer : $outputBuffer(...); } public function render(\Throwable $exception): FlattenException { $headers = ['Content-Type' => 'text/html; charset='.$this->charset]; if (\is_bool($this->debug) ? $this->debug : ($this->debug)($exception)) { $headers['X-Debug-Exception'] = rawurlencode($exception->getMessage()); $headers['X-Debug-Exception-File'] = rawurlencode($exception->getFile()).':'.$exception->getLine(); } $exception = FlattenException::createWithDataRepresentation($exception, null, $headers); return $exception->setAsString($this->renderException($exception)); } /** * Gets the HTML content associated with the given exception. */ public function getBody(FlattenException $exception): string { return $this->renderException($exception, 'views/exception.html.php'); } /** * Gets the stylesheet associated with the given exception. */ public function getStylesheet(): string { if (!$this->debug) { return $this->include('assets/css/error.css'); } return $this->include('assets/css/exception.css'); } public static function isDebug(RequestStack $requestStack, bool $debug): \Closure { return static function () use ($requestStack, $debug): bool { if (!$request = $requestStack->getCurrentRequest()) { return $debug; } return $debug && $request->attributes->getBoolean('showException', true); }; } public static function getAndCleanOutputBuffer(RequestStack $requestStack): \Closure { return static function () use ($requestStack): string { if (!$request = $requestStack->getCurrentRequest()) { return ''; } $startObLevel = $request->headers->get('X-Php-Ob-Level', -1); if (ob_get_level() <= $startObLevel) { return ''; } Response::closeOutputBuffers($startObLevel + 1, true); return ob_get_clean(); }; } private function renderException(FlattenException $exception, string $debugTemplate = 'views/exception_full.html.php'): string { $debug = \is_bool($this->debug) ? $this->debug : ($this->debug)($exception); $statusText = $this->escape($exception->getStatusText()); $statusCode = $this->escape($exception->getStatusCode()); if (!$debug) { return $this->include(self::$template, [ 'statusText' => $statusText, 'statusCode' => $statusCode, ]); } $exceptionMessage = $this->escape($exception->getMessage()); return $this->include($debugTemplate, [ 'exception' => $exception, 'exceptionMessage' => $exceptionMessage, 'statusText' => $statusText, 'statusCode' => $statusCode, 'logger' => null !== $this->logger && class_exists(DebugLoggerConfigurator::class) ? DebugLoggerConfigurator::getDebugLogger($this->logger) : null, 'currentContent' => \is_string($this->outputBuffer) ? $this->outputBuffer : ($this->outputBuffer)(), ]); } private function dumpValue(Data $value): string { $dumper = new HtmlDumper(); $dumper->setTheme('light'); return $dumper->dump($value, true); } private function formatArgs(array $args): string { $result = []; foreach ($args as $key => $item) { if ('object' === $item[0]) { $formattedValue = sprintf('<em>object</em>(%s)', $this->abbrClass($item[1])); } elseif ('array' === $item[0]) { $formattedValue = sprintf('<em>array</em>(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); } elseif ('null' === $item[0]) { $formattedValue = '<em>null</em>'; } elseif ('boolean' === $item[0]) { $formattedValue = '<em>'.strtolower(var_export($item[1], true)).'</em>'; } elseif ('resource' === $item[0]) { $formattedValue = '<em>resource</em>'; } elseif (preg_match('/[^\x07-\x0D\x1B\x20-\xFF]/', $item[1])) { $formattedValue = '<em>binary string</em>'; } else { $formattedValue = str_replace("\n", '', $this->escape(var_export($item[1], true))); } $result[] = \is_int($key) ? $formattedValue : sprintf("'%s' => %s", $this->escape($key), $formattedValue); } return implode(', ', $result); } private function formatArgsAsText(array $args): string { return strip_tags($this->formatArgs($args)); } private function escape(string $string): string { return htmlspecialchars($string, \ENT_COMPAT | \ENT_SUBSTITUTE, $this->charset); } private function abbrClass(string $class): string { $parts = explode('\\', $class); $short = array_pop($parts); return sprintf('<abbr title="%s">%s</abbr>', $class, $short); } private function getFileRelative(string $file): ?string { $file = str_replace('\\', '/', $file); if (null !== $this->projectDir && str_starts_with($file, $this->projectDir)) { return ltrim(substr($file, \strlen($this->projectDir)), '/'); } return null; } /** * Formats a file path. * * @param string $file An absolute file path * @param int $line The line number * @param string $text Use this text for the link rather than the file path */ private function formatFile(string $file, int $line, ?string $text = null): string { $file = trim($file); if (null === $text) { $text = $file; if (null !== $rel = $this->getFileRelative($text)) { $rel = explode('/', $rel, 2); $text = sprintf('<abbr title="%s%2$s">%s</abbr>%s', $this->projectDir, $rel[0], '/'.($rel[1] ?? '')); } } if (0 < $line) { $text .= ' at line '.$line; } $link = $this->fileLinkFormat->format($file, $line); return sprintf('<a href="%s" title="Click to open this file" class="file_link">%s</a>', $this->escape($link), $text); } /** * Returns an excerpt of a code file around the given line number. * * @param string $file A file path * @param int $line The selected line number * @param int $srcContext The number of displayed lines around or -1 for the whole file */ private function fileExcerpt(string $file, int $line, int $srcContext = 3): string { if (is_file($file) && is_readable($file)) { // highlight_file could throw warnings // see https://bugs.php.net/25725 $code = @highlight_file($file, true); if (\PHP_VERSION_ID >= 80300) { // remove main pre/code tags $code = preg_replace('#^<pre.*?>\s*<code.*?>(.*)</code>\s*</pre>#s', '\\1', $code); // split multiline code tags $code = preg_replace_callback('#<code ([^>]++)>((?:[^<]*+\\n)++[^<]*+)</code>#', fn ($m) => "<code $m[1]>".str_replace("\n", "</code>\n<code $m[1]>", $m[2]).'</code>', $code); // Convert spaces to html entities to preserve indentation when rendered $code = str_replace(' ', ' ', $code); $content = explode("\n", $code); } else { // remove main code/span tags $code = preg_replace('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s', '\\1', $code); // split multiline spans $code = preg_replace_callback('#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span>#', fn ($m) => "<span $m[1]>".str_replace('<br />', "</span><br /><span $m[1]>", $m[2]).'</span>', $code); $content = explode('<br />', $code); } $lines = []; if (0 > $srcContext) { $srcContext = \count($content); } for ($i = max($line - $srcContext, 1), $max = min($line + $srcContext, \count($content)); $i <= $max; ++$i) { $lines[] = '<li'.($i == $line ? ' class="selected"' : '').'><code>'.$this->fixCodeMarkup($content[$i - 1]).'</code></li>'; } return '<ol start="'.max($line - $srcContext, 1).'">'.implode("\n", $lines).'</ol>'; } return ''; } private function fixCodeMarkup(string $line): string { // </span> ending tag from previous line $opening = strpos($line, '<span'); $closing = strpos($line, '</span>'); if (false !== $closing && (false === $opening || $closing < $opening)) { $line = substr_replace($line, '', $closing, 7); } // missing </span> tag at the end of line $opening = strrpos($line, '<span'); $closing = strrpos($line, '</span>'); if (false !== $opening && (false === $closing || $closing < $opening)) { $line .= '</span>'; } return trim($line); } private function formatFileFromText(string $text): string { return preg_replace_callback('/in ("|")?(.+?)\1(?: +(?:on|at))? +line (\d+)/s', fn ($match) => 'in '.$this->formatFile($match[2], $match[3]), $text) ?? $text; } private function formatLogMessage(string $message, array $context): string { if ($context && str_contains($message, '{')) { $replacements = []; foreach ($context as $key => $val) { if (\is_scalar($val)) { $replacements['{'.$key.'}'] = $val; } } if ($replacements) { $message = strtr($message, $replacements); } } return $this->escape($message); } private function addElementToGhost(): string { if (!isset(self::GHOST_ADDONS[date('m-d')])) { return ''; } return '<path d="'.self::GHOST_ADDONS[date('m-d')].'" fill="#fff" fill-opacity="0.6"></path>'; } private function include(string $name, array $context = []): string { extract($context, \EXTR_SKIP); ob_start(); include is_file(\dirname(__DIR__).'/Resources/'.$name) ? \dirname(__DIR__).'/Resources/'.$name : $name; return trim(ob_get_clean()); } /** * Allows overriding the default non-debug template. * * @param string $template path to the custom template file to render */ public static function setTemplate(string $template): void { self::$template = $template; } } error-handler/ErrorRenderer/FileLinkFormatter.php 0000644 00000006164 15060133305 0016165 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorRenderer; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; /** * Formats debug file links. * * @author Jérémy Romey <jeremy@free-agent.fr> * * @final */ class FileLinkFormatter { private array|false $fileLinkFormat; /** * @param string|\Closure $urlFormat The URL format, or a closure that returns it on-demand */ public function __construct( string|array|null $fileLinkFormat = null, private ?RequestStack $requestStack = null, private ?string $baseDir = null, private string|\Closure|null $urlFormat = null, ) { $fileLinkFormat ??= $_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? ''; if (!\is_array($f = $fileLinkFormat)) { $f = (ErrorRendererInterface::IDE_LINK_FORMATS[$f] ?? $f) ?: \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l'; $i = strpos($f, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: \strlen($f); $fileLinkFormat = [substr($f, 0, $i)] + preg_split('/&([^>]++)>/', substr($f, $i), -1, \PREG_SPLIT_DELIM_CAPTURE); } $this->fileLinkFormat = $fileLinkFormat; } public function format(string $file, int $line): string|false { if ($fmt = $this->getFileLinkFormat()) { for ($i = 1; isset($fmt[$i]); ++$i) { if (str_starts_with($file, $k = $fmt[$i++])) { $file = substr_replace($file, $fmt[$i], 0, \strlen($k)); break; } } return strtr($fmt[0], ['%f' => $file, '%l' => $line]); } return false; } /** * @internal */ public function __sleep(): array { $this->fileLinkFormat = $this->getFileLinkFormat(); return ['fileLinkFormat']; } /** * @internal */ public static function generateUrlFormat(UrlGeneratorInterface $router, string $routeName, string $queryString): ?string { try { return $router->generate($routeName).$queryString; } catch (\Throwable) { return null; } } private function getFileLinkFormat(): array|false { if ($this->fileLinkFormat) { return $this->fileLinkFormat; } if ($this->requestStack && $this->baseDir && $this->urlFormat) { $request = $this->requestStack->getMainRequest(); if ($request instanceof Request && (!$this->urlFormat instanceof \Closure || $this->urlFormat = ($this->urlFormat)())) { return [ $request->getSchemeAndHttpHost().$this->urlFormat, $this->baseDir.\DIRECTORY_SEPARATOR, '', ]; } } return false; } } error-handler/ErrorRenderer/CliErrorRenderer.php 0000644 00000002517 15060133305 0016012 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorRenderer; use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper; // Help opcache.preload discover always-needed symbols class_exists(CliDumper::class); /** * @author Nicolas Grekas <p@tchwork.com> */ class CliErrorRenderer implements ErrorRendererInterface { public function render(\Throwable $exception): FlattenException { $cloner = new VarCloner(); $dumper = new class() extends CliDumper { protected function supportsColors(): bool { $outputStream = $this->outputStream; $this->outputStream = fopen('php://stdout', 'w'); try { return parent::supportsColors(); } finally { $this->outputStream = $outputStream; } } }; return FlattenException::createFromThrowable($exception) ->setAsString($dumper->dump($cloner->cloneVar($exception), true)); } } error-handler/ErrorRenderer/SerializerErrorRenderer.php 0000644 00000006256 15060133305 0017420 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\ErrorRenderer; use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Serializer\Exception\NotEncodableValueException; use Symfony\Component\Serializer\SerializerInterface; /** * Formats an exception using Serializer for rendering. * * @author Nicolas Grekas <p@tchwork.com> */ class SerializerErrorRenderer implements ErrorRendererInterface { private string|\Closure $format; private ErrorRendererInterface $fallbackErrorRenderer; private bool|\Closure $debug; /** * @param string|callable(FlattenException) $format The format as a string or a callable that should return it * formats not supported by Request::getMimeTypes() should be given as mime types * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it */ public function __construct( private SerializerInterface $serializer, string|callable $format, ?ErrorRendererInterface $fallbackErrorRenderer = null, bool|callable $debug = false, ) { $this->format = \is_string($format) ? $format : $format(...); $this->fallbackErrorRenderer = $fallbackErrorRenderer ?? new HtmlErrorRenderer(); $this->debug = \is_bool($debug) ? $debug : $debug(...); } public function render(\Throwable $exception): FlattenException { $headers = ['Vary' => 'Accept']; $debug = \is_bool($this->debug) ? $this->debug : ($this->debug)($exception); if ($debug) { $headers['X-Debug-Exception'] = rawurlencode($exception->getMessage()); $headers['X-Debug-Exception-File'] = rawurlencode($exception->getFile()).':'.$exception->getLine(); } $flattenException = FlattenException::createFromThrowable($exception, null, $headers); try { $format = \is_string($this->format) ? $this->format : ($this->format)($flattenException); $headers['Content-Type'] = Request::getMimeTypes($format)[0] ?? $format; $flattenException->setAsString($this->serializer->serialize($flattenException, $format, [ 'exception' => $exception, 'debug' => $debug, ])); } catch (NotEncodableValueException) { $flattenException = $this->fallbackErrorRenderer->render($exception); } return $flattenException->setHeaders($flattenException->getHeaders() + $headers); } public static function getPreferredFormat(RequestStack $requestStack): \Closure { return static function () use ($requestStack) { if (!$request = $requestStack->getCurrentRequest()) { throw new NotEncodableValueException(); } return $request->getPreferredFormat(); }; } } error-handler/BufferingLogger.php 0000644 00000003770 15060133305 0013073 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler; use Psr\Log\AbstractLogger; /** * A buffering logger that stacks logs for later. * * @author Nicolas Grekas <p@tchwork.com> */ class BufferingLogger extends AbstractLogger { private array $logs = []; public function log($level, $message, array $context = []): void { $this->logs[] = [$level, $message, $context]; } public function cleanLogs(): array { $logs = $this->logs; $this->logs = []; return $logs; } public function __sleep(): array { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } public function __wakeup(): void { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } public function __destruct() { foreach ($this->logs as [$level, $message, $context]) { if (str_contains($message, '{')) { foreach ($context as $key => $val) { if (null === $val || \is_scalar($val) || (\is_object($val) && \is_callable([$val, '__toString']))) { $message = str_replace("{{$key}}", $val, $message); } elseif ($val instanceof \DateTimeInterface) { $message = str_replace("{{$key}}", $val->format(\DateTimeInterface::RFC3339), $message); } elseif (\is_object($val)) { $message = str_replace("{{$key}}", '[object '.get_debug_type($val).']', $message); } else { $message = str_replace("{{$key}}", '['.\gettype($val).']', $message); } } } error_log(sprintf('%s [%s] %s', date(\DateTimeInterface::RFC3339), $level, $message)); } } } error-handler/CHANGELOG.md 0000644 00000001757 15060133305 0011127 0 ustar 00 CHANGELOG ========= 7.1 --- * Increase log level to "error" at least for all PHP errors 6.4 --- * `FlattenExceptionNormalizer` no longer implements `ContextAwareNormalizerInterface` 6.3 --- * Display exception properties in the HTML error page 6.1 --- * Report overridden `@final` constants and properties * Read environment variable `SYMFONY_IDE` to configure file link format 5.4 --- * Make `DebugClassLoader` trigger deprecation notices on missing return types * Add `SYMFONY_PATCH_TYPE_DECLARATIONS='force=2'` mode to `DebugClassLoader` to turn annotations into native return types 5.2.0 ----- * added the ability to set `HtmlErrorRenderer::$template` to a custom template to render when not in debug mode. 5.1.0 ----- * The `HtmlErrorRenderer` and `SerializerErrorRenderer` add `X-Debug-Exception` and `X-Debug-Exception-File` headers in debug mode. 4.4.0 ----- * added the component * added `ErrorHandler::call()` method utility to turn any PHP error into `\ErrorException` error-handler/composer.json 0000644 00000002147 15060133305 0012032 0 ustar 00 { "name": "symfony/error-handler", "type": "library", "description": "Provides tools to manage errors and ease debugging PHP code", "keywords": [], "homepage": "https://symfony.com", "license": "MIT", "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", "symfony/var-dumper": "^6.4|^7.0" }, "require-dev": { "symfony/http-kernel": "^6.4|^7.0", "symfony/serializer": "^6.4|^7.0", "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "symfony/deprecation-contracts": "<2.5", "symfony/http-kernel": "<6.4" }, "autoload": { "psr-4": { "Symfony\\Component\\ErrorHandler\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "bin": [ "Resources/bin/patch-type-declarations" ], "minimum-stability": "dev" } error-handler/LICENSE 0000644 00000002054 15060133305 0010312 0 ustar 00 Copyright (c) 2019-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. error-handler/Debug.php 0000644 00000002064 15060133305 0011045 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler; /** * Registers all the debug tools. * * @author Fabien Potencier <fabien@symfony.com> */ class Debug { public static function enable(): ErrorHandler { error_reporting(-1); if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) { ini_set('display_errors', 0); } elseif (!filter_var(\ini_get('log_errors'), \FILTER_VALIDATE_BOOL) || \ini_get('error_log')) { // CLI - display errors only if they're not already logged to STDERR ini_set('display_errors', 1); } @ini_set('zend.assertions', 1); ini_set('assert.active', 1); ini_set('assert.exception', 1); DebugClassLoader::enable(); return ErrorHandler::register(new ErrorHandler(new BufferingLogger(), true)); } } error-handler/Error/UndefinedMethodError.php 0000644 00000001462 15060133305 0015165 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Error; class UndefinedMethodError extends \Error { public function __construct(string $message, \Throwable $previous) { parent::__construct($message, $previous->getCode(), $previous->getPrevious()); foreach ([ 'file' => $previous->getFile(), 'line' => $previous->getLine(), 'trace' => $previous->getTrace(), ] as $property => $value) { $refl = new \ReflectionProperty(\Error::class, $property); $refl->setValue($this, $value); } } } error-handler/Error/OutOfMemoryError.php 0000644 00000000515 15060133305 0014346 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Error; class OutOfMemoryError extends FatalError { } error-handler/Error/ClassNotFoundError.php 0000644 00000001460 15060133305 0014643 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Error; class ClassNotFoundError extends \Error { public function __construct(string $message, \Throwable $previous) { parent::__construct($message, $previous->getCode(), $previous->getPrevious()); foreach ([ 'file' => $previous->getFile(), 'line' => $previous->getLine(), 'trace' => $previous->getTrace(), ] as $property => $value) { $refl = new \ReflectionProperty(\Error::class, $property); $refl->setValue($this, $value); } } } error-handler/Error/UndefinedFunctionError.php 0000644 00000001464 15060133305 0015534 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Error; class UndefinedFunctionError extends \Error { public function __construct(string $message, \Throwable $previous) { parent::__construct($message, $previous->getCode(), $previous->getPrevious()); foreach ([ 'file' => $previous->getFile(), 'line' => $previous->getLine(), 'trace' => $previous->getTrace(), ] as $property => $value) { $refl = new \ReflectionProperty(\Error::class, $property); $refl->setValue($this, $value); } } } error-handler/Error/FatalError.php 0000644 00000005350 15060133305 0013152 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\ErrorHandler\Error; class FatalError extends \Error { /** * @param array $error An array as returned by error_get_last() */ public function __construct( string $message, int $code, private array $error, ?int $traceOffset = null, bool $traceArgs = true, ?array $trace = null, ) { parent::__construct($message, $code); if (null !== $trace) { if (!$traceArgs) { foreach ($trace as &$frame) { unset($frame['args'], $frame['this'], $frame); } } } elseif (null !== $traceOffset) { if (\function_exists('xdebug_get_function_stack') && \in_array(\ini_get('xdebug.mode'), ['develop', false], true) && $trace = @xdebug_get_function_stack()) { if (0 < $traceOffset) { array_splice($trace, -$traceOffset); } foreach ($trace as &$frame) { if (!isset($frame['type'])) { // XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695 if (isset($frame['class'])) { $frame['type'] = '::'; } } elseif ('dynamic' === $frame['type']) { $frame['type'] = '->'; } elseif ('static' === $frame['type']) { $frame['type'] = '::'; } // XDebug also has a different name for the parameters array if (!$traceArgs) { unset($frame['params'], $frame['args']); } elseif (isset($frame['params']) && !isset($frame['args'])) { $frame['args'] = $frame['params']; unset($frame['params']); } } unset($frame); $trace = array_reverse($trace); } else { $trace = []; } } foreach ([ 'file' => $error['file'], 'line' => $error['line'], 'trace' => $trace, ] as $property => $value) { if (null !== $value) { $refl = new \ReflectionProperty(\Error::class, $property); $refl->setValue($this, $value); } } } public function getError(): array { return $this->error; } } error-handler/Resources/views/exception_full.html.php 0000644 00000003463 15060133305 0017115 0 ustar 00 <!-- <?= $_message = sprintf('%s (%d %s)', $exceptionMessage, $statusCode, $statusText); ?> --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="<?= $this->charset; ?>" /> <meta name="robots" content="noindex,nofollow" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title><?= $_message; ?></title> <link rel="icon" type="image/png" href="<?= $this->include('assets/images/favicon.png.base64'); ?>" /> <style><?= $this->include('assets/css/exception.css'); ?></style> <style><?= $this->include('assets/css/exception_full.css'); ?></style> </head> <body> <script> document.body.classList.add( localStorage.getItem('symfony/profiler/theme') || (matchMedia('(prefers-color-scheme: dark)').matches ? 'theme-dark' : 'theme-light') ); </script> <?php if (class_exists(\Symfony\Component\HttpKernel\Kernel::class)) { ?> <header> <div class="container"> <h1 class="logo"><?= $this->include('assets/images/symfony-logo.svg'); ?> Symfony Exception</h1> <div class="help-link"> <a href="https://symfony.com/doc/<?= Symfony\Component\HttpKernel\Kernel::VERSION; ?>/index.html"> <span class="icon"><?= $this->include('assets/images/icon-book.svg'); ?></span> <span class="hidden-xs-down">Symfony</span> Docs </a> </div> </div> </header> <?php } ?> <?= $this->include('views/exception.html.php', $context); ?> <script> <?= $this->include('assets/js/exception.js'); ?> </script> </body> </html> <!-- <?= $_message; ?> --> error-handler/Resources/views/traces.html.php 0000644 00000005377 15060133305 0015364 0 ustar 00 <div class="trace trace-as-html" id="trace-box-<?= $index; ?>"> <div class="trace-details"> <div class="trace-head"> <div class="sf-toggle" data-toggle-selector="#trace-html-<?= $index; ?>" data-toggle-initial="<?= $expand ? 'display' : ''; ?>"> <span class="icon icon-close"><?= $this->include('assets/images/icon-minus-square-o.svg'); ?></span> <span class="icon icon-open"><?= $this->include('assets/images/icon-plus-square-o.svg'); ?></span> <?php $separator = strrpos($exception['class'], '\\'); $separator = false === $separator ? 0 : $separator + 1; $namespace = substr($exception['class'], 0, $separator); $class = substr($exception['class'], $separator); ?> <?php if ('' === $class) { ?> <br> <?php } else { ?> <h3 class="trace-class"> <?php if ('' !== $namespace) { ?> <span class="trace-namespace"><?= $namespace; ?></span> <?php } ?> <?= $class; ?> </h3> <?php } ?> <?php if ($exception['message'] && $index > 1) { ?> <p class="break-long-words trace-message"><?= $this->escape($exception['message']); ?></p> <?php } ?> </div> <?php if (\count($exception['data'] ?? [])) { ?> <details class="exception-properties-wrapper"> <summary>Show exception properties</summary> <div class="exception-properties"> <?= $this->dumpValue($exception['data']) ?> </div> </details> <?php } ?> </div> <div id="trace-html-<?= $index; ?>" class="sf-toggle-content"> <?php $isFirstUserCode = true; foreach ($exception['trace'] as $i => $trace) { $isVendorTrace = $trace['file'] && (str_contains($trace['file'], '/vendor/') || str_contains($trace['file'], '/var/cache/')); $displayCodeSnippet = $isFirstUserCode && !$isVendorTrace; if ($displayCodeSnippet) { $isFirstUserCode = false; } ?> <div class="trace-line <?= $isVendorTrace ? 'trace-from-vendor' : ''; ?>"> <?= $this->include('views/trace.html.php', [ 'prefix' => $index, 'i' => $i, 'trace' => $trace, 'style' => $isVendorTrace ? 'compact' : ($displayCodeSnippet ? 'expanded' : ''), ]); ?> </div> <?php } ?> </div> </div> </div> error-handler/Resources/views/logs.html.php 0000644 00000004133 15060133305 0015034 0 ustar 00 <table class="logs" data-filter-level="Emergency,Alert,Critical,Error,Warning,Notice,Info,Debug" data-filters> <?php $channelIsDefined = isset($logs[0]['channel']); ?> <thead> <tr> <th data-filter="level">Level</th> <?php if ($channelIsDefined) { ?><th data-filter="channel">Channel</th><?php } ?> <th class="full-width">Message</th> </tr> </thead> <tbody> <?php foreach ($logs as $log) { if ($log['priority'] >= 400) { $status = 'error'; } elseif ($log['priority'] >= 300) { $status = 'warning'; } else { $severity = 0; if (($exception = $log['context']['exception'] ?? null) instanceof \ErrorException || $exception instanceof \Symfony\Component\ErrorHandler\Exception\SilencedErrorContext) { $severity = $exception->getSeverity(); } $status = \E_DEPRECATED === $severity || \E_USER_DEPRECATED === $severity ? 'warning' : 'normal'; } ?> <tr class="status-<?= $status; ?>" data-filter-level="<?= strtolower($this->escape($log['priorityName'])); ?>"<?php if ($channelIsDefined) { ?> data-filter-channel="<?= $this->escape($log['channel']); ?>"<?php } ?>> <td class="text-small nowrap"> <span class="colored text-bold"><?= $this->escape($log['priorityName']); ?></span> <span class="text-muted newline"><?= date('H:i:s', $log['timestamp']); ?></span> </td> <?php if ($channelIsDefined) { ?> <td class="text-small text-bold nowrap"> <?= $this->escape($log['channel']); ?> </td> <?php } ?> <td> <?= $this->formatLogMessage($log['message'], $log['context']); ?> <?php if ($log['context']) { ?> <pre class="text-muted prewrap m-t-5"><?= $this->escape(json_encode($log['context'], \JSON_PRETTY_PRINT | \JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES)); ?></pre> <?php } ?> </td> </tr> <?php } ?> </tbody> </table> error-handler/Resources/views/exception.html.php 0000644 00000010661 15060133305 0016071 0 ustar 00 <div class="exception-summary <?= !$exceptionMessage ? 'exception-without-message' : ''; ?>"> <div class="exception-metadata"> <div class="container"> <h2 class="exception-hierarchy"> <?php foreach (array_reverse($exception->getAllPrevious(), true) as $index => $previousException) { ?> <a href="#trace-box-<?= $index + 2; ?>"><?= $this->abbrClass($previousException->getClass()); ?></a> <span class="icon"><?= $this->include('assets/images/chevron-right.svg'); ?></span> <?php } ?> <a href="#trace-box-1"><?= $this->abbrClass($exception->getClass()); ?></a> </h2> <h2 class="exception-http"> HTTP <?= $statusCode; ?> <small><?= $statusText; ?></small> </h2> </div> </div> <div class="exception-message-wrapper"> <div class="container"> <h1 class="break-long-words exception-message<?= mb_strlen($exceptionMessage) > 180 ? ' long' : ''; ?>"><?= $this->formatFileFromText(nl2br($exceptionMessage)); ?></h1> <div class="exception-illustration hidden-xs-down"> <?= $this->include('assets/images/symfony-ghost.svg.php'); ?> </div> </div> </div> </div> <div class="container"> <div class="sf-tabs"> <div class="tab"> <?php $exceptionAsArray = $exception->toArray(); $exceptionWithUserCode = []; $exceptionAsArrayCount = count($exceptionAsArray); $last = $exceptionAsArrayCount - 1; foreach ($exceptionAsArray as $i => $e) { foreach ($e['trace'] as $trace) { if ($trace['file'] && !str_contains($trace['file'], '/vendor/') && !str_contains($trace['file'], '/var/cache/') && $i < $last) { $exceptionWithUserCode[] = $i; } } } ?> <h3 class="tab-title"> <?php if ($exceptionAsArrayCount > 1) { ?> Exceptions <span class="badge"><?= $exceptionAsArrayCount; ?></span> <?php } else { ?> Exception <?php } ?> </h3> <div class="tab-content"> <?php foreach ($exceptionAsArray as $i => $e) { echo $this->include('views/traces.html.php', [ 'exception' => $e, 'index' => $i + 1, 'expand' => in_array($i, $exceptionWithUserCode, true) || ([] === $exceptionWithUserCode && 0 === $i), ]); } ?> </div> </div> <?php if ($logger) { ?> <div class="tab <?= !$logger->getLogs() ? 'disabled' : ''; ?>"> <h3 class="tab-title"> Logs <?php if ($logger->countErrors()) { ?><span class="badge status-error"><?= $logger->countErrors(); ?></span><?php } ?> </h3> <div class="tab-content"> <?php if ($logger->getLogs()) { ?> <?= $this->include('views/logs.html.php', ['logs' => $logger->getLogs()]); ?> <?php } else { ?> <div class="empty"> <p>No log messages</p> </div> <?php } ?> </div> </div> <?php } ?> <div class="tab"> <h3 class="tab-title"> <?php if ($exceptionAsArrayCount > 1) { ?> Stack Traces <span class="badge"><?= $exceptionAsArrayCount; ?></span> <?php } else { ?> Stack Trace <?php } ?> </h3> <div class="tab-content"> <?php foreach ($exceptionAsArray as $i => $e) { echo $this->include('views/traces_text.html.php', [ 'exception' => $e, 'index' => $i + 1, 'numExceptions' => $exceptionAsArrayCount, ]); } ?> </div> </div> <?php if ($currentContent) { ?> <div class="tab"> <h3 class="tab-title">Output content</h3> <div class="tab-content"> <?= $currentContent; ?> </div> </div> <?php } ?> </div> </div> error-handler/Resources/views/error.html.php 0000644 00000001463 15060133305 0015224 0 ustar 00 <!DOCTYPE html> <html lang="en"> <head> <meta charset="<?= $this->charset; ?>" /> <meta name="robots" content="noindex,nofollow,noarchive" /> <title>An Error Occurred: <?= $statusText; ?></title> <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>❌</text></svg>" /> <style><?= $this->include('assets/css/error.css'); ?></style> </head> <body> <div class="container"> <h1>Oops! An Error Occurred</h1> <h2>The server returned a "<?= $statusCode; ?> <?= $statusText; ?>".</h2> <p> Something is broken. Please let us know what you were doing when this error occurred. We will fix it as soon as possible. Sorry for any inconvenience caused. </p> </div> </body> </html> error-handler/Resources/views/trace.html.php 0000644 00000005017 15060133305 0015170 0 ustar 00 <div class="trace-line-header break-long-words <?= $trace['file'] ? 'sf-toggle' : ''; ?>" data-toggle-selector="#trace-html-<?= $prefix; ?>-<?= $i; ?>" data-toggle-initial="<?= 'expanded' === $style ? 'display' : ''; ?>"> <?php if ($trace['file']) { ?> <span class="icon icon-close"><?= $this->include('assets/images/icon-minus-square.svg'); ?></span> <span class="icon icon-open"><?= $this->include('assets/images/icon-plus-square.svg'); ?></span> <?php } ?> <?php if ('compact' !== $style && $trace['function']) { ?> <span class="trace-class"><?= $this->abbrClass($trace['class']); ?></span><?php if ($trace['type']) { ?><span class="trace-type"><?= $trace['type']; ?></span><?php } ?><span class="trace-method"><?= $trace['function']; ?></span><?php if (isset($trace['args'])) { ?><span class="trace-arguments">(<?= $this->formatArgs($trace['args']); ?>)</span><?php } ?> <?php } ?> <?php if ($trace['file']) { ?> <?php $lineNumber = $trace['line'] ?: 1; $fileLink = $this->fileLinkFormat->format($trace['file'], $lineNumber); $filePath = strtr(strip_tags($this->formatFile($trace['file'], $lineNumber)), [' at line '.$lineNumber => '']); $filePathParts = explode(\DIRECTORY_SEPARATOR, $filePath); ?> <span class="block trace-file-path"> in <a href="<?= $fileLink; ?>"> <?= implode(\DIRECTORY_SEPARATOR, array_slice($filePathParts, 0, -1)).\DIRECTORY_SEPARATOR; ?><strong><?= end($filePathParts); ?></strong> </a> <?php if ('compact' === $style && $trace['function']) { ?> <span class="trace-type"><?= $trace['type']; ?></span> <span class="trace-method"><?= $trace['function']; ?></span> <?php } ?> (line <?= $lineNumber; ?>) <span class="icon icon-copy hidden" data-clipboard-text="<?php echo implode(\DIRECTORY_SEPARATOR, $filePathParts).':'.$lineNumber; ?>"> <?php echo $this->include('assets/images/icon-copy.svg'); ?> </span> </span> <?php } ?> </div> <?php if ($trace['file']) { ?> <div id="trace-html-<?= $prefix.'-'.$i; ?>" class="trace-code sf-toggle-content"> <?= strtr($this->fileExcerpt($trace['file'], $trace['line'], 5), [ '#DD0000' => 'var(--highlight-string)', '#007700' => 'var(--highlight-keyword)', '#0000BB' => 'var(--highlight-default)', '#FF8000' => 'var(--highlight-comment)', ]); ?> </div> <?php } ?> error-handler/Resources/views/traces_text.html.php 0000644 00000004020 15060133305 0016410 0 ustar 00 <table class="trace trace-as-text"> <thead class="trace-head"> <tr> <th class="sf-toggle" data-toggle-selector="#trace-text-<?= $index; ?>" data-toggle-initial="<?= 1 === $index ? 'display' : ''; ?>"> <div class="trace-class"> <?php if ($numExceptions > 1) { ?> <span class="text-muted">[<?= $numExceptions - $index + 1; ?>/<?= $numExceptions; ?>]</span> <?php } ?> <?= ($parts = explode('\\', $exception['class'])) ? end($parts) : ''; ?> <span class="icon icon-close"><?= $this->include('assets/images/icon-minus-square-o.svg'); ?></span> <span class="icon icon-open"><?= $this->include('assets/images/icon-plus-square-o.svg'); ?></span> </div> </th> </tr> </thead> <tbody id="trace-text-<?= $index; ?>"> <tr> <td> <?php if ($exception['trace']) { ?> <pre class="stacktrace"> <?php echo $this->escape($exception['class']).":\n"; if ($exception['message']) { echo $this->escape($exception['message'])."\n"; } foreach ($exception['trace'] as $trace) { echo "\n "; if ($trace['function']) { echo $this->escape('at '.$trace['class'].$trace['type'].$trace['function']).'('.(isset($trace['args']) ? $this->formatArgsAsText($trace['args']) : '').')'; } if ($trace['file'] && $trace['line']) { echo($trace['function'] ? "\n (" : 'at ').strtr(strip_tags($this->formatFile($trace['file'], $trace['line'])), [' at line '.$trace['line'] => '']).':'.$trace['line'].($trace['function'] ? ')' : ''); } } ?> </pre> <?php } ?> </td> </tr> </tbody> </table> error-handler/Resources/assets/css/error.css 0000644 00000000423 15060133305 0015212 0 ustar 00 body { background-color: #fff; color: #222; font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; margin: 0; } .container { margin: 30px; max-width: 600px; } h1 { color: #dc3545; font-size: 24px; } h2 { font-size: 18px; } error-handler/Resources/assets/css/exception.css 0000644 00000041045 15060133305 0016064 0 ustar 00 /* This file is based on WebProfilerBundle/Resources/views/Profiler/profiler.css.twig. If you make any change in this file, verify the same change is needed in the other file. */ :root { --font-sans-serif: Helvetica, Arial, sans-serif; --page-background: #f9f9f9; --color-text: #222; /* when updating any of these colors, do the same in toolbar.css.twig */ --color-success: #4f805d; --color-warning: #a46a1f; --color-error: #b0413e; --color-muted: #999; --tab-background: #f0f0f0; --tab-border-color: #e5e5e5; --tab-active-border-color: #d4d4d4; --tab-color: #444; --tab-active-background: #fff; --tab-active-color: var(--color-text); --tab-disabled-background: #f5f5f5; --tab-disabled-color: #999; --selected-badge-background: #e5e5e5; --selected-badge-color: #525252; --selected-badge-shadow: inset 0 0 0 1px #d4d4d4; --selected-badge-warning-background: #fde496; --selected-badge-warning-color: #785b02; --selected-badge-warning-shadow: inset 0 0 0 1px #e6af05; --selected-badge-danger-background: #FCE9ED; --selected-badge-danger-color: #83122A; --selected-badge-danger-shadow: inset 0 0 0 1px #F5B8C5; --metric-value-background: #fff; --metric-value-color: inherit; --metric-unit-color: #999; --metric-label-background: #e0e0e0; --metric-label-color: inherit; --table-border: #e0e0e0; --table-background: #fff; --table-header: #e0e0e0; --trace-selected-background: #F7E5A1; --tree-active-background: #F7E5A1; --exception-title-color: var(--base-2); --shadow: 0px 0px 1px rgba(128, 128, 128, .2); --border: 1px solid #e0e0e0; --background-error: var(--color-error); --highlight-comment: #969896; --highlight-default: #222222; --highlight-keyword: #a71d5d; --highlight-string: #183691; --base-0: #fff; --base-1: #f5f5f5; --base-2: #e0e0e0; --base-3: #ccc; --base-4: #666; --base-5: #444; --base-6: #222; } .theme-dark { --page-background: #36393e; --color-text: #e0e0e0; --color-muted: #777; --color-error: #d43934; --tab-background: #404040; --tab-border-color: #737373; --tab-active-border-color: #171717; --tab-color: var(--color-text); --tab-active-background: #d4d4d4; --tab-active-color: #262626; --tab-disabled-background: var(--page-background); --tab-disabled-color: #a3a3a3; --selected-badge-background: #555; --selected-badge-color: #ddd; --selected-badge-shadow: none; --selected-badge-warning-background: #fcd55f; --selected-badge-warning-color: #785b02; --selected-badge-warning-shadow: inset 0 0 0 1px #af8503; --selected-badge-danger-background: #B41939; --selected-badge-danger-color: #FCE9ED; --selected-badge-danger-shadow: none; --metric-value-background: #555; --metric-value-color: inherit; --metric-unit-color: #999; --metric-label-background: #777; --metric-label-color: #e0e0e0; --trace-selected-background: #71663acc; --table-border: #444; --table-background: #333; --table-header: #555; --info-background: rgba(79, 148, 195, 0.5); --tree-active-background: var(--metric-label-background); --exception-title-color: var(--base-2); --shadow: 0px 0px 1px rgba(32, 32, 32, .2); --border: 1px solid #666; --background-error: #b0413e; --highlight-comment: #dedede; --highlight-default: var(--base-6); --highlight-keyword: #ff413c; --highlight-string: #70a6fd; --base-0: #2e3136; --base-1: #444; --base-2: #666; --base-3: #666; --base-4: #666; --base-5: #e0e0e0; --base-6: #f5f5f5; --card-label-background: var(--tab-active-background); --card-label-color: var(--tab-active-color); } html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}summary{cursor: pointer} html { /* always display the vertical scrollbar to avoid jumps when toggling contents */ overflow-y: scroll; } body { background-color: var(--page-background); color: var(--base-6); font: 14px/1.4 Helvetica, Arial, sans-serif; padding-bottom: 45px; } a { cursor: pointer; text-decoration: none; } a:hover { text-decoration: underline; } abbr[title] { border-bottom: none; cursor: help; text-decoration: none; } code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; } table, tr, th, td { background: var(--base-0); border-collapse: collapse; vertical-align: top; } table { background: var(--base-0); border: var(--border); box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; } table th, table td { border: solid var(--base-2); border-width: 1px 0; padding: 8px 10px; } table th { background-color: var(--base-2); font-weight: bold; text-align: left; } .m-t-5 { margin-top: 5px; } .hidden-xs-down { display: none; } .block { display: block; } .full-width { width: 100%; } .hidden { display: none; } .prewrap { white-space: pre-wrap; } .nowrap { white-space: nowrap; } .newline { display: block; } .break-long-words { word-wrap: break-word; overflow-wrap: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; hyphenate-character: ''; min-width: 0; } .text-small { font-size: 12px !important; } .text-muted { color: #999; } .text-bold { font-weight: bold; } .empty { border: 4px dashed var(--base-2); color: #999; margin: 1em 0; padding: .5em 2em; } .status-success { background: rgba(94, 151, 110, 0.3); } .status-warning { background: rgba(240, 181, 24, 0.3); } .status-error { background: rgba(176, 65, 62, 0.2); } .status-success td, .status-warning td, .status-error td { background: transparent; } tr.status-error td, tr.status-warning td { border-bottom: 1px solid var(--base-2); border-top: 1px solid var(--base-2); } .status-warning .colored { color: #A46A1F; } .status-error .colored { color: var(--color-error); } .sf-toggle { cursor: pointer; position: relative; } .sf-toggle-content { -moz-transition: display .25s ease; -webkit-transition: display .25s ease; transition: display .25s ease; } .sf-toggle-content.sf-toggle-hidden { display: none; } .sf-toggle-content.sf-toggle-visible { display: block; } thead.sf-toggle-content.sf-toggle-visible, tbody.sf-toggle-content.sf-toggle-visible { display: table-row-group; } .sf-toggle-off .icon-close, .sf-toggle-on .icon-open { display: none; } .sf-toggle-off .icon-open, .sf-toggle-on .icon-close { display: block; } .tab-navigation { background-color: var(--tab-background); border-radius: 6px; box-shadow: inset 0 0 0 1px var(--tab-border-color), 0 0 0 5px var(--page-background); display: inline-flex; flex-wrap: wrap; margin: 0 0 15px; padding: 0; user-select: none; -webkit-user-select: none; } .sf-tabs-sm .tab-navigation { box-shadow: inset 0 0 0 1px var(--tab-border-color), 0 0 0 4px var(--page-background); margin: 0 0 10px; } .tab-navigation .tab-control { background: transparent; border: 0; box-shadow: none; transition: box-shadow .05s ease-in, background-color .05s ease-in; cursor: pointer; font-size: 14px; font-weight: 500; line-height: 1.4; margin: 0; padding: 4px 14px; position: relative; text-align: center; z-index: 1; } .sf-tabs-sm .tab-navigation .tab-control { font-size: 13px; padding: 2.5px 10px; } .tab-navigation .tab-control:before { background: var(--tab-border-color); bottom: 15%; content: ""; left: 0; position: absolute; top: 15%; width: 1px; } .tab-navigation .tab-control:first-child:before, .tab-navigation .tab-control.active + .tab-control:before, .tab-navigation .tab-control.active:before { width: 0; } .tab-navigation .tab-control .badge { background: var(--selected-badge-background); box-shadow: var(--selected-badge-shadow); color: var(--selected-badge-color); display: inline-block; font-size: 12px; font-weight: bold; line-height: 1; margin-left: 8px; min-width: 10px; padding: 2px 6px; text-align: center; white-space: nowrap; } .tab-navigation .tab-control.disabled { color: var(--tab-disabled-color); } .tab-navigation .tab-control.active { background-color: var(--tab-active-background); border-radius: 6px; box-shadow: inset 0 0 0 1.5px var(--tab-active-border-color); color: var(--tab-active-color); position: relative; z-index: 1; } .theme-dark .tab-navigation li.active { box-shadow: inset 0 0 0 1px var(--tab-border-color); } .tab-content > *:first-child { margin-top: 0; } .tab-navigation .tab-control .badge.status-warning { background: var(--selected-badge-warning-background); box-shadow: var(--selected-badge-warning-shadow); color: var(--selected-badge-warning-color); } .tab-navigation .tab-control .badge.status-error { background: var(--selected-badge-danger-background); box-shadow: var(--selected-badge-danger-shadow); color: var(--selected-badge-danger-color); } .sf-tabs .tab:not(:first-child) { display: none; } [data-filters] { position: relative; } [data-filtered] { cursor: pointer; } [data-filtered]:after { content: '\00a0\25BE'; } [data-filtered]:hover .filter-list li { display: inline-flex; } [class*="filter-hidden-"] { display: none; } .filter-list { position: absolute; border: var(--border); box-shadow: var(--shadow); margin: 0; padding: 0; display: flex; flex-direction: column; } .filter-list :after { content: ''; } .filter-list li { background: var(--tab-disabled-background); border-bottom: var(--border); color: var(--tab-disabled-color); display: none; list-style: none; margin: 0; padding: 5px 10px; text-align: left; font-weight: normal; } .filter-list li.active { background: var(--tab-background); color: var(--tab-color); } .filter-list li.last-active { background: var(--tab-active-background); color: var(--tab-active-color); } .filter-list-level li { cursor: s-resize; } .filter-list-level li.active { cursor: n-resize; } .filter-list-level li.last-active { cursor: default; } .filter-list-level li.last-active:before { content: '\2714\00a0'; } .filter-list-choice li:before { content: '\2714\00a0'; color: transparent; } .filter-list-choice li.active:before { color: unset; } .container { max-width: 1024px; margin: 0 auto; padding: 0 15px; } .container::after { content: ""; display: table; clear: both; } header { background-color: #222; color: rgba(255, 255, 255, 0.75); font-size: 13px; height: 33px; line-height: 33px; padding: 0; } header .container { display: flex; justify-content: space-between; } .logo { flex: 1; font-size: 13px; font-weight: normal; margin: 0; padding: 0; } .logo svg { height: 18px; width: 18px; opacity: .8; vertical-align: -5px; } .help-link { margin-left: 15px; } .help-link a { color: inherit; } .help-link .icon svg { height: 15px; width: 15px; opacity: .7; vertical-align: -2px; } .help-link a:hover { color: #EEE; text-decoration: none; } .help-link a:hover svg { opacity: .9; } .exception-summary { background: var(--background-error); border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 15px; } .exception-metadata { background: rgba(0, 0, 0, 0.1); padding: 7px 0; } .exception-metadata .container { display: flex; flex-direction: row; justify-content: space-between; } .exception-metadata h2, .exception-metadata h2 > a { color: rgba(255, 255, 255, 0.8); font-size: 13px; font-weight: 400; margin: 0; } .exception-http small { font-size: 13px; opacity: .7; } .exception-hierarchy { flex: 1; } .exception-hierarchy .icon { margin: 0 3px; opacity: .7; } .exception-hierarchy .icon svg { height: 13px; width: 13px; vertical-align: -2px; } .exception-without-message .exception-message-wrapper { display: none; } .exception-message-wrapper .container { display: flex; align-items: flex-start; min-height: 70px; padding: 10px 15px 8px; } .exception-message { flex-grow: 1; } .exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; } .exception-message.long { font-size: 18px; } .exception-message a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); font-size: inherit; text-decoration: none; } .exception-message a:hover { border-bottom-color: #ffffff; } .exception-properties-wrapper { margin: .8em 0; } .exception-properties { background: var(--base-0); border: var(--border); box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); } .exception-properties pre { margin: 0; padding: 0.2em 0; } .exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; } .trace + .trace { margin-top: 30px; } .trace-head { background-color: var(--base-2); padding: 10px; position: relative; } .trace-head .trace-class { color: var(--base-6); font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; } .trace-head .trace-namespace { color: #999; display: block; font-size: 13px; } .trace-head .icon { position: absolute; right: 0; top: 0; } .trace-head .icon svg { fill: var(--base-5); height: 24px; width: 24px; } .trace-details { background: var(--base-0); border: var(--border); box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 0 0 1em; table-layout: fixed; } .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; } .trace-line { position: relative; padding-top: 8px; padding-bottom: 8px; } .trace-line + .trace-line { border-top: var(--border); } .trace-line:hover { background: var(--base-1); } .trace-line a { color: var(--base-6); } .trace-line .icon { opacity: .4; position: absolute; left: 10px; } .trace-line .icon svg { fill: var(--base-5); height: 16px; width: 16px; } .trace-line .icon.icon-copy { left: auto; top: auto; padding-left: 5px; display: none } .trace-line:hover .icon.icon-copy:not(.hidden) { display: inline-block } .trace-line-header { padding-left: 36px; padding-right: 10px; } .trace-file-path, .trace-file-path a { color: var(--base-6); font-size: 13px; } .trace-class { color: var(--color-error); } .trace-type { padding: 0 2px; } .trace-method { color: var(--color-error); font-weight: bold; } .trace-arguments { color: #777; font-weight: normal; padding-left: 2px; } .trace-code { background: var(--base-0); font-size: 12px; margin: 10px 10px 2px 10px; padding: 10px; overflow-x: auto; white-space: nowrap; } .trace-code ol { margin: 0; float: left; } .trace-code li { color: #969896; margin: 0; padding-left: 10px; float: left; width: 100%; } .trace-code li + li { margin-top: 5px; } .trace-code li.selected { background: var(--trace-selected-background); margin-top: 2px; } .trace-code li code { color: var(--base-6); white-space: nowrap; } .trace-as-text .stacktrace { line-height: 1.8; margin: 0 0 15px; white-space: pre-wrap; } @media (min-width: 575px) { .hidden-xs-down { display: initial; } .help-link { margin-left: 30px; } } error-handler/Resources/assets/css/exception_full.css 0000644 00000005300 15060133305 0017100 0 ustar 00 .sf-reset .traces { padding-bottom: 14px; } .sf-reset .traces li { font-size: 12px; color: #868686; padding: 5px 4px; list-style-type: decimal; margin-left: 20px; } .sf-reset #logs .traces li.error { font-style: normal; color: #AA3333; background: #f9ecec; } .sf-reset #logs .traces li.warning { font-style: normal; background: #ffcc00; } /* fix for Opera not liking empty <li> */ .sf-reset .traces li:after { content: "\00A0"; } .sf-reset .trace { border: 1px solid #D3D3D3; padding: 10px; overflow: auto; margin: 10px 0 20px; } .sf-reset .block-exception { -moz-border-radius: 16px; -webkit-border-radius: 16px; border-radius: 16px; margin-bottom: 20px; background-color: #f6f6f6; border: 1px solid #dfdfdf; padding: 30px 28px; word-wrap: break-word; overflow: hidden; } .sf-reset .block-exception div { color: #313131; font-size: 10px; } .sf-reset .block-exception-detected .illustration-exception, .sf-reset .block-exception-detected .text-exception { float: left; } .sf-reset .block-exception-detected .illustration-exception { width: 152px; } .sf-reset .block-exception-detected .text-exception { width: 670px; padding: 30px 44px 24px 46px; position: relative; } .sf-reset .text-exception .open-quote, .sf-reset .text-exception .close-quote { font-family: Arial, Helvetica, sans-serif; position: absolute; color: #C9C9C9; font-size: 8em; } .sf-reset .open-quote { top: 0; left: 0; } .sf-reset .close-quote { bottom: -0.5em; right: 50px; } .sf-reset .block-exception p { font-family: Arial, Helvetica, sans-serif; } .sf-reset .block-exception p a, .sf-reset .block-exception p a:hover { color: #565656; } .sf-reset .logs h2 { float: left; width: 654px; } .sf-reset .error-count, .sf-reset .support { float: right; width: 170px; text-align: right; } .sf-reset .error-count span { display: inline-block; background-color: #aacd4e; -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; padding: 4px; color: white; margin-right: 2px; font-size: 11px; font-weight: bold; } .sf-reset .support a { display: inline-block; -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; padding: 4px; color: #000000; margin-right: 2px; font-size: 11px; font-weight: bold; } .sf-reset .toggle { vertical-align: middle; } .sf-reset .linked ul, .sf-reset .linked li { display: inline; } .sf-reset #output-content { color: #000; font-size: 12px; } .sf-reset #traces-text pre { white-space: pre; font-size: 12px; font-family: monospace; } error-handler/Resources/assets/images/symfony-ghost.svg.php 0000644 00000017764 15060133305 0020201 0 ustar 00 <svg viewBox="0 0 136 81" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.4"><path d="M92.4 20.4a23.2 23.2 0 0 1 9 1.9 23.7 23.7 0 0 1 5.2 3 24.3 24.3 0 0 1 3.4 3.4 24.8 24.8 0 0 1 5 9.4c.5 1.7.8 3.4 1 5.2v14.5h.4l.5.2a7.4 7.4 0 0 0 2.5.2l.2-.2.6-.8.8-1.3-.2-.1a5.5 5.5 0 0 1-.8-.3 5.6 5.6 0 0 1-2.3-1.8 5.7 5.7 0 0 1-.9-1.6 6.5 6.5 0 0 1-.2-2.8 7.3 7.3 0 0 1 .5-2l.3-.3.8-.9.3-.3c.2-.2.5-.3.8-.3H120.7c.2 0 .3-.1.4 0h.4l.2.1.3.2.2-.4.3-.4.1-.1 1.2-1 .3-.2.4-.1.4-.1h.3l1.5.1.4.1.8.5.1.2 1 1.1v.2H129.4l.4-.2 1.4-.5h1.1c.3 0 .7.2 1 .4.2 0 .3.2.5.3l.2.2.5.3.4.6.1.3.4 1.4.1.4v.6a7.8 7.8 0 0 1-.1.6 9.9 9.9 0 0 1-.8 2.4 7.8 7.8 0 0 1-3 3.3 6.4 6.4 0 0 1-1 .5 6.1 6.1 0 0 1-.6.2l-.7.1h-.1a23.4 23.4 0 0 1-.2 1.7 14.3 14.3 0 0 1-.6 2.1l-.8 2a9.2 9.2 0 0 1-.4.6l-.7 1a9.1 9.1 0 0 1-2.3 2.2c-.9.5-2 .6-3 .7l-1.4.1h-.5l-.4.1a15.8 15.8 0 0 1-2.8-.1v4.2a9.7 9.7 0 0 1-.7 3.5 9.6 9.6 0 0 1-1.7 2.8 9.3 9.3 0 0 1-3 2.3 9 9 0 0 1-5.4.7 9 9 0 0 1-3-1 9.4 9.4 0 0 1-2.7-2.5 10 10 0 0 1-1 1.2 9.3 9.3 0 0 1-2 1.3 9 9 0 0 1-2.4 1 9 9 0 0 1-6.5-1.1A9.4 9.4 0 0 1 85 77V77a10.9 10.9 0 0 1-.6.6 9.3 9.3 0 0 1-2.7 2 9 9 0 0 1-6 .8 9 9 0 0 1-2.4-1 9.3 9.3 0 0 1-2.3-1.7 9.6 9.6 0 0 1-1.8-2.8 9.7 9.7 0 0 1-.8-3.7v-4a18.5 18.5 0 0 1-2.9.2l-1.2-.1c-1.9-.3-3.7-1-5.1-2.2a8.2 8.2 0 0 1-1.1-1 10.2 10.2 0 0 1-.9-1.2 15.3 15.3 0 0 1-.7-1.3 20.8 20.8 0 0 1-1.9-6.2v-.2a6.5 6.5 0 0 1-1-.3 6.1 6.1 0 0 1-.6-.3 6.6 6.6 0 0 1-.9-.6 8.2 8.2 0 0 1-2.7-3.7 10 10 0 0 1-.3-1 10.3 10.3 0 0 1-.3-1.9V47v-.4l.1-.4.6-1.4.1-.2a2 2 0 0 1 .8-.8l.3-.2.3-.2a3.2 3.2 0 0 1 1.8-.5h.4l.3.2 1.4.6.2.2.4.3.3.4.7-.7.2-.2.4-.2.6-.2h2.1l.4.2.4.2.3.2.8 1 .2-.1h.1v-.1H63l1.1.1h.3l.8.5.3.4.7 1 .2.3.1.5a11 11 0 0 1 .2 1.5c0 .8 0 1.6-.3 2.3a6 6 0 0 1-.5 1.2 5.5 5.5 0 0 1-3.3 2.5 12.3 12.3 0 0 0 1.4 3h.1l.2.1 1 .2h1.5l.5-.2H67.8l.5-.2h.1V44v-.4a26.7 26.7 0 0 1 .3-2.3 24.7 24.7 0 0 1 5.7-12.5 24.2 24.2 0 0 1 3.5-3.3 23.7 23.7 0 0 1 4.9-3 23.2 23.2 0 0 1 5.6-1.7 23.7 23.7 0 0 1 4-.3zm-.3 2a21.2 21.2 0 0 0-8 1.7 21.6 21.6 0 0 0-4.8 2.7 22.2 22.2 0 0 0-3.2 3 22.7 22.7 0 0 0-5 9.2 23.4 23.4 0 0 0-.7 4.9v15.7l-.5.1a34.3 34.3 0 0 1-1.5.3h-.2l-.4.1h-.4l-.9.2a10 10 0 0 1-1.9 0c-.5 0-1-.2-1.5-.4a1.8 1.8 0 0 1-.3-.2 2 2 0 0 1-.3-.3 5.2 5.2 0 0 1-.1-.2 9 9 0 0 1-.6-.9 13.8 13.8 0 0 1-1-2 14.3 14.3 0 0 1-.6-2 14 14 0 0 1-.1-.8v-.2h.3a12.8 12.8 0 0 0 1.4-.2 4.4 4.4 0 0 0 .3 0 3.6 3.6 0 0 0 1.1-.7 3.4 3.4 0 0 0 1.2-1.7l.2-1.2a5.1 5.1 0 0 0 0-.8 7.2 7.2 0 0 0-.1-.8l-.7-1-1.2-.2-1 .7-.1 1.3a5 5 0 0 1 .1.4v.6a1 1 0 0 1 0 .3c-.1.3-.4.4-.7.5l-1.2.4v-.7A9.9 9.9 0 0 1 60 49l.3-.6v-.2l.1-.1v-1.6l-1-1.2h-1.5l-1 1.1v.4a5.3 5.3 0 0 0-.2.6 5.5 5.5 0 0 0 0 .5c0 .7 0 1.4.3 2 0 .4.2.8.4 1.2L57 51a9.5 9.5 0 0 1-1.1-.5h-.2a2 2 0 0 1-.4-.3c-.4-.4-.5-1-.6-1.6a5.6 5.6 0 0 1 0-.5v-.5-.5l-.6-1.5-1.4-.6-.9.3s-.2 0-.3.2a2 2 0 0 1-.1 0l-.6 1.4v.7a8.5 8.5 0 0 0 .5 2c.4 1.1 1 2.1 2 2.8a4.7 4.7 0 0 0 2.1.9h1a22.8 22.8 0 0 0 .1 1 18.1 18.1 0 0 0 .8 3.8 18.2 18.2 0 0 0 1.6 3.7l1 1.3c1 1 2.3 1.6 3.7 2a11.7 11.7 0 0 0 4.8 0h.4l.5-.2.5-.1.6-.2v6.6a8 8 0 0 0 .1 1.3 7.5 7.5 0 0 0 2.4 4.3 7.2 7.2 0 0 0 2.3 1.3 7 7 0 0 0 7-1.1 7.5 7.5 0 0 0 2-2.6A7.7 7.7 0 0 0 85 72V71a8.2 8.2 0 0 0 .2 1.3c0 .7.3 1.4.6 2a7.5 7.5 0 0 0 1.7 2.3 7.3 7.3 0 0 0 2.2 1.4 7.1 7.1 0 0 0 4.6.2 7.2 7.2 0 0 0 2.4-1.2 7.5 7.5 0 0 0 2.1-2.7 7.8 7.8 0 0 0 .7-2.4V71a9.3 9.3 0 0 0 .1.6 7.6 7.6 0 0 0 .6 2.5 7.5 7.5 0 0 0 2.4 3 7.1 7.1 0 0 0 7 .8 7.3 7.3 0 0 0 2.3-1.5 7.5 7.5 0 0 0 1.6-2.3 7.6 7.6 0 0 0 .5-2l.1-1.1v-6.7l.4.1a12.2 12.2 0 0 0 2 .5 11.1 11.1 0 0 0 2.5 0h.8l1.2-.1a9.5 9.5 0 0 0 1.4-.2l.9-.3a3.5 3.5 0 0 0 .6-.4l1.2-1.4a12.2 12.2 0 0 0 .8-1.2c0-.3.2-.5.3-.7a15.9 15.9 0 0 0 .7-2l.3-1.6v-1.3l.2-.9V54.6a15.5 15.5 0 0 0 1.8 0 4.5 4.5 0 0 0 1.4-.5 5.7 5.7 0 0 0 2.5-3.2 7.6 7.6 0 0 0 .4-1.5v-.3l-.4-1.4a5.2 5.2 0 0 1-.2-.1l-.4-.4a3.8 3.8 0 0 0-.2 0 1.4 1.4 0 0 0-.5-.2l-1.4.4-.7 1.3v.7a5.7 5.7 0 0 1-.1.8l-.7 1.4a1.9 1.9 0 0 1-.5.3h-.3a9.6 9.6 0 0 1-.8.3 8.8 8.8 0 0 1-.6 0l.2-.4.2-.5.2-.3v-.4l.1-.2V50l.1-1 .1-.6v-.6a4.8 4.8 0 0 0 0-.8v-.2l-1-1.1-1.5-.2-1.1 1-.2 1.4v.1l.2.4.2.3v.4l.1 1.1v.3l.1.5v.8a9.6 9.6 0 0 1-.8-.3l-.2-.1h-.3l-.8-.1h-.2a1.6 1.6 0 0 1-.2-.2.9.9 0 0 1-.2-.2 1 1 0 0 1-.1-.5l.2-.9v-1.2l-.9-.8h-1.2l-.8.9v.3a4.8 4.8 0 0 0-.3 2l.3.9a3.5 3.5 0 0 0 1.2 1.6l1 .5.8.2 1.4.1h.4l.2.1a12.1 12.1 0 0 1-1 2.6 13.2 13.2 0 0 1-.8 1.5 9.5 9.5 0 0 1-1 1.2l-.2.3a1.7 1.7 0 0 1-.4.3 2.4 2.4 0 0 1-.7.2h-2.5a7.8 7.8 0 0 1-.6-.2l-.7-.2h-.2a14.8 14.8 0 0 1-.6-.2 23.4 23.4 0 0 1-.4-.1l-.4-.1-.3-.1V43.9a34.6 34.6 0 0 0 0-.6 23.6 23.6 0 0 0-.4-3 22.7 22.7 0 0 0-1.5-4.7 22.6 22.6 0 0 0-4.6-6.7 21.9 21.9 0 0 0-6.9-4.7 21.2 21.2 0 0 0-8.1-1.8H92zm9.1 33.7l.3.1a1 1 0 0 1 .6.8v.4a8.4 8.4 0 0 1 0 .5 8.8 8.8 0 0 1-1.6 4.2l-1 1.3A10 10 0 0 1 95 66c-1.3.3-2.7.4-4 .3a10.4 10.4 0 0 1-2.7-.8 10 10 0 0 1-3.6-2.5 9.3 9.3 0 0 1-.8-1 9 9 0 0 1-.7-1.2 8.6 8.6 0 0 1-.8-3.4V57a1 1 0 0 1 .3-.6 1 1 0 0 1 1.3-.2 1 1 0 0 1 .4.8v.4a6.5 6.5 0 0 0 .5 2.2 7 7 0 0 0 2.1 2.8l1 .6c2.6 1.6 6 1.6 8.5 0a8 8 0 0 0 1.1-.6 7.6 7.6 0 0 0 1.2-1.2 7 7 0 0 0 1-1.7 6.5 6.5 0 0 0 .4-2.5 1 1 0 0 1 .7-1h.4zM30.7 43.7c-15.5 1-28.5-6-30.1-16.4C-1.2 15.7 11.6 4 29 1.3 46.6-1.7 62.3 5.5 64 17.1c1.6 10.4-8.7 21-23.7 25a31.2 31.2 0 0 0 0 .9v.3a19 19 0 0 0 .1 1l.1.4.1.9a4.7 4.7 0 0 0 .5 1l.7 1a9.2 9.2 0 0 0 1.2 1l1.5.8.6.8-.7.6-1.1.3a11.2 11.2 0 0 1-2.6.4 8.6 8.6 0 0 1-3-.5 8.5 8.5 0 0 1-1-.4 11.2 11.2 0 0 1-1.8-1.2 13.3 13.3 0 0 1-1-1 18 18 0 0 1-.7-.6l-.4-.4a23.4 23.4 0 0 1-1.3-1.8l-.1-.1-.3-.5V45l-.3-.6v-.7zM83.1 36c3.6 0 6.5 3.2 6.5 7.1 0 4-3 7.2-6.5 7.2S76.7 47 76.7 43 79.6 36 83 36zm18 0c3.6 0 6.5 3.2 6.5 7.1 0 4-2.9 7.2-6.4 7.2S94.7 47 94.7 43s3-7.1 6.5-7.1zm-18 6.1c2 0 3.5 1.6 3.5 3.6S85 49.2 83 49.2s-3.4-1.6-3.4-3.6S81.2 42 83 42zm17.9 0c1.9 0 3.4 1.6 3.4 3.6s-1.5 3.6-3.4 3.6c-2 0-3.5-1.6-3.5-3.6S99.1 42 101 42zM17 28c-.3 1.6-1.8 5-5.2 5.8-2.5.6-4.1-.8-4.5-2.6-.4-1.9.7-3.5 2.1-4.5A3.5 3.5 0 0 1 8 24.6c-.4-2 .8-3.7 3.2-4.2 1.9-.5 3.1.2 3.4 1.5.3 1.1-.5 2.2-1.8 2.5-.9.3-1.6 0-1.7-.6a1.4 1.4 0 0 1 0-.7s.3.2 1 0c.7-.1 1-.7.9-1.2-.2-.6-1-.8-1.8-.6-1 .2-2 1-1.7 2.6.3 1 .9 1.6 1.5 1.8l.7-.2c1-.2 1.5 0 1.6.5 0 .4-.2 1-1.2 1.2a3.3 3.3 0 0 1-1.5 0c-.9.7-1.6 1.9-1.3 3.2.3 1.3 1.3 2.2 3 1.8 2.5-.7 3.8-3.7 4.2-5-.3-.5-.6-1-.7-1.6-.1-.5.1-1 .9-1.2.4 0 .7.2.8.8a2.8 2.8 0 0 1 0 1l.7 1c.6-2 1.4-4 1.7-4 .6-.2 1.5.6 1.5.6-.8.7-1.7 2.4-2.3 4.2.8.6 1.6 1 2.1 1 .5-.1.8-.6 1-1.2-.3-2.2 1-4.3 2.3-4.6.7-.2 1.3.2 1.4.8.1.5 0 1.3-.9 1.7-.2-1-.6-1.3-1-1.3-.4.1-.7 1.4-.4 2.8.2 1 .7 1.5 1.3 1.4.8-.2 1.3-1.2 1.7-2.1-.3-2.1.9-4.2 2.2-4.5.7-.2 1.2.1 1.4 1 .4 1.4-1 2.8-2.2 3.4.3.7.7 1 1.3.9 1-.3 1.6-1.5 2-2.5l-.5-3v-.3s1.6-.3 1.8.6v.1c.2-.6.7-1.2 1.3-1.4.8-.1 1.5.6 1.7 1.6.5 2.2-.5 4.4-1.8 4.7H33a31.9 31.9 0 0 0 1 5.2c-.4.1-1.8.4-2-.4l-.5-5.6c-.5 1-1.3 2.2-2.5 2.4-1 .3-1.6-.3-2-1.1-.5 1-1.3 2.1-2.4 2.4-.8.2-1.5-.1-2-1-.3.8-.9 1.5-1.5 1.7-.7.1-1.5-.3-2.4-1-.3.8-.4 1.6-.4 2.2 0 0-.7 0-.8-.4-.1-.5 0-1.5.3-2.7a10.3 10.3 0 0 1-.7-.8zm38.2-17.8l.2.9c.5 1.9.4 4.4.8 6.4 0 .6-.4 3-1.4 3.3-.2 0-.3 0-.4-.4-.1-.7 0-1.6-.3-2.6-.2-1.1-.8-1.6-1.5-1.5-.8.2-1.3 1-1.6 2l-.1-.5c-.2-1-1.8-.6-1.8-.6a6.2 6.2 0 0 1 .4 1.3l.2 1c-.2.5-.6 1-1.2 1l-.2.1a7 7 0 0 0-.1-.8c-.3-1.1-1-2-1.6-1.8a.7.7 0 0 0-.4.3c-1.3.3-2.4 2-2.1 3.9-.2.9-.6 1.7-1 1.9-.5 0-.8-.5-1.1-1.8l-.1-1.2a4 4 0 0 0 0-1.7c0-.4-.4-.7-.8-.6-.7.2-.9 1.7-.5 3.8-.2 1-.6 2-1.3 2-.4.2-.8-.2-1-1l-.2-3c1.2-.5 2-1 1.8-1.7-.1-.5-.8-.7-.8-.7s0 .7-1 1.2l-.2-1.4c-.1-.6-.4-1-1.7-.6l.4 1 .2 1.5h-1v.8c0 .3.4.3 1 .2 0 1.3 0 2.7.2 3.6.3 1.4 1.2 2 2 1.7 1-.2 1.6-1.3 2-2.3.3 1.2 1 2 1.9 1.7.7-.2 1.2-1.1 1.6-2.2.4.8 1.1 1.1 2 1 1.2-.4 1.7-1.6 1.8-2.8h.2c.6-.2 1-.6 1.3-1 0 .8 0 1.5.2 2.1.1.5.3.7.6.6.5-.1 1-.9 1-.9a4 4 0 0 1-.3-1c-.3-1.3.3-3.6 1-3.7.2 0 .3.2.5.7v.8l.2 1.5v.7c.2.7.7 1.3 1.5 1 1.3-.2 2-2.6 2.1-3.9.3.2.6.2 1 .1-.6-2.2 0-6.1-.3-7.9-.1-.4-1-.5-1.7-.5h-.4zm-21.5 12c.4 0 .7.3 1 1.1.2 1.3-.3 2.6-.9 2.8-.2 0-.7 0-1-1.2v-.4c0-1.3.4-2 1-2.2zm-5.2 1c.3 0 .6.2.6.5.2.6-.3 1.3-1.2 2-.3-1.4.1-2.3.6-2.5zm18-.4c-.5.2-1-.4-1.2-1.2-.2-1 0-2.1.7-2.5v.5c.2.7.6 1.5 1.3 1.9 0 .7-.2 1.2-.7 1.3zm10-1.6c0 .5.4.7 1 .6.8-.2 1-1 .8-1.6 0-.5-.4-1-1-.8-.5.1-1 .9-.8 1.8zm-14.3-5.5c0-.4-.5-.7-1-.5-.8.2-1 1-.9 1.5.2.6.5 1 1 .8.5 0 1.1-1 1-1.8z" fill="#fff" fill-opacity=".6"/><?= $this->addElementToGhost(); ?></svg> error-handler/Resources/assets/images/icon-minus-square-o.svg 0000644 00000000660 15060133305 0020363 0 ustar 00 <svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1344 800v64q0 14-9 23t-23 9H480q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h832q14 0 23 9t9 23zm128 448V416q0-66-47-113t-113-47H480q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113zm128-832v832q0 119-84.5 203.5T1312 1536H480q-119 0-203.5-84.5T192 1248V416q0-119 84.5-203.5T480 128h832q119 0 203.5 84.5T1600 416z"/></svg> error-handler/Resources/assets/images/symfony-logo.svg 0000644 00000001656 15060133305 0017220 0 ustar 00 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#FFF" d="M12 .9C5.8.9.9 5.8.9 12a11 11 0 1 0 22.2 0A11 11 0 0 0 12 .9zm6.5 6c-.6 0-.9-.3-.9-.8 0-.2 0-.4.2-.6l.2-.4c0-.3-.5-.4-.6-.4-1.8.1-2.3 2.5-2.7 4.4l-.2 1c1 .2 1.8 0 2.2-.3.6-.4-.2-.7-.1-1.2.1-.3.5-.5.7-.6.5 0 .7.5.7.9 0 .7-1 1.8-3 1.8l-.6-.1-.6 2.4c-.4 1.6-.8 3.8-2.4 5.7-1.4 1.7-2.9 1.9-3.5 1.9-1.2 0-1.9-.6-2-1.5 0-.8.7-1.3 1.2-1.3.6 0 1.1.5 1.1 1s-.2.6-.4.6c-.1.1-.3.2-.3.4 0 .1.1.3.4.3.5 0 .8-.3 1.1-.5 1.2-.9 1.6-2.7 2.2-5.7l.1-.7.7-3.2c-.8-.6-1.3-1.4-2.4-1.7-.6-.1-1.1.1-1.5.5-.4.5-.2 1.1.2 1.5l.7.6c.7.8 1.2 1.6 1 2.5-.3 1.5-2 2.6-4 1.9-1.8-.6-2-1.8-1.8-2.5.2-.6.6-.7 1.1-.6.5.2.6.7.6 1.2l-.1.3c-.2.1-.3.3-.3.4-.1.4.4.6.7.7.7.3 1.6-.2 1.8-.8a1 1 0 0 0-.4-1.1l-.7-.8c-.4-.4-1.1-1.4-.7-2.6.1-.5.4-.9.7-1.3a4 4 0 0 1 2.8-.6c1.2.4 1.8 1.1 2.6 1.8.5-1.2 1-2.4 1.8-3.5.9-.9 1.9-1.6 3.1-1.7 1.3.2 2.2.7 2.2 1.6 0 .4-.2 1.1-.9 1.1z"/></svg> error-handler/Resources/assets/images/chevron-right.svg 0000644 00000000424 15060133305 0017325 0 ustar 00 <svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path fill="#FFF" d="M1363 877l-742 742q-19 19-45 19t-45-19l-166-166q-19-19-19-45t19-45l531-531-531-531q-19-19-19-45t19-45L531 45q19-19 45-19t45 19l742 742q19 19 19 45t-19 45z"/></svg> error-handler/Resources/assets/images/icon-support.svg 0000644 00000001172 15060133305 0017211 0 ustar 00 <svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path fill="#FFF" d="M896 0q182 0 348 71t286 191 191 286 71 348-71 348-191 286-286 191-348 71-348-71-286-191-191-286T0 896t71-348 191-286T548 71 896 0zm0 128q-190 0-361 90l194 194q82-28 167-28t167 28l194-194q-171-90-361-90zM218 1257l194-194q-28-82-28-167t28-167L218 535q-90 171-90 361t90 361zm678 407q190 0 361-90l-194-194q-82 28-167 28t-167-28l-194 194q171 90 361 90zm0-384q159 0 271.5-112.5T1280 896t-112.5-271.5T896 512 624.5 624.5 512 896t112.5 271.5T896 1280zm484-217l194 194q90-171 90-361t-90-361l-194 194q28 82 28 167t-28 167z"/></svg> error-handler/Resources/assets/images/icon-book.svg 0000644 00000001621 15060133305 0016426 0 ustar 00 <svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path fill="#FFF" d="M1703 478q40 57 18 129l-275 906q-19 64-76.5 107.5T1247 1664H324q-77 0-148.5-53.5T76 1479q-24-67-2-127 0-4 3-27t4-37q1-8-3-21.5t-3-19.5q2-11 8-21t16.5-23.5T116 1179q23-38 45-91.5t30-91.5q3-10 .5-30t-.5-28q3-11 17-28t17-23q21-36 42-92t25-90q1-9-2.5-32t.5-28q4-13 22-30.5t22-22.5q19-26 42.5-84.5T404 411q1-8-3-25.5t-2-26.5q2-8 9-18t18-23 17-21q8-12 16.5-30.5t15-35 16-36 19.5-32 26.5-23.5 36-11.5T620 134l-1 3q38-9 51-9h761q74 0 114 56t18 130l-274 906q-36 119-71.5 153.5T1089 1408H220q-27 0-38 15-11 16-1 43 24 70 144 70h923q29 0 56-15.5t35-41.5l300-987q7-22 5-57 38 15 59 43zm-1064 2q-4 13 2 22.5t20 9.5h608q13 0 25.5-9.5T1311 480l21-64q4-13-2-22.5t-20-9.5H702q-13 0-25.5 9.5T660 416zm-83 256q-4 13 2 22.5t20 9.5h608q13 0 25.5-9.5T1228 736l21-64q4-13-2-22.5t-20-9.5H619q-13 0-25.5 9.5T577 672z"/></svg> error-handler/Resources/assets/images/icon-plus-square-o.svg 0000644 00000001016 15060133305 0020207 0 ustar 00 <svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1344 800v64q0 14-9 23t-23 9H960v352q0 14-9 23t-23 9h-64q-14 0-23-9t-9-23V896H480q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h352V416q0-14 9-23t23-9h64q14 0 23 9t9 23v352h352q14 0 23 9t9 23zm128 448V416q0-66-47-113t-113-47H480q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113zm128-832v832q0 119-84.5 203.5T1312 1536H480q-119 0-203.5-84.5T192 1248V416q0-119 84.5-203.5T480 128h832q119 0 203.5 84.5T1600 416z"/></svg> error-handler/Resources/assets/images/icon-plus-square.svg 0000644 00000000672 15060133305 0017762 0 ustar 00 <svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1408 960V832q0-26-19-45t-45-19h-320V448q0-26-19-45t-45-19H832q-26 0-45 19t-19 45v320H448q-26 0-45 19t-19 45v128q0 26 19 45t45 19h320v320q0 26 19 45t45 19h128q26 0 45-19t19-45v-320h320q26 0 45-19t19-45zm256-544v960q0 119-84.5 203.5T1376 1664H416q-119 0-203.5-84.5T128 1376V416q0-119 84.5-203.5T416 128h960q119 0 203.5 84.5T1664 416z"/></svg> error-handler/Resources/assets/images/favicon.png.base64 0000644 00000002327 15060133305 0017247 0 ustar 00 data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAgCAYAAAABtRhCAAADVUlEQVRIx82XX0jTURTHLYPyqZdefQx66CEo80+aYpoIkqzUikz6Z5klQoWUWYRIJYEUGpQ+lIr9U5dOTLdCtkmWZis3rbnC5fw/neYW002307mX/cZvP3/7o1PwwOdh95x7vnf39zvnd29AgBer2xO6DclAXiMqZAqxIiNIN/IYSUS2BPhjmGATchUxI+ADWiRhpWK7HKuHFVBFdmU5YvnI4grFGCaReF/EBH4KsZlGgj2JBTuCYBWRIYF8YoEOJ6wBt/gEs7mBbyOjQXruPLSdOgPCiEiPSUUHDoL8Ug5IUo9B/d5wrt+G7OAKNrODPuVdB6vRCIzN6SdBlpW9RIgk/1FeAXabzRlrUPVCS/JhbmwudztnGeeH9AyXBIwtmM3wLinZJZHifjHw2V+NBoRh+9ixQrbgbnaSIcl7cGea6hoXQbNe7za241oeO5Z0p42M4BV2EqP2D50wo+6HzvwC6C4sApNOR8cmOrtcnhtj2kYRyC9eBvXzKrBZrXSs72kFd1t3MoKVbMekQkEnSNKOO8fac3LpmK6l1TlGtsxmsdKFsecPYgwxst0cwROMYDXboSotg0WLBRqjY51jLYcENElXwW2XJKPydvoI2GN9T8rBtrAArYIUruBJXkFheCQYlCpQP6uk5dAQFQNaUROMSGVQFxLmkoQsxDJrhLbTZ+nvVsERME9MgPJRKV/58AsyomTSzE813WLFvWK++qI0xSfQl8k8Pg46sYRuv5t6dS+4RqxDwaa4BGjYH+NTQvKScIp9+YL/hoZh3jDtLRHtt2C3g6bmhX+CpsFBWg7ilDSPgj0lD2ncr5ev/BP8VvyAJhqVyZeUhPOrEhEFxgEtjft846Z/guQTNT89Q5P9flMLoth4F7808wKtWWKzAwNQHxrh/1vaid2F+XpYTSbQf1XA2McOmOpROnvpvMEA4tSjq1cW0sws2gCYxswY6TKkvzYnJq1NHZLnRU4BX+4U0uburvusu8Kv8iHY7qefkM4IFngJHEOUXmLEPgiGsI8YnlZILit3vSSLRTQe/MPIZva5pshNIEmyFQlCvruJKXPkCEfmePzkphXHdzZNQdoRI9KPlBAxlj/I8U97ERPS5bjGbWDFbEdqHVe5caTBeZZx2H/IMvzeN15yoQAAAABJRU5ErkJggg== error-handler/Resources/assets/images/icon-copy.svg 0000644 00000000411 15060133305 0016442 0 ustar 00 <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>