‘CakePHP’ タグのついている投稿
baserCMS2系から3系への道(アップデートメモ)
オレはようやくのぼりはじめたばかりだからな
このはてしなく遠いbaserCMS坂をよ… 未完
という気持ちになるくらいイロイロとありましたが(2日かかってしまいました)、無事2.1.1から3.0.2へ移行できました。注意点のメモを残します。
まずは、移行作業の参考にさせていただいたサイト。
本家本元、
baserCMS開発ブログ『baserCMS 2.1系 から baserCMS3に移行する』
うっかりナスビさんでもお馴染み我流さんの、
我流天性 がらくた屋『baserCM2.1.2から 3.0.0へ移行を試してみたよ』
作業手順については上記、2つサイトを参考にすれば問題ないと思います。
移行作業で使用するプラグインは、現在、baserマーケットからダウンロード出来ます。
・DBマイグレーター(作業時バージョン1.0.3)
・アドオンマイグレーター(作業時バージョン1.0.1)
では、はじめに大事なことを。
普通に運用しているサイトであれば重要なのは1点。
「まず、3.0.0に移行するのだ! 後のアップデートは3.0.0で動いてからだ!」
baserCMSユーザーズフォーラムの「2→3 の移行途中で /maintenance/index に強制リダイレクト」というトピックに書きましたが、DBマイグレーターの移行対象は(現状)、3.0.0のみだと思われます。
よって、一旦3.0.0で対応作業を行ってから以降のバージョンへのアップデートを行う、という手順で比較的容易に移行できると思います(移行用プラグイン優秀!)。
私が作業していた時には http://basercms.net/download/past から3系の旧バージョンがダウンロード出来なくなっていたのですが、素早く対応していただき、現在はダウンロード出来るようになっています。
私の最初の一日は3.0.2へのデータ復元後のメンテナンス画面への強制リダイレクトやデータベースアクセスエラーの原因を調べるのに費やされました。この情報で、みなさんの一日が守られればと思います。
以降はサイトの状況によってまったく異なってくると思いますので参考程度に。
弊社の場合はいろいろとカスタマイズしていたので様々な問題がでました。デバッグモードで動かし、表示されるNoticeを一つ一つ潰していきました。
・プラグインが無効化されずにサイト全体が死亡
DBマイグレーターで変換したデータを3.0.0に復元した後、なぜかプラグインが無効化されておらず、プラグイン周りでエラーが発生してサイトが死亡しました。データベース直編集でプラグインを無効化してエラーを回避しました。
・テーマヘルパーでエラー
テーマヘルパーはアドオンマイグレーターの変換では動作しませんでした。まずはテーマヘルパーを使用しているところをコメントアウトしてサイトが動作する状況にした後、デフォルトのヘルパーを参考に手作業で対応作業を行いました。
・カスタマイズしていたサイトマップでエラー
前の記事をご参照下さい。
・パンくず周りの変更に対応
古いテーマだとパンくず周りで「Not Found: …/elements/navi.php」といったNoticeが出てパンくずが正常に動作しません。3系に添付されているテーマの elements/crumbs.php をコピー、カスタマイズして対応するのが良いと思います。
・$bcBaser->paramsの値を直接参照していたところでエラー
反則に近いかもしれないのですが$bcBaser->params(2系の場合)中の値を直接参照して判定を行っていた箇所が結構ありまして、エラーとなりました。$this->BcBaser->params(3系の場合)の内容を調べて、同様の動作をするように書き換えました。3系のほうが情報が増えていて便利でした。
・3.0.0セットアップ時テーマのメールフォームと移行元メールフォームのフィールドが混在
これはプラグインが無効化されなかったときに直接データベースをいじって対応したのが原因かもしれないのですが(データ復元時にエラーが出ていたのかもしれない)、既存テーマのcontactメールフォームと移行元のcontactメールフォームのフィールド情報などが混在状態になってしまい難儀しました。既存テーマの情報をデータベースから直接削除するなどして対応しました。
以上、アップデートメモでした。
baserCMS3系でのサイトマップカスタマイズ
bserCMS3系へのメジャーバージョンアップからはや半年以上、やっと弊社サイトを2.1.1から3.0.2へバージョンアップしました。
バリバリにカスタマイズしているので腰が引けていたのです。
旬を外していたり、イレギュラーなカスタマイズをやっていたりでバージョンアップにはかなり苦労したのですが、その話は次にとっておきまして、まずは以前書いた『baserCMSのサイトマップをカスタマイズ』のフォローを。
上記記事のコードですが、アドオンマイグレーター(http://barket.jp/products/detail.php?product_id=11)でテーマを変換しただけでは(やはり)動きませんでした。
少し修正が必要でしたので修正後の弊社のサイトマップのコードを置いておきます。参考にどうぞ。
BcBaser->sitemap() で呼び出す
*/
//ここから設定
//表示しないurl
$disables = array(
'/index_test',
);
//$inserts = array(before_url=>array(title, url),,,)
//before_urlの次に挿入url,titleで作成したアイテムを挿入
//ブログ、メールフォームなどpage以外を途中に挿入することが出来る。
$inserts = array(
'/sitemap' => array('新着情報', '/news'),
'/news' => array('お問い合わせ', '/contact'),
'/it-model/faq' => array('ダウンサイジングについてのお問い合わせ', '/contact_itmodel'),
'/roundcube/faq' => array('Roundcubeについてのお問い合わせ', '/contact_roundcube'),
'/kolab/faq' => array('Kolabについてのお問い合わせ', '/contact_kolab'),
);
//設定ここまで
//関数
$bcBaser = $this->BcBaser;
$outputPageItem = function ($recursive, $title, $url) use (&$bcBaser) {
?>
-
BcBaser->element('sitemap', array(
'pageList' => $pageCategories['children'],
'category_title' => $category_title,
'category_url' => $category_url,
'recursive' => $recursive+1
));
endif;
if($outputed_category_li):
$outputCategoryItemTail();
endif;
endforeach;
endif; ?>
ポイントは「$bcBaser = $this->BcBaser;」して$bcBaserを無形関数に渡して使用するところです。
baserCMSの認証をカスタマイズ(WordPressのアカウントでログイン)
baserCMSのバージョンは2.0.1です。
現在弊社のサイトはbaserCMSとWordPress(スタッフブログ)が同居しています。
アカウントの管理を統一したかったのでbaserCMSにWordPressのアカウントでログイン可能なようにbaserCMSの認証をカスタマイズしました(先にWordPressを運用していたのでスタッフのアカウントがWordPressにありますので)。
次のような仕様にしました。
- WordPressのアカウントでbaserCMSにログインした場合、事前に作成したstaffアカウントとしての操作になる。
- 公開ページで表示される名前(氏名)はstaffとする。
- ログイン中、氏名を「staff(WordPressアカウント)」とする。
- 誰が操作したかの記録は欲しいのでダッシュボードの「最近の動き」に by 氏名 と付加して記録する。
baserCMSの認証はbcAuthComponent(baser/controllers/components/bc_auth.php)に切り出してありますので、このファイルをappディレクトリの同一階層にコピーしてそのファイルを修正します。元のbcAuthComponentは携帯の簡単ログイン対応になっていたのですが携帯でのログインは必要なかったので全部削除して書き直しって感じになりました。
app/controllers/components/bc_auth.php
App::import('Component', 'Auth', 'Sanitize');
class BcAuthComponent extends AuthComponent {
//WordPressアカウントでstaffとしてログイン murave
function login($data = null) {
$this->__setDefaults();
$this->_loggedIn = false;
$wordpress_id = null;
if (empty($data)) {
$data = $this->data;
}
$model =& $this->getModel();
$alias = $model->alias;
$cnt = $model->find('count',
array('conditions' => array($this->userModel . '.' . $this->fields['username'] => $data['User']['name'])));
if($cnt == 0) {//baserCMSには存在しないユーザー
if($this->_wp_CheckPassword($_POST['data']['User']['name'], $_POST['data']['User']['password'])) {
$wordpress_id = $_POST['data']['User']['name'];
$staff_record = $model->find('first',
array('conditions' => array($this->userModel . '.' . $this->fields['username'] => 'staff')));
$username = $staff_record[$this->userModel][$this->fields['username']];
$password = $staff_record[$this->userModel][$this->fields['password']];
$data = array(
$this->userModel . '.' . $this->fields['username'] => $username,
$this->userModel . '.' . $this->fields['password'] => $password
);
}
}
if ($user = $this->identify($data)) {
if(!is_null($wordpress_id)) {
$user['real_name_2'] = '('.$wordpress_id.')';
}
$this->Session->write($this->sessionKey, $user);
$this->_loggedIn = true;
}
return $this->_loggedIn;
}
/**
* WordPressユーザー認証を行う。
*/
private function _wp_CheckPassword($wordpress_id, $password) {
//WordPressの関数を使って認証を行ない結果をtrue/falseで返す処理です。
}
}
_wp_CheckPasswordがWordPressに認証させる処理で「肝心な所」ではあるのですが、環境によって異なるはずなので申し訳ありませんが割愛しています。
baserCMSにアカウントが無かったらWordPressに認証させて、WordPressで認証に成功したらstaffというアカウントでbaserCMS側は認証、その場合には $user[‘real_name_2’] = ‘(‘.$wordpress_id.’)’ とreal_name_2に設定してからセッションに書き込んでいます。real_name_1はstaffにしているので操作中の氏名は「staff(WordPressアカウント)」となります。
次に「最近の動き」への記録ですがBaserAppModelのsaveDbLogメソッドで行われていますのでこのメソッドの動作を変更します。baser/models/app_model.phpをappディレクトリの同一階層にコピーしてメソッドを定義すれば上書きできます。
app/models/app_model.php
class AppModel extends BaserAppModel {
function saveDbLog($message) {
// ログを記録する
App::import('Model', 'Dblog');
$Dblog = new Dblog();
$logdata['Dblog']['name'] = $message
.' by '.@$_SESSION['Auth']['User']['real_name_1'].@$_SESSION['Auth']['User']['real_name_2'];
$logdata['Dblog']['user_id'] = @$_SESSION['Auth']['User']['id'];
return $Dblog->save($logdata);
}
}
これで当初予定していたカスタマイズが一通り完了しました。baserCMSはカスタマイズしやすくていいですね。
baserCMS 2系で追加されたテーマヘルパーが便利(だけど要注意)
本日の「忘れないうちに書いとくぜ!baserCMSネタ」のお時間がやってまいりました。
baserCMS 2系でテーマヘルパーが追加されました。
http://basercms.net/functions/theme_helper
弊社サイトはlancardというテーマを作ってその下で構築しています。
現在、その中で、これから追加や編集をしていく予定があり、書き方が複雑だけど規則性がある、「ホームページのピックアップ(上部の画像切替)」と「ウィジェットのバナー表示」についてテーマヘルパーを利用して更新コストを下げています。
参考に、バナー表示用のヘルパーのコードを抽出して貼ります(ピックアップについては複雑なので)。
app/webroot/themed/lancard/helpers/lancard.php
BcBaser->getUrl('/themed/lancard/img/bunner/'.$imgFile, true)
.')';
$this->BcBaser->link(
$title,
$url,
$linkAttributes
);
}
}
?>
使用方法はこんな感じ。
bunner('OSSダウンサイジング', 'downsizing.jpg', '/it-model') ?>
と書くと
OSSダウンサイジング
と出力されます。便利です。
しかし、このテーマヘルパー、注意して扱わないと大変ヒドイ目にあいます。
テーマヘルパー用のディレクトリに「正しいテーマヘルパー以外のファイル」を置くとサイトのルーティングが全滅します。管理ページもアクセス不能! 初めてこの現象に遭遇したときは超ビビリました。
ヘルパーを読み込もうとしてエラーが発生するわけですが、その影響範囲が想定の範囲外!
私がやってしまったパターン
- テーマヘルパー開発中に動作確認しようとしたらサイトが全滅してた。
- テーマヘルパーを更新するときに旧ファイルをlancard.php_oldとリネームしたら全滅。
いかがでしょうか。恐ろしいですね。
しかしこの副作用、次のチケットでかなり解決しそうですので一時的な問題だと思います。
TODO #2604 「テーマヘルパの読み込みはテーマ名のヘルパのみにする」
そうそう、ヘルパー名は上記チケットの内容にしたがってつけておきましょうね。
baserCMSのサイトマップをカスタマイズ
baserCMS のバージョンは 2.0.1 です。
baserCMSにはサイトマップを出力するヘルパーがあって、
sitemap() ?>
と書くと固定ページについてはサイトマップを自動で更新してくれて便利です。
なのですが、いろいろと不満もあるヘルパーです。
- 固定ページ以外(Blogとかメールフォーム等)はダメ。
- カテゴリのindexにページ名をつけるとカテゴリとページで同じURLが表示されてダブる。
てなわけでカスタマイズしました。
baser/views/helpers/bc_baser.php を読んでヘルパーの仕組みを調べると再帰的にページをたどっていること、テンプレート baser/views/elements/sitemap.php を使用していることが分かりました。
検討した結果、「固定ページ以外はダメ」に関して根本的な解決は出来ないのですがテンプレートのカスタマイズで対応することにしました。
データの取得方法から変更した独自ヘルパーの作成も考えたのですが、結局どこかに「Blogやメールフォームを何処に表示するか」という情報を持たせなければならないので完全自動化はできない。それならデータ取得は既存の方法を使って必要な追加設定はテンプレート内で配列にでももたせるか、と。
まず、baserCMS カスタマイズの常道としてbaserディレクトリのファイルを自分のテーマの同階層にコピー。これでコピーしたファイルが有効になるのでこのファイルを改造していきます。
改造した結果、こうなっちゃいました。
//ここから設定
//表示しないurl
$disables = array(
'/index_test',
);
//$inserts = array(before_url=>array(title, url),,,)
//before_urlの次に挿入url,titleで作成したアイテムを挿入
//ブログ、メールフォームなどpage以外を途中に挿入することが出来る。
$inserts = array(
'/sitemap' => array('新着情報', '/news'),
'/news' => array('お問い合わせ', '/contact'),
'/it-model/faq' => array('ダウンサイジングについてのお問い合わせ', '/contact_itmodel'),
);
//設定ここまで
//関数
$outputPageItem = function ($recursive, $title, $url) use (&$bcBaser) {
?>
-
element('sitemap', array(
'pageList' => $pageCategories['children'],
'category_title' => $category_title,
'category_url' => $category_url,
'recursive' => $recursive+1
));
endif;
if($outputed_category_li):
$outputCategoryItemTail();
endif;
endforeach;
endif; ?>
「カテゴリのindexにページ名をつけるとカテゴリとページで同じURLが表示されてダブる」のは解消。
追加設定に特定ページを表示したくない時のための$disables配列、Blogやメールフォームを思い通りの場所に挿入するための$inserts配列を用意することで希望する表示ができるようになりました。
しかし、元テンプレートからかけ離れているのはともかくとして、
basercmsのエレメント内で無名関数を定義して再帰呼び出しやってやったぜ〜。ワイルドだろ?
— murave (@murave) June 15, 2012
てなことをやってるので万人にはオススメできない。無名関数を使っているのでPHP5.3以降限定ですし。
関数を定義したのは「処理をパーツ化しないとコードがあまりにもカオス」「処理を再帰呼び出しする必要があった」からです。
無名関数を使ったのは「関数の影響範囲を限定したかった」から。テンプレートが複数回呼び出しされるので関数の再定義エラーがでるという問題もありましたが、これは無名関数にしなくても未定義の場合のみ関数を定義するようにすれば回避できると思います(試してませんが)。
baserCMSのfeedプラグインで既存フィードを合成したフィードを作成
baserCMSでリニューアルした弊社サイトですが、このスタッフブログはWordPressのまま残しました。
そうなるとサイトのフィードはbaserCMSで運用している新着情報とWordPressで運用しているスタッフブログの両方の情報が入っていて欲しい。「両方のデータベースからデータさらってきてフィードを作るプログラムでも書くかなー」などと調査したりしていたのですが、気づきました。
「feedプラグインで合成表示は出来るんだからRSSのフォーマットで出せばいいんじゃね?」
てなわけで、現在弊社のフィード(RSS 2.0) http://www.lancard.com/rss はfeedプラグインでbaserCMSのBlogのフィードとWordPressのフィードを合成して作成しています。
どうやったかといいますと、
feedプラグインで合成したフィードを作成。テンプレートについては後ほど。
フィードのキャッシュ期間ですが、baserCMSのBlogについては更新時にフィードのキャッシュはクリアされるはずなのでとりあえず1日(※2013.7.27追記:公開日時の予約をすると公開時にはキャッシュクリアされないので1時間に変更しました)、外部のスタッフブログについては長くキャッシュされるとまずいので1分にしています。
RSS 2.0 用 のレイアウト作成(ただし、WordPressに合わせて古い項目を追加してます)。
$this->cacheAction = false とすることでサーバーキャッシュは無効化。
テーマ名/layouts/rss2.php
cacheAction = false;
echo "\n";
?>
Lancard.com
http://www.lancard.com/
Lancard.com 新着情報とスタッフブログのRSS(RSS 2.0)
feed(3); ?>
ITEM作成用に作成したfeedのテンプレートはこんな感じ。
テーマ名/feed/rss2item.php
$item): ?>
-
]]>
name; ?>]]>
]]>
]]>
そして固定ページ管理でフィード用のページを作成。
layout = 'rss2'; ?>
以上です。もっといい方法がある気も。
baserCMSで作成したサイトの公開時にルーティングでハマった話
ぶっちゃけ、弊社サイトリニューアルでの話です(baserCMS 2.0.1)。
baserCMSが2系にバージョンアップしたので「まってました!」とサイトをリニューアルしました。
さすがは「コーポレイトサイトにちょうどいい」CMS。作成はサクサクでした。
なのですが公開時に謎のルーティング不具合でハマって「ギニャー!」
どんな症状かといいますと、作成中は表示出来ていたページがなぜか
404 NOT FOUND
Error : The requested address '/recruit' was not found on this server.
app/config/routes.php に直接
Router::connect('/recruit', array('controller' => 'pages', 'action' => 'display', 'recruit'));
と追加すると表示される。どう見てもルーティングがおかしい。
baser/config/routes.php の処理を追っかけてみたりして判明したのはrecruitと渡ってきてほしいurlがrecruit/と末尾に/がついて渡って来ているということ。
公開前までは大丈夫だったのになんでだー?
結論は「apacheのwebrootに旧コンテンツのrecruitディレクトリを残していたためにbaserCMSに渡される前に/をつけられていた」のでした。
.htaccessでbaserCMSに制御をすべて渡すように設定したので、影響でるとは思いもよらず、旧コンテンツを残していたのが不幸の始まり。みなさんもお気をつけください。
ボクはこれで6時間程ハマってました。ネムイヨ。
PHP + SQLite で正規表現(PDO_SQLITE編) in CakePHP
CakePHP 1.3.6 での話です。
これ、やったのはずいぶんと前なんですが書いてなかったですね。
私はすごくうれしかったのですが、周りの反応は薄かった覚えがあります。
CakePHP 1.3 でSQLite3ってのはかなりイレギュラーで、確か、CakePHPのTracから探してきたドライバ(dbo_sqlite3.php)を使用しています。
確認しようと思ったのですがTracに繋がらないぞ?ってな状況。
そんなこんなでとてもマイナーな話なんですよね。 反応が薄いのもむべなるかな。
ですが、PDOでSQLiteにアクセスしている場合なら応用が効くかと思います。
dbo_sqlite3.php 内部でPDOが使用されておりますので。
app_model.php でやっているので関係するところをまとめて引用。
class AppModel extends Model {
function beforeFind($queryData) {
parent::beforeFind($queryData);
//正規表現関数有効化
$this->regxEnable();
}
/**
* 正規表現関数有効化
*/
public function regxEnable(){
$db =& $this->getDataSource();
$db->connection->sqliteCreateFunction('RGX', array('AppModel', 'sqliteRegexMatch'), 2);
}
/**
* 正規表現関数
*/
public function sqliteRegexMatch($regex, $str) {
if(empty($regex)){
return true;
}else{
return preg_match($regex, $str);
}
}
}
$db->connection がPDOのインスタンスです。
sqliteRegexMatch で $regex が empty の場合に true を返しているのは実装しているアプリケーションの都合ですのでご注意を。
実装の際、下記サイトを参考にさせていただきました。
違いはPDOであること、preg_matchを使用していること、SQL関数を処理するコールバック関数としてメソッドを使っていることです。
CakePHP的ポイント
- DBOからPDOのインスタンスを取得
- beforeFindで検索時に正規表現関数を有効化
PDO_SQLITE的ポイント
- sqliteCreateFunctionでSQL関数登録
- sqliteCreateFunctionのコールバック関数にメソッドを登録する際の書式
使い方ですが、参考サイトと違ってpreg_matchでの正規表現であることに注意してくださいね。
私の場合、CakePHPでの使用ですので
/**
* モデルのconditonsに正規表現を使う場合の配列を返す。
*
* @param $pattern 正規表現
* @param $cols 判定対象のカラムの配列
* @return
*/
function whereByRgx($pattern, $cols) {
$conditons = array();
if(!empty($pattern)){
foreach ($cols as $colname) {
$conditons[] = "RGX('" . $pattern . "', " . $colname . ")";
}
}
return $conditons;
}
てなモデルでのfindなどで使用するconditions用の配列を返すメソッドを作って使っております。
CakePHP で debug.log にクエリを出力する(手抜き版)
シェル書いてる時とか Web でも Ajax のときとか
「画面にクエリだされても見えないし、困る〜」
ってときありますよね?
「つーか、なんで画面にだすんだ?ログファイルに出してくれればいいのに 」
とか思いません?
で、調べたらみなさんDBのドライバを拡張して対応されてるようです。
ですが、常にクエリをログに書いて欲しいわけでもないし、ドライバ拡張とか面倒。
クエリを画面にだしてるとこどうなってるのかなぁ、と思って調べたら
cake/libs/view/elements/sql_dump.ctp
にゴリゴリに書いてありました。
これマネしてapp_model.phpにでもメソッド作ればいいんじゃないかなーってことで、作りました。
/**
* 通常画面に表示されるログを取得してdebug.logに出力する。
*/
function sqlToLog(){
$db =& $this->getDataSource();
if ($db->isInterfaceSupported('getLog')){
$this->log($db->getLog(), LOG_DEBUG);
}
}
元のログ表示と違って明示的に呼び出すものなのでgetLogインターフェースの確認とか要らない気がしますが、一応。
このメソッドを実行すると debug.log にそれまでに実行されたクエリやら付帯情報(画面に表示されてますよね)が書き出されます。注意点として、画面に出すデータを横取りしているのでログに書き出されたものは画面には表示されません。
配列を print_r で出力した形式なので見やすくはないですが、まぁ、十分かな。
CakePHP でシェルを書くときに最初にやったこと(親クラス作り)
CakePHP にはデバッグで便利な pr() って関数があります。
出力の前後に pre タグ を付けてくれる print_r() のラッパーです。当然Web用。
シェルにも欲しいです。てなわけで作りました。
function pr($obj){
$pr = print_r($obj, true);
$this->out($pr);
}
簡単。こんな感じで使います。
$popopopooon = array('こんにちわ' => 'こんにちワン', 'ありがとう' => 'ありがとウサギ');
$this->pr($popopopooon);
このようにコンソールに出力されます。
Array
(
[こんにちわ] => こんにちワン
[ありがとう] => ありがとウサギ
)
print_r() は第2引数に true を渡すと出力せずに結果を返してくれるのでオブジェクト構造をログに出したりするときにも便利です。
でもコレ、全部のシェルに書きたくない。
どこに集約したらいいのか悩んだのですが、実際に実行するシェルの親クラスを作成して継承することにしました。継承の段数が増えるのはあまりすきじゃないのですが、Shellクラス をそのまま継承するよりも1段増やしといたほうがいいかな?って気もしたので。
appbase.php
class AppbaseShell extends Shell {
function initialize() {
parent::initialize();
}
function startup() {
//ウェルカムメッセージを消すためにオーバーライド
}
/**
* デバッグ用
*/
protected function pr($obj){
$pr = print_r($obj, true);
$this->out($pr);
}
}
ついでに startup() をオーバライドしてウェルカムメッセージを消しました。
こういう事やるには1段あいだに入ってるほうがいいですね。
実際に起動するシェルは AppbaseShellクラス を継承します。
aisatu.php
App::import('Shell', 'Appbase');
class AisatuShell extends AppbaseShell {
function initialize() {
parent::initialize();
}
function main() {
$popopopooon = array('こんにちわ' => 'こんにちワン', 'ありがとう' => 'ありがとウサギ');
$this->pr($popopopooon);
}
}
サンプルコードは今テキトーに書いたのでちょっとあやしいかも。
最後にオチを。
このprメソッド、実際に作ったんですが普通に print_r() すればコンソールに出力されるんですよねー。「そりゃそうだ」と後で気づいたわけで。
CakePHP のシェルでは $this->out() でコンソールへの出力をすることになってますが、開発中にしか使わないし print_r() をそのまま使えばいいかなと。親クラスは機能の集約に役立ってます。