[Из песочницы] Расширение Yii 1.1 oci8Pdo и бинд параметров CLOB/BLOB
В моей компании есть проект, который построен на Yii 1.1 и использует базу данных Oracle 9g. Для работы с базой используется расширение oci8Pdo.
Недавно появилась задача загрузки сканов в базу в поле BLOB. Т.к. автор расширения пишет:
The goal of this PDO class is to simulate 99% of the PDO functions that you use in an application.
то причин сомневаться в реализации данного функционала было мало.
Пробуем загрузить скан:
<?php
$doc_scan = file_get_contents($file);
$db = Yii::app()->dbOracle;
$stmt = $db->createCommand("update scan_document set DOCUM_SCAN=:doc_xml, DOC_SCAN=:doc_scan where DOCUM_ID=:docum_id");
$stmt->bindParam(':doc_xml', $doc_xml, PDO::PARAM_LOB);
$stmt->bindParam(':doc_scan', $doc_scan, PDO::PARAM_LOB);
$stmt->bindValue(':docum_id', $add->DOCUM_ID);
$stmt->query();
?>
Но не тут-то было: ORA-01465: invalid hex number.
Стали раскуривать проблему и наткнулись на реализацию метода bindParam в классе Oci8PDO_Statement вышеописанного расширения:
<?php
public function bindParam(
$parameter,
&$variable,
$data_type = PDO::PARAM_STR,
$length = -1,
$driver_options = null
) {
//Not checking for $data_type === PDO::PARAM_INT, because this gives problems when inserting/updating integers into a VARCHAR column.
if ($driver_options !== null) {
throw new PDOException('$driver_options is not implemented for Oci8PDO_Statement::bindParam()');
}
if (is_array($variable)) {
return oci_bind_array_by_name(
$this->_sth,
$parameter,
$variable,
count($variable),
$length
);
} else {
if ($length == -1) {
$length = strlen((string)$variable);
}
return oci_bind_by_name($this->_sth, $parameter, $variable, $length);
}
}
?>
Аргумент $data_type принимается, но нигде не обрабатывается. Так и получается что CLOB или BLOB записать у нас не выйдет. Деваться было некуда, пришлось допиливать oci8Pdo.
В класс Oci8PDO который наследуется от PDO добавили константы и метод вытаскивающий ресурс подключения к ДБ:
<?php
/**
* Ananalog constant OCI_B_CLOB
*
* @const int
*/
const PARAM_CLOB = 112;
/**
* Ananalog constant OCI_B_BLOB
*
* @const int
*/
const PARAM_BLOB = 113;
// ..............
/**
* Return the resource connection
*
* @return mixed
*/
public function getDbh() {
return $this->_dbh;
}
?>
И немного допилили метод bindParam в классе Oci8PDO_Statement:
<?php
public function bindParam(
$parameter,
&$variable,
$data_type = PDO::PARAM_STR,
$length = -1,
$driver_options = null
) {
// ................
if ($data_type == Oci8PDO::PARAM_BLOB) {
$clob = oci_new_descriptor($this->_pdoOci8->getDbh(), OCI_D_LOB);
$res = oci_bind_by_name($this->_sth, $parameter, $clob, -1, OCI_B_BLOB);
$clob->writeTemporary($variable, OCI_TEMP_BLOB);
return $res;
} else if ($data_type == Oci8PDO::PARAM_CLOB) {
$clob = oci_new_descriptor($this->_pdoOci8->getDbh(), OCI_D_LOB);
$res = oci_bind_by_name($this->_sth, $parameter, $clob, -1, OCI_B_CLOB);
$clob->writeTemporary($variable, OCI_TEMP_CLOB);
return $res;
}
else {
return oci_bind_by_name($this->_sth, $parameter, $variable, $length);
}
}
?>
Теперь обработка CLOB/BLOB проходит успешно:
<?php
$doc_scan = file_get_contents($file);
$db = Yii::app()->dbOracle;
$stmt = $db->createCommand("update scan_document set DOCUM_SCAN=:doc_xml, DOC_SCAN=:doc_scan where DOCUM_ID=:docum_id");
$stmt->bindParam(':doc_xml', $doc_xml, Oci8PDO::PARAM_CLOB); // Используем наши константы
$stmt->bindParam(':doc_scan', $doc_scan, Oci8PDO::PARAM_BLOB); // Используем наши константы
$stmt->bindValue(':docum_id', $add->DOCUM_ID);
$stmt->query();
?>
Итог:
Допил был организован в пулреквест и отправлен разработчику oci8Pdo yjeroen. Когда гуглилась проблема, было замечено много нерешенных вопросов на эту тему. Надеюсь, мой опыт кому-нибудь поможет.
P.S.: Буду рад критике и конструктивным замечаниям в комментах.