Helper.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <?php
  2. namespace TYPO3\PharStreamWrapper;
  3. /*
  4. * This file is part of the TYPO3 project.
  5. *
  6. * It is free software; you can redistribute it and/or modify it under the terms
  7. * of the MIT License (MIT). For the full copyright and license information,
  8. * please read the LICENSE file that was distributed with this source code.
  9. *
  10. * The TYPO3 project - inspiring people to share!
  11. */
  12. /**
  13. * Helper provides low-level tools on file name resolving. However it does not
  14. * (and should not) maintain any runtime state information. In order to resolve
  15. * Phar archive paths according resolvers have to be used.
  16. *
  17. * @see \TYPO3\PharStreamWrapper\Resolvable::resolve()
  18. */
  19. class Helper
  20. {
  21. /*
  22. * Resets PHP's OPcache if enabled as work-around for issues in `include()`
  23. * or `require()` calls and OPcache delivering wrong results.
  24. *
  25. * @see https://bugs.php.net/bug.php?id=66569
  26. */
  27. public static function resetOpCache()
  28. {
  29. if (function_exists('opcache_reset')
  30. && function_exists('opcache_get_status')
  31. ) {
  32. $status = opcache_get_status();
  33. if (!empty($status['opcache_enabled'])) {
  34. opcache_reset();
  35. }
  36. }
  37. }
  38. /**
  39. * Determines base file that can be accessed using the regular file system.
  40. * For e.g. "phar:///home/user/bundle.phar/content.txt" that would result
  41. * into "/home/user/bundle.phar".
  42. *
  43. * @param string $path
  44. * @return string|null
  45. */
  46. public static function determineBaseFile($path)
  47. {
  48. $parts = explode('/', static::normalizePath($path));
  49. while (count($parts)) {
  50. $currentPath = implode('/', $parts);
  51. if (@is_file($currentPath)) {
  52. return $currentPath;
  53. }
  54. array_pop($parts);
  55. }
  56. return null;
  57. }
  58. /**
  59. * @param string $path
  60. * @return bool
  61. */
  62. public static function hasPharPrefix($path)
  63. {
  64. return stripos($path, 'phar://') === 0;
  65. }
  66. /**
  67. * @param string $path
  68. * @return string
  69. */
  70. public static function removePharPrefix($path)
  71. {
  72. $path = trim($path);
  73. if (!static::hasPharPrefix($path)) {
  74. return $path;
  75. }
  76. return substr($path, 7);
  77. }
  78. /**
  79. * Normalizes a path, removes phar:// prefix, fixes Windows directory
  80. * separators. Result is without trailing slash.
  81. *
  82. * @param string $path
  83. * @return string
  84. */
  85. public static function normalizePath($path)
  86. {
  87. return rtrim(
  88. static::normalizeWindowsPath(
  89. static::removePharPrefix($path)
  90. ),
  91. '/'
  92. );
  93. }
  94. /**
  95. * Fixes a path for windows-backslashes and reduces double-slashes to single slashes
  96. *
  97. * @param string $path File path to process
  98. * @return string
  99. */
  100. private static function normalizeWindowsPath($path)
  101. {
  102. return str_replace('\\', '/', $path);
  103. }
  104. /**
  105. * Resolves all dots, slashes and removes spaces after or before a path...
  106. *
  107. * @param string $path Input string
  108. * @return string Canonical path, always without trailing slash
  109. */
  110. private static function getCanonicalPath($path)
  111. {
  112. $path = static::normalizeWindowsPath($path);
  113. $absolutePathPrefix = '';
  114. if (static::isAbsolutePath($path)) {
  115. if (static::isWindows() && strpos($path, ':/') === 1) {
  116. $absolutePathPrefix = substr($path, 0, 3);
  117. $path = substr($path, 3);
  118. } else {
  119. $path = ltrim($path, '/');
  120. $absolutePathPrefix = '/';
  121. }
  122. }
  123. $pathParts = explode('/', $path);
  124. $pathPartsLength = count($pathParts);
  125. for ($partCount = 0; $partCount < $pathPartsLength; $partCount++) {
  126. // double-slashes in path: remove element
  127. if ($pathParts[$partCount] === '') {
  128. array_splice($pathParts, $partCount, 1);
  129. $partCount--;
  130. $pathPartsLength--;
  131. }
  132. // "." in path: remove element
  133. if ((isset($pathParts[$partCount]) ? $pathParts[$partCount] : '') === '.') {
  134. array_splice($pathParts, $partCount, 1);
  135. $partCount--;
  136. $pathPartsLength--;
  137. }
  138. // ".." in path:
  139. if ((isset($pathParts[$partCount]) ? $pathParts[$partCount] : '') === '..') {
  140. if ($partCount === 0) {
  141. array_splice($pathParts, $partCount, 1);
  142. $partCount--;
  143. $pathPartsLength--;
  144. } elseif ($partCount >= 1) {
  145. // Rremove this and previous element
  146. array_splice($pathParts, $partCount - 1, 2);
  147. $partCount -= 2;
  148. $pathPartsLength -= 2;
  149. } elseif ($absolutePathPrefix) {
  150. // can't go higher than root dir
  151. // simply remove this part and continue
  152. array_splice($pathParts, $partCount, 1);
  153. $partCount--;
  154. $pathPartsLength--;
  155. }
  156. }
  157. }
  158. return $absolutePathPrefix . implode('/', $pathParts);
  159. }
  160. /**
  161. * Checks if the $path is absolute or relative (detecting either '/' or
  162. * 'x:/' as first part of string) and returns TRUE if so.
  163. *
  164. * @param string $path File path to evaluate
  165. * @return bool
  166. */
  167. private static function isAbsolutePath($path)
  168. {
  169. // Path starting with a / is always absolute, on every system
  170. // On Windows also a path starting with a drive letter is absolute: X:/
  171. return (isset($path[0]) ? $path[0] : null) === '/'
  172. || static::isWindows() && (
  173. strpos($path, ':/') === 1
  174. || strpos($path, ':\\') === 1
  175. );
  176. }
  177. /**
  178. * @return bool
  179. */
  180. private static function isWindows()
  181. {
  182. return stripos(PHP_OS, 'WIN') === 0;
  183. }
  184. }