PageRenderTime 26ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/league/flysystem/src/Adapter/Local.php

https://gitlab.com/madwanz64/laravel
PHP | 533 lines | 333 code | 83 blank | 117 comment | 27 complexity | 2642b8301c26d1de448c4a9b1f90bb34 MD5 | raw file
  1. <?php
  2. namespace League\Flysystem\Adapter;
  3. use DirectoryIterator;
  4. use FilesystemIterator;
  5. use finfo as Finfo;
  6. use League\Flysystem\Config;
  7. use League\Flysystem\Exception;
  8. use League\Flysystem\NotSupportedException;
  9. use League\Flysystem\UnreadableFileException;
  10. use League\Flysystem\Util;
  11. use LogicException;
  12. use RecursiveDirectoryIterator;
  13. use RecursiveIteratorIterator;
  14. use SplFileInfo;
  15. class Local extends AbstractAdapter
  16. {
  17. /**
  18. * @var int
  19. */
  20. const SKIP_LINKS = 0001;
  21. /**
  22. * @var int
  23. */
  24. const DISALLOW_LINKS = 0002;
  25. /**
  26. * @var array
  27. */
  28. protected static $permissions = [
  29. 'file' => [
  30. 'public' => 0644,
  31. 'private' => 0600,
  32. ],
  33. 'dir' => [
  34. 'public' => 0755,
  35. 'private' => 0700,
  36. ],
  37. ];
  38. /**
  39. * @var string
  40. */
  41. protected $pathSeparator = DIRECTORY_SEPARATOR;
  42. /**
  43. * @var array
  44. */
  45. protected $permissionMap;
  46. /**
  47. * @var int
  48. */
  49. protected $writeFlags;
  50. /**
  51. * @var int
  52. */
  53. private $linkHandling;
  54. /**
  55. * Constructor.
  56. *
  57. * @param string $root
  58. * @param int $writeFlags
  59. * @param int $linkHandling
  60. * @param array $permissions
  61. *
  62. * @throws LogicException
  63. */
  64. public function __construct($root, $writeFlags = LOCK_EX, $linkHandling = self::DISALLOW_LINKS, array $permissions = [])
  65. {
  66. $root = is_link($root) ? realpath($root) : $root;
  67. $this->permissionMap = array_replace_recursive(static::$permissions, $permissions);
  68. $this->ensureDirectory($root);
  69. if ( ! is_dir($root) || ! is_readable($root)) {
  70. throw new LogicException('The root path ' . $root . ' is not readable.');
  71. }
  72. $this->setPathPrefix($root);
  73. $this->writeFlags = $writeFlags;
  74. $this->linkHandling = $linkHandling;
  75. }
  76. /**
  77. * Ensure the root directory exists.
  78. *
  79. * @param string $root root directory path
  80. *
  81. * @return void
  82. *
  83. * @throws Exception in case the root directory can not be created
  84. */
  85. protected function ensureDirectory($root)
  86. {
  87. if ( ! is_dir($root)) {
  88. $umask = umask(0);
  89. if ( ! @mkdir($root, $this->permissionMap['dir']['public'], true)) {
  90. $mkdirError = error_get_last();
  91. }
  92. umask($umask);
  93. clearstatcache(false, $root);
  94. if ( ! is_dir($root)) {
  95. $errorMessage = isset($mkdirError['message']) ? $mkdirError['message'] : '';
  96. throw new Exception(sprintf('Impossible to create the root directory "%s". %s', $root, $errorMessage));
  97. }
  98. }
  99. }
  100. /**
  101. * @inheritdoc
  102. */
  103. public function has($path)
  104. {
  105. $location = $this->applyPathPrefix($path);
  106. return file_exists($location);
  107. }
  108. /**
  109. * @inheritdoc
  110. */
  111. public function write($path, $contents, Config $config)
  112. {
  113. $location = $this->applyPathPrefix($path);
  114. $this->ensureDirectory(dirname($location));
  115. if (($size = file_put_contents($location, $contents, $this->writeFlags)) === false) {
  116. return false;
  117. }
  118. $type = 'file';
  119. $result = compact('contents', 'type', 'size', 'path');
  120. if ($visibility = $config->get('visibility')) {
  121. $result['visibility'] = $visibility;
  122. $this->setVisibility($path, $visibility);
  123. }
  124. return $result;
  125. }
  126. /**
  127. * @inheritdoc
  128. */
  129. public function writeStream($path, $resource, Config $config)
  130. {
  131. $location = $this->applyPathPrefix($path);
  132. $this->ensureDirectory(dirname($location));
  133. $stream = fopen($location, 'w+b');
  134. if ( ! $stream || stream_copy_to_stream($resource, $stream) === false || ! fclose($stream)) {
  135. return false;
  136. }
  137. $type = 'file';
  138. $result = compact('type', 'path');
  139. if ($visibility = $config->get('visibility')) {
  140. $this->setVisibility($path, $visibility);
  141. $result['visibility'] = $visibility;
  142. }
  143. return $result;
  144. }
  145. /**
  146. * @inheritdoc
  147. */
  148. public function readStream($path)
  149. {
  150. $location = $this->applyPathPrefix($path);
  151. $stream = fopen($location, 'rb');
  152. return ['type' => 'file', 'path' => $path, 'stream' => $stream];
  153. }
  154. /**
  155. * @inheritdoc
  156. */
  157. public function updateStream($path, $resource, Config $config)
  158. {
  159. return $this->writeStream($path, $resource, $config);
  160. }
  161. /**
  162. * @inheritdoc
  163. */
  164. public function update($path, $contents, Config $config)
  165. {
  166. $location = $this->applyPathPrefix($path);
  167. $size = file_put_contents($location, $contents, $this->writeFlags);
  168. if ($size === false) {
  169. return false;
  170. }
  171. $type = 'file';
  172. $result = compact('type', 'path', 'size', 'contents');
  173. if ($visibility = $config->get('visibility')) {
  174. $this->setVisibility($path, $visibility);
  175. $result['visibility'] = $visibility;
  176. }
  177. return $result;
  178. }
  179. /**
  180. * @inheritdoc
  181. */
  182. public function read($path)
  183. {
  184. $location = $this->applyPathPrefix($path);
  185. $contents = @file_get_contents($location);
  186. if ($contents === false) {
  187. return false;
  188. }
  189. return ['type' => 'file', 'path' => $path, 'contents' => $contents];
  190. }
  191. /**
  192. * @inheritdoc
  193. */
  194. public function rename($path, $newpath)
  195. {
  196. $location = $this->applyPathPrefix($path);
  197. $destination = $this->applyPathPrefix($newpath);
  198. $parentDirectory = $this->applyPathPrefix(Util::dirname($newpath));
  199. $this->ensureDirectory($parentDirectory);
  200. return rename($location, $destination);
  201. }
  202. /**
  203. * @inheritdoc
  204. */
  205. public function copy($path, $newpath)
  206. {
  207. $location = $this->applyPathPrefix($path);
  208. $destination = $this->applyPathPrefix($newpath);
  209. $this->ensureDirectory(dirname($destination));
  210. return copy($location, $destination);
  211. }
  212. /**
  213. * @inheritdoc
  214. */
  215. public function delete($path)
  216. {
  217. $location = $this->applyPathPrefix($path);
  218. return @unlink($location);
  219. }
  220. /**
  221. * @inheritdoc
  222. */
  223. public function listContents($directory = '', $recursive = false)
  224. {
  225. $result = [];
  226. $location = $this->applyPathPrefix($directory);
  227. if ( ! is_dir($location)) {
  228. return [];
  229. }
  230. $iterator = $recursive ? $this->getRecursiveDirectoryIterator($location) : $this->getDirectoryIterator($location);
  231. foreach ($iterator as $file) {
  232. $path = $this->getFilePath($file);
  233. if (preg_match('#(^|/|\\\\)\.{1,2}$#', $path)) {
  234. continue;
  235. }
  236. $result[] = $this->normalizeFileInfo($file);
  237. }
  238. unset($iterator);
  239. return array_filter($result);
  240. }
  241. /**
  242. * @inheritdoc
  243. */
  244. public function getMetadata($path)
  245. {
  246. $location = $this->applyPathPrefix($path);
  247. clearstatcache(false, $location);
  248. $info = new SplFileInfo($location);
  249. return $this->normalizeFileInfo($info);
  250. }
  251. /**
  252. * @inheritdoc
  253. */
  254. public function getSize($path)
  255. {
  256. return $this->getMetadata($path);
  257. }
  258. /**
  259. * @inheritdoc
  260. */
  261. public function getMimetype($path)
  262. {
  263. $location = $this->applyPathPrefix($path);
  264. $finfo = new Finfo(FILEINFO_MIME_TYPE);
  265. $mimetype = $finfo->file($location);
  266. if (in_array($mimetype, ['application/octet-stream', 'inode/x-empty', 'application/x-empty'])) {
  267. $mimetype = Util\MimeType::detectByFilename($location);
  268. }
  269. return ['path' => $path, 'type' => 'file', 'mimetype' => $mimetype];
  270. }
  271. /**
  272. * @inheritdoc
  273. */
  274. public function getTimestamp($path)
  275. {
  276. return $this->getMetadata($path);
  277. }
  278. /**
  279. * @inheritdoc
  280. */
  281. public function getVisibility($path)
  282. {
  283. $location = $this->applyPathPrefix($path);
  284. clearstatcache(false, $location);
  285. $permissions = octdec(substr(sprintf('%o', fileperms($location)), -4));
  286. $type = is_dir($location) ? 'dir' : 'file';
  287. foreach ($this->permissionMap[$type] as $visibility => $visibilityPermissions) {
  288. if ($visibilityPermissions == $permissions) {
  289. return compact('path', 'visibility');
  290. }
  291. }
  292. $visibility = substr(sprintf('%o', fileperms($location)), -4);
  293. return compact('path', 'visibility');
  294. }
  295. /**
  296. * @inheritdoc
  297. */
  298. public function setVisibility($path, $visibility)
  299. {
  300. $location = $this->applyPathPrefix($path);
  301. $type = is_dir($location) ? 'dir' : 'file';
  302. $success = chmod($location, $this->permissionMap[$type][$visibility]);
  303. if ($success === false) {
  304. return false;
  305. }
  306. return compact('path', 'visibility');
  307. }
  308. /**
  309. * @inheritdoc
  310. */
  311. public function createDir($dirname, Config $config)
  312. {
  313. $location = $this->applyPathPrefix($dirname);
  314. $umask = umask(0);
  315. $visibility = $config->get('visibility', 'public');
  316. $return = ['path' => $dirname, 'type' => 'dir'];
  317. if ( ! is_dir($location)) {
  318. if (false === @mkdir($location, $this->permissionMap['dir'][$visibility], true)
  319. || false === is_dir($location)) {
  320. $return = false;
  321. }
  322. }
  323. umask($umask);
  324. return $return;
  325. }
  326. /**
  327. * @inheritdoc
  328. */
  329. public function deleteDir($dirname)
  330. {
  331. $location = $this->applyPathPrefix($dirname);
  332. if ( ! is_dir($location)) {
  333. return false;
  334. }
  335. $contents = $this->getRecursiveDirectoryIterator($location, RecursiveIteratorIterator::CHILD_FIRST);
  336. /** @var SplFileInfo $file */
  337. foreach ($contents as $file) {
  338. $this->guardAgainstUnreadableFileInfo($file);
  339. $this->deleteFileInfoObject($file);
  340. }
  341. unset($contents);
  342. return rmdir($location);
  343. }
  344. /**
  345. * @param SplFileInfo $file
  346. */
  347. protected function deleteFileInfoObject(SplFileInfo $file)
  348. {
  349. switch ($file->getType()) {
  350. case 'dir':
  351. rmdir($file->getRealPath());
  352. break;
  353. case 'link':
  354. unlink($file->getPathname());
  355. break;
  356. default:
  357. unlink($file->getRealPath());
  358. }
  359. }
  360. /**
  361. * Normalize the file info.
  362. *
  363. * @param SplFileInfo $file
  364. *
  365. * @return array|void
  366. *
  367. * @throws NotSupportedException
  368. */
  369. protected function normalizeFileInfo(SplFileInfo $file)
  370. {
  371. if ( ! $file->isLink()) {
  372. return $this->mapFileInfo($file);
  373. }
  374. if ($this->linkHandling & self::DISALLOW_LINKS) {
  375. throw NotSupportedException::forLink($file);
  376. }
  377. }
  378. /**
  379. * Get the normalized path from a SplFileInfo object.
  380. *
  381. * @param SplFileInfo $file
  382. *
  383. * @return string
  384. */
  385. protected function getFilePath(SplFileInfo $file)
  386. {
  387. $location = $file->getPathname();
  388. $path = $this->removePathPrefix($location);
  389. return trim(str_replace('\\', '/', $path), '/');
  390. }
  391. /**
  392. * @param string $path
  393. * @param int $mode
  394. *
  395. * @return RecursiveIteratorIterator
  396. */
  397. protected function getRecursiveDirectoryIterator($path, $mode = RecursiveIteratorIterator::SELF_FIRST)
  398. {
  399. return new RecursiveIteratorIterator(
  400. new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
  401. $mode
  402. );
  403. }
  404. /**
  405. * @param string $path
  406. *
  407. * @return DirectoryIterator
  408. */
  409. protected function getDirectoryIterator($path)
  410. {
  411. $iterator = new DirectoryIterator($path);
  412. return $iterator;
  413. }
  414. /**
  415. * @param SplFileInfo $file
  416. *
  417. * @return array
  418. */
  419. protected function mapFileInfo(SplFileInfo $file)
  420. {
  421. $normalized = [
  422. 'type' => $file->getType(),
  423. 'path' => $this->getFilePath($file),
  424. ];
  425. $normalized['timestamp'] = $file->getMTime();
  426. if ($normalized['type'] === 'file') {
  427. $normalized['size'] = $file->getSize();
  428. }
  429. return $normalized;
  430. }
  431. /**
  432. * @param SplFileInfo $file
  433. *
  434. * @throws UnreadableFileException
  435. */
  436. protected function guardAgainstUnreadableFileInfo(SplFileInfo $file)
  437. {
  438. if ( ! $file->isReadable()) {
  439. throw UnreadableFileException::forFileInfo($file);
  440. }
  441. }
  442. }