今日もWEB更新してたら -2ページ目

PHPのClass fieldに日本語を使う

コード規約で、使うな!と言われてると思いますが。。PHPは変数に日本語が使えます。 変数に日本語を利用するには?

class table extends DB_DataObject {
   var $カラム1
   var $カラム2
}

さてこれを参照するには、いくつか方法があって。 誰でもすぐ試すのが、

$table = DB_DataObject::factory("table");
$table->カラム1 = "アミノバリュー";
$table->カラム2 = "大塚製薬";

これは、UNICODEでソースを書いたときだけ100%成功。MS932(Shift_JIS)などで書いて、EUC環境で実行するとホゲる。 一応、PHPのコード規約で変数の設定上日本語使って良い。いろいろやってたけど文字コードが違う環境で実行は出来ないようだ。

PHPで利用できる命名の文字
a ~ z
A ~ Z
0 ~ 9 (先頭には利用できない)
0x7F ~ 0xFF までのコードを持つ文字
_ (アンダーバー)

という制限があるので、日本語も通る。 でもコレは実行環境を選ぶので、怖い。そこで可変変数をちょっと、応用した。

$col1 = "カラム1";
$col2 = "カラム2";
$table = DB_DataObject::factory("table");
$table->$col1 = "アミノバリュー";
$table->$col2 = "大塚製薬";

とすれば文字コードを選ばない。 次にコレをもうちょっとスマートにしてみた。

$table = DB_DataObject::factory("table");
$table->{"カラム1"}= "アミノバリュー";
$table->{"カラム2"} = "大塚製薬";


これで問題なく、変数に日本語が使えるわけだ。 スクリプト言語ならではの可変変数は便利。 Javaでこれやるとreflectとか色々大変。。。T_T


------------

追記。

MS932(Shift_JIS)などで書いて、EUC環境で実行するとホゲる。

php.ini のinternal_encoding とか detect_order でうまく、ソースのコードを変換出来ないことが原因。


Includeしたパッケージの文字コードとかにも注意をしなくちゃならない。

ZendFramworkとSmarty

class MyView extends Smarty implements Zend_View_Interface{


}


なんて出来たら便利だろうなぁ。と思うのですが。出来なかった。

現実的には

class MyView exnteds Zend_View_Abstract{

  var $smarty

  public function __construct(){

    $this->smarty = new Smarty()

  }

}


程度に落ち着くしかない。

Smartyの設定をどこに書くか?

Smartyの初期設定をどこに書くか?

PHPの本や、解説サイトには、良く、


 Smarty Class を Extends して MySmarty を作る


と書いてある。Javaプログラマーとしては随分と違和感がする。ClassをExtendsするときは、メソッドの追加なりオーバーライドするためだし、車 exteds 乗り物。のように、オブジェクトに継承関係がキッチリ存在しなくては行けないと思う。設定の遣い回しだけに、ClassのExtendsすると、他人がソースコードを読むときにずいぶんと読みにくいだろう。


Extendsよりも、Delegationをちょっと改造した感じでつかってみ。

そして、設定はPropertiesに書くべきであろう。たとえば、Javaはプリコンパイラ通るのでClassに設定を書かない。


PHPならiniファイルに書いて parse_ini したほうが、後々使いやすいと思うのですが。



Smarty.ini

[SmartySetting]
template_dir = ""
compile_dir = ""
cache_dir = ""
config_dir = ""
caching = ""
cache_lifetime = ""
cache_modified_check = ""

$view = new ApplicationView( parse_ini_file( "Smarty.ini" ) );

$view->smarty->display('index.tpl');


ま、スクリプト言語なのであまり変わりませんが。。。$viewがSmartyつかってますよ!と言うことはApplicationview.classのソースコード見なくても分かるので、この方が好き。ただそれだけ。


ま、resourceとかの振る舞いを大幅に変えるなら継承もありかな。

ディレクトリの違いくらいで継承しないと自分ルールを決めておく。

ZendFramwork

ZendFramwworkの人柱ちゅうです・・・


http://docs.bookmacro.com/zendframework/


Controllerはシンプルでかなり便利だ。

DB周りはDataObjectには敵わないかも。

ViewはInterfaceなら使えたのに。


Zend_View_HelperはQuickFormのシンプル版。



Smartyニュース

Smartyの本が出るそうです。


Smartyの本


Zend FrameWrokが発表されました。http://framework.zend.com/

このフレームワーク内でSmartyを使う方法は、http://kpumuk.info/php/zend-framework-using-smarty-as-template-engine/ のブログに載っている。


Zend Framework’s View class has very bad capability for extending. It contains template variables but does not allow to access them, it has array with different pathes (templates, filters), but does not allow to add another type or access them. Therefor only way to use Smarty with Zend Framework is to abandon Zend_View and manipulate Smarty object directly.

First we need to create Smarty object. I do it in index.php after including Zend.php:


気ままに翻訳。)ZendフレームワークのViewクラスは拡張性がダメダメだ。Viewクラスがテンプレートに使う変数を確保してくれるが、変更がままならない。そして、パス(テンプレートとフィルタ)をいちいち配列に格納してやる必要がある。(*1)。かといってZendが決めた方法でしかアクセス出来ない。(*2)SmartyをZendFrameWrokで使うとしたらViewクラスに登録することを諦めて、Smartyインスタンスを直接呼び出すしかないようだ。

Zend.phpをInludeしたあとSmartyをZendFrameWrokに登録する方法を考えよう。



(注釈1:Smartyにはフィルタ機能が搭載されてるから、二度手間だと言うこと)

(注釈2:ViewクラスはテンプレートをAbstractしたものだから。Smartyの機能を生かすには、キャッシュとかフィルタとか、DBにテンプレ格納とか、そういうことはZend_viewでやるとめんどくさそうだ。)

<?php require('Zend.php');
 
include 'smarty/Smarty.class.php';
;
$smarty->debugging = false;
$smarty->force_compile = true;
$smarty->caching = false;
$smarty->compile_check = true;
$smarty->cache_lifetime = -1;
$smarty->template_dir = 'resources/templates';
$smarty->compile_dir = 'resources/templates_c';
$smarty->plugins_dir = array(SMARTY_DIR . 'plugins','resources/plugins');

?>

I don’t like global variables therefor I’ve added Smarty object into Zend Framework’s registry:
グローバル変数は名前空間を汚すので好きじゃない、だからZendFrameWrokに変数を登録したい。
<?php Zend::register('smarty', $smarty); ?>

Using is pretty simple. Just initialize Smarty variables in your Controller class and display template:

めっちゃ単純にやると、Smartyをインスタンス化しController側でテンプレートを表示してやれ。

<?php class IndexController extends Zend_Controller_Action { function index() { $smarty = Zend::registry('smarty');
  $smarty->assign('title', 'Test');
    $smarty->display('index.tpl');
  } function noRoute() { $smarty = Zend::registry('smarty');
    $smarty->display('error404.tpl');
  } 
}

?>

As you can see Smarty integration with Zend Framework is very simple task. Zend_View has ability to use your own filters and helper functions, but with Smarty you don’t need them because Smarty has its own plugins, filters, modifiers. Just forget about Zend_View and use best template engine for PHP in the world!


見てきたように、SmartyをZend_Frameworkに統合はとてもシンプルだ。Zend_Viewは独自のFilterやFunctionにアクセスするように設計されている。だけど、Smartyにはそんなモノ要らない。Smartyが強力なFilterやModiferを備えているからだ。Zend_Viewの事はこの際忘れて、PHPの最高峰テンプレートSmartyを使おうじゃありませんか。


と書いてある。

BOMが紛れ込む

PHP書いていてこまるのは requre requre_once include したときに 、BOMやら空白文字やら改行まで取り込んでしまうこと。コードをたどって探すのが面倒だ。


知らない間に\t やら\n が紛れ込む。HTMLなら問題ないけどXML作成しようとすると大問題。


そこで

<?php

ob_start();

require

include

requre_once

on_enc_clear();

//

$smarty->display();

echo "hogehoge";


としてやる。これである程度は解決しそう。

でもなぜかSmarty通したらBOM除去ができなかった。


まぁPHPではBOM入れるなってコトですかね。

BOMつけておくことでIEがUTF-8エンコードをしっかり解釈してくれるのですが。。。残念。

Exceptionの内容をブラウザに出力する

JSPを書いてて困るのはExceptionの取り扱い。


例外がどこでどんな内容で出るのかが困る。

<% !

private javax.servlet.jsp.JspWriter http_out;

//中略

}catch( Exception e){
e.printStackTrace( new PrintWriter(this.http_out) );
return false;
}


%>


this.http_out = out;

のように書いておけばブラウザに出力される。


ずいぶん楽になる。



Tomcatの問題点

Tomcat 4.x系にはちょっと困ったエラーがあることが分かった。さんざん調べて疲れました><


JSPがServlertにコンパイル(?)されるとき、JSPが実行される文字コードはunicode (UCS-2らしい)。

Javaの内部コードは unicode だ。だけど、JavaVMはオプションでソースの文字コードを指定出来る。

Tomcatがブラウザに出力を返すとき、Content-typeに従ってunicodeを変換して出力する


JSP→Servlet変換の文字コードが間違っていても、Tomcatがブラウザに返すときにエンコードを変えて送信することで文字化けは起こらない。


たいていの場合は、文字コードなど意識無くてもイイ。Content-type書いてればいい。これで問題ない。問題が起きるのは@includeを使うとき。ソースファイルを読み込むときJSPはUCSだと決めうちでソースファイルを合体させてしまう。ソースに適切な文字コードと適切でない文字コードが混在してしまう。


解決方法は @page Encoding や @page Content-type を使うのですが



日本語 Shift_JISで文字コードでJSPのソースを書く

<% @page Content-type="text/html; encoding=shift_jis" pageEncoding="shift_jis" %>


↓インクルードする

<%@ incude file="hoge.html" />

↓インクルードする

<%@ incude file="fooooo.jsp" />


↓JSPエンジンがservletソース変換する。


できあがったservlet ソースは

original.jsp + hoge.html + fooo.jsp


の順に並べただけのソース。


このソースをjavac でコンパイルしてServlet Class が出来る。


困ったことに、ソースは文字化けと通常の2種が混在する。なぜか?


できあがったソースは JSP(unicode) で保存されている。

original.jspはcontent-typeとpageEncoding属性があるから*.javaに変換される際、適切なエンコード処理を通って、unicodeに変換される。

@include ファイルたちは javaに変換される際に取り込まれるだけ。取り込まれるときにエンコード処理を受けない。なのでunicode ソース中にShift_JISの文字列まま埋め込まれる。Tomcatはunicodeだと思ってコンパイルする。


これはTomcatの限界らしいので、どうしようにも解決が不能だろう。



@include タグ で指定したソースファイルを変換無しに取り込む。JSPコンパイラの仕様だそうだ。


@include で指定したソースファイルの文字コードは指定してやる必要がある。


テクニックとして取り込まれるファイルと、取り込むファイルに content-type と pageEncoding を分けてやるとうまくいく。



本質的解決でないけど、これでうまくいく。


なぜか?

Content-typeはページ出力時にエンコーディングを変換する

pageEncodingはJSP→Servlert自動生成にエンコーディングを変換する


このことに起因するようだ。


ところが取り込むファイルが2個以上あるとそうも行かない。



pageEncodingをたくさん書けばいいじゃないか?そりゃそうなのですが。

Tomcat4.2ではJSP→Servletで作られるJavaコードにpageEncodingがたくさんあるとエラーになる。multiple conten-tye multiple pageencoding エラーになる。Tomcat4.xでは複数のContet-type宣言や、PageEncoding宣言があると、どの文字コードか分からなくなるから何度も使うな!怒られます。



面倒なのでweb.xmlやserver.xmlでJSP→Servletで作られるJavaソースファイルのエンコード指定する。ソースの文字コードはShift_JISに一括指定してやる。

本質的解決ではないがこれでうまくいく。




そもそもpageEncodingはソースの文字コードを指定するようだ。ServletJavaコード作成時にShift_JIS→Unicode変換しなさいよと命令しているようだ。content-typeはServlet実行時に暗黙オブジェクトoutにUnicode からShift_JISに変換てブラウザに送信しなさい。と命令するためのモノらしい



原因はどこにあるのかというと、@include するときに文字コードを変換指定出来ないこと。


どうもTomcat5.xでは解決してるようなので、アップグレードすれば済みました。


ま、確実なのはserver.xmlなどの設定をいじること。@includeは諦めて<jsp:include />を使うこと。Tomcatをアップグレードすること。日本語は諦めること。。。なんて後ろ向きな解決法ばかりだ


なんだよぉ。。><

Javaのエンコード

JavaのエンコードはUnicodeと言うことになっている。内部encodingはUTF-8。ソースもUTF-8なのだが。


BOM をつけるとコンパイルエラーになる。


さて、BOM.。この厄介な問題。

 


UTF-8 についてはBOMは任意もしくは不要ということらしい。



イロイロ試してみた。


JSPをUTF-8で書く。TomcatがJavaコードに変換する。→Javaコードで文字化け→JavaVMで化ける

JSPをUTF-8で書く。PageEncoding="UTF-8"をつける。TomcatがJavaコードに変換する→正しく処理。→JavaVMで化けない。


JSPをUTF-8で書く。@icludeタグでUTF-8のJSPをソースごと引っ張る。TomcatがJavaコードに変換する。

→Javaコードで文字化け→JavaVMで化ける。


@icnludeタグを使う場合。ソースをIncludeするのでPageEncoding属性や、Content-typeは重複指定のエラーになる。


つまりPHPやPerlののように何回もIncludeするのは非常にややこしい。




Ajaxにはタイムアウト処理がない

Ajaxの特集の本をいくつか読んでみた。


XmlHttp のインスタンス生成。について書かれているが、abrot()メソッドについて記述がほとんどない。


IE6においては timeout( xmlhtttp.abort() 10000);


を記述しないとIEがエラーで落ちる。


たいていの場合、これで落ちるようだ。


・XmlHttpのインスタンスを大量に作りすぎ。

・XmlHttlpのレスポンスをずっと待ってて、

・IEがタイムアウトすると、Xmlhttpが例外を返す。


例外をキャッチして無いと、IEが落ちる。


つまり


例外をキャッチしないといけない。

XmlHttpのタイムアウトを意識しないといけない。

サーバーに負荷がかかってレスポンスがなかなかこない。


このへんを意識しておかないとIE落ちまくりのダメダメAjaxコードの出来上がり。

この問題、絶対に意識しておかないと・・・


つまり、IE7までAjaxは待てってことか?