cakeのエレメントをDB管理する(変数に入ったhtml/phpコードをincludeする)
いくつかのエレメントファイルがview/elements/foo/以下にあって、それをDBで更新することになりました。
ctpの内容をDBの1フィールドに入れて、表示は $this->element(foo/bar)の代わりに DBから取ってきたデータをecho($foo['Model']['bar']);すれば良いかなって思ってたら、なんとbar.ctpの中に
<?php $this->element(baz/menu);?>
みたいな記述があるじゃありませんか!!
このままだと、単純にechoすると、phpのコードがそのままhtml出力されてしまうので対策しました。
結論からいうと、viewテンプレートの中でincludeで読み込めばOK。ただしincludeでファイルを読み込むわけじゃなく、変数に入ってるデータを読み込む必要があります。
ストリームラッパー
Twitterで変数に入れたデータをincludeしたいとつぶやいたら反応があり、ストリームラッパーを使えば変数に入れたコンテンツをincludeできるんじゃないかと、@iakioさんから教えてもらいました。ありがとうございました!
stream wrapperを使うと、includeやfopenでファイル以外にも色々な形式のものが開けて、http://から始まるURLや、ftp://, zlib://なんてものが透過的にread/writeできます。
http://www.php.net/manual/en/wrappers.php
標準のストリームラッパーを試す
data://というストリームがあったので下記のようにしたところうまくいきました。
<?php $data = 'hello <?php echo "world"; ?>'; include('data://text/plain,'.$data);
ただし、php.iniにallow_url_fopen, allow_url_includeがONじゃないと動きません。この設定はOFFにしておきたかったので、しょうがなくストリームラッパーを作ることにしました。
ストリームラッパーを自作
下記URLに変数に入れたものをストリームラッパーで取り出すサンプルがあり、基本的にはこれで問題ありません。
http://php.net/manual/ja/stream.streamwrapper.example-1.php
使い方は下記のように。
<?php stream_wrapper_register("var", "VariableStream"); $myvar = 'hello <?php echo "world"; ?>'; include("var://myvar");
$myvarの変数の中身がstream_wrapper_register()によって$GLOBAL['mybar']の中に格納され、VariableStreamクラスではそのGlobal変数を参照してread()メソッドの中でそれを返しているだけです。
ただ、cakephpのviewテンプレートの中でこれを使うと、GLOBAL変数に$myvarの値が格納されず、期待した動作になりませんでした。そこで、色々と改良し、下記のようにしました。
このファイルを、vendors/variable_stream.phpとして保存し、下記のようにすると動作します。
<?php App::import('Vendor', 'VariableStream'); $val = 'Hello <?php echo "World"; ?>'; stream_wrapper_register("var", "VariableStream"); include("var://".urlencode($val));
includeで渡す値はurlencode必須になってます($valの中にphpタグが入っているとうまく動かないため)。内部ではurldecodeしてデータを扱ってます。
これで、elementのデータをDBから取ってきてviewの中で変数をincludeするだけでOKとなりました。