素の PHP スクリプトに .env とエラーログ機能を足してみる
このごろは Laravel か WordPress を使った開発が多いので、フレームワークを使わない PHP を使うことはめっきり減ったのですが、ちょっとしたメールフォームくらいであれば素の PHP で簡単に作ってしまったほうが楽だったりします。そんなときに不便に感じるのが、設定周りとエラー処理。というわけで、素の PHP に設定周りとエラー処理をトッピングしてみることにします。
必要なパッケージのインストール
使うパッケージは、Laravel でも使われている vlucas/phpdotenv と Seldaek/monolog を使います。重大なエラーが発生したときにメールで通知するようにしたいので、symfony/mailer を追加します。ということで、以下のコマンドでインストールします。
composer require vlucas/phpdotenv Seldaek/monolog symfony/mailer
.env の作成
ログ周りの設定については、.env に以下のような項目を作成しました。
HOSTNAME='www.example.com' # メール通知の見出しなどに使うホスト名
LOGFILE='/var/log/php_error.log' # ログファイルの置き場所
MAX_LOGFILES=14 # ログファイルの保存数。設定した数を超えると古いものから削除される。0 に設定すると無制限に保存される(デフォルト)
MAIL_HOST='example.com' # SMTP のホスト名
MAIL_USER='webmaster@example.com' # SMTP のアカウント名
MAIL_PASSWORD='password' # SMTP のパスワード
MAIL_PORT='587' # SMTP のポート番号
MAIL_OPTIONS='{"verify_peer":false}' # SSL(TSL)接続時に SSL サーバー証明書の検証を要求するかどうか。true が望ましいが、SSL 周りでエラーが出るときは false に設定
MAILER_DSN='smtp://webmaster%40example.com:password@example.com:587?verify_peer=0' # DSN 形式で書きたいときはこちらで。@ は %40 に置き換える必要があります
ERROR_REPORT_MAIL_FROM='webmaster@example.com' # エラー通知の FROM アドレス
ERROR_REPORT_MAIL_TO='user@example.com' # エラー通知の TO アドレス
設定とエラーログの共通処理
本題の共通処理部分はこんな感じで書いてみました。require_once のパスなどは適宜調整してください。
<?php
require_once __DIR__.'/vendor/autoload.php';
// 日本時間にしておかないと時刻がUTCになってしまう
date_default_timezone_set("Asia/Tokyo");
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\SymfonyMailerHandler;
use Monolog\Logger;
use Monolog\ErrorHandler;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Transport\Dsn;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransportFactory;
use Symfony\Component\Mime\Email;
$logger = new Logger('local');
// PHP のエラーを Monolog で扱うための設定
ErrorHandler::register($logger);
// エラーをログに記録
// 日付ごとに1ファイル。$_ENV['LOGFILE'] に /var/log/php_error.log を指定すると
// php_error-2023-02-13.log のようなファイルが生成される
$logger->pushHandler(new RotatingFileHandler($_ENV['LOGFILE'], $_ENV['MAX_LOGFILES']));
// メール通知のための設定
$subject = 'エラーレポート(' . $_ENV['HOSTNAME'] . ')';
$email = (new Email())
->from($_ENV['ERROR_REPORT_MAIL_FROM'])
->to($_ENV['ERROR_REPORT_MAIL_TO'])
->subject($subject);
$dsn = new Dsn(
'smtp',
$_ENV['MAIL_HOST'],
$_ENV['MAIL_USER'],
$_ENV['MAIL_PASSWORD'],
$_ENV['MAIL_PORT'],
json_decode($_ENV['MAIL_OPTIONS'], true)
);
//$dsn = Dsn::fromString($_ENV['MAILER_DSN']); // DSN 形式を使いたいときはこちらで
$factory = new EsmtpTransportFactory();
$transport = $factory->create($dsn);
$mailer = new Mailer($transport);
// エラーレベルが ERROR 以上のエラーが発生したときはメール通知する
$logger->pushHandler(new SymfonyMailerHandler($mailer, $email, Logger::ERROR));
ちなみに、エラーログは以下のような感じになります。19行目で指定した文字(local)がログの中に記載されるので、複数のドメインを1つのサーバで扱っているような場合には、この部分をドメイン名にすることでログが識別しやすくなります。
[2023-02-13T17:53:24.835594+09:00] local.WARNING: E_WARNING: Undefined variable $description {"code":2,"message":"Undefined variable $foo","file":"/home/example/www/example.com/inquiry/thanks.php","line":8} []
あとは、このスクリプトを個々のスクリプトファイルの中で require_once すれば、機能を使うことができます。
できあがったスクリプトを見ると、簡単に書けているように見えますが、Monolog も Symfony Mailer も単独で使用する場合の使用例が少なく、意外に時間がかかりました。同じようなことをやりたい方の参考になればと思います。
おまけ:Symfony Mailer で SMTP を使ったメール送信をする
Monolog を使った場合、$email と $mailer を SymfonyMailerHandler に渡しますが、素の Symfony Mailer でメールを送信する場合はこんな感じになります。
<?php
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\Transport\Dsn;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransportFactory;
use Symfony\Component\Mime\Email;
require_once 'vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
$dsn = new Dsn(
'smtp',
$_ENV['MAIL_HOST'],
$_ENV['MAIL_USER'],
$_ENV['MAIL_PASSWORD'],
$_ENV['MAIL_PORT'],
json_decode($_ENV['MAIL_OPTIONS'], true)
);
// $dsn = Dsn::fromString($_ENV['MAILER_DSN']);
$factory = new EsmtpTransportFactory();
$transport = $factory->create($dsn);
$mailer = new Mailer($transport);
$text_content = $_ENV['HOSTNAME'] . 'に Fatal Error が出ています。ログをご確認ください' . "\n";
$subject = 'エラーレポート(' . $_ENV['HOSTNAME'] . ')';
$email = (new Email())
->from($_ENV['ERROR_REPORT_MAIL_FROM'])
->to($_ENV['ERROR_REPORT_MAIL_TO'])
->subject($subject)
->text($text_content);
try {
$mailer->send($email);
} catch (TransportExceptionInterface $e) {
echo 'Caught exception: ' . $e->getMessage() . "\n";
}
ディスカッション
コメント一覧
まだ、コメントがありません