oci_bind_by_name

(PHP 5, PHP 7, PHP 8, PECL OCI8 >= 1.1.0)

oci_bind_by_nameПрикрепляет переменную PHP к соответствующей метке в SQL-выражении

Описание

oci_bind_by_name(
    resource $statement,
    string $param,
    mixed &$var,
    int $max_length = -1,
    int $type = 0
): bool

Прикрепляет переменную 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:

Возвращаемые значения

Возвращает 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]);
}
?>

Смотрите также

  • oci_bind_array_by_name() - Связывает PHP массив с массивом Oracle PL/SQL
  • oci_parse() - Подготавливает запрос к выполнению