oci_bind_by_name

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

oci_bind_by_nameAssocie une variable PHP à un marqueur Oracle

Description

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

Lie une variable PHP var au marqueur Oracle param. Le fait de lier une variable est important en terme de performance de la base de données Oracle, mais aussi en terme de sécurité relative aux injections SQL.

Le liage permet aux bases de données de réutiliser le contexte d'exécution de la requête ainsi que le cache associé, y compris lorsqu'un autre utilisateur ou processus l'exécute. Le liage réduit le risque d'injection SQL car les données associées à une variable liée ne sont jamais traitées comme faisant partie de la requête SQL. Inutile donc d'ajouter des quotes ou d'échapper ces données.

Les variables PHP liées peuvent changées et la requête ré-exécutée sans avoir besoin d'analyser de nouveau la requête ou de lier de nouveau les variables.

Avec Oracle, les variables liées sont habituellement divisées en lien IN pour les valeurs passées à la base de données, et en lien OUT pour les valeurs à retourner à PHP. Une variable liée peut être à la fois en lien IN et OUT. Dans ce cas, le fait de savoir si la variable liée doit être utilisée pour l'entrée ou la sortie sera déterminé au moment de l'exécution.

Vous devez spécifier le paramètre max_length lors de l'utilisation du lien OUT afin que PHP alloue assez de mémoire pour contenir la valeur de retour.

Pour les liens IN, il est recommandé de définir le paramètre max_length si la requête est exécutée plusieurs fois avec des valeurs différentes pour les variables PHP. Sinon, Oracle peut tronquer les données à la longueur de la valeur initiale de la variable PHP. Si vous ne connaissez pas la valeur maximale dont vous avez besoin, alors appelez de nouveau la fonction oci_bind_by_name() avec les données courantes, avant chaque appel à la fonction oci_execute(). Le fait de l'associer une taille supérieure à ce dont vous avez besoin a un impact sur la mémoire associée au processus pour la base de données.

Un appel à la fonctionnalité d'association de variables donne à Oracle l'adresse mémoire à utiliser pour lire les données. Pour les liens IN, cette adresse doit contenir des données valides lors de l'appel à la fonction oci_execute(). Ceci signifie que la variable liée doit rester dans le contexte jusqu'à l'exécution. Si ce n'est pas le cas, des résultats non attendus peuvent survenir, ainsi que des erreurs de type "ORA-01460: unimplemented or unreasonable conversion requested". Pour les liens OUT, un symptôme peut être qu'aucune valeur n'a été définie dans la variable PHP.

Pour une requête qui est exécutée à plusieurs reprises, les valeurs liées qui ne changent jamais peuvent réduire la capacité de l'optimisateur d'Oracle à choisir le meilleur plan d'exécution. Pour les requêtes prenant du temps, qui sont rarement appelées plusieurs fois, l'association de variables n'apporte aucun bénéfice. Cependant, dans les 2 cas, l'association est plus sécurisée que de placer directement les chaînes de caractères dans la requête SQL, sachant qu'il y a un risque de mauvais filtrage de l'entrée utilisateur.

Liste de paramètres

statement

Un identifiant de requête OCI8 valide.

param

Le marqueur, préfixé par une virgule, utilisé dans la requête. La virgule est optionnelle dans le paramètre param.

var

La variable PHP à associer avec le marqueur du paramètre param.

max_length

Spécifie la taille maximale pour les données. Si la valeur est -1, la fonction utilisera la longueur courante des données du paramètre var pour définir la longueur maximale. Dans ce cas, le paramètre var doit exister et contenir des données lors de l'appel à la fonction oci_bind_by_name().

type

Le type de données à utiliser par Oracle pour traiter les données. Par défaut, vaut SQLT_CHR. Oracle convertira les données entre ce type et la colonne de la base de données (ou des variables de type PL/SQL), lorsque c'est possible.

Si vous devez lier des types abstraits de données (LOB/ROWID/BFILE), vous devrez l'allouer dans un premier temps, avec oci_new_descriptor(). La longueur length ne sert pas pour ces types et devrait être fixée à -1.

Les valeurs possibles pour le paramètre type sont :

  • SQLT_FILE ou OCI_B_BFILE - pour les BFILEs ;

  • SQLT_CFILE ou OCI_B_CFILEE - pour les CFILEs ;

  • SQLT_CLOB ou OCI_B_CLOB - pour les CLOBs ;

  • SQLT_BLOB ou OCI_B_BLOB - pour les BLOBs ;

  • SQLT_RDD ou OCI_B_ROWID - pour les ROWIDs ;

  • SQLT_NTY ou OCI_B_NTY - pour les types de données nommés ;

  • SQLT_INT ou OCI_B_INT - pour les entiers ;

  • SQLT_CHR - pour les VARCHAR ;

  • SQLT_BIN ou OCI_B_BIN - pour les colonnes RAW ;

  • SQLT_LNG - pour les colonnes LONG ;

  • SQLT_LBI - pour les colonnes LONG RAW ;

  • SQLT_RSET - pour les curseurs créés avec la fonction oci_new_cursor();

  • SQLT_BOL ou OCI_B_BOL - pour les booléens (requiert Oracle Database 12c)

Valeurs de retour

Cette fonction retourne true en cas de succès ou false si une erreur survient.

Exemples

Exemple #1 Insertion de données avec la fonction oci_bind_by_name()

<?php

// Création de la table avec :
// 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 = "Données à insérer ";
oci_bind_by_name($stid, ":id_bv", $id);
oci_bind_by_name($stid, ":text_bv", $text);
oci_execute($stid);

// La table contient maintenant : 1, 'Données à insérer

?>

Exemple #2 Une association avec de multiples appels

<?php

// Création de la table avec :
// 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); // Données à insérer

$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); // Ne pas valider automatiquement
}
oci_commit($conn); // Une seule validation

// La table contient maintenant 5 lignes : 1, 3, 5, 7, 11

oci_free_statement($stid);
oci_close($conn);

?>

Exemple #3 Association avec une boucle 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) ne fonctionne pas car
// elle associe chaque marqueur au même endroit : $val
// au lieu d'utiliser l'endroit actuel de la donnée $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);

?>

Exemple #4 Association dans une clause 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";
}

// Affiche :
// Austin
// Ernst
// Hunold
// Lorentz
// Pataballa

oci_free_statement($stid);
oci_close($conn);

?>

Exemple #5 Association avec une clause LIKE

<?php

$conn
= oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}

// Trouve tous les critères qui commencent par 'South'
$stid = oci_parse($conn, "SELECT city FROM locations WHERE city LIKE :bv");
$city = 'South%'; // '%' is a wildcard in 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";
}
// Affiche :
// South Brunswick
// South San Francisco
// Southlake

oci_free_statement($stid);
oci_close($conn);

?>

Exemple #6 Association avec REGEXP_LIKE

<?php

$conn
= oci_connect('hr', 'welcome', 'localhost/XE');
if (!
$conn) {
$m = oci_error();
trigger_error(htmlentities($m['message']), E_USER_ERROR);
}

// Trouve tous les critères qui contiennent '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";
}
// Affiche :
// Beijing
// Singapore

oci_free_statement($stid);
oci_close($conn);

?>

Pour quelques conditions utilisées dans les clauses IN, utilisez des variables liées individuelles. Les valeurs inconnues au moment de l'exécution pourront être définies à NULL. Ceci permet d'utiliser une seule requête pour l'ensemble des utilisateurs de l'application, maximisant ainsi l'efficacité du cache Oracle DB.

Exemple #7 Association de plusieurs valeurs dans une clause 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; // pretend we were not given this value
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";
}

// Affiche :
// Ernst
// Hunold

oci_free_statement($stid);
oci_close($conn);

?>

Exemple #8 Association d'un ROWID retourné par une requête

<?php

// Création de la table avec :
// 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'];

// Modification du nom en lettre majuscule & sauvegarde de cette modification
$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);

// La table contient maintenant 1, 100, CHRIS

oci_free_statement($stid);
oci_close($conn);

?>

Exemple #9 Association d'un ROWID dans un INSERT

<?php

// Cet exemple insère un id & un nom, puis, met à jour le salaire
// Création de la table avec :
// CREATE TABLE mytab (id NUMBER, salary NUMBER, name VARCHAR2(40));
//
// Basé sur l'exemple original de ROWID, fourni par thies at thieso dot 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);

// ids et noms à insérer
$data = array(1111 => "Larry",
2222 => "Bill",
3333 => "Jim");

// Salaire de chaque personne
$salary = 10000;

// Insertion et mise à jour immédiate de chaque ligne
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);

// Affichage des nouvelles lignes
$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);

?>

Exemple #10 Association pour une fonction stockée PL/SQL

<?php

// Avant d'exécuter le programme PHP, une fonction stockée doit être créée
// au format SQL*Plus ou 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);

// La valeur retournée est un lien OUT. Le type par défaut devra être
// de type string, aussi, l'association sur une longueur de 40 signifie
// que la chaîne retournée fera au maximum 40 caractères.
oci_bind_by_name($stid, ':r', $r, 40);

oci_execute($stid);

print
"$r\n"; // Affiche 24

oci_free_statement($stid);
oci_close($conn);

?>

Exemple #11 Association de paramètres à une procédure stockée PL/SQL

<?php

// Avant d'exécuter le programme PHP, une procédure stockée doit être créée
// au format SQL*Plus ou 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);

// Le second paramètre de la procédure stockée est un lien OUT.
// Le type par défaut devra être de type string, aussi,
// l'association sur une longueur de 40 signifie
// que la chaîne retournée fera au maximum 40 caractères.

oci_bind_by_name($stid, ':p2', $p2, 40);

oci_execute($stid);

print
"$p2\n"; // Affiche 16

oci_free_statement($stid);
oci_close($conn);

?>

Exemple #12 Association sur une colonne CLOB

<?php

// Avant l'exécution, il est nécessaire de créer la table :
// 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; // Clé arbitraire pour l'exemple ;

$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);

// Récupération des données 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>';
// Dans une boucle, le fait de libérer la variable avant la récupèration de
// la ligne suivante réduit l'utilisation mémoire de PHP
unset($row);
}
print
'</table>';

?>

Exemple #13 Association sur un booléen dans un script PL/SQL

<?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

?>

Notes

Avertissement

N'utilisez pas la fonction addslashes() et oci_bind_by_name() simultanément, car aucun ajout de guillemets n'est nécessaire. Si c'est le cas, alors les guillemets ajoutés seront écrits dans la base de données ; en effet, la fonction oci_bind_by_name() insère les données brutes et ne supprime ni les guillemets ajoutés, ni les caractères d'échappement.

Note:

Si vous associez une chaîne de caractères à une colonne de type CHAR dans une clause WHERE, souvenez-vous qu'Oracle utilise une comparaison en ajoutant des caractères vides pour les colonnes de type CHAR. Aussi, votre variable PHP doit être compléter par des caractères vides afin d'atteindre la même taille que la colonne pour que la clause WHERE fonctionne.

Note:

L'argument PHP var est une référence. Aussi, quelques formats de boucle peuvent ne pas fonctionner comme prévu :

<?php
foreach ($myarray as $key => $value) {
oci_bind_by_name($stid, $key, $value);
}
?>

Ceci associe chaque clé à la valeur pointée par $value, aussi, toutes les variables associées pointent vers la valeur de la dernière itération de la boucle. A la place, utilisez ceci :

<?php
foreach ($myarray as $key => $value) {
oci_bind_by_name($stid, $key, $myarray[$key]);
}
?>

Voir aussi

add a note add a note

User Contributed Notes 18 notes

up
6
abiyi2000 at yahoo dot com
13 years ago
I unfortunately spent the whole day trying to make this work as part of OCI bind_by_name insert:

<?php
     
if(is_numeric($v2)){
       
oci_bind_by_name($stmth, $bvar, $v2,  -1, OCI_B_INT);
      }else{
       
$v2 = (string) $v2;
       
oci_bind_by_name($stmth, $bvar, $v2, -1, SQLT_CHR);
      }
?>

The string field is always inserting correctly w/o any truncation. The string field is a  varchar2(160) CHAR, but the data used to populate it is 40 chars in length.

The numeric part is of Type Number in the database which is being used to store unix time (10 digit seconds since 1970/01/01.

The problem, the insert was truncating to 9 digits with some bogus value not even related to the input i.e., it's not just a matter of dropping the leftmost or rightmost digit, it'll just insert a 9 digit bogus number.

The only way I was able to resolve this for the numeric field was to set the maxlength to 8 (not 10 which is the number of digits in the input):

<?php
     
if(is_numeric($v2)){
       
oci_bind_by_name($stmth, $bvar, $v28, OCI_B_INT);
      }else{
       
$v2 = (string) $v2;
       
oci_bind_by_name($stmth, $bvar, $v2, -1, SQLT_CHR);
      }
?>

Hopefully you'll see this soon before you expend a lot of time repeating the same problem I had.
up
8
martin dot abbrent at ufz dot de
8 years ago
Example #7 only shows the binding of a small fixed number of values in an IN clause. There is also a way to bind multiple conditions with a variable number of values.

<?php
$ids
= array(
   
103,
   
104
);

$conn         = oci_pconnect($user, $pass, $tns);
// Using ORACLE table() function to get the ids from the subquery
$sql          = 'SELECT * FROM employees WHERE employee_id IN (SELECT column_value FROM table(:ids))';
$stmt         = oci_parse($conn, $sql);
// Create collection of numbers. Build in type for strings is ODCIVARCHAR2LIST, but you can also create own types.
$idCollection = oci_new_collection($conn, 'ODCINUMBERLIST', 'SYS');

// Maximum length of collections of type ODCINUMBERLIST is 32767, maybe you should check that!
foreach ($ids as $id) {
   
$idCollection->append($id);
}

oci_bind_by_name($stmt, ':ids', $idCollection, -1, SQLT_NTY);
oci_execute($stmt, OCI_DEFAULT);
oci_fetch_all($stmt, $return);
oci_free_statement($stmt);

oci_close($conn);

?>
up
1
avenger at php dot net
15 years ago
Dont forget the 5th parameter: $type. It's will slowly your code some times. Eg:

<?php
$sql
= "select * from (select * from b xxx) where rownum < :rnum";
$stmt = OCIParse($conn,$sql);
OCIBindByName($stmt, ":rnum", $NUM, -1);
OCIExecute($stmt);
?>

Below code was slow 5~6 time than not use bind value.Change the 3rd line to:

<?php
OCIBindByName
($stmt, ":rnum", $NUM, -1, SQLT_INT);
?>

will resloved this problem.

This issue is also in the ADODB DB class(adodb.sf.net), you will be careful for use the SelectLimit method.
up
2
splintyg at gmail dot com
7 years ago
Guys, i've been looking for long time, how to pass clob to and get from procedure
CREATE OR REPLACE PROCEDURE myproc(p1 IN clob, p2 OUT clob);

Here You are an answer:

<?php
        $conn
= oci_connect("TEST", "html", "//hostname", "UTF8");

   
$filename = "./clob.txt";
   
$handle = fopen($filename, "r");
   
$f = fread($handle, filesize($filename));
   
fclose($handle);

   
$stid = oci_parse($conn, "begin myproc(:p1, :p2); end;");
   
$p1 = oci_new_descriptor($conn, OCI_D_LOB);
   
$p2 = oci_new_descriptor($conn, OCI_D_LOB);
   
   
oci_bind_by_name($stid, ":p1", $p1, -1, OCI_B_CLOB);
   
oci_bind_by_name($stid, ":p2", $p2, -1, OCI_B_CLOB);
   
$p1->writeTemporary($f, OCI_TEMP_BLOB);
   
oci_execute($stid); -- Figure out OCI_NO_AUTO_COMMIT
    oci_commit
($conn);
    echo
$p2->load();
   
   
$p1    ->close();
   
$p2    ->close();
   
oci_free_statement($stid);
   
oci_close($conn);
?>

And perfect book about "PHP and Oracle"
http://www.oracle.com/technetwork/topics/php/underground-php-oracle-manual-098250.html
up
0
dub357 at gmail dot com
10 months ago
The note about the PHP var argument being a reference and some kinds of loops not working is very important here.  However, you can make a foreach loop work if you create a temporary variable, use that in the bind and then unset it.  For example:

<?php
foreach ($myarray as $key => $val)  {
   
$value = $val;
   
oci_bind_by_name($stid, $key, $value);
    unset(
$value);
}
?>

This binds each key to the location of $value, but when you unset it after binding, it can be set and used again.

https://www.php.net/manual/en/function.unset.php
up
0
charles dot fisher at arconic dot com
3 years ago
I am trying to rework ADOdb library calls to OCI, and I wrote this function today which is helping.

function OraQry(&$Results, $Query, $Binds = false) {
  global $xdb;

  $Results = oci_parse($xdb, $Query);

  if($Binds) foreach($Binds as $BindNm => $BindValJunk)
    oci_bind_by_name($Results, $BindNm, $Binds[$BindNm], -1);

  oci_execute($Results, OCI_NO_AUTO_COMMIT);

  return null;
}

This also has similarity to PDO in passing an array of bind variables, with the added benefit that if they are named numerically (starting at zero), then the call to the array() function can be omitted:

OraQry($rs,
  'select status from all_tables where owner=:0 and table_name=:1',
    [$owner, $table_name]);

while($arr = oci_fetch_assoc($rs)) echo $arr['STATUS'] . "\n";
up
0
asui dot dev dot null at gmail dot com
5 years ago
If you are getting "ORA-01722: invalid number error" while inserting/updating a FLOAT value into a NUMBER column, please check the correctness of a binded value format according to the current locale settings.

Default "american" locale assumes that value send to oracle will be a dot decimal separator (just like 4127.5). But with setlocale('pl_PL.UTF-8') your float number would be represented as 4127,5 and that form will be used while sending data do oracle causing a problem...
That was my case (8 hours of debugging).

You can check your current locale with setlocale(LC_ALL, 0).

What I can recommend as a solutions:
a) do not set locale, or set it to 'C' for a time of sending data;
b) convert float to a string format compatible with current oracle session NLS_NUMERIC_CHARACTERS parameter value.
For example: when NLS_NUMERIC_CHARACTERS = '.,' float value 4127.5 should be converted to '4127.5'.  Then oracle will catch it correctly even if current locale are set differently.
up
0
splintyg at gmail dot com
7 years ago
Guys, i've been looking for long time, how to pass clob to and get from procedure
CREATE OR REPLACE PROCEDURE myproc(p1 IN clob, p2 OUT clob);

Here You are an answer:

<?php
        $conn
= oci_connect("TEST", "html", "//hostname", "UTF8");

   
$filename = "./clob.txt";
   
$handle = fopen($filename, "r");
   
$f = fread($handle, filesize($filename));
   
fclose($handle);

   
$stid = oci_parse($conn, "begin myproc(:p1, :p2); end;");
   
$p1 = oci_new_descriptor($conn, OCI_D_LOB);
   
$p2 = oci_new_descriptor($conn, OCI_D_LOB);
   
   
oci_bind_by_name($stid, ":p1", $p1, -1, OCI_B_CLOB);
   
oci_bind_by_name($stid, ":p2", $p2, -1, OCI_B_CLOB);
   
$p1->writeTemporary($f, OCI_TEMP_BLOB);
   
oci_execute($stid); -- Figure out OCI_NO_AUTO_COMMIT
    oci_commit
($conn);
    echo
$p2->load();
   
   
$p1    ->close();
   
$p2    ->close();
   
oci_free_statement($stid);
   
oci_close($conn);
?>

And perfect book about "PHP and Oracle"
http://www.oracle.com/technetwork/topics/php/underground-php-oracle-manual-098250.html
up
0
Anonymous
7 years ago
Bear in mind that you cannot use reserved words for bind variables. Otherwise you'll get ORA-01745: Invalid host/bind variable name error.
up
0
marki at trash-mail dot com
8 years ago
Please note that in my earlier note about having oci_bind_by_name() in a function, this becomes a little more complicated when returning values like "UPDATE table SET bla='blubb' RETURNING id INTO :id".

You can do it as follows:

<?php
function sql($q, &$vars_in=array(), &$vars_out=array()) {
  ...
 
$stid = oci_parse($conn, $q);
  ...
 
reset($vars_in);
  do {
    if (
current($vars_in)===FALSE) {
      break;
    }
   
$b = oci_bind_by_name($stid, key($vars_in), current($vars_in));
   
// insert exception handling here
 
} while (each($vars_in) !== FALSE);

 
// VARS TO RETURN
  // we'll fix this to integer type because for now we need this for index IDs
 
foreach ($vars_out as $k => $v) {
   
$b = oci_bind_by_name($stid, $k, $vars_out[$k], -1, SQLT_INT);
   
// insert exception handling here
 
}

  ...
}
?>

Use like this:

<?php
$blubb
= 'blubb';
$b = array(':bla' => $blubb);
$b_out = array(':id' => ''); // leave value empty
$x = sql($q, $b, $b_out);
$id = $b_out[':id'];
?>

(The point is: you would not be able to return anything into $b[':bla'] because $b[':bla'] becomes current($vars_in) inside sql() and cannot be written to.)
up
0
marki at trash-mail dot com
8 years ago
I had a query that was working properly at first sight, no errors on execute, nothing, but there were simply no results returned at runtime.

Be careful when putting the database commands into a function and binding your variables there while using oci_fetch_xxx() outside the function.

function sql($conn, $stmt, $var) {
  $stid = oci_parse($conn, $stmt);
  ...
  oci_bind_by_name($stid, ':val', $var);
  ...
}
sql($conn, $q, $var);
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS);

As you see from the definition of oci_bind_by_name(), $var needs to be passed as reference, so your function has to have this reference ready like this:

function sql($conn, $stmt, &$var) {
  $stid = oci_parse($conn, $stmt);
  ...
  oci_bind_by_name($stid, ':val', $var);
  ...
}

The background is that if you don't pass by reference (in which case $var inside the function is a copy of $var outside the function), then oci_bind_by_name() will work just fine at first glance.
However, since the oci_fetch statements that you use to actually get the data will reference the $var that has ceased to exist when the function finished. In fact, since the varbind seems to be a pointer, that pointer will point to an invalid location at that point and your variables won't be substitued in the SQL.

All this also means that:

1) You have to pass a variable, and not just a value

This doesn't work:

$stid = sql($conn, $q, array('bla'=>'blubb'));

This is better:

$vars = array('bla'=>'blubb');
$stid = sql($conn, $q, $vars);

2) Even when passing as reference to your helper function you cannot use e.g. foreach:

This doesn't work:

function sql($conn, $q, $vars) {
  ...
  foreach ($vars as $k => $v) {
    oci_bind_by_name($stid, $k, $v);
  }
  ...
}

Again, because $k and $v are local variables that will have disappeared once you perform an oci_fetch outside the function.

Instead you have to work the array in a more low-level way like this:

function sql($conn, $q, &$vars) {
  ...
  $stid = oci_parse($conn, $q);
  ...
  reset($vars);
  do {
    if (current($vars)===FALSE) { // end of array
      break;
    }
    $b = oci_bind_by_name($stid, key($vars), current($vars));
    if ($b === FALSE) {
      DIE('Could not bind var');
    }
  } while (each($vars) !== FALSE);
}
$binds = array(':bla1' => 'blubb1',
               ':bla2' => 'blubb2');
$stid = sql($conn, $q, $binds);
$row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS);

Wherever you oci_bind_by_name(), the pointer to the initial data has to exist from beginning to end.
up
0
xorinox at gmx dot ch
10 years ago
Working with Oracle and raw types in and out worked like the following for me.

<?php
 
/*oracle procedure
  procedure open_session(
    i_instance_id in raw,
    o_session_id out raw,
    o_errcode out number,
    o_errmsg out varchar2
  ); 
  */

  //open database
 
$conn = DBOpen( DB_DEV_USER );

 
//get session id
 
$sql = "begin p_loader.open_session( hextoraw( :instance_id ), :session_id, :errcode, :errmsg ); end;";
 
$stmt = oci_parse( $conn, $sql );
 
$instanceId = DB_INSTANCE_ID;
 
oci_bind_by_name( $stmt, ":instance_id", $instanceId, 1, SQLT_CHR );
 
oci_bind_by_name( $stmt, ":session_id", $sessionId, 16, SQLT_BIN );
 
oci_bind_by_name( $stmt, ":errcode", $errcode, 12, SQLT_INT );
 
oci_bind_by_name( $stmt, ":errmsg", $errmsg, 4000, SQLT_CHR );
 
 
oci_execute( $stmt );
 
$sessionId = bin2hex( $sessionId ); //now this is a hex string
 
  //close database
 
DBClose( $conn );
?>
up
0
ajitsingh4u at gmail dot com
16 years ago
//Calling Oracle Stored Procedure
//I assume that you have a users table and three columns in users table i.e. id, user, email in oracle
// For example I made connection in constructor, you can modify as per your requirement.
//http://www.devshed.com/c/a/PHP/Understanding-Destructors-in-PHP-5/1/
<?php
class Users{
    private
$connection;
   
    public function
__construct()
    {
       
$this->connection = oci_connect("scott", "tiger", $db); // Establishes a connection to the Oracle server;
   
}

    public function
selectUsers($start_index=1, $numbers_of_rows=20)
    {
       
$sql ="BEGIN sp_users_select(:p_start_index, :p_numbers_of_rows, :p_cursor, :p_result); END;";
       
$stmt = oci_parse($this->connection, $sql);

       
//Bind in parameter
       
oci_bind_by_name($stmt, ':p_start_index', $start_index, 20);
       
oci_bind_by_name($stmt, ':p_numbers_of_rows', $numbers_of_rows, 20);

       
//Bind out parameter
       
oci_bind_by_name($stmt, ':p_result', $result, 20); // returns 0 if stored procedure succeessfully executed.

        //Bind Cursor
       
$p_cursor = oci_new_cursor($this->connection);
       
oci_bind_by_name($stmt, ':p_cursor', $p_cursor, -1, OCI_B_CURSOR);

       
// Execute Statement
       
oci_execute($stmt);
       
oci_execute($p_cursor, OCI_DEFAULT);

       
oci_fetch_all($p_cursor, $cursor, null, null, OCI_FETCHSTATEMENT_BY_ROW);

        echo
$result;
        echo
'<br>';
       
var_dump($cursor); // $cursor is an associative array so we can use print_r() to print this data.
        // you can return data from this function to use it at your user interface.
   
}

    public function
deleteUser($id)
    {
       
$sql ="BEGIN sp_user_delete(:p_id, :p_result); END;";
       
$stmt = oci_parse($this->connection, $sql);

       
// bind in and out variables
       
oci_bind_by_name($stmt, ':p_id', $id, 20);
       
oci_bind_by_name($stmt, ':p_result', $result, 20);

       
//Execute the statement
       
$check = oci_execute($stmt);

        if(
$check == true)
       
$commit = oci_commit($this->connection);
        else
       
$commit = oci_rollback($this->connection);

        return
$result;
    }
   
   
// You can make function for insert ,update using above two functions

}
?>
up
0
Chris Delcamp
17 years ago
This is an example of returning the primary key from an insert so that you can do inserts on other tables with foreign keys based on that value.  The date is just used to provied semi-unique data to be inserted.

$conn = oci_connect("username", "password")
$stmt = oci_parse($conn, "INSERT INTO test (test_msg) values (:data) RETURN test_id INTO :RV");
$data = date("d-M-Y H:i:s");
oci_bind_by_name($stmt, ":RV", $rv, -1, SQLT_INT);
oci_bind_by_name($stmt, ":data", $data, 24);
oci_execute($stmt);
print $rv;
up
-1
Anonymous
17 years ago
This is what the old OCI_B_* constants are now called:
(PHP 5.1.6 win32)

OCI_B_NTY - SQLT_NTY
OCI_B_BFILE - SQLT_BFILEE
OCI_B_CFILEE - SQLT_CFILEE
OCI_B_CLOB - SQLT_CLOB
OCI_B_BLOB - SQLT_BLOB
OCI_B_ROWID - SQLT_RDD
OCI_B_CURSOR - SQLT_RSET
OCI_B_BIN - SQLT_BIN
OCI_B_INT - SQLT_INT
OCI_B_NUM - SQLT_NUM
up
-2
hfuecks at nospam dot org
19 years ago
Note that there have been some changes on the constant identifiers and the documentation is currently not entirely accurate.

Running the following script;

<?php
foreach (array_keys(get_defined_constants()) as $const) {
    if (
preg_match('/^OCI_B_/', $const) ) {
        print
"$const\n";
    }
}
?>

Under PHP 4.4.0 I get;

OCI_B_SQLT_NTY < renamed to OCI_B_NTY with PHP5
OCI_B_BFILE
OCI_B_CFILEE
OCI_B_CLOB
OCI_B_BLOB
OCI_B_ROWID
OCI_B_CURSOR
OCI_B_BIN

Under PHP 5.0.4 I get;

OCI_B_NTY
OCI_B_BFILE < docs are wrong right now
OCI_B_CFILEE < docs are wrong right now
OCI_B_CLOB
OCI_B_BLOB
OCI_B_ROWID
OCI_B_CURSOR
OCI_B_BIN < it's a mystery
up
-1
adrian dot crossley at hesa dot ac dot uk
15 years ago
Sometimes you get the error "ORA-01461: can bind a LONG value only for insert into a LONG column".  This error is highly misleading especially when you have no LONG columns or LONG values.

From my testing it seems this error can be caused when the value of a bound variable exceeds the length allocated.

To avoid this error make sure you specify lengths when binding varchars e.g.
<?php
oci_bind_by_name
($stmt,':string',$string, 256);
?>

And for numerics use the default length (-1) but tell oracle its an integer e.g.
<?php
oci_bind_by_name
($stmt,':num',$num, -1, SQLT_INT);
?>
up
-1
jjeffman at cpovo.net
10 years ago
It is very important to set up the maxlength of the returning parameter (:r), even when it is returning a number, otherwise the ORA-01460 exception (unimplemented or unreasonable conversion requested) may be raised.
To Top