|
11 | 11 | use Magento\CloudPatches\Filesystem\Filesystem; |
12 | 12 | use Magento\CloudPatches\Filesystem\FileSystemException; |
13 | 13 | use Symfony\Component\Yaml\Yaml; |
| 14 | +use Symfony\Component\Yaml\Tag\TaggedValue; |
14 | 15 | use Symfony\Component\Yaml\Exception\ParseException; |
15 | 16 |
|
16 | 17 | /** |
@@ -60,11 +61,92 @@ public function read(): array |
60 | 61 | if (!$this->filesystem->exists($path)) { |
61 | 62 | $this->config = []; |
62 | 63 | } else { |
63 | | - $parseFlag = defined(Yaml::class . '::PARSE_CONSTANT') ? Yaml::PARSE_CONSTANT : 0; |
64 | | - $this->config = (array)Yaml::parse($this->filesystem->get($path), $parseFlag); |
| 64 | + $flags = 0; |
| 65 | + if (defined(Yaml::class . '::PARSE_CONSTANT')) { |
| 66 | + $flags |= Yaml::PARSE_CONSTANT; |
| 67 | + } |
| 68 | + if (defined(Yaml::class . '::PARSE_CUSTOM_TAGS')) { |
| 69 | + $flags |= Yaml::PARSE_CUSTOM_TAGS; |
| 70 | + } |
| 71 | + $this->config = (array) Yaml::parse( |
| 72 | + $this->filesystem->get($path), |
| 73 | + $flags |
| 74 | + ); |
| 75 | + |
| 76 | + $this->config = $this->normalizeYamlData($this->config); |
65 | 77 | } |
66 | 78 | } |
67 | 79 |
|
68 | 80 | return $this->config; |
69 | 81 | } |
| 82 | + |
| 83 | + /** |
| 84 | + * Recursively normalizes Symfony YAML TaggedValue objects into PHP-native values. |
| 85 | + * |
| 86 | + * Handles the following YAML tags: |
| 87 | + * - !env: resolves environment variables. |
| 88 | + * - !include: parses and normalizes included YAML files. |
| 89 | + * - !php/const: resolves PHP constants (e.g. !php/const:\PDO::ATTR_ERRMODE). |
| 90 | + * - Other or unknown tags: recursively normalize their values. |
| 91 | + * |
| 92 | + * Ensures all YAML data is converted to scalars or arrays suitable for safe merging. |
| 93 | + * |
| 94 | + * @param mixed $data The parsed YAML data (array, scalar, or TaggedValue). |
| 95 | + * @return mixed The normalized data structure. |
| 96 | + * |
| 97 | + * @SuppressWarnings("PHPMD.NPathComplexity") |
| 98 | + * @SuppressWarnings("PHPMD.CyclomaticComplexity") Method is intentionally complex due to tag resolution logic. |
| 99 | + */ |
| 100 | + private function normalizeYamlData(mixed $data): mixed |
| 101 | + { |
| 102 | + if ($data instanceof TaggedValue) { |
| 103 | + $tag = $data->getTag(); // e.g. "php/const:\PDO::MYSQL_ATTR_LOCAL_INFILE" |
| 104 | + $value = $data->getValue(); |
| 105 | + |
| 106 | + // Handle php/const tags (Symfony strips leading '!') |
| 107 | + if (str_starts_with($tag, 'php/const:')) { |
| 108 | + // Extract the constant name |
| 109 | + $constName = substr($tag, strlen('php/const:')); |
| 110 | + $constName = ltrim($constName, '\\'); |
| 111 | + |
| 112 | + // Resolve the constant name to its value if defined |
| 113 | + $constKey = defined($constName) ? constant($constName) : $constName; |
| 114 | + |
| 115 | + // Handle YAML quirk where ": 1" is parsed literally |
| 116 | + $raw = is_string($value) ? $value : (string)$value; |
| 117 | + $cleanVal = str_replace([':', ' '], '', $raw); |
| 118 | + $constVal = is_numeric($cleanVal) ? (int)$cleanVal : $cleanVal; |
| 119 | + |
| 120 | + return [$constKey => $constVal]; |
| 121 | + } |
| 122 | + |
| 123 | + // Handle !env |
| 124 | + if ($tag === 'env') { |
| 125 | + $envValue = getenv((string)$value); |
| 126 | + return $envValue !== false ? $envValue : null; |
| 127 | + } |
| 128 | + |
| 129 | + // Handle !include |
| 130 | + if ($tag === 'include') { |
| 131 | + if (file_exists((string)$value)) { |
| 132 | + $included = Yaml::parseFile((string)$value); |
| 133 | + return $this->normalizeYamlData($included); |
| 134 | + } |
| 135 | + return null; |
| 136 | + } |
| 137 | + |
| 138 | + // Default — recursively normalize nested tagged structures |
| 139 | + $normalized = $this->normalizeYamlData($value); |
| 140 | + return is_array($normalized) ? $normalized : [$normalized]; |
| 141 | + } |
| 142 | + |
| 143 | + // Recursively normalize arrays |
| 144 | + if (is_array($data)) { |
| 145 | + foreach ($data as $key => $value) { |
| 146 | + $data[$key] = $this->normalizeYamlData($value); |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + return $data; |
| 151 | + } |
70 | 152 | } |
0 commit comments