bindhelper.php


<?php

//###define
define( "TD_NAME", 0 );//テーブル名
define( "TD_N", 1 );//col数
define( "TD_KEY", 2 );//wherekeyの数
define( "TD_COLNAME0", 3 );//col名先頭

define( "TD_AUTO", -1 );//通常
define( "TD_NUM", 0 );//num
define( "TD_STR", 1 );//str
define( "TD_DATE", 2 );//date
define( "TD_DATE_I", 3 );//insert時オートsysdate
define( "TD_DATE_U", 4 );//insert/update時オートsysdate
define( "TD_NOBIND", 5 );//バインドしない

//###val
$debug_buf = "";

//###Bindhelper_Con
class Bindhelper_Con {
    var $con;//OCI

    function &Init( $id, $pass, $dbstr )
    {
        $this->con = OCIPLogon( $id, $pass, $dbstr );

        return $this->con;
    }
};


//###Bindhelper_Con_EX
class Bindhelper_Con_EX extends Bindhelper_Con {
    var $dateformat_old;

    function &Init( $id, $pass, $dbstr )
    {
        $this->con = OCIPLogon( $id, $pass, $dbstr );
        bh_AddDebugBuf("Init Bindhelper_Con_EX");

        $this->dateformat_old = $this->GetNLSDATEFORMAT();
        $this->ChangeNLSDATEFORMAT( "YYYY/MM/DD HH24:MI:SS" );

        return $this->con;
    }


    function End()
    {
        if( $this->con )
        {
//          $this->ChangeNLSDATEFORMAT("RR-MM-DD");
            $this->ChangeNLSDATEFORMAT($this->dateformat_old);//"RR-MM-DD");
            bh_Rollback();
            bh_AddDebugBuf("End Bindhelper_Con_EX");
            $this->con = NULL;
        }
    }

    function ChangeNLSDATEFORMAT( $str )
    {
        $req = "ALTER SESSION SET NLS_DATE_FORMAT='" . $str . "'";
    
    //  bh_Request($req,false);
    
        bh_AddDebugBuf($req);
        $stmt = OCIParse( $this->con, $req );
        OCIExecute( $stmt );
        OCIFreeStatement( $stmt );
    }

    function GetNLSDATEFORMAT()
    {
        $stmt = OCIParse( $this->con, "SELECT VALUE FROM NLS_SESSION_PARAMETERS WHERE PARAMETER = 'NLS_DATE_FORMAT'" );
        OCIExecute( $stmt );
        OCIFetchInto( $stmt,$row );
    // bh_AddDebugBuf($row[0]);
        OCIFreeStatement( $stmt );
    
        return $row[0];
    }
};

$bhc = new Bindhelper_Con_EX();//globalに投げ出す


//###Bindhelper
class Bindhelper {
//##protected:
        var $TD;//テーブル定義への参照
        var $pkupdate;
        var $bLoad;//データロードされてるフラグ
        var $Value;//データ本体リスト
        var $pkshadow;//$pkupdate用。。
        var $bUpdate;//更新フラグリスト


        //内部処理用
        function Fill( &$stmt )//フェッチ済stmtからデータもらう
        {
            $this->bLoad = false;

            for( $i = 0; $i < $this->TD[TD_N]; $i++ )
            {
                $this->Value[$i] = OCIResult($stmt,$i+1);

                if( $i < $this->TD[TD_KEY] )
                {
                    $this->pkshadow[$i] = $this->Value[$i];//プライマリキーコピー。。
                }

                $this->Value[$i] = trim( $this->Value[$i] );

                $this->bUpdate[$i] = false;
            }

            $this->bLoad = true;
        }


//##public:
        //コンストラクタ
        function Bindhelper( &$td, $pkupdate=false )
        {
            $this->TD =& $td;//テーブル定義への参照もらう
            $this->pkupdate = $pkupdate;
            $this->bLoad = false;
            $this->Value = array();
            $this->pkshadow = array();
            $this->bUpdate = array();
        }


        function Load( $binddata, $forupdate=false )//標準load
        {
            global $bhc;

            $update = 0;//:V?

            $colstr = bh_CatColName( $this->TD );//colm名ならべる

            $req = "SELECT $colstr FROM " . $this->TD[TD_NAME] . " WHERE ";

            for( $i = 0; $i < $this->TD[TD_KEY]; $i++ )
            {
                $req .= sprintf( "%s%s=:V%d", (0<$i)?" AND ":"", $this->TD[ TD_COLNAME0+$i*2 ], $update );//:Vx
                $update++;
            }
    
            //おしまい
            if( $forupdate )
            {
                $req .= " FOR UPDATE";
            }

bh_AddDebugBuf($req);
            $stmt = OCIParse( $bhc->con, $req );

            //バインド
            for( $i = 0; $i < $update; $i++ )
            {
                $buf = sprintf( ":V%d", $i );
                OCIBindByName( $stmt, $buf, $binddata[$i], -1 );
bh_AddDebugBuf("bind:V".$i."=".$binddata[$i]);
            }

            @OCIExecute( $stmt, ($forupdate)? OCI_DEFAULT:OCI_COMMIT_ON_SUCCESS );//$forupdateでロックするならコミットするな
            if( $err = OCIError($stmt) )
            {
                foreach( $err as $key=>$val )
                {
                    bh_AddDebugBuf("$key=>$val");
                }
                return false;
            }

            $ret = OCIFetch($stmt);//先頭1行もらう
            if( $ret )
            {
                $this->Fill( $stmt );//データfill
            }
            OCIFreeStatement($stmt);

            if( $ret )
            {
                return true;
            }
            else
            {
                return false;
            }
        }


        function Save( $autocommit=true )//標準Save
        {
            global $bhc;

            if( !$this->bLoad )
            {
                return false;
            }

            $update = 0;//:V?
            $autosysdate = false;
            $binddata = array();//bind送りするデータ
            $req = "UPDATE " . $this->TD[TD_NAME] . " SET ";

            $i0 = ($this->pkupdate)? 0:$this->TD[TD_KEY];//プライマリキーのupdate有り無し

            for( $i = $i0; $i < $this->TD[TD_N]; $i++ )
            {
                if( $this->TD[ TD_COLNAME0+$i*2+1 ] == TD_DATE_U )//fixme
                {   //auto sysdate
                    $req .= sprintf( "%s%s=SYSDATE", (0<$update||$autosysdate)?",":"", $this->TD[ TD_COLNAME0+$i*2 ] );
                    $autosysdate = true;
                }
                else if( $this->bUpdate[$i] )
                {   //request文構成
                    $req .= sprintf( "%s%s=:V%d", (0<$update||$autosysdate)?",":"", $this->TD[ TD_COLNAME0+$i*2 ], $update );
                    $binddata[$update] = $this->Value[$i];
                    $update++;
                }
            }
            //検索キーとフッタ追加
            if( 0 < $update || $autosysdate )
            {
                $req .= " WHERE ";

                for( $i = 0; $i < $this->TD[TD_KEY]; $i++ )
                {
                    $req .= sprintf( "%s%s=:V%d", (0<$i)?" AND ":"", $this->TD[ TD_COLNAME0+$i*2 ], $update );//:Vx
                    $binddata[$update] = $this->pkshadow[$i];
                    $update++;
                }

bh_AddDebugBuf($req);
                $stmt = OCIParse( $bhc->con, $req );
                //バインド
                for( $i = 0; $i < $update; $i++ )
                {
                    OCIBindByName( $stmt, ":V$i", $binddata[$i], -1 );
//                  $binddata[$i] = stripslashes($binddata[$i]);//余計なお世話
bh_AddDebugBuf("bind:V".$i."=".$binddata[$i]);
                }

                @OCIExecute($stmt, ($autocommit)? OCI_COMMIT_ON_SUCCESS:OCI_DEFAULT );
                if( $err = OCIError($stmt) )
                {
                    foreach( $err as $key=>$val )
                    {
                        bh_AddDebugBuf("$key=>$val");
                    }
                    return false;
                }

                OCIFreeStatement($stmt);

                for( $i = 0; $i < count($this->bUpdate); $i++ )
                {
                    $this->bUpdate[$i] = false;
                }

                return true;
            }

            return true;
        }


        function Insert( $binddata, $autocommit=true )//標準insert
        {
            global $bhc;

            $colstr = "(".bh_CatColName($this->TD).")";//colm名ならべる

            $update = 0;//:V?
            $autosysdate = false;
            $req = "INSERT INTO " . $this->TD[TD_NAME] . $colstr . " VALUES(";


            for( $i = 0; $i < $this->TD[TD_N]; $i++ )
            {   //request文構成
                switch( $this->TD[ TD_COLNAME0+$i*2+1 ] )
                {
                    case TD_DATE_I://auto sysdate
                    case TD_DATE_U:
                        $req .= sprintf( "%sSYSDATE", (0<$update||$autosysdate)?",":"" );
                        $autosysdate = true;
                        break;

                    case TD_NOBIND:
                        $req .= sprintf( "%s%s", (0<$update||$autosysdate)?",":"", $binddata[$i] );
                        $update++;
                        $autosysdate = true;
                        break;

                    default:
                        $req .= sprintf( "%s:V%d", (0<$update)?",":"", $update );
                        $update++;
                        break;
                }
            }

            $req .= ")";//おしまい

bh_AddDebugBuf($req);
            $stmt = OCIParse( $bhc->con, $req );
            //バインド
            for( $i = 0; $i < $update; $i++ )
            {
                if( $this->TD[ TD_COLNAME0+$i*2+1 ] != TD_NOBIND )
                {
                    OCIBindByName( $stmt, ":V$i", $binddata[$i], -1 );
//                  $binddata[$i] = stripslashes($binddata[$i]);//余計なお世話
bh_AddDebugBuf("bind:V".$i."=".$binddata[$i]);
                }
            }

            @OCIExecute($stmt, ($autocommit)? OCI_COMMIT_ON_SUCCESS:OCI_DEFAULT );
            if( $err = OCIError($stmt) )
            {
                foreach( $err as $key=>$val )
                {
                    bh_AddDebugBuf("$key=>$val");
                }
                return false;
            }

            OCIFreeStatement($stmt);

            return true;
        }


        function Delete( $binddata, $autocommit=true )//標準delete
        {
            global $bhc;

            $update = 0;//:V?
            $req = "DELETE FROM " . $this->TD[TD_NAME] . " WHERE ";

            for( $i = 0; $i < $this->TD[TD_KEY]; $i++ )
            {
                $req .= sprintf( "%s%s=:V%d", (0<$i)?" AND ":"", $this->TD[ TD_COLNAME0+$i*2 ], $update );//:Vx
                $update++;
            }


bh_AddDebugBuf($req);
            $stmt = OCIParse( $bhc->con, $req );
            //バインド
            for( $i = 0; $i < $update; $i++ )
            {
                OCIBindByName( $stmt, ":V$i", $binddata[$i], -1 );
bh_AddDebugBuf("bind:V".$i."=".$binddata[$i]);
            }

            @OCIExecute($stmt, ($autocommit)? OCI_COMMIT_ON_SUCCESS:OCI_DEFAULT );
            if( $err = OCIError($stmt) )
            {
                foreach( $err as $key=>$val )
                {
                    bh_AddDebugBuf("$key=>$val");
                }
                return false;
            }

            OCIFreeStatement($stmt);

            return true;
        }


        function Merge( $binddata, $autocommit=true )//update/insert
        {
            global $bhc;

            $wherekeystr = " WHERE ";
            for( $i = 0; $i < $this->TD[TD_KEY]; $i++ )
            {
                $wherekeystr .= sprintf( "%s%s=:V%d", (0<$i)?" AND ":"", $this->TD[TD_COLNAME0+$i*2], $update );//:Vx
                $update++;
            }

            $update = 0;//:V?
            $autosysdate = false;
            $req_update .= "UPDATE ".$this->TD[TD_NAME]." SET ";
            for( $i = 0; $i < $this->TD[TD_N]; $i++ )
            {
                if( $this->TD[ TD_COLNAME0+$i*2+1 ] == TD_DATE_I )
                {   //null
                }
                else if( $this->TD[ TD_COLNAME0+$i*2+1 ] == TD_DATE_U )
                {   //auto sysdate
                    $req_update .= sprintf( "%s%s=SYSDATE", (0<$update||$autosysdate)?",":"", $this->TD[ TD_COLNAME0+$i*2 ] );
                    $autosysdate = true;
                }
                else
                {   //request文構成
                    $req_update .= sprintf( "%s%s=:V%d", (0<$update||$autosysdate)?",":"", $this->TD[ TD_COLNAME0+$i*2 ], $update );
                    $update++;
                }
            }
            $req_update .= " $wherekeystr;";


            $update = 0;//:V?
            $autosysdate = false;
            $req_insert .= "INSERT INTO ".$this->TD[TD_NAME]."(".bh_CatColName($this->TD).")"." VALUES(";
            for( $i = 0; $i < $this->TD[TD_N]; $i++ )
            {   //request文構成
                switch( $this->TD[ TD_COLNAME0+$i*2+1 ] )
                {
                    case TD_DATE_I://auto sysdate
                    case TD_DATE_U:
                        $req_insert .= sprintf( "%sSYSDATE", (0<$update||$autosysdate)?",":"" );
                        $autosysdate = true;
                        break;

                    case TD_NOBIND:
                        $req_insert .= sprintf( "%s%s", (0<$update||$autosysdate)?",":"", $binddata[$i] );
                        $update++;
                        $autosysdate = true;
                        break;

                    default:
                        $req_insert .= sprintf( "%s:V%d", (0<$update)?",":"", $update );
                        $update++;
                        break;
                }
            }
            $req_insert .= ");";//おしまい


            $req = ""
                ." DECLARE"
                ."  TMP_NUM NUMBER(10);"
                ." BEGIN"
                ."  SELECT COUNT(*) INTO TMP_NUM FROM ".$this->TD[TD_NAME].$wherekeystr.";"
                ."  IF 0<TMP_NUM THEN"
                ."   $req_update"
                ."  ELSE"
                ."   $req_insert"
                ."  END IF;"
                ." END;";


bh_AddDebugBuf($req);
            $stmt = OCIParse( $bhc->con, $req );
            //バインド
            for( $i = 0; $i < $update; $i++ )
            {
                if( $this->TD[ TD_COLNAME0+$i*2+1 ] != TD_NOBIND )
                {
                    OCIBindByName( $stmt, ":V$i", $binddata[$i], -1 );
//                  $binddata[$i] = stripslashes($binddata[$i]);//余計なお世話
bh_AddDebugBuf("bind:V".$i."=".$binddata[$i]);
                }
            }

            @OCIExecute($stmt, ($autocommit)? OCI_COMMIT_ON_SUCCESS:OCI_DEFAULT );
            if( $err = OCIError($stmt) )
            {
                foreach( $err as $key=>$val )
                {
                    bh_AddDebugBuf("$key=>$val");
                }
                return false;
            }

            OCIFreeStatement($stmt);

            return true;
        }

        function GetValue( $x )
        {
            return $this->Value[$x];
        }


        function SetValue( $index, $value )
        {
            if( $this->Value[$index] != $value )
            {
                $this->Value[$index] = $value;
                $this->bUpdate[$index] = true;
            }
        }


        //丸ごと返す
        function GetValueAll()
        {
            return $this->Value;
        }
};


//###BindHelperList
class BindHelperList {
//##protected:
    var $rows;//array
    var $row_classname;//classname
    var $TD;//テーブル定義への参照

//##public:
        //コンストラクタ
    function BindHelperList( &$td, $row_classname=null )
    {
        $this->TD =& $td;//テーブル定義への参照もらう
        $this->row_classname = $row_classname;//arrayクラス名
        $this->$rows = array();
    }


    function Load( $binddata=null, $forupdate=false )
    {
        global $bhc;

        $update = 0;//:V?

        $colstr = bh_CatColName( $this->TD );//colm名ならべる

        $req = "SELECT $colstr FROM " . $this->TD[TD_NAME];

        if( $binddata != null )//pk指定あり
        {
            $req .= " WHERE ";
            for( $i = 0; $i < count($binddata); $i++ )
            {
                $req .= sprintf( "%s%s=:V%d", (0<$i)?" AND ":"", $this->TD[ TD_COLNAME0+$i*2 ], $update );//where列追加//:Vx
                $update++;
            }
        }

        $req .= " ORDER BY ";
        for( $i = 0; $i < $this->TD[TD_KEY]; $i++ )
        {
            $req .= sprintf( "%s%s ASC", (0<$i)?",":"", $this->TD[ TD_COLNAME0+$i*2 ] );//order列追加
        }

        //おしまい
        if( $forupdate )
        {
            $req .= " FOR UPDATE";
        }

bh_AddDebugBuf($req);
        $stmt = OCIParse( $bhc->con, $req );

            //バインド
            for( $i = 0; $i < $update; $i++ )
            {
                $buf = sprintf( ":V%d", $i );
                OCIBindByName( $stmt, $buf, $binddata[$i], -1 );
bh_AddDebugBuf("bind:V".$i."=".$binddata[$i]);
            }

        @OCIExecute( $stmt, ($forupdate)? OCI_DEFAULT:OCI_COMMIT_ON_SUCCESS );//$forupdateでロックするならコミットするな
        if( $err = OCIError($stmt) )
        {
            foreach( $err as $key=>$val )
            {
                bh_AddDebugBuf("$key=>$val");
            }
            return false;
        }

        $i = 0;
        while( OCIFetch($stmt) )
        {
            if( $this->row_classname == null )
            {   //子クラス名を指定しないと標準のBindhelperにTDを渡して生成するが
                //子クラスでextendしたメソッドは使えないしpkshadowの設定もできない
                $this->rows[$i] = new BindHelper($this->TD);
            }
            else
            {
                $this->rows[$i] = new $this->row_classname();
            }

            $this->rows[$i]->Fill( $stmt );//データfill
            $i++;
        }

        OCIFreeStatement($stmt);
        return true;
    }


    function Save( $autocommit=true )
    {
        for( $i = 0; $i < count($this->rows); $i++ )
        {
            if( !$this->rows[$i]->Save($autocommit) )
            {
                return false;
            }
        }
        return true;
    }
}

//###util
function bh_CatColName( &$td )
{
    $colstr = "";//colm名ならべる
    for( $i = 0; $i < $td[TD_N]; $i++ )
    {
        $colstr .= $td[ TD_COLNAME0+$i*2 ];
        if( $i+1 < $td[TD_N] )
        {
            $colstr .= ",";
        }
    }

    return $colstr;
}


function bh_Request($req,$autocommit=false)
{
    global $bhc;

bh_AddDebugBuf($req);
    $stmt = OCIParse( $bhc->con, $req );
    @OCIExecute($stmt, ($autocommit)? OCI_COMMIT_ON_SUCCESS:OCI_DEFAULT );
    if( $err = OCIError($stmt) )
    {
        foreach( $err as $key=>$val )
        {
            bh_AddDebugBuf("$key=>$val");
        }
        return false;
    }

    while( OCIFetchInto( $stmt, $row, OCI_NUM|OCI_RETURN_NULLS ) )
    {
        for( $i = 0; $i < count($row); $i++ )
        {
            $row[$i] = trim($row[$i]);
        }

        $ret[] = $row;
    }

    OCIFreeStatement($stmt);


    if( is_array($ret) )
    {
        return $ret;
    }
    else
    {
        return true;
    }
}


function bh_RequestBind($req,&$binddata,$autocommit=false)
{
    global $bhc;

bh_AddDebugBuf($req);
    $stmt = OCIParse( $bhc->con, $req );

    for( $i = 0; $i < count($binddata); $i++ )
    {
        OCIBindByName( $stmt, sprintf(":V%d",$i), $binddata[$i], 1024 );
//      OCIBindByName( $stmt, sprintf(":V%d",$i), $binddata[$i], -1, OCI_B_SQLT_NTY );
bh_AddDebugBuf("bind:V".$i."=".$binddata[$i]);
    }


    @OCIExecute($stmt, ($autocommit)? OCI_COMMIT_ON_SUCCESS:OCI_DEFAULT );
    if( $err = OCIError($stmt) )
    {
        foreach( $err as $key=>$val )
        {
            bh_AddDebugBuf("$key=>$val");
        }
        return false;
    }

    while( @OCIFetchInto( $stmt, $row, OCI_NUM|OCI_RETURN_NULLS ) )
    {
        for( $i = 0; $i < count($row); $i++ )
        {
            $row[$i] = trim($row[$i]);
        }
        $ret[] = $row;
    }

    OCIFreeStatement($stmt);


    if( is_array($ret) )
    {
        return $ret;
    }
    else
    {
        return true;
    }
}


function bh_Commit()
{
    global $bhc;
    return OCICommit( $bhc->con );
}


function bh_Rollback()
{
    global $bhc;
    return OCIRollback( $bhc->con );
}


//##debug
function bh_AddDebugBuf($req)
{
    global $debug_buf;
    $debug_buf .= htmlspecialchars($req);
    $debug_buf .= "<br>";
}

function bh_PrintDebugBuf( $mode=0 )
{
    global $debug_buf;

    if( mode == 0 )
    {
        echo $debug_buf;
    }
    else
    {
        echo "<!--";
        echo $debug_buf;
        echo ">";
    }
}

function bh_SetDebugBuf($req)
{
    global $debug_buf;
    $debug_buf = $req;
}

?>