tags:

views:

72

answers:

3

I am trying to understand the difference between mysqli's query() and prepare(). I have code like below, and I would like to get the same results from both. However the prepare() does not work as expected. why?

  // this works

  if ($query = $this->db->query("SELECT html FROM static_pages WHERE page = 'cities'")) {

     $result = $query->fetch_row();
     echo $result[0];

  $query->close();

  }

  //this does not work
  //result is empty

     $cities = 'cities';

     $stmt = $this->db->prepare("SELECT html FROM static_pages WHERE page = ?");
     $stmt -> bind_param("s", $cities);
     $stmt->execute();
     $stmt->bind_result($result);
     $stmt->fetch();
     echo $result;
     $stmt->close();

     $this->db->close();

these are my server configs as requested:

OS

Vista 64bit / PHP Version 5.2.9

mysqli

MysqlI Support enabled

Client API library version 5.0.51a

Client API header version 5.0.51a

MYSQLI_SOCKET /tmp/mysql.sock

Directive Local Value Master Value

mysqli.default_host no value no value

mysqli.default_port 3306 3306

mysqli.default_pw no value no value

mysqli.default_socket no value no value

mysqli.default_user no value no value

mysqli.max_links Unlimited Unlimited

mysqli.reconnect Off Off

A: 

Should be echo $result; not echo $results;

Mchl
Whoops, that is an error, but not the reason why this does not work. I just fixed it in the live code and it still returns empty.
Mike
@Mike for the reason you have to ask your program, not people who have no idea what's going on. I don't have a PHP interpreter in my head to run your code and see the result. You have to do it yourself. No offence - it's just the only way.
Col. Shrapnel
well the fact that query() works and prepare() does not, despite the fact that they have the same exact data should tell you something. Are those two functions not interchangeable, in the sense of the code I have above.
Mike
@Mike they ARE interchangeable. Just two different ways of passing data to the query. Both just querying a database.
Col. Shrapnel
Thats what I thought, as @a1ex07 said, the key to the prepare() appears to be store_results();
Mike
+2  A: 

Can you try $stmt->store_result(); between $stmt->execute(); and $stmt->bind_result($result); ?

a1ex07
THAT DOES THE TRICK. Now on to studying, store_results().Thanks a lot. I am new to the mysqli, trying to wrap my head around it.
Mike
Check for more details: http://us3.php.net/manual/en/mysqli-stmt.store-result.php .Btw, if it helps, I would be happy to get a '+'
a1ex07
On a side note, it does make you wonder why bind_result was not suffice.
Mike
@Mike: It should suffice. It's "only" the difference between buffered and unbuffered queries. http://us3.php.net/manual/en/mysqli-stmt.store-result.php says: "You must call mysqli_stmt_store_result() for every query that successfully produces a result set (SELECT, SHOW, DESCRIBE, EXPLAIN), _and only if you want to buffer the complete result set by the client_, so that the subsequent mysqli_stmt_fetch() call returns buffered data."
VolkerK
Very nice. And to continue the lesson here is a nice post on buffered vs unbuffered http://www.sitepoint.com/forums/showthread.php?t=471650PS. I would provide a + but I need 15 reps for that.
Mike
Ok, one more thing to clarify. As the author of this post says. http://us3.php.net/manual/en/mysqli-stmt.store-result.php#86670 You should use store_result EVERY TIME your query returns a result set. If anyone has difference of opinion it would be interesting to know why.
Mike
@Mike: Try my example script. You only need to change the login credentials and database name. It doesn't use store_result and yet it prints `htmlCities` -at least on my winxp/php5.3.3 system. Maybe it depends on the mysqli/api version. Or the driver (libmysql vs mysqlnd). Or the fact that the script uses a temporary table. I doubt that very much. Does it work on your machine? There is a significant difference when using store_results: The whole (multi-)result set is transferred to the client, thus you can immediately run another query; there can be only one "open" (multi-)resultset at a time.
VolkerK
@VolkerK, your code is identical to mine. I also tried to output an error as @Col. Shrapnel mentioned.If that code works on your machine, that is very strange. However I think the thing to know about prepare() is that you have to use store_result() to make sure the query is buffered. Otherwise you will continue on to bind_results() without your result set. As mentioned in the API and above this is only necessary for (SELECT, SHOW, DESCRIBE, EXPLAIN) queries.
Mike
@Mike: Can you please edit your question and include the mysqli section of the output of phpinfo() and the mysql server version and your operating system? VolkerK's script worked fine on my gentoo vm using php 5.2.14 and mysql 5.0.90.
chendral
@chendral mysqli, OS and php version have been added.
Mike
@Mike. I suggest that it depends on mysql client library version... At least you are not the only one who has this issue (Another can be found in user comments http://us3.php.net/manual/en/mysqli-stmt.store-result.php ( Check neromir 's comment).
a1ex07
+2  A: 

Any of the mysqli_* functions can fail. In this case the return value is false and the error/errno properties of the mysqli or mysqli_stmt object contains more information about the error. The script has to test each and every return value and react appropriately on error condition (e.g. it doesn't make sense to prepare the statement if the connection failed).

<?php
$foo = new Foo;
$foo->init();
$foo->bar();
class Foo {
  public function bar() {
    $cities = 'cities';  
    $stmt = $this->db->prepare("SELECT html FROM soTest WHERE page = ?");
    if ( !$stmt ) {
      echo "prepare failed\n";
      echo "error: ", $this->db->error, "\n";
      return;
    }

    $rc = $stmt->bind_param("s", $cities);
    if ( !$rc ) {
      echo "bind_param failed\n";
      echo "error: ", $stmt->error, "\n";
      return;
    }

    $rc=$stmt->execute();
    if ( !$rc ) {
      echo "execute failed\n";
      echo "error: ", $stmt->error, "\n";
      return;
    }

    $rc = $stmt->bind_result($result);
    if ( !$rc ) {
      echo "bind_result failed\n";
      echo "error: ", $stmt->error, "\n";
      return;
    }

    $rc = $stmt->fetch();
    if ( !$rc ) {
      echo "no such record\n";
    }
    else {
      echo 'result: ', $result, "\n";
    }
    $stmt->close();
  }

  public function init() {
    $this->db = new mysqli('localhost', 'localonly', 'localonly', 'test');
    if ($this->db->connect_error) {
      die('connection failed: ' . $this->db->connect_error);
    }
    $rc = $this->db->query('CREATE TEMPORARY TABLE
      soTest (id int auto_increment, html varchar(16), page varchar(16),primary key(id))'
    );
    if ( !$rc ) {  die('error: '.$this->db->error); }
    $rc = $this->db->query("INSERT INTO soTest (html,page) VALUES ('htmlFoo','foo'),('htmlCities','cities')");
    if ( !$rc ) {  die('error: '.$this->db->error); }
  }
}

Keep CWE-209: Information Exposure Through an Error Message in mind. Printing the actual error message in my example script is only for testing. And you might use a slightly more sophisticated error handling than just die().

VolkerK