Directory.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. <?php
  2. /**
  3. * Hoa
  4. *
  5. *
  6. * @license
  7. *
  8. * New BSD License
  9. *
  10. * Copyright © 2007-2017, Hoa community. All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions are met:
  14. * * Redistributions of source code must retain the above copyright
  15. * notice, this list of conditions and the following disclaimer.
  16. * * Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in the
  18. * documentation and/or other materials provided with the distribution.
  19. * * Neither the name of the Hoa nor the names of its contributors may be
  20. * used to endorse or promote products derived from this software without
  21. * specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  24. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  25. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  26. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
  27. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  28. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  29. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  30. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  31. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  32. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. */
  35. namespace Hoa\File;
  36. use Hoa\Stream;
  37. /**
  38. * Class \Hoa\File\Directory.
  39. *
  40. * Directory handler.
  41. *
  42. * @copyright Copyright © 2007-2017 Hoa community
  43. * @license New BSD License
  44. */
  45. class Directory extends Generic
  46. {
  47. /**
  48. * Open for reading.
  49. *
  50. * @const string
  51. */
  52. const MODE_READ = 'rb';
  53. /**
  54. * Open for reading and writing. If the directory does not exist, attempt to
  55. * create it.
  56. *
  57. * @const string
  58. */
  59. const MODE_CREATE = 'xb';
  60. /**
  61. * Open for reading and writing. If the directory does not exist, attempt to
  62. * create it recursively.
  63. *
  64. * @const string
  65. */
  66. const MODE_CREATE_RECURSIVE = 'xrb';
  67. /**
  68. * Open a directory.
  69. *
  70. * @param string $streamName Stream name.
  71. * @param string $mode Open mode, see the self::MODE* constants.
  72. * @param string $context Context ID (please, see the
  73. * \Hoa\Stream\Context class).
  74. * @param bool $wait Differ opening or not.
  75. */
  76. public function __construct(
  77. $streamName,
  78. $mode = self::MODE_READ,
  79. $context = null,
  80. $wait = false
  81. ) {
  82. $this->setMode($mode);
  83. parent::__construct($streamName, $context, $wait);
  84. return;
  85. }
  86. /**
  87. * Open the stream and return the associated resource.
  88. *
  89. * @param string $streamName Stream name (e.g. path or URL).
  90. * @param \Hoa\Stream\Context $context Context.
  91. * @return resource
  92. * @throws \Hoa\File\Exception\FileDoesNotExist
  93. * @throws \Hoa\File\Exception
  94. */
  95. protected function &_open($streamName, Stream\Context $context = null)
  96. {
  97. if (false === is_dir($streamName)) {
  98. if ($this->getMode() == self::MODE_READ) {
  99. throw new Exception\FileDoesNotExist(
  100. 'Directory %s does not exist.',
  101. 0,
  102. $streamName
  103. );
  104. } else {
  105. self::create(
  106. $streamName,
  107. $this->getMode(),
  108. null !== $context
  109. ? $context->getContext()
  110. : null
  111. );
  112. }
  113. }
  114. $out = null;
  115. return $out;
  116. }
  117. /**
  118. * Close the current stream.
  119. *
  120. * @return bool
  121. */
  122. protected function _close()
  123. {
  124. return true;
  125. }
  126. /**
  127. * Recursive copy of a directory.
  128. *
  129. * @param string $to Destination path.
  130. * @param bool $force Force to copy if the file $to already exists.
  131. * Use the \Hoa\Stream\IStream\Touchable::*OVERWRITE
  132. * constants.
  133. * @return bool
  134. * @throws \Hoa\File\Exception
  135. */
  136. public function copy($to, $force = Stream\IStream\Touchable::DO_NOT_OVERWRITE)
  137. {
  138. if (empty($to)) {
  139. throw new Exception(
  140. 'The destination path (to copy) is empty.',
  141. 1
  142. );
  143. }
  144. $from = $this->getStreamName();
  145. $fromLength = strlen($from) + 1;
  146. $finder = new Finder();
  147. $finder->in($from);
  148. self::create($to, self::MODE_CREATE_RECURSIVE);
  149. foreach ($finder as $file) {
  150. $relative = substr($file->getPathname(), $fromLength);
  151. $_to = $to . DS . $relative;
  152. if (true === $file->isDir()) {
  153. self::create($_to, self::MODE_CREATE);
  154. continue;
  155. }
  156. // This is not possible to do `$file->open()->copy();
  157. // $file->close();` because the file will be opened in read and
  158. // write mode. In a PHAR for instance, this operation is
  159. // forbidden. So a special care must be taken to open file in read
  160. // only mode.
  161. $handle = null;
  162. if (true === $file->isFile()) {
  163. $handle = new Read($file->getPathname());
  164. } elseif (true === $file->isDir()) {
  165. $handle = new Directory($file->getPathName());
  166. } elseif (true === $file->isLink()) {
  167. $handle = new Link\Read($file->getPathName());
  168. }
  169. if (null !== $handle) {
  170. $handle->copy($_to, $force);
  171. $handle->close();
  172. }
  173. }
  174. return true;
  175. }
  176. /**
  177. * Delete a directory.
  178. *
  179. * @return bool
  180. */
  181. public function delete()
  182. {
  183. $from = $this->getStreamName();
  184. $finder = new Finder();
  185. $finder->in($from)
  186. ->childFirst();
  187. foreach ($finder as $file) {
  188. $file->open()->delete();
  189. $file->close();
  190. }
  191. if (null === $this->getStreamContext()) {
  192. return @rmdir($from);
  193. }
  194. return @rmdir($from, $this->getStreamContext()->getContext());
  195. }
  196. /**
  197. * Create a directory.
  198. *
  199. * @param string $name Directory name.
  200. * @param string $mode Create mode. Please, see the self::MODE_CREATE*
  201. * constants.
  202. * @param string $context Context ID (please, see the
  203. * \Hoa\Stream\Context class).
  204. * @return bool
  205. * @throws \Hoa\File\Exception
  206. */
  207. public static function create(
  208. $name,
  209. $mode = self::MODE_CREATE_RECURSIVE,
  210. $context = null
  211. ) {
  212. if (true === is_dir($name)) {
  213. return true;
  214. }
  215. if (empty($name)) {
  216. return false;
  217. }
  218. if (null !== $context) {
  219. if (false === Stream\Context::contextExists($context)) {
  220. throw new Exception(
  221. 'Context %s was not previously declared, cannot retrieve ' .
  222. 'this context.',
  223. 2,
  224. $context
  225. );
  226. } else {
  227. $context = Stream\Context::getInstance($context);
  228. }
  229. }
  230. if (null === $context) {
  231. return @mkdir(
  232. $name,
  233. 0755,
  234. self::MODE_CREATE_RECURSIVE === $mode
  235. );
  236. }
  237. return @mkdir(
  238. $name,
  239. 0755,
  240. self::MODE_CREATE_RECURSIVE === $mode,
  241. $context->getContext()
  242. );
  243. }
  244. }