忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2024/04/24 05:43 |
PHP1-16:Pear::MDB2/プリペアードステートメント
プリペアードステートメントとは、まずSQL文の雛形を用意しておいて、実行時には動的に変更する部分だけを送り込むというSQLの実行方法です。
今回のように単発のSQL文を発行する場合にはあまり意味がありませんが、たとえば最初に別のファイルからIDかなにかのリストを取得して、その全リストに対してforeachでSQLを発行したい、などという場合に非常に早くなるそうです。
まあDBのバージョンや対応状況によってはキャッシュが効かないとか逆に遅くなるとか問題点もあるみたいだけれど気にしない。

peardb.class.php 

<?php
    define('PEAR_MDB2_DIR',$_SERVER['DOCUMENT_ROOT'].'/src/php/pear/MDB2/');
    require_once(PEAR_MDB2_DIR.'MDB2.php');

    class pearMDB2Model {
        
        //メンバ変数
        protected $db_dsn = array(
            'phptype'  => 'mysqli',
            'username' => 'testuser',
            'password' => 'testpass',
            'hostspec' => 'localhost',
            'database' => 'testdb'
            );
        protected $db='';
        protected $res='';
        protected $sql_insert='';
        protected $sql_update='';
        protected $sql_select='';

        //コンストラクタ
        public function __construct(){
            //接続
                ini_set('include_path',PEAR_MDB2_DIR.PATH_SEPARATOR.ini_get('include_path'));
                $this->db= & MDB2::singleton($this->db_dsn,'charset=utf8');
                if (PEAR::isError($this->db)) {
                    die($this->db->getMessage());
                }
                $this->db->setFetchMode(MDB2_FETCHMODE_ASSOC);
                $this->db->query('SET NAMES UTF8');          

            //insert文の用意
                $sql_insert =' INSERT INTO books ';
                $sql_insert.=' (name,author,publisher,price,isbn,date,comment,created_at) ';
                $sql_insert.=' values ';
                $sql_insert.=' ( :name , :author , :publisher , :price , :isbn , :date , :comment , now() )';
                $this->sql_insert=$this->db->prepare($sql_insert);
                
            //update文の用意
                $sql_update ='UPDATE books SET ';
                $sql_update.='  name= :name ';
                $sql_update.=', author= :author ';
                $sql_update.=', publisher= :publisher ';
                $sql_update.=', price= :price ';
                $sql_update.=', isbn= :isbn ';
                $sql_update.=', date= :date ';
                $sql_update.=', comment=:comment ';
                $sql_update.=', updated_at=now() ';
                $sql_update.=' where id= :id ';
                $this->sql_update=$this->db->prepare($sql_update);
                
            //select文の用意
                $sql_select =' SELECT * FROM books ';
                $sql_select.=' WHERE ';
                $sql_select.=' name LIKE :name ';
                $sql_select.='and author LIKE :author ';
                $sql_select.='and publisher LIKE :publisher ';
                $sql_select.='and price LIKE :price ';
                $sql_select.='and isbn LIKE :isbn ';
                $sql_select.='and date LIKE :date ';
                $sql_select.='and comment LIKE :comment ';
                $this->sql_select=$this->db->prepare($sql_select);
        }

        //インサート
        public function insert_prepare($sql_array){
            $res=$this->sql_insert->execute($sql_array);
            if (PEAR::isError($this->res)){
                return false;
            }else{
                return $this->db->lastInsertId();
            }
        }

        //セレクト
        public function select_prepare($sql_array){
            if(isset($sql_array['name']))        {$sql_array['name']='%'.$sql_array['name'].'%';                }else{$sql_array['name']='%%';}
            if(isset($sql_array['author']))        {$sql_array['author']='%'.$sql_array['author'].'%';            }else{$sql_array['author']='%%';}
            if(isset($sql_array['publisher']))    {$sql_array['publisher']='%'.$sql_array['publisher'].'%';    }else{$sql_array['publisher']='%%';}
            if(isset($sql_array['price']))        {$sql_array['price']='%'.$sql_array['price'].'%';            }else{$sql_array['price']='%%';}
            if(isset($sql_array['isbn']))        {$sql_array['isbn']='%'.$sql_array['isbn'].'%';                }else{$sql_array['isbn']='%%';}
            if(isset($sql_array['comment']))    {$sql_array['comment']='%'.$sql_array['comment'].'%';        }else{$sql_array['comment']='%%';}

            $res=$this->sql_select->execute($sql_array);
            $res=$res->fetchAll();
            if (PEAR::isError($this->res)){
                return false;
            }else{
                return $res;
            }
        }

        //アップデート
        public function update_prepare($id,$sql_array){
            $sql_array['id']=$id;
            $res=$this->sql_update->execute($sql_array);
            if (PEAR::isError($this->res)){
                return false;
            }else{
                return $this->db->lastInsertId();
            }
        }       

        //プリペアードステートメント使わないセレクト
        public function select($sql_array){
            $sql =' select * from books ';
            $sql.=' where deleted_at is null ';
            foreach($sql_array as $key=>$val){
                $sql.=" and ".$key." like ".$this->db->quote('%'.$val.'%','text');
            }
            $res=$this->db->query($sql);
            $res=$res->fetchAll();
            if (PEAR::isError($this->res)){
                return false;
            }else{
                return $res;
            }
        }

        //プリペアードステートメント使わないアップデート
        public function update($id,$sql_array){
            $sql =' update books set update_date=now(), ';
            foreach($sql_array as $key=>$val){
                $sql.=$key."=".$this->db->quote($val,'text')." , ";
            }
            $sql.=' where id='.$this->db->quote($id,'text');            
            $res=$this->db->query($sql);
            $res=$res->fetchAll();
            if (PEAR::isError($this->res)){
                return false;
            }else{
                return $res;
            }
        }

#↓クラスのおわり
}


peardb.php

<?php
    require_once('./peardb.class.php');
    $db = new pearMDB2Model();

    $ins_array['name']            ='恋空〈上〉';
    $ins_array['author']        ='美嘉';
    $ins_array['publisher']        ='スターツ出版';
    $ins_array['price']            ='1050';
    $ins_array['isbn']            ='978-4883810451';
    $ins_array['date']            ='2006-10-01';
    $ins_array['comment']        ='スイーツ(笑)';
    
    $ret1=$db->insert_prepare($ins_array);
    $ret2=$db->select_prepare($ins_array);
    print("<pre>");var_dump($ret1,$ret2);


まずコンストラクタで一気にSQL文を作成してしまいます。
一般には、
$sql_insert =' INSERT INTO books (name,author,publisher,price,isbn,date,comment,created_at) values ( ? , ? , ? , ? , ? , ? , ? , now() )';
というふうに?を使用します。

各メソッドでは、最初に用意したSQL文を呼び出し、executeメソッドに突っ込みます。
execute($sql_array)を実行すると、$sql_array[0]が最初の?に、$sql_array[1]がその次の?に、というふうに自動的に割り振られた上でSQL文を実行してくれます。
今回はかわりに:nameという風に指定しています。
この場合は連想配列のキーを参照してくれ、:nameの部分には$sql_array['name']を、:authorには$sql_array['author']を、という風に割り当てを行ってくれます。

なんといっても便利なのは、この場合エスケープの処理を書かなくてもMySQL側で自動的にエスケープしてくれることです。
ついうっかり一箇所エスケープし忘れた、等という事がなくなります。


逆にプリペアードステートメント最大の欠点は、可変数の引数を設定出来ないことです。
例えば上記のSELECT文は、検索しないカラムがあった場合、LIKE '%%'というまったく無意味な条件で検索を行うことになります。
nameだけで検索を行いたいのでそれ以外のカラムはSELECT文に入れたくない、という設定が不可能です。

SELECTならまだいいですが、特に面倒なのがUPDATE。
ひとつのカラムだけをUPDATEしたいという場合にも、全てのカラムの内容を用意する必要があります。
priceはかわってないからそのままでいいよ、というときに、',price= ?'の部分だけを削除する、というようなことができないのです。
このような場合はプリペアードステートメントを使用しないselectやupdateメソッドのほうがいいかもしれません。


まあ正直プリペアードステートメントはわかりにくいので無理に使う必要はないかもしれません。
エラーがでた場合の特定も困難ですし。
勿論使用しない場合は忘れずにインジェクション対策を行う必要があります。

PR


2008/08/18 20:38 | Comments(1) | TrackBack() | PHP

トラックバック

トラックバックURL:

コメント

トラックバックの仕方がわかりません><

http://yuubiseiharukana.blog.shinobi.jp/Entry/78/
>「無理に使う必要はないかもしれません」なんてことを書いているわけですが、忘れてください。
>「可能な限り使用しなければなりません」に修正します。
posted by 名無しさん必死だな at 2009/01/30 17:45 [ コメントを修正する ]

コメントを投稿する






Vodafone絵文字 i-mode絵文字 Ezweb絵文字 (絵文字)



<<PHP1-17:RSSリーダを作ってみる | HOME | PHP1-15:データベースを使う>>
忍者ブログ[PR]