<?php require_once __DIR__ . '/RepositoryList.php'; require_once __DIR__ . '/git/Git.php'; require_once __DIR__ . '/pages/CommitsPage.php'; require_once __DIR__ . '/pages/DiffPage.php'; require_once __DIR__ . '/pages/HomePage.php'; require_once __DIR__ . '/pages/FilePage.php'; require_once __DIR__ . '/pages/RawPage.php'; require_once __DIR__ . '/pages/TagsPage.php'; require_once __DIR__ . '/pages/ClonePage.php'; require_once __DIR__ . '/pages/ComparePage.php'; class Router { private const ACTION_TREE = 'tree'; private const ACTION_BLOB = 'blob'; private const ACTION_RAW = 'raw'; private const ACTION_COMMITS = 'commits'; private const ACTION_COMMIT = 'commit'; private const ACTION_COMPARE = 'compare'; private const ACTION_TAGS = 'tags'; private const GET_REPOSITORY = 'repo'; private const GET_ACTION = 'action'; private const GET_HASH = 'hash'; private const GET_NAME = 'name'; private const REFERENCE_HEAD = 'HEAD'; private const ROUTE_REPO = 'repo'; private const EXTENSION_GIT = '.git'; private array $repos = []; private Git $git; private string $repoName = ''; private array $repoData = []; private string $action = ''; private string $commitHash = ''; private string $filePath = ''; private string $baseHash = ''; public function __construct( string $reposPath ) { $this->git = new Git( $reposPath ); $list = new RepositoryList( $reposPath ); $list->eachRepository( function( $repo ) { $this->repos[$repo['safe_name']] = $repo; }); } public function route(): Page { $this->normalizeQueryString(); $uriParts = $this->parseUriParts(); $repoName = !empty( $uriParts ) ? array_shift( $uriParts ) : ''; $page = new HomePage( $this->repos, $this->git ); if( $repoName !== '' ) { if( str_ends_with( $repoName, self::EXTENSION_GIT ) ) { $page = $this->handleCloneRoute( $repoName, $uriParts ); } elseif( isset( $this->repos[$repoName] ) ) { $page = $this->resolveActionRoute( $repoName, $uriParts ); } } return $page; } private function handleCloneRoute( string $repoName, array $uriParts ): Page { $realName = substr( $repoName, 0, -4 ); $path = ''; if( isset( $this->repos[$realName]['path'] ) ) { $path = $this->repos[$realName]['path']; } elseif( isset( $this->repos[$repoName]['path'] ) ) { $path = $this->repos[$repoName]['path']; } if( $path === '' ) { http_response_code( 404 ); exit( "Repository not found" ); } $this->git->setRepository( $path ); return new ClonePage( $this->git, implode( '/', $uriParts ) ); } private function resolveActionRoute( string $repoName, array $uriParts ): Page { $this->repoData = $this->repos[$repoName]; $this->repoName = $repoName; $this->git->setRepository( $this->repoData['path'] ); $act = array_shift( $uriParts ); $this->action = $act ?: self::ACTION_TREE; $this->commitHash = self::REFERENCE_HEAD; $this->filePath = ''; $this->baseHash = ''; $hasHash = [ self::ACTION_TREE, self::ACTION_BLOB, self::ACTION_RAW, self::ACTION_COMMITS ]; if( in_array( $this->action, $hasHash ) ) { $hash = array_shift( $uriParts ); $this->commitHash = $hash ?: self::REFERENCE_HEAD; $this->filePath = implode( '/', $uriParts ); } elseif( $this->action === self::ACTION_COMMIT ) { $this->commitHash = array_shift( $uriParts ) ?? self::REFERENCE_HEAD; } elseif( $this->action === self::ACTION_COMPARE ) { $this->commitHash = array_shift( $uriParts ) ?? self::REFERENCE_HEAD; $this->baseHash = array_shift( $uriParts ) ?? ''; } $this->populateGet(); return $this->createPage(); } private function createPage(): Page { return match( $this->action ) { self::ACTION_TREE, self::ACTION_BLOB => new FilePage( $this->repos, $this->repoData, $this->git, $this->commitHash, $this->filePath ), self::ACTION_RAW => new RawPage( $this->git, $this->commitHash ), self::ACTION_COMMITS => new CommitsPage( $this->repos, $this->repoData, $this->git, $this->commitHash ), self::ACTION_COMMIT => new DiffPage( $this->repos, $this->repoData, $this->git, $this->commitHash ), self::ACTION_TAGS => new TagsPage( $this->repos, $this->repoData, $this->git ), self::ACTION_COMPARE => new ComparePage( $this->repos, $this->repoData, $this->git, $this->commitHash, $this->baseHash ), default => new FilePage( $this->repos, $this->repoData, $this->git, self::REFERENCE_HEAD, '' ) }; } private function normalizeQueryString(): void { if( empty( $_GET ) && !empty( $_SERVER['QUERY_STRING'] ) ) { parse_str( $_SERVER['QUERY_STRING'], $_GET ); } } private function parseUriParts(): array { $requestUri = parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ); $scriptName = dirname( $_SERVER['SCRIPT_NAME'] ); if( $scriptName !== '/' && strpos( $requestUri, $scriptName ) === 0 ) { $requestUri = substr( $requestUri, strlen( $scriptName ) ); } $requestUri = trim( $requestUri, '/' ); $uriParts = explode( '/', $requestUri ); if( !empty( $uriParts ) && $uriParts[0] === self::ROUTE_REPO ) { array_shift( $uriParts ); } return $uriParts; } private function populateGet(): void { $_GET[self::GET_REPOSITORY] = $this->repoName; $_GET[self::GET_ACTION] = $this->action; $_GET[self::GET_HASH] = $this->commitHash; $_GET[self::GET_NAME] = $this->filePath; } }