Glob.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Stdlib;
  10. /**
  11. * Wrapper for glob with fallback if GLOB_BRACE is not available.
  12. */
  13. abstract class Glob
  14. {
  15. /**#@+
  16. * Glob constants.
  17. */
  18. const GLOB_MARK = 0x01;
  19. const GLOB_NOSORT = 0x02;
  20. const GLOB_NOCHECK = 0x04;
  21. const GLOB_NOESCAPE = 0x08;
  22. const GLOB_BRACE = 0x10;
  23. const GLOB_ONLYDIR = 0x20;
  24. const GLOB_ERR = 0x40;
  25. /**#@-*/
  26. /**
  27. * Find pathnames matching a pattern.
  28. *
  29. * @see http://docs.php.net/glob
  30. * @param string $pattern
  31. * @param int $flags
  32. * @param bool $forceFallback
  33. * @return array
  34. * @throws Exception\RuntimeException
  35. */
  36. public static function glob($pattern, $flags = 0, $forceFallback = false)
  37. {
  38. if (!defined('GLOB_BRACE') || $forceFallback) {
  39. return static::fallbackGlob($pattern, $flags);
  40. }
  41. return static::systemGlob($pattern, $flags);
  42. }
  43. /**
  44. * Use the glob function provided by the system.
  45. *
  46. * @param string $pattern
  47. * @param int $flags
  48. * @return array
  49. * @throws Exception\RuntimeException
  50. */
  51. protected static function systemGlob($pattern, $flags)
  52. {
  53. if ($flags) {
  54. $flagMap = [
  55. self::GLOB_MARK => GLOB_MARK,
  56. self::GLOB_NOSORT => GLOB_NOSORT,
  57. self::GLOB_NOCHECK => GLOB_NOCHECK,
  58. self::GLOB_NOESCAPE => GLOB_NOESCAPE,
  59. self::GLOB_BRACE => defined('GLOB_BRACE') ? GLOB_BRACE : 0,
  60. self::GLOB_ONLYDIR => GLOB_ONLYDIR,
  61. self::GLOB_ERR => GLOB_ERR,
  62. ];
  63. $globFlags = 0;
  64. foreach ($flagMap as $internalFlag => $globFlag) {
  65. if ($flags & $internalFlag) {
  66. $globFlags |= $globFlag;
  67. }
  68. }
  69. } else {
  70. $globFlags = 0;
  71. }
  72. ErrorHandler::start();
  73. $res = glob($pattern, $globFlags);
  74. $err = ErrorHandler::stop();
  75. if ($res === false) {
  76. throw new Exception\RuntimeException("glob('{$pattern}', {$globFlags}) failed", 0, $err);
  77. }
  78. return $res;
  79. }
  80. /**
  81. * Expand braces manually, then use the system glob.
  82. *
  83. * @param string $pattern
  84. * @param int $flags
  85. * @return array
  86. * @throws Exception\RuntimeException
  87. */
  88. protected static function fallbackGlob($pattern, $flags)
  89. {
  90. if (!$flags & self::GLOB_BRACE) {
  91. return static::systemGlob($pattern, $flags);
  92. }
  93. $flags &= ~self::GLOB_BRACE;
  94. $length = strlen($pattern);
  95. $paths = [];
  96. if ($flags & self::GLOB_NOESCAPE) {
  97. $begin = strpos($pattern, '{');
  98. } else {
  99. $begin = 0;
  100. while (true) {
  101. if ($begin === $length) {
  102. $begin = false;
  103. break;
  104. } elseif ($pattern[$begin] === '\\' && ($begin + 1) < $length) {
  105. $begin++;
  106. } elseif ($pattern[$begin] === '{') {
  107. break;
  108. }
  109. $begin++;
  110. }
  111. }
  112. if ($begin === false) {
  113. return static::systemGlob($pattern, $flags);
  114. }
  115. $next = static::nextBraceSub($pattern, $begin + 1, $flags);
  116. if ($next === null) {
  117. return static::systemGlob($pattern, $flags);
  118. }
  119. $rest = $next;
  120. while ($pattern[$rest] !== '}') {
  121. $rest = static::nextBraceSub($pattern, $rest + 1, $flags);
  122. if ($rest === null) {
  123. return static::systemGlob($pattern, $flags);
  124. }
  125. }
  126. $p = $begin + 1;
  127. while (true) {
  128. $subPattern = substr($pattern, 0, $begin)
  129. . substr($pattern, $p, $next - $p)
  130. . substr($pattern, $rest + 1);
  131. $result = static::fallbackGlob($subPattern, $flags | self::GLOB_BRACE);
  132. if ($result) {
  133. $paths = array_merge($paths, $result);
  134. }
  135. if ($pattern[$next] === '}') {
  136. break;
  137. }
  138. $p = $next + 1;
  139. $next = static::nextBraceSub($pattern, $p, $flags);
  140. }
  141. return array_unique($paths);
  142. }
  143. /**
  144. * Find the end of the sub-pattern in a brace expression.
  145. *
  146. * @param string $pattern
  147. * @param int $begin
  148. * @param int $flags
  149. * @return int|null
  150. */
  151. protected static function nextBraceSub($pattern, $begin, $flags)
  152. {
  153. $length = strlen($pattern);
  154. $depth = 0;
  155. $current = $begin;
  156. while ($current < $length) {
  157. if (!$flags & self::GLOB_NOESCAPE && $pattern[$current] === '\\') {
  158. if (++$current === $length) {
  159. break;
  160. }
  161. $current++;
  162. } else {
  163. if (($pattern[$current] === '}' && $depth-- === 0) || ($pattern[$current] === ',' && $depth === 0)) {
  164. break;
  165. } elseif ($pattern[$current++] === '{') {
  166. $depth++;
  167. }
  168. }
  169. }
  170. return ($current < $length ? $current : null);
  171. }
  172. }