LoginSignup
10
10

More than 5 years have passed since last update.

PHPで使うGoFパターン ひとり Advent Calendar - ストラテジー

Last updated at Posted at 2012-07-26

ストラテジーってなに?

戦略って意味ですね。実行時の状況ごとにアルゴリズムを切り替える…… というデザインパターンです。この記事の想定読者であるPHPプログラマが主に使うPHPやJavaScriptではクロージャなんかも有るのでオブジェクト指向のこのパターンを大真面目に適用することも少いかも知れませんが一応基礎ということで。
JDKや.NETFrameworkの中だと、ソートする時のComparatorとかもこのパターンだと思います。

<?php
function buildEscaper ( $type ) {
  $type = strtolower ( $type );
  if     ( $type == 'html'  ) return function ( $s ) { return htmlspecialchars ( $s, ENT_QUOTES ); }
  elseif ( $type == 'url'   ) return function ( $s ) { return urlencode ( $s ); }
  elseif ( $type == 'regex' ) return function ( $s ) { return preg_quote ( $s ); }
  elseif ( $type == 'sql'   ) return function ( $s ) { return mysql_real_escape_string ( $s ); }
  else   throw new Exception ( 'そんなの有りません|' . $type );
}

$h = buildEscaper ( 'html' );
print $h ( '<html>' );

ストラテジーの構造

  • コンテキスト - 使用者
  • ストラテジーインターフェース - インターフェースね
  • 実ストラテジー - 実際のアルゴリズム

それぞれのクラス構造

<?php
/* Context */
class TemplateEngine {
  private $_Escaper = null;
  private $_TemplatePath = null;
  private $_Params = array ();

  public function __construct ( $escaper ) {
    if ( !( $escaper instanceof IEscaper ) )
      throw new Exception ( 'Escaperじゃないもん' );
    $this->_Escaper = $escaper;
  }

  public function setTemplatePath ( $path ) {
    if ( !is_file ( $path ) )
      throw new Exception ( 'そんなファイル無いもん' );
  }

  public function assign ( $s ) {
    print $this->_Escaper->escape ( $s ); 
  }

  public function parse () { 
    ob_start ();
    include $this->_TemplatePath;
    return ob_get_clean();
  }

  public function setParam ( $key, $val ) {
    $this->_Params[$key] = $val;
  }

  public function getParam ( $key ) {
    return $this->_Params[$key];
  }
}

/* Strategy Interface */
interface IEscaper {
  public function escape();
}

/* Strategy */
class HTMLEscaper implements IEscaper {
  public function escape ( $s ) { return htmlspecialchars ( $s, ENT_QUOTES ); }
}

class URLscaper implements IEscaper {
  public function escape ( $s ) { return urlencode ( $s ) ; }
}

class RegExpEscaper implements IEscaper {
  public function escape ( $s ) { return preg_quote ( $s ) ; }
}

class SQLEscaper implements IEscaper {
  public function escape ( $s ) { return mysql_real_escape_string ( $s ) ; }
}

HTMLテンプレート

<html>
<body>
<table>
<?php foreach ( $this->getParam ( 'arr' ) as $row ): ?>
<tr>
  <td><?php $this->assign ( $row->id ) )?></td>
  <td><?php $this->assign ( $row->name ) )?></td>
</tr>
<?php endforeach; ?>
</table>
</body>
</html>

SQLテンプレート

SELECT * FROM tbl WHERE name = '<?php $this->assign ( $this->getParam('name') ) ?>'

実際に使うところ

<?php
$html = new TemplateEngine ( new HTMLEscaper() );
$html->setTemplatePath ( '/path/to/html_template' );
$html->setParam ( array (
  'arr' => array ( 
    (object)array ('id' => 1, 'name' => '<script>alert("name1");</script>' ) ,
    (object)array ('id' => 2, 'name' => 'name2' ) ,
    (object)array ('id' => 3, 'name' => 'name3' ) ,
  ),
));
print $html->parse();

$sql = new TemplateEngine ( new SQLEscaper () );
$sql->setTemplatePath ( '/path/to/sql_template' );
$sql->setParam ( array ( 'name' => "n'am'e" ) );
print $sql->parse();
10
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10