PHP with stored procedure - cursor output in Oracle - php

I've been trying to get the result from a stored procedure in Oracle using PHP. This stored procedure has some input parameters and one output cursor which give me the result.
Here is my stored procedure:
CREATE OR REPLACE PROCEDURE "TEST1" (
pPARAM1 CLOB,
pPARAM2 VARCHAR2,
p_record OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN p_record FOR
SELECT * FROM PARAMS
WHERE id_param = pPARAM1 AND id_param2 = pPARAM2;
END;
Here is my code:
$conexion = oci_connect($username, $password, $connection);
if (!$conexion) {
$e = oci_error();
echo $e['message'];
}
$params = array("pPARAM1" => 'apple', "pPARAM2" => 'banana');
$params_values = [];
$params_marks = [];
$marks = "";
$i = 0;
if(count($params) > 0) {
foreach ($params as $key => $value) {
$params_marks[$i] = ":" . $key;
$params_values[$key] = $value;
$i++;
}
$marks = implode(",", $params_marks);
$query = 'BEGIN TEST1(' . $marks . ',:cursbv); END;';
} else {
$query = 'BEGIN TEST1(:cursbv); END;';
}
$rs = oci_parse($conexion, $query) or die();
foreach ($params_values as $key => $value) {
oci_bind_by_name($rs, $key, $params_values[$key]);
}
$cursor = oci_new_cursor($conexion);
oci_bind_by_name($rs, ":cursbv", $cursor, -1, OCI_B_CURSOR) or die();
oci_execute($rs);
oci_execute($cursor);
if ($rs) {
while (($row = oci_fetch_array($cursor, OCI_ASSOC + OCI_RETURN_NULLS)) != false) {
var_dump($row);
}
}
oci_free_statement($rs);
oci_free_statement($cursor);
This give me a warning in oci_fetch_array but don't understand why because $cursor is a oci-resource. Need any suggestion. Thanks.
Note: My table have some CLOB columns and the stored procedure may have some CLOB input parameters.

If it's usefull for someone else: because of the use of CLOB columns and parameters I needed to add...
Binding variables:
$clob['apple'] = oci_new_descriptor($conexion, OCI_D_LOB);
oci_bind_by_name($rs, 'apple', $clob['apple'], -1, OCI_B_CLOB);
$clob['apple']->writeTemporary($params_values['apple']);
oci_bind_by_name($rs, 'banana', $params_values['banana']);
Fetching result:
while (($row = oci_fetch_array($cursor, OCI_ASSOC + OCI_RETURN_LOBS + OCI_RETURN_NULLS)) != false) {

Related

How to create multidimensional array with name of columns in PDO

I have this query and I would like to create multidimensional array. I tried this:
$columns = array("col1", "col2", "col3", "col4");
$query = "SELECT " . implode(",", $columns) . " FROM my_table";
$sql = $db->prepare($query);
$sql->execute();
$data = array();
while ($row = $stm->fetch()) {
$nestedData = array();
for ($i = 0, $m = count($columns); $i < $m; $i++) {
$value = $row[$columns[$i]];
$nestedData[] = empty($value) === false ? $value : "";
}
$data[] = $nestedData;
}
I get something like this:
[["value11","value12","value13","value14"],
["value21","value22","value23","value24"], etc]
I would like to have there also names of columns (like this):
[["col1":"value11","col2":"value12","col3":"value13","col4":"value14"],
["col1":"value21","col2":"value22","col3":"value23","col4":"value24"], etc]
Could anybody help me how to achieve this?
Have a look at "fetch_style" properties:
http://php.net/manual/en/pdostatement.fetch.php
And use
http://php.net/manual/en/pdostatement.fetchall.php
$columns = array("col1", "col2", "col3", "col4");
$query = "SELECT " . implode(",", $columns) . " FROM my_table";
$sql = $db->prepare($query);
$sql->execute();
$data = $sql->fetchAll(PDO::FETCH_ASSOC);
I believe what you're looking for is that PDO returns an associative array. This can easily be achieved by doing this:
$results = $stmt->fetch(PDO::FETCH_ASSOC);
or if you want this to be default for all your queries, you can create your connection that way:
$conn = new PDO($connStr, $usr, $pass, [
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
For more information or options, read the php manual: http://php.net/manual/en/pdostatement.fetch.php
Firstly, $results = $stmt->fetch(PDO::FETCH_ASSOC);
Then you need:
$nestedData[] = empty($value) === false ? $value : "";
needs to look like
$nestedData[$column_name] = empty($value) === false ? $value : "";
where $column_name is the name of the field. Look at the manual to work out how to get the field names that you need.
Something like this might work:
foreach ($row AS $key => $value)
{
$nestedData[$key] = empty($value) === false ? $value : "";
}
Instead for loop you can do foreach loop
foreach($array as $key=>$element){
$nesteddata[$key] = $element;
}

Executing pre-build-up array with PDO Execute

I am trying to pass through any query to a function using PDO.
I have build up the array through a loop function and try to insert it into the execute(array(....)) function, but it's not getting through.
FUNCTION CODE
public function ShowData($sql,$variable)
{
$execute_string = "";
echo "<pre>";
print_r($variable);
echo "</pre>";
$q = $this->conn->prepare($sql);
for($i = 0; $i < sizeof($variable); $i++)
{
if($i != 0) $execute_string .= ",";
$placeholder = $i + 1;
$execute_string .= "':$placeholder' => '".$variable[$i]."'";
}
echo $sql."<br>";
echo $execute_string;
$q->execute(array($execute_string));
echo "<br>Execute Succeeded";
return $row = $q->fetchAll();
}
VIEWPAGE CODE
$author = "Nemoza";
$name = "MBICO_mailer";
$project = $data->ShowData("SELECT * FROM mbico_projects WHERE author=:1 AND name=:2", array($author,$name));
OUTPUT FROM FUNCTION W/ DEBUGGING
Array
(
[0] => Nemoza
[1] => MBICO_mailer
)
SELECT * FROM mbico_projects WHERE author=:1 AND name=:2
':1' => 'Nemoza',':2' => 'MBICO_mailer'
However, the 'Execute Succeeded' text is not being printed, and the execute(array...)) is not actually executing.
What am I doing wrong, and how else should I do it?
here's an example you can use:
public function ShowData($sql,$variable) {
$bind = [];
foreach ($variable as $key => $value) {
$ph = $key+1;
$bind[":" . $ph] = $value;
}
$stmt = $this->conn->prepare($sql);
$stmt->execute($bind);
return $stmt->fetchAll();
}
it's used like this:
$sql = 'select * from users where username = :1 or username = :2';
$bind = ['123', '456'];
$db->ShowData($sql, $bind);
as mentioned in the comments to your question, you need to send an array to execute() function, and not a string.
Managed to do it like this:
public function ShowData($sql,$variable)
{
$execute_string = array();
$q = $this->conn->prepare($sql);
foreach($variable as $item)
{
$execute_string[] = $item;
}
$q->execute($execute_string);
return $q->fetchAll();
}
$project = $data->ShowData("SELECT * FROM mbico_projects WHERE author=? AND title=?", array($author, $title));

Modify generated json from PDO and PHP

I will be quick with more code less talk.
I have a mySQL DB and i want to generate json from it with PHP and PDO.
<?php
require("db.php");
$dsn = "mysql:host=localhost;dbname=$dbname";
$pdo = new PDO($dsn, $username, $password);
$rows = array();
$stmt = $pdo->prepare("SELECT * FROM table");
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo '{"jsons":';
echo json_encode($rows);
echo "}";
?>
The above code will return
{"jsons":[{"date_time":"2012-09-06 20:55:44","name":"theOne","message":"i am god","ip":"","device":"HTC","device_type":"Phone","class":"class1","id":"1"}]}
I want to modify the php code so my json output will be
{"jsons":[{"date_time":"2012-09-06 20:55:44","name":"theOne","message":"i am god","ip":"","devs":{"device":"HTC","device_type":"Phone","class":"class1","id":"1"}}]}
The difference is that i want to nest device,device_type,class and id into devs.
I couldn't do anything or find from google such a task.
Any help will be appreciate.
Thank you.
$push_in_devs = array("device","device_type","class","id") ;
$old_set = json_decode('{"jsons":[{"date_time":"2012-09-06 20:55:44","name":"theOne","message":"i am god","ip":"","device":"HTC","device_type":"Phone","class":"class1","id":"1"}]}') ;
$old_object = $old_set->jsons[0] ;
$new_set = new STDClass() ;
$new_set->jsons = array(0 => new STDClass) ;
$new_object = $new_set->jsons[0] ;
foreach($old_object as $name => $value){
if (!in_array(strtolower($name), $push_in_devs))
$new_object->$name = $value ;
}
$new_object->devs = array() ;
$dev = array() ;
foreach($push_in_devs as $name){
$dev[$name] = $old_object->$name ;
}
$new_object->devs[] = $dev ;
echo json_encode($new_set) ;
Run this block right after the $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); line:
foreach ($rows as $key => $row) {
foreach ($row as $column => value) {
if ($column == 'device' || $column == 'device_type' || $column == 'class' || $column == 'id') {
$row[$key]['devs'][$column] = $value;
} else {
$row[$key][$column] = $value;
}
}
}
This will nest the values underneath the 'devs' key for each row, before proceeding to encode to JSON.
$results = array();
foreach ($rows as $row) {
$result = array();
foreach ($row as $key => $value) {
if ($key === 'device' || $key === 'device_type'
|| $key === 'class' || $key === 'id') {
$result['devs'][] = $value;
} else {
$result[$key] = $value;
}
}
$results[] = $result;
}
json_encode($results);

Mysqli get_result alternative

I've just changed all my sql queries to prepared statements using mysqli. To speed this process up I created a function (called performQuery) which replaces mysql_query. It takes the query, the bindings (like "sdss") and the variables to pass in, this then does all the perpared statement stuff. This meant changing all my old code was easy. My function returns a mysqli_result object using mysqli get_result().
This meant I could change my old code from:
$query = "SELECT x FROM y WHERE z = $var";
$result = mysql_query($query);
while ($row = mysql_fetch_assoc($result)){
echo $row['x'];
}
to
$query = "SELECT x FROM y WHERE z = ?";
$result = performQuery($query,"s",$var);
while ($row = mysql_fetch_assoc($result)){
echo $row['x'];
}
This works fine on localhost, but my web hosting server does not have mysqlnd available, therefore get_result() does not work. Installing mysqlnd is not an option.
What is the best way to go from here? Can I create a function which replaces get_result(), and how?
Here is a neater solution based on the same principle as lx answer:
function get_result( $Statement ) {
$RESULT = array();
$Statement->store_result();
for ( $i = 0; $i < $Statement->num_rows; $i++ ) {
$Metadata = $Statement->result_metadata();
$PARAMS = array();
while ( $Field = $Metadata->fetch_field() ) {
$PARAMS[] = &$RESULT[ $i ][ $Field->name ];
}
call_user_func_array( array( $Statement, 'bind_result' ), $PARAMS );
$Statement->fetch();
}
return $RESULT;
}
With mysqlnd you would normally do:
$Statement = $Database->prepare( 'SELECT x FROM y WHERE z = ?' );
$Statement->bind_param( 's', $z );
$Statement->execute();
$Result = $Statement->get_result();
while ( $DATA = $Result->fetch_array() ) {
// Do stuff with the data
}
And without mysqlnd:
$Statement = $Database->prepare( 'SELECT x FROM y WHERE z = ?' );
$Statement->bind_param( 's', $z );
$Statement->execute();
$RESULT = get_result( $Statement );
while ( $DATA = array_shift( $RESULT ) ) {
// Do stuff with the data
}
So the usage and syntax are almost identical. The main difference is that the replacement function returns a result array, rather than a result object.
I encountered the same problem and solved it using the code provided in the answer of
What's wrong with mysqli::get_result?
My function looks like this now (error handling stripped out for clarity):
function db_bind_array($stmt, &$row)
{
$md = $stmt->result_metadata();
$params = array();
while($field = $md->fetch_field()) {
$params[] = &$row[$field->name];
}
return call_user_func_array(array($stmt, 'bind_result'), $params);
}
function db_query($db, $query, $types, $params)
{
$ret = FALSE;
$stmt = $db->prepare($query);
call_user_func_array(array($stmt,'bind_param'),
array_merge(array($types), $params));
$stmt->execute();
$result = array();
if (db_bind_array($stmt, $result) !== FALSE) {
$ret = array($stmt, $result);
}
$stmt->close();
return $ret;
}
Usage like this:
$userId = $_GET['uid'];
$sql = 'SELECT name, mail FROM users WHERE user_id = ?';
if (($qryRes = db_query($db, $sql, 'd', array(&$userId))) !== FALSE) {
$stmt = $qryRes[0];
$row = $qryRes[1];
while ($stmt->fetch()) {
echo '<p>Name: '.$row['name'].'<br>'
.'Mail: '.$row['mail'].'</p>';
}
$stmt->close();
}
I found the anonymous advice posted as a note at the API documentation page for mysqli_stmt::get_result very useful (I couldn't think of a better way than the eval trick), as we very often want to fetch_array() on our result. However, because I wanted to cater for a generic database object, I found it a problem that the code assumed numeric array was fine for all callsites, and I needed to cater for all callers using assoc arrays exclusively. I came up with this:
class IImysqli_result {
public $stmt, $ncols;
}
class DBObject {
function iimysqli_get_result($stmt) {
$metadata = $stmt->result_metadata();
$ret = new IImysqli_result;
if (!$ret || !$metadata) return NULL; //the latter because this gets called whether we are adding/updating as well as returning
$ret->ncols = $metadata->field_count;
$ret->stmt = $stmt;
$metadata->free_result();
return $ret;
}
//this mimics mysqli_fetch_array by returning a new row each time until exhausted
function iimysqli_result_fetch_array(&$result) {
$stmt = $result->stmt;
$stmt->store_result();
$resultkeys = array();
$thisName = "";
for ( $i = 0; $i < $stmt->num_rows; $i++ ) {
$metadata = $stmt->result_metadata();
while ( $field = $metadata->fetch_field() ) {
$thisName = $field->name;
$resultkeys[] = $thisName;
}
}
$ret = array();
$code = "return mysqli_stmt_bind_result(\$result->stmt ";
for ($i=0; $i<$result->ncols; $i++) {
$ret[$i] = NULL;
$theValue = $resultkeys[$i];
$code .= ", \$ret['$theValue']";
}
$code .= ");";
if (!eval($code)) {
return NULL;
}
// This should advance the "$stmt" cursor.
if (!mysqli_stmt_fetch($result->stmt)) {
return NULL;
}
// Return the array we built.
return $ret;
}
}

prepared statements - creating a single method*

FYI. ended up going with PDO solution as this was simpler.
I'm trying to add a single method to handle all queries to the database. I want the queries to use parameter binding. How do I handle a variable amount of function parameters in mysqli_stmt_bind_param()?
This post here led me to understand the pros of parameter binding.
Here is my example code..where I am currently stuck at is marked.
INPUT PARAMETERS
$query = "INSERT INTO b0 VALUES (?, ?, ?)"
$par_arr = {'bookmark', 'http://www.bookmark.com', 'tag'}
PROTOTYPE CODE
protected static function query($query, $par_arr)
{
if($statement=mysqli_prepare(one::$db, $query)
{
mysqli_stmt_bind_param($statement, "s", ...variable amount of parameters...);<----how should this be handled?
...
Update 2: If you experience any further problems with this code, then you should probably follow this advice and use PDO instead.
This is how you should be using call_user_func_array [docs]:
protected static function query($query, $types, $values) {
if($statement = mysqli_prepare(one::$db, $query) {
$parameters = array_merge(array($statement, $types), $values);
call_user_func_array('mysqli_stmt_bind_param', $parameters);
// ...
}
}
where $types is a string indicating the type of each value, as described in the mysqli_stmt_bind_param documentation (call_user_func_array is even mentioned there).
Update: It seems it is not that easy after all, and you have to create references to the values first:
foreach($values as $k => $v) {
$values[$k] = &$v;
}
$parameters = array_merge(array($statement, $types), $values);
call_user_func_array('mysqli_stmt_bind_param', $parameters);
// ...
call_user_func_array is for user defined functions per php.net
No it's not. The first parameter is of type callback, and the documentation says (emphasis mine):
A PHP function is passed by its name as a string. Any built-in or user-defined function can be used, except language constructs such as: array(), echo(), empty(), eval(), exit(), isset(), list(), print() or unset().
Next remark:
is just used to simplify syntax for passing arrays to user defined functions
Have you had a look at the examples? Each element of the array you pass to call_user_func_array will be passed as argument to the function you specify. Arrays are the only way to have a collection of values of variable size.
Because i find prepared statements boring, I am processing placeholders manually, and experience not a single problem of yours
private function prepareQuery($args)
{
$raw = $query = array_shift($args);
preg_match_all('~(\?[a-z?])~',$query,$m,PREG_OFFSET_CAPTURE);
$pholders = $m[1];
$count = 0;
foreach ($pholders as $i => $p)
{
if ($p[0] != '??')
{
$count++;
}
}
if ( $count != count($args) )
{
throw new E_DB_MySQL_parser("Number of args (".count($args).") doesn't match number of placeholders ($count) in [$raw]");
}
$shift = 0;
$qmarks = 0;
foreach ($pholders as $i => $p)
{
$pholder = $p[0];
$offset = $p[1] + $shift;
if ($pholder != '??')
{
$value = $args[$i-$qmarks];
}
switch ($pholder)
{
case '?n':
$value = $this->escapeIdent($value);
break;
case '?s':
$value = $this->escapeString($value);
break;
case '?i':
$value = $this->escapeInt($value);
break;
case '?a':
$value = $this->createIN($value);
break;
case '?u':
$value = $this->createSET($value);
break;
case '??':
$value = '?';
$qmarks++;
break;
default:
throw new E_DB_MySQL_parser("Unknown placeholder type ($pholder) in [$raw]");
}
$query = substr_replace($query,$value,$offset,2);
$shift+= strlen($value) - strlen($pholder);
}
$this->lastquery = $query;
return $query;
}
and thus an insert query can be called as simple as
$db->run("INSERT INTO table SET ?u",$data);
I have added the complete code to create a single method for select prepared statement and insert prepared statement, Please follow the instruction and read all the comments.
create database with the name 'test' and add the following query to create "users" table in the
CREATE TABLE IF NOT EXISTS `users` (
`users_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`first_name` varchar(100) NOT NULL,
`last_name` varchar(100) NOT NULL,
PRIMARY KEY (`users_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;
INSERT INTO `users` (`users_id`, `first_name`, `last_name`) VALUES
(1, 'daniel', 'martin'),
(2, 'daniel', 'martin');
<?php
error_reporting(E_ALL);
ini_set('display_errors',1);
session_start();
class mysqli_access extends mysqli{
private $ip1;
private $dbconn;
private $hostname = HST; // hostname
private $username = USR; // username
private $password = PWD; // password
private $dbname = DBN; // datbase name
function mysqli_access()
{
$ip= $_SERVER['REMOTE_ADDR'];
$ip1="ip_".str_replace('.', "", $ip);
if(!is_resource($_SESSION[$ip1]))
{
$this->dbconn = new mysqli($this->hostname,$this->username,$this->password,$this->dbname);
$_SESSION[$ip1] = $this->dbconn;
$dbconn = $this->dbconn;
if( $this->connect_error ) {
$this->Display_error('', $this->connect_errno, $this->connect_error, __FUNCTION__);
}
}
else {
$this->dbconn = $_SESSION[$ip1]; // success
}
return $this->dbconn;
}
function SelectPrepared($sql,$types,$params,$rows = '')
{
$results = array();
if ($stmt = $this->dbconn->prepare($sql)) {
if($types&&$params)
{
$bind_names[] = $types;
for ($i=0; $i<count($params);$i++)
{
$bind_name = 'bind' . $i;
$$bind_name = $params[$i];
$bind_names[] = &$$bind_name;
}
$return = call_user_func_array(array($stmt,'bind_param'),$bind_names);
}
$stmt->execute(); /* execute query */
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$var = $field->name;
$$var = null;
$fields_arr[$var] = &$$var;
}
call_user_func_array(array($stmt,'bind_result'),$fields_arr);
if($rows == 1){
while ($stmt->fetch()) {
$results = array();
foreach($fields_arr as $k => $v)
$results[$k] = $v;
}
}else{
$i = 0;
while ($stmt->fetch()) {
$results[$i] = array();
foreach($fields_arr as $k => $v)
$results[$i][$k] = $v;
$i++;
}
}
return $results;
}
}
public function InsertPrepared($tblName,$arrFieldNameValue,$replace_flag=0){
$TableName = $tblName;
if($replace_flag==0)
{
$sqlFirst ="INSERT INTO " . $TableName . "(";
}
if($replace_flag==1)
{
$sqlFirst ="INSERT IGNORE INTO " . $TableName . "(";
}
if($replace_flag==2)
{
$sqlFirst ="REPLACE INTO " . $TableName . "(";
}
$sqlSecond =" values(";
$params = array();
$types = '';
while(list($key,$value) = each($arrFieldNameValue))
{
$sqlFirst = $sqlFirst . $key . ",";
$sqlSecond = $sqlSecond . '?' . ",";
$params[] = $value;
$types = $types . $this->GetValType($value);
}
$sqlFirst = substr($sqlFirst,0,strlen($sqlFirst)-1) . ") ";
$sqlSecond = substr($sqlSecond,0,strlen($sqlSecond)-1) .")";
$sql = $sqlFirst . $sqlSecond;
if ($stmt = $this->dbconn->prepare($sql)) {
if($types&&$params)
{
$bind_names[] = $types;
for ($i=0; $i<count($params);$i++)
{
$bind_name = 'bind' . $i;
$$bind_name = $params[$i];
$bind_names[] = &$$bind_name;
}
$return = call_user_func_array(array($stmt,'bind_param'),$bind_names);
}
$stmt->execute(); /* execute query */
}
return mysqli_insert_id($this->dbconn);
}
private function GetValType($Item)
{
switch (gettype($Item)) {
case 'NULL':
case 'string':
return 's';
break;
case 'integer':
return 'i';
break;
case 'blob':
return 'b';
break;
case 'double':
return 'd';
break;
}
return 's';
}
}
class Model_NAME extends mysqli_access
{
function Model_NAME() {
$this->tablename = TABLENAME;
$this->mysqli_access();
}
##---------------------------- Custom function start from here -----------------#
## fetch settings values
function getUserRow($id,$key) {
$sql ="SELECT first_name,last_name FROM ".$this->tablename." WHERE first_name=? and users_id = ?";
$param = "si";
$array_of_params[] = addslashes($key);
$array_of_params[] = addslashes($id);
$result= $this->SelectPrepared($sql,$param,$array_of_params,1);
//last parameter 1 use if want fetch single row , other wise function will return multi dimensional array
return $result;
}
## fetch settings values
function getUserRows($last_name) {
$sql ="SELECT first_name,last_name FROM ".$this->tablename." WHERE last_name= ?";
$param = "s";
$array_of_params[] = addslashes($last_name);
$result= $this->SelectPrepared($sql,$param,$array_of_params);
//last parameter 1 use if want fetch single row , other wise function will return multi dimensional array
return $result;
}
function addValue($Array) {
return $this->InsertPrepared( $this->tablename , $Array);
}
}
// configuration
define('HST','localhost');
define('USR','root');
define('PWD','techmodi');
define('DBN','test');
define('TABLENAME','users');
$obj = new Model_NAME();
$arr = array();
$arr['first_name'] = addslashes("daniel");
$arr['last_name'] = addslashes("martin");
$obj->addValue($arr); // for insert records
// after inserting get the records
$singleRow = $obj->getUserRow(1,'daniel'); // for select single records
$multiRow =$obj->getUserRows('martin'); // for select records
echo '<pre>';
echo '<br/>-------- Single Records -----------------<br/>';
print_r($singleRow);
echo '<br/>-------- Multiple Records-----------------<br/>';
print_r($multiRow);
?>

Categories