Archive for the ‘CakePHP’ Category

CakePHP、規定内/外の呼び方チートシート

コンポーネントやヘルパーなどからモデル等を呼び出すときに何を使えばいいかの表。
2010062901
黄色は普通のやりかた。紫色が普通じゃないやりかた。灰色は知らない。
とりあえず、
・「コンポーネントからモデル」「ヘルパーからモデル」の場合、邪道な方法を使う
・「コンポーネントからコンポーネント」は普通に var $components = … で宣言しておけばいい
・「ヘルパーからヘルパー」も同様に var $helpers = … で宣言しておけばいい

hetemlで共用ssl(https)でcakephpを使う方法

先日ログを見たところ、検索キーワードランキングでダントツの1位で「cakephp heteml ssl」とかきてました。
該当記事がなかったので大変申し訳ございませんでした。
と同時にこれは書け、ということなので、一応書いておきます。
(この前ようやくうまくいったんで)

・・・ま、いろいろ頑張った結果、こうするしかなかったです。


--- A/webroot/index.php      2010-06-05 21:34:15.000000000 +0900
+++ B/webroot/index.php   2010-06-06 12:42:34.000000000 +0900
@@ -78,6 +78,12 @@
                        define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
                }
        }
+       // define img/css/js
+       define ( "URLBASE" , "/../自分のユーザー名/ディレクトリ/" ) ;
+        define('IMAGES_URL', URLBASE . 'img/');
+        define('CSS_URL', URLBASE . 'css/');
+        define('JS_URL', URLBASE . 'js/');
+
        if (!include(CORE_PATH . 'cake' . DS . 'bootstrap.php')) {
                trigger_error("CakePHP core could not be found.  Check the value of CAKE_CORE_INCLUDE_PATH in APP/webroot/index.php.  It should point to the directory containing your " . DS . "cake core directory and your " . DS . "vendors root directory.", E_USER_ERROR);
        }

URLBASE、IMAGES_URL、CSS_URL、JS_URLを webroot/index.php で変更するしかなかったです。
しかもURLBASEを相対パスで書かなければならないという。

hetemlでPHP OpenID Libraryを使うときの注意事項(mixi.jpの場合)

というか、hetemlでOpenIDでmixiを使うときにdiscoverに失敗するって例なんだけどね。
CakePHPのexamplesにある「discover.php」を使っても「discoverに失敗したお」エラーが出るので確認はできると思います。
2010050202

なお、今回はこの場合が該当します。

※Symfonyに関しては同等のライブラリ提供は無さげなので今回は割愛です。
なお、symfonyでOpenIDを実装してみた | ueblogで実装されているようです。

まあ、ぶっちゃけ解決策は「証明書インストールしる!」ってことです。
○mixiでログインできない件
きさら’s – 利用報告(heteml)
○証明書インストールの方法(root権限がない場合)
mixi OpenIDのサンプルコードをPHP OpenID Libraryを使って動かしてみた(訂正) – 大阪 本町で働くソーシャルアプリ開発者 ダイアリー

Auth_Yadis_ParanoidHTTPFetcher#get関数と、Auth_Yadis_ParanoidHTTPFetcher#post関数でcurl_exec関数を実行する直前の、132行目と196行目に下記のコードを追記

このへんを参照しました。
(1)まず、https://mixi.jp/ にアクセスして、証明書を保存する。
(2)保存した証明書をサーバへUPする。
(3)Yadisのソースで、cURLでアクセスしてる部分でcURLオプション(CURLOPT_CAINFO) を変更する。
ソース変更点は、こういうかんじになります。

--- old/Auth/Yadis/ParanoidHTTPFetcher.php	2009-04-21 11:31:20.000000000 +0900
+++ new/Auth/Yadis/ParanoidHTTPFetcher.php	2010-05-02 16:14:15.000000000 +0900
@@ -128,6 +128,12 @@
             curl_setopt($c, CURLOPT_TIMEOUT, $off);
             curl_setopt($c, CURLOPT_URL, $url);
 
+            if ( $this->isHTTPS( $url )  && mb_ereg( "mixi\.jp" , $url ) ) {
+                curl_setopt( 
+                        $c , 
+                        CURLOPT_CAINFO ,
+                        '/home/sites/heteml/users***/*/*/*/*/ssl/mixi.jp.crt' ) ;
+            }
             curl_exec($c);
 
             $code = curl_getinfo($c, CURLINFO_HTTP_CODE);
@@ -191,6 +197,12 @@
         curl_setopt($c, CURLOPT_URL, $url);
         curl_setopt($c, CURLOPT_WRITEFUNCTION,
                     array(&$this, "_writeData"));
+            if ( $this->isHTTPS( $url )  && mb_ereg( "mixi\.jp" , $url ) ) {
+                curl_setopt( 
+                        $c , 
+                        CURLOPT_CAINFO ,
+                        '/home/sites/heteml/users***/*/*/*/*/*/ssl/mixi.jp.crt' ) ;
+            }
 
         curl_exec($c);

これで、さっくり動きました。
あ、/home/sites〜に関しては各自のホームディレクトリと証明書のある場所を指定してください。

なお、証明書に関する件はFAQにも述べられています

※追記
mixi.jp 以外の場合に挙動がおかしくなるのでなおしました。
証明書が「ない」OP毎にこの処理を追加してあげる必要があります。

hetemlでCakePHPを使うときの.htaccess設定注意事項

さきほどはまってしまったので、忘れないためにもメモ。

インストールは普通にやってOK。特にはまった記憶は無いです。
私の場合、webディレクトリと(CakePHPの)appディレクトリをわけています。
このへんに関しては省略。

それはさておき。
.htaccessが通常の設定のやりかたと違うので、忘れないためにメモ。
ちなみに、RewriteBaseの設定です。

(1)http://*.heteml.jp/hoge/ でアクセスするときは、RewriteBaseの設定は不要
(2)http://example.com/ (独自ドメイン)でアクセスするときは、RewriteBaseの設定が必要

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteBase    /
    RewriteRule    ^$    webroot/    [L]
    RewriteRule    (.*) webroot/$1    [L]
</IfModule>

※省略しましたが webroot/.htaccess にも同様に RewriteBase を追加してあげてください。

つまり、Apacheのデフォルト設定でやった時と完全に逆です。

ちなみに。
RewriteBaseを設定するということは、アクセス方法は(1)か(2)のどちらかしか選べません、ということです。
(2)の設定で(1)にアクセスされる心配は(たぶん)ありません。

・・・というか。
確実なのは、どっちにせよ「RewriteBaseでちゃんと明示しとけ」ってことですねw

CakePHPでヘルパーからモデルを直接呼びたい

ヘルパーからモデルを呼びたいとき。
まぁ、そもそも正直言っておすすめできないです。
(コントローラからモデルを呼んでおいて、結果を set でviewに与えてそこで処理するのが正しいやりかたと思ってます・・・ちょっとくどいけど。)

Nyaというヘルパーのmyu関数から、Mofuというモデルを呼びたいという設定。

以前こうすればいいらしいときいたので試したら

class NyaHelper extends Helper {
        function myu() {
                $this->view->controller->Mofu->find("all","");
                return "nya!!" ;
        }
}

これをviewでこうすれば、

echo $nya->myu() ;

OKかなと思って、やってみたら、エラー・・・。
というか helper.php 見たら、viewがクラス変数じゃないし!

というわけで調べてたら見つかりまして
こうするしかないようです。

class NyaHelper extends Helper {
        function myu() {
                $r = ClassRegistry::getObject('Mofu');
                $r->find("all","");
                return "nya!!" ;
        }
}

正直気持ち悪い・・・です・・・。

条件をつけたpaginator

paginator便利ですよねー。
ところが条件づけをやるとうまくいかないことがあります。
特に、ページ繰りでURLに条件をつっこめないなど。

“条件をつけたpaginateでページ繰りができない” フォーラム – CakePHP Users in Japan
ここでいろいろ話になってますが、正しくはこうすればいいようです。

●コントローラ(filter引数のみ抜粋)

if( isset( $this->params["named"]["filter"] ) ) {
    $filter = $this->params["named"]["filter"] ;
} else {
    $filter = null ;
}
$this->set( "filter_cond" , $filter ) ;

●ビュー(paginatorのページ処理だけ抜粋)

if ( $filter_cond !== null ) {
    $u_filter = array( "url" => array( "filter:" . $filter_url ) ) ;
} else {
    $u_filter = array() ;
}
echo $paginator->prev('<< '.__('previous', true), $u_filter , null, array( 'class'=>'disabled'));
echo $paginator->numbers( $u_filter );
echo $paginator->next(__('next', true).' >>', $u_filter , null, array('class' => 'disabled'));

prev,nextは、2番目の引数にURLのオプションを指定します。

2番目が$optionsなんですよね。
ちなみに$optionsは、prev/next関数が呼ばれてる、__pagingLink関数の中でこう扱われています。

$options = array_merge($_defaults, (array)$options);

この$optionsから$urlを作っています。
ですからoptionsの2番目に設定しないとだめです。

cake/libs/view/helpers/paginator.php の、prev / next 関数から呼ばれてる、__pagingLink 関数を参照してみてください。



追記。$paginator->optionsでよかったのかよ。

$paginator->options( array( "url" => array( "filter:" . $filter_cond ) ) )  ;

参考文献
CakePHP1.2 RC2 Paginate で検索条件をURLで引き継ぐ – 忍び歩く男 – SLYWALKER とか
Abstract Sunriseな日々 | [CakePHP]paginatorで楽にパラメータを引き継ぐ あたり。

CakePHPとセキュリティコンポーネントに関するメモ

主に、真っ白画面(ブラックホール行き)関係で。
昨日のtwitterに書いたのそのままだけど。

○メモ:CakePHPとセキュリティ

●2009-12-21 17:25:47 by P3:PeraPeraPrv
見てるぅ>CakePHPのSecurityコンポーネントの機能を縮小してみた – モトクロスとプログラムと粉砕骨折と: http://d.hatena.ne.jp/sngmr/20091219/1261235966 確かにデフォルトのSecurityは気合い入りすぎw *P3
CakePHPでセキュリティにひっかかったとき真っ白になるアレは blackHoleCallbackなのね。 メモ >> CakePHP Security コンポーネントのまとめ | Sun Limited Mt.: http://bit.ly/8LtCty
●2009-12-21 18:09:19 by P3:PeraPeraPrv
メモ (復習的意味で) PHPで脆弱性の少ないセキュアなアプリを作るために実装時に意識すること | Web&MUSICブログ QUALL: http://blog.quall.net/program/63/ *P3
●2009-12-21 19:49:15 by P3:PeraPeraPrv
CakePHPでSecurityコンポーネントのblackhole動作について調べるも、あっというまに宇宙のブラックホールのほうがひっかかるようになった。


CakePHP で最後に追加したレコードのIDをとってくる。

getInsertID()とgetLastInsertID() という関数。
2つあるらしく。
CakePHP:getLastInsertIDって、どうよ(PHP)–WEBシステム開発 技術的備忘録(通称 技忘録)

何してるのかわからんかったので調べた。
from cake/libs/model/model.php :

        function getLastInsertID() {
                return $this->getInsertID();
        }

ヽ(`Д´)ノもう来ねぇよウワァァァァン

というわけで気をとりなおして、getInsertID()。

        function getInsertID() {
                return $this->__insertID;
        }

modelクラス変数 $__insertID を見に行ってるのね。
そんなわけで、$__insertID を探したら・・・

        function setInsertID($id) {
                $this->__insertID = $id;
        }

・・・。
ヽ(`Д´)ノもう来ねぇよウワァァァァン

またまた気を取り直して。

ちなみに、setInsertID は、データソースのほうで使用してます。
cake/libs/model/datasources/dbo_source.php

function create(&$model, $fields = null, $values = null) {
// 中略
    if ($this->execute($this->renderStatement('create', $query))) {
        if (empty($id)) {
            $id = $this->lastInsertId($this->fullTableName($model, false),
                    $model->primaryKey);
            }
            $model->setInsertID($id);
// 以下略

そして、lastInsertID は各DBによって違う挙動となります。
一番最後に挿入したレコードのidを取得する方法はRDBMSによってまちまちですからね。

MySQLの場合:datasources/dbo/dbo_mysql.php

        function lastInsertId($source = null) {
                $id = $this->fetchRow('SELECT LAST_INSERT_ID() AS insertID', false);
// 以下略

つまり、LAST_INSERT_ID() の値。

PostgreSQLの場合:datasources/dbo/dbo_postgres.php

        function lastInsertId($source, $field = 'id') {
                $seq = $this->getSequence($source, $field);
                $data = $this->fetchRow("SELECT currval('{$seq}') as max");
                return $data[0]['max'];

$seq というのはシーケンスの名前。
詳しくはPostgreSQLのシーケンス操作関数あたり参照。

CakePHPで、検索条件がちょっと複雑なケースの扱い

本日はまったこととか。

SELECT * FROM Post Where date between '2009-11-20' and '2009-11-22'

みたいなSQLであらわされる条件をCakePHPのconditionsでやる場合。

“1.2RCのLIKE,BETWEEN,INの書き方まとめ” フォーラム – CakePHP Users in Japan
条件が複数の場合こういう書き方をするとよいらしい。

<?php
$conditions = array("Post.title LIKE" => "%post%");
$conditions = array("Post.date BETWEEN ? AND ?" => array("2008-1-1", "2009-1-1"));
$conditions = array("Post.title LIKE ?" => array("%post%"));
$conditions = array("Post.id" => array(1,2,3,4,5));
?>

betweenの場合は、valueにさらにarrayで与えるのね。

CakePHP / Securityコンポーネント入れてはまったこと

昨日、はまったことがあったので、メモ。

【CakePHPのバージョン】
1.2.5

【こんなことが起きた】
・特定のプレフィクスのときだけ、basic認証をつけるようにしたい。
・そのために、Securityコンポーネントを、app_controller.phpに入れた。

   var $components = array( "Security" ) ;

・特定のプレフィクスのときだけbasic認証させるコードを、AppController::beforeFilter()に書いた

$this->Security->loginOptions = array('type'=>'basic','realm'=>'nyaaaa');
$this->Security->loginUsers =>array('hoge'=>'fuga');
$this->Security->requireLogin('*');

・よっしゃbasic認証できた!
・ところが、basic認証かけてない、ほかの一部ページが突然動かなくなった。
・動かなくなったページは、全部POSTで処理してる。GETで処理してるところは問題なく動いてる。
・いろいろ調べたら、POSTしてるときだけコントローラのアクション関数に飛んでない雰囲気がしている。

【原因】
Securityコンポーネントを入れるとデフォルトで POSTのvalidaition をやってくれます。
POST先でトークンのチェックをやってるので、CakePHPのformヘルパーを使わずにフォームのコードを書いてる場合、確実におかしくなります。
自前でやってるよとか気にする必要がないとかいう場合、validatePostをfalseにしちゃいます。

$this->Security->validatePost = false ;

これをやるとPOSTのトークンチェックを行わないので、当然のことながらCSRFに対して弱くなります
自分で対策してるからOK!とかいう場合を除き、この設定はしないほうがいいでしょう(まさに『素人にはおすすめできない』設定)。

参考:CakePHPでBasic認証対応ページを作る – blog.katsuma.tv

また、CSRFに関してはこちらがわかりやすい。
連載:[はまちちゃんのセキュリティ講座]ここがキミの脆弱なところ…!|gihyo.jp … 技術評論社