2007年12月27日木曜日

PHPTALの使い方 PHPスクリプトの記述

●PHPスクリプト上での使い方
   ・定数
    PHPTAL_VERSION
        利用されているPHPTALのバージョン
        
    PHPTAL_PHP_CODE_DESTINATION
        PHPTALの一時ファイル置き場、PHPTAL.phpを読み込む前に設定すれば変更可能。
        デフォルトでは [ /tmp ] になる。
    
    PHPTAL_FORCE_REPARSE
        一時ファイルを無視して強制的にテンプレートファイルをパースするかどうかの設定。
       PHPTAL.phpを読み込む前に true にすれば、設定可能。デフォルトは false
    
    PHPTAL_TEMPLATE_REPOSITORY
        テンプレートファイル置き場のデフォルト指定。標準では指定なし。
    
    ・フィルター
        prefilterとpostfilterの二種類がある。
        
        -prefilter  : テンプレートファイルをパースする前に操作するフィルタ。
                      直後にパースすることになるため、XML整形式でなくなるような操作をするとエラーになる。
                      登録の仕方 $phptal->setPreFilter(new Prefilter());
                     
        -postfilter : テンプレートファイルをパースし、TAL式などすべて実行後に操作するフィルタ。
                      prefilterと違いパースエラーの心配はない。
                      登録の仕方 $phptal->setPostFilter(new Postfilter());
        
        なお、共通する制限として、setPre(Post)Filterで設定できるものはひとつのみになるため、
        複数のフィルターを使いたい場合は同一のフィルターに複数の処理を記載するか、以下のような
        クラスを用意するなどの対応が必要
        
                require_once 'PHPTAL_Filter.php';
        
        /**
         * PHPTALにてフィルターを複数使えるようにするフィルター
         */
        class FilterChain implements PHPTAL_Filter {
            private $_filters = array();
        
            public function add(PHPTAL_Filter $filter){
                $this->_filters[] = $filter;
            }
        
            public function filter($source){
                foreach ($this->_filters as $filter){
                    $source = $filter->filter($source);
                }
                return $source;
            }
        }
        
        
    ・トリガー
        登録しているトリガーのID を phptal:id に設定すると、そのIDに対して、トリガーを呼び出す。
        トリガーは start() → end()の順で呼び出され、start() で return される値により、タグの動作が変わる。
        -return self::SKIPTAG  :  そのタグ自身、要素などは出力せずに end() を実行する。
        -return self::PROCEED  :  そのタグ自身、要素などを出力した後に end() を実行する。
        return しなかった場合は SKIPTAG と同等の動きをする。
        
        
    ・TranslationService
        標準の PHPTAL_GetTextTranslator とは別に独自の文字列置換用クラスを実装することが可能。
        以下の4つのメソッドを実装しなくてはならない。
        
        - method setLanguage(...)
        使用言語を設定するメソッド。
        func_get_args() で 引数を取得するようにし、最初に設定されている言語が標準で利用される。
        
        - method useDomain($domain)
        ドメインを指定するメソッド。アプリケーションごとの言語設定の保存先を決定していれば、その中で
        開くドメインが決定される。
        
        - method setVar($key,$value)
        このメソッドは i18:name をコールした際に、最後に呼ばれるので、文章に反映される。
        
        - method translate($key)
        実際に文字列を置き換えるメソッド。 
        

                require_once 'PHPTAL/TranslationService.php';
        
        class MyTranslator implements PHPTAL_TranslationService {
        
            private $_currentDomain;
            private $_domains = array();
            private $_currentLang;
            private $_context = array();
            
            public function setLanguage(){
                $langs = func_get_args();
                foreach ($langs as $lang){
                    // if $lang known use it and stop the loop
                    $this->_currentLang = $lang;
                    return;
                }
            }

            public function useDomain($domain){
                if (!array_key_exists($domain, $this->_domains)){
                    $file = "domains/$this->_currentLang/$domain.php";
                    $this->_domains[$domain] = include($file);
                }
                $this->_currentDomain = $this->_domains[$domain];
            }
            
            public function setVar($key, $value){
                $this->_context[$key] = $value;
            }
            
            public function translate($key){
                $value = $this->_currentDomain[$key];
                
                // interpolate ${myvar} using context associative array
                while (preg_match('/\${(.*?)\}/sm', $value, $m)){
                    list($src,$var) = $m;
                    if (!array_key_exists($var, $this->_context)){
                        $err = sprintf('Interpolation error, var "%s" not set', 
                                       $var);
                        throw new Exception($err);
                    }
                    $value = str_replace($src, $this->_context[$var], $value);
                }
                
                return $value;
            }
        }
        ?>
       
    ・PHPTAL_GetTextTranslator
    gettextはPHPでGNU Gettextをサポートさせているときに利用可能な、標準のGNU国際化/翻訳システム。
    簡単に利用できるが、gettext関連の関数ををインストールしないといけないのが難点。
    
    http://php.mirror.camelnetwork.com/manual/ja/function.gettext.php

        - gettext のインストール
        - 「--with-gettext[=DIR]」をつけてPHPを再インストール
        - gettext が利用できるかは以下で確認可能
        if (function_exists("gettext")){
            echo "gettext is supported\n";
        } else {
            echo "gettext is not installed\n";
        }
        
        -ディレクトリ構成
       こんな感じの予定。translationsじゃなくてlocalのほうがいいか・・・?
        views
        `-- translations
            |-- en_US
            |   `-- LC_MESSAGES
            |       |-- zfs.mo
            |       `-- zfs.po
            |-- ja_JP
            |   `-- LC_MESSAGES
            |       |-- messages.mo
            |       |-- zfs.mo
            |       `-- zfs.po
            `-- zfs.pot
        
        - pot ファイルの作成
        多言語対応するための基本となるファイル。phpに直接記載していれば、xgettext コマンドでとってこれる。
        今回はPHPTALに記述されるので自動抽出ができない為、手動で作成。以下のような感じ。
        !!!このファイルを言語関連の基本ファイルとし、msgid の追記はこのファイルのみに行う。!!!
        ※"Content-Transfer-Encoding: 8bit\n"までの部分は xgettext に --force-po オプションで自動生成
        
        # SOME DESCRIPTIVE TITLE.
        # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
        # This file is distributed under the same license as the PACKAGE package.
        # FIRST AUTHOR , YEAR.
        #
        #, fuzzy
        msgid ""
        msgstr ""
        "Project-Id-Version: PACKAGE VERSION\n"
        "Report-Msgid-Bugs-To: \n"
        "POT-Creation-Date: 2007-12-27 01:00+0900\n"
        "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
        "Last-Translator: FULL NAME \n"
        "Language-Team: LANGUAGE \n"
        "MIME-Version: 1.0\n"
        "Content-Type: text/plain; charset=CHARSET\n"
        "Content-Transfer-Encoding: 8bit\n"
        
        msgid "Hello World"
        msgstr ""
        
        msgid "Welcome"
        msgstr ""
        
        msgid "null"
        msgstr ""
        
        
        
        TODO:TAL形式から何らかの形式に変換して、自動で pot ファイルを作成できるようにする。
        
        - po ファイルの作成 & マージ
        一番最初は差分などないので、zfs.pot を 翻訳したい言語のディレクトリにコピーする。
        
        % cp zfs.pot ./ja_JP/LC_MESSAGES/zfs.po
        
        コピーしたら、そのファイルを編集する。こんな感じ>>> msgstr "ここに翻訳を書く"
        
        zfs.pot に文章を追加したら、差分が発生してしまい、コピーして対応するには手間が
        かかりすぎるので、マージを行う。※ 第一引数と第二引数の順番に注意!
        
        % msgmerge ja_JP/LC_MESSAGES/zfs.po zfs.pot -o ja_JP/LC_MESSAGES/zfs.po
        
        これで、差分を反映してくれる。差分は msgid に値が入っているもののみ反映。
        また、pot 側で削除した場合、翻訳済みのものはコメントアウト、未翻訳の場合は削除される。
        
        # SOME DESCRIPTIVE TITLE.
        # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
        # This file is distributed under the same license as the PACKAGE package.
        # FIRST AUTHOR , YEAR.
        #
        #, fuzzy
        msgid ""
        msgstr ""
        "Project-Id-Version: PACKAGE VERSION\n"
        "Report-Msgid-Bugs-To: \n"
        "POT-Creation-Date: 2007-12-27 01:00+0900\n"
        "PO-Revision-Date: 2007-12-26 22:05+0900\n"
        "Last-Translator: FULL NAME \n"
        "Language-Team: LANGUAGE \n"
        "MIME-Version: 1.0\n"
        "Content-Type: text/plain; charset=t=UTF-8\n"
        "Content-Transfer-Encoding: 8bit\n"
        
        #, fuzzy
        msgid "Hello World"
        msgstr "はろーわーるど"
        
        msgid "Welcome"
        msgstr "ようこそ"
        
        msgid "null"
        msgstr ""
        
        また、差分を反映した際、幾つ未翻訳や変更点があるかなどを確認したいときには、
        以下のコマンドで確認できる
        
        % msgfmt --verbose ja_JP/LC_MESSAGES/zfs.po
        1 個の翻訳メッセージ, 1 個の翻訳があいまいです, 1 個の未訳のメッセージ.
        
        結果の説明
        ・翻訳メッセージ -> そのまま文字通り。
        ・翻訳があいまい -> pot ファイル側で変更があった場合、po 側に変更があったことを通知するために
                            変更があった msgid の前の行に #, fuzzy と追記される。これの数が表示される。
                            つまり、翻訳済みだが変更があったため要確認 な状態。
        ・未訳のメッセージ -> 新規に追加された msgid の分 msgstr が空の状態。
        
        - mo ファイルの作成
        プログラム側で扱うバイナリ形式に変換する
        
        % cd ./ja_JP/LC_MESSAGES/
        % msgfmt zfs.po [-f] -o zfs.mo
        ※ -f オプションはfuzzyを含むかどうかの指定
        
        - View への組み込み
        
        $tr = new PHPTAL_GetTextTranslator();
        $tr->setLanguage('ja_JP.UTF-8', 'ja_JP');
        $tr->addDomain('zfs', '/path/to/app/views/translations/');
        $tr->useDomain('zfs');
        
        $view = new ZFS_View();
        $view->setTranslator($tr); 
        

2007年12月16日日曜日

PHPTAL ZendFrameworkへの組み込み

1.View クラスの作成

Zend/View/Interface.php を 実装し動作するようにする。
機能はとりあえず最低限

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//一時(中間)ファイル置き場を設定する。
define('PHPTAL_PHP_CODE_DESTINATION','/tmp/phptal/');

//強制的にキャッシュを上書きする
define('PHPTAL_FORCE_REPARSE',true);

//テンプレートファイル置き場
//define('PHPTAL_TEMPLATE_REPOSITORY','')

//文字コード設定、標準はUTF-8
//define('PHPTAL_DEFAULT_ENCODING','')

require_once 'Zend/View/Interface.php';
require_once 'Zend/Loader.php';

require_once 'PHPTAL.php';
require_once 'PHPTAL/GetTextTranslator.php';

/**
* PHPTALをZend_View 同様に使うためのクラス。
*/
class ZFS_View implements Zend_View_Interface
{
/**
* 各スクリプト配置ディレクトリへのパス.
* Zend_View にあわせておく
*
* @var array
*/
private $_path = array(
'script' => array()
,'helper' => array()
,'filter' => array()
);
/**
* エンコードの指定、標準は UTF-8
* require_once 'PHPTAL.php' の前に定数の設定をする必要がある。
*
* @var string
*/
private $_encoding = PHPTAL_DEFAULT_ENCODING;

/**
* PHPTAL のインスタンスを格納する
* @var mixed
*/
protected $_phptal;

/**
* アサインした変数を格納する配列
* @var array
*/
protected $_context;

/**
* i18n対応 PHPTAL_GetTextTranslator のコンストラクタ
* @var mixed
*/
protected $_gettext;

/**
* Constructor.
*
* @param array $config Configuration key-value pairs.
* @todo helper/filterを実装した際追加すること、helperはあるのか?
*/
public function __construct($config = array()){

$this->_phptal = new PHPTAL();

// set inital paths and properties
$this->setScriptPath(null);

// base path
if (array_key_exists('basePath', $config)) {
$prefix = 'Zend_View_Phptal';
if (array_key_exists('basePathPrefix', $config)) {
$prefix = $config['basePathPrefix'];
}
$this->setBasePath($config['basePath'], $prefix);
}

// user-defined view script path
if (array_key_exists('scriptPath', $config)) {
$this->addScriptPath($config['scriptPath']);
}
}

/**
* 各スクリプトのパスを返す、Zend_View にあわせる
*
* @param string $type The path type ('helper', 'filter', 'script')
* @return array
*/
private function _getPaths($type)
{
return $this->_path[$type];
}


/**
* PHPTAL オブジェクトを返す
*
* @return mixed
*/
public function getEngine(){
return $this->_phptal;
}

/**
* テンプレート設置パスを初期化し設定する。
*
* @param string|array The directory (-ies) to set as the path. Note that
* the concrete view implentation may not necessarily support multiple
* directories.
* @return void
*/
public function setScriptPath($path){
$this->_path['script'] = array();
$this->_addPath('script', $path);
return $this;
}

/**
* テンプレート設置パスを追加する
*
* @param string|array The directory (-ies) to set as the path. Note that
* the concrete view implentation may not necessarily support multiple
* directories.
* @return void
*/
public function addScriptPath($path){
$this->_addPath('script', $path);
return $this;
}

/**
* テンプレート設置パスを返す。
*
* @return array
*/
public function getScriptPaths()
{
return $this->_path['script'];
}


/**
* view 用の各リソース設置のパスを設定する。
*
* @param string $path
* @param string $classPrefix
* @return void
* @todo helper/filterを実装した際追加すること、helperはあるのか?
*/
public function setBasePath($path, $classPrefix = 'Zend_View_Phptal'){
$path = rtrim($path, '/');
$path = rtrim($path, '\\');
$path .= DIRECTORY_SEPARATOR;
$classPrefix = rtrim($classPrefix, '_') . '_';
$this->setScriptPath($path . 'templates/');
return $this;
}

/**
* Add an additional path to view resources
*
* @param string $path
* @param string $classPrefix
* @return void
* @todo helper/filterを実装した際追加すること、helperはあるのか?
*/
public function addBasePath($path, $classPrefix = 'Zend_View_Phptal'){
$path = rtrim($path, '/');
$path = rtrim($path, '\\');
$path .= DIRECTORY_SEPARATOR;
$classPrefix = rtrim($classPrefix, '_') . '_';
$this->addScriptPath($path . 'templates/');
return $this;
}

/**
* __setをオーバーロード。テンプレート変数へのアサインをする。
*
* @param string $key The variable name.
* @param mixed $val The variable value.
* @return void
*/
public function __set($key, $val){
if ('_' != substr($key, 0, 1)) {
$this->assign($key, $val);
return;
}
require_once 'Zend/View/Exception.php';
throw new Zend_View_Exception('Setting private or protected class members is not allowed', $this);
}

/**
* __issetをオーバーロード、テンプレート変数へのアサイン状況を確認
*
* @param string $key
* @return boolean
*/
public function __isset($key){
return isset($this->_context[$key]);
}


/**
* __unset をオーバーロード、テンプレート変数から変数を削除する。
*
* @param string $key
* @return void
*/
public function __unset($key){
unnset($this->_context[$key]);
}

/**
* テンプレート変数へ値をアサインする。
*
* @see __set()
* @param string|array $spec The assignment strategy to use (key or array of key
* => value pairs)
* @param mixed $value (Optional) If assigning a named variable, use this
* as the value.
* @return void
*/
public function assign($spec, $value = null){
// which strategy to use?
if (is_string($spec)) {
// assign by name and value
if ('_' == substr($spec, 0, 1)) {
require_once 'Zend/View/Exception.php';
throw new Zend_View_Exception('Setting private or protected class members is not allowed', $this);
}
$this->_context[$spec] = $value;
} elseif (is_array($spec)) {
// assign from associative array
$error = false;
foreach ($spec as $key => $val) {
if ('_' == substr($key, 0, 1)) {
$error = true;
break;
}
$this->_context[$key] = $val;
}
if ($error) {
require_once 'Zend/View/Exception.php';
throw new Zend_View_Exception('Setting private or protected class members is not allowed', $this);
}
} else {
require_once 'Zend/View/Exception.php';
throw new Zend_View_Exception('assign() expects a string or array, received ' . gettype($spec), $this);
}
return $this;
}

/**
* Clear all assigned variables
*
* Clears all variables assigned to Zend_View either via {@link assign()} or
* property overloading ({@link __get()}/{@link __set()}).
*
* @return void
*/
public function clearVars(){
$this->_phptal->_context = null;
}

/**
* 設定してきた値を PHPTAL のインスタンスににセットし、レンダリングを行う。
*
* @param string $name The script script name to process.
* @return string The script output.
*/
public function render($name){
$this->_setContext();
$this->_phptal->setEncoding($this->_encoding);
if(!empty($this->_gettext)){
$this->_phptal->setTranslator($this->_gettext);
}
$this->_phptal->setTemplateRepository($this->_path['script']);

$this->_phptal->setTemplate($name);
return $this->_phptal->execute();
}

/**
* 後から追加した物を優先的に使用する為に先頭に追加する。
*
* Zend_View_Phptal::_addPath($type, 'dirname') adds one directory
* to the path stack.
*
* Zend_View_Phptal::_addPath($type, $array) adds one directory for
* each array element value.
*
* In the case of filter and helper paths, $prefix should be used to
* specify what class prefix to use with the given path.
*
* @param string $type The path type ('script', 'helper', or 'filter').
* @param string|array $path The path specification.
* @param string $prefix Class prefix to use with path (helpers and filters
* only)
* @return void
* @todo helper/filterを実装した際追加すること、helperはあるのか?
*/
private function _addPath($type, $path, $prefix = null)
{
foreach ((array) $path as $dir) {
// attempt to strip any possible separator and
// append the system directory separator
$dir = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $dir);
$dir = rtrim($dir, DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR);

switch ($type) {
case 'script':
// add to the top of the stack.
array_unshift($this->_path[$type], $dir);
break;
default:
// add as array with prefix and dir keys
array_unshift($this->_path[$type], array('prefix' => $prefix, 'dir' => $dir));
break;
}
}
}

/**
* PHPTALに設定済みの変数をアサインする。
*
* @return void
*/
protected function _setContext(){
if(empty($this->_context)){
return false;
}
foreach($this->_context as $key => $item){
$this->_phptal->set($key, $item);
}
}

/**
* アサインされた変数を返す
*
* @return array
*/
public function getContexts(){
return $this->_context;
}

/**
* エンコードを設定する。
*/
public function setEncoding($encode){
$this->_encoding = $encode;
}

public function setTranslator($tr){
$this->_phptal->setTranslator($tr);
}

public function setPreFilter($filter){
$this->_phptal->setPreFilter($filter);
}

public function setPostFilter($filter){
$this->_phptal->setPostFilter($filter);
}

/**
* Register a trigger for specified phptal:id.
* @param $id string phptal:id to look for
*/
public function addTrigger($id, $trigger)
{
$this->_phptal->addTrigger($id, $trigger);
}
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

2.ViewRenderer への組み込み

「Zend_Controller_Front::run('/path/to/app/controllers');」の前に以下を記載、
setViewで上記Viewを設定し、テンプレートの拡張子を「.tpl.html」に変更。


+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
$view = new Zend_View_Phptal();
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
$viewRenderer->setView($view,$param);
$viewRenderer->setViewSuffix('tpl.html');
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

ZendFrameworkの標準だと、「/path/to/app/views/scripts/」以下にコントローラー名と
アクション名を使って「controller_name/action_name.phtml」になる、これと同様に
phtmlだけtpl.htmlに変えたものでテンプレートにアクセスするので、各アクション毎に
テンプレートファイルを用意し設置する。

2007年12月7日金曜日

PHPTAL 残っているTAL関連の記述に関して。

残っているTAL関連の記述方法

マニュアルだけだと何を行っているのかわからないので、次からは実際に使ってみる。
英語力がないだけなのか・・・

●I18N namespace(国際化) について
I18N?:InternationalizatioN I と N の間には18文字あるから

こんな感じで呼び出すみたい。すぐにつかわないから設定ファイルについて
調べるのは保留。

$gettext = new PHPTAL_GetTextTranslator();
$gettext->addDomain($domain,$path);
$gettext->setEncoding($this->_encoding);
$phptal = new PHPTAL();
$phptal->setTranslator($gettext);

1.i18n:translate
2.i18n:name
3.i18n:attributes
4.i18n:domain
5.i18n:target


●PHPTAL namespace
TAL仕様の拡張。PHPTAL専用
1.phptal:debug

2.phptal:tales
この属性をもつタグに囲まれた部分は、TALES 方式ではなく 指定の方式で属性、要素
の内容を設定するように変更される。
指定できる方式は、tales と php のみで、デフォルトは talesになる。

<table phptal:tales="php">
<tr tal:repeat="myobject document.getChildren()">
<td
tal:content="myobject.mymethod()[10].otherMethod()['hashkey']"></td>
</tr>
</table>

3.phptal:cache
指定時間HTML要素をキャッシュする。時間は数字に以下の4つのどれかをつけ'd', 'h', 'm' or 's'
指定する。
基本的にそのページの共有キャッシュになるが、対象変数を指定することによりその変数の値により
キャッシュを切り替えることが可能。変数には、tal:define など、テンプレート内でで定義したものを
利用できない。

Examples:
三時間キャッシュされる
<div phptal:cache="3h">...</div>

$object->idの値にが同一の場合1日キャッシュされたものが表示される。
<ul phptal:cache="1d per object/id">...</ul>

●*:block
出力しないタグ、繰り返しのために記述するタグにおいて、同一のネームスペースを大量に使う
場合に利用すると記述量が少なくてすむ。以下のどちらも同じ内容を表示する。
タグの省略とともに、出力しないタグを存在するタグで記載しなくて済むメリットもある

<tal:block condition="someCondition" repeat="item someRepeat">
<div macro="x">
</tal:block>

<div tag="" condition="someCondition" repeat="item someRepeat">
<div macro="x">
</div>


●PHPTALES
TAL、METAL、PHPTAL にて使われる式構文。1つの属性が複数のPHPTALESを含む場合は、
「;」で区切って記述する。

1.path:
基本修飾語、何も指定されなければ先頭にこれがついているものと同等のものとして動作する。
なお、変数をテキストノードやほかのTALESの中で変数を使用したい場合には
「${path/to/my/variable}」と記述する。

2.Conditional statements
比較演算子、XML属性には<や>を記載するのを避けたほうがいいので、以下のようにテキストで代替する、
tal:condition や php:で利用する。
< : LT (less than) > : GT (greater than)
<= : LE (less or equal) >= : GE (greater or equal)

string:
文字列をあらわす。ただし、「;」は式のセパレータとして働き、「$」はPATHの開始を示すので、
そのままでは利用できない。それぞれ「;」=>「;;」、「$」=>「$$」と記述する。

php:
この後に続いてPHPの構文を記述することで式を評価する。「->」は「.」で記述すること。また
変数名の$は記述する必要はない。スペースを挟んで「.」を記載した場合には、結合演算子として
扱われる。

この属性は慎重に使用すべきであるとマニュアルにはある。どうしてもメソッドが必要な場合以外は
使わないようにする。

not:
真偽を表現するものに対して、真偽値を逆転させる。

exists:
PATH存在を確認し、存在すれば真、存在しなければ偽を返す。存在するかしないかわからないPATHを
指定する場合は、これで確認してからそのPATHを使用する。

default
式ではなく、キーワードとして利用する。要素や属性として標準で設定されているものを利用することを
宣言する。以下のように「|」で連結することで、path/to/possible/varが存在しない場合は、defineで
myVarは「default my var value」になる。

<span define="myVar path/to/possible/var | default">
default my var value
</span>

structure
default と同様式ではなく、キーワード。エスケープを抑止する。変数の出力は標準でエスケープされるので、
エスケープされたくない場合には、このキーワードを設定する。

Expression chains
default の例のように、「|」を使って式を連鎖的に記述することが可能。左側から式を評価し、偽で
あるときのみ次の式を実行する。Stringは必ず真になる。


メーリングリストメモ
http://lists.motion-twin.com/pipermail/phptal/

2007年12月2日日曜日

PHPTAL

PHPTAL

●インストール

PHPTAL の最新版は以下の PEAR コマンドを使ってインストール/アップグレード可能

pear install http://phptal.motion-twin.com/latest.tar.gz
pear upgrade http://phptal.motion-twin.com/latest.tar.gz

バージョンを指定してインストールしたい場合は以下のように行える

pear install http://phptal.motion-twin.com/files/PHPTAL-1.1.9.tar.gz
pear upgrade http://phptal.motion-twin.com/files/PHPTAL-1.1.9.tar.gz

自分は ZendFramework に組み込むことを目的としているので、PEAR でのインストールは使い
ませんでした。最新版ののファイルをDL・解凍し、PATH の通ったところに設置するだけでOKです。

*Example
tar zxvf PHPTAL-1.1.9.tar.gz
cp -r PHPTAL-1.1.9/PHPTAL* /path/to/php/incluede/folder

●TAL(Template Attribute Language)の記述方法
・属性の優先順位
どの順番で属性を宣言しても問題ない。TALの仕様と同様以下のような優先度になる。
1.define
2.condition
3.repeat
4.content or replacea
5.attributes
6.omit-tag

・TAL namespace での各属性の使い方。
1.tal:define
・変数の定義をする。
tal:define="[local | global] name expression [; define-expression...]"

local : 宣言したタグとそのタグの内側にあるタグでのみ有効な変数になる。
global : 宣言以降そのテンプレート内で有効な変数になる。
name : 変数名
expression : 変数の値

2.tal:condition
・条件命令
tal:condition="expression"
expression : expressionが偽の場合、記述されたタグとその内側のタグを出力しません。

3.tal:repeat
・繰り返し構文
tal:repeat="name expression"
expression : シーケンス型を渡した場合、記述されたタグとその子要素をシーケンスの
各要素ごとに一度ずつ繰り返する。
name : 現在繰り返し処理している要素の値が設定される。

TAL Path式 を使って repeat/name という名前で繰り返し変数の名前にアクセスできる。
また、繰り返し変数には以下のプロパティがあり、 repeat/name/Property でアクセスできる。
index : 繰り返しの回数を 0 から数えた値
number : 繰り返しの回数を 1 から数えた値
even : 繰り返しが偶数回目の場合に真
odd : 繰り返しが奇数回目の場合に真
start : 現在の要素がシーケンスの最初の要素である場合に真
end : 現在の要素がシーケンスの最後の要素である場合に真シーケンスがイテレータ型の場合には必ず偽.
length : シーケンスの長さです.シーケンスがイテレータ型の場合,長さが分からないので,値は maxint になる.
key : シーケンスのキーを返す。

4.tal:content
・記述したタグで囲まれた部分を expression で置き換える
tal:content="[text | structure] expression"
text : 必要に応じてエスケープして出力される
structure : エスケープせずに出力

5.tal:replace
・記述したタグで囲まれた部分をタグも含め expression で置き換える
tal:content="[text | structure] expression"
text : 必要に応じてエスケープして出力される
structure : エスケープせずに出力

6.tal:attributes
・記述したタグの属性を設定する。nothing の場合はその属性を削除する。
default の場合は記述されている属性をそのまま出力する。
「;」区切りで複数記述することができる。属性に「;」が必要な場合「;;」と記述する必要がある。
tal:attributes="name expression[;attributes-expression]"
name : 属性名
expression : 属性の値

7.tal:omit-tag
・タグの消去
tal:omit-tag="expression"
expression : 値が真の場合、記述されたタグのみを除去する。空でも真になる。


8.tal:on-error
・エラー、例外処理
tal:on-error="expression"
パスエラーが発生した場合や、例外を受け取った場合には、タグの中身をexpressionに置き換える。


●METAL(Macro Extension for TAL) namespace について
1.metal:define-macro
・マクロの宣言
metal:define-macro="name"
name : マクロの名

2.metal:use-macro
・マクロの呼び出し
metal:use-macro="expression"
expression : マクロの名 もしくは 「ファイル名/マクロ名」

3.metal:define-slot="name"
・マクロ内の動的内容の指定、fill-slotで指定された要素内を置き換える。
define-macroを持つ要素の中のみ存在可能
metal:define-slot="name"
name : スロット名

4.metal:fill-slot="name"
・マクロ内の動的変更可能要素の指定、define-slotで指定した値に置き換える。
define-macroを持つ要素の中のみ存在可能
metal:fill-slot="name"
name : スロット名


●I18N namespace(国際化) について
1.i18n:translate
2.i18n:name

●PHPTAL namespace
TAL仕様の拡張。PHPTAL専用
1.phptal:debug
2.phptal:tales

●*:block
たくさんのTAL属性を含んでたり、繰り返し使う場合にいいみたい。


●PHPTALES
path:
Conditional statements
string:
php:
not:
exists:
default
structure
Expression chains


●TALES(TAL Expression Syntax)について

TALESの型
path - パスで値を指定する
exists - パスが存在するかテストする
nocall - パスでオブジェクトを指定する
not - 式の否定です
string - 文字列を整形する
組み込みの名前



●Zend Frameworkへの組み込み


TODO
Zend_View_Phptal的なものの作成
テンプレートの拡張子って何がいいんだろ考える
I18N namespace以下のテンプレート側の機能の調査
テンプレート側の機能のExampleを作成。


参考にしたサイト
TAL ガイド
PHPTAL マニュアル

2007年11月17日土曜日

Javascript の this についてのメモ

this の扱いに困り果てたので、メモ

以下のサイトを参考にさせてもらって、イベント経由で呼び出した場合、
クラス内の this が別のものに変わってしまう点は対応できた。

[JavaScript] イベント上の this をめぐる攻防

こんな感じでクラスを書いてみる。

testFunction = function() {
this.init();
}

testFunction.prototype = {
init : function(){
this.setEvent();
},
setEvent : function() {
Event.observe($('testbButton'), 'click', this.doFunction, false);
},
doFunction : function(obj){
this.alert(obj);
},
alert: function(obj){
alert(obj.getAttribute('id')+ 'で doFunction が実行されたよ') ;
}
}

new testFunction();

HTML上にはこんなのがあるとして。
<button type="'button'" id="'testButton'">ぼたんだよ</button>


これだと、クリックしたときのに呼び出される this.Function 内の thsi が ボタンの部分を
参照してしまうので、動かない・・・Firebugで「this.console is not a function」とエラー

で上記のサイトで説明してくれてる クロージャーを使って this を 無名関数内に
閉じ込めて?あげる、、、こんな感じかな?

setEvent : function() {
Event.observe($('testbButton'), 'click', (function(obj){
return function(){
obj.doFunction(this);
};
})(this), false);
}

Firefox だと「testbButtonで doFunction が実行されたよ」ってでるけど、IE7で動かない・・・IE7 と Firefox だと this が違うみたい。

いろいろ試したり、for ~ in で表示させたりしたところ、IE7 だと window オブジェクトが this っぽい?
window オブジェクトからは イベント発生源とれなそうなので、イベントオブジェクトが関数側に渡されるので、そちらから、「event.srcElement」で取得する。
setEvent : function() {
Event.observe($('testbButton'), 'click', (function(obj){
return function(evt){
var params = Prototype.Browser.IE ? evt.srcElement : this;
obj.doFunction(params);
};
})(this), false);
}
これで IE7 でも 「testbButtonで doFunction が実行されたよ」って表示された。おしまい。

Firefox なら event.target でも取得できそうだけど、どっちを使うのがいいんだろうかとかどっちの実装が正しいんだろうか(Firefoxだろうけど)とかは Under Translation of ECMA-262 3rd Edition(邦訳)このあたり参照で。

2007年11月8日木曜日

HTML崩壊


KAZUMiX memo

で公開されているブックマークレット、実行するとHTMLが崩壊していく。
会社で30分くらい楽しい時間を過ごしてしまった。

これがを拡張?して何かできないかなぁと

しかし、こういうの思いつく人はほんとにすごいと思う。作るのはまだできるとおもうけど絶対思いつけない。こういう発想ができるようになりたい。

2007年11月7日水曜日

rico.js 20.b3 を使ってみる2

昨日の続き

includeでどう呼び出すか?と悩んでたけど、モジュール単位で呼び出してくれる Rico.loadModule のほうがよさそうなので、とりあえずそれを使ってみる。
--モジュール名は Rico.moduleDependencies参照
--内部的にモジュールの配列分解 → include してるので、ファイル名書く手間とどのファイルが影響してるか考える時間が減るだけ

Rico.loadModule('Corner');
Rico.onLoad(function() {
Rico.Corner.round.bind(Rico.Corner);
Rico.Corner.round("leftBox", {corners:"all",bgColor:"red",blend:false} );
Rico.Corner.round("rightBox", {corners:"all",bgColor:"red",blend:false} );
});

こんな感じで読み込める。関数呼び出しは Rico.onLoad 内じゃないと動かない・・・?



普通に Script タグで呼び出して使うのと使い勝手が全く変わってくるけれども、どっちがどんな感じでいいのかわからないから、もう少し調べてみようと思う。

jQuery でもいいかなぁとか思いだしてたりもするけど

2007年11月6日火曜日

rico.js 20.b3 を使ってみる

とりあえず DragAndDrop と Corner.roundを使ってみる。

こんな感じ
 http://www.reirou.jp/sample/RicoSample1.html


まあ、ほとんどデモ そのままだけど


new してるのは、特に意味は無い。
あえて言えば、別で使ったの流用だから this 外すの面倒だったから


こんなに簡単にD&Dができちゃうと、いろいろ楽しい

直前にドロップしたDIVのtop,leftを引きずってドラッグ中にマウスからずれるので気になる・・・position:absolute;は使っちゃダメなのかな。

あと、ricoCommon.js は rico.js の中で this.include で読み込まれてるっぽいので今回使った ricoStyles.js / ricoDragDrop.js もそれで読み込めないかなぁと思ったけど、眠いので今日はおしまい。

2007年10月28日日曜日

JavaScript のデバッグ

IE で JavaScrit のデバックがしたい!といろいろ探してたら、phpspotで紹介してたので、とりあえず DebugBarCompanionJS 両方ともインストールしてみた。

エラー出ると自動で該当箇所を表示してくれてこれまでよりデバッグが楽になったし、DOMとかいろいろ見れるから、JavaScript だけでなく、いろいろ楽になった、、、と思う。もう少し使い込んでみよう。



JavaScript と言えば、今朝 Eclipse に AptanaSpket IDE をインストールしてみた。どっちがいいのか使い勝手を試してみたいのが意図。

とりあえず、軽くコード書いた感じではどちらも JavaScript 関連の機能ではそれほど違いは無いみたい、コードのアウトラインがでるので、とりあえずはそれだけでもかなりらくになったかな

Aptana は HTML/CSS など Web開発用 、Spket は SVG/XUL などの XML 開発用と両方入れて使い分けるのがベターな気がする。

HTMLとかCSSはいろいろエディタがあるけれども、XUL を楽に書ける環境がなかったので、Firefox の Extension も作ったりすることを考えると Spket をメインにしようかなぁとか思ってたりも。

あとで読む
http://d.hatena.ne.jp/teramako/20070409/p1
http://www.atmarkit.co.jp/fwcr/rensai/freeauthoring01/freeauthoring01_1.html

2007年10月25日木曜日

screen

端末マルチプレクサのscreenを使ってみる

portinstall screen でインストール

.screenrcに以下を記述、emacs使わないから大して問題ないけど、
そうするのがセオリーのようなので、あわせておく。
2行目は仮想コンソールの一覧が一番下に出るようになる。
escape ^Tt
hardstatus alwayslastline "%w"
とりあえず、覚えるコマンド
^Tt [space] 仮想コンソールの移動
^T^T 直前のコンソールに戻る
^Tc 新しい仮想コンソールを作成
^Td デタッチ(作業状態そのままでscreenから抜ける)
※デタッチしたものに戻りたい場合
   screen -r セッション名
※セッションのリスト
   secren -ls で見れる。

fake@natbox ~ % screen -ls
There is a screen on:
29826.ttyp1.reirou (Detached)
1 Socket in /tmp/screens/S-fake

fake@natbox ~ % screen -r 29826

こんな感じで戻れる、1つしかセッションが無い場合は
数字は省略可能

これでいままで複数のTeraTerm起動してたのを1つにできる!

2007年10月13日土曜日

Imagick

Imagick を使いたかったので

pecl install imagick とインストールしようとしたが下記のエラーでインストールできなかった。
checking Wand-config in default path... configure: error: Cannot locate configuration program Wand-config
ERROR: `/tmp/pear/download/imagick-2.0.0/configure --with-imagick' failed
pecl がだめなら apt-get にないかなと探したら php5-imagick というのがあったのでインストール
してみたけど、new Imagic()ができない。。。

こちら(→ [type33 wiki] )のサイトで Wand-config をインストールするためには以下で可能と
記載があったので入れてみて、再度 pecl からインストール

apt-get install libmagick++9-dev

無事起動しました。

2007年10月10日水曜日

PHPのデバッグ (2)

Zend Debugger が include_path をうまく認識してくれず、スクリプトは1ステップごとに進めるのにソースが見れないので諦めてXdebugに変更

ちなみにhtdocs以外のプロジェクトにあるファイルを参照してほしいときに以下のようなエラー表示がされてエラーになる。一応どのファイルのどこを読み込んでるかはわかるけど、ブレイクポイントとか設定のしようが無い・・・
Please add the file to the Project or Include Path in project, or structure the files in Project/Include Path to more closely resemble the layout of the files on the server.

でXdebugを有効にするのに、ZendDebuggerと同時に有効にできないので、Zend Debugger をコメントアウトしてから xdebug を有効化す

php -i | grep xdebug をしてみたら、↓こんなエラーが出てたので、extension= からzend_extension= に変更する。
PHP Warning: Xdebug MUST be loaded as a Zend extension in Unknown on line 0
php.ini の設定はこんな感じ
zend_extension=/usr/local/lib/php/20060613/xdebug.so
xdebug.dump_undefined=true
xdebug.remote_enable=true
xdebug.remote_host=xxx.xxx.xxx.xxx
PTD側の標準デバッガも Xdebug に変更して、起動してみると、しっかりとinclude_path を読み込んでくれてスクリプトのどこを実行しているか表示してくれた。

ただ、同じファイル名のものが eclipse 側にあると、どのファイルか確認するダイアログが出るので、欲を言えば、パスから予測して自動で読み込んでほしい。


そういえば、CakePHP のおいしい食べ方 で、Zend Debugger は mod_rewrite の設定していると正常に動かないって書いてあるし、その所為かな?

ZendFramework ベースで作ってるので、mod_rewriteは必須だし、そもそも同じZendなんだからそのくらい対応してほしい、、、と自分の設定がわるいだけだったらごめんなさい、ZendDebuggerの中の人

2007年10月9日火曜日

PHPのデバック

xdebug
pecl にあるようなのでインストールは簡単
#pecl install xdebug

php.ini に「 extension=xdebug.so 」と追記して終了

とりあえずZendDebuggerを使いたいのでコメントアウトしておく

ZendDebugger
http://downloads.zend.com/pdt/server-debugger/ には Linux版、MacOS版、Windows版しかないので、
とりあえずLinux版を入れて見るけど、ライブラリのリンク関連で読み込めず。。。
仕方ないので、いろいろ調べたらZendPlatformのベータ版に入ってるみたい

http://www.zend.com/products/zend_platform/zend_platform_3_0_beta

ユーザー登録面倒だなぁと思ってたら置いてくれてる人発見。ちょっと怖いけど入れてみる

http://kestas.kuliukas.com/ZendDebugger/

DLしたファイルを解答して、以下のファイルを適当な場所にコピー
ZendPlatform-3.0.3-freebsd6.0-i386/data/5_2_x_comp/ZendDebugger.so

php.ini に以下を追記して、apache を再起動
zend_extension=/path/to/ZendDebugger.so
zend_debugger.allow_hosts=xxx.xxx.xxx.xxx



php -i | grep Zend とでもして↓こんな記載が出てきてればOK

Zend Engine v2.2.0, Copyright (c) 1998-2007 Zend Technologies
with Zend Debugger v5.2.8, Copyright (c) 1999-2007, by Zend Technologies

PDTからデバッガを起動してみたけど、エントリポイントになる index.php と
ZendFramework とか コントローラー系を別プロジェクトで include path 設定で
読み込んでる所為か index.php から先に進めない。。。どうしたらいいんだろ。。。

2007年10月6日土曜日

地下展

今日は科学未来館の地下展に行ってきました

『地下展 UNDERGROUND-空想と科学がもたらす闇の冒険』
と壮大な感じですが
意外と短く終わってちょっと物足りなかったです。それでも大満足とは行かないだけで
十分満足できる内容でした。

地下の元素の組成から、地下生物、生物起源地下説、、、ときて、最後には、
地球時計的な(3人組が占拠してていじれませんでしたけど)物があり、
最後のは見ただけでも結構面白かったかな。

とはいえ、一番目を惹いたのは、地下展の中身ではなく、地下展に入る直前のフロアに
設置してある Geo-Cosmos の地球の状況。まだ工事中な感じではあるけれども、宇宙から
見た地球の状況が直径 6.5m の球に表示されていて一見の価値はあるんではないかと

お台場に行く予定があったら、1日券を買って行ってみてはどうでしょう

2007年10月4日木曜日

一部のファイルがコミットできない

commit -m "" D:/htdocs/index.php
Adding D:/htdocs/index.php
No such file or directory
svn: Commit failed (details follow):
svn: PROPFIND request failed on '/repos/htdocs/index.php'
svn: Could not open the requested SVN filesystem

commit -m "" D:/htdocs/.htaccess
Adding D:/htdocs/.htaccess
No such file or directory
svn: Commit failed (details follow):
svn: PROPFIND request failed on '/repos/htdocs/.htaccess'
svn: Could not open the requested SVN filesystem


ほかのディレクトリとかファイルはコミットできるから理由が良くわからない
調査中

10/6 追記
コミットできないのはプロジェクト直下のファイル全てと判明

コミットできない
project-
index.html

コミットできる
project-
htdocs/
index.html

理由はわからないけど、とりあえずフォルダを作ってそれをコミット
subversionってそういうものなのかは追って考えよう

2007年10月2日火曜日

彼氏あほ過ぎるwww

彼氏がもうダメかも・・・

http://imihu.blog30.fc2.com/blog-entry-2988.html


って釣りかよ

2007年10月1日月曜日

Firefoxの拡張

よく使う拡張メモ
拡張作ってみたいけど、既存ので満足しちゃっていいのが思いつかない

●普段使い

All-in-One Gestures Extension
マウスジェスチャーで業務効率化

MR Tech's Local Install

いろいろ高機能化、特に拡張

PDF Download
PDFのDL管理

Locationbar2
ロケーションバーを見やすく

Autocomplete Manager

オートコンプリートを高機能化、途中に含まれてるだけでOK

Screengrab!
画面キャプチャ、Webページ全体をキャプチャ可能

Hit a Hint!
マウスなしでブラウジング

Internote

付箋紙。Webページごと

IETab
IEをFirefoxのタブ内に開ける


●開発用

Console2
エラーコンソール拡張

firebug
javascriptとかデバッグ

LiveHTTPHeaders
HTTPヘッダ確認

Web Developer

Web開発に役立つ機能いろいろ

View Source Chart
ソースが色分け・段付けされて見やすい


●どうだろ?
Japanize
英語のページを日本語化

Leak Monitor
メモリリークを表示

QuickNote
メモ帳