ディレクトリ構成と規約 - ha1t/php-gm-server GitHub Wiki
php-gm-server プロジェクトのディレクトリ構成は、REACTPHPによるイベント駆動型Geminiサーバーを実装するため、機能別に整理されています。本ページはプロジェクトディレクトリ構成(bin/、src/、tests/、content/、certs/)、各ディレクトリの役割、ファイル配置規約、PSR-4オートロード規約、および.gitignoreでの除外ファイルについて説明します。本ページの内容は、プロジェクト概要およびアーキテクチャ設計と関連しています。
php-gm-serverプロジェクトのトップレベルディレクトリ構成は、以下のとおりです。
php-gm-server/
├── bin/ # 実行可能なエントリーポイント
├── src/ # メインコンポーネント(GeminiServer、RequestParser等)
├── tests/ # ユニットテスト
├── content/ # Geminiコンテンツ(.gmi ファイル)
├── certs/ # TLS/SSL証明書
├── composer.json # PHP依存パッケージ定義
├── phpunit.xml # PHPUnit テスト設定
├── README.md # プロジェクト説明
└── .gitignore # Git除外ファイル定義
以下、各ディレクトリの詳細な役割と配置規約を説明します。
bin/ディレクトリは、プロジェクトの実行可能なエントリーポイント(CLIスクリプト)を格納します。このディレクトリに配置されたPHPスクリプトは、コマンドラインから直接実行されます。
| ファイル | 役割 |
|---|---|
server.php |
Geminiサーバーの起動スクリプト。環境変数を読み込み、証明書生成、サーバー初期化、実行ループ起動を行う。 |
bin/server.php(リポジトリ参照)は、以下の処理フローに従います。
- Composerオートローダーを読み込む(
require __DIR__ . '/../vendor/autoload.php') - 環境変数から設定値を取得する(GEMINI_HOST、GEMINI_PORT、GEMINI_DOC_ROOT、GEMINI_CERT_DIR、GEMINI_HOSTNAME)
- CertificateGeneratorで自己署名証明書を生成する
- StaticFileHandlerでドキュメントルートを初期化する
- GeminiServerを生成し、run()メソッドで実行ループを開始する
<?php
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use GeminiServer\CertificateGenerator;
use GeminiServer\GeminiServer;
use GeminiServer\StaticFileHandler;
$host = getenv('GEMINI_HOST') ?: '0.0.0.0';
$port = (int) (getenv('GEMINI_PORT') ?: 1965);
$docRoot = getenv('GEMINI_DOC_ROOT') ?: __DIR__ . '/../content';
$certDir = getenv('GEMINI_CERT_DIR') ?: __DIR__ . '/../certs';
$hostname = getenv('GEMINI_HOSTNAME') ?: 'localhost';
$certGenerator = new CertificateGenerator($certDir);
$certPath = $certGenerator->generate($hostname);
$fileHandler = new StaticFileHandler($docRoot);
$server = new GeminiServer($host, $port, $fileHandler, $certPath);
$server->run();出典: bin/server.php
src/ディレクトリは、Geminiサーバーの核となるコンポーネントクラスを格納します。REACTPHPベースのイベント駆動型実装により、非同期I/Oを実現しています。
| ファイル | クラス名 | 役割 |
|---|---|---|
GeminiServer.php |
GeminiServer |
TCP接続受け入れ、TLS/SSL化、リクエスト受信、バッファ管理、レスポンス送信を統括 |
RequestParser.php |
RequestParser |
Geminiリクエスト文字列(gemini://host/path\r\n)のパース、スキーム検証、ホスト名・パス抽出 |
ResponseBuilder.php |
ResponseBuilder |
Geminiプロトコル形式のレスポンス構築(STATUS SPACE META\r\n BODY) |
StaticFileHandler.php |
StaticFileHandler |
ドキュメントルートに基づくファイル解決、ディレクトリハンドリング、MIMEタイプ判定、パストラバーサル防止 |
CertificateGenerator.php |
CertificateGenerator |
OpenSSL拡張を使用した自己署名TLS証明書生成、PEM形式での保存 |
composer.jsonで以下のPSR-4マッピングが定義されています(composer.json参照)。
"autoload": {
"psr-4": {
"GeminiServer\\": "src/"
}
}PSR-4規約に従い、src/ディレクトリ内のクラスファイルは以下の命名規則で配置されます。
-
ファイル名 = クラス名 + .php
-
GeminiServer.php→GeminiServerクラス -
RequestParser.php→RequestParserクラス
-
-
ネームスペース = ディレクトリ構造
-
src/→GeminiServer\
-
例えば、src/GeminiServer.php内のクラス定義は以下のとおりです。
<?php
declare(strict_types=1);
namespace GeminiServer;
class GeminiServer
{
// クラス実装
}各コンポーネント間の処理フローを以下のシーケンスダイアグラムで示します。
sequenceDiagram
participant Client
participant GeminiServer as GeminiServer
participant RequestParser as RequestParser
participant StaticFileHandler as FileHandler
participant ResponseBuilder as ResponseBuilder
Client ->> GeminiServer: TLSハンドシェイク
GeminiServer ->> GeminiServer: バッファにデータ蓄積
GeminiServer ->> RequestParser: リクエスト文字列をパース
RequestParser -->> GeminiServer: ホスト・パス抽出結果
GeminiServer ->> StaticFileHandler: ファイル解決要求
StaticFileHandler -->> GeminiServer: (MIME, body)
GeminiServer ->> ResponseBuilder: Geminiレスポンス構築
ResponseBuilder -->> GeminiServer: ステータス・メタ・ボディ
GeminiServer ->> Client: レスポンス送信
tests/ディレクトリは、各コンポーネントのユニットテストを格納します。PHPUnitフレームワークを使用した自動テストで、実装の正確性を保証します。
| ファイル | テスト対象クラス | テスト対象 |
|---|---|---|
RequestParserTest.php |
RequestParser |
Geminiリクエストのパース処理、スキーム検証、ホスト名・パス抽出 |
ResponseBuilderTest.php |
ResponseBuilder |
Geminiレスポンス形式の構築、ステータスコード・メタ情報の正確性 |
StaticFileHandlerTest.php |
StaticFileHandler |
ファイル解決、ディレクトリハンドリング、MIMEタイプ判定、パストラバーサル防止検証 |
CertificateGeneratorTest.php |
CertificateGenerator |
自己署名証明書の生成、PEM形式ファイルの作成、既存証明書の再利用 |
composer.jsonで以下のPSR-4マッピングがテスト開発時に定義されています(composer.json参照)。
"autoload-dev": {
"psr-4": {
"GeminiServer\\Tests\\": "tests/"
}
}テストクラスはPSR-4規約に従い、以下の命名規則で配置されます。
-
ファイル名 = クラス名 + .php
-
RequestParserTest.php→RequestParserTestクラス
-
-
ネームスペース = GeminiServer\Tests + ディレクトリ構造
-
tests/→GeminiServer\Tests\
-
テストクラスの例:
<?php
declare(strict_types=1);
namespace GeminiServer\Tests;
use GeminiServer\RequestParser;
use PHPUnit\Framework\TestCase;
class RequestParserTest extends TestCase
{
public function testParseValidRequest(): void
{
$result = RequestParser::parse("gemini://example.com/hello.gmi\r\n");
$this->assertSame('example.com', $result['host']);
$this->assertSame('/hello.gmi', $result['path']);
}
}出典: tests/RequestParserTest.php:1-17
PHPUnitの実行設定はphpunit.xmlで定義されており、以下のコマンドでテストを実行します。
vendor/bin/phpunitcontent/ディレクトリは、Geminiサーバーで配信するコンテンツファイルを格納します。デフォルトのドキュメントルート(GEMINI_DOC_ROOT)として機能します。環境変数で別のディレクトリに変更可能です。
| ファイル | 用途 |
|---|---|
index.gmi |
ルートパス(/)へのアクセス時に配信されるメインページ。Gemini形式(text/gemini)のマークアップで記述。 |
sub.gmi |
サブディレクトリ内のコンテンツファイル。Gemini形式のページ。 |
Geminiコンテンツは.gmi拡張子を持つテキストファイルで、以下の形式に従います。
# Welcome to Gemini
This is a PHP Gemini server powered by ReactPHP.
## Links
=> gemini://geminiprotocol.net/ Gemini Protocol
=> gemini://geminiprotocol.net/docs/specification.gmi Gemini Specification
=> gemini://192.168.3.253/sub.gmi sub
## About
This server was built for a PHP study group presentation.
環境変数GEMINI_DOC_ROOTで、ドキュメントルートを指定できます。指定しない場合、デフォルトは__DIR__ . '/../content'です。
export GEMINI_DOC_ROOT=/path/to/custom/content
php bin/server.phpcerts/ディレクトリは、GeminiサーバーのTLS/SSL証明書を格納します。CertificateGeneratorによって自動生成される自己署名証明書を保存します。
| ファイル | 用途 | 権限 |
|---|---|---|
server.pem |
TLS/SSL証明書とRSA秘密鍵が格納されたPEM形式ファイル。GeminiServerが起動時に読み込む。 |
600(オーナー読み書き可のみ) |
CertificateGeneratorは、以下の条件で自動的に証明書を生成します。
-
certs/ディレクトリが存在しない場合は、ディレクトリを作成する -
server.pemが存在しない場合は、新規に生成する - 既存の
server.pemが存在する場合は、再利用する
環境変数により、証明書ディレクトリとホスト名を指定できます。
export GEMINI_CERT_DIR=/path/to/custom/certs
export GEMINI_HOSTNAME=example.com
php bin/server.php.gitignoreファイルは、Gitレポジトリに追跡させないファイルを指定します。php-gm-serverでは、以下のファイル・ディレクトリをGit管理の対象外としています。
/vendor/
/certs/
composer.lock
各除外定義の詳細:
| 除外対象 | 理由 |
|---|---|
/vendor/ |
Composerが管理する依存パッケージ。composer.jsonとcomposer.lockで復元可能なため、Gitレポジトリに含めない。 |
/certs/ |
サーバーの自己署名TLS証明書ディレクトリ。開発環境・本番環境ごとに異なる証明書が生成されるため、レポジトリに含めない。 |
composer.lock |
Composerの依存パッケージロックファイル。複数の開発環境での一貫性保証には有用だが、本プロジェクトではcomposer.jsonで十分とする設計判断。 |
出典: .gitignore
PSR-4(PHP Standards Recommendation 4)は、PHPのクラスオートロード規約です。ファイルシステム上のディレクトリ構造とPHPのネームスペース構造を対応させることで、自動でクラスを読み込みます。
php-gm-serverでは、composer.jsonで以下の2つのPSR-4マッピングを定義しています。
{
"autoload": {
"psr-4": {
"GeminiServer\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"GeminiServer\\Tests\\": "tests/"
}
}
}PSR-4規約に従い、以下の対応が成立します。
| ファイルパス | ネームスペース\クラス名 | 説明 |
|---|---|---|
src/GeminiServer.php |
GeminiServer\GeminiServer |
メインサーバークラス |
src/RequestParser.php |
GeminiServer\RequestParser |
リクエストパーサー |
src/ResponseBuilder.php |
GeminiServer\ResponseBuilder |
レスポンスビルダー |
src/StaticFileHandler.php |
GeminiServer\StaticFileHandler |
ファイルハンドラー |
src/CertificateGenerator.php |
GeminiServer\CertificateGenerator |
証明書ジェネレーター |
| ファイルパス | ネームスペース\クラス名 |
|---|---|
tests/RequestParserTest.php |
GeminiServer\Tests\RequestParserTest |
tests/ResponseBuilderTest.php |
GeminiServer\Tests\ResponseBuilderTest |
tests/StaticFileHandlerTest.php |
GeminiServer\Tests\StaticFileHandlerTest |
tests/CertificateGeneratorTest.php |
GeminiServer\Tests\CertificateGeneratorTest |
Composerのオートローダーは、スクリプト実行時にvendor/autoload.phpを読み込むことで機能します。bin/server.phpの例:
require __DIR__ . '/../vendor/autoload.php';
use GeminiServer\CertificateGenerator;
use GeminiServer\GeminiServer;
use GeminiServer\StaticFileHandler;
// 以降、クラス名でインスタンス化可能
$certGenerator = new CertificateGenerator($certDir);
$fileHandler = new StaticFileHandler($docRoot);
$server = new GeminiServer($host, $port, $fileHandler, $certPath);プロジェクト全体のディレクトリ構成と各要素の関係性を以下のダイアグラムで示します。
graph TD
A["php-gm-server/"] --> B["bin/"]
A --> C["src/"]
A --> D["tests/"]
A --> E["content/"]
A --> F["certs/"]
A --> G["composer.json<br/>phpunit.xml<br/>.gitignore"]
B --> B1["server.php<br/>エントリーポイント"]
C --> C1["GeminiServer.php<br/>接続管理"]
C --> C2["RequestParser.php<br/>リクエスト解析"]
C --> C3["ResponseBuilder.php<br/>レスポンス構築"]
C --> C4["StaticFileHandler.php<br/>ファイル配信"]
C --> C5["CertificateGenerator.php<br/>証明書生成"]
D --> D1["RequestParserTest.php"]
D --> D2["ResponseBuilderTest.php"]
D --> D3["StaticFileHandlerTest.php"]
D --> D4["CertificateGeneratorTest.php"]
E --> E1["index.gmi<br/>Geminiコンテンツ"]
E --> E2["sub.gmi"]
F --> F1["server.pem<br/>TLS証明書"]
G --> G1["設定ファイル"]
style B1 fill:#e1f5ff
style C1 fill:#f3e5f5
style C2 fill:#f3e5f5
style C3 fill:#f3e5f5
style C4 fill:#f3e5f5
style C5 fill:#f3e5f5
style D1 fill:#f1f8e9
style D2 fill:#f1f8e9
style D3 fill:#f1f8e9
style D4 fill:#f1f8e9
style E1 fill:#fff3e0
style E2 fill:#fff3e0
style F1 fill:#fce4ec
以下のテーブルは、ファイル配置規約の全体像をまとめたものです。
| 階層 | ディレクトリ | ファイル形式 | ネームスペース | 用途 |
|---|---|---|---|---|
| 実行 | bin/ |
.php |
N/A | CLIエントリーポイント |
| コンポーネント | src/ |
.php |
GeminiServer\ |
コアロジック実装 |
| テスト | tests/ |
.php |
GeminiServer\Tests\ |
ユニットテスト |
| コンテンツ | content/ |
.gmi |
N/A | Gemini形式ドキュメント |
| 証明書 | certs/ |
.pem |
N/A | TLS/SSL証明書 |
| 設定 | プロジェクトルート |
.json、.xml、.md
|
N/A | プロジェクト設定・ドキュメント |
新しいコンポーネントを追加する際は、以下の規約に従ってください。
-
コアロジック →
src/ディレクトリに配置- ファイル名はクラス名と同一にする(例:
MyComponent.php) - ネームスペースは
GeminiServer\として定義する
- ファイル名はクラス名と同一にする(例:
-
ユニットテスト →
tests/ディレクトリに配置- ファイル名はテスト対象クラス名に
Testサフィックスを付ける(例:MyComponentTest.php) - ネームスペースは
GeminiServer\Tests\として定義する - テストクラス名はテスト対象クラス名に
Testサフィックスを付ける(例:class MyComponentTest extends TestCase)
- ファイル名はテスト対象クラス名に
-
Geminiコンテンツ →
content/ディレクトリに配置- ファイル拡張子は
.gmiとする - テキスト形式のGeminiプロトコル仕様に準拠する
- ファイル拡張子は
- プロジェクト概要 — PHP GM Serverのプロジェクト全体像、目的、技術スタック、主な機能
- アーキテクチャ設計 — システム全体のコンポーネント構成、データフロー、設計パターン
- インストール・セットアップガイド — システム要件、Composerでの依存パッケージ導入、初回起動方法
- 環境変数設定リファレンス — GEMINI_HOST、GEMINI_PORT等の環境変数の詳細設定
- GeminiServerコンポーネント — GeminiServerクラスの役割と処理フロー
- RequestParserコンポーネント — Geminiリクエストのパース実装
- ResponseBuilderコンポーネント — Geminiレスポンス形式の構築
- StaticFileHandlerコンポーネント — ファイル解決とMIMEタイプ判定
- テスト戦略と実装 — PHPUnitによるテスト戦略と実装方法