[TASK] Update Symfony packages to latest releases
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Mvc / Configuration / YamlSource.php
1 <?php
2 declare(strict_types=1);
3 namespace TYPO3\CMS\Form\Mvc\Configuration;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It originated from the Neos.Form package (www.neos.io)
9 *
10 * It is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License, either version 2
12 * of the License, or any later version.
13 *
14 * For the full copyright and license information, please read the
15 * LICENSE.txt file that was distributed with this source code.
16 *
17 * The TYPO3 project - inspiring people to share!
18 */
19
20 use Symfony\Component\Yaml\Exception\ParseException;
21 use Symfony\Component\Yaml\Yaml;
22 use TYPO3\CMS\Core\Resource\File;
23 use TYPO3\CMS\Core\Utility\ArrayUtility;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25 use TYPO3\CMS\Form\Mvc\Configuration\Exception\NoSuchFileException;
26 use TYPO3\CMS\Form\Mvc\Configuration\Exception\ParseErrorException;
27
28 /**
29 * Configuration source based on YAML files
30 *
31 * Scope: frontend / backend
32 * @internal
33 */
34 class YamlSource
35 {
36 /**
37 * Will be set if the PHP YAML Extension is installed.
38 * Having this installed massively improves YAML parsing performance.
39 *
40 * @var bool
41 * @see http://pecl.php.net/package/yaml
42 */
43 protected $usePhpYamlExtension = false;
44
45 /**
46 * Use PHP YAML Extension if installed.
47 * @internal
48 */
49 public function __construct()
50 {
51 if (extension_loaded('yaml')) {
52 $this->usePhpYamlExtension = true;
53 }
54 }
55
56 /**
57 * Loads the specified configuration files and returns its merged content
58 * as an array.
59 *
60 * @param array $filesToLoad
61 * @return array
62 * @throws ParseErrorException
63 * @throws NoSuchFileException
64 * @internal
65 */
66 public function load(array $filesToLoad): array
67 {
68 $configuration = [];
69 foreach ($filesToLoad as $fileToLoad) {
70 if ($fileToLoad instanceof File) {
71 $fileIdentifier = $fileToLoad->getIdentifier();
72 $rawYamlContent = $fileToLoad->getContents();
73 if ($rawYamlContent === false) {
74 throw new NoSuchFileException(
75 'The file "' . $fileToLoad . '" does not exist.',
76 1498802253
77 );
78 }
79 } else {
80 $fileIdentifier = $fileToLoad;
81 $fileToLoad = GeneralUtility::getFileAbsFileName($fileToLoad);
82 if (is_file($fileToLoad)) {
83 $rawYamlContent = file_get_contents($fileToLoad);
84 } else {
85 throw new NoSuchFileException(
86 'The file "' . $fileToLoad . '" does not exist.',
87 1471473378
88 );
89 }
90 }
91
92 try {
93 if ($this->usePhpYamlExtension) {
94 $loadedConfiguration = @yaml_parse($rawYamlContent);
95 if ($loadedConfiguration === false) {
96 throw new ParseErrorException(
97 'A parse error occurred while parsing file "' . $fileIdentifier . '".',
98 1391894094
99 );
100 }
101 } else {
102 $loadedConfiguration = Yaml::parse($rawYamlContent);
103 }
104
105 if (is_array($loadedConfiguration)) {
106 ArrayUtility::mergeRecursiveWithOverrule($configuration, $loadedConfiguration);
107 }
108 } catch (ParseException $exception) {
109 throw new ParseErrorException(
110 'An error occurred while parsing file "' . $fileIdentifier . '": ' . $exception->getMessage(),
111 1480195405,
112 $exception
113 );
114 }
115 }
116
117 $configuration = ArrayUtility::convertBooleanStringsToBooleanRecursive($configuration);
118 return $configuration;
119 }
120
121 /**
122 * Save the specified configuration array to the given file in YAML format.
123 *
124 * @param File|string $fileToSave The file to write to.
125 * @param array $configuration The configuration to save
126 * @internal
127 */
128 public function save($fileToSave, array $configuration)
129 {
130 $header = $this->getHeaderFromFile($fileToSave);
131 $yaml = Yaml::dump($configuration, 99, 2);
132 if ($fileToSave instanceof File) {
133 $fileToSave->setContents($header . LF . $yaml);
134 } else {
135 @file_put_contents($fileToSave, $header . LF . $yaml);
136 }
137 }
138
139 /**
140 * Read the header part from the given file. That means, every line
141 * until the first non comment line is found.
142 *
143 * @param File|string $file
144 * @return string The header of the given YAML file
145 */
146 protected function getHeaderFromFile($file): string
147 {
148 $header = '';
149 if ($file instanceof File) {
150 $fileLines = explode(LF, $file->getContents());
151 } elseif (is_file($file)) {
152 $fileLines = file($file);
153 } else {
154 return '';
155 }
156
157 foreach ($fileLines as $line) {
158 if (preg_match('/^#/', $line)) {
159 $header .= $line;
160 } else {
161 break;
162 }
163 }
164 return $header;
165 }
166 }