PHP Webservice with PDO - php

I want to retreive some data from my mssql data base using the following code :
public function areaGetCities($json){
$request = json_decode($json);
$response = '';
$response->list = array();
if (!empty($request->language_id) &&
!empty($request->country_id) &&
!empty($request->postal_code)){
$this->db_stmt = new PDOStatement();
$this->db_stmt = $this->db->prepare('EXECUTE areaGetCities :language_id, :country_id, :postal_code');
$this->db_stmt->bindParam(':language_id', $request->language_id, PDO::PARAM_INT);
$this->db_stmt->bindParam(':country_id', $request->country_id, PDO::PARAM_INT);
$this->db_stmt->bindParam(':postal_code', $request->postal_code, PDO::PARAM_STR, 40);
$this->db_stmt->execute();
while ($obj = $this->db_stmt->fetchObject()){
$response->list[] = $obj;
unset($obj);
}
}
return json_encode($response);
}
if i print errorInfo() i get
Tried to bind parameter number 0. SQL Server supports a maximum of 2100 parameters.
IMSSP
-29
my problem is that i don't get any result from my database and i have to get (i ran the procedure with the same parameters and i get 2 result).
ideas ?
Edit : I edited my code. Now i get :
[Microsoft][SQL Server Native Client 11.0][SQL Server]The formal parameter "#postal_code" >was not declared as an OUTPUT parameter, but the actual parameter passed in requested >output.
CREATE PROCEDURE areaGetCities(#language_id TINYINT, #country_id INT, #postal_code VARCHAR(40))

Try using :param_name instead of #param_name both on queries and binds. This is recommended by the PDO manual.
Also, if the parameter is an output parameter, you should mark it as such:
$this->db_stmt->bindParam(':postal_code', $request->postal_code, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 40);
The param return will be at the $request->postal_code attribute.

Use :language_id instead of #language_id as it is also done in the PDO manual. The # is used for variables in SQL, so when you prepare
EXECUTE areaGetCities #language_id, #country_id, #postal_code
It's being interpreted as
EXECUTE areaGetCities NULL, NULL, NULL
Since the variables (most likely) aren't defined in SQL Server.

My problem was that postal_code contained numbers and i wanted to send it as a string because sometimes the numbers starts with 0. I changed the code to :
$this->db_stmt->bindParam(':postal_code', $request->postal_code);
and it worked like a charm.
Thanks a lot guys !

Related

A stored procedure that doesn't work in my PHP code but it works with a SQL Client

I'm working on a business monitor (a pannel that presents some metrics).
To get that data I do a sql request. By the way, I used a stored procedure.
My code is :
public function execErrorWarnLogs($id){
try {
$sql = "exec [BUSINESS_MONITOR_LOGS] #id='".$id."'";
$req = $this->_bdd->prepare($sql);
$req->execute();
$res = $req->fetchAll(PDO::FETCH_ASSOC);
$req->closeCursor();
return $res;
} catch (Exception $e) {
echo $e->getMessage();
}
}
When I'm trying to get some data indexed by $id, I get some troubles. I got an array that has null values... However, if I execute that stored procedure with an SQL client I get results.
Is that already happened to someone here ? Can someone explain me why I get that ?
If you want more information, please let me know.
Thanks.
Is $id an integer or a string?
Try using bound parameter instead. This is a example how it is working perfectly in my code:
public function execErrorWarnLogs($id){
try {
$sql = "exec BUSINESS_MONITOR_LOGS #id=:id";
$req = $this->_bdd->prepare($sql);
$req->execute([
'id' => $id
]);
$res = $req->fetchAll(PDO::FETCH_ASSOC);
$req->closeCursor();
return $res;
} catch (Exception $e) {
echo $e->getMessage();
}
}
You should use parameters for security reasons, too!
Two site notes:
If you do string interpolation you don't need to prepare the statement. Then you could just do:
$req = $this->_bdd->query($sql);
$res = $req->fetchAll(PDO::FETCH_ASSOC);
But the recommendet approach (for security) is to provide values as bound parameters and prepare the query.
As far as I know, you don't need $req->closeCursor() if you use Microsofts latest pdo driver for MSSQL. Whether you need closeCursor depends on the driver you use.
My stored procedure :
CREATE PROCEDURE BUSINESS_MONITOR #id VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
SELECT e.METRIC_NAME, e.METRIC_VALUE
FROM MONITOR_EVENTS e
WHERE e.MAIN_ID = #id
END
There are two more possible reasons:
Encoding issue
I would assume, that you have an encoding issue. If $id contains chars, that are out of the ascii-range, and you have another encoding, this could cause the query to fail. So check the encoding of $id and of you db connection
Whitespaces in $id
Maybe you have whitespaces in you id.

ADODB and SQL SERVER functions

I have a web service, that connects to my MSSQL data base using ADODB for PHP. Then, I created a simple function to return a string, just for tests, but, I'll implement with another logic.
Here is my SQL SERVER FUNCTION:
USE [myDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [myDB].[VerifyUser]
(
#user varchar(50),
#pass varchar(50)
)
RETURNS varchar(32)
AS
BEGIN
DECLARE #token varchar(32);
SET #token = 'Works!';
RETURN #token;
END
And, this is my web service method:
function wseAutenticacao($usuario, $senha) {
require_once('include/adodb5/adodb.inc.php');
$db = &ADONewConnection('mssqlnative');
$db->Connect(<host>, <user>, <pass>, <db_name>);
$stmt = $db->Prepare('VerifyUser');
$db->InParameter($stmt, $usuario, 'user');
$db->InParameter($stmt, $senha, 'pass');
$db->OutParameter($stmt, $token, 'token');
$result = $db->Execute($stmt);
return array('result' => $result);
}
I don't get the string "Works!" returned by my sql function. What is returned is a boolean with a false value, like I'm doing something wrong, but, I followed the ADODB Documentation example and now, I don't know what I have to do. I'm stuck!
If anyone would share your knowledge, I'll be greatful!
Thanks!
The #token local variable is only visible within the function itself. The result of the scalar function needs to be assigned to the output parameter in the executed script. One method you could try is a parameterized SELECT statement to assign the function return value to the output parameter:
$stmt = $db->Prepare('SELECT #token = VerifyUser(#user, #pass);');
I know the thread is old, but I'm looking for a solution for something similar. It seems that the current implementation of 'mssqlnative' does not support parameters at all. That is, the base class ADOConnection implements a mock metod Parameter which should be overridden in subclasses. ADODB_mssqlnative does not have an override of this method, hence, your parameters disappear into the void. Next thing, the current implementaton of _query() expects input parameters as array.
So, you could change your code to this to make it work
$db = &ADONewConnection('mssqlnative');
$db->Connect(<host>, <user>, <pass>, <db_name>);
$stmt = 'VerifyUser';
$params = array($usuario, $senha, $token);
$result = $db->Execute($stmt, $params);
return array('result' => $result);
Also, note that the params array is not associative. The current implementation of sqlsrv_query does not allow named parameters.

When using PDO::bindParam my query returns no results

I am doing the following query. If i put the numbers straight into the query the query returns lots of results... Now if I use the bindParam to pass the values there are no results.
I've tested whether or not the passed values have values and the echo shows that they do... so I have no idea why this is happening
Could anyone tell me what am i doing wrong?
public function searchWithPagination( $startPage = 0, $numberResultsPerPage = 10 ) {
$q = $this->db->prepare( 'SELECT * FROM ecm LIMIT :startpage, :numberresultsperpage' );
$q->bindParam(':startpage', $startPage);
$q->bindParam(':numberresultsperpage', $numberResultsPerPage);
$q->execute();
echo $numberResultsPerPage . $startPage ;
$f = $q->fetchAll();
var_dump($f);
}
EDIT: tried PDO::PARAM_INT still doesnt work
Try using bindValue instead of bindParam. In the user submitted notes in the PHP manual (php.net/manual/en/pdostatement.bindvalue.php) there is a note about bindParam passing by reference, whereas bindValue doesn't.
The limit parameters has to be bound as integers, the default binding is string.
$q->bindParam(':startpage', $startPage, PDO::PARAM_INT);
$q->bindParam(':numberresultsperpage', $numberResultsPerPage, PDO::PARAM_INT);
As it is stated in another question/answer:
How to apply bindValue method in LIMIT clause?
You need to bind the params explicitly as INT's, also you should cast them as integers.
$q->bindParam(':numberresultsperpage', (int)$numberResultsPerPage, PDO::PARAM_INT);
The problem is with your question.
In the code that actually runs, you are binding constant values
$q->bindParam(':startpage', 0);
$q->bindParam(':numberresultsperpage', 10);
which causes the error you mention:
Cannot pass parameter 2 by reference
But in the code you posted here you are binding variables
$q->bindParam(':startpage', $startPage);
$q->bindParam(':numberresultsperpage', $numberResultsPerPage);
which works all right, if fed with PDO::PARAM_INT or if emulation code is turned off.

PHP, PDO & MYSQL : lastInsertId() returns NULL

I am rather new at PDO-based MySQL and I'm running into a problem.
This is the method I'm executing :
public function insert( $table, $data )
{
// utility functions to auto-format the statements
$keys = $this->getKeys($data);
$placeholders = $this->getPlaceholders($data);
$q = "INSERT INTO $table ($keys) VALUES ($placeholders)";
// this simply returns a new PDO object
$dbh = $this->createSession();
$stmt = $dbh->prepare($q);
$stmt->execute( array_values($data) );
return $dbh->lastInsertId();
}
After that, I run my method and store the returned value in a variable :
$new_user_id = $U->insert( $data );
var_dump($new_user_id);
And I get
NULL
Note the query is actually executed, and my data is correctly inserted into my table; no problem on that side. It seems it just can't grab the last insert ID as I ask for it.
Thanks for your time.
Not sure about any PDO-specific issues, but by default MySQL only returns an insert id if there's an auto_increment integer field in the database (generally but not necessarily the primary key). If your table doesn't include this nothing is returned by $dbh->lastInsertId()
I've reviewed the code again and I found that my value wasn't returned because of an intermediate method that wasn't passing the value correctly to the top-layer method.
Checking the value at the source shows no problem.
Thanks for the replies anyway.

MySQLi prepared statements are silentely mangling my data. Is this expected behaviour?

My code currently uses mysqli::query and checks mysqli::$warning_count. When trying to insert text into an integer column, 0 gets inserted and a warning is generated. However, I'm just starting to learn prepared statements and for the same query, no warning is generated.
Here's an excerpt from my code:
$db_err_msg = 'Database Error: Failed to update profile';
$sql = "UPDATE tblProfiles SET intBookId = ? WHERE lngProfileId = ?";
$stmt = $mysqli->prepare($sql) or output_error($db_err_msg);
$book = 'CA';
$id = 10773;
$stmt->bind_param('ii', $book, $id) or output_error($db_err_msg);
$stmt->execute() or output_error($db_err_msg);
echo '$stmt->affected_rows is ', $stmt->affected_rows, "\n";
if ($mysqli->warning_count) {
$warnings = $stmt->get_warnings();
do {
trigger_error('Database Warning (' . $warnings->errno . '): '
. $warnings->message, E_USER_WARNING);
} while ( $warnings->next() );
}
else {
echo 'no warnings', "\n";
}
which produces the following output:
$stmt->affected_rows is 1
no warnings
Note that the intBookId column has a TINYINT data type. The same query generates a warning when using mysqli::query, but not when using prepared statements.
Enabling strict mode does not help. It will turn the warning into an error when using mysqli::query, but when using prepared statements the column is silently updated with a 0;
For the record, my application already does extensive validation before it gets to this point. But I wanted this extra validation as way to catch anything I might miss by mistake.
Note: It's beyond the scope of the current project to switch to PDO.
Why is this happening with MySQLi prepared statements? Is this expected behaviour?
Though it may not be immediately obvious, it is expected behaviour.
Because of this line of code:
$stmt->bind_param('ii', $book, $id) or output_error($db_err_msg);
PHP is casting the value to an integer before sending it to MySQL. MySQL receives the value 0 which is a valid value for the column and no warning is generated.
If you don't want PHP to do this, then you need to send it as a string. Like so:
$stmt->bind_param('si', $book, $id) or output_error($db_err_msg);
MySQL will receive the string 'CA' and generate a warning about it being an incorrect integer value.
Note: By sending everything as a string, MySQL will have to do a little more processing to convert the strings to integers, but it was already doing that anyway when the whole query was sent as a string using mysqli::query.
Note: A related problem occurs when using integers that are greater than PHP_INT_MAX. If you think an integer value will surpass that maximum (which is platform-dependent and only 2147483647 on 32-bit platforms) and precision is important, it's safer to send it as a string.

Categories