[CLEANUP] The correct case must be used for standard PHP types in phpdoc
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / FolderStructure / FileNode.php
1 <?php
2 namespace TYPO3\CMS\Install\FolderStructure;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Messaging\FlashMessage;
18
19 /**
20 * A file
21 */
22 class FileNode extends AbstractNode implements NodeInterface
23 {
24 /**
25 * @var null|int Default for files is octal 0664 == decimal 436
26 */
27 protected $targetPermission = '0664';
28
29 /**
30 * @var string|null Target content of file. If NULL, target content is ignored
31 */
32 protected $targetContent = null;
33
34 /**
35 * Implement constructor
36 *
37 * @param array $structure Structure array
38 * @param NodeInterface $parent Parent object
39 * @throws Exception\InvalidArgumentException
40 */
41 public function __construct(array $structure, NodeInterface $parent = null)
42 {
43 if (is_null($parent)) {
44 throw new Exception\InvalidArgumentException(
45 'File node must have parent',
46 1366927513
47 );
48 }
49 $this->parent = $parent;
50
51 // Ensure name is a single segment, but not a path like foo/bar or an absolute path /foo
52 if (strstr($structure['name'], '/') !== false) {
53 throw new Exception\InvalidArgumentException(
54 'File name must not contain forward slash',
55 1366222207
56 );
57 }
58 $this->name = $structure['name'];
59
60 if (isset($structure['targetPermission'])) {
61 $this->setTargetPermission($structure['targetPermission']);
62 }
63
64 if (isset($structure['targetContent']) && isset($structure['targetContentFile'])) {
65 throw new Exception\InvalidArgumentException(
66 'Either targetContent or targetContentFile can be set, but not both',
67 1380364361
68 );
69 }
70
71 if (isset($structure['targetContent'])) {
72 $this->targetContent = $structure['targetContent'];
73 }
74 if (isset($structure['targetContentFile'])) {
75 if (!is_readable($structure['targetContentFile'])) {
76 throw new Exception\InvalidArgumentException(
77 'targetContentFile ' . $structure['targetContentFile'] . ' does not exist or is not readable',
78 1380364362
79 );
80 }
81 $this->targetContent = file_get_contents($structure['targetContentFile']);
82 }
83 }
84
85 /**
86 * Get own status
87 * Returns warning if file not exists
88 * Returns error if file exists but content is not as expected (can / shouldn't be fixed)
89 *
90 * @return FlashMessage[]
91 */
92 public function getStatus(): array
93 {
94 $result = [];
95 if (!$this->exists()) {
96 $result[] = new FlashMessage(
97 'By using "Try to fix errors" we can try to create it',
98 'File ' . $this->getRelativePathBelowSiteRoot() . ' does not exist',
99 FlashMessage::WARNING
100 );
101 } else {
102 $result = $this->getSelfStatus();
103 }
104 return $result;
105 }
106
107 /**
108 * Fix structure
109 *
110 * If there is nothing to fix, returns an empty array
111 *
112 * @return FlashMessage[]
113 */
114 public function fix(): array
115 {
116 $result = $this->fixSelf();
117 return $result;
118 }
119
120 /**
121 * Fix this node: create if not there, fix permissions
122 *
123 * @return FlashMessage[]
124 */
125 protected function fixSelf(): array
126 {
127 $result = [];
128 if (!$this->exists()) {
129 $resultCreateFile = $this->createFile();
130 $result[] = $resultCreateFile;
131 if ($resultCreateFile->getSeverity() === FlashMessage::OK
132 && !is_null($this->targetContent)
133 ) {
134 $result[] = $this->setContent();
135 if (!$this->isPermissionCorrect()) {
136 $result[] = $this->fixPermission();
137 }
138 }
139 } elseif (!$this->isFile()) {
140 $fileType = @filetype($this->getAbsolutePath());
141 if ($fileType) {
142 $messageBody =
143 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a file,' .
144 ' but is of type ' . $fileType . '. This cannot be fixed automatically. Please investigate.'
145 ;
146 } else {
147 $messageBody =
148 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a file,' .
149 ' but is of unknown type, probably because an upper level directory does not exist. Please investigate.'
150 ;
151 }
152 $result[] = new FlashMessage(
153 $messageBody,
154 'Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a file',
155 FlashMessage::ERROR
156 );
157 } elseif (!$this->isPermissionCorrect()) {
158 $result[] = $this->fixPermission();
159 }
160 return $result;
161 }
162
163 /**
164 * Create file if not exists
165 *
166 * @throws Exception
167 * @return FlashMessage
168 */
169 protected function createFile(): FlashMessage
170 {
171 if ($this->exists()) {
172 throw new Exception(
173 'File ' . $this->getRelativePathBelowSiteRoot() . ' already exists',
174 1367048077
175 );
176 }
177 $result = @touch($this->getAbsolutePath());
178 if ($result === true) {
179 return new FlashMessage(
180 '',
181 'File ' . $this->getRelativePathBelowSiteRoot() . ' successfully created.'
182 );
183 }
184 return new FlashMessage(
185 'The target file could not be created. There is probably a'
186 . ' group or owner permission problem on the parent directory.',
187 'File ' . $this->getRelativePathBelowSiteRoot() . ' not created!',
188 FlashMessage::ERROR
189 );
190 }
191
192 /**
193 * Get status of file
194 *
195 * @return FlashMessage[]
196 */
197 protected function getSelfStatus(): array
198 {
199 $result = [];
200 if (!$this->isFile()) {
201 $result[] = new FlashMessage(
202 'Path ' . $this->getAbsolutePath() . ' should be a file,'
203 . ' but is of type ' . filetype($this->getAbsolutePath()),
204 $this->getRelativePathBelowSiteRoot() . ' is not a file',
205 FlashMessage::ERROR
206 );
207 } elseif (!$this->isWritable()) {
208 $result[] = new FlashMessage(
209 'File ' . $this->getRelativePathBelowSiteRoot() . ' exists, but is not writable.',
210 'File ' . $this->getRelativePathBelowSiteRoot() . ' is not writable',
211 FlashMessage::NOTICE
212 );
213 } elseif (!$this->isPermissionCorrect()) {
214 $result[] = new FlashMessage(
215 'Default configured permissions are ' . $this->getTargetPermission()
216 . ' but file permissions are ' . $this->getCurrentPermission(),
217 'File ' . $this->getRelativePathBelowSiteRoot() . ' permissions mismatch',
218 FlashMessage::NOTICE
219 );
220 }
221 if ($this->isFile() && !$this->isContentCorrect()) {
222 $result[] = new FlashMessage(
223 'File content is not identical to default content. This file may have been changed manually.'
224 . ' The Install Tool will not overwrite the current version!',
225 'File ' . $this->getRelativePathBelowSiteRoot() . ' content differs',
226 FlashMessage::NOTICE
227 );
228 } else {
229 $result[] = new FlashMessage(
230 'Is a file with the default content and configured permissions of ' . $this->getTargetPermission(),
231 'File ' . $this->getRelativePathBelowSiteRoot()
232 );
233 }
234 return $result;
235 }
236
237 /**
238 * Compare current file content with target file content
239 *
240 * @throws Exception If file does not exist
241 * @return bool TRUE if current and target file content are identical
242 */
243 protected function isContentCorrect()
244 {
245 $absolutePath = $this->getAbsolutePath();
246 if (is_link($absolutePath) || !is_file($absolutePath)) {
247 throw new Exception(
248 'File ' . $absolutePath . ' must exist',
249 1367056363
250 );
251 }
252 $result = false;
253 if (is_null($this->targetContent)) {
254 $result = true;
255 } else {
256 $targetContentHash = md5($this->targetContent);
257 $currentContentHash = md5(file_get_contents($absolutePath));
258 if ($targetContentHash === $currentContentHash) {
259 $result = true;
260 }
261 }
262 return $result;
263 }
264
265 /**
266 * Sets content of file to target content
267 *
268 * @throws Exception If file does not exist
269 * @return FlashMessage
270 */
271 protected function setContent(): FlashMessage
272 {
273 $absolutePath = $this->getAbsolutePath();
274 if (is_link($absolutePath) || !is_file($absolutePath)) {
275 throw new Exception(
276 'File ' . $absolutePath . ' must exist',
277 1367060201
278 );
279 }
280 if (is_null($this->targetContent)) {
281 throw new Exception(
282 'Target content not defined for ' . $absolutePath,
283 1367060202
284 );
285 }
286 $result = @file_put_contents($absolutePath, $this->targetContent);
287 if ($result !== false) {
288 return new FlashMessage(
289 '',
290 'Set content to ' . $this->getRelativePathBelowSiteRoot()
291 );
292 }
293 return new FlashMessage(
294 'Setting content of the file failed for unknown reasons.',
295 'Setting content to ' . $this->getRelativePathBelowSiteRoot() . ' failed',
296 FlashMessage::ERROR
297 );
298 }
299
300 /**
301 * Checks if not is a file
302 *
303 * @return bool
304 */
305 protected function isFile()
306 {
307 $path = $this->getAbsolutePath();
308 return !is_link($path) && is_file($path);
309 }
310 }