[TASK] Split DataHandler::copyRecord_procBasedOnFieldType
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / FileStreamWrapper.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests;
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 /**
18 * Stream wrapper for the file:// protocol
19 *
20 * Implementation details:
21 * Due to the nature of PHP, it is not possible to switch to the default handler
22 * other then restoring the default handler for file:// and registering it again
23 * around each call.
24 * It is important that the default handler is restored to allow autoloading (including)
25 * of files during the test run.
26 * For each method allowed to pass paths, the passed path is checked against the
27 * the list of paths to overlay and rewritten if needed.
28 *
29 * = Usage =
30 * <code title="Add use statements">
31 * use org\bovigo\vfs\vfsStream;
32 * use org\bovigo\vfs\visitor\vfsStreamStructureVisitor;
33 * </code>
34 *
35 * <code title="Usage in test">
36 * $root = \org\bovigo\vfs\vfsStream::setup('root');
37 * $subfolder = \org\bovigo\vfs\vfsStream::newDirectory('fileadmin');
38 * $root->addChild($subfolder);
39 * // Load fixture files and folders from disk
40 * \org\bovigo\vfs\vfsStream::copyFromFileSystem(__DIR__ . '/Fixture/Files', $subfolder, 1024*1024);
41 * FileStreamWrapper::init(PATH_site);
42 * FileStreamWrapper::registerOverlayPath('fileadmin', 'vfs://root/fileadmin');
43 *
44 * // Use file functions as usual
45 * mkdir(PATH_site . 'fileadmin/test/');
46 * $file = PATH_site . 'fileadmin/test/Foo.bar';
47 * file_put_contents($file, 'Baz');
48 * $content = file_get_contents($file);
49 * $this->assertSame('Baz', $content);
50 *
51 * $this->assertEqual(**array(file system structure as array**), vfsStream::inspect(new vfsStreamStructureVisitor())->getStructure());
52 *
53 * FileStreamWrapper::destroy();
54 * </code>
55 *
56 * @see http://www.php.net/manual/en/class.streamwrapper.php
57 */
58 class FileStreamWrapper {
59
60 /**
61 * @var resource
62 */
63 protected $dirHandle = NULL;
64
65 /**
66 * @var resource
67 */
68 protected $fileHandle = NULL;
69
70 /**
71 * Switch whether class has already been registered as stream wrapper or not
72 *
73 * @type bool
74 */
75 static protected $registered = FALSE;
76
77 /**
78 * Array of paths to overlay
79 *
80 * @var array
81 */
82 static protected $overlayPaths = array();
83
84 /**
85 * The first part of each (absolute) path that shall be ignored
86 *
87 * @var string
88 */
89 static protected $rootPath = '';
90
91 /**
92 * Initialize the stream wrapper with a root path and register itself
93 *
94 * @param $rootPath
95 * @return void
96 */
97 static public function init($rootPath) {
98 self::$rootPath = rtrim(str_replace('\\', '/', $rootPath), '/') . '/';
99 self::register();
100 }
101
102 /**
103 * Unregister the stream wrapper and reset all static members to their default values
104 * @return void
105 */
106 static public function destroy() {
107 self::$overlayPaths = array();
108 self::$rootPath = '';
109 if (self::$registered) {
110 self::restore();
111 }
112 }
113
114 /**
115 * Register a path relative to the root path (set in init) to be overlaid
116 *
117 * @param string $overlay Relative path to the root folder
118 * @param string $replace The path that should replace the overlay path
119 * @param bool $createFolder TRUE of the folder should be created (mkdir)
120 * @return void
121 */
122 static public function registerOverlayPath($overlay, $replace, $createFolder = TRUE) {
123 $overlay = trim(str_replace('\\', '/', $overlay), '/') . '/';
124 $replace = rtrim(str_replace('\\', '/', $replace), '/') . '/';
125 self::$overlayPaths[$overlay] = $replace;
126 if ($createFolder) {
127 mkdir($replace);
128 }
129 }
130
131 /**
132 * Checks and overlays a path
133 *
134 * @param string $path The path to check
135 * @return string The potentially overlaid path
136 */
137 static protected function overlayPath($path) {
138 $path = str_replace('\\', '/', $path);
139 $hasOverlay = FALSE;
140 if (strpos($path, self::$rootPath) !== 0) {
141 // Path is not below root path, ignore it
142 return $path;
143 }
144
145 $newPath = ltrim(substr($path, strlen(self::$rootPath)), '/');
146 foreach (self::$overlayPaths as $overlay => $replace) {
147 if (strpos($newPath, $overlay) === 0) {
148 $newPath = $replace . substr($newPath, strlen($overlay));
149 $hasOverlay = TRUE;
150 break;
151 }
152 }
153 return $hasOverlay ? $newPath : $path;
154 }
155
156 /**
157 * Method to register the stream wrapper
158 *
159 * If the stream is already registered the method returns silently. If there
160 * is already another stream wrapper registered for the scheme used by
161 * file:// scheme a \BadFunctionCallException will be thrown.
162 *
163 * @throws \BadFunctionCallException
164 * @return void
165 */
166 static protected function register() {
167 if (self::$registered) {
168 return;
169 }
170
171 if (@stream_wrapper_unregister('file') === FALSE) {
172 throw new \BadFunctionCallException('Cannot unregister file:// stream wrapper.', 1396340331);
173 }
174 if (@stream_wrapper_register('file', __CLASS__) === FALSE) {
175 throw new \BadFunctionCallException('A handler has already been registered for the file:// scheme.', 1396340332);
176 }
177
178 self::$registered = TRUE;
179 }
180
181 /**
182 * Restore the file handler
183 *
184 * @return void
185 */
186 static protected function restore() {
187 if (!self::$registered) {
188 return;
189 }
190 if (@stream_wrapper_restore('file') === FALSE) {
191 throw new \BadFunctionCallException('Cannot restore the default file:// stream handler.', 1396340333);
192 }
193 self::$registered = FALSE;
194 }
195
196
197 /*
198 * The following list of functions is implemented as of
199 * @see http://www.php.net/manual/en/streamwrapper.dir-closedir.php
200 */
201
202 /**
203 * Close the directory
204 *
205 * @return bool
206 */
207 public function dir_closedir() {
208 if ($this->dirHandle === NULL) {
209 return FALSE;
210 } else {
211 self::restore();
212 closedir($this->dirHandle);
213 self::register();
214 $this->dirHandle = NULL;
215 return TRUE;
216 }
217 }
218
219 /**
220 * Opens a directory for reading
221 *
222 * @param string $path
223 * @param int $options
224 * @return bool
225 */
226 public function dir_opendir($path, $options = 0) {
227 if ($this->dirHandle !== NULL) {
228 return FALSE;
229 }
230 self::restore();
231 $path = self::overlayPath($path);
232 $this->dirHandle = opendir($path);
233 self::register();
234 return $this->dirHandle !== FALSE;
235 }
236
237 /**
238 * Read a single filename of a directory
239 *
240 * @return string|bool
241 */
242 public function dir_readdir() {
243 if ($this->dirHandle === NULL) {
244 return FALSE;
245 }
246 self::restore();
247 $success = readdir($this->dirHandle);
248 self::register();
249 return $success;
250 }
251
252 /**
253 * Reset directory name pointer
254 *
255 * @return bool
256 */
257 public function dir_rewinddir() {
258 if ($this->dirHandle === NULL) {
259 return FALSE;
260 }
261 self::restore();
262 rewinddir($this->dirHandle);
263 self::register();
264 return TRUE;
265 }
266
267 /**
268 * Create a directory
269 *
270 * @param string $path
271 * @param int $mode
272 * @param int $options
273 * @return bool
274 */
275 public function mkdir($path, $mode, $options = 0) {
276 self::restore();
277 $path = self::overlayPath($path);
278 $success = mkdir($path, $mode, (bool)($options & STREAM_MKDIR_RECURSIVE));
279 self::register();
280 return $success;
281 }
282
283 /**
284 * Rename a file
285 *
286 * @param string $pathFrom
287 * @param string $pathTo
288 * @return bool
289 */
290 public function rename($pathFrom, $pathTo) {
291 self::restore();
292 $pathFrom = self::overlayPath($pathFrom);
293 $pathTo = self::overlayPath($pathTo);
294 $success = rename($pathFrom, $pathTo);
295 self::register();
296 return $success;
297 }
298
299 /**
300 * Remove a directory
301 *
302 * @param string $path
303 * @return bool
304 */
305 public function rmdir($path) {
306 self::restore();
307 $path = self::overlayPath($path);
308 $success = rmdir($path);
309 self::register();
310 return $success;
311 }
312
313 /**
314 * Retrieve the underlying resource
315 *
316 * @param int $castAs Can be STREAM_CAST_FOR_SELECT when stream_select()
317 * is calling stream_cast() or STREAM_CAST_AS_STREAM when stream_cast()
318 * is called for other uses.
319 * @return resource|bool
320 */
321 public function stream_cast($castAs) {
322 if ($this->fileHandle !== NULL && $castAs & STREAM_CAST_AS_STREAM) {
323 return $this->fileHandle;
324 } else {
325 return FALSE;
326 }
327 }
328
329 /**
330 * Close a file
331 *
332 */
333 public function stream_close() {
334 self::restore();
335 if ($this->fileHandle !== NULL) {
336 fclose($this->fileHandle);
337 $this->fileHandle = NULL;
338 }
339 self::register();
340 }
341
342 /**
343 * Test for end-of-file on a file pointer
344 *
345 * @return bool
346 */
347 public function stream_eof() {
348 if ($this->fileHandle === NULL) {
349 return FALSE;
350 }
351 self::restore();
352 $success = feof($this->fileHandle);
353 self::register();
354 return $success;
355 }
356
357 /**
358 * Flush the output
359 *
360 * @return bool
361 */
362 public function stream_flush() {
363 if ($this->fileHandle === NULL) {
364 return FALSE;
365 }
366 self::restore();
367 $success = fflush($this->fileHandle);
368 self::register();
369 return $success;
370 }
371
372 /**
373 * Advisory file locking
374 *
375 * @param int $operation
376 * @return bool
377 */
378 public function stream_lock($operation) {
379 if ($this->fileHandle === NULL) {
380 return FALSE;
381 }
382 self::restore();
383 $success = flock($this->fileHandle, $operation);
384 self::register();
385 return $success;
386 }
387
388 /**
389 * Change file options
390 *
391 * @param string $path
392 * @param int $options
393 * @param mixed $value
394 * @return bool
395 */
396 public function stream_metadata($path, $options, $value) {
397 self::restore();
398 $path = self::overlayPath($path);
399 switch ($options) {
400 case STREAM_META_TOUCH:
401 $success = touch($path, $value[0], $value[1]);
402 break;
403 case STREAM_META_OWNER_NAME:
404 // Fall through
405 case STREAM_META_OWNER:
406 $success = chown($path, $value);
407 break;
408 case STREAM_META_GROUP_NAME:
409 // Fall through
410 case STREAM_META_GROUP:
411 $success = chgrp($path, $value);
412 break;
413 case STREAM_META_ACCESS:
414 $success = chmod($path, $value);
415 break;
416 default:
417 $success = FALSE;
418 }
419 self::register();
420 return $success;
421 }
422
423 /**
424 * Open a file
425 *
426 * @param string $path
427 * @param string $mode
428 * @param int $options
429 * @param string &$opened_path
430 * @return bool
431 */
432 public function stream_open($path, $mode, $options, &$opened_path) {
433 if ($this->fileHandle !== NULL) {
434 return FALSE;
435 }
436 self::restore();
437 $path = self::overlayPath($path);
438 $this->fileHandle = fopen($path, $mode, (bool)($options & STREAM_USE_PATH));
439 self::register();
440 return $this->fileHandle !== FALSE;
441 }
442
443 /**
444 * Read from a file
445 *
446 * @param int $length
447 * @return string
448 */
449 public function stream_read($length) {
450 if ($this->fileHandle === NULL) {
451 return FALSE;
452 }
453 self::restore();
454 $content = fread($this->fileHandle, $length);
455 self::register();
456 return $content;
457 }
458
459 /**
460 * Seek to specific location in a stream
461 *
462 * @param int $offset
463 * @param int $whence = SEEK_SET
464 * @return bool
465 */
466 public function stream_seek($offset, $whence = SEEK_SET) {
467 if ($this->fileHandle === NULL) {
468 return FALSE;
469 }
470 self::restore();
471 $success = fseek($this->fileHandle, $offset, $whence);
472 self::register();
473 return $success;
474 }
475
476 /**
477 * Change stream options (not implemented)
478 *
479 * @param int $option
480 * @param int $arg1
481 * @param int $arg2
482 * @return bool
483 */
484 public function stream_set_option($option, $arg1, $arg2) {
485 return FALSE;
486 }
487
488 /**
489 * Retrieve information about a file resource
490 *
491 * @return array
492 */
493 public function stream_stat() {
494 if ($this->fileHandle === NULL) {
495 return FALSE;
496 }
497 self::restore();
498 $stats = fstat($this->fileHandle);
499 self::register();
500 return $stats;
501 }
502
503 /**
504 * Retrieve the current position of a stream
505 *
506 * @return int
507 */
508 public function stream_tell() {
509 if ($this->fileHandle === NULL) {
510 return FALSE;
511 }
512 self::restore();
513 $position = ftell($this->fileHandle);
514 self::register();
515 return $position;
516 }
517
518 /**
519 * Truncates a file to the given size
520 *
521 * @param int $size Truncate to this size
522 * @return bool
523 */
524 public function stream_truncate($size) {
525 if ($this->fileHandle === NULL) {
526 return FALSE;
527 }
528 self::restore();
529 $success = ftruncate($this->fileHandle, $size);
530 self::register();
531 return $success;
532 }
533
534 /**
535 * Write to stream
536 *
537 * @param string $data
538 * @return int
539 */
540 public function stream_write($data) {
541 if ($this->fileHandle === NULL) {
542 return FALSE;
543 }
544 self::restore();
545 $length = fwrite($this->fileHandle, $data);
546 self::register();
547 return $length;
548 }
549
550 /**
551 * Unlink a file
552 *
553 * @param string $path
554 * @return bool
555 */
556 public function unlink($path) {
557 self::restore();
558 $path = self::overlayPath($path);
559 $success = unlink($path);
560 self::register();
561 return $success;
562 }
563
564 /**
565 * Retrieve information about a file
566 *
567 * @param string $path
568 * @param int $flags
569 * @return array
570 */
571 public function url_stat($path, $flags) {
572 self::restore();
573 $path = self::overlayPath($path);
574 if ($flags & STREAM_URL_STAT_LINK) {
575 $information = @lstat($path);
576 } else {
577 $information = @stat($path);
578 }
579 self::register();
580 return $information;
581 }
582
583 }