Util.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. <?php
  2. namespace League\Flysystem;
  3. use League\Flysystem\Util\MimeType;
  4. use LogicException;
  5. use function strcmp;
  6. class Util
  7. {
  8. /**
  9. * Get normalized pathinfo.
  10. *
  11. * @param string $path
  12. *
  13. * @return array pathinfo
  14. */
  15. public static function pathinfo($path)
  16. {
  17. $pathinfo = compact('path');
  18. if ('' !== $dirname = dirname($path)) {
  19. $pathinfo['dirname'] = static::normalizeDirname($dirname);
  20. }
  21. $pathinfo['basename'] = static::basename($path);
  22. $pathinfo += pathinfo($pathinfo['basename']);
  23. return $pathinfo + ['dirname' => ''];
  24. }
  25. /**
  26. * Normalize a dirname return value.
  27. *
  28. * @param string $dirname
  29. *
  30. * @return string normalized dirname
  31. */
  32. public static function normalizeDirname($dirname)
  33. {
  34. return $dirname === '.' ? '' : $dirname;
  35. }
  36. /**
  37. * Get a normalized dirname from a path.
  38. *
  39. * @param string $path
  40. *
  41. * @return string dirname
  42. */
  43. public static function dirname($path)
  44. {
  45. return static::normalizeDirname(dirname($path));
  46. }
  47. /**
  48. * Map result arrays.
  49. *
  50. * @param array $object
  51. * @param array $map
  52. *
  53. * @return array mapped result
  54. */
  55. public static function map(array $object, array $map)
  56. {
  57. $result = [];
  58. foreach ($map as $from => $to) {
  59. if ( ! isset($object[$from])) {
  60. continue;
  61. }
  62. $result[$to] = $object[$from];
  63. }
  64. return $result;
  65. }
  66. /**
  67. * Normalize path.
  68. *
  69. * @param string $path
  70. *
  71. * @throws LogicException
  72. *
  73. * @return string
  74. */
  75. public static function normalizePath($path)
  76. {
  77. return static::normalizeRelativePath($path);
  78. }
  79. /**
  80. * Normalize relative directories in a path.
  81. *
  82. * @param string $path
  83. *
  84. * @throws LogicException
  85. *
  86. * @return string
  87. */
  88. public static function normalizeRelativePath($path)
  89. {
  90. $path = str_replace('\\', '/', $path);
  91. $path = static::removeFunkyWhiteSpace($path);
  92. $parts = [];
  93. foreach (explode('/', $path) as $part) {
  94. switch ($part) {
  95. case '':
  96. case '.':
  97. break;
  98. case '..':
  99. if (empty($parts)) {
  100. throw new LogicException(
  101. 'Path is outside of the defined root, path: [' . $path . ']'
  102. );
  103. }
  104. array_pop($parts);
  105. break;
  106. default:
  107. $parts[] = $part;
  108. break;
  109. }
  110. }
  111. $path = implode('/', $parts);
  112. return $path;
  113. }
  114. /**
  115. * Rejects unprintable characters and invalid unicode characters.
  116. *
  117. * @param string $path
  118. *
  119. * @return string $path
  120. */
  121. protected static function removeFunkyWhiteSpace($path)
  122. {
  123. if (preg_match('#\p{C}+#u', $path)) {
  124. throw CorruptedPathDetected::forPath($path);
  125. }
  126. return $path;
  127. }
  128. /**
  129. * Normalize prefix.
  130. *
  131. * @param string $prefix
  132. * @param string $separator
  133. *
  134. * @return string normalized path
  135. */
  136. public static function normalizePrefix($prefix, $separator)
  137. {
  138. return rtrim($prefix, $separator) . $separator;
  139. }
  140. /**
  141. * Get content size.
  142. *
  143. * @param string $contents
  144. *
  145. * @return int content size
  146. */
  147. public static function contentSize($contents)
  148. {
  149. return defined('MB_OVERLOAD_STRING') ? mb_strlen($contents, '8bit') : strlen($contents);
  150. }
  151. /**
  152. * Guess MIME Type based on the path of the file and it's content.
  153. *
  154. * @param string $path
  155. * @param string|resource $content
  156. *
  157. * @return string|null MIME Type or NULL if no extension detected
  158. */
  159. public static function guessMimeType($path, $content)
  160. {
  161. $mimeType = MimeType::detectByContent($content);
  162. if ( ! (empty($mimeType) || in_array($mimeType, ['application/x-empty', 'text/plain', 'text/x-asm']))) {
  163. return $mimeType;
  164. }
  165. return MimeType::detectByFilename($path);
  166. }
  167. /**
  168. * Emulate directories.
  169. *
  170. * @param array $listing
  171. *
  172. * @return array listing with emulated directories
  173. */
  174. public static function emulateDirectories(array $listing)
  175. {
  176. $directories = [];
  177. $listedDirectories = [];
  178. foreach ($listing as $object) {
  179. [$directories, $listedDirectories] = static::emulateObjectDirectories($object, $directories, $listedDirectories);
  180. }
  181. $directories = array_diff(array_unique($directories), array_unique($listedDirectories));
  182. foreach ($directories as $directory) {
  183. $listing[] = static::pathinfo($directory) + ['type' => 'dir'];
  184. }
  185. return $listing;
  186. }
  187. /**
  188. * Ensure a Config instance.
  189. *
  190. * @param null|array|Config $config
  191. *
  192. * @return Config config instance
  193. *
  194. * @throw LogicException
  195. */
  196. public static function ensureConfig($config)
  197. {
  198. if ($config === null) {
  199. return new Config();
  200. }
  201. if ($config instanceof Config) {
  202. return $config;
  203. }
  204. if (is_array($config)) {
  205. return new Config($config);
  206. }
  207. throw new LogicException('A config should either be an array or a Flysystem\Config object.');
  208. }
  209. /**
  210. * Rewind a stream.
  211. *
  212. * @param resource $resource
  213. */
  214. public static function rewindStream($resource)
  215. {
  216. if (ftell($resource) !== 0 && static::isSeekableStream($resource)) {
  217. rewind($resource);
  218. }
  219. }
  220. public static function isSeekableStream($resource)
  221. {
  222. $metadata = stream_get_meta_data($resource);
  223. return $metadata['seekable'];
  224. }
  225. /**
  226. * Get the size of a stream.
  227. *
  228. * @param resource $resource
  229. *
  230. * @return int|null stream size
  231. */
  232. public static function getStreamSize($resource)
  233. {
  234. $stat = fstat($resource);
  235. if ( ! is_array($stat) || ! isset($stat['size'])) {
  236. return null;
  237. }
  238. return $stat['size'];
  239. }
  240. /**
  241. * Emulate the directories of a single object.
  242. *
  243. * @param array $object
  244. * @param array $directories
  245. * @param array $listedDirectories
  246. *
  247. * @return array
  248. */
  249. protected static function emulateObjectDirectories(array $object, array $directories, array $listedDirectories)
  250. {
  251. if ($object['type'] === 'dir') {
  252. $listedDirectories[] = $object['path'];
  253. }
  254. if ( ! isset($object['dirname']) || trim($object['dirname']) === '') {
  255. return [$directories, $listedDirectories];
  256. }
  257. $parent = $object['dirname'];
  258. while (isset($parent) && trim($parent) !== '' && ! in_array($parent, $directories)) {
  259. $directories[] = $parent;
  260. $parent = static::dirname($parent);
  261. }
  262. if (isset($object['type']) && $object['type'] === 'dir') {
  263. $listedDirectories[] = $object['path'];
  264. return [$directories, $listedDirectories];
  265. }
  266. return [$directories, $listedDirectories];
  267. }
  268. /**
  269. * Returns the trailing name component of the path.
  270. *
  271. * @param string $path
  272. *
  273. * @return string
  274. */
  275. private static function basename($path)
  276. {
  277. $separators = DIRECTORY_SEPARATOR === '/' ? '/' : '\/';
  278. $path = rtrim($path, $separators);
  279. $basename = preg_replace('#.*?([^' . preg_quote($separators, '#') . ']+$)#', '$1', $path);
  280. if (DIRECTORY_SEPARATOR === '/') {
  281. return $basename;
  282. }
  283. // @codeCoverageIgnoreStart
  284. // Extra Windows path munging. This is tested via AppVeyor, but code
  285. // coverage is not reported.
  286. // Handle relative paths with drive letters. c:file.txt.
  287. while (preg_match('#^[a-zA-Z]{1}:[^\\\/]#', $basename)) {
  288. $basename = substr($basename, 2);
  289. }
  290. // Remove colon for standalone drive letter names.
  291. if (preg_match('#^[a-zA-Z]{1}:$#', $basename)) {
  292. $basename = rtrim($basename, ':');
  293. }
  294. return $basename;
  295. // @codeCoverageIgnoreEnd
  296. }
  297. }