Welcome to MSDN Blogs Sign in | Join | Help
The SQL Server Driver for PHP is now available in the Web Platform Installer 2.0 beta

For those who are not already aware of the Web Platform Installer (WPI), it's a great tool for setting up a Windows machine for Web app development and deployment.  You can download the beta of WPI 2.0 or the WPI 1.0 release here.

 

PHP itself is available via the WPI 2.0 beta.  Installing PHP through the WPI 2.0 beta also automatically configures FastCGI as part of the installation.  Mai-lan from the API team has a great blog post here with more information about installing PHP via WPI.

 

I'm pleased to announce that the SQL Server Driver for PHP is also now available in the Web Platform Installer 2.0 beta.  Enjoy!

 

SQL Server Driver for PHP in Web Platform Installer 2.0 beta

 

David Sceppa

Program Manager - Microsoft SQL Server Driver for PHP

 

April 2009 Cumulative Update Now Available!

It is my pleasure to announce that Microsoft has released an update to version 1.0 of the Microsoft SQL Server Driver for PHP.  The update to the driver is the April 2009 Cumulative Update and it contains a series of targeted fixes, the majority of which were reported by the community via the MSDN Forum.  For more details on the specific fixes, please see the readme included in the download package.

 

There are two non-technical fixes we've made in the update worth noting.

  1. The extension is now the Microsoft SQL Server Driver for PHP, version 1.0.  We'd received a number of questions about whether or not the extension supported SQL Server 2008 because the extension had been named the SQL Server 2005 Driver for PHP.  The extension supports both SQL Server 2008 and SQL Server 2005, including all editions from Express to Enterprise.
  2. We've improved the error message returned when the ODBC driver that the extension relies on is not installed.  The error message now calls out that the cause of the error and includes the URL that developers can use to download and install the ODBC driver.

The updated binary version of the extension is available for download on the MSDN download site and replaces the v1.0 RTM release.

 

We have also published the updates to the source on the CodePlex site.  We've modified the source code specifically so that the extension can be compiled and used with the current builds of PHP 5.3.

 

Enjoy!

 

David Sceppa

Program Manager - Microsoft SQL Server Driver for PHP

Paging Data with the SQL Server Driver for PHP

One of the most common tasks of any web application involves allowing a user to ‘page’ through a large amount of data. In other words, given a large result set, the task is to present the user with a manageable subset and allow him/her to navigate through the result set by viewing ‘pages’ (i.e. subsets) of data. In this post, I’ll demonstrate two ways to do this with the existing functionality of the SQL Server Driver for PHP. Also note that the strategies discussed here can be applied to paging data regardless of the programming language or database.

 

Before diving into the details of the two examples, I want to point out two assumptions I have made:

1)      Data on the server is dynamic (i.e. data may be updated between our query executions).

2)      We want the most recent data with each page view.

With these assumptions in mind, the examples below demonstrate two similar strategies for paging. If your paging needs do not share these assumptions, you may want to consider another strategy (such as executing queries against an ordered temporary table or bringing a complete result set into client memory) that meets your needs.

 

The first example allows users to navigate through data with “Previous Page” and “Next Page” links, while the second example allows users to navigate directly to any page.

 

Note that both examples use the AdventureWorks database and that I’ve removed some of the connection code and error handling for the sake of brevity. Complete scripts are attached to this post if you want to see the code in-tact.

 

“Previous Page” and “Next Page” Navigation

This example presents users with ten products at a time and allows him/her to navigate through pages with “Previous Page” and “Next Page” links. The basic strategy is to retrieve an “extra” row (an 11th row in my example) and use it as a flag for enabling the “Next Page” link. This avoids multiple trips to the database to display one page of data.

This first section of code sets the number of rows to display on a page and executes a query that returns and “extra” row.  The query uses the ROW_NUMBER() function and OVER clause in the context of a common table expression to produce an efficient query. Common table expressions were added to T-SQL in SQL Server 2005 and are extremely helpful for paging scenarios such as this, along with scenarios where you want to execute recursive queries or want to reference a complex result multiple times within a single statement. The code for connecting to the database has been removed and the code assumes “lowRowNum” and “highRowNum” values are being passed in the $_REQUEST variable:

/* Set the number of results to display on each page. */

$rowsPerPage = 10;

 

/* Order target data by ID and select only items

   (by row number) to display on a given page.

   The query asks for one "extra" row as an

   indicator that another page of data exists. */

$tsql = "WITH Products AS

            (SELECT ROW_NUMBER() OVER(ORDER BY ProductID) AS

                  RowNumber,

                  Name,

                  ProductNumber

       FROM Production.Product)

         SELECT * FROM Products

         WHERE RowNumber BETWEEN ? AND ? + 1";

 

/* Determine which row numbers to display. */

if(isset($_REQUEST['lowRowNum']) &&

   isset($_REQUEST['highRowNum']))

{

      $lowRowNum = $_REQUEST['lowRowNum'];

      $highRowNum = $_REQUEST['highRowNum'];

}

else

{

      $lowRowNum = 1;

      $highRowNum = $rowsPerPage;

}

 

/* Set query parameter values. */

$params = array($lowRowNum, $highRowNum);

 

/* Execute the query. */

$stmt = sqlsrv_query($conn, $tsql, $params); 

 

Next, we need to display the retrieved data - if there is any! This code retrieves one row of data to make sure some data was returned. (We are working on features in v1.1 of the driver that will make this easier.) Then we display a row of data at a time until we have displayed $rowsPerPage rows, or until there are no more rows to display. If we have displayed $rowsPerPage rows and there is another row of data (that “extra” row), we display the “Next Page” link:

 

/* Retrieve one row to see if there is any data. */

$row = sqlsrv_fetch_array($stmt);

if($row === false)

{

      echo "Error in fetching row.";

     die( print_r( sqlsrv_errors(), true));

}

elseif($row[0] == 0) /* Special case of no data returned. */

{

      echo "No data returned.";

}

else /* A row was retrieved! */

{

   /* Set the number of rows that have been retrieved. */

   $rowsRetrieved = 1;

 

   /*Display table header. */

   print("<table border='1px'>

                <tr>

                   <td>Row Number</td>

                   <td>Product Name</td>

                   <td>Product ID</td>

                </tr>");

 

   /* Display the retrieved rows while we haven't

      displayed all of $rowsPerPage and there is

      another row to display. */

   do

   {

         print("<tr>

                    <td>$row[0]</td>

                    <td>$row[1]</td>

                    <td>$row[2]</td>

                    </tr>");

         $rowsRetrieved++;

     

   } while ($rowsRetrieved <= $rowsPerPage &

            $row = sqlsrv_fetch_array($stmt));

     

   /* Close table. */

   print("</table></br></br>");

 

   /*If there are previous results, display the

     Previous Page link.*/

   if($lowRowNum > 1)

   {

      $prev_page_high = $lowRowNum;

      $prev_page_low = $lowRowNum - $rowsPerPage;

      $prevPage = "?lowRowNum=$prev_page_low".

                  "&highRowNum=$prev_page_high";

      print("<a href=$prevPage>".

            "Previous Page</a>&nbsp;&nbsp;&nbsp;");

   }

 

   /* If there are more results, display the Next Page link.

      We know there are more results if the last call to

      sqlsrv_fetch_array returned a row (the "extra" row).

   */

   if($row != false)

   {       

      $next_page_low = $highRowNum;

      $next_page_high = $highRowNum + $rowsPerPage;

      $nextPage = "?lowRowNum=$next_page_low".

                  "&highRowNum=$next_page_high";

      print("<a href=$nextPage>Next Page</a>");

   }    

} 

 

Direct Page Navigation

The next example presents the user with a series of page links that allows the user to click to any page of the results he/she wants to see (without having to click “Next Page” or “Previous Page” several times).  This strategy requires executing two queries: one to retrieve the number of rows that are available, and one to actually retrieve the rows that will be displayed.

 

The following code executes a query to see how many rows of data are on the server, then populates the page with links that will query for $rowsPerPage at a time. Note that this query is executed every time the page is loaded, which is desirable if the data on the server is changing frequently. Also note that the query leverages the SQL Server COUNT function to query for the number of rows that are available without actually querying for the row data.

  

/* Set the number of results to display on each page. */

$rowsPerPage = 10;

  

/* Define a query to get the number of rows on the server.

   This query doesn't actually retrieve rows, it just

   retrieves the number of rows that are available. */

$tsql = "SELECT COUNT(ProductNumber)

             FROM Production.Product";

 

/* Execute the query. */

$stmt = sqlsrv_query($conn, $tsql);

if($stmt === false)

{

     echo "Error in query execution.";

     die( print_r( sqlsrv_errors(), true));

}

 

/* Get the number of rows returned. */

$rowsReturned = sqlsrv_fetch_array($stmt);

if($rowsReturned === false)

{

     echo "Error in retrieving number of rows.";

     die( print_r( sqlsrv_errors(), true));

}

elseif($rowsReturned[0] == 0)

{

      echo "No rows returned.";

}

else

{    

      /* Display page links. */

      $numOfPages = ceil($rowsReturned[0]/$rowsPerPage);

      for($i = 1; $i<=$numOfPages; $i++)

      {

            $pageNum = "?pageNum=$i";

            print("<a href=$pageNum>$i</a>&nbsp;&nbsp;");

      }

      echo "</br></br>";

} 

 

 Now we need to retrieve and display the data. We’ll use the same query as in the first example to retrieve data. The only tricky part here is in determining what the query parameters are. I’m determining the $highRowNum value by multiplying the page clicked by the $rowsPerPage  (25, in my case). The $lowRowNum value is determined by subtracting $resultsPerPage from $highRowNum. (I suppose I could have included this logic in the code above when printing the series of links to pages, but one way doesn’t seem to have significant advantages over the other.)

 

 

/* Order target data by ID and select only items

   (by row number) to display on a given page. */

$tsql = "WITH Products AS

            (SELECT ROW_NUMBER() OVER(ORDER BY ProductID)

                         AS RowNumber,

                            Name,    

                            ProductNumber

             FROM Production.Product)

          SELECT * FROM Products

          WHERE RowNumber BETWEEN ? AND ?";

 

/* Determine which row numbers to display. */

if(isset($_REQUEST['pageNum']))

{

      $highRowNum = $_REQUEST['pageNum'] * $rowsPerPage;

      $lowRowNum = $highRowNum - $rowsPerPage;

}

else

{

      $lowRowNum = 1;

      $highRowNum = $rowsPerPage;

}

 

/* Set query parameter values. */

$params = array($lowRowNum, $highRowNum);

 

/* Execute the query. */

$stmt2 = sqlsrv_query($conn, $tsql, $params);

if($stmt2 === false)

{

     echo "Error in query execution.";

     die( print_r( sqlsrv_errors(), true));

}

 

/* Print table header. */

print("<table border='1px'>

           <tr>

              <td>Row Number</td>

              <td>Product Name</td>

              <td>Product ID</td>

           </tr>");

 

/* Display results. */

while($row = sqlsrv_fetch_array($stmt2) )

{

      print("<tr>

                 <td>$row[0]</td>

                 <td>$row[1]</td>

                 <td>$row[2]</td>

             </tr>");

}

 

/* Close table. */

print("</table>"); 

 

And that’s it! Two similar strategies for paging with different UI’s. I hope this has been a useful post – please let us know what you think. For more information about the SQL Server Driver for PHP, see the product documentation.

 

Thanks.

Brian Swan

Programming Writer, SQL Server Driver for PHP

 

Working on version 1.1 of the SQL Server Driver for PHP

The SQL Server Driver for PHP team is hard at work on the version 1.1 of the extension.  We've received a lot of feedback for feature requests from PHP developers through the blog, forums, the Seattle PHP meetup group, and various conferences.  The most frequent feature requests have been in the following areas: 

  1. Support for PHP 5.3
  2. Support for handling UTF-8 strings in parameters and resultset values, without requiring calls to iconv or other extensions
  3. Support for scrollable results and row count

We are working on addressing these feature requests for the next version of the extension.

 

We are doing some preliminary work to test our existing version 1.0 extension with PHP 5.3 and providing guidance to help developers working with current builds of PHP 5.3 use the version 1.0 extension.  Keep an eye on this blog for more information regarding using the version 1.0 driver with PHP 5.3.  We expect to fully support PHP 5.3 with version 1.1 of our extension.

 

For UTF-8 strings, we are working on adding support for this and other character sets to the extension.  Having the SQL Server Driver for PHP handle UTF-8 strings means that the extension will handle the translations between UTF-8 and UCS-2 (little endian) since the SQL Server database engine does not currently support the UTF-8 character set.  We're working on handling parameters and resultset values using UTF-8, as well as handling the query strings in UTF-8 format.

 

We are also working on adding scrollable resultset support to the extension.  The initial version of the SQL Server Driver for PHP retrieves query results in an unbuffered forward-only mode, an approach that yields the best possible performance for retrieving query results from SQL Server but one where there is no way to know how many rows the resultset contains ahead of time.  We are planning on adding support for scrollable resultsets to the extension in version 1.1, giving PHP developers the ability to access any row as well as the ability to determine how many rows are available within the resultset.

 

David Sceppa

Program Manager - Microsoft SQL Server Driver for PHP

Using the FILESTREAM Functionality of SQL Server 2008

In this post I want to demonstrate how the streaming capabilities of the SQL Server 2005 Driver for PHP work seamlessly with the new FILESTREAM functionality in SQL Server 2008.

The FILESTREAM functionality in SQL Server 2008 enables applications to store unstructured data, such as documents and images, on the file system. Applications can leverage the rich streaming APIs and performance of the file system and at the same time maintain transactional consistency between the unstructured data and corresponding structured data. You can learn more about the FILESTREAM feature by starting here: Designing and Implementing FILESTREAM Storage.

To follow this demonstration, you will need to do two things first:

1)      Enable FILESTREAM on an instance of SQL Server. Instructions for doing this are here: How to: Enable FILESTREAM.

2)      Create a FILESTREAM-enabled database. Instructions for doing this are here: How to: Create a FILESTREAM-Enabled Database. The database I will use for demonstration purposes can be created with this script:

USE [master];
GO
 
IF EXISTS (SELECT * FROM sys.databases
           WHERE name = 'FilestreamDB')
    DROP DATABASE FilestreamDB;
GO
 
CREATE DATABASE FilestreamDB
ON
PRIMARY (NAME = F_Stream1, FILENAME = 'c:\data\FilestreamDB.mdf'),
    FILEGROUP FileStreamGroup1 CONTAINS

         FILESTREAM(NAME = F_Stream2, FILENAME = 'c:\data\filestream1')
    LOG ON (NAME = F_StreamLog1, FILENAME = 'c:\data\FilestreamDB_log.ldf')
GO
 
USE FilestreamDB;
GO
 
Create Table StreamTable (
     ID uniqueidentifier rowguidcol NOT NULL unique,
     myFileStream varbinary(max) FILESTREAM
)

 

Once you have completed these two steps, the coding patterns for sending and retrieving stream data with the SQL Server 2005 Driver for PHP are no different than those for sending/retrieving stream data from other column types (see How to: Send Stream Data and Retrieving Stream Data for more information).

Let’s look at an example of sending and retrieving FILESTREAM data.

Sending FILESTREAM Data

Although the code pattern for sending stream data to a FILESTREAM column is the same as it is for sending stream data to any column, the Transact-SQL that defines the query may new. A FILESTREAM-enabled database requires a uniqueidentifier column, and there is no function (such as SCOPE_IDENTITY()) to retrieve the generated GUID after performing an INSERT. So, in the code below, I’ve modified the Transact-SQL so I can select the GUID for use later. Also note that, in the parameter array, I’ve specified the PHP type as a binary stream and the SQL type as varbinary(‘max’) (see How to: Specify PHP Data Types and How to: Specify SQL Server Data Types for more information).

/* Define a parameterized query to insert data. */
$tsql = "DECLARE @id UNIQUEIDENTIFIER;
         SET @id = NEWID();
         INSERT INTO StreamTable (ID, myFileStream)

             VALUES (@id, ?);
         SELECT @id AS ID";
 
/* Open data as a stream. */
$pic = fopen("C:\Pics\Tree.jpg","r");
 
/* Define the parameter array. */
/* Note the specification of the PHPTYPE and SQLTYPE. */
$params = array(
           array(
                $pic,
                 SQLSRV_PARAM_IN,
                 SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY),
                 SQLSRV_SQLTYPE_VARBINARY('max')
                )
           );
 
/* Execute the query. */
$stmt = sqlsrv_query($conn, $tsql, $params);
if ($stmt === false) {
     echo "Error in statement execution.</br>";
     die( print_r( sqlsrv_errors(), true));
}

 

Retrieving FILESTREAM Data

The coding pattern for retrieving FILESTREAM data is exactly as it is for retrieving any type of streamable data. In the code below, I have to do a little work to retrieve the GUID by moving to the next result set of the query executed in the code above.

/* Skip the rows affected result by moving to the next result. */
if(sqlsrv_next_result($stmt) === false)
{
     echo "Error in moving to next result.</br>";
     die( print_r( sqlsrv_errors(), true));
}
 
/* Retrieve the first (only) row of the next result. */
if(sqlsrv_fetch($stmt) === false)
{
     echo "Error in retrieving row.</br>";
     die( print_r( sqlsrv_errors(), true));
}
 
/* Get the first field of the row and assign the value to $id. */
if( !($id = sqlsrv_get_field($stmt, 0)) )
{
     echo "Error in retrieving field.</br>";
     die( print_r( sqlsrv_errors(), true));
}
 
/* Define a parameterized query to retrieve the newly inserted data. */
$tsql = "SELECT myFileStream FROM StreamTable WHERE ID = ?";
 
/* Use the retrieved uniqueidentifier as the parameter value. */
$params = array($id);
 
/* Execute the query. */
$stmt = sqlsrv_query($conn, $tsql, $params);
if( $stmt === false )
{
     echo "Error in statement execution.</br>";
     die( print_r( sqlsrv_errors(), true));
}
 
/* Retrieve the results as a binary stream and display in the browser. */
if ( sqlsrv_fetch( $stmt ) )
{
   $image = sqlsrv_get_field($stmt, 0,

                             SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY));
   fpassthru($image);
}
else
{
     echo "Error in retrieving data.</br>";
     die(print_r( sqlsrv_errors(), true));
}

 

That’s it! There are many advantages to using the FILESTREAM functionality of SQL Server 2008, and it is easy to leverage this functionality with the streaming capabilities of the the SQL Server 2005 Driver for PHP.

Thanks.

Brian Swan

Programming Writer, SQL Server 2005 Driver for PHP

Working with the New SQL Server 2008 Data Types

One question we have heard a lot since releasing the SQL Server 2005 Driver for PHP is “Does the driver work with SQL Server 2008?” The short answer is “Yes!” However, because the driver relies on the SQL Server 2005 ODBC driver to handle low-level communications with the server, we have heard some follow up questions about how our driver works with the new data types that are available in SQL Server 2008. In this post, I will demonstrate how the SQL Server 2005 Driver for PHP interacts with these new data types. If you are not familiar with the new SQL Server 2008 data types, you can read an overview here: SQL Server 2008: New Data Types.

 

New Data Types Summary

Here’s a quick summary of the new data types in SQL Server 2008 (with links to more detailed explanations):

·         date: A date.

·         time: A time.

·         datetimeoffset : A date that is combined with a time of a day that has time zone awareness and is based on a 24-hour clock.

·         datetime2 : A date that is combined with a time of day that is based on 24-hour clock. This type that has a larger date range, a larger default fractional precision than the legacy datetime type, and it allows for optional user-specified precision.

·         hierarchyid : A variable length, system data type used to represent position in a hierarchy.

·         geometry : A .NET Common Language Runtime (CLR) data type that represents data in a Euclidean (flat) coordinate system.

·         geography : A .NET common language runtime (CLR) data type that represents data in a round-earth coordinate system.

 

Retrieving New Data Types

You can retrieve any of the types mentioned above as strings. With the date, time, datetimeoffset, and datetime2 types, this happens by default when you retrieve these types with sqlsrv_fetch_array or the combination of sqlsrv_fetch/sqlsrv_get_field.  Note that the legacy datetime and smalldatetime types are returned as  PHP Datetime objects by default. We’d be interested in your feedback about the preferred default return types for the new data types, as well as the legacy types.

The process for retrieving the hierarchyid, geometry, and geography types as strings has a couple of twists, so let’s take a look at some examples. All the examples below are run against the NewTypesDB database; a SQL script that creates and populates this database is attached to this post. Also attached is a PHP script that contains the examples shown here, plus examples of how to retrieve the date, time, datetimeoffset, and datetime2 types.

To retrieve the geometry and geography types as strings, the .ToString() method must be called on each type in the Transact-SQL that defines the query.  This method converts the data to the SQL Server nvarchar(max) data type, which the SQL Server 2005 Driver for PHP converts to a PHP stream by default.  So, one option is to retrieve these types with sqlsrv_fetch_array, which will convert the streams to strings automatically:

Note: SQL Server does not return a column name by default when the .ToString() method is called on a column. In the query below, I have specified column names in the Transact-SQL.

$tsql = "SELECT myGeometry.ToString()AS geom,
         myGeography.ToString() AS geog
         FROM GeoTable";
$stmt = sqlsrv_query($conn, $tsql);
 
while($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))
{
     print("<pre>");
     print_r($row);
     print("</pre>");
}

 

  

Another option would be to retrieve these types with the combination of sqlsrv_fetch/sqlsrv_get_field and specify the PHP return type as a string:

$tsql = "SELECT myGeometry.ToString()AS geom,
         myGeography.ToString() AS geog
         FROM GeoTable";
$stmt = sqlsrv_query($conn, $tsql);
 
while(sqlsrv_fetch($stmt))
{
     echo sqlsrv_get_field($stmt, 0,

          SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR))."</br>";
     echo sqlsrv_get_field($stmt, 1,

          SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR))."</br>";
}

 

  

And, finally, a third option would be to retrieve these types as streams (the default):

$tsql = "SELECT myGeometry.ToString()AS geom,
         myGeography.ToString() AS geog
         FROM GeoTable";
$stmt = sqlsrv_query($conn, $tsql);

while(sqlsrv_fetch($stmt))
{
    fpassthru(sqlsrv_get_field($stmt, 0));
    echo "</br>";
}

 

  

Retrieving the hierarchyid type also requires calling the .ToString() method on the type in the query, but this time the method returns a nvarchar(4000) data type. The SQL Server 2005 Driver for PHP returns this type as a string by default, so retrieving this type with sqlsrv_fetch_array or the combination of sqlsrv_fetch/sqlsrv_get_field should be straight forward. However, again note that SQL Server does not return a column name by default when the .ToString() method is called on the column:

Retrieving hierarchyid type using sqlsrv_fetch_array:

$tsql = "SELECT EmployeeName, Title,

                OrgNode.ToString() AS org
         FROM HierarchyTable";
$stmt = sqlsrv_query($conn, $tsql);

 

while($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))
{
     print("<pre>");
     print_r($row);
     print("</pre>");
}

 

  

Retrieving hierarchyid type using sqlsrv_fetch/sqlsrv_get_field:

$tsql = "SELECT EmployeeName, Title,

                OrgNode.ToString() AS org
         FROM HierarchyTable";
$stmt = sqlsrv_query($conn, $tsql);

 

while($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))
{
     echo sqlsrv_get_field($stmt, 0)."</br>";
     echo sqlsrv_get_field($stmt, 1)."</br>";
     echo sqlsrv_get_field($stmt, 2)."</br>";
}

 

  

Inserting New Data Types

With all of the new SQL Server 2008 data types you can simply send strings to the database. You will have to pay some attention to the format of the strings you send, but the server does a fairly good job of converting strings to the correct data type.

With the date, time, datetimeoffset, and datetime2 types, the format of the strings must be close to the expected format of the destination type. In some cases, however, the server will not be able to convert the supplied string to the desired data type (resulting in an error from the server).

In this example I send strings that closely match the format of the destination type:

$tsql = "INSERT INTO DateTimeTable (myDate,
                                    myTime,
                                    myDateTimeOffset,
                                    myDatetime2)
         VALUES (?, ?, ?, ?)";

 

$params = array(

            date("Y-m-d"), // Current date in Y-m-d format.
            "15:30:41.987", // Time as a string.
            date("c"), // Current date in ISO 8601 format.
            date("Y-m-d H:i:s.u") // Current date and time.
          );


$stmt = sqlsrv_query($conn, $tsql, $params);

 

  

With the geometry and geography types, strings must conform to the Open Geospatial Consortium (OGC) Well-Known Text (WKT) format. (For more information, see Designing and Implementing Spatial Storage.) In the following example, I send POLYGON and LINESTRING data to the GeoTable:

 

$tsql = "INSERT INTO GeoTable (myGeometry,
                               myGeography)
         VALUES (?, ?)";

 

$params = array(

           'POLYGON ((0 0, 150 0, 150 150, 0 150, 0 0))',
           'LINESTRING(-122.360 47.656, -122.343 47.656)'

          );

 

$stmt = sqlsrv_query($conn, $tsql, $params);

 

  

And finally, the hierarchyid type can be sent to as a string as long as the string is a series of slashes separated by numerical values (e.g. /2/3/1/). The following code inserts an employee just below the CEO level (as a sibling to Sariya in the hierarchy defined in the HierarchyTable): 

 

$tsql = "INSERT INTO HierarchyTable
               (EmployeeName, Title, OrgNode)
         VALUES (?, ?, ?)";

 

$params = array("Brian", "Specialist", "/2/");

$stmt = sqlsrv_query($conn, $tsql, $params);

 

  

That’s it. Please let me know if this has been useful/helpful. In my next post, I’ll address how to use the new filestream capabilities of SQL Server 2008 from PHP scripts.

 

Thanks.

Brian Swan

Programming Writer, SQL Server 2005 Driver for PHP

October 2008 Cumulative Update Now Available!
 

It is my pleasure to announce that Microsoft has released an update to version 1.0 of the Microsoft SQL Server 2005 Driver for PHP.  The update to the driver is the October 2008 Cumulative Update and it contains fixes to two high priority bugs reported via the MSDN Forum:

  1. Failure when executing UPDATE / DELETE query that affects 0 rows
  2. Default packet size exceeds SSL max. limit

Thanks to W1lz0r and Jos Juffermans for bringing those issues to our attention.

 

The updated version of the driver is available for download on the MSDN download site and replaces the v1.0 RTM release.  We have also published the updates to the source on the CodePlex site.

 

Enjoy!

 

David Sceppa

Program Manager - Microsoft SQL Server Driver for PHP

How and Why to Use Parameterized Queries

I recently attended ZendCon 08 in Santa Clara, CA with David Sceppa, Program Manager for the SQL Server 2005 Driver for PHP. We hosted a "Birds of a Feather" (BOF) session and were happy to get feedback and field lots of good questions from developers who are using our driver. During the session, our discussion touched on how and why to use parameterized queries. The focus of this post is to recap that conversation and to demonstrate how to use parameterized queries with the SQL Server 2005 Driver for PHP.

A parameterized query is a query in which placeholders are used for parameters and the parameter values are supplied at execution time. The most important reason to use parameterized queries is to avoid SQL injection attacks.

Let's take a look at what can happen if we don't use parameterized queries. Consider the following code that concatenates user input with SQL syntax:

$name = $_REQUEST['name'];

$email = $_REQUEST['email'];

$sql = "INSERT INTO CustomerTable (Name, Email)

        VALUES ('$name', '$email')";

Now suppose a user enters the following data:

           FormInput

The resulting SQL query (defined by $sql) is the following:

INSERT INTO CustomerTable (Name, Email)
    VALUES ('Brian', 'bswan@microsoft.com');
DROP TABLE CustomerTable;
PRINT 'Gotcha!'--')

This is a perfectly valid SQL query, and, as you can see, the results of executing this on the server (with a function such as sqlsrv_query) would not be desired. This does assume that the user has some knowledge of your database (or that he guessed the table name correctly) and that credentials used to access the server have sufficient permissions to drop a table. However far-fetched these assumptions may seem, when you construct SQL queries by concatenating user input with SQL syntax you run the risk of the user supplying input that may cause your query to do something that you had not expected.

The simplest and most effective way to avoid the scenario described above is to use parameterized queries. Here is how the code above would look when using a parameterized query:

$name = $_REQUEST['name'];
$email = $_REQUEST['email'];
$params = array($name, $email);
$sql = 'INSERT INTO CustomerTable (Name, Email) VALUES (?, ?)';

Now, to execute the query, we just pass an open connection ($conn), the SQL query ($sql), and the parameter array ($params) to the sqlsrv_query function:

$stmt = sqlsrv_query($conn, $tsql, $params);

(The sqlsrv_query function returns a PHP statement resource.)

The difference here (as opposed to concatenating user input with SQL syntax) is that a query plan is constructed on the server before the query is executed with parameter values. In other words, a query plan is constructed on the server for this query:

INSERT INTO CustomerTable (Name, Email) VALUES (?, ?)

When you execute this query using parameterized values and the same user input , only the INSERT query is executed. The server accepts the user input of

bswan@microsoft.com'; DROP TABLE CustomerTable; PRINT 'Gotcha!'--

and inserts that entire value into the Email field. Using a parameterized query prevents the user input from leading to SQL injection. Plus, using a parameterized query allows you to handle less malicious scenarios, such as where the user supplies a value like "O'Leary" without forcing you to replace single quotes with double single quotes.

For more information about avoiding SQL injection attacks, see SQL Injection. For more information about how to execute parameterized queries, see How to: Perform Parameterized Queries, How to: Execute a Single Query, and How to: Execute a Query Multiple Times in the driver documentation.

Regardless of which database server or driver you use, a best practice for writing code is to use parameterized queries for security reasons.

Please let me know if this post has been helpful, and/or post questions or other comments.

Thanks.

Brian Swan
Programming Writer, SQL Server Driver for PHP

Greetings from ZendCon08!

Brian Swan and I are here at ZendCon08 to talk with PHP developers about the SQL Server Driver for PHP.  Come visit us in the Microsoft booth and say, "Hi!"

 

We've also signed up for an un-con session Wednesday at 11:00 to get more feedback from the community.  For more information, see the un-con wiki page:  http://www.zendcon.com/ZendCon08/public/wiki/UnCon_Talks

 

David Sceppa

Program Manager - Microsoft SQL Server Driver for PHP

New SQL Server Driver for PHP Whitepaper Available

A new technical article covering the SQL Server 2005 Driver for PHP is now available on MSDN: Accessing SQL Server Databases with PHP. This article highlights many of the features of the driver by examining portions of the Example Application that is available in the documentation. Here’s the table of contents to pique your interest: 

 

- Introduction

- Loading the Driver

- Configuring the Driver

- Creating a Connection

- Connection Pooling

- Executing a Query

- Retrieving Data from the Server

- Retrieving Data as an Array

- Retrieving Images

- Sending Data to the Server

- Sending Images to the Server

- Moving Through Groups of Result Sets

- Handling Errors and Warnings

- Resources

- Conclusion

 

Brian Swan

Programming Writer – SQL Server Driver for PHP
Unicode Support and Endianness

We’ve seen more activity on our MSDN Forum over the past couple weeks (yay!) and there have been a few threads about how/if the SQL Server Driver for PHP supports Unicode.  Rest assured, the SQL Server Driver for PHP does support Unicode.  The driver expects Unicode strings to be in little endian format, since that's what SQL Server uses.

 

When you convert strings between different encodings in PHP using iconv or mb_convert_encoding, PHP will assume big endian format if you do not specify endianness.  In other words, you must specify "ucs-2le" instead of just "ucs-2".  The example in our documentation for working with UTF-8 strings did not specify endianness so the strings were converted to and from Unicode in big endian format.  We're in the process of correcting that error and apologize for the confusion.

 

For more information on endianness and the origin of the term "endian", see Michael Kaplan's blog post or the Wikipedia entry on endianness.

 

David Sceppa

Program Manager - Microsoft SQL Server Driver for PHP

A new look to the blog

A number of people contacted me to say that the previous color scheme made the blog difficult to read.  This should be a little easier on the eyes.

David Sceppa

Program Manager, Microsoft SQL Server Driver for PHP

Microsoft SQL Server 2005 Driver for PHP v1.0 has released

It is my pleasure to announce that version 1.0 of the Microsoft SQL Server 2005 Driver for PHP has released!  This release marks another step in Microsoft’s continued commitment to interoperability.  To keep up with our announcements and customer feedback, please check out our team blog.


The SQL Server Driver for PHP team would like to thank everyone who has provided feedback and bug reports throughout the development cycle for the driver.  We've come a long way since the initial CTP in October of 2007 thanks to your feedback.

 

Where do I get the bits?

The driver is available for download on the MSDN download site.  We encourage you to download the driver and explore the sample application described in the .chm file.  If you'd prefer to access our documentation on-line, it's available on the MSDN site.  We welcome your feedback, feature requests and bug reports on our MSDN Forum.

 

What about the source?

Microsoft has published the source code to the driver on the CodePlex site.  There, you can download the source code and provide feedback in the form of bug reports and feature requests.

 

We understand that many developers will download the source code from CodePlex to create their own build(s) of the driver.  However, Microsoft supports only the signed version of the driver from the MSDN download site.  If you're using a custom built version of the driver and encounter problems, please reproduce the problem with the Microsoft-built driver before contacting Microsoft support.

 

How will Microsoft respond to feedback and update the driver?

We will be responsive to feedback on the CodePlex, MSDN Forum and through our blog, weighing in on both feature requests and bug reports.  We currently plan to update the driver available on the MSDN download site approximately once every three months.  When we update the driver on the MSDN download site, we will also push the corresponding changes to the source code up to the CodePlex site.

 

With each successive update, we will revisit this plan.  We've seen too many projects over-reach in their plans to be responsive to the community.  We would prefer to start with a more conservative approach and make sure we're successfully delivering on that before expanding.

 

David Sceppa

Program Manager, Microsoft SQL Server Driver for PHP
What's new in the May 2008 CTP?

A new Community Technology Preview (CTP) version of the SQL Server 2005 Driver for PHP is now available for download. The May CTP version (1.0.821.1) is a significant improvement over the previous CTP version (1.0.515.1 of February 2008) and it includes several new and/or refined features.

 

The list of major changes includes:

·         A new API, sqlsrv_fetch_object, offers a more sophisticated alternative to sqlsrv_fetch_array by enabling retrieval of data rows as PHP objects.

·         Another new API, sqlsrv_num_fields, returns the number of fields in an active result set (thus reducing the need for calling sqlsrv_field_metadata).

·         A redesigned configuration facility anchored on two APIs,  sqlsrv_get_config  and sqlsrv_configure, allows you to explore and/or modify the error handling settings and to configure of the logging system.

·         By default, the driver treats most (i.e. all but four) of ODBC warnings as errors. This behavior can be turned on and off via sqlsrv_configure.

·         Connection options offer several new attributes, including the ability to enable (or disable) connection pooling. (Note: connection pooling is enabled by default.)

·         New query options (in both sqlsrv_query and sqlsrv_prepare) allow a better control of query execution. QueryTimeout aims to avoid execution deadlocks and SendStreamParamsAtExec eliminates the need for subsequent calls to sqlsrv_send_stream_data.

·         User Data Type (UDT) is now supported.

·         Results set fields are no longer starting at index 1. Both sqlsrv_get_field and sqlsrv_fetch_array have first data field at index 0.

·         All APIs (including sqlsrv_close and sqlsrv_free_stmt) will now return false when called with an invalid parameter.

 

Additional improvements aim to provide better indications of error conditions and reduce the potential for resource and/or memory leakage.

 

For additional details, please consult “What’s New” topic at:

http://msdn.microsoft.com/en-us/library/cc296192.aspx

 

The CTP release and documentation are available for download at:

http://www.microsoft.com/downloads/details.aspx?familyid=85f99a70-5df5-4558-991f-8aee8506833c&displaylang=en

More info is available on the MSDN site and the PHP blog site: http://blogs.msdn.com/sqlphp/ 

For any questions regarding the SQL Server 2005 Driver for PHP driver, please feel free to post a question on the   MSDN forum site for the PHP driver.

 

- Serban Iliescu

  Test Lead, SQL Server Driver for PHP

The May '08 CTP is now available!

I'm pleased to announce that the May '08 CTP for the SQL Server 2005 Driver for PHP is now available for download here.  Keep in mind that this is a technology preview and is not intended for production purposes.  The documentation for the driver is available on our MSDN siteWe look forward to your feedback on the new MSDN forum specifically for the driver.

 

Please stay tuned, as we will soon be blogging about some of the new functionality in the May '08 CTP.

 

David Sceppa

Program Manager, SQL Server Driver for PHP

More Posts Next page »
Page view tracker