Util.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. <?php
  2. namespace League\Flysystem;
  3. use League\Flysystem\Util\MimeType;
  4. use LogicException;
  5. class Util
  6. {
  7. /**
  8. * Get normalized pathinfo.
  9. *
  10. * @param string $path
  11. *
  12. * @return array pathinfo
  13. */
  14. public static function pathinfo($path)
  15. {
  16. $pathinfo = compact('path');
  17. if ('' !== $dirname = dirname($path)) {
  18. $pathinfo['dirname'] = static::normalizeDirname($dirname);
  19. }
  20. $pathinfo['basename'] = static::basename($path);
  21. $pathinfo += pathinfo($pathinfo['basename']);
  22. return $pathinfo + ['dirname' => ''];
  23. }
  24. /**
  25. * Normalize a dirname return value.
  26. *
  27. * @param string $dirname
  28. *
  29. * @return string normalized dirname
  30. */
  31. public static function normalizeDirname($dirname)
  32. {
  33. return $dirname === '.' ? '' : $dirname;
  34. }
  35. /**
  36. * Get a normalized dirname from a path.
  37. *
  38. * @param string $path
  39. *
  40. * @return string dirname
  41. */
  42. public static function dirname($path)
  43. {
  44. return static::normalizeDirname(dirname($path));
  45. }
  46. /**
  47. * Map result arrays.
  48. *
  49. * @param array $object
  50. * @param array $map
  51. *
  52. * @return array mapped result
  53. */
  54. public static function map(array $object, array $map)
  55. {
  56. $result = [];
  57. foreach ($map as $from => $to) {
  58. if ( ! isset($object[$from])) {
  59. continue;
  60. }
  61. $result[$to] = $object[$from];
  62. }
  63. return $result;
  64. }
  65. /**
  66. * Normalize path.
  67. *
  68. * @param string $path
  69. *
  70. * @throws LogicException
  71. *
  72. * @return string
  73. */
  74. public static function normalizePath($path)
  75. {
  76. return static::normalizeRelativePath($path);
  77. }
  78. /**
  79. * Normalize relative directories in a path.
  80. *
  81. * @param string $path
  82. *
  83. * @throws LogicException
  84. *
  85. * @return string
  86. */
  87. public static function normalizeRelativePath($path)
  88. {
  89. $path = str_replace('\\', '/', $path);
  90. $path = static::removeFunkyWhiteSpace($path);
  91. $parts = [];
  92. foreach (explode('/', $path) as $part) {
  93. switch ($part) {
  94. case '':
  95. case '.':
  96. break;
  97. case '..':
  98. if (empty($parts)) {
  99. throw new LogicException(
  100. 'Path is outside of the defined root, path: [' . $path . ']'
  101. );
  102. }
  103. array_pop($parts);
  104. break;
  105. default:
  106. $parts[] = $part;
  107. break;
  108. }
  109. }
  110. return implode('/', $parts);
  111. }
  112. /**
  113. * Removes unprintable characters and invalid unicode characters.
  114. *
  115. * @param string $path
  116. *
  117. * @return string $path
  118. */
  119. protected static function removeFunkyWhiteSpace($path)
  120. {
  121. // We do this check in a loop, since removing invalid unicode characters
  122. // can lead to new characters being created.
  123. while (preg_match('#\p{C}+|^\./#u', $path)) {
  124. $path = preg_replace('#\p{C}+|^\./#u', '', $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. list($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 stream size
  231. */
  232. public static function getStreamSize($resource)
  233. {
  234. $stat = fstat($resource);
  235. return $stat['size'];
  236. }
  237. /**
  238. * Emulate the directories of a single object.
  239. *
  240. * @param array $object
  241. * @param array $directories
  242. * @param array $listedDirectories
  243. *
  244. * @return array
  245. */
  246. protected static function emulateObjectDirectories(array $object, array $directories, array $listedDirectories)
  247. {
  248. if ($object['type'] === 'dir') {
  249. $listedDirectories[] = $object['path'];
  250. }
  251. if (empty($object['dirname'])) {
  252. return [$directories, $listedDirectories];
  253. }
  254. $parent = $object['dirname'];
  255. while ( ! empty($parent) && ! in_array($parent, $directories)) {
  256. $directories[] = $parent;
  257. $parent = static::dirname($parent);
  258. }
  259. if (isset($object['type']) && $object['type'] === 'dir') {
  260. $listedDirectories[] = $object['path'];
  261. return [$directories, $listedDirectories];
  262. }
  263. return [$directories, $listedDirectories];
  264. }
  265. /**
  266. * Returns the trailing name component of the path.
  267. *
  268. * @param string $path
  269. *
  270. * @return string
  271. */
  272. private static function basename($path)
  273. {
  274. $separators = DIRECTORY_SEPARATOR === '/' ? '/' : '\/';
  275. $path = rtrim($path, $separators);
  276. $basename = preg_replace('#.*?([^' . preg_quote($separators, '#') . ']+$)#', '$1', $path);
  277. if (DIRECTORY_SEPARATOR === '/') {
  278. return $basename;
  279. }
  280. // @codeCoverageIgnoreStart
  281. // Extra Windows path munging. This is tested via AppVeyor, but code
  282. // coverage is not reported.
  283. // Handle relative paths with drive letters. c:file.txt.
  284. while (preg_match('#^[a-zA-Z]{1}:[^\\\/]#', $basename)) {
  285. $basename = substr($basename, 2);
  286. }
  287. // Remove colon for standalone drive letter names.
  288. if (preg_match('#^[a-zA-Z]{1}:$#', $basename)) {
  289. $basename = rtrim($basename, ':');
  290. }
  291. return $basename;
  292. // @codeCoverageIgnoreEnd
  293. }
  294. }