はじめに
baserCMS 4系は最近サポートが終了しましたが、まだ現役で運用されているサイトも多いのではないでしょうか。今回は、そんなbaserCMS 4系を運用・保守している方に向けて、実際に遭遇したハマりポイントと対処法を紹介します。
baserCMS 4系を、MySQLのデフォルトのsql_mode(STRICT_TRANS_TABLES、NO_ZERO_DATE等)が有効な環境に構築すると、特定の操作で突然PDOExceptionが発生することがあります。
原因は、baserCMSが「公開期間の制限なし」を表す慣習的な値として使っている'0000-00-00 00:00:00'(ゼロ日付)です。
レンタルサーバーやお客様が用意したサーバーでは、DB側のsql_mode設定を自由に変更できないケースも多く、地味に困るポイントだったので、対処方法と、実際に作成したプラグインを紹介します。
すでに4系はサポートが終了しているため、この問題がコア側の修正で解消される可能性は低いと考えられます。そのため、アプリケーション側で対処する方法をまとめておく必要があると考え、この記事を書きました。
こんな方におすすめです。
- baserCMS 4系を現在も運用・保守している方
- レンタルサーバーなど、MySQLの
sql_mode設定を自由に変更できない環境で運用している方 - 「限定公開ファイル」機能などでInternal Errorが発生し、原因が分からず困っている方
起きる問題:ゼロ日付とPDOException
strictなsql_modeの環境で、'0000-00-00 00:00:00'をdate/datetime/timestamp/time型のカラムに対して、SELECTの条件やINSERT/UPDATEの値として使おうとすると、以下のようなエラーになります。
PDOException: SQLSTATE[HY000]: General error: 1525
Incorrect DATETIME value: '0000-00-00 00:00:00'
(MySQLのバージョンによっては1292系のエラーコードになる場合もあります)
たとえばlib/Baser/Plugin/Uploader/Controller/UploaderFilesController.phpの限定公開ファイル配信処理では、「公開期間の制限なし」を表すために
['UploaderFile.publish_begin' => '0000-00-00 00:00:00']
という条件をそのままSELECT文のbind値として使っています。そのため、strictなsql_modeの環境では、単にファイルにアクセスするだけでこのエラーが発生し、Internal Errorになってしまいます。
最も簡単な解決策は、MySQLのsql_modeからNO_ZERO_DATEを外すなどの設定変更ですが、共有のレンタルサーバーやお客様側で管理しているサーバーでは、DBの設定自体に手を入れられないことがよくあります。今回は、DB側を触らずにアプリケーション側で対処する方法を2つ紹介します。
対処方法1:BcMysqlをapp/Model/で上書きする
app/Model/Datasource/Database/BcMysql.phpを作成し、以下のように標準のBcMysqlを上書きします。
<?php
App::uses('Mysql', 'Model/Datasource/Database');
class BcMysql extends Mysql
{
/**
* テーブル名のリネームステートメントを生成
*
* @param string $sourceName
* @param string $targetName
* @return string
*/
public function buildRenameTable($sourceName, $targetName)
{
return "ALTER TABLE `" . $sourceName . "` RENAME `" . $targetName . "`";
}
/**
* {@inheritDoc}
*/
public function value($data, $column = null, $null = true)
{
$value = parent::value($data, $column, $null);
switch ($column) {
case 'date':
case 'datetime':
case 'timestamp':
case 'time':
if ($data === '0000-00-00 00:00:00' || $data === '0000-00-00') {
return $null ? 'NULL' : "'" . date('Y-m-d H:i:s', 0) . "'";
}
}
return $value;
}
}
ゼロ日付が渡されたときにNULLへ変換することで、strictなsql_mode環境でもエラーを起こさずに済みます。
- メリット:とにかく簡単。ファイルを1つ置くだけで対応できる。
-
デメリット:
app/Model/配下でのコア上書きは推奨されない方法です。プロジェクトを引き継いだ別の開発者が見たときに、どこでカスタマイズされているかが分かりにくくなります。
対処方法2:MysqlZeroDatePatchプラグインを使う
同じ対処を、baserCMSのプラグインの仕組みに乗せて行えるようにしたのがMysqlZeroDatePatchです。
- メリット:導入・有効化だけで済み、比較的簡単
- メリット:プラグインとして分離されているため、「ゼロ日付対策をしている」ことが一目で分かる
導入方法
このリポジトリのフォルダをapp/Plugin/にコピーし、baserCMSの管理画面(プラグイン管理)からインストール・有効化するだけです。app/Config/bootstrap.phpやapp/Config/database.phpなど、フォルダ外のファイルを変更する必要は一切ありません。
有効/無効の切り替え
- baserCMSのプラグイン管理画面で、プラグイン自体を有効/無効にできます。
- プラグインは有効のまま、パッチ処理だけを一時的に止めたい場合は、プラグイン内の
config.phpでも切り替え可能です。
$config['enabled'] = true; // 有効(デフォルト)
$config['enabled'] = false; // 無効
導入後、実際にプロジェクトで発生していた「限定公開ファイル配信でInternal Errorになる」現象(UploaderFilesControllerのSELECTで1525エラー)が解消することを確認しています。
技術的な補足:なぜ実行時に差し替える方式にしたのか
このプラグインは、baserCMS標準のBcMysqlデータソースにゼロ日付ガードを追加したサブクラス(Model/Datasource/Database/BcMysqlZeroDatePatch.php)を用意し、ConnectionManagerにキャッシュ済みの'default'データソースのインスタンスを、既存のPDO接続を引き継いだままこのサブクラスのインスタンスへ実行時に差し替える、という方式を採っています。
最初に試したのは、標準のBcMysqlをプラグイン側の同名ファイルで差し替え、App::buildでModel/Datasource/Databaseの検索パスにプラグインのパスを追加する(プラグインが読み込まれた時点で、標準のBcMysqlより先にプラグイン自身のファイルが見つかるようにする)という方式でした。
しかしbaserCMS本体(lib/Baser/Config/bootstrap.php)は、プラグインのConfig/bootstrap.phpを読み込むより前にBcSite::findAll()等で最初のDBアクセスを行っており、その時点で'default'コネクションのデータソースがコア版のBcMysqlとして生成・キャッシュされてしまいます。つまり、プラグイン自身のConfig/bootstrap.phpにどれだけ早くApp::buildを書いても手遅れで、実際にこの方式では効果がありませんでした('0000-00-00 00:00:00'を含むSELECTで1525エラーが再現し続けました)。
そこで、「先に検索パスを通して解決させる」のではなく、ConnectionManagerに既にキャッシュされているデータソースのインスタンス自体を、ゼロ日付ガード付きのサブクラスのインスタンスへ実行時に差し替える方式に変更しました。この差し替え処理はプラグインが読み込まれるタイミング(通常のリクエスト処理の早い段階)で行われ、実際にコントローラのアクションでクエリが発行されるよりも十分前になるため、通常の方法(フォルダをコピーしてプラグイン管理画面で有効化)で導入するだけで確実に効きます。
おわりに
MySQLのstrictなsql_modeとbaserCMSのゼロ日付慣習が噴火するポイントは、意外と踏みやすい割に、DB設定を変えられない環境だと打つ手が見えにくい問題です。
とりあえず動かしたいだけなら方法1(app/Model/での上書き)で十分ですが、プロジェクトを保守していく前提であれば、カスタマイズ箇所が明確に分離できる方法2(プラグイン)がおすすめです。
現在baserマーケットへの公開準備中ですので、それまではGitHubから直接導入してみてください。
ダウンロードはこちらから
MysqlZeroDatePatch(GitHub)
追記(2026/08/12)
もっと簡単な方法があることが判明しました。app/Config/database.phpの接続設定に、以下のようにsettingsを追記するだけで、DB側の設定変更もコアの上書きもプラグイン導入も不要で解消できます。
'settings' => array(
'sql_mode' => "NO_ENGINE_SUBSTITUTION",
),
PDO接続時に渡すsql_modeをこの値で明示的に上書きすることで、サーバー側のグローバル設定はそのままに、このコネクションのsql_modeからNO_ZERO_DATEやSTRICT_TRANS_TABLESを外すことができます。
実際の記述例は以下の通りです($defaultのencodingの下に追記しています)。
<?php
//
// Database Configuration File created by baserCMS Installation
//
class DATABASE_CONFIG {
public $default = array(
'datasource' => 'Database/BcMysql',
'persistent' => false,
'host' => 'データベースのホスト',
'port' => '3306',
'login' => 'DBのユーザー',
'password' => 'DBのパスワード',
'database' => 'DB名',
'schema' => '',
'prefix' => 'プレフィックス',
'encoding' => 'utf8', // ← ,を追加し忘れないように
// ここに追記
'settings' => array(
'sql_mode' => "NO_ENGINE_SUBSTITUTION",
)
);
public $test = array(
'datasource' => 'Database/BcMysql',
'persistent' => false,
'host' => 'データベースのホスト',
'port' => '3306',
'login' => 'DBのユーザー',
'password' => 'DBのパスワード',
'database' => 'DB名',
'schema' => '',
'prefix' => 'プレフィックスtest_',
'encoding' => 'utf8',
);
}
encodingの行末に,を追加し忘れると構文エラーになるので注意してください。
レンタルサーバーなど、サーバー側のsql_modeのグローバル設定を変更できない環境でも、この方法ならbaserCMSの設定ファイル1箇所の変更だけで対応できます。コードの上書きやプラグインの導入が不要なため、まずはこちらを試してみることをおすすめします。
