crontabでcakePHPのShellが実行できなかったときの話

これは本当に困りました。
ある日cakePHPでShell化をしていたら、与えられた環境でcronでcakePHPのShellが実行できなかったのです。
困ったので、ログファイルを書き出したら普通に10GBを超えてしまった。これはヤバい。開けない…笑
調べても情報がなかったし、違うサーバーでは普通に実行できたりして…
苦労したのでまとめておきます。
cakePHPは2.2系です。

◆そもそもShellってなんじゃらほい?
cakePHPはURLで処理を実行できて便利なのですが、データベースとかcakePHP&cronで処理したいけどURLでやってたらセキュアじゃないよね。スマートじゃないよね?
ってことで、コマンドベースで実行できるのがcakePHPのShellなのです。
使い方は簡単!
詳しくはこちらを参照してください→CakePHP 2.x で Cron を使う
1.appパス/Console/Command/MyShell.phpを作成します。(MyShell Class)
2.中身の記述は下記の通り

<?php
class MyShell extends AppShell {
	// モデルを使用する場合はここに記述。
	public $uses = array('Member');

	// メソッドを無名にする場合は "main" を用いる
	public function main() {
		echo "Hello world!\n";
	}

	function myMethod() {
		// メソッド名を指定する場合。
	}
}

3.Shellを起動するためのコマンドファイルを作成する。(ここでは仮に/home/script/cakeというファイル)
4.中身は下記の通り(bash、phpのパスは環境によって違います。)

#!/bin/bash
/usr/bin/php /home/appパス/Console/cake.php my >> /home/log/cron_cake.log 2>&1

/usr/bin/phpは各自phpのパスに読み替えてください。
cake.phpを読み込んで「my」という引数を与えています。(MyShellを起動するという意味。)
その後にメソッド名を記述するのですが、省略すればmain()が自動的に起動します。
「>> /home/log/cron_cake.log 2>&1」の記述は/home/log/cron_cake.logにログを保存して終了!って意味です。
5.cronのこのファイル(/home/script/cake)を起動するように記述

さて、ここまでやって問題が生じました。
cronが実行されないのです。
ログをみてみたら11GBを超えて、しかも増え続けている…!!!

「これはヤバい!」

ぼくの直感がそう感じました。
とにかくヤバいのです。
とりあえず、ヤバいヤバいと思いながらその日は寝ました。
翌日も必死でいろいろやりましたが同じ状況。
もちろんネットでもいろいろ調べました。
英語も読みました。けど出てこない!!
そして、最終的にログファイルを分割して頭の部分だけ読むことに成功しました。
最初の行を見てみると…

PHP Notice:  Undefined variable: argv in /home/…appパス/Console/cake.php on line 33

ん??むむむ…
argvが未定義?argvって聞いたことあるぞ、見たことあるぞ。。。
とりあえず/home/…appパス/Console/cake.phpを開いてみました。

#!/usr/bin/php -q
<?php
/**
 * Command-line code generation utility to automate programmer chores.
 *
 * PHP 5
 *
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright 2005-2012, Cake Software Foundation, Inc.
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @package       app.Console
 * @since         CakePHP(tm) v 2.0
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 */
$ds = DIRECTORY_SEPARATOR;
$dispatcher = 'Cake' . $ds . 'Console' . $ds . 'ShellDispatcher.php';

if (function_exists('ini_set')) {
	$root = dirname(dirname(dirname(__FILE__)));
	ini_set('include_path', $root . $ds . 'lib' . PATH_SEPARATOR . ini_get('include_path'));
}

if (!include ($dispatcher)) {
	trigger_error('Could not locate CakePHP core files.', E_USER_ERROR);
}
unset($paths, $path, $dispatcher, $root, $ds);

return ShellDispatcher::run($argv);

ありましたね。「argv」!!
そして定義している場所がない!!
そうです、外部から与えられた引数が入るところです。
そして、見つけました。根本原因。
PHPのマニュアル「argv」の項目です。

注意: この変数は、register_argc_argv が無効になっている場合には使えません。

http://php.net/manual/ja/reserved.variables.argv.php
もしや…!!!

register_argc_argv boolean
PHPが変数argvとargcを宣言するかどうかを指定します (これらにはGETの情報が格納されます)。コマンドラインも参照ください。

http://www.php.net/manual/ja/ini.core.php#ini.register-argc-argv
はい。php.iniが悪かったみたいです。
知らねーよ!そんなん!!
cakePHPもそこチェックして処理止めろよ!とか思います。10GB超えるログとかみたくありません。
なので…
以下のように/home/…appパス/Console/cake.phpの末尾を変更しときました。

// /home/…appパス/Console/cake.php
unset($paths, $path, $dispatcher, $root, $ds);
if(!isset($argv)) exit;
return ShellDispatcher::run($argv);

処理の強制終了です。笑
エラーも出してないのは良くないなって思うので

throw new Exception('argvが未定義');

とかやっとくと親切かもしれません。
とにかく今回は疲れました。もうやだ!笑
てなわけであまり使ってる人がいない気がするcakePHPのShellのお話でした。
使いこなせれば便利だよ!!


追記:PHPのバージョン5.2.8以上じゃないとShell起動時にエラーが出ます。

PHP Warning:  SplFileInfo::openFile(/home/...appパス/tmp/cache/persistent/myapp_cake_core_file_map): failed to open stream: Success in /home/...Cakeパス/lib/Cake/Cache/Engine/FileEngine.php on line 313

Warning: SplFileInfo::openFile(/home/...appパス/tmp/cache/persistent/myapp_cake_core_file_map): failed to open stream: Success in /home/...Cakeパス/lib/Cake/Cache/Engine/FileEngine.php on line 313
PHP Notice:  Undefined index:  tag in /home/...Cakeパス/lib/Cake/Console/ConsoleOutput.php on line 200

Notice: Undefined index:  tag in /home/...Cakeパス/lib/Cake/Console/ConsoleOutput.php on line 200
PHP Notice:  Undefined index:  emergency in /home/...Cakeパス/lib/Cake/Console/ConsoleOutput.php on line 215


こんな感じのエラーが2000行くらい出力されます。
レンタルサーバーによっては複数のphpがインストールされていることがあり、/usr/bin/phpコマンドではphpの古いバージョンが起動されて上記の現象が起こることがあります。
その場合は、phpのコマンドを確認して/usr/bin/php5.3などに書き換えてください。
とおもったけど、これもphp.iniの設定の問題かも。
apc.enabledがtrueじゃないとだめだからね。
複数のphpがインストールされているような環境ではphp.iniも複数(ドメインごと)あることが多いので、それを明示的に読み込まないとシステムデフォルトが適用されるので、それが原因になることがあるのではないかと。
対処法は下記の通り。

#!/bin/bash
/usr/bin/php5.3 -c /home/php.iniのパス/php.ini /home/appパス/Console/cake.php my >> /home/log/cron_cake.log 2>&1


-cのオプションでphp.iniを明示的に読み込んでいます。
php.iniの場所は調べるか、サーバー屋さんに聞くか、探しましょう。
以上!

    • Victoria
    • 2014 08/20 1:46pm

    I think other web-site proprietors should take this web site as an model, very clean and wonderful user friendly style and design, let alone the content. You’re an expert in this topic!

  1. 2013 11/24
    トラックバック先:創作メモ帳

return top