[CLEANUP] The correct case must be used for standard PHP types in phpdoc
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / FolderStructure / DirectoryNode.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\Utility\StringUtility;
18 use TYPO3\CMS\Install\Status;
19
20 /**
21 * A directory
22 */
23 class DirectoryNode extends AbstractNode implements NodeInterface
24 {
25 /**
26 * @var null|int Default for directories is octal 02775 == decimal 1533
27 */
28 protected $targetPermission = '2775';
29
30 /**
31 * Implement constructor
32 *
33 * @param array $structure Structure array
34 * @param NodeInterface $parent Parent object
35 * @throws Exception\InvalidArgumentException
36 */
37 public function __construct(array $structure, NodeInterface $parent = null)
38 {
39 if (is_null($parent)) {
40 throw new Exception\InvalidArgumentException(
41 'Node must have parent',
42 1366222203
43 );
44 }
45 $this->parent = $parent;
46
47 // Ensure name is a single segment, but not a path like foo/bar or an absolute path /foo
48 if (strstr($structure['name'], '/') !== false) {
49 throw new Exception\InvalidArgumentException(
50 'Directory name must not contain forward slash',
51 1366226639
52 );
53 }
54 $this->name = $structure['name'];
55
56 if (isset($structure['targetPermission'])) {
57 $this->setTargetPermission($structure['targetPermission']);
58 }
59
60 if (array_key_exists('children', $structure)) {
61 $this->createChildren($structure['children']);
62 }
63 }
64
65 /**
66 * Get own status and status of child objects
67 *
68 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
69 */
70 public function getStatus()
71 {
72 $result = [];
73 if (!$this->exists()) {
74 $status = new Status\WarningStatus();
75 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' does not exist');
76 $status->setMessage('The Install Tool can try to create it');
77 $result[] = $status;
78 } else {
79 $result = $this->getSelfStatus();
80 }
81 $result = array_merge($result, $this->getChildrenStatus());
82 return $result;
83 }
84
85 /**
86 * Create a test file and delete again if directory exists
87 *
88 * @return bool TRUE if test file creation was successful
89 */
90 public function isWritable()
91 {
92 $result = true;
93 if (!$this->exists()) {
94 $result = false;
95 } elseif (!$this->canFileBeCreated()) {
96 $result = false;
97 }
98 return $result;
99 }
100
101 /**
102 * Fix structure
103 *
104 * If there is nothing to fix, returns an empty array
105 *
106 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
107 */
108 public function fix()
109 {
110 $result = $this->fixSelf();
111 foreach ($this->children as $child) {
112 /** @var $child NodeInterface */
113 $result = array_merge($result, $child->fix());
114 }
115 return $result;
116 }
117
118 /**
119 * Fix this directory:
120 *
121 * - create with correct permissions if it was not existing
122 * - if there is no "write" permissions, try to fix it
123 * - leave it alone otherwise
124 *
125 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
126 */
127 protected function fixSelf()
128 {
129 $result = [];
130 if (!$this->exists()) {
131 $resultCreateDirectory = $this->createDirectory();
132 $result[] = $resultCreateDirectory;
133 if ($resultCreateDirectory instanceof Status\OkStatus &&
134 !$this->isPermissionCorrect()
135 ) {
136 $result[] = $this->fixPermission();
137 }
138 } elseif (!$this->isWritable()) {
139 // If directory is not writable, we might have permissions to fix that
140 // Try it:
141 $result[] = $this->fixPermission();
142 } elseif (!$this->isDirectory()) {
143 $status = new Status\ErrorStatus();
144 $status->setTitle('Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a directory');
145 $fileType = @filetype($this->getAbsolutePath());
146 if ($fileType) {
147 $status->setMessage(
148 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
149 ' but is of type ' . $fileType . '. This cannot be fixed automatically. Please investigate.'
150 );
151 } else {
152 $status->setMessage(
153 'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
154 ' but is of unknown type, probably because an upper level directory does not exist. Please investigate.'
155 );
156 }
157 $result[] = $status;
158 }
159 return $result;
160 }
161
162 /**
163 * Create directory if not exists
164 *
165 * @throws Exception
166 * @return \TYPO3\CMS\Install\Status\StatusInterface
167 */
168 protected function createDirectory()
169 {
170 if ($this->exists()) {
171 throw new Exception(
172 'Directory ' . $this->getAbsolutePath() . ' already exists',
173 1366740091
174 );
175 }
176 $result = @mkdir($this->getAbsolutePath());
177 if ($result === true) {
178 $status = new Status\OkStatus();
179 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' successfully created.');
180 } else {
181 $status = new Status\ErrorStatus();
182 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' not created!');
183 $status->setMessage(
184 'The target directory could not be created. There is probably a' .
185 ' group or owner permission problem on the parent directory.'
186 );
187 }
188 return $status;
189 }
190
191 /**
192 * Get status of directory - used in root and directory node
193 *
194 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
195 */
196 protected function getSelfStatus()
197 {
198 $result = [];
199 if (!$this->isDirectory()) {
200 $status = new Status\ErrorStatus();
201 $status->setTitle($this->getRelativePathBelowSiteRoot() . ' is not a directory');
202 $status->setMessage(
203 'Directory ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,' .
204 ' but is of type ' . filetype($this->getAbsolutePath())
205 );
206 $result[] = $status;
207 } elseif (!$this->isWritable()) {
208 $status = new Status\ErrorStatus();
209 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' is not writable');
210 $status->setMessage(
211 'Path ' . $this->getAbsolutePath() . ' exists, but no file underneath it' .
212 ' can be created.'
213 );
214 $result[] = $status;
215 } elseif (!$this->isPermissionCorrect()) {
216 $status = new Status\NoticeStatus();
217 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot() . ' permissions mismatch');
218 $status->setMessage(
219 'Default configured permissions are ' . $this->getTargetPermission() .
220 ' but current permissions are ' . $this->getCurrentPermission()
221 );
222 $result[] = $status;
223 } else {
224 $status = new Status\OkStatus();
225 $status->setTitle('Directory ' . $this->getRelativePathBelowSiteRoot());
226 $status->setMessage(
227 'Is a directory with the configured permissions of ' . $this->getTargetPermission()
228 );
229 $result[] = $status;
230 }
231 return $result;
232 }
233
234 /**
235 * Get status of children
236 *
237 * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
238 */
239 protected function getChildrenStatus()
240 {
241 $result = [];
242 foreach ($this->children as $child) {
243 /** @var $child NodeInterface */
244 $result = array_merge($result, $child->getStatus());
245 }
246 return $result;
247 }
248
249 /**
250 * Create a test file and delete again - helper for isWritable
251 *
252 * @return bool TRUE if test file creation was successful
253 */
254 protected function canFileBeCreated()
255 {
256 $testFileName = StringUtility::getUniqueId('installToolTest_');
257 $result = @touch($this->getAbsolutePath() . '/' . $testFileName);
258 if ($result === true) {
259 unlink($this->getAbsolutePath() . '/' . $testFileName);
260 }
261 return $result;
262 }
263
264 /**
265 * Checks if not is a directory
266 *
267 * @return bool True if node is a directory
268 */
269 protected function isDirectory()
270 {
271 $path = $this->getAbsolutePath();
272 return !@is_link($path) && @is_dir($path);
273 }
274
275 /**
276 * Create children nodes - done in directory and root node
277 *
278 * @param array $structure Array of children
279 * @throws Exception\InvalidArgumentException
280 */
281 protected function createChildren(array $structure)
282 {
283 foreach ($structure as $child) {
284 if (!array_key_exists('type', $child)) {
285 throw new Exception\InvalidArgumentException(
286 'Child must have type',
287 1366222204
288 );
289 }
290 if (!array_key_exists('name', $child)) {
291 throw new Exception\InvalidArgumentException(
292 'Child must have name',
293 1366222205
294 );
295 }
296 $name = $child['name'];
297 foreach ($this->children as $existingChild) {
298 /** @var $existingChild NodeInterface */
299 if ($existingChild->getName() === $name) {
300 throw new Exception\InvalidArgumentException(
301 'Child name must be unique',
302 1366222206
303 );
304 }
305 }
306 $this->children[] = new $child['type']($child, $this);
307 }
308 }
309 }