おそらく、PDO を直接使用するのではなく、アプリケーションの抽象化を使用する必要があります。抽象化レイヤーでは、実行したい任意の型変換を実行できます。
これが絶対に不可能で、 PDO を直接使用する必要がある場合は、サブクラス化と委譲を試して、必要な型キャストを実行する PDO に似たオブジェクトを生成できます。ただし、 null を予期する列がある場合、抽象化レイヤーが非常に複雑になります。データベースのイントロスペクションやその他のトリックが必要になる場合があります。おそらく、PDO API を正確に保存することはできません。
PDOStatement
委任
これは非常に簡単ですbindValue
。ただし、bindParam
は参照を使用しており、それらを書き換えずに型キャストすることはできないため、が呼び出されbindValue
たときにこれらを呼び出しに変換する回避策が必要execute
です。
最初にサブクラス化PDO
して、新しい wraped を返しますPDOStatement
。
class PDO_nullcast extends PDO {
public function prepare($statement, $driver_options=array()) {
$prepared = parent::prepare($statement, $driver_options);
$delegated_prepared = new PDOStatement_nullcast($prepared);
return $delegated_prepared;
}
}
PDOStatement_nullcast
次に、null キャスト セマンティクスを持つデリゲートを作成します。最初の試行はオーバーライドのみbindValue
です。
class PDOStatement_nullcast {
protected $pstmt;
protected $bindparams; // this is for later
function __construct(PDOStatement $pstmt) {
$this->pstmt = $pstmt;
$this->bindparams = array();
}
function __get($k) {
return $this->pstmt->{$k};
}
function __set($k, $v) {
$this->pstmt->{$k} = $v;
}
function __call($k, $a) {
return call_user_func_array(array($this->pstmt, $k), $a);
}
function bindValue($parameter, $value, $data_type=PDO::PARAM_STR) {
$newvalue = $this->castValue($value, $data_type);
return $this->pstmt->bindValue($parameter, $newvalue, $data_type);
}
static public function castValue($val, $typehint) {
$newval = $val;
if ($val===NULL) {
if ($typehint===PDO::PARAM_STR) {
$newval = '';
} else if ($typehint===PDO::PARAM_INT) {
$newval = 0;
} else if ($typehint===PDO::PARAM_BOOL) {
$newval = false;
}
} else {
if ($typehint===PDO::PARAM_STR) {
$newval = (string) $val;
} else if ($typehint===PDO::PARAM_INT) {
$newval = (int) $val;
} else if ($typehint===PDO::PARAM_BOOL) {
$newval = (bool) $val;
}
}
return $newval;
}
}
ここにいくつかのデモコードがあります。例として次の表を使用します。
CREATE TABLE `typetest` (
`intcol` int(11) NOT NULL,
`strcol` varchar(255) NOT NULL,
`intnullcol` int(11) DEFAULT NULL,
`intstrcol` varchar(255) DEFAULT NULL,
)
次に、PHP コードです。PDO_nullcast
に割り当てられたオブジェクトがあると仮定します$db
:
$sql = 'INSERT INTO typetest (`intcol`, `strcol`, `intnullcol`, `intstrcol`) VALUES (?,?,?,?)';
$insert = $db->prepare($sql);
$insert->bindValue(1, null, PDO::PARAM_INT);
$insert->bindValue(2, null, PDO::PARAM_STR);
$insert->bindValue(3, null, PDO::PARAM_INT);
$insert->bindValue(4, null, PDO::PARAM_STR);
$insert->execute();
$insert->closeCursor();
$select = $d->prepare('SELECT * FROM typetest');
$select->execute();
$res = $select->fetchAll();
$select->closeCursor();
var_dump($res);
castValue
関数を必要なセマンティクスに変更できます。
ただし、これはbindParam
ケースを処理しません。ここではexecute
、ラッパーで が呼び出されるまで参照を内部的に保持し、それらをbindValue
呼び出しに変換する必要があります。ただし、この方法のすべての使用を処理することはできませんbindParam
! 型キャストによって参照を保持できないため、INOUT パラメータの回避策はありません。
必要なものを取得するために、そのようにインターセプトbindParam
してexecute
呼び出すことができます (PDOStatement_nullcast
上記のクラスに次のメソッドを追加します)。
function bindParam($parameter, &$variable, $data_type=PDO::PARAM_STR, $length=null, $driver_options=null) {
if (isset($length) || isset($driver_options) || ($data_type & PDO::PARAM_INPUT_OUTPUT)) {
// in either of these cases, we cannot wrap!
return $this->pstmt->bindParam($parameter, $variable, $data_type, $length, $driver_options);
}
// note we preserve a reference to the variable
$this->bindparams[] = array($parameter, &$variable, $data_type);
return true; // this is a bit of a lie--we can't know if we would have an error until later.
}
function execute($input_parameters=null) {
if ($input_parameters!==null) {
return $this->pstmt->execute($input_parameters);
}
// for-loop is to preserve references more clearly
// foreach is trickier
for ($i=0; $i < count($this->bindparams); $i++) {
call_user_func_array(array($this,'bindValue'), $this->bindparams[$i]);
}
return $this->pstmt->execute();
}
そして、これを使用したテストコードがありますbindParam
:
$var = null;
$insert->bindParam(1, $var, PDO::PARAM_INT);
$insert->bindParam(2, $var, PDO::PARAM_STR);
$insert->bindParam(3, $var, PDO::PARAM_INT);
$insert->bindParam(4, $var, PDO::PARAM_STR);
error_log($var);
$insert->execute();
$var = 1;
$insert->execute();
$var = 2;
$insert->execute();