&pgid();

11月から新しく入社した小沼です。現在研修中でPHPの勉強をしています。
先日、研修の一環として「SmartyとPearDBを利用した掲示板」を作成しました。
自分の頭の中を整理する意味もこめて、ここにその成果の一部を発表したいと思います。

#blikimore

**目次 [#nce29998]
#contents();
**概要 [#ffc248be]
***環境 [#sf8649dd]
動作環境 : Linux
Webサーバ : Apache
開発言語 : PHP
DB : MySQL
使用したライブラリなど : Smarty、PearDB
開発はWindowsマシンからSSHでLinuxにつなぎ、おこないました。
***システム構成 [#z45c15bc]
作成したアプリケーションは以下のような構成になりました。

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 --新規投稿を行うクラス

**困った点 [#k07e5025]

***Smartyとjavascript [#j6accfa2]
テンプレートファイルの中では中括弧{}が予約語として扱われます。そのためテンプレート内に以下のような記述をするとエラーになってしまいます。
 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)
-- http://sky.freespace.jp/smarty/SmartyManual_2-6-6J_html/language.function.literal.html


***文字化け [#t08c9daa]
始めは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と'(シングルクォーテーション) [#u091fe98]
入力された値をそのままDBに登録するといろいろ問題が発生します。例えば「\n」が入力されると、改行されて表示されてしまいます。もっとひどいのは「'(シングルクォーテーション)」を入力された場合です。SQLインジェクションが可能になってしまいます。さいわいPHPには
 addslashes()
という便利な関数があるので、簡単に対応できます。


2006/11/09 追記 上のSQLインジェクション対策では完全ではないようです。
http://d.hatena.ne.jp/hoshikuzu/20060211#P20060211PHPSQLINJECTION
**ソース [#g1df0ee8]
***list.tpl [#g3a926a4]
 <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 [#c12499f7]
 <?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 [#r21ca826]
 <?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://carrot.ark-web.jp/~konuma/smarty_board/list.php");
 
 ?>
***paging_manager.php [#a7afbe30]
 <?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 [#nf2ddffb]
 <?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 [#vd9922dc]
 function addPageNum(num) {
 	document.list_form.page_num.value = new Number(document.list_form.page_num.value) + num;
 }

#blikifooter(小沼);

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS

アークウェブのサービスやソリューションはこちら