web-dev-qa-db-ja.com

プレーンPHPを使用してテンプレートシステムを構築する方法は?

私はstackoverflowでこの質問を読んでいました:

https://stackoverflow.com/questions/104516/calling-php-functions-within-heredoc-strings

そして受け入れられた答えは、このような単純なPHPテンプレートを実行することです:

template.php:

<html> 
 <head> 
 <title> <?= $ title?> </ title> 
 </ head> 
 <body> 
 <?= getContent()?> 
 </ body> 
 </ html>

index.php:

<?php 
 
 $ title = 'デモタイトル'; 
 
 function getContent(){
 return '<p> Hello World!</p> '; 
} 
 
 include(' template.php '); 
 
?>

私にとって、上記は、template.phpが他のスクリプトで定義されている変数に依存しているという意味で、うまく構造化されていません。また、include('template.php')を実行するときにinclude()を使用してコードを実行します(include()を使用して、すぐに実行されないクラスまたは関数を含めるのとは対照的です)。

テンプレートを関数内にラップする方が良いアプローチだと思います。

template.php:

<?php 
 
 function template($ title、$ content){
 ob_start(); ?> 
 
 <html> 
 <head> 
 <title> <?= $ title?> </ title> 
 </ head > 
 <body> 
 <?= $ content?> 
 </ body> 
 </ html> 
 
 < ?php return ob_get_clean(); 
} 
 
?>

index.php:

<?php 
 
 require_once( 'template.php'); 
 
 print template( 'Demo Title'、 '<p> Hello World!</ p > '); 
 
?>

2番目のアプローチの方が優れていますか?それを行うためのさらに良い方法はありますか?

15
Ryan

パラメータが指定されていないため、2番目のアプローチは行いません。

よく書かれた テンプレートシステムの動作の説明に関するエッセイ の動作はParrによるもので、テンプレートシステムやweb-mvcフレームワークを作成している人から引用されていることがよくあります。

個人的に、私が通常好むのは、プロパティ配列を使用してArrayObjectクラスを実装することであり、テンプレートは$this->propertyNameを参照します。これは、実際にはテンプレートオブジェクトの$this->property['name']です。これは、__setおよび__getを使用するだけでも実現できるため、次のようになります。

class Template {
  private $_scriptPath=TEMPLATE_PATH;//comes from config.php
  public $properties;
  public function setScriptPath($scriptPath){
    $this->_scriptPath=$scriptPath;
  }
  public function __construct(){
      $this->properties = array();
  }
  public function render($filename){

   ob_start();
   if(file_exists($this->_scriptPath.$filename)){
     include($this->_scriptPath.$filename);
    } else throw new TemplateNotFoundException();
    return ob_get_clean();
  }
  public function __set($k, $v){
      $this->properties[$k] = $v;
  }
  public function __get($k){
      return $this->properties[$k];
  }
}

テンプレートは次のようになります。

<html>
      <head>
         <title><?=$this->title?></title>
      </head>
      <body>Hey <?=$this->name?></body>
</html>

そしてそれを呼び出すことは次のようになります:

$view = new Template();
$view->title="Hello World app";
$view->properties['name'] = "Jude";
echo $view->render('hello.inc');

私が覚えている限り、これは古いSymfony 1.xとZend_Viewテンプレートエンジンがどのように見えるかであり、私にとっては問題ありません。

12
Aadaam

Codeigniterは私のお気に入りのフレームワークなので、Codeigniter風のソリューションを提案します。

class Template {
    protected $path, $data;

    public function __construct($path, $data = array()) {
        $this->path = $path;
        $this->data = $data;
    }

    public function render() {
        if(file_exists($this->path)){
            //Extracts vars to current view scope
            extract($this->data);

            //Starts output buffering
            ob_start();

            //Includes contents
            include $this->path;
            $buffer = ob_get_contents();
            @ob_end_clean();

            //Returns output buffer
            return $buffer;
        } else {
            //Throws exception
        }
    }       
}

テンプレートは次のようになります。

<html>
    <head>
        <title><?=$title?></title>
    </head>
    <body>
        <?=$content?>
    </body>
</html>

そして、クラスをインスタンス化し、配列のデータをコンストラクターパラメーターとして渡し、 'render'メソッドを呼び出すことによって、それをレンダリングします。

$data = array('title' => 'My title', 'content' => 'My content');
$tmpl = new Template('template.php', $data);
echo $tmpl->render();

このようにして、インクルードされたコードを直接実行するのではなく、テンプレートを読み取り可能に保つので、デザイナーは満足します。

2
lortabac

最初 template.phpは、そのままdreamweaver/bluegriffon/whateverにロードして、サーバー側の知識のないWebデザイナーが編集する方が簡単です。編集プロセス中に壊れる可能性があります。

私は最初にそれを利点のあるHTMLのように見せるために行きます、サードパーティのデザイナーが通常好む方法。

1
ZJR

テンプレートは常に外部変数に依存し、PHPは動的言語であるため、コンパイル時にそれらが存在することを強制する方法はありません。したがって、最初のアプローチはおそらく問題ありませんが、まだいくつかの問題があります。

  • この例では、HTMLエンコードによる出力は処理されません。つまり、XSSの潜在的な脆弱性が存在することになります。
  • これらの変数のいずれかが設定されていない場合、コードは警告を発します。警告が意味があるかどうかはわかりません(たぶん変数はオプションです)。

非常に簡単なアプローチは、できれば非常に短い名前で、これらの両方を処理する関数を書くことです。それを_()と呼ぶことは一般的な慣習のようです。単純なバージョンは次のようになります。

_function _($raw) {
    if (isset($raw)) {
        return htmlspecialchars($raw);
    }
}
_

そして、あなたのテンプレートでは、次のようにします:

_<title><?= _($title) ?></title>
_

_()関数を使用して行うその他のこと:

  • 生の文字列ではなくHTMLを渡す場合に、htmlspecialchars呼び出しをオーバーライドできるように、2番目の引数を指定します。
  • 型チェックを追加して、配列とオブジェクトがArrayObjectではなく、意味のあるもの(または空の文字列)を生成しないようにします。
  • 国際化のサポートを追加します(まだ別の引数)。
  • デバッグモードでは、変数の名前が定義されていない場合は、特別な形式で出力します。これにより、テンプレートに問題があるかどうかを一目で確認できます。
1
tdammers