SmartyとPearDBを利用した掲示板作成 http://www.ark-web.jp/sandbox/wiki/179.html
11月から新しく入社した小沼です。現在研修中でPHPの勉強をしています。
先日、研修の一環として「SmartyとPearDBを利用した掲示板」を作成しました。
自分の頭の中を整理する意味もこめて、ここにその成果の一部を発表したいと思います。
目次 †
概要 †
環境 †
動作環境 : Linux
Webサーバ : Apache
開発言語 : PHP
DB : MySQL
使用したライブラリなど : Smarty、PearDB
開発はWindowsマシンからSSHでLinuxにつなぎ、おこないました。
システム構成 †
作成したアプリケーションは以下のような構成になりました。
smarty_board
├ js
│ └ smarty_board.js --list.tpl内で利用するjavaScript
├ templates
│ └ list.tpl --画面のテンプレート
├ article_dao.php --データアクセスオブジェクト
├ list.php --list.tplに変数を設定するモジュール
├ paging_manager.php --ページング管理をおこなうクラス
└ submit_new_article.php --新規投稿を行うクラス
困った点 †
Smartyとjavascript †
テンプレートファイルの中では中括弧{}が予約語として扱われます。そのためテンプレート内に以下のような記述をするとエラーになってしまいます。
function addPageNum(num) { document.list_form.page_num.value = new Number(document.list_form.page_num.value) + num; }
回避方法としては以下の二つがあります。
- javascriptは外部ファイルに記述する。
- Smartyの中括弧が持っている機能を他の記号に割り当てる。(以下例)
$smarty->left_delimiter = '<!--{'; $smarty->right_delimiter = '}-->';
ちなみに私は前者の方法を取りました。
- もう1つ、javascriptの部分を{literal}{/literal}で囲むという表記法があります。(by 竹村 on 06/11/13)
文字化け †
始めはHTMLのエスケープを、Smartyの機能を使いテンプレートの中で以下のようにやろうと思いました。
<td>{$article.name|escape:"htmlall"}</td>
そしたら文字化けするようになってしまったので、テンプレートを次のように修正し、
<td>{$article.name}</td>
php内で以下の関数を使ってエスケープすることにしました。
function escape($str) { return nl2br(htmlentities($str, ENT_QUOTES, 'EUC-JP')); }
\nと'(シングルクォーテーション) †
入力された値をそのままDBに登録するといろいろ問題が発生します。例えば「\n」が入力されると、改行されて表示されてしまいます。もっとひどいのは「'(シングルクォーテーション)」を入力された場合です。SQLインジェクションが可能になってしまいます。さいわいPHPには
addslashes()
という便利な関数があるので、簡単に対応できます。
2007/10/25 追記 上のSQLインジェクション対策では完全ではないようです。
http://www.irisdti-jp.com/web/
ソース †
list.tpl †
<html> <head> <meta http-equiv="content-type" content="text/html;charset=EUC-JP "> <title>スマーティーを利用した掲示板</title> <script language="javascript" src= "./js/smarty_board.js" ></script> </head> <body> <center>スマーティーを利用した掲示板</center> <!-- 入力部分 --> <form action="submit_new_article.php" name="submit_form" method="POST" > <table align="center"> <tr> <td>名前</td> <td><input type="text" name="name" size="40"></td> </tr> <tr> <td>内容</td> <td><textarea name="message" rows="6" cols="70" ></textarea></td> </tr> <tr> <td><input type="submit" value="新規投稿" /></td> </tr> </table> </form> <!-- 一覧部分 --> <form action="list.php" name="list_form" method="POST" > <input type="submit" value="前へ" {$is_prev_disabled} onclick="addPageNum(-1);" /> <input type="submit" value="次へ" {$is_next_disabled} onclick="addPageNum(1);" /> {$page_num} / {$page_count} <input type="hidden" name="page_num" value="{$page_num}" /> {foreach from=$articles item=article} <hr> <table> <tr> <td>名前:</td> <!--<td>{$article.name|escape:"htmlall"}</td>--> <td>{$article.name}</td> </tr> <tr> <td nowrap>投稿日時:</td> <td>{$article.date|date_format:"%Y/%m/%d %H:%M:%S"}</td> </tr> <tr> <td>内容:</td> <td>{$article.message}</td> </tr> </table> {/foreach} <hr> <input type="submit" value="前へ" {$is_prev_disabled} onclick="addPageNum(-1);" /> <input type="submit" value="次へ" {$is_next_disabled} onclick="addPageNum(1);" /> {$page_num} / {$page_count} </form> </body> </html>
list.php †
<?php require_once('Smarty.class.php'); require('paging_manager.php'); //検索 $pager = new PagingManager(getPageNum()); $result = $pager->getArticles(); //画面用のリストに詰め替える。 $articles = array(); foreach ($result as $row){ $articles[sizeof($articles)] = array(name=>escape($row[1]), date=>$row[2], message=>escape($row[3]) ); } //スマーティーの設定 $smarty = new Smarty; $smarty->assign('articles', $articles); $smarty->assign('page_num', getPageNum()); $smarty->assign('page_count', $pager->getPageCount()); if (!$pager->hasPrevPage() ) { $smarty->assign('is_prev_disabled', 'disabled'); } if (!$pager->hasNextPage()) { $smarty->assign('is_next_disabled', 'disabled'); } $smarty->template_dir = './templates/'; $smarty->display('list.tpl'); function getPageNum(){ $page_num = (int) $_POST[page_num]; if ($page_num <= 1){ $page_num = 1; } return $page_num; } function escape($str) { return nl2br(htmlentities($str, ENT_QUOTES, 'EUC-JP')); } ?>
submit_new_article.php †
<?php require('article_dao.php'); //画面からの情報を取得する。 $article = array(); $article[name] = addslashes($_POST[name]); $article[message] = addslashes($_POST[message]); //DBへの登録処理をおこなう。 $dao = New ArticleDao; $dao -> registArticle($article); //リダイレクト header("Location:http://www.irisdti-jp.com/playonline/); ?>
paging_manager.php †
<?php require('article_dao.php'); define('PAGE_COUNT', 5); class PagingManager { var $current_page_num; var $articles; function PagingManager($page_num) { $this->current_page_num = $page_num; $dao = new ArticleDao; $this->articles = $dao->getArticles(); } function getArticles() { $limit_from = (PAGE_COUNT * $this->current_page_num) - PAGE_COUNT; $limit_to = $limit_from + PAGE_COUNT -1; $index = 0; $part_of_articles = array(); while($row =& $this->articles->fetchRow()){ if( $limit_from <= $index && $index <= $limit_to ) { $part_of_articles[sizeof($part_of_articles)] = $row; } $index++; } return $part_of_articles; } function hasPrevPage() { return ( $this->current_page_num > 1 ); } function hasNextPage() { return ( $this->current_page_num * PAGE_COUNT < $this->articles->numRows() ); } function getPageCount() { return ceil($this->articles->numRows() / PAGE_COUNT); } } ?>
article_dao.php †
<?php require('DB.php'); class ArticleDao { //一件の投稿をDBに登録する。 function registArticle($article) { //コネクション $dsn = "mysql://root:@localhost/konuma_training"; $db = DB :: connect($dsn, true); //sql文の作成 $sql = "insert into articles( submitter_name ,submit_date ,message ) values ( ? ,sysdate() ,? )"; //SQLの実行 $db->query($sql, array($article[name], $article[message])); //コネクションの開放 $db -> disconnect(); } //DBより書き込みを取得する。 function getArticlesLimit($limit_from, $count){ //コネクション $dsn = "mysql://root:@localhost/konuma_training"; $db = DB :: connect($dsn, true); //sql文の作成 $sql = "select * from articles order by id desc"; //SQLの実行 $result = $db->limitQuery($sql, $limit_from, $count); //コネクションの開放 $db -> disconnect(); return $result; } //DBより書き込みを取得する。 function getArticles(){ //コネクション $dsn = "mysql://root:@localhost/konuma_training"; $db = DB :: connect($dsn, true); //sql文の作成 $sql = "select * from articles order by id desc"; //SQLの実行 $result = $db->query($sql); //コネクションの開放 $db -> disconnect(); return $result; } } ?>
2006/11/09 一部修正 インサート時にブレースホルダを使用するように修正しました。
smarty_board.js †
function addPageNum(num) { document.list_form.page_num.value = new Number(document.list_form.page_num.value) + num; }