|
13 | 13 | use Symfony\Component\Console\Command\Command; |
14 | 14 | use Symfony\Component\Console\Input\InputInterface; |
15 | 15 | use Symfony\Component\Console\Output\OutputInterface; |
| 16 | +use Symfony\Component\Yaml\Tag\TaggedValue; |
16 | 17 | use Symfony\Component\Yaml\Yaml; |
17 | 18 |
|
18 | 19 | /** |
@@ -64,22 +65,98 @@ protected function configure(): void |
64 | 65 | */ |
65 | 66 | protected function execute(InputInterface $input, OutputInterface $output): int |
66 | 67 | { |
67 | | - $errors = Yaml::parse( |
| 68 | + $parseFlags = 0; |
| 69 | + if (defined(Yaml::class . '::PARSE_CONSTANT')) { |
| 70 | + $parseFlags |= Yaml::PARSE_CONSTANT; |
| 71 | + } |
| 72 | + if (defined(Yaml::class . '::PARSE_CUSTOM_TAGS')) { |
| 73 | + $parseFlags |= Yaml::PARSE_CUSTOM_TAGS; |
| 74 | + } |
| 75 | + $errors = (array) Yaml::parse( |
68 | 76 | $this->file->fileGetContents($this->fileList->getErrorSchema()), |
69 | | - Yaml::PARSE_CONSTANT |
| 77 | + $parseFlags |
70 | 78 | ); |
71 | 79 |
|
| 80 | + $errors = $this->normalizeYamlData($errors); |
72 | 81 | $errors = $this->groupErrors($errors); |
73 | | - |
74 | | - $docs = $this->generateDocs($errors); |
| 82 | + $docs = $this->generateDocs($errors); |
75 | 83 |
|
76 | 84 | $this->file->filePutContents($this->fileList->getErrorDistConfig(), $docs); |
77 | | - |
78 | 85 | $output->writeln(sprintf('File %s was generated', $this->fileList->getErrorDistConfig())); |
79 | 86 |
|
80 | 87 | return Cli::SUCCESS; |
81 | 88 | } |
82 | 89 |
|
| 90 | + /** |
| 91 | + * Recursively normalizes Symfony YAML TaggedValue objects into PHP-native values. |
| 92 | + * |
| 93 | + * Handles the following YAML tags: |
| 94 | + * - !env: resolves environment variables. |
| 95 | + * - !include: parses and normalizes included YAML files. |
| 96 | + * - !php/const: resolves PHP constants (e.g. !php/const:\PDO::ATTR_ERRMODE). |
| 97 | + * - Other or unknown tags: recursively normalize their values. |
| 98 | + * |
| 99 | + * Ensures all YAML data is converted to scalars or arrays suitable for safe merging. |
| 100 | + * |
| 101 | + * @param mixed $data The parsed YAML data (array, scalar, or TaggedValue). |
| 102 | + * @return mixed The normalized data structure. |
| 103 | + * |
| 104 | + * @SuppressWarnings("PHPMD.NPathComplexity") |
| 105 | + * @SuppressWarnings("PHPMD.CyclomaticComplexity") Method is intentionally complex due to tag resolution logic. |
| 106 | + */ |
| 107 | + private function normalizeYamlData(mixed $data): mixed |
| 108 | + { |
| 109 | + if ($data instanceof TaggedValue) { |
| 110 | + $tag = $data->getTag(); // e.g. "php/const:\PDO::MYSQL_ATTR_LOCAL_INFILE" |
| 111 | + $value = $data->getValue(); |
| 112 | + |
| 113 | + // Handle php/const tags (Symfony strips leading '!') |
| 114 | + if (str_starts_with($tag, 'php/const:')) { |
| 115 | + // Extract the constant name |
| 116 | + $constName = substr($tag, strlen('php/const:')); |
| 117 | + $constName = ltrim($constName, '\\'); |
| 118 | + |
| 119 | + // Resolve the constant name to its value if defined |
| 120 | + $constKey = defined($constName) ? constant($constName) : $constName; |
| 121 | + |
| 122 | + // Handle YAML quirk where ": 1" is parsed literally |
| 123 | + $raw = is_string($value) ? $value : (string)$value; |
| 124 | + $cleanVal = str_replace([':', ' '], '', $raw); |
| 125 | + $constVal = is_numeric($cleanVal) ? (int)$cleanVal : $cleanVal; |
| 126 | + |
| 127 | + return [$constKey => $constVal]; |
| 128 | + } |
| 129 | + |
| 130 | + // Handle !env |
| 131 | + if ($tag === 'env') { |
| 132 | + $envValue = getenv((string)$value); |
| 133 | + return $envValue !== false ? $envValue : null; |
| 134 | + } |
| 135 | + |
| 136 | + // Handle !include |
| 137 | + if ($tag === 'include') { |
| 138 | + if (file_exists((string)$value)) { |
| 139 | + $included = Yaml::parseFile((string)$value); |
| 140 | + return $this->normalizeYamlData($included); |
| 141 | + } |
| 142 | + return null; |
| 143 | + } |
| 144 | + |
| 145 | + // Default — recursively normalize nested tagged structures |
| 146 | + $normalized = $this->normalizeYamlData($value); |
| 147 | + return is_array($normalized) ? $normalized : [$normalized]; |
| 148 | + } |
| 149 | + |
| 150 | + // Recursively normalize arrays |
| 151 | + if (is_array($data)) { |
| 152 | + foreach ($data as $key => $value) { |
| 153 | + $data[$key] = $this->normalizeYamlData($value); |
| 154 | + } |
| 155 | + } |
| 156 | + |
| 157 | + return $data; |
| 158 | + } |
| 159 | + |
83 | 160 | /** |
84 | 161 | * Groups errors by type and stage |
85 | 162 | * |
|
0 commit comments