[BUGFIX] EXT:form - catch exceptions within page module and form engine
[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 'A parse error occurred while parsing file "' . $fileIdentifier . '". Error message: ' . $exception->getMessage(),
111 1480195405
112 );
113 }
114 }
115
116 $configuration = ArrayUtility::convertBooleanStringsToBooleanRecursive($configuration);
117 return $configuration;
118 }
119
120 /**
121 * Save the specified configuration array to the given file in YAML format.
122 *
123 * @param File|string $fileToSave The file to write to.
124 * @param array $configuration The configuration to save
125 * @internal
126 */
127 public function save($fileToSave, array $configuration)
128 {
129 $header = $this->getHeaderFromFile($fileToSave);
130 $yaml = Yaml::dump($configuration, 99, 2);
131 if ($fileToSave instanceof File) {
132 $fileToSave->setContents($header . LF . $yaml);
133 } else {
134 @file_put_contents($fileToSave, $header . LF . $yaml);
135 }
136 }
137
138 /**
139 * Read the header part from the given file. That means, every line
140 * until the first non comment line is found.
141 *
142 * @param File|string $file
143 * @return string The header of the given YAML file
144 */
145 protected function getHeaderFromFile($file): string
146 {
147 $header = '';
148 if ($file instanceof File) {
149 $fileLines = explode(LF, $file->getContents());
150 } elseif (is_file($file)) {
151 $fileLines = file($file);
152 } else {
153 return '';
154 }
155
156 foreach ($fileLines as $line) {
157 if (preg_match('/^#/', $line)) {
158 $header .= $line;
159 } else {
160 break;
161 }
162 }
163 return $header;
164 }
165 }