| | <?php |
| | class RepositoryList { |
| | - private $path; |
| | + private $reposPath; |
| | |
| | public function __construct($path) { |
| | - $this->path = $path; |
| | + $this->reposPath = $path; |
| | } |
| | |
| | - public function eachRepository($callback) { |
| | - if (!is_dir($this->path)) return; |
| | + public function eachRepository(callable $callback) { |
| | + $repos = []; |
| | + $dirs = glob($this->reposPath . '/*', GLOB_ONLYDIR); |
| | |
| | - $dirs = scandir($this->path); |
| | if ($dirs === false) return; |
| | - |
| | - $repos = []; |
| | |
| | foreach ($dirs as $dir) { |
| | - if ($dir === '.' || $dir === '..') continue; |
| | - |
| | - $fullPath = $this->path . '/' . $dir; |
| | + $basename = basename($dir); |
| | + if ($basename[0] === '.') continue; |
| | |
| | - // Check for non-bare repository (working directory with .git) |
| | - if (is_dir($fullPath . '/.git')) { |
| | - $repos[] = [ |
| | - 'name' => $dir, |
| | - 'safe_name' => $dir, |
| | - 'path' => $fullPath . '/.git' |
| | - ]; |
| | - } |
| | - // Check for bare repository (has objects and HEAD directly) |
| | - elseif (is_dir($fullPath . '/objects') && is_file($fullPath . '/HEAD')) { |
| | - $repos[] = [ |
| | - 'name' => $dir, |
| | - 'safe_name' => $dir, |
| | - 'path' => $fullPath |
| | - ]; |
| | - } |
| | + $repos[$basename] = [ |
| | + 'name' => $basename, |
| | + 'safe_name' => $basename, |
| | + 'path' => $dir |
| | + ]; |
| | } |
| | |
| | - $this->sortAndFilter($repos); |
| | + $this->sortRepositories($repos); |
| | |
| | foreach ($repos as $repo) { |
| | - call_user_func($callback, $repo); |
| | + $callback($repo); |
| | } |
| | } |
| | - |
| | - private function sortAndFilter(array &$repos) { |
| | - $orderFile = $this->path . '/order.txt'; |
| | - if (!file_exists($orderFile)) return; |
| | - |
| | - $lines = file($orderFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); |
| | - if ($lines === false) return; |
| | - |
| | - $weights = []; |
| | - $hidden = []; |
| | - $rank = 0; |
| | |
| | - foreach ($lines as $line) { |
| | - $line = trim($line); |
| | - if ($line === '') continue; |
| | + private function sortRepositories(array &$repos) { |
| | + $orderFile = __DIR__ . '/order.txt'; |
| | |
| | - // Use substr instead of str_starts_with for PHP < 8.0 compatibility |
| | - if (substr($line, 0, 1) === '-') { |
| | - // Store hidden names in lowercase for case-insensitive comparison |
| | - $hidden[strtolower(substr($line, 1))] = true; |
| | - } else { |
| | - $weights[strtolower($line)] = $rank++; |
| | - } |
| | + if (!file_exists($orderFile)) { |
| | + ksort($repos, SORT_NATURAL | SORT_FLAG_CASE); |
| | + return; |
| | } |
| | |
| | - // Filter hidden repositories (case-insensitive) |
| | - $repos = array_filter($repos, function($r) use ($hidden) { |
| | - return !isset($hidden[strtolower($r['name'])]); |
| | - }); |
| | + $lines = file($orderFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); |
| | + $order = array_flip(array_map('trim', $lines)); |
| | |
| | - // Sort based on weights (case-insensitive) |
| | - usort($repos, function($a, $b) use ($weights) { |
| | - $nameA = strtolower($a['name']); |
| | - $nameB = strtolower($b['name']); |
| | + uasort($repos, function($a, $b) use ($order) { |
| | + $nameA = $a['safe_name']; |
| | + $nameB = $b['safe_name']; |
| | |
| | - $wa = isset($weights[$nameA]) ? $weights[$nameA] : PHP_INT_MAX; |
| | - $wb = isset($weights[$nameB]) ? $weights[$nameB] : PHP_INT_MAX; |
| | + $posA = $order[$nameA] ?? PHP_INT_MAX; |
| | + $posB = $order[$nameB] ?? PHP_INT_MAX; |
| | |
| | - if ($wa === $wb) { |
| | - return strcasecmp($a['name'], $b['name']); |
| | + if ($posA === $posB) { |
| | + return strcasecmp($nameA, $nameB); |
| | } |
| | - return $wa <=> $wb; |
| | + return $posA <=> $posB; |
| | }); |
| | } |