Using PHP with the mysqli Extension

Before we look at how to invoke stored programs using PHP and the mysqli extension, let's look at how we perform operations in PHP involving simple SQL statements. These operations will form the foundation for using stored programs in PHP. If you already feel very familiar with mysqli, you might want to skip forward to "Calling Stored Programs with mysqli," later in this chapter.

13.2.1. Enabling the mysqli Extension

The mysqli extension ships as standard with PHP 5.0 and above, but you may need to enable it. You do this by ensuring that ext_mysqi is listed in the extensions section of your php.ini file. The ext_mysqli extension should be included in your default php.ini file, but may be commented out. In Windows, it can be found in the Windows extension section. The relevant line will look something like this:

 extension=php_mysqli.dll

On Unix or Linux, the line should look like:

 extension=mysqli.so

 

13.2.2. Connecting to MySQL

To connect to MySQL we first create an object representing a connection using the mysqli call. The mysqli call takes arguments containing the hostname, username, password, database, and port number. The mysqli_connect_errno( ) call will contain any error code associated with the connection, and mysqi_connect_error( ) will contain the error text.

In Example 13-1 we create an object$mysqlirepresenting a MySQL connection, and check for any error condition.

Example 13-1. Creating a mysqli connection

# Create a connection

13.2.3. Checking for Errors

The mysqli connection object includes properties that reflect any error condition associated with the most recent operation. These properties include:

 

errno

Contains the MySQL-specific error code

 

sqlstate

Contains the ANSI SQLSTATE error code

 

error

Contains the text of the most recent error

When we are using prepared statements (see the section "Using Prepared Statements" later in this chapter), similar properties can be accessed as part of the statement object.

Although PHP 5 supports Java- or C#-style exception handling, the mysqli classes do not currently throw exceptions, so it is usually necessary to check these error codes after every operation.

There are a couple of different common styles for error checking. First, we could check to see if the mysqli call returned trUE (1) or FALSE (0):

 if ($mysqli->query($sql) <> TRUE) {
 printf("Statement failed %d: (%s) %s
"
 ,$mysqli->errno,$mysqli->sqlstate,$mysqli->error);
 }

If we wanted to make our code very compact, we could do this using an "or" statement, as in this example:

 $mysqli->query($sql) or printf("Statement failed %d: (%s) %s
"
 ,$mysqli->errno,$mysqli->sqlstate,$mysqli->error);

Unfortunately, this technique is not very reliable, as there are some mysqli methods that return the number of rows affected, rather than TRUE or FALSE. For these calls, you need to explicitly check the value of $mysqli->errno after the calls, as follows:

 $mysqli->query($sql);
 if ($mysqli->errno <> 0 ) {
 printf("Statement failed %d: (%s) %s
"
 ,$mysqli->errno,$mysqli->sqlstate,$mysqli->error);
 }

It is probably wiser to explicitly check the value of errno after key method calls so that you can use a consistent style of coding and can avoid introducing bugs that may occur if you misinterpret a method that returns no rows as having encountered an error.

13.2.4. Executing a Simple Non-SELECT Statement

To issue a "one-off" statement that returns no result set, we can use the query method of the mysqli connection object. Example 13-2 provides an example of issuing a simple, one-off statement.

Example 13-2. Issuing a simple statement in mysqli

$mysqli->query("CREATE TABLE guy_1 (guys_integers INT)");
if ($mysqli->errno <> 0 ) {
 printf("Statement failed %d: (%s) %s
"
 ,$mysqli->errno,$mysqli->sqlstate,$mysqli->error);
}

13.2.5. Retrieving a Result Set

If the statement issued from the query object returns a result set, we can retrieve the rows using the fetch_object( ) method. This method returns a row object, from which we can retrieve the values of the columns returned. Example 13-3 shows us cycling through the results of a query.

Example 13-3. Retrieving a result set from a simple query

$sql="SELECT employee_id, surname, salary
 FROM employees
 WHERE salary>95000
 AND department_id=1
 AND status='G'";
$results=$mysqli->query($sql);
if ($mysqli->errno) { die ($mysqli->errno." ".$mysqli->error); }
while($row=$results->fetch_object( )) {
 printf("%d	%s	%d
",$row->employee_id,$row->surname,$row->salary);
}

An alternative to the fetch_object( ) method is the fetch_row( ) method, in which columns can be referenced by number rather than name. Example 13-4 illustrates this technique.

Example 13-4. Retrieving a result set using fetch_row

$sql="SELECT employee_id, surname, salary
 FROM employees
 WHERE salary>95000
 AND department_id=1
 AND status='G'";
$results=$mysqli->query($sql);
if ($mysqli->errno) { die ($mysqli->errno." ".$mysqli->error); }
while($row=$results->fetch_row( )) {
 printf("%d	%s	%d
",$row[0],$row[1],$row[2]);
}

The use of fetch_row( ) results in code that is harder to read and maintain and is not generally recommended. However, as we shall soon see, the use of fetch_row( ) is convenient when you don't know what the result set will look like when you are writing your code (for instance, when processing a dynamic SQL statement).

13.2.6. Managing Transactions

As with most of the programmatic interfaces to MySQL, you are always free to manage transactions by executing the usual MySQL statementsfor example, SET AUTOCOMMIT, START TRANSACTION, COMMIT, and ROLLBACK. However, instead of using these statements, you may want to take advantage of the native methods available in the mysqli interface. These methods can assist with managing transactions and can be more convenient and result in simpler code. Of course, these statements are only meaningful if you are using a transactional storage engine such as InnoDB.

The following methods of the mysqli object (illustrated in Example 13-5) are transaction-oriented:

 

autocommit( )

Enables or disables the autocommit setting for the current connection

 

commit( )

Issues a COMMIT of the transaction

 

rollback( )

Issues a (you guessed it) rollback of the transaction

Example 13-5. Using mysqli transaction-handling methods

$mysqli->autocommit(FALSE);
$mysqli->query("UPDATE account_balance
 SET balance=balance-$tfer_amount
 WHERE account_id=$from_account");
if ($mysqli->errno) {
 printf("transaction aborted: %s
",$mysqli->error);
 $mysqli->rollback( );
 }
 else {
 $mysqli->query("UPDATE account_balance
 SET balance=balance+$tfer_amount
 WHERE account_id=$to_account");
 if ($mysqli->errno) {
 printf("transaction aborted: %s
",$mysqli->error);
 $mysqli->rollback( );
 }
 else {
 printf("transaction succeeded
");
 $mysqli->commit( );
 }
}

13.2.7. Using Prepared Statements

For SQL statements that may be re-executed, you can use the mysqli prepared statement interfaces. By preparing a statement before execution, you reduce the overhead of re-executing the statement. Furthermore, if a statement contains variable parameters, using the prepare and execute calls is safer than appending these parameters to the SQL and executing, since SQL code cannot be "injected" into prepared statement parameters (see Chapter 18 for a discussion of the security implications of SQL injection).

To create a prepared statement, we use the prepare( ) method of the mysqli interface, which returns a mysqli_stmt object. Any parameters within the prepared statement should be represented by ? characters, which can then be associated with PHP variables through the bind_param( ) method.

Example 13-6 illustrates the process of preparing a statement, binding parameters, and repeatedly executing a SQL statement.

Example 13-6. Preparing and multi-executing a simple SQL statement

1 #Preparing the statment
2 $insert_stmt=$mysqli->prepare("INSERT INTO x VALUES(?,?)")
3 or die($mysqli->error);
4 #associate variables with the input parameters
5 $insert_stmt->bind_param("is", $my_number,$my_string); #i=integer
6 #Execute the statement multiple times....
7 for ($my_number = 1; $my_number <= 10; $my_number++) {
8 $my_string="row ".$my_number;
9 $insert_stmt->execute( ) or die ($insert_stmt->error);
10 }
11 $insert_stmt->close( );

The relevant sections of this code are shown here:

Line

Explanation

2

Prepare an INSERT statement. The statement has two input parameters, corresponding to the values to be inserted into the table.

5

Use bind_param( ) to associate PHP variables with the SQL parameters. bind_param( ) takes two input values: first a string indicating the data types of the parameters to follow (i=integer, d=double, s=string, b=blob). So the "is" string indicates that the first parameter is to be treated as an integer, and the second as a string. The following arguments to bind_param( ) signify the PHP variables to be associated with the ? placeholders.

7

Create a loop that repeats for each of the numbers 1 to 10.

9

Execute the prepared statement. Each execution will insert the values of the PHP variables $my_number and $my_string into the table.

11

Close the prepared statement, releasing any resources associated with the statement.

 

13.2.8. Retrieving Result Sets from Prepared Statements

To retrieve a result set from a prepared statement, we must first associate the columns in the result set with the PHP variables that will hold their values. This is done using the bind_result( ) method of the prepared statement object. We then use the fetch( ) method of the prepared statement to retrieve each row. Example 13-7 illustrates this technique.

Example 13-7. Retrieving a result set from a prepared statement

 $sql="SELECT employee_id,surname,firstname
 FROM employees
 WHERE department_id=?
 AND status=?
 LIMIT 5";
 $stmt = $mysqli->prepare($sql);
 if ($mysqli->errno<>0) {die($mysqli->errno.": ".$mysqli->error);}
 $stmt->bind_param("is",$input_department_id,$input_status) or die($stmt-error);
 $stmt->bind_result( $employee_id,$surname,$firstname) or die($stmt->error);

 $input_department_id=1;
 $input_status='G';
 $stmt->execute( );
 if ($mysqli->errno<>0) {die($stmt.errno.": ".$stmt->error) ;}
 while ($stmt->fetch( )) {
 printf("%s %s %s
", $employee_id,$surname,$firstname);
 }

13.2.9. Getting Result Set Metadata

If we don't know in advance the structure of the result set being returned by our query, we can use the result_metadata( ) method of the prepared statement to retrieve the column definitions. This method returns a result object that can be queried to return the names, lengths, and types of the columns to be returned.

Example 13-8 shows us retrieving the structure of a result set from a prepared statement.

Example 13-8. Retrieving metadata from a prepared statement

 $metadata = $stmt->result_metadata( );
 $field_cnt = $metadata->field_count;
 while ($colinfo = $metadata->fetch_field( )) {
 printf("Column: %s
", $colinfo->name);
 printf("max. Len: %d
", $colinfo->max_length);
 printf("Type: %d

", $colinfo->type);
 }

13.2.10. Processing a Dynamic Result Set

Sometimes we need to process a SQL statement without knowing exactly what the columns in the result set will be. In these cases, we can use the result_metadata( ) interface to determine the composition of the result set and dynamically bind the resulting columns. However, the process is not exactly intuitive. Example 13-9 provides some PHP code that will produce an HTML table based on an arbitrary SELECT statement.

Example 13-9. Processing a dynamic result set

1 require_once "HTML/Table.php";
2 $table =new HTML_Table('border=1');
3
4 $stmt=$mysqli->prepare($sql);
5 if ($mysqli->errno) {die($mysqli->errno.": ".$mysqli->error);}
6
7 # Retrieve meta-data and print table headings
8 $metadata = $stmt->result_metadata( );
9 $field_cnt = $metadata->field_count;
10 $colnames=array( );
11 while ($colinfo = $metadata->fetch_field( )) {
12 array_push($colnames,$colinfo->name);
13 }
14 $table->addRow($colnames);
15 $table->setRowAttributes(0,array("bgcolor" => "silver"));
16
17
18 # Declare an array to receive column data
19 $stmt_results=array_fill(0,$field_cnt,'');
20 # Set first element of the bind_result parameter as the statement handle
21 $bind_result_parms[0]=$stmt;
22 # Add the references to the column arrays to the parameter list
23 for ($i=0;$i<$field_cnt;$i++) {
24 array_push($bind_result_parms, &$stmt_results[$i]);
25 }
26 #Pass the array to the bind_result function
27 call_user_func_array("mysqli_stmt_bind_result", $bind_result_parms);
28 $stmt->execute( );
29 $row=0;
30 while($stmt->fetch( )) {
31 $row++;
32 for ($i=0;$i<$field_cnt;$i++) {
33 $table->setCellContents($row,$i,$stmt_results[$i]);
34 }
35 }
36 $stmt->close( );
37 print $table->toHtml( );

Let us step through this rather complicated example:

Line(s)

Explanation

1 and 2

Set up the HTML table that will hold our result set. We're using the PEAR Table class to create our HTML tableavailable at http://pear.php.net.

4

Prepare the SQL statement. The text of the SQL statement is contained in the variable $sql: we don't have to know the text of the SQL, since this code will process the output from any SELECT statement.

8

Retrieve the result set metadata.

9

Note the number of columns that will be returned by the query.

10-13

Retrieve the name of each column to be returned into an array.

14 and 15

Create and format a nHTML table row containing our column names.

19

Initialize an array that will contain the column values for each row returned by the SQL statemnet.

21

Create an array variable that we are going to use to pass to the bind_result( ) call. To perform a dynamic bind ,we have to use the procedural version of bind_result( )mysqli_stmt_bind_result( )which takes as its first argument the prepared statement object. So the first element of our array is the statement object.

23 and 24

Add an element to $bind_result_parms for each column to be returned. Because mysqli_stmt_bind_result( ) expects to have these passed "by reference" rather than "by value," we prefix these array elements with the & symbol.

27

Bind the result variables to the dynamic SQL. The process is complicatedbecause bind_result( ) cannot accept an array of result variables, we need to call the PHP function call_user_func_array( ), which allows an array to be passed as an argument to a function that normally requires a static set of variables. We also have to use the procedural version of bind_result( ), mysqli_stmt_bind_result( ). Neverthelessdespite the complexitywe have now successfully bound the elements of stmt_results to receive the output of the fetch command.

2834

Execute the SQL and fetch the results of the SQL. The results for each column will be placed in the stmt_results array.

36 and 37

Close the prepared statement and print out the contents of the HTML table that we have built.

The procedure for rendering the results of dynamic SQL in mysqli is more complicated than we would like. However, the technique outlined above can be used when we do not know in advance what the SQL is or what result set it will outputand this can be particularly important when dealing with stored procedures, since they may return an unpredictable result set sequence.

Figure 13-1 shows the output produced by Example 13-9 when provided with a simple query against the departments table.

13.2.11. Calling Stored Programs with mysqli

All of the mysqli methods for calling standard SQL statements can also be used to call stored programs. For instance, in Example 13-10, we call a stored procedure that does not return a result set using the query method.

Example 13-10. Calling a stored procedure without a result set in mysqli

 $sql = 'call simple_stored_proc( )';
 $mysqli->query($sql);
 if ($mysqli->errno) {
 die("Execution failed: ".$mysqli->errno.": ".$mysqli->error);
 }
 else {
 printf("Stored procedure execution succeeded
");
 }

If the stored procedure returns a single result set, we can retrieve the result set as for a SELECT statement by using the fetch_object( ) method. Example 13-11 shows such a simple stored procedure.

Figure 13-1. Sample output from the dynamic SQL PHP routine

 

Example 13-11. Stored procedure with a single result set

CREATE PROCEDURE department_list( )
 READS SQL DATA
 SELECT department_name,location from departments;

Example 13-12 shows how we would retrieve the result set from this stored procedure call using query( ) and fetch_object( ).

Example 13-12. Retrieving a result set from a stored procedure

 $sql = "call department_list( )";
 $results = $mysqli->query($sql);
 if ($mysqli->errno) {
 die("Execution failed: ".$mysqli->errno.": ".$mysqli->error);
 }
 while ($row = $results->fetch_object( )) {
 printf("%s	%s
", $row->department_name, $row->location);
 }

You will often want to execute the same stored procedure multiple timespossibly with varying input parametersso it is a best practice to use mysqli prepared statements. We can use prepared statements with stored procedure in pretty much the same way as we would for any other SQL statement. For instance, in Example 13-13, we see a stored procedure that accepts an input parameter and generates a result set based on the value of that input parameter.

Example 13-13. Stored procedure with result set and input parameter

CREATE PROCEDURE customers_for_rep(in_sales_rep_id INT)
 READS SQL DATA
 SELECT customer_id,customer_name
 FROM customers
 WHERE sales_rep_id=in_sales_rep_id;

We can create a prepared statement for this stored procedure and use the bind_param( ) method to associate the stored procedure input parameter with a PHP variable. Example 13-14 illustrates this technique.

Example 13-14. Using a prepared statement to execute a stored procedure with input parameter and result set

1 $sql = "CALL customers_for_rep(?)";
2 $stmt = $mysqli->prepare($sql);
3 if ($mysqli->errno) {die($mysqli->errno.":: ".$mysqli->error);}
4
5 $stmt->bind_param("i", $in_sales_rep_id);
6 $in_sales_rep_id = 1;
7 $stmt->execute( );
8 if ($mysqli->errno) {die($mysqli->errno.": ".$mysqli->error);}
9
10 $stmt->bind_result($customer_id,$customer_name);
11 while ($stmt->fetch( )) {
12 printf("%d %s 
", $customer_id,$customer_name);
13 }

Let's look at this example line by line:

Line(s)

Explanation

13

Create a prepared statement for the stored procedure call; the ? symbol in the SQL text indicates the presence of an input parameter.

5

Associate a PHP variable ($in_sales_rep_id) with the stored procedure's input parameter.

710

Execute the stored procedure and associate PHP variables ($customer_id and $customer_name) with the columns in the output result set.

1113

Retrieve the result set from the stored procedure call.

 

13.2.12. Handling Output Parameters

The mysqli extension does not currently include a method for directly retrieving output parameters from a stored program. However, it is relatively easy to work around this limitation by using a user variable to hold the output parameter and then using a simple SQL statement to retrieve that value. Example 13-15 shows a stored procedure that returns the number of customers for a specific sales representative as a stored procedure output variable.

Example 13-15. Stored procedure with an output parameter

CREATE PROCEDURE sp_rep_customer_count(
 in_emp_id DECIMAL(8,0),
 OUT out_cust_count INT)
 NOT DETERMINISTIC READS SQL DATA
BEGIN

 SELECT count(*)
 INTO out_cust_count
 FROM customers
 WHERE sales_rep_id=in_emp_id;
END;

To retrieve the output parameter from this stored procedure, we specify a user variable (see Chapter 3 for a description of user variables) to hold the value of the output parameter, and then we issue a simple SELECT statement to retrieve the value. Example 13-16 illustrates the technique.

Example 13-16. Retrieving the value of an output parameter in mysqli

 $sql="CALL sp_rep_customer_count(1,@customer_count)";
 $stmt = $mysqli->prepare($sql);
 if ($mysqli->errno) {die($mysqli->errno.": ".$mysqli->error);}
 $stmt->execute( );
 if ($mysqli->errno) {die($mysqli->errno.": ".$mysqli->error);}
 $stmt->close( );

 $results = $mysqli->query("SELECT @customer_count AS customer_count");
 $row = $results->fetch_object( );
 printf("Customer count=%d
",$row->customer_count);

13.2.13. Retrieving Multiple Result Sets

If a stored procedure returns more than one result set, then you can use mysqli's multi_query( ) method to process all the results. The specific coding technique in PHP depends somewhat on whether you know the exact number and structure of the result sets. For instance, in the case of the very simple stored procedure in Example 13-17, we know that two, and only two, result sets will be returned, and we know the exact structure of each.

Example 13-17. Stored procedure that returns two result sets

 CREATE PROCEDURE stored_proc_with_2_results(in_sales_rep_id INT)
 DETERMINISTIC READS SQL DATA
BEGIN

 SELECT employee_id,surname,firstname
 FROM employees
 WHERE employee_id=in_sales_rep_id;

 SELECT customer_id,customer_name
 FROM customers
 WHERE sales_rep_id=in_sales_rep_id;
END;

To process this stored procedure, we first call multi_query( ) to set up the multiple results, and then we call store_result( ) to initialize each result set. We can use fetch_object( ) or fetch_row( ) to access each row in the result set. Example 13-18 illustrates this technique.

Example 13-18. Fetching two result sets from a stored procedure in mysqli

 $query = "call stored_proc_with_2_results( $employee_id )";
 if ($mysqli->multi_query($query)) {

 $result = $mysqli->store_result( );
 while ($row = $result->fetch_object( )) {
 printf("%d %s %s
",$row->employee_id,$row->surname,$row->firstname);
 }
 $mysqli->next_result( );
 $result = $mysqli->store_result( );
 while ($row = $result->fetch_object( )) {
 printf("%d %s 
",$row->customer_id,$row->customer_name);
 }
 }

Of course, we don't always know exactly how many result sets a stored procedure might return, and each result set can have an unpredictable structure. The next_result( ) method will return TRUE if there is an additional result set, and we can use the field_count property and fetch_field( ) method to retrieve the number of columns as well as their names and other properties, as shown in Example 13-19.

Example 13-19. mysqli code to process a variable number of result sets

1 $query = "call stored_proc_with_2_results( $employee_id )";
2 if ($mysqli->multi_query($query)) {
3 do {
4 if ($result = $mysqli->store_result( )) {
5 while ($finfo = $result->fetch_field( )) {
6 printf("%s	", $finfo->name);
7 }
8 printf("
");
9
10 while ($row = $result->fetch_row( )) {
11 for ($i=0;$i<$result->field_count;$i++) {
12 printf("%s	", $row[$i]);
13 }
14 printf("
");
15 }
16 $result->close( );
17 }
18 } while ($mysqli->next_result( ));

Let's look at this example line by line:

Line(s)

Explanation

2

Use the multi_query( ) call to invoke the stored procedure.

318

Define a loop that will continue so long as mysqli->next_result( ) returns TRUE: the loop will execute at least once, and then will continue as long as there are result sets to process.

4

Use store_result( ) to retrieve the result set into the $result object. We can use either store_result( ) or use_result( ): store_result( ) uses more memory, but allows some additional functionality (such as seek_result( )).

57

Loop through the column in the result set. Each call to fetch_field( ) stores the details of a new column into the $finfo object. On line 6 we print the name of the column.

1015

This loop repeats for each row in the result set. We use fetch_row( ) rather than fetch_object( ), since it is easier to refer to a column by number when we do not know its name.

1113

Loop through each column in a particular row. We use the field_count property of the result set to control that loop. On line 12 we print the value of a particular column, referring to the column by number.

16

Close the result set.

18

The while condition on this line will cause the loop to repeat if there is an additional result set and to terminate otherwise.


Part I: Stored Programming Fundamentals

Introduction to MySQL Stored Programs

MySQL Stored Programming Tutorial

Language Fundamentals

Blocks, Conditional Statements, and Iterative Programming

Using SQL in Stored Programming

Error Handling

Part II: Stored Program Construction

Creating and Maintaining Stored Programs

Transaction Management

MySQL Built-in Functions

Stored Functions

Triggers

Part III: Using MySQL Stored Programs in Applications

Using MySQL Stored Programs in Applications

Using MySQL Stored Programs with PHP

Using MySQL Stored Programs with Java

Using MySQL Stored Programs with Perl

Using MySQL Stored Programs with Python

Using MySQL Stored Programs with .NET

Part IV: Optimizing Stored Programs

Stored Program Security

Tuning Stored Programs and Their SQL

Basic SQL Tuning

Advanced SQL Tuning

Optimizing Stored Program Code

Best Practices in MySQL Stored Program Development



MySQL Stored Procedure Programming
MySQL Stored Procedure Programming
ISBN: 0596100892
EAN: 2147483647
Year: 2004
Pages: 208

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net