PHP 版のインストール
PHP 版は、そのままレンタルサーバーに置けば動きます。Composer も、パッケージマネージャーも、 常駐させるサービスも必要ありません。ディレクトリがひとつ、設定ファイルがひとつ、cron がひとつ。 このチュートリアルは、空のディレクトリから本番稼働までを通しで扱います。多くの手引きが飛ばす ところ、つまり権限、TLS、cron、バックアップ、更新も含めて。
必要なもの#
| 項目 | バージョン | なぜ必要か |
|---|---|---|
| PHP | 8.1 以上 | ユニオン型、enum、readonly、never。コードが使っています。 |
pdo_sqlite | — | データベースそのもの。これがなければ何も動きません。 |
mbstring | — | 16 言語での文字列の正しい扱い。 |
openssl | 推奨 | データベース内の秘密情報(SMTP パスワード、遠隔保管先のトークン)の暗号化。 |
curl | 推奨 | Webhook の送出と、バックアップの外部送信。 |
intl | 推奨 | Node 版と同じ日付表記。なければ月名は辞書から取られます。 |
インストーラーはこれらをひとつずつ確認し、画面に表示してから先へ進ませます。必須の 項目が欠けていればインストールは中断し、任意の項目は知らせるだけで妨げません。
composer で入れるものは何もありません。IMAP の読み取り、tar.gz の書庫、
PDF、CSV、QR コード、HTTP クライアントはすべて製品の中に書かれています。だからこそ、
コマンドラインすら使えないサーバーにも置けるのです。
1. ファイルを置く#
PHP 版の書庫を入手して転送します。構成はこうなっています。
toutadmin/
├── app/ コード(Web サーバーは決して配信しません)
│ ├── Core/ カーネル:ルーティング、DB、セッション、セキュリティ
│ ├── Modules/ 業務:人事、経理、案件、品質……
│ ├── Controllers/
│ ├── views/ テンプレート
│ ├── locales/ 16 の辞書
│ └── schema.sql Node 版とまったく同じ DB 構造
├── public/ ← ドキュメントルートはここ、他のどこでもありません
│ ├── index.php フロントコントローラー
│ ├── css/ js/
├── data/ DB、金庫、署名綴じ、受領書類、バックアップ
├── tools/ cron.php、check-keys.php
├── tests/
├── config.sample.php
└── VERSION
public/、ディレクトリ全体ではありません
このページで一番大事な一点です。サーバーがディレクトリ全体を配信すると、
data/app.sqlite は誰でもダウンロードできてしまいます。パスワードのハッシュも
金庫も含めたデータベースまるごとです。data/ が public/ の外にあるのは
そのためです。
レンタルサーバーの場合#
二つの場合があります。ドメインのルートを選べる事業者(cPanel、Plesk、「ルートディレクトリ」を
指定できるサーバー全般)なら、ディレクトリ全体を www/ の外に置き、ドメインを
toutadmin/public に向けてください。
ルートが固定されている場合——多くは www/ か public_html/ ——
public/ の中身をそこに置き、残りをその一階層上に置いて、
public/index.php の先頭にあるパスを書き換えます。
www/ ← 固定されたルート
├── index.php
├── css/ js/
toutadmin/ ← 残り、Web から届かない場所
├── app/
├── data/
└── config.php
// www/index.php —— 直す一行
require_once __DIR__ . '/../toutadmin/app/bootstrap.php';
どうしてもルートの外に何も置けないときのために、data/ に同梱の
.htaccess がアクセスを拒否します。これは安全帯であって解決策ではありません。
置けるなら必ずルートの外へ。
2. 設定を書く#
cp config.sample.php config.php
そして config.php を開きます。
<?php
return [
// SQLite の DB とデータ用ディレクトリ。ドキュメントルートの外に置きます。
'db_path' => __DIR__ . '/data/app.sqlite',
'data_dir' => __DIR__ . '/data',
// このインスタンス固有の鍵。一度だけ生成します:
// php -r "echo bin2hex(random_bytes(32));"
'session_secret' => '…',
// インストール用トークン。次の手順を参照。
'install_token' => '',
// メールに入るリンクで使う公開アドレス。
'base_url' => 'https://intranet.mise.co.jp',
// 信頼できるプロキシの背後にあるときだけ有効にします。
'trust_proxy' => false,
// 上限:アドレスごと 15 分あたりのログイン試行回数と、
// アドレスごと 1 分あたりのリクエスト数。
'login_rate_limit' => 10,
'global_rate_limit' => 300,
// 期限:無操作での時間と、何をしても延びない絶対的な上限。
'session_idle_minutes' => 60,
'session_max_hours' => 12,
];
署名綴じの署名を封じ、データベース内の秘密情報を暗号化します。失うと既存の署名は検証
できなくなります。見本の値のままにすると、製品が data/.instance-key に自分で
生成します(権限 0600)。その場合はこのファイルもバックアップに含めてください。
設定は別の場所にも置けます。環境変数 TOUTADMIN_CONFIG で別のファイルを指せます。
3. インストール用トークンを決める#
ファイルを置いてからあなたがインストーラーを開くまでの間、このインスタンスは先に 見つけた者のものです。最初に着いた人が管理者アカウントを作ります。短い隙間ですが、 それで十分です。だから公開する前にランダムな値を入れておきます。
php -r "echo bin2hex(random_bytes(16));"
# そして config.php に:
'install_token' => 'c3f1…',
インストーラーはこれを求め、一定時間で照合し——素朴な比較ではトークンが 一文字ずつ言い当てられます——拒否のたびに記録します。トークンがなければ何も作られません。 インストールが済むとインストーラーは自分で閉じます。トークンは残しておいて構いません。 もう何の役にも立ちません。
4. 権限#
| パス | 権限 | 誰が書くか |
|---|---|---|
data/ | 0770 | Web サーバー、それだけ |
data/app.sqlite | 0660 | インストール時に作られます |
config.php | 0640 | 誰も——サーバーからは読むだけ |
app/、public/ | 0755 | 誰も——読むだけ |
# PHP が www-data として動くサーバーの例
sudo chown -R nin:www-data /var/www/toutadmin
sudo find /var/www/toutadmin -type d -exec chmod 750 {} \;
sudo find /var/www/toutadmin -type f -exec chmod 640 {} \;
sudo chmod 770 /var/www/toutadmin/data
sudo chmod 750 /var/www/toutadmin/public
SQLite は DB の隣に付随ファイル(-wal、-shm)も書きます。書き込み可能で
なければならないのはディレクトリであって、DB ファイルだけではありません。
5. Web サーバー#
Nginx#
server {
listen 443 ssl http2;
server_name intranet.mise.co.jp;
root /var/www/toutadmin/public; # 他のどこでもなく
index index.php;
ssl_certificate /etc/letsencrypt/live/intranet.mise.co.jp/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/intranet.mise.co.jp/privkey.pem;
client_max_body_size 20M; # 金庫への預け入れ、受領書類
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# 預けられたファイルは決して直接配信しません。セッションと
# ファイルのハッシュを確かめるルートを必ず通ります。
location ~ ^/(data|app|tools|tests)/ { deny all; }
}
server {
listen 80;
server_name intranet.mise.co.jp;
return 301 https://$host$request_uri;
}
Apache#
<VirtualHost *:443>
ServerName intranet.mise.co.jp
DocumentRoot /var/www/toutadmin/public
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/intranet.mise.co.jp/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/intranet.mise.co.jp/privkey.pem
<Directory /var/www/toutadmin/public>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
同梱の public/.htaccess がすべてのアドレスを index.php に回します。
Apache のバーチャルホストなら、これで足ります。
TLS#
接続が暗号化されているときだけ、製品は Strict-Transport-Security ヘッダーを付け、
平文のときは決して付けません。暗号化されていないページから宣言してもブラウザーは受け取らず、
かえって手元の試用を半年ものあいだ https に縛りかねないからです。自分で管理する
サーバーなら:
sudo certbot --nginx -d intranet.mise.co.jp
前段のプロキシやロードバランサーが TLS を終端している場合は 'trust_proxy' => true
にします——その場合だけです。そうでないと、誰でもヘッダーで好きな IP とプロトコルを名乗れて
しまいます。
6. cron#
PHP のサイトはリクエストがあるときしか動きません。Node 版が毎時の巡回でこなす 8 つの仕事は、 ここではサーバーの cron が引き受けます。
* * * * * /usr/bin/php /var/www/toutadmin/tools/cron.php >> /var/log/toutadmin-cron.log 2>&1
| cron がすること | 頻度 |
|---|---|
| 契約が終わったアカウントを閉じる | 毎回 |
| 期日の来た定期契約を請求する | 毎回、二重請求はしません |
| 期限を通知に変える | 毎回、重複は除きます |
| 既読の通知と監査記録を片づける | 設定した保存期間に従って |
| 経理のメールボックスを読む(IMAP) | 取り込みを設定していれば |
| Webhook の待ち行列を再試行ごと流す | 毎回 |
| 自動バックアップを作り外部へ送る | 設定した間隔が来たとき |
設定した間隔より頻繁にスクリプトを呼んでも、バックアップが増えるわけではありません。時が来て
いなければ何も起きないだけです。サーバーが cron を 15 分おきに制限しているなら
* * * * * を */15 * * * * にしてください。Webhook がその分だけ遅れて
出ていくだけです。
入門向けのサーバーには無いことがあります。外部サービス(死活監視など)からの HTTP 呼び出しは
使えません。cron.php はコマンドライン以外からの実行を拒みます。公開アドレスから
バックアップを起こさせないためです。その場合はバックアップ画面から手で実行してください。
7. インストーラー#
ドメインを開きます。アカウントがひとつも無いあいだ、すべてのアドレスは
/installation に向きます。全体がひとつのページに収まっています。
- 前提条件を一つずつ確認して表示——必須のものは明示されます。
- 設定してあればインストール用トークン。
- 会社:あちこちに出てくる名称と、(フリーランス以外の)新入社員に与える年次有給休暇の日数。
- インスタンスの既定の言語。16 言語から選びます。
- 管理者アカウント:メールアドレスと、12 文字以上のパスワード。
すべてがひとつのトランザクションで書き込まれます。アカウントができると
/installation はログイン画面へ向きます。インストーラーは自分で閉じるので、
消すファイルはありません。
8. 動いていることを確かめる#
# テスト。手元で動かします(あなたの DB には書きません)
php tests/run.php
# 翻訳:辞書に無い鍵を使っている画面がひとつも無いこと
php tools/check-keys.php
それから画面で。メンバーを作り、そのアカウントでログインし、添付ファイルを預け、バックアップを 手で実行してバックアップ画面で検証します。この四つで DB、ファイル、権限、書庫に触れます。 つまりサーバーに求められることのすべてです。
9. 更新する#
# 1. まずバックアップ。いつでも
php tools/cron.php # または「バックアップ」画面から
# 2. データではなくコードを入れ替えます
# app/ public/ tools/ tests/ VERSION ← 入れ替え
# data/ config.php ← そのまま
# 3. DB は最初のリクエストで自分を移行します
DB 構造は冪等な移行で進みます。更新を二度走らせても何も壊れません。Node 版のインスタンスと PHP 版のインスタンスは同じ構造——142 表、1311 列——と 同じパスワードを共有します。データベースは変換なしにどちらへでも移せます。
10. 本当にバックアップする#
tar.gz の書庫には、データベース(VACUUM INTO で複製するので書き込みの
最中でも整合します)と、五つのファイル用ディレクトリが入ります。顔写真、受け取った履歴書、
金庫、署名綴じ、受領書類です。書庫の各ファイルには SHA-256 が添えられ、復元時に一つずつ
照合されます。
守っているはずのサーバーに置いたままの書庫は、何も守りません。バックアップ画面で 外部の保管先(FTPS または Google ドライブ)を設定してください。詳しくは バックアップと復元のページへ。
まず手元で試す#
cp config.sample.php config.php
php -S localhost:8000 -t public public/index.php
PHP 内蔵のサーバーで製品を一通り見ることはできます。本番向きではありません。プロセスはひとつ、 TLS なし、流量の制限もありません。
Toutadmin ドキュメント —— 2026-09-13 に生成。 ソフトウェアから独立した単体のサイトです。