[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\Install\Status;
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 array<\TYPO3\CMS\Install\Status\StatusInterface>
91 */
92 public function getStatus()
93 {
94 $result = [];
95 if (!$this->exists()) {
96 $status = new Status\WarningStatus();
97 $status->setTitle('File ' . $this->getRelativePathBelowSiteRoot() . ' does not exist');
98 $status->setMessage('By using "Try to fix errors" we can try to create it');
99 $result[] = $status;
100 } else {
101 $result = $this->getSelfStatus();
102 }
103 return $result;
104 }
105
106 /**
107 * Fix structure
108 *
109 * If there is nothing to fix, returns an empty array
110 *
111 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
112 */
113 public function fix()
114 {
115 $result = $this->fixSelf();
116 return $result;
117 }
118
119 /**
120 * Fix this node: create if not there, fix permissions
121 *
122 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
123 */
124 protected function fixSelf()
125 {
126 $result = [];
127 if (!$this->exists()) {
128 $resultCreateFile = $this->createFile();
129 $result[] = $resultCreateFile;
130 if ($resultCreateFile instanceof Status\OkStatus
131 && !is_null($this->targetContent)
132 ) {
133 $result[] = $this->setContent();
134 if (!$this->isPermissionCorrect()) {
135 $result[] = $this->fixPermission();
136 }
137 }
138 } elseif (!$this->isFile()) {
139 $status = new Status\ErrorStatus();
140 $status->setTitle('Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a file');
141 $fileType = @filetype($this->getAbsolutePath());
142 if ($fileType) {
143 $status->setMessage(
144 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a file,' .
145 ' but is of type ' . $fileType . '. This cannot be fixed automatically. Please investigate.'
146 );
147 } else {
148 $status->setMessage(
149 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a file,' .
150 ' but is of unknown type, probably because an upper level directory does not exist. Please investigate.'
151 );
152 }
153 $result[] = $status;
154 } elseif (!$this->isPermissionCorrect()) {
155 $result[] = $this->fixPermission();
156 }
157 return $result;
158 }
159
160 /**
161 * Create file if not exists
162 *
163 * @throws Exception
164 * @return \TYPO3\CMS\Install\Status\StatusInterface
165 */
166 protected function createFile()
167 {
168 if ($this->exists()) {
169 throw new Exception(
170 'File ' . $this->getRelativePathBelowSiteRoot() . ' already exists',
171 1367048077
172 );
173 }
174 $result = @touch($this->getAbsolutePath());
175 if ($result === true) {
176 $status = new Status\OkStatus();
177 $status->setTitle('File ' . $this->getRelativePathBelowSiteRoot() . ' successfully created.');
178 } else {
179 $status = new Status\ErrorStatus();
180 $status->setTitle('File ' . $this->getRelativePathBelowSiteRoot() . ' not created!');
181 $status->setMessage(
182 'The target file could not be created. There is probably a' .
183 ' group or owner permission problem on the parent directory.'
184 );
185 }
186 return $status;
187 }
188
189 /**
190 * Get status of file
191 *
192 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
193 */
194 protected function getSelfStatus()
195 {
196 $result = [];
197 if (!$this->isFile()) {
198 $status = new Status\ErrorStatus();
199 $status->setTitle($this->getRelativePathBelowSiteRoot() . ' is not a file');
200 $status->setMessage(
201 'Path ' . $this->getAbsolutePath() . ' should be a file,' .
202 ' but is of type ' . filetype($this->getAbsolutePath())
203 );
204 $result[] = $status;
205 } elseif (!$this->isWritable()) {
206 $status = new Status\NoticeStatus();
207 $status->setTitle('File ' . $this->getRelativePathBelowSiteRoot() . ' is not writable');
208 $status->setMessage(
209 'File ' . $this->getRelativePathBelowSiteRoot() . ' exists, but is not writable.'
210 );
211 $result[] = $status;
212 } elseif (!$this->isPermissionCorrect()) {
213 $status = new Status\NoticeStatus();
214 $status->setTitle('File ' . $this->getRelativePathBelowSiteRoot() . ' permissions mismatch');
215 $status->setMessage(
216 'Default configured permissions are ' . $this->getTargetPermission() .
217 ' but file permissions are ' . $this->getCurrentPermission()
218 );
219 $result[] = $status;
220 }
221 if ($this->isFile() && !$this->isContentCorrect()) {
222 $status = new Status\NoticeStatus();
223 $status->setTitle('File ' . $this->getRelativePathBelowSiteRoot() . ' content differs');
224 $status->setMessage(
225 'File content is not identical to default content. This file may have been changed manually.' .
226 ' The Install Tool will not overwrite the current version!'
227 );
228 $result[] = $status;
229 } else {
230 $status = new Status\OkStatus();
231 $status->setTitle('File ' . $this->getRelativePathBelowSiteRoot());
232 $status->setMessage(
233 'Is a file with the default content and configured permissions of ' . $this->getTargetPermission()
234 );
235 $result[] = $status;
236 }
237 return $result;
238 }
239
240 /**
241 * Compare current file content with target file content
242 *
243 * @throws Exception If file does not exist
244 * @return bool TRUE if current and target file content are identical
245 */
246 protected function isContentCorrect()
247 {
248 $absolutePath = $this->getAbsolutePath();
249 if (is_link($absolutePath) || !is_file($absolutePath)) {
250 throw new Exception(
251 'File ' . $absolutePath . ' must exist',
252 1367056363
253 );
254 }
255 $result = false;
256 if (is_null($this->targetContent)) {
257 $result = true;
258 } else {
259 $targetContentHash = md5($this->targetContent);
260 $currentContentHash = md5(file_get_contents($absolutePath));
261 if ($targetContentHash === $currentContentHash) {
262 $result = true;
263 }
264 }
265 return $result;
266 }
267
268 /**
269 * Sets content of file to target content
270 *
271 * @throws Exception If file does not exist
272 * @return \TYPO3\CMS\Install\Status\StatusInterface
273 */
274 protected function setContent()
275 {
276 $absolutePath = $this->getAbsolutePath();
277 if (is_link($absolutePath) || !is_file($absolutePath)) {
278 throw new Exception(
279 'File ' . $absolutePath . ' must exist',
280 1367060201
281 );
282 }
283 if (is_null($this->targetContent)) {
284 throw new Exception(
285 'Target content not defined for ' . $absolutePath,
286 1367060202
287 );
288 }
289 $result = @file_put_contents($absolutePath, $this->targetContent);
290 if ($result !== false) {
291 $status = new Status\OkStatus();
292 $status->setTitle('Set content to ' . $this->getRelativePathBelowSiteRoot());
293 } else {
294 $status = new Status\ErrorStatus();
295 $status->setTitle('Setting content to ' . $this->getRelativePathBelowSiteRoot() . ' failed');
296 $status->setMessage('Setting content of the file failed for unknown reasons.');
297 }
298 return $status;
299 }
300
301 /**
302 * Checks if not is a file
303 *
304 * @return bool
305 */
306 protected function isFile()
307 {
308 $path = $this->getAbsolutePath();
309 return !is_link($path) && is_file($path);
310 }
311 }