(PHP 5, PHP 7, PHP 8, PECL OCI8 >= 1.1.0)
oci_bind_by_name — Прикрепляет переменную PHP к соответствующей метке в SQL-выражении
$statement
,$param
,&$var
,$max_length
= -1,$type
= 0
Прикрепляет переменную var
к метке
param
. Такое прикрепление позволяет повысить
производительность и избежать SQL-инъекций.
Прикрепление переменной позволяет базе данных повторно использовать кешированные контекстные выражения от предыдущих запросов, даже если они первоначально были запущены другим пользователем или процессом. Это также снижает риск SQL-инъекций, поскольку данные в таком случае никогда не рассматриваются как инструкции SQL. Данные не требуется экранировать или заключать в кавычки.
Прикреплённые PHP-переменные могут быть изменены и снова выполнены без необходимости повторной обработки запроса или повторного прикрепления.
В Oracle, прикрепление переменных обычно разделяют на
IN
(прикрепляет значения, передаваемые в
базу данных) и OUT
(прикрепляет значения,
возвращаемые PHP). Переменная может быть одновременно
IN
и OUT
. Независимо от
этого, характер прикрепления переменных будет определён во
время выполнения.
Необходимо указать max_length
при использовании
OUT
-привязки, что позволяет PHP зарезервировать
больше памяти для хранения возвращаемого значения
Для IN
-привязки рекомендуется также указать
параметр max_length
, если выражение
выполняется несколько раз с различными значениями PHP-переменной.
В противном случае, Oracle может урезать размер данных до размера
первоначального значения переменной PHP. Если максимальная длина
значения неизвестна, рекомендуется вызывать
oci_bind_by_name() перед каждым вызовом
oci_execute().
Прикрепление неоправданно большой переменной повлияет на
процесс сохранения базы данных.
Вид прикрепления указывает Oracle как работать с памятью при
чтении данных. Для IN
-прикрепления адрес в памяти должен
содержать допустимые данные при вызове
oci_execute(). Это значит, что значение переменной
должно находиться в памяти во время исполнения. Если это не так,
возможны некорректные результаты или ошибки наподобие
"ORA-01460: unimplemented or unreasonable conversion requested"
(запрошены невыполнимые или некорректные преобразования)
Для OUT
-прикрепления основным признаком
является установка значения в переменную PHP.
Для многократно выполняемого выражения, привязка одних и тех же значений может уменьшить возможности оптимизатора Oracle по выработке наилучшего варианта выполнения инструкции. Длительное прикрепление выражений, которые редко исполняются, может также не принести пользы. Тем не менее, в обоих случаях, прикрепление является более безопасным, чем конкатенация строки запроса и непроверенных пользовательских данных.
statement
Допустимый идентификатор выражения OCI8.
param
Метка с префиксом в виде двоеточия, используемая в выражении.
Двоеточие опционально в param
.
Oracle не использует знак вопроса для меток.
var
Переменная PHP, ассоциированная с param
max_length
Устанавливает максимальный размер данных. Если указать -1,
функция будет использовать текущий размер переменной
var
в качестве максимального.
При этом переменная var
должна
существовать и содержать данные во время вызова
oci_bind_by_name().
type
Тип данных, к которому Oracle будет приводить значения.
По умолчанию type
имеет значение
SQLT_CHR
. Oracle приводит данные от
данного типа к типу поля (или типу переменной PL/SQL), если
это возможно.
Если необходимо прикрепить переменную абстрактного типа
(LOB/ROWID/BFILE), следует предварительно использовать
oci_new_descriptor(). Параметр
length
не используется для абстрактных типов
и должен быть установлен в -1.
Допустимые значения параметра type
:
SQLT_BFILEE
или OCI_B_BFILE
- для BFILE-объектов;
SQLT_CFILEE
или OCI_B_CFILEE
- для CFILE-объектов;
SQLT_CLOB
или OCI_B_CLOB
- для CLOB-объектов;
SQLT_BLOB
или OCI_B_BLOB
- для BLOB-объектов;
SQLT_RDD
или OCI_B_ROWID
- для ROWID-объектов;
SQLT_NTY
или OCI_B_NTY
- для именованных типов даты;
SQLT_INT
или OCI_B_INT
- для целых чисел;
SQLT_CHR
- для символов VARCHAR;
SQLT_BIN
или OCI_B_BIN
- для RAW-полей;
SQLT_LNG
- для LONG-полей;
SQLT_LBI
- для LONG RAW полей;
SQLT_RSET
- для курсоров, созданных
функцией oci_new_cursor();
SQLT_BOL
или OCI_B_BOL
- для PL/SQL BOOLEAN (Требуется Oracle Database 12c)
Возвращает true
в случае успешного выполнения или false
в случае возникновения ошибки.
Пример #1 Добавление данных с использованием oci_bind_by_name()
<?php
// Создание таблицы:
// CREATE TABLE mytab (id NUMBER, text VARCHAR2(40));
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$stid = oci_parse($conn,"INSERT INTO mytab (id, text) VALUES(:id_bv, :text_bv)");
$id = 1;
$text = "Data to insert ";
oci_bind_by_name($stid, ":id_bv", $id);
oci_bind_by_name($stid, ":text_bv", $text);
oci_execute($stid);
// В таблице содержится: 1, 'Data to insert '
?>
Пример #2 Одна привязка для многократного использования
<?php
// Создание таблицы:
// CREATE TABLE mytab (id NUMBER);
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$a = array(1,3,5,7,11); // данные для вставки
$stid = oci_parse($conn, 'INSERT INTO mytab (id) VALUES (:bv)');
oci_bind_by_name($stid, ':bv', $v, 20);
foreach ($a as $v) {
$r = oci_execute($stid, OCI_DEFAULT); // не использовать автоматическое завершение транзакции
}
oci_commit($conn); // завершение транзакции
// Таблица содержит пять записей: 1, 3, 5, 7, 11
oci_free_statement($stid);
oci_close($conn);
?>
Пример #3 Прикрепление в цикле foreach
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$sql = 'SELECT * FROM departments WHERE department_name = :dname AND location_id = :loc';
$stid = oci_parse($conn, $sql);
$ba = array(':dname' => 'IT Support', ':loc' => 1700);
foreach ($ba as $key => $val) {
// oci_bind_by_name($stid, $key, $val) не работает,
// потому что прикрепляет каждое значение в одно место: $val
// Вместо этого следует указывать конкретное место: $ba[$key]
oci_bind_by_name($stid, $key, $ba[$key]);
}
oci_execute($stid);
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS);
foreach ($row as $item) {
print $item."<br>\n";
}
oci_free_statement($stid);
oci_close($conn);
?>
Пример #4 Прикрепление в выражение WHERE
<?php
$conn = oci_connect("hr", "hrpwd", "localhost/XE");
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$sql = 'SELECT last_name FROM employees WHERE department_id = :didbv ORDER BY last_name';
$stid = oci_parse($conn, $sql);
$didbv = 60;
oci_bind_by_name($stid, ':didbv', $didbv);
oci_execute($stid);
while (($row = oci_fetch_array($stid, OCI_ASSOC)) != false) {
echo $row['LAST_NAME'] ."<br>\n";
}
// Выводом будет
// Austin
// Ernst
// Hunold
// Lorentz
// Pataballa
oci_free_statement($stid);
oci_close($conn);
?>
Пример #5 Прикрепление в выражение LIKE
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
// Поиск всех городов, начинающихся на 'South'
$stid = oci_parse($conn, "SELECT city FROM locations WHERE city LIKE :bv");
$city = 'South%'; // '%' - это знак шаблона SQL
oci_bind_by_name($stid, ":bv", $city);
oci_execute($stid);
oci_fetch_all($stid, $res);
foreach ($res['CITY'] as $c) {
print $c . "<br>\n";
}
// Выводом будет:
// South Brunswick
// South San Francisco
// Southlake
oci_free_statement($stid);
oci_close($conn);
?>
Пример #6 Прикрепление в выражение REGEXP_LIKE
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
// Поиск названий городов, содержащих 'ing'
$stid = oci_parse($conn, "SELECT city FROM locations WHERE REGEXP_LIKE(city, :bv)");
$city = '.*ing.*';
oci_bind_by_name($stid, ":bv", $city);
oci_execute($stid);
oci_fetch_all($stid, $res);
foreach ($res['CITY'] as $c) {
print $c . "<br>\n";
}
// Выводом будет:
// Beijing
// Singapore
oci_free_statement($stid);
oci_close($conn);
?>
Для небольшого, фиксированного количества условий в выражении IN, используются индивидуальные имена переменных. Неизвестные значения при исполнении могут быть установлены в NULL. Это позволяет использовать одно выражение нескольким пользователям, что повышает эффективность кеширования Oracle DB.
Пример #7 Прикрепление нескольких значений в выражение IN
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$sql = 'SELECT last_name FROM employees WHERE employee_id in (:e1, :e2, :e3)';
$stid = oci_parse($conn, $sql);
$mye1 = 103;
$mye2 = 104;
$mye3 = NULL; // притворимся, что не получили это значение
oci_bind_by_name($stid, ':e1', $mye1);
oci_bind_by_name($stid, ':e2', $mye2);
oci_bind_by_name($stid, ':e3', $mye3);
oci_execute($stid);
oci_fetch_all($stid, $res);
foreach ($res['LAST_NAME'] as $name) {
print $name ."<br>\n";
}
// Выводом будет:
// Ernst
// Hunold
oci_free_statement($stid);
oci_close($conn);
?>
Пример #8 Прикрепление ROWID, возвращаемое запросом
<?php
// Создадим и наполним таблицу:
// CREATE TABLE mytab (id NUMBER, salary NUMBER, name VARCHAR2(40));
// INSERT INTO mytab (id, salary, name) VALUES (1, 100, 'Chris');
// COMMIT;
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$stid = oci_parse($conn, 'SELECT ROWID, name FROM mytab WHERE id = :id_bv FOR UPDATE');
$id = 1;
oci_bind_by_name($stid, ':id_bv', $id);
oci_execute($stid);
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS);
$rid = $row['ROWID'];
$name = $row['NAME'];
// Переведём имя в верхний регистр и зафиксируем изменения
$name = strtoupper($name);
$stid = oci_parse($conn, 'UPDATE mytab SET name = :n_bv WHERE ROWID = :r_bv');
oci_bind_by_name($stid, ':n_bv', $name);
oci_bind_by_name($stid, ':r_bv', $rid, -1, OCI_B_ROWID);
oci_execute($stid);
// Теперь таблица содержит: 1, 100, CHRIS
oci_free_statement($stid);
oci_close($conn);
?>
Пример #9 OUT-прикрепление ROWID, возвращаемое при INSERT
<?php
// В данном примере добавляется запись с идентификатором и именем,
// после чего увеличивается заработная плата
// Создание таблицы:
// CREATE TABLE mytab (id NUMBER, salary NUMBER, name VARCHAR2(40));
//
// На основе собственных ROWID на примере thies[at]thieso.net (980221)
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}
$sql = "INSERT INTO mytab (id, name) VALUES(:id_bv, :name_bv)
RETURNING ROWID INTO :rid";
$ins_stid = oci_parse($conn, $sql);
$rowid = oci_new_descriptor($conn, OCI_D_ROWID);
oci_bind_by_name($ins_stid, ":id_bv", $id, 10);
oci_bind_by_name($ins_stid, ":name_bv", $name, 32);
oci_bind_by_name($ins_stid, ":rid", $rowid, -1, OCI_B_ROWID);
$sql = "UPDATE mytab SET salary = :salary WHERE ROWID = :rid";
$upd_stid = oci_parse($conn, $sql);
oci_bind_by_name($upd_stid, ":rid", $rowid, -1, OCI_B_ROWID);
oci_bind_by_name($upd_stid, ":salary", $salary, 32);
// идентификаторы и имена для вставки
$data = array(1111 => "Larry",
2222 => "Bill",
3333 => "Jim");
// Заработная плата для каждого сотрудника
$salary = 10000;
// Вставка и немедленное обновление каждой строки
foreach ($data as $id => $name) {
oci_execute($ins_stid);
oci_execute($upd_stid);
}
$rowid->free();
oci_free_statement($upd_stid);
oci_free_statement($ins_stid);
// Показать новые записи
$stid = oci_parse($conn, "SELECT * FROM mytab");
oci_execute($stid);
while ($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) {
var_dump($row);
}
oci_free_statement($stid);
oci_close($conn);
?>
Пример #10 Прикрепление для хранимой функции PL/SQL
<?php
// Перед запуском PHP-сценария, создайте хранимую функцию в
// SQL*Plus или SQL Developer:
//
// CREATE OR REPLACE FUNCTION myfunc(p IN NUMBER) RETURN NUMBER AS
// BEGIN
// RETURN p * 3;
// END;
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message']), E_USER_ERROR);
}
$p = 8;
$stid = oci_parse($conn, 'begin :r := myfunc(:p); end;');
oci_bind_by_name($stid, ':p', $p);
// Возвращаемое значение OUT-прикреплено. По умолчаннию типом данных будет строка.
// Прикрепление со значением 40 означает, что будет возвращено 40 символов.
oci_bind_by_name($stid, ':r', $r, 40);
oci_execute($stid);
print "$r\n"; // выведет 24
oci_free_statement($stid);
oci_close($conn);
?>
Пример #11 Прикрепление параметров для PL/SQL хранимой процедуры
<?php
// Перед запуском PHP-сценария, создайте хранимую процедуру в
// SQL*Plus или SQL Developer:
//
// CREATE OR REPLACE PROCEDURE myproc(p1 IN NUMBER, p2 OUT NUMBER) AS
// BEGIN
// p2 := p1 * 2;
// END;
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message']), E_USER_ERROR);
}
$p1 = 8;
$stid = oci_parse($conn, 'begin myproc(:p1, :p2); end;');
oci_bind_by_name($stid, ':p1', $p1);
// Второй параметр процедуры OUT-прикреплён. По умолчаннию типом данных будет строка.
// Прикрепление со значением 40 означает, что будет возвращено 40 символов.
oci_bind_by_name($stid, ':p2', $p2, 40);
oci_execute($stid);
print "$p2\n"; // выведет 16
oci_free_statement($stid);
oci_close($conn);
?>
Пример #12 Прикрепление CLOB-объекта
<?php
// Перед запуском создаём таблицу:
// CREATE TABLE mytab (mykey NUMBER, myclob CLOB);
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message']), E_USER_ERROR);
}
$mykey = 12343; // произвольный ключ для примера
$sql = "INSERT INTO mytab (mykey, myclob)
VALUES (:mykey, EMPTY_CLOB())
RETURNING myclob INTO :myclob";
$stid = oci_parse($conn, $sql);
$clob = oci_new_descriptor($conn, OCI_D_LOB);
oci_bind_by_name($stid, ":mykey", $mykey, 5);
oci_bind_by_name($stid, ":myclob", $clob, -1, OCI_B_CLOB);
oci_execute($stid, OCI_DEFAULT);
$clob->save("A very long string");
oci_commit($conn);
// Получение CLOB-данных
$query = 'SELECT myclob FROM mytab WHERE mykey = :mykey';
$stid = oci_parse ($conn, $query);
oci_bind_by_name($stid, ":mykey", $mykey, 5);
oci_execute($stid);
print '<table border="1">';
while ($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_LOBS)) {
print '<tr><td>'.$row['MYCLOB'].'</td></tr>';
// В цикле, очищение больших переменных перед повторным получением данных, уменьшает пиковое потребление памяти PHP
unset($row);
}
print '</table>';
?>
Пример #13 Прикрепление PL/SQL BOOLEAN
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message']), E_USER_ERROR);
}
$plsql =
"begin
:output1 := true;
:output2 := false;
end;";
$s = oci_parse($c, $plsql);
oci_bind_by_name($s, ':output1', $output1, -1, OCI_B_BOL);
oci_bind_by_name($s, ':output2', $output2, -1, OCI_B_BOL);
oci_execute($s);
var_dump($output1); // true
var_dump($output2); // false
?>
Не используйте addslashes() одновременно с oci_bind_by_name(), так как кавычек быть не должно. Все указанные кавычки будут записаны в базу данных, потому что oci_bind_by_name() вставляет данные дословно и не удаляет кавычки или символы экранирования.
Замечание:
Если прикрепляется строка к
CHAR
-полю в выраженииWHERE
, помните, что Oracle использует при сравнении значенияCHAR
, дополненные пробелами. Переменная PHP должна быть дополнена пробелами до того же размера, что и поле, чтобы выражениеWHERE
выполнялось верно.
Замечание:
Переменная PHP
var
является ссылкой. Некоторые виды циклов могут работать не так, как ожидается:<?php
foreach ($myarray as $key => $value) {
oci_bind_by_name($stid, $key, $value);
}
?>В этом случае каждый ключ прикрепляется к $value, поэтому все прикреплённые переменные указывают на значение в последней итерации цикла. Вместо этого следует использовать:
<?php
foreach ($myarray as $key => $value) {
oci_bind_by_name($stid, $key, $myarray[$key]);
}
?>