[CakePHPのバグ]キャッシュ処理でunserializeエラーが発生する

CakePHP バージョン1系をLinuxサーバで動作させているときに、shellバッチを使っているとウェブアクセスでごくまれに次のようなエラーが出ることがあります。
ごくまれにしか発生しない現象で、気づきにくくリロードすると解消されるため調査・検証がしづらいです。

[参考記事] キャッシュ処理でunlinkエラーが発生する

Notice (8): unserialize() [http://php.net/function.unserialize]: Error at offset 4080 of 4085 bytes [CORE/cake/libs/cache/file.php, line 187]
unserialize - [internal], line ??
FileEngine::read() - CORE/cake/libs/cache/file.php, line 187
Cache::read() - CORE/cake/libs/cache.php, line 362
DataSource::__cacheDescription() - CORE/cake/libs/model/datasources/datasource.php, line 477
DboMysqlBase::describe() - CORE/cake/libs/model/datasources/dbo/dbo_mysql.php, line 155
Model::schema() - CORE/cake/libs/model/model.php, line 929
Model::setSource() - CORE/cake/libs/model/model.php, line 780
Model::__construct() - CORE/cake/libs/model/model.php, line 474
ClassRegistry::init() - CORE/cake/libs/class_registry.php, line 142
Controller::loadModel() - CORE/cake/libs/controller/controller.php, line 637
Controller::constructClasses() - CORE/cake/libs/controller/controller.php, line 502
Dispatcher::_invoke() - CORE/cake/dispatcher.php, line 186
Dispatcher::dispatch() - CORE/cake/dispatcher.php, line 171
[main] - /xxxxx/html/index.php, line xxx

これはTMP cache models以下のデータベース用キャッシュファイルがシェル実行権限のユーザーにより644でファイルが作成されたものを ウェブアクセス実行権限のユーザーが処理しようとしてエラーとなっていることが考えられます。

apacheの権限ユーザーやシェル実行権限ユーザーを変更できる場合はユーザーを共通にすることで対処できますが、これは難しいことが多々あります。

そもそもこれが起こる理由はCakePHPのバグです。

エラーが発生している箇所をたどっていくと次のようなコードになっています。

エラーの該当箇所付近
CORE/cake/libs/cache/file.php, line 187

		$data = $this->_File->read(true);

		if ($data !== '' && !empty($this->settings['serialize'])) {
			if ($this->settings['isWindows']) {
				$data = str_replace('¥¥¥¥¥¥¥¥', '¥¥', $data);
			}
			$data = unserialize((string)$data);
		}

$this->_File->read(true)の戻り値$dataを扱っています。

この$this->_Fileが定義されている箇所
CORE/cake/libs/cache/file.php, line 82

		if (!isset($this->_File)) {
			$this->_File =& new File($this->settings['path'] . DS . 'cake');
		}

つまり$this->_FileはFileオブジェクトでCORE/cake/libs/file.phpにあります。

$this->_File->read()が書かれている箇所
CORE/cake/libs/file.php, line 168

	function read($bytes = false, $mode = 'rb', $force = false) {
		if ($bytes === false && $this->lock === null) {
			return file_get_contents($this->path);
		}
		if ($this->open($mode, $force) === false) {
			return false;
		}
		if ($this->lock !== null && flock($this->handle, LOCK_SH) === false) {
			return false;
		}
		if (is_int($bytes)) {
			return fread($this->handle, $bytes);
		}

		$data = '';
		while (!feof($this->handle)) {
			$data .= fgets($this->handle, 4096);
		}

		if ($this->lock !== null) {
			flock($this->handle, LOCK_UN);
		}
		if ($bytes === false) {
			$this->close();
		}
		return trim($data);
	}

つまり$this->_File->read()の戻り値にはfalseが返ってくることがあります。

ところが
CORE/cake/libs/cache/file.php, line 184

		if ($data !== '' && !empty($this->settings['serialize'])) {

となっているため、falseでもunserialize関数に進んでしまいます。

この箇所を次のように変更することで、このバグは修正することができます。

		if ($data !== false && $data !== '' && !empty($this->settings['serialize'])) {

CakePHPバージョン2系でも
CORE/cake/lib/Cake/Cache/Engine/FileEngine.php, line 195
に同じコードがありますが、$dataは文字列型(String)で扱われているので問題はないです。

スポンサーリンク

関連記事

スポンサーリンク

CURRENT_DATE関数 現在の日付を求める

ホームページ製作・web系アプリ系の製作案件募集中です。

上に戻る