← Back
mainapp/services/BlobStore.php
<?php

class BlobStore
{
  private string $base;

  public function __construct(string $storageBase)
  {
    $this->base = rtrim($storageBase, '/');
  }

  private function repoBase(int $repoId): string
  {
    return $this->base . "/repos/" . $repoId;
  }

  public function ensureRepoDirs(int $repoId): void
  {
    $paths = [
      $this->repoBase($repoId),
      $this->repoBase($repoId) . "/blobs",
      $this->repoBase($repoId) . "/commits",
    ];
    foreach ($paths as $p) {
      if (!is_dir($p)) mkdir($p, 0750, true);
    }
  }

  public function blobPath(int $repoId, string $sha): string
  {
    $a = substr($sha, 0, 2);
    $b = substr($sha, 2, 2);
    return $this->repoBase($repoId) . "/blobs/$a/$b/$sha";
  }

  public function putBlob(PDO $db, int $repoId, string $content): array
  {
    $sha = hash('sha256', $content);
    $size = strlen($content);
    $path = $this->blobPath($repoId, $sha);

    $stmt = $db->prepare("SELECT id, storage_path FROM blobs WHERE repo_id=? AND sha256=? LIMIT 1");
    $stmt->execute([$repoId, $sha]);
    $row = $stmt->fetch();
    if ($row) {
      return ['blob_id' => (int)$row['id'], 'sha' => $sha, 'path' => $row['storage_path'], 'size' => $size];
    }

    $dir = dirname($path);
    if (!is_dir($dir)) mkdir($dir, 0750, true);
    file_put_contents($path, $content, LOCK_EX);

    $stmt = $db->prepare("INSERT INTO blobs (repo_id, sha256, size_bytes, storage_path, created_at) VALUES (?,?,?,?,?)");
    $stmt->execute([$repoId, $sha, $size, $path, now()]);
    $blobId = (int)$db->lastInsertId();

    return ['blob_id' => $blobId, 'sha' => $sha, 'path' => $path, 'size' => $size];
  }

  public function readBlobById(PDO $db, int $blobId): string
  {
    $stmt = $db->prepare("SELECT storage_path FROM blobs WHERE id=? LIMIT 1");
    $stmt->execute([$blobId]);
    $row = $stmt->fetch();
    if (!$row) throw new RuntimeException("Blob not found");
    $path = $row['storage_path'];
    if (!is_file($path)) throw new RuntimeException("Blob file missing");
    return file_get_contents($path);
  }
}