Helper.php 5.5 KB

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