シェルでコンポーネントを使う by CakePHP2系

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
どんなスタンド能力なのか、私気になります

おまとめ三行

シェルでコンポーネントを使いたい
シェルとえるをかけている……わけではない
ロードローラーの運転には資格が必要

シェルを使って何やらデータを操作したいときってありますよね。cronを設定して自動で何か処理したいときとか、おもたーい処理をバックグラウンドでやりたいときとか。

CakePHPでシェルを使う場合、基本的にはコントローラーでやるようなことをそのまま書けば動くんだけど、ちょーっとだけ違うことをしなきゃいけない部分もある。知っとかないと困ることもある。

例えば、シェルの中でコンポーネントを使う場合。コントローラーと同じような書き方では動かない。じゃーどうすれば良いのか。私、気になります!

ってことで、調べました。



シェルの使い方

一応、さらっとおさらいしときましょう。さらっとと言いつつ、長くなりそうな予感がビンビンですけど……まるで朝起きたばかりのちn(自主規制)

appフォルダの中に「Console」ってフォルダがあって、その中にさらに「Command」というフォルダがあります。シェル用のファイルはその中に作る。

こんな風に。

/app
 └ /Console
    └ /Command
       └ SampleShell.php

シェルファイルの名前のつけ方は、基本的に「○○Shell.php」となる。先頭は大文字じゃないといけないのかな……?

ファイルの中身はこんな感じです。ざっくりと。

App::uses('Shell', 'Console');
class SampleShell extends Shell {
  public $uses = array('Model');

  public sampleFunc() {

  }
}

使いたいモデルがある場合は、コントローラーのときと同じように「$uses」で読み込むことができる。

で、この「sampleFunc」ってやつを使う場合は、以下のように書いてcronで自動的に実行したり、execとかshell_exec、systemを使ってPHPから実行したりすればオッケーっす。

//cronで毎日深夜0時に実行する場合
0 0 * * * /usr/bin/php /Cakeフォルダまでのパス/Console/cake.php SampleShell sampleFunc -app /appフォルダまでのパス/

//PHPから実行する場合(shell_execやsystemも同様)
exec('/usr/bin/php /Cakeフォルダまでのパス/Console/cake.php SampleShell sampleFunc -app /appフォルダまでのパス/');

「/usr/bin/php」というのは、えー……こういうの何て言うんだっけね……よく分かんないですが、とにかくコマンドライン上でPHPを実行するときのコマンドです。サーバーの設定によってこのパスは変わるので、人によっては「/usr/local/bin/php」とかかもしれない。分からないって人は、whichコマンドを使って調べれば分かります。

//コマンド
# which php

//実行結果
/usr/bin/php

それから「Cakeフォルダまでのパス」と「appフォルダまでのパス」は、まさにそのまま、サーバー上でCakeのコアライブラリが入ってるフォルダと、appフォルダのパスを書けば良いです。cronに書く場合は自分でパスを書く必要がありますけど、もしCakePHPの中でexecとか使って実行する場合は、「CAKE」「APP」という定数がそれぞれすでに用意されているので、それを書けば大丈夫です。

exec('/usr/bin/php '.CAKE.'Console/cake.php SampleShell sampleFunc -app '.APP);

こうですね。クォーテーションの中に書いちゃダメよ。

ちなみに、cronの方に書いてある「0 0 * * *」は、毎日0時0分に実行するという意味です。「分 時 日 月 曜日」という風にスペースとかタブで区切って書きます。曜日は、日曜から土曜までを0から6の数字で書く。0は日曜、6が土曜。

いくつか適当な例を挙げてみまっしょい。

//1時間に1回実行
0 * * * * /usr/bin/php

//7月10日の18時に実行
0 18 10 7 * /usr/bin/php

//毎週金曜日の4時に実行
0 4 * * 5 /usr/bin/php

//毎月1日の0時に実行
0 0 1 * * /usr/bin/php

もうちょいいろんな書き方がありますが、今回はここは別に本題じゃないんで、省略で。



コンポーネントの読み込み方

モデルを使う場合はコントローラーと同じように$usesを書けば良いって言うくらいだから、同じ要領でコンポーネントも使えそうとか思うじゃん? 思うよね? 俺は思いました。

でもダメでした。

App::uses('Shell', 'Console');
class SampleShell extends Shell {
  public $uses = array('Model');
  public $components = array('Auth');

  public sampleFunc() {
    //Authコンポーネント
    $pass = $this->Auth->password('test');
  }
}

例えばこんな風にAuthコンポーネントを使いたい。コントローラーならこれで動く。でもシェルはそうもいかない。もしかしたらクラスを読み込むために「App::uses()」を使えば良いのかと思って、こんなことも試してみたけど、やっぱりダメでした。

App::uses('Shell', 'Console');

//以下の一行を追加してみた
App::uses('AuthComponent', 'Controller/Component');

class SampleShell extends Shell {
  public $uses = array('Model');
  public $components = array('Auth');

  public sampleFunc() {
    //Authコンポーネント
    $pass = $this->Auth->password('test');
  }
}

エラーの内容とかちゃんと確認したわけじゃないけど、動かないのは間違いない。「ふん、この程度で動かせると思うとは、なめられたもんだな。俺はコントローラーのようには甘くないぜ? この俺を動かしたきゃロードローラーでも持って来るんだな」と言わんばかりに動かん。

まあ、ぶっちゃけそんなことを言われたところで、別に僕らは今、承太郎の上にロードローラーを落としたいわけでもないので、ロードローラーを持って来るのはDIOに任せるとして、僕たちはコンポーネントを使うために必要なものを持って来ることにしましょう。

App::uses('Shell', 'Console');

//以下の二行を追加
App::uses('ComponentCollection', 'Controller');
App::uses('AuthComponent', 'Controller/Component');

class SampleShell extends Shell {
  public $uses = array('Model');

  public sampleFunc() {
    //以下の二行を追加
    $collection = new ComponentCollection();
    $this->Auth = new AuthComponent($collection);

    //Authコンポーネント
    $pass = $this->Auth->password('test');
  }
}

どうやら「ComponentCollection」ってやつを読み込んでやる必要があるようで、それを使って自分でインスタンスを生成すれば、コントローラーのときと同じようにAuthコンポーネントなり他のコンポーネントが使えるようになります。






いつものことながら、分かってしまえばどーってことないささいなことなんですけど、まあ、たいていのことは分かってしまえばそんなもんですよね。世の中、分からないから難しいわけで。

ロードローラーだって、運転したことないからやり方が分かんないだけで、運転の仕方を覚えてさえしまえば、乗りこなせるようになるもんね。まあ、仮に運転を覚えても、大型免許と締固め用機械運転者とかいう資格がないと、運転できないみたいだけど。

まだコメントはいただけてないみたい……
もしかしたら何か関連しているかも?