diff options
author | Daniel Weipert <git@mail.dweipert.de> | 2025-07-23 18:45:43 +0200 |
---|---|---|
committer | Daniel Weipert <git@mail.dweipert.de> | 2025-07-23 18:45:43 +0200 |
commit | 0eccccfab33bbad8d5731700c1ed959debe98bbd (patch) | |
tree | 3a0623d996803b6cc0669c0489db1a26560650e6 | |
parent | d51eee7ed707568f58d305abb750349cb2f27bcc (diff) |
display blob based on mime type
-rw-r--r-- | index.php | 178 |
1 files changed, 148 insertions, 30 deletions
@@ -1,5 +1,11 @@ <?php + +## +# Git +## + + /** * find projects at $path * @@ -7,12 +13,18 @@ */ function find_projects(string $path, array $projects = []): array { $directories = glob($path . "/{.[!.],}*", GLOB_ONLYDIR | GLOB_BRACE); - if (($idx = array_search("$path/.git", $directories)) !== false) { - $projects[] = substr($directories[$idx], 0, -4); + + if (array_search("$path/.git", $directories) !== false) { + $projects[] = $path; } else { - foreach ($directories as $directory) { - $projects += find_projects($directory, $projects); + if (count(array_intersect(scandir($path), ["HEAD", "objects", "refs"])) == 3) { + $projects[] = $path; + } + else { + foreach ($directories as $directory) { + $projects += find_projects($directory, $projects); + } } } @@ -53,6 +65,7 @@ function parse_log(string $path): array { return $cache_parse_log[$path] = $json ?? []; } + /** * parse git status at $path */ @@ -138,6 +151,7 @@ function parse_status(string $path): array { return $status; } + /** * get git file tree at $path for $revision under $tree_base_path */ @@ -145,7 +159,7 @@ function get_file_tree(string $path, string $revision, string $tree_base_path): { $proxy = md5(strval(microtime(true))); $process = proc_open( - "git -C \"{$path}\" ls-tree {$revision} {$tree_base_path}" . " --format='{%n {$proxy}mode{$proxy}: {$proxy}%(objectmode){$proxy},%n {$proxy}type{$proxy}: {$proxy}%(objecttype){$proxy},%n {$proxy}name{$proxy}: {$proxy}%(objectname){$proxy},%n {$proxy}size{$proxy}: {$proxy}%(objectsize){$proxy},%n {$proxy}path{$proxy}: {$proxy}%(path){$proxy}%n},'", + "git -C \"{$path}\" ls-tree {$revision} \"{$tree_base_path}\"" . " --format='{%n {$proxy}mode{$proxy}: {$proxy}%(objectmode){$proxy},%n {$proxy}type{$proxy}: {$proxy}%(objecttype){$proxy},%n {$proxy}name{$proxy}: {$proxy}%(objectname){$proxy},%n {$proxy}size{$proxy}: {$proxy}%(objectsize){$proxy},%n {$proxy}path{$proxy}: {$proxy}%(path){$proxy}%n},'", [ ["pipe", "r"], ["pipe", "w"], @@ -165,6 +179,69 @@ function get_file_tree(string $path, string $revision, string $tree_base_path): /** + * get git file info for single file at $file_path in repository at $path + */ +function get_file_info(string $path, string $file_path): array +{ + $tree = get_file_tree($path, "HEAD", $file_path); + + $info = []; + foreach ($tree as $file) { + if ($file["path"] == $file_path) { + $info = $file; + break; + } + } + + return $info; +} + + +/** + * get file contents based on $name (git object id) in repository at $path + */ +function read_object_file(string $path, string $name): string +{ + $process = proc_open( + "git -C \"{$path}\" cat-file -p \"{$name}\"", + [ + ["pipe", "r"], + ["pipe", "w"], + ["pipe", "w"], + ], + $pipes + ); + + return stream_get_contents($pipes[1]); +} + + +## +# Utility +## + + +/** + * urlencode whole $url + */ +function uri_encode(string $url): string +{ + return implode( + "/", + array_map( + fn ($value) => urlencode($value), + explode("/", $url) + ) + ); +} + + +## +# Template +## + + +/** * Template root */ function template_root(string $content, array $meta = []): string @@ -180,12 +257,17 @@ function template_root(string $content, array $meta = []): string <html> <head> <title><?php echo $meta["title"]; ?></title> + <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0"> <style> .blob-code-block { pre { border: 1px solid; display: inline-block; padding: 1rem; + box-sizing: border-box; + max-width: 100vw; + white-space: pre-wrap; + word-break: break-word; } } </style> @@ -198,6 +280,34 @@ function template_root(string $content, array $meta = []): string } +/** + * build breadcrumbs + */ +function breadcrumbs(string $root, string $path): string +{ + ob_start(); + + $breadcrumbs = explode("/", trim(str_replace($root, "", $path), "/")); + echo "<a href=\"$root\">root</a>"; + $crumb_parts = ""; + foreach ($breadcrumbs as $idx => $crumb) { + $crumb_parts .= "/$crumb"; + if ($idx < count($breadcrumbs) - 1) { + echo "/<a href=\"$root$crumb_parts\">$crumb</a>"; + } else { + echo "/$crumb"; + } + } + + return ob_get_clean(); +} + + +## +# Output +## + + $projects_path = realpath($_ENV["PHPGIT_PROJECTS_PATH"]); $url = parse_url("http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]"); @@ -251,7 +361,7 @@ if ($url["path"] == "/") { ?><tr><?php - ?><td><a href="<?php echo $name; ?>/tree"><?php + ?><td><a href="/<?php echo uri_encode($name); ?>/tree"><?php echo $name; ?></a></td><?php @@ -285,13 +395,7 @@ elseif (preg_match("#(.*)/tree/?(.*)#", $url["path"], $matches)) { $tree = get_file_tree("$projects_path/$project", "HEAD", "./{$file_path}/"); // show breadcrumbs - $breadcrumbs = explode("/", $file_path); - echo "<a href='/$project/tree'>root</a>"; - $crumb_parts = ""; - foreach ($breadcrumbs as $idx => $crumb) { - $crumb_parts .= "/$crumb"; - echo "/<a href='/$project/tree$crumb_parts'>$crumb</a>"; - } + echo breadcrumbs("/$project/tree", "/$project/tree/$file_path"); // build list ?> @@ -306,7 +410,7 @@ elseif (preg_match("#(.*)/tree/?(.*)#", $url["path"], $matches)) { <?php foreach ($tree as $file) { - $current_file_path = $file_path . "/"; + $current_file_path = empty($file_path) ? "" : $file_path . "/"; $basename = basename($file["path"]); ?><tr><?php @@ -314,9 +418,9 @@ elseif (preg_match("#(.*)/tree/?(.*)#", $url["path"], $matches)) { ?><td><?php echo $file["mode"]; ?></td><?php if ($file["type"] == "blob"): - ?><td><a href="<?php echo "/$project/blob/$current_file_path$basename"; ?>"><?php echo $basename; ?></a><td><?php + ?><td><a href="<?php echo uri_encode("/$project/blob/$current_file_path$basename"); ?>"><?php echo $basename; ?></a><td><?php else: - ?><td><a href="<?php echo "/$project/tree/$current_file_path$basename"; ?>"><?php echo $basename; ?></a><td><?php + ?><td><a href="<?php echo uri_encode("/$project/tree/$current_file_path$basename"); ?>"><?php echo $basename; ?></a><td><?php endif; ?></tr><?php @@ -328,35 +432,49 @@ elseif (preg_match("#(.*)/tree/?(.*)#", $url["path"], $matches)) { <?php } + // Route: /$project/blob/$file_path elseif (preg_match("#(.*)/blob/?(.*)#", $url["path"], $matches)) { $project = urldecode(ltrim($matches[1], "/")); $file_path = urldecode(rtrim($matches[2], "/")); - + // show breadcrumbs - $breadcrumbs = explode("/", $file_path); - echo "<a href='/$project/tree'>root</a>"; - $crumb_parts = ""; - foreach ($breadcrumbs as $idx => $crumb) { - $crumb_parts .= "/$crumb"; - if ($idx != count($breadcrumbs) - 1) { - echo "/<a href='/$project/tree$crumb_parts'>$crumb</a>"; - } else { - echo "/$crumb"; - } - } + echo breadcrumbs("/$project/tree", "/$project/tree/$file_path"); + + // get file info + $info = get_file_info("$projects_path/$project", $file_path); + $contents = read_object_file("$projects_path/$project", $info["name"]); + $mime_type = (new finfo(FILEINFO_MIME_TYPE))->buffer($contents); + + $memory_limit = (int)ini_get("memory_limit") * 1024 ** ["k" => 1, "m" => 2, "g" => 3][strtolower(ini_get("memory_limit")[-1])]; ?> - <div class="blob-code-block"> - <pre><code><?php echo file_get_contents("$projects_path/$project/$file_path"); ?></code></pre> + <div> + Mime Type: <?php echo $mime_type; ?> + <br> + <?php if (strlen($contents) * 2 < $memory_limit - memory_get_usage()): ?> + <?php if (str_starts_with($mime_type, "image/")): ?> + <img src="data:image/<?php echo substr($mime_type, 6); ?>;base64,<?php echo base64_encode($contents); ?>"> + <?php elseif (str_starts_with($mime_type, "audio/")): ?> + <audio controls src="data:audio/<?php echo substr($mime_type, 6); ?>;base64,<?php echo base64_encode($contents); ?>" /> + <?php else: ?> + <div class="blob-code-block"> + <pre><code><?php echo htmlspecialchars($contents); ?></code></pre> + </div> + <?php endif; ?> + <?php else: ?> + Can't process. Memory limit of <?php echo ini_get("memory_limit"); ?> would be exhausted. + <?php endif; ?> </div> <?php } + else { $path = $projects_path . $url["path"]; var_dump(parse_log($path)); } + // display echo template_root(ob_get_clean()); |