PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/python/php/sdk/third_party/vfsstream/vendor/mikey179/vfsStream/src/main/php/org/bovigo/vfs/vfsStreamWrapper.php

http://googleappengine.googlecode.com/
PHP | 985 lines | 541 code | 114 blank | 330 comment | 94 complexity | 85933e7f3259dc05c59cdb5723345ab2 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause, GPL-2.0, LGPL-2.1, MIT
  1. <?php
  2. /**
  3. * This file is part of vfsStream.
  4. *
  5. * For the full copyright and license information, please view the LICENSE
  6. * file that was distributed with this source code.
  7. *
  8. * @package org\bovigo\vfs
  9. */
  10. namespace org\bovigo\vfs;
  11. /**
  12. * Stream wrapper to mock file system requests.
  13. */
  14. class vfsStreamWrapper
  15. {
  16. /**
  17. * open file for reading
  18. */
  19. const READ = 'r';
  20. /**
  21. * truncate file
  22. */
  23. const TRUNCATE = 'w';
  24. /**
  25. * set file pointer to end, append new data
  26. */
  27. const APPEND = 'a';
  28. /**
  29. * set file pointer to start, overwrite existing data
  30. */
  31. const WRITE = 'x';
  32. /**
  33. * set file pointer to start, overwrite existing data; or create file if
  34. * does not exist
  35. */
  36. const WRITE_NEW = 'c';
  37. /**
  38. * file mode: read only
  39. */
  40. const READONLY = 0;
  41. /**
  42. * file mode: write only
  43. */
  44. const WRITEONLY = 1;
  45. /**
  46. * file mode: read and write
  47. */
  48. const ALL = 2;
  49. /**
  50. * switch whether class has already been registered as stream wrapper or not
  51. *
  52. * @type bool
  53. */
  54. protected static $registered = false;
  55. /**
  56. * root content
  57. *
  58. * @type vfsStreamContent
  59. */
  60. protected static $root;
  61. /**
  62. * disk space quota
  63. *
  64. * @type Quota
  65. */
  66. private static $quota;
  67. /**
  68. * file mode: read only, write only, all
  69. *
  70. * @type int
  71. */
  72. protected $mode;
  73. /**
  74. * shortcut to file container
  75. *
  76. * @type vfsStreamFile
  77. */
  78. protected $content;
  79. /**
  80. * shortcut to directory container
  81. *
  82. * @type vfsStreamDirectory
  83. */
  84. protected $dir;
  85. /**
  86. * shortcut to directory container iterator
  87. *
  88. * @type vfsStreamDirectory
  89. */
  90. protected $dirIterator;
  91. /**
  92. * method to register the stream wrapper
  93. *
  94. * Please be aware that a call to this method will reset the root element
  95. * to null.
  96. * If the stream is already registered the method returns silently. If there
  97. * is already another stream wrapper registered for the scheme used by
  98. * vfsStream a vfsStreamException will be thrown.
  99. *
  100. * @throws vfsStreamException
  101. */
  102. public static function register()
  103. {
  104. self::$root = null;
  105. self::$quota = Quota::unlimited();
  106. if (true === self::$registered) {
  107. return;
  108. }
  109. if (@stream_wrapper_register(vfsStream::SCHEME, __CLASS__) === false) {
  110. throw new vfsStreamException('A handler has already been registered for the ' . vfsStream::SCHEME . ' protocol.');
  111. }
  112. self::$registered = true;
  113. }
  114. /**
  115. * sets the root content
  116. *
  117. * @param vfsStreamContainer $root
  118. * @return vfsStreamContainer
  119. */
  120. public static function setRoot(vfsStreamContainer $root)
  121. {
  122. self::$root = $root;
  123. return self::$root;
  124. }
  125. /**
  126. * returns the root content
  127. *
  128. * @return vfsStreamContainer
  129. */
  130. public static function getRoot()
  131. {
  132. return self::$root;
  133. }
  134. /**
  135. * sets quota for disk space
  136. *
  137. * @param Quota $quota
  138. * @since 1.1.0
  139. */
  140. public static function setQuota(Quota $quota)
  141. {
  142. self::$quota = $quota;
  143. }
  144. /**
  145. * returns content for given path
  146. *
  147. * @param string $path
  148. * @return vfsStreamContent
  149. */
  150. protected function getContent($path)
  151. {
  152. if (null === self::$root) {
  153. return null;
  154. }
  155. if (self::$root->getName() === $path) {
  156. return self::$root;
  157. }
  158. if ($this->isInRoot($path) && self::$root->hasChild($path) === true) {
  159. return self::$root->getChild($path);
  160. }
  161. return null;
  162. }
  163. /**
  164. * helper method to detect whether given path is in root path
  165. *
  166. * @param string $path
  167. * @return bool
  168. */
  169. private function isInRoot($path)
  170. {
  171. return substr($path, 0, strlen(self::$root->getName())) === self::$root->getName();
  172. }
  173. /**
  174. * returns content for given path but only when it is of given type
  175. *
  176. * @param string $path
  177. * @param int $type
  178. * @return vfsStreamContent
  179. */
  180. protected function getContentOfType($path, $type)
  181. {
  182. $content = $this->getContent($path);
  183. if (null !== $content && $content->getType() === $type) {
  184. return $content;
  185. }
  186. return null;
  187. }
  188. /**
  189. * splits path into its dirname and the basename
  190. *
  191. * @param string $path
  192. * @return string[]
  193. */
  194. protected function splitPath($path)
  195. {
  196. $lastSlashPos = strrpos($path, '/');
  197. if (false === $lastSlashPos) {
  198. return array('dirname' => '', 'basename' => $path);
  199. }
  200. return array('dirname' => substr($path, 0, $lastSlashPos),
  201. 'basename' => substr($path, $lastSlashPos + 1)
  202. );
  203. }
  204. /**
  205. * helper method to resolve a path from /foo/bar/. to /foo/bar
  206. *
  207. * @param string $path
  208. * @return string
  209. */
  210. protected function resolvePath($path)
  211. {
  212. $newPath = array();
  213. foreach (explode('/', $path) as $pathPart) {
  214. if ('.' !== $pathPart) {
  215. if ('..' !== $pathPart) {
  216. $newPath[] = $pathPart;
  217. } else {
  218. array_pop($newPath);
  219. }
  220. }
  221. }
  222. return implode('/', $newPath);
  223. }
  224. /**
  225. * open the stream
  226. *
  227. * @param string $path the path to open
  228. * @param string $mode mode for opening
  229. * @param string $options options for opening
  230. * @param string $opened_path full path that was actually opened
  231. * @return bool
  232. */
  233. public function stream_open($path, $mode, $options, $opened_path)
  234. {
  235. $extended = ((strstr($mode, '+') !== false) ? (true) : (false));
  236. $mode = str_replace(array('b', '+'), '', $mode);
  237. if (in_array($mode, array('r', 'w', 'a', 'x', 'c')) === false) {
  238. if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
  239. trigger_error('Illegal mode ' . $mode . ', use r, w, a, x or c, flavoured with b and/or +', E_USER_WARNING);
  240. }
  241. return false;
  242. }
  243. $this->mode = $this->calculateMode($mode, $extended);
  244. $path = $this->resolvePath(vfsStream::path($path));
  245. $this->content = $this->getContentOfType($path, vfsStreamContent::TYPE_FILE);
  246. if (null !== $this->content) {
  247. if (self::WRITE === $mode) {
  248. if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
  249. trigger_error('File ' . $path . ' already exists, can not open with mode x', E_USER_WARNING);
  250. }
  251. return false;
  252. }
  253. if (
  254. (self::TRUNCATE === $mode || self::APPEND === $mode) &&
  255. $this->content->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false
  256. ) {
  257. return false;
  258. }
  259. if (self::TRUNCATE === $mode) {
  260. $this->content->openWithTruncate();
  261. } elseif (self::APPEND === $mode) {
  262. $this->content->openForAppend();
  263. } else {
  264. if (!$this->content->isReadable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup())) {
  265. if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
  266. trigger_error('Permission denied', E_USER_WARNING);
  267. }
  268. return false;
  269. }
  270. $this->content->open();
  271. }
  272. return true;
  273. }
  274. $content = $this->createFile($path, $mode, $options);
  275. if (false === $content) {
  276. return false;
  277. }
  278. $this->content = $content;
  279. return true;
  280. }
  281. /**
  282. * creates a file at given path
  283. *
  284. * @param string $path the path to open
  285. * @param string $mode mode for opening
  286. * @param string $options options for opening
  287. * @return bool
  288. */
  289. private function createFile($path, $mode = null, $options = null)
  290. {
  291. $names = $this->splitPath($path);
  292. if (empty($names['dirname']) === true) {
  293. if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
  294. trigger_error('File ' . $names['basename'] . ' does not exist', E_USER_WARNING);
  295. }
  296. return false;
  297. }
  298. $dir = $this->getContentOfType($names['dirname'], vfsStreamContent::TYPE_DIR);
  299. if (null === $dir) {
  300. if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
  301. trigger_error('Directory ' . $names['dirname'] . ' does not exist', E_USER_WARNING);
  302. }
  303. return false;
  304. } elseif ($dir->hasChild($names['basename']) === true) {
  305. if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
  306. trigger_error('Directory ' . $names['dirname'] . ' already contains a director named ' . $names['basename'], E_USER_WARNING);
  307. }
  308. return false;
  309. }
  310. if (self::READ === $mode) {
  311. if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
  312. trigger_error('Can not open non-existing file ' . $path . ' for reading', E_USER_WARNING);
  313. }
  314. return false;
  315. }
  316. if ($dir->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
  317. if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
  318. trigger_error('Can not create new file in non-writable path ' . $names['dirname'], E_USER_WARNING);
  319. }
  320. return false;
  321. }
  322. return vfsStream::newFile($names['basename'])->at($dir);
  323. }
  324. /**
  325. * calculates the file mode
  326. *
  327. * @param string $mode opening mode: r, w, a or x
  328. * @param bool $extended true if + was set with opening mode
  329. * @return int
  330. */
  331. protected function calculateMode($mode, $extended)
  332. {
  333. if (true === $extended) {
  334. return self::ALL;
  335. }
  336. if (self::READ === $mode) {
  337. return self::READONLY;
  338. }
  339. return self::WRITEONLY;
  340. }
  341. /**
  342. * closes the stream
  343. *
  344. * @see https://github.com/mikey179/vfsStream/issues/40
  345. */
  346. public function stream_close()
  347. {
  348. $this->content->lock($this, LOCK_UN);
  349. }
  350. /**
  351. * read the stream up to $count bytes
  352. *
  353. * @param int $count amount of bytes to read
  354. * @return string
  355. */
  356. public function stream_read($count)
  357. {
  358. if (self::WRITEONLY === $this->mode) {
  359. return '';
  360. }
  361. if ($this->content->isReadable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
  362. return '';
  363. }
  364. return $this->content->read($count);
  365. }
  366. /**
  367. * writes data into the stream
  368. *
  369. * @param string $data
  370. * @return int amount of bytes written
  371. */
  372. public function stream_write($data)
  373. {
  374. if (self::READONLY === $this->mode) {
  375. return 0;
  376. }
  377. if ($this->content->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
  378. return 0;
  379. }
  380. if (self::$quota->isLimited()) {
  381. $data = substr($data, 0, self::$quota->spaceLeft(self::$root->sizeSummarized()));
  382. }
  383. return $this->content->write($data);
  384. }
  385. /**
  386. * truncates a file to a given length
  387. *
  388. * @param int $size length to truncate file to
  389. * @return bool
  390. * @since 1.1.0
  391. */
  392. public function stream_truncate($size)
  393. {
  394. if (self::READONLY === $this->mode) {
  395. return false;
  396. }
  397. if ($this->content->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
  398. return false;
  399. }
  400. if ($this->content->getType() !== vfsStreamContent::TYPE_FILE) {
  401. return false;
  402. }
  403. if (self::$quota->isLimited() && $this->content->size() < $size) {
  404. $maxSize = self::$quota->spaceLeft(self::$root->sizeSummarized());
  405. if (0 === $maxSize) {
  406. return false;
  407. }
  408. if ($size > $maxSize) {
  409. $size = $maxSize;
  410. }
  411. }
  412. return $this->content->truncate($size);
  413. }
  414. /**
  415. * sets metadata like owner, user or permissions
  416. *
  417. * @param string $path
  418. * @param int $option
  419. * @param mixed $var
  420. * @return bool
  421. * @since 1.1.0
  422. */
  423. public function stream_metadata($path, $option, $var)
  424. {
  425. $path = $this->resolvePath(vfsStream::path($path));
  426. $content = $this->getContent($path);
  427. switch ($option) {
  428. case STREAM_META_TOUCH:
  429. if (null === $content) {
  430. $content = $this->createFile($path);
  431. }
  432. if (isset($var[0])) {
  433. $content->lastModified($var[0]);
  434. }
  435. if (isset($var[1])) {
  436. $content->lastAccessed($var[1]);
  437. }
  438. return true;
  439. case STREAM_META_OWNER_NAME:
  440. return false;
  441. case STREAM_META_OWNER:
  442. if (null === $content) {
  443. return false;
  444. }
  445. return $this->doPermChange($path,
  446. $content,
  447. function() use ($content, $var)
  448. {
  449. $content->chown($var);
  450. }
  451. );
  452. case STREAM_META_GROUP_NAME:
  453. return false;
  454. case STREAM_META_GROUP:
  455. if (null === $content) {
  456. return false;
  457. }
  458. return $this->doPermChange($path,
  459. $content,
  460. function() use ($content, $var)
  461. {
  462. $content->chgrp($var);
  463. }
  464. );
  465. case STREAM_META_ACCESS:
  466. if (null === $content) {
  467. return false;
  468. }
  469. return $this->doPermChange($path,
  470. $content,
  471. function() use ($content, $var)
  472. {
  473. $content->chmod($var);
  474. }
  475. );
  476. default:
  477. return false;
  478. }
  479. }
  480. /**
  481. * executes given permission change when necessary rights allow such a change
  482. *
  483. * @param string $path
  484. * @param vfsStreamAbstractContent $content
  485. * @param Closure $change
  486. * @return bool
  487. */
  488. private function doPermChange($path, vfsStreamAbstractContent $content, \Closure $change)
  489. {
  490. if (!$content->isOwnedByUser(vfsStream::getCurrentUser())) {
  491. return false;
  492. }
  493. if (self::$root->getName() !== $path) {
  494. $names = $this->splitPath($path);
  495. $parent = $this->getContent($names['dirname']);
  496. if (!$parent->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup())) {
  497. return false;
  498. }
  499. }
  500. $change();
  501. return true;
  502. }
  503. /**
  504. * checks whether stream is at end of file
  505. *
  506. * @return bool
  507. */
  508. public function stream_eof()
  509. {
  510. return $this->content->eof();
  511. }
  512. /**
  513. * returns the current position of the stream
  514. *
  515. * @return int
  516. */
  517. public function stream_tell()
  518. {
  519. return $this->content->getBytesRead();
  520. }
  521. /**
  522. * seeks to the given offset
  523. *
  524. * @param int $offset
  525. * @param int $whence
  526. * @return bool
  527. */
  528. public function stream_seek($offset, $whence)
  529. {
  530. return $this->content->seek($offset, $whence);
  531. }
  532. /**
  533. * flushes unstored data into storage
  534. *
  535. * @return bool
  536. */
  537. public function stream_flush()
  538. {
  539. return true;
  540. }
  541. /**
  542. * returns status of stream
  543. *
  544. * @return array
  545. */
  546. public function stream_stat()
  547. {
  548. $fileStat = array('dev' => 0,
  549. 'ino' => 0,
  550. 'mode' => $this->content->getType() | $this->content->getPermissions(),
  551. 'nlink' => 0,
  552. 'uid' => $this->content->getUser(),
  553. 'gid' => $this->content->getGroup(),
  554. 'rdev' => 0,
  555. 'size' => $this->content->size(),
  556. 'atime' => $this->content->fileatime(),
  557. 'mtime' => $this->content->filemtime(),
  558. 'ctime' => $this->content->filectime(),
  559. 'blksize' => -1,
  560. 'blocks' => -1
  561. );
  562. return array_merge(array_values($fileStat), $fileStat);
  563. }
  564. /**
  565. * retrieve the underlaying resource
  566. *
  567. * Please note that this method always returns false as there is no
  568. * underlaying resource to return.
  569. *
  570. * @param int $cast_as
  571. * @since 0.9.0
  572. * @see https://github.com/mikey179/vfsStream/issues/3
  573. * @return bool
  574. */
  575. public function stream_cast($cast_as)
  576. {
  577. return false;
  578. }
  579. /**
  580. * set lock status for stream
  581. *
  582. * @param int $operation
  583. * @return bool
  584. * @since 0.10.0
  585. * @see https://github.com/mikey179/vfsStream/issues/6
  586. * @see https://github.com/mikey179/vfsStream/issues/31
  587. * @see https://github.com/mikey179/vfsStream/issues/40
  588. */
  589. public function stream_lock($operation)
  590. {
  591. if ((LOCK_NB & $operation) == LOCK_NB) {
  592. $operation = $operation - LOCK_NB;
  593. }
  594. return $this->content->lock($this, $operation);
  595. }
  596. /**
  597. * sets options on the stream
  598. *
  599. * @param int $option key of option to set
  600. * @param int $arg1
  601. * @param int $arg2
  602. * @return bool
  603. * @since 0.10.0
  604. * @see https://github.com/mikey179/vfsStream/issues/15
  605. * @see http://www.php.net/manual/streamwrapper.stream-set-option.php
  606. */
  607. public function stream_set_option($option, $arg1, $arg2)
  608. {
  609. switch ($option) {
  610. case STREAM_OPTION_BLOCKING:
  611. // break omitted
  612. case STREAM_OPTION_READ_TIMEOUT:
  613. // break omitted
  614. case STREAM_OPTION_WRITE_BUFFER:
  615. // break omitted
  616. default:
  617. // nothing to do here
  618. }
  619. return false;
  620. }
  621. /**
  622. * remove the data under the given path
  623. *
  624. * @param string $path
  625. * @return bool
  626. */
  627. public function unlink($path)
  628. {
  629. $realPath = $this->resolvePath(vfsStream::path($path));
  630. $content = $this->getContent($realPath);
  631. if (null === $content) {
  632. return false;
  633. }
  634. if ($content->getType() !== vfsStreamContent::TYPE_FILE) {
  635. trigger_error('unlink(' . $path . '): Operation not permitted', E_USER_WARNING);
  636. return false;
  637. }
  638. return $this->doUnlink($realPath);
  639. }
  640. /**
  641. * removes a path
  642. *
  643. * @param string $path
  644. * @return bool
  645. */
  646. protected function doUnlink($path)
  647. {
  648. if (self::$root->getName() === $path) {
  649. // delete root? very brave. :)
  650. self::$root = null;
  651. clearstatcache();
  652. return true;
  653. }
  654. $names = $this->splitPath($path);
  655. $content = $this->getContent($names['dirname']);
  656. if (!$content->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup())) {
  657. return false;
  658. }
  659. clearstatcache();
  660. return $content->removeChild($names['basename']);
  661. }
  662. /**
  663. * rename from one path to another
  664. *
  665. * @param string $path_from
  666. * @param string $path_to
  667. * @return bool
  668. * @author Benoit Aubuchon
  669. */
  670. public function rename($path_from, $path_to)
  671. {
  672. $srcRealPath = $this->resolvePath(vfsStream::path($path_from));
  673. $dstRealPath = $this->resolvePath(vfsStream::path($path_to));
  674. $srcContent = $this->getContent($srcRealPath);
  675. if (null == $srcContent) {
  676. trigger_error(' No such file or directory', E_USER_WARNING);
  677. return false;
  678. }
  679. $dstNames = $this->splitPath($dstRealPath);
  680. $dstParentContent = $this->getContent($dstNames['dirname']);
  681. if (null == $dstParentContent) {
  682. trigger_error('No such file or directory', E_USER_WARNING);
  683. return false;
  684. }
  685. if (!$dstParentContent->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup())) {
  686. trigger_error('Permission denied', E_USER_WARNING);
  687. return false;
  688. }
  689. if ($dstParentContent->getType() !== vfsStreamContent::TYPE_DIR) {
  690. trigger_error('Target is not a directory', E_USER_WARNING);
  691. return false;
  692. }
  693. // remove old source first, so we can rename later
  694. // (renaming first would lead to not being able to remove the old path)
  695. if (!$this->doUnlink($srcRealPath)) {
  696. return false;
  697. }
  698. $dstContent = $srcContent;
  699. // Renaming the filename
  700. $dstContent->rename($dstNames['basename']);
  701. // Copying to the destination
  702. $dstParentContent->addChild($dstContent);
  703. return true;
  704. }
  705. /**
  706. * creates a new directory
  707. *
  708. * @param string $path
  709. * @param int $mode
  710. * @param int $options
  711. * @return bool
  712. */
  713. public function mkdir($path, $mode, $options)
  714. {
  715. $umask = vfsStream::umask();
  716. if (0 < $umask) {
  717. $permissions = $mode & ~$umask;
  718. } else {
  719. $permissions = $mode;
  720. }
  721. $path = $this->resolvePath(vfsStream::path($path));
  722. if (null !== $this->getContent($path)) {
  723. trigger_error('mkdir(): Path vfs://' . $path . ' exists', E_USER_WARNING);
  724. return false;
  725. }
  726. if (null === self::$root) {
  727. self::$root = vfsStream::newDirectory($path, $permissions);
  728. return true;
  729. }
  730. $maxDepth = count(explode('/', $path));
  731. $names = $this->splitPath($path);
  732. $newDirs = $names['basename'];
  733. $dir = null;
  734. $i = 0;
  735. while ($dir === null && $i < $maxDepth) {
  736. $dir = $this->getContent($names['dirname']);
  737. $names = $this->splitPath($names['dirname']);
  738. if (null == $dir) {
  739. $newDirs = $names['basename'] . '/' . $newDirs;
  740. }
  741. $i++;
  742. }
  743. if (null === $dir
  744. || $dir->getType() !== vfsStreamContent::TYPE_DIR
  745. || $dir->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
  746. return false;
  747. }
  748. $recursive = ((STREAM_MKDIR_RECURSIVE & $options) !== 0) ? (true) : (false);
  749. if (strpos($newDirs, '/') !== false && false === $recursive) {
  750. return false;
  751. }
  752. vfsStream::newDirectory($newDirs, $permissions)->at($dir);
  753. return true;
  754. }
  755. /**
  756. * removes a directory
  757. *
  758. * @param string $path
  759. * @param int $options
  760. * @return bool
  761. * @todo consider $options with STREAM_MKDIR_RECURSIVE
  762. */
  763. public function rmdir($path, $options)
  764. {
  765. $path = $this->resolvePath(vfsStream::path($path));
  766. $child = $this->getContentOfType($path, vfsStreamContent::TYPE_DIR);
  767. if (null === $child) {
  768. return false;
  769. }
  770. // can only remove empty directories
  771. if (count($child->getChildren()) > 0) {
  772. return false;
  773. }
  774. if (self::$root->getName() === $path) {
  775. // delete root? very brave. :)
  776. self::$root = null;
  777. clearstatcache();
  778. return true;
  779. }
  780. $names = $this->splitPath($path);
  781. $dir = $this->getContentOfType($names['dirname'], vfsStreamContent::TYPE_DIR);
  782. if ($dir->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
  783. return false;
  784. }
  785. clearstatcache();
  786. return $dir->removeChild($child->getName());
  787. }
  788. /**
  789. * opens a directory
  790. *
  791. * @param string $path
  792. * @param int $options
  793. * @return bool
  794. */
  795. public function dir_opendir($path, $options)
  796. {
  797. $path = $this->resolvePath(vfsStream::path($path));
  798. $this->dir = $this->getContentOfType($path, vfsStreamContent::TYPE_DIR);
  799. if (null === $this->dir || $this->dir->isReadable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
  800. return false;
  801. }
  802. $this->dirIterator = $this->dir->getIterator();
  803. return true;
  804. }
  805. /**
  806. * reads directory contents
  807. *
  808. * @return string
  809. */
  810. public function dir_readdir()
  811. {
  812. $dir = $this->dirIterator->current();
  813. if (null === $dir) {
  814. return false;
  815. }
  816. $this->dirIterator->next();
  817. return $dir->getName();
  818. }
  819. /**
  820. * reset directory iteration
  821. *
  822. * @return bool
  823. */
  824. public function dir_rewinddir()
  825. {
  826. return $this->dirIterator->rewind();
  827. }
  828. /**
  829. * closes directory
  830. *
  831. * @return bool
  832. */
  833. public function dir_closedir()
  834. {
  835. $this->dirIterator = null;
  836. return true;
  837. }
  838. /**
  839. * returns status of url
  840. *
  841. * @param string $path path of url to return status for
  842. * @param int $flags flags set by the stream API
  843. * @return array
  844. */
  845. public function url_stat($path, $flags)
  846. {
  847. $content = $this->getContent($this->resolvePath(vfsStream::path($path)));
  848. if (null === $content) {
  849. if (($flags & STREAM_URL_STAT_QUIET) != STREAM_URL_STAT_QUIET) {
  850. trigger_error(' No such file or directory: ' . $path, E_USER_WARNING);
  851. }
  852. return false;
  853. }
  854. $fileStat = array('dev' => 0,
  855. 'ino' => 0,
  856. 'mode' => $content->getType() | $content->getPermissions(),
  857. 'nlink' => 0,
  858. 'uid' => $content->getUser(),
  859. 'gid' => $content->getGroup(),
  860. 'rdev' => 0,
  861. 'size' => $content->size(),
  862. 'atime' => $content->fileatime(),
  863. 'mtime' => $content->filemtime(),
  864. 'ctime' => $content->filectime(),
  865. 'blksize' => -1,
  866. 'blocks' => -1
  867. );
  868. return array_merge(array_values($fileStat), $fileStat);
  869. }
  870. }
  871. ?>