Archive for the 'php' Category

[symfony]Class ‘BaseFormPropel’ not foundエラー解決

10 月 10th, 2008 | Category: php
このエントリをはてなブックマークに追加[symfony]Class ‘BaseFormPropel’ not foundエラー解決のはてなブックマーク被リンク数このエントリをdel.icio.usに追加このエントリをLivedoor Clipに追加このエントリをBuzzurl(バザール)に追加

以前、[symfony]Fatal error: Class 'BaseFormPropel' not foundというsymfony 1.1でフォームとPropelを組み合わせたときのエラーについて書いたことがあります。その時は解決には至らず、適当な回避策でごまかしていましたが、やはり開発に影響が出たので詳しく調べて、原因を突き止め、(たぶん)解決まで至りました。

どうやら、一定の条件を満たしているときに発生することがわかりました。

発生条件

以下のすべてを満たしているとき、「Class 'BaseFormPropel' not found」エラーが発生すると思われます。

  • symfony 1.1(普通)
  • O/RマッパーはPropel(普通)
  • symfony propel:build-formコマンドなどで作った、Propelと連携したフォームクラスがある(普通)
  • autoload.ymlを拡張していない(普通)
  • symfony本体を/libディレクトリの中に保持している
  • /libディレクトリの中に保持しているsymfony本体のディレクトリ名が"symfony"以外(ex: symfony-1.1.x)

「Class 'BaseFormPropel' not found」エラー内容のおさらい

Propelと連携したフォームクラスを用いると、以下のようなエラーが表示され、スクリプトが中断します。

php:
  1. [?php /** * Project form base class. * * @package form * @version SVN: $Id: sfPropelFormBaseTemplate.php 6174 2007-11-27 06:22:40Z fabien $ */
  2. abstract class BaseFormPropel extends sfFormPropel { public function setup() { } }
  3. Fatal error: Class 'BaseFormPropel' not found in /path-to-project/lib/form/base/BaseMemberForm.class.php on line 10

調査

  • 画面に表示された内容は以下のファイル内容そのまま
    TEXT:
    1. /path-to-project/lib/symfony-1.1.x/lib/plugins/sfPropelPlugin/data/generator/sfPropelForm/default/template/sfPropelFormBaseTemplate.php

  • キャッシュとして生成されたconfig_autoload.yml.phpの内容からBaseFormPropelに関する内容を抜き出すと以下のようになる
    php:
    1. // auto-generated by sfAutoloadConfigHandler
    2. // date: 2008/10/09 16:56:11
    3. return array(
    4. // project
    5. 'BaseFormPropel' => '/path-to-project/lib/form/BaseFormPropel.class.php',
    6. 'BaseFormPropel' => '/path-to-project/lib/symfony-1.1.x/lib/plugins/sfPropelPlugin/test/functional/fixtures/lib/form/BaseFormPropel.class.php',
    7. 'BaseFormPropel' => '/path-to-project/lib/symfony-1.1.x/lib/plugins/sfPropelPlugin/data/generator/sfPropelForm/default/template/sfPropelFormBaseTemplate.php',
    8. );

  • 上記から読み取れることは、autoload.ymlのprojectというルールにおいて、BaseFormPropelの定義が複数ある。一番最後に上書きした定義はエラーとして画面に表示されたファイルを指している
  • つまり、autoload.ymlのprojectルールがよくない。

原因

デフォルトのautoload.ymlは$sf_symfony_lib_dir/config/autoload.ymlにあります。このうち、projectルールだけを取り出すと以下のようになっています。

php:
  1. autoload:
  2.   # プロジェクト
  3.   project:
  4.     name:           project
  5.     path:           %SF_LIB_DIR%
  6.     recursive:      on
  7.     exclude:        [model, symfony]

日本語で書けば、projectという名前のルールは、%SF_LIB_DIR%(/path-to-project/lib)を基準に再帰的にクラスを検索するが、modelとsymfonyという名前のディレクトリは除外するルールのようです。おそらく、/path-to-project/libにsymfony本体を置く場合、ディレクトリ名をsymfonyとする習慣があるので、それを考慮して除外ルールが設定されているのではないでしょうか。そして、今回はsymfony-1.1.xというディレクトリ名でsymfony本体を置いてしまいました。当然除外ルールには該当しないので、symfony本体に含まれる大量のクラスがautoload対象になってしまったのです。

解決策

1つは、/path-to-project/libに置いたsymfony本体のディレクトリ名をsymfonyに変更することです。おそらくこれが一番シンプルな解決策です。前述した除外ルールも適用されるはずです。
もう1つは、各アプリケーションにautoload.ymlを設置し、projectルールを上書きして、除外ディレクトリ名にsymfony-1.1.xなどのsymfony本体のディレクトリ名を追加することです。何らかの理由によってsymfony本体のディレクトリを変更できない場合はこの方法が有効です。

たぶんこれで解決したのではないでしょうか。
それにしてもsymfonyって設定に慣れないと面倒ですね。。。

No comments

[OpenPNE3]alpha1の設定周りを見る

10 月 03rd, 2008 | Category: php
このエントリをはてなブックマークに追加[OpenPNE3]alpha1の設定周りを見るのはてなブックマーク被リンク数このエントリをdel.icio.usに追加このエントリをLivedoor Clipに追加このエントリをBuzzurl(バザール)に追加

世の中的にはOpenPNE3 alpha3の開発が始まったようですが、うえちょこはまだalpha1もalpha2も中身は見てません。せっかくオープンソースソフトウェアがゼロから徐々に作られていくわけですから、順番に追っていくのも面白いかとおもって、しっかりとalpha1から追っていきたいとか妄想しています。続くといいんだけどww

そんなわけで、今回はOpenPNE3alpha1の設定周りを中心に見ていきます。

/apps/*

/appsには、3つのアプリケーションが存在します。メインの(index.phpと対応する)アプリケーションはpc_frontendです。残りの2つはpc_backendとmobile_frontendですが、alpha1ではさら地の状態です。

/apps/pc_frontend/config/app.yml
TEXT:
  1. all:
  2.   openpne_auth_mode: LoginID

OpenPNEのユーザー認証方法を指定します。ここで設定しなかった場合はデフォルトで"LoginID"となります。sfOpenPNESecurityUserPluginにて用いられます。OpenPNE Trac Ticket #2857で触れられていますが、ログイン方法の変更時にapp.ymlを直接書き換える運用方法はちょっと危ないので、いずれは代替手段(管理画面GUIとか別途設定ファイルとか)に移行すると思われます。

/apps/pc_frontend/config/settings.yml
TEXT:
  1. prod:
  2.   .settings:
  3.     no_script_name:         on
  4.     check_lock:             on
  5.   .actions:
  6.     error_404_action:       error
  7.     module_disabled_action: error

変更された箇所だけ抜粋しています。

  • prod(本番環境用設定)
    • no_script_name=on ・・・ URLからコントローラ名(index.phpなど)を省略する
    • check_lock=on ・・・ 内部サーバーエラー発生時に/web/errors/error500.phpを表示し、キャッシュクリア中のアクセス時に/web/errors/unavailable.phpを表示する。
    • error_404_action=error ・・・ 404エラー時のアクションwo
      /default/error404から/default/errorに変更
    • module_disabled_action=error ・・・ module.ymlなどでモジュールが無効化されていた場合のアクションを/default/disabledから/default/errorに変更
TEXT:
  1. all:
  2.   .actions:
  3.     login_module:           member
  4.     login_action:           login
  5.  .settings:
  6.     i18n:                   on
  7.     standard_helpers:       [Partial, Cache, Form, I18N]
  8.     enabled_modules:        [default, loginId]
  9.     default_culture:        ja_JP

  • all(共通設定)
    • login_module=member、login_action=login ・・・ 認証画面のモジュールとアクション。認証が必要なページにアクセスしたとき、まだ認証が済んでいない時は/module/loginに飛ぶ
    • i18n=on ・・・ テンプレートの翻訳機能(インターフェース翻訳)を有効にする
    • standard_helpers+=I18N ・・・ 標準で翻訳ヘルパーをロードする
    • enabled_modules+=loginId ・・・ プラグインモジュールとしてloginIdを使用可能にする。
    • default_culture=ja_JP ・・・ 国際化機能において、デフォルトの国と言語の組み合わせを日本-日本語に設定する
/apps/pc_frontend/config/routing.yml
TEXT:
  1. # openpne rules
  2. homepage:
  3.   url:   /
  4.   param: { module: member, action: home }
  5.  
  6. member_index:
  7.   url:   /member
  8.   param: { module: member, action: home }
  9.  
  10. member_profile:
  11.   url:   /member/:id
  12.   param: { module: member, action: profile }
  13.   requirements: { id: \d+ }
  14.  
  15. community_home:
  16.   url:   /community/:id
  17.   param: { module: community, action: home }
  18.   requirements: { id: \d+ }

基本的には/member/homeが基準となるようですが、/member/1や/community/1のように、モジュール名の次に数字が来た場合は、その数字をidと見なすようです。

/apps/pc_frontend/i18n/form_member.ja.xml

XLIFF(XML Localization Interchange File Format)形式の辞書ファイルです。MemberFormクラスの翻訳カタログとして用いられています。

/apps/pc_frontend/lib/myUser.class.php

myUserクラスは、sfBasicSecurityUserクラスから派生したsfOpenPNESecurityUserクラスを継承しています。これは、OpenPNE3での認証方法が複数個あることを意識した構造のようです。

/apps/pc_frontend/modules/*

pc_frontendアプリケーションのモジュールとして、default、member、friend、communityの4つがあります。SNSの基本要素ですね。

/lib/helper/PagenationHelper.php

sfPropelPagerで取得したページャに対し、レンダリングを簡略化するためのpager_navigation()関数があります。

/plugins/*

sfOpenPNEAuthLoginIDPluginsfOpenPNESecurityUserPluginがあります。どちらのプラグインもOpenPNE Trac Ticket #2857 - sfOpenPNEAuthLoginIDPlugin による簡易的な登録機能の追加に詳しいです。その中から一部だけ引用しました。

SNSメンバーを表すクレデンシャル
SNSMember : SNSのメンバーである(メンバーID を所有し、SNSにログイン可能である)ことを表すクレデンシャルです
SNSRegisterBegin : 登録用のデータを入力する権限を持つことを表すクレデンシャルです。SNSに登録しようとする場合、この権限をまず得なければいけません
SNSRegisterEnd : 登録処理を完了するアクションを実行する権限を持つことを表すクレデンシャルです。SNSへの登録を完了しようとする(ログイン可能になる)場合、この権限を得なければなりません
※これらのクレデンシャル名は変更されるかもしれません。

No comments

[php]curlでダイジェスト認証(Digest-Auth)を通過する

9 月 30th, 2008 | Category: php
このエントリをはてなブックマークに追加[php]curlでダイジェスト認証(Digest-Auth)を通過するのはてなブックマーク被リンク数このエントリをdel.icio.usに追加このエントリをLivedoor Clipに追加このエントリをBuzzurl(バザール)に追加

phpスクリプトでダイジェスト認証(Digest-Auth)を通過する方法を調べていたのですが、どうやらcurlライブラリを用いるしか方法が見当たりません。その他の方法があれば教えてください。

で本題のダイジェスト認証の通過ですが、ネットで調べて2件だけありました。
Perl Tips | PHP で、ダイジェスト認証(Digest Auth)をする HTTP クライアント
PHPでダイジェスト認証(curl) - ユーウツな雨がふりつづいても雪がハートを曇らせてもドアの中で待っていた君に魔法をかけたいのさ

とにかくまずは CURL が PHP で動けば、あとはプロトコルとして HTTP、HTTPS(SSL/TLS)、FTP、Telnet、LDAP プロトコルでアクセスするクライアントを作れるようだ。 CURL では HTTP だと、GET、POST、PUT、FTP アップロード、フォームからのアップロード、Proxy、cookie、ユーザ名とパスワードによる基本認証/ダイジェスト認証、HTTP(または HTTPS)の認証…と、フルサポートするようだ。

php:
  1. $ch = curl_init();
  2. curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
  3. curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
  4. curl_setopt($ch, CURLOPT_URL, $url);
  5. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  6. $str = curl_exec($ch);
  7. curl_close($ch);

curlのいいところは、「HTTPS+ダイジェスト認証」みたいな際どいURLなんかにもアクセスできちゃうこと。しかも認証されていないhttpsにアクセスするためのオプションもちゃんとある。以下がそのオプション。

php:
  1. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  2. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

これでやりたいことができるようになった!うーん・・・でも世の中的にはBasic認証の方が一般的ですよね。HTTPS+Digest-Authってレアかなぁ。。。

No comments

[php]文字列の各文字の間に別の文字列を挿入するスクリプト

9 月 24th, 2008 | Category: php
このエントリをはてなブックマークに追加[php]文字列の各文字の間に別の文字列を挿入するスクリプトのはてなブックマーク被リンク数このエントリをdel.icio.usに追加このエントリをLivedoor Clipに追加このエントリをBuzzurl(バザール)に追加

文章を書いていた時に、たまたま「1と2と3と5と6」と言う文字列を順番どおりに書いて、入力切り替えのかったるさを実感しました。ふと考えてみると。「12356」を先に書いてから、あとから間に「と」を入れてくれるとスマートな気がして、でもphpでどうやって書くんだろうっていう、変な妄想にふけりました。

妄想の結果:

php:
  1. $insert = 'と';
  2. $str = '12356';
  3.  
  4. $len = strlen($str);
  5. for ($i = 0; $i <$len; $i++) {
  6.     echo ($i> 0) ? $insert . $str[$i] : $str[$i];
  7. }

もう少し、スマートに書けるんじゃないかと思ったけど、これで満足・・・・と思いきや、$strがマルチバイトだと動かなそうな気がした。で、第2版がこちら

php:
  1. $insert = 'to';
  2. $str = 'あいうえお';
  3.  
  4. mb_internal_encoding('UTF-8'); // 環境に合わせて
  5. $len = mb_strlen($str);
  6. for ($i = 0; $i <$len; $i++) {
  7.     echo (($i> 0) ? $insert : '') . mb_substr($str, $i, 1);
  8. }

まーこれでいいかなぁー。自分の要望は満たしてるし。でもphpならもっとスマートにできる関数があると思うんだけどな。

2 comments

[symfony]ログイン認証バリデータ(データベース接続)のサンプル

9 月 21st, 2008 | Category: php
このエントリをはてなブックマークに追加[symfony]ログイン認証バリデータ(データベース接続)のサンプルのはてなブックマーク被リンク数このエントリをdel.icio.usに追加このエントリをLivedoor Clipに追加このエントリをBuzzurl(バザール)に追加

symfony1.1で、メールアドレスとパスワードによるログインフォームを作りたい。

ログインフォーム

ログインフォーム

メンバー情報などが記録されたMemberテーブルは以下の仕様

TEXT:
  1. propel:
  2. member:
  3. _attributes: { idMethod: native }
  4. id: { type: INTEGER, required: true, autoIncrement: true, primaryKey: true }
  5. name: { type: VARCHAR, size: '255', required: true, default: '' }
  6. mail_pc: { type: VARCHAR, size: '255', required: true, default: '' }
  7. password: { type: VARCHAR, size: '255', required: true, default: '' }
  8. created_at: { type: TIMESTAMP }
  9. updated_at: { type: TIMESTAMP }
  10. last_logined_at: { type: TIMESTAMP }
  11. is_admin: { type: TINYINT, default: '0' }
  12. enabled: { type: TINYINT, required: true, default: '1' }

ログインフォームに必要なバリデータは、個々の項目(mail_pc、password)のバリデートと、データベースへの問い合わせの2つ。個々の項目のバリデートはバリデータを使えばすぐにできそう。以下のような感じ。

php:
  1. class LoginForm extends sfForm
  2. {
  3. public function configure()
  4. {
  5. $this-&gt;setWidgets(array(
  6. 'mail_pc'  =&gt; new sfWidgetFormInput(array(), array('size' =&gt; 50)),
  7. 'password' =&gt; new sfWidgetFormInputPassword(array(), array('size' =&gt; 32)),
  8. ));
  9.  
  10. $this-&gt;widgetSchema-&gt;setLabels(array(
  11. 'mail_pc'  =&gt; 'PCメールアドレス',
  12. 'password' =&gt; 'パスワード',
  13. ));
  14.  
  15. $this-&gt;widgetSchema-&gt;setNameFormat('login[%s]');
  16.  
  17. $this-&gt;setValidators(array(
  18. 'mail_pc'  =&gt; new sfValidatorEmail(
  19. 'required' =&gt; 'メールアドレスを入力してください。',
  20. 'invalid'  =&gt; 'メールアドレスが正しくありません。'
  21. )
  22. ),
  23. 'password' =&gt; new sfValidatorString(
  24. array('min_length' =&gt; 6, 'max_length' =&gt; 64),
  25. 'required'   =&gt; 'パスワードを入力してください。',
  26. 'min_length' =&gt; 'パスワードは%min_length%文字以上です。',
  27. 'max_length' =&gt; 'パスワードは%max_length%文字以下です。',
  28. )
  29. )
  30. ));
  31. }
  32.  
  33. }

エラー処理とかもバリデータが一括で請け負ってくれるので、データベースへの問い合わせもバリデータにすればすっきりすると思って、作ろうと思ったけど、ネット上に資料が少ない。どうやっていいかよくわからない。

とりあえず、複数項目のバリデートだから、validatorSchemaのsetPostValidator()に登録するんじゃないかと気づく。こんな感じでsfValidatorSchemaLoginクラスを作ってみた。

php:
  1. /**
  2. * sfValidatorSchemaLogin
  3. *
  4. * @package    Travi
  5. * @subpackage validator
  6. */
  7. class sfValidatorSchemaLogin extends sfValidatorBase
  8. {
  9. /**
  10. * @see sfValidatorBase
  11. */
  12. protected function doClean($values)
  13. {
  14. if ($values['mail_pc'] != '' &amp;&amp; $values['password'] != '') {
  15. // 両パラメータが与えられたときだけ実行
  16. $encrypted_password = myUtil::encrypt($values['password']);
  17. if (MemberPeer::isValidUser($values['mail_pc'], $encrypted_password)) {
  18. return $values;
  19. } else {
  20. throw new sfValidatorError($this, 'invalid', array());
  21. }
  22. }
  23. return $values;
  24. }
  25. }

このクラスのミソは、必要なパラメータ(メールアドレスとパスワード)がそろったときだけ、データベースに問い合わせること。なぜかというと、メールアドレスかパスワードのバリデータが成功した時点でデータベースの問い合わせをした方が効率がいいので。

最後に、LoginFormのPostValidatorを登録

php:
  1. $this-&gt;validatorSchema-&gt;setPostValidator(
  2. new sfValidatorSchemaLogin(
  3. 'invalid'  =&gt; 'メールアドレスかパスワードが間違っています。'
  4. )
  5. )
  6. );

意外と簡単にいけたけど、もっとスマートな方法ないかなぁ。。。Propel系の標準Validatorがいまいちよくわからないんだな。

No comments

次ページへ »