[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\Messaging\FlashMessage;
18 use TYPO3\CMS\Core\Utility\StringUtility;
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 FlashMessage[]
69 */
70 public function getStatus(): array
71 {
72 $result = [];
73 if (!$this->exists()) {
74 $status = new FlashMessage(
75 'The Install Tool can try to create it',
76 'Directory ' . $this->getRelativePathBelowSiteRoot() . ' does not exist',
77 FlashMessage::WARNING
78 );
79 $result[] = $status;
80 } else {
81 $result = $this->getSelfStatus();
82 }
83 $result = array_merge($result, $this->getChildrenStatus());
84 return $result;
85 }
86
87 /**
88 * Create a test file and delete again if directory exists
89 *
90 * @return bool TRUE if test file creation was successful
91 */
92 public function isWritable()
93 {
94 $result = true;
95 if (!$this->exists()) {
96 $result = false;
97 } elseif (!$this->canFileBeCreated()) {
98 $result = false;
99 }
100 return $result;
101 }
102
103 /**
104 * Fix structure
105 *
106 * If there is nothing to fix, returns an empty array
107 *
108 * @return FlashMessage[]
109 */
110 public function fix(): array
111 {
112 $result = $this->fixSelf();
113 foreach ($this->children as $child) {
114 /** @var $child NodeInterface */
115 $result = array_merge($result, $child->fix());
116 }
117 return $result;
118 }
119
120 /**
121 * Fix this directory:
122 *
123 * - create with correct permissions if it was not existing
124 * - if there is no "write" permissions, try to fix it
125 * - leave it alone otherwise
126 *
127 * @return FlashMessage[]
128 */
129 protected function fixSelf()
130 {
131 $result = [];
132 if (!$this->exists()) {
133 $resultCreateDirectory = $this->createDirectory();
134 $result[] = $resultCreateDirectory;
135 if ($resultCreateDirectory->getSeverity() === FlashMessage::OK &&
136 !$this->isPermissionCorrect()
137 ) {
138 $result[] = $this->fixPermission();
139 }
140 } elseif (!$this->isWritable()) {
141 // If directory is not writable, we might have permissions to fix that
142 // Try it:
143 $result[] = $this->fixPermission();
144 } elseif (!$this->isDirectory()) {
145 $fileType = @filetype($this->getAbsolutePath());
146 if ($fileType) {
147 $messageBody =
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 $messageBody =
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[] = new FlashMessage(
158 $messageBody,
159 'Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a directory',
160 FlashMessage::ERROR
161 );
162 }
163 return $result;
164 }
165
166 /**
167 * Create directory if not exists
168 *
169 * @throws Exception
170 * @return FlashMessage
171 */
172 protected function createDirectory(): FlashMessage
173 {
174 if ($this->exists()) {
175 throw new Exception(
176 'Directory ' . $this->getAbsolutePath() . ' already exists',
177 1366740091
178 );
179 }
180 $result = @mkdir($this->getAbsolutePath());
181 if ($result === true) {
182 return new FlashMessage(
183 '',
184 'Directory ' . $this->getRelativePathBelowSiteRoot() . ' successfully created.'
185 );
186 }
187 return new FlashMessage(
188 'The target directory could not be created. There is probably a'
189 . ' group or owner permission problem on the parent directory.',
190 'Directory ' . $this->getRelativePathBelowSiteRoot() . ' not created!',
191 FlashMessage::ERROR
192 );
193 }
194
195 /**
196 * Get status of directory - used in root and directory node
197 *
198 * @return FlashMessage[]
199 */
200 protected function getSelfStatus(): array
201 {
202 $result = [];
203 if (!$this->isDirectory()) {
204 $result[] = new FlashMessage(
205 'Directory ' . $this->getRelativePathBelowSiteRoot() . ' should be a directory,'
206 . ' but is of type ' . filetype($this->getAbsolutePath()),
207 $this->getRelativePathBelowSiteRoot() . ' is not a directory',
208 FlashMessage::ERROR
209 );
210 } elseif (!$this->isWritable()) {
211 $result[] = new FlashMessage(
212 'Path ' . $this->getAbsolutePath() . ' exists, but no file underneath it'
213 . ' can be created.',
214 'Directory ' . $this->getRelativePathBelowSiteRoot() . ' is not writable',
215 FlashMessage::ERROR
216 );
217 } elseif (!$this->isPermissionCorrect()) {
218 $result[] = new FlashMessage(
219 'Default configured permissions are ' . $this->getTargetPermission()
220 . ' but current permissions are ' . $this->getCurrentPermission(),
221 'Directory ' . $this->getRelativePathBelowSiteRoot() . ' permissions mismatch',
222 FlashMessage::NOTICE
223 );
224 } else {
225 $result[] = new FlashMessage(
226 'Is a directory with the configured permissions of ' . $this->getTargetPermission(),
227 'Directory ' . $this->getRelativePathBelowSiteRoot()
228 );
229 }
230 return $result;
231 }
232
233 /**
234 * Get status of children
235 *
236 * @return FlashMessage[]
237 */
238 protected function getChildrenStatus(): array
239 {
240 $result = [];
241 foreach ($this->children as $child) {
242 /** @var $child NodeInterface */
243 $result = array_merge($result, $child->getStatus());
244 }
245 return $result;
246 }
247
248 /**
249 * Create a test file and delete again - helper for isWritable
250 *
251 * @return bool TRUE if test file creation was successful
252 */
253 protected function canFileBeCreated()
254 {
255 $testFileName = StringUtility::getUniqueId('installToolTest_');
256 $result = @touch($this->getAbsolutePath() . '/' . $testFileName);
257 if ($result === true) {
258 unlink($this->getAbsolutePath() . '/' . $testFileName);
259 }
260 return $result;
261 }
262
263 /**
264 * Checks if not is a directory
265 *
266 * @return bool True if node is a directory
267 */
268 protected function isDirectory()
269 {
270 $path = $this->getAbsolutePath();
271 return !@is_link($path) && @is_dir($path);
272 }
273
274 /**
275 * Create children nodes - done in directory and root node
276 *
277 * @param array $structure Array of children
278 * @throws Exception\InvalidArgumentException
279 */
280 protected function createChildren(array $structure)
281 {
282 foreach ($structure as $child) {
283 if (!array_key_exists('type', $child)) {
284 throw new Exception\InvalidArgumentException(
285 'Child must have type',
286 1366222204
287 );
288 }
289 if (!array_key_exists('name', $child)) {
290 throw new Exception\InvalidArgumentException(
291 'Child must have name',
292 1366222205
293 );
294 }
295 $name = $child['name'];
296 foreach ($this->children as $existingChild) {
297 /** @var $existingChild NodeInterface */
298 if ($existingChild->getName() === $name) {
299 throw new Exception\InvalidArgumentException(
300 'Child name must be unique',
301 1366222206
302 );
303 }
304 }
305 $this->children[] = new $child['type']($child, $this);
306 }
307 }
308 }