Source for file SqlsrvConnector.php

Documentation is available at SqlsrvConnector.php

  1. <?php
  2.  
  3. ///////////////////////////////////////////////////////////////////////////////
  4. /**
  5.  * IConnector implementation for the sqlsrv (Microsoft SQL Server 2005 Driver
  6.  * for PHP) database extension.
  7.  *
  8.  * System requirements:
  9.  * <ul>
  10.  * <li>PHP 5</li>
  11.  * <li>The {@link http://www.codeplex.com/SQL2K5PHP Microsoft SQL Server 2005 Driver for PHP}</li>
  12.  * </ul>
  13.  *
  14.  * This library is free software: you can redistribute it and/or modify
  15.  * it under the terms of the GNU Lesser General Public License as published by
  16.  * the Free Software Foundation, either version 3 of the License, or
  17.  * (at your option) any later version.
  18.  * The Connector library is distributed in the hope that it will be useful,
  19.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21.  * {@link http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License}
  22.  * for more details.
  23.  *
  24.  * @author Per Egil Roksvaag
  25.  * @copyright 2009 Per Egil Roksvaag
  26.  * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  27.  * @package connector
  28.  * @version 2.0.0
  29.  */
  30.  
  31. ///////////////////////////////////////////////////////////////////////////////
  32. /**
  33.  * Include the parent class.
  34.  */
  35.  
  36. require_once("BaseConnector.php");
  37.  
  38. /**
  39.  * IConnector implementation for Microsoft SQL Server 2005 Driver for PHP.
  40.  * @package connector
  41.  */
  42.  
  43. class SqlsrvConnector extends BaseConnector implements IConnector
  44. {
  45.     ///////////////////////////////////////////////////////////////////////////
  46.     /**
  47.      * @var array Multibyte character encodings matching the MSSQL unicode format.
  48.      */
  49.  
  50.     static protected $unicode = array("UCS-2LE""UTF-16LE");
  51.  
  52.     ///////////////////////////////////////////////////////////////////////////
  53.     /**
  54.      * Open a connection to a MSSQL Server 2005 or higher and set global options.
  55.      *
  56.      * @param array $connection An associated array of connection settings, like host and user name.
  57.      * @param array $options An associated array of global options for the resulting instance.
  58.      * @return SqlsrvConnector 
  59.      */
  60.  
  61.     public function SqlsrvConnector($connection$options array())
  62.     {
  63.         if($this->open($connection))
  64.         {
  65.             $this->options[self::PARAM_QUERIEStrue;
  66.             $this->options[self::PARAM_NAMEDfalse;
  67.             $this->options[self::PARAM_PREFIX"?";
  68.             parent::BaseConnector($options);
  69.         }
  70.         else if($this->lookup("throwException"$options))
  71.         {
  72.             throw new Exception("SqlsrvConnector error: Connection failed.");
  73.         }
  74.         else if($this->lookup(self::LOG_ERROR$options))
  75.         {
  76.             $log "SqlsrvConnector error: Connection failed in ";
  77.             error_log($log.$_SERVER["SCRIPT_FILENAME"]);
  78.         }
  79.     }
  80.  
  81.     ///////////////////////////////////////////////////////////////////////////
  82.     /**
  83.      * Open a database connection.
  84.      * @param array $connection An associated array of connection settings, like host and user name.
  85.      * @return bool <var>true</var>, if a database connection was successfully opened, <var>false</var> otherwise.
  86.      */
  87.  
  88.     protected function open($connection)
  89.     {
  90.         $hostname $this->lookup(self::CONN_HOSTNAME$connection);
  91.         $database $this->lookup(self::CONN_DATABASE$connection);
  92.         $username $this->lookup(self::CONN_USERNAME$connection);
  93.         $password $this->lookup(self::CONN_PASSWORD$connection);
  94.  
  95.         $port $this->lookup(self::CONN_PORT$connection);
  96.         $pool $this->lookup(self::CONN_POOL$connectiontrue);
  97.         $native $this->lookup(self::CONN_NATIVE$connectionarray());
  98.  
  99.         $database && $native["Database"$database;
  100.         $username && $native["UID"$username;
  101.         $password && $native["PWD"$password;
  102.         $native["ConnectionPooling"$pool;
  103.         $port && $hostname.= ",".$port;
  104.  
  105.         $this->conn = sqlsrv_connect($hostname$native);
  106.         return is_resource($this->conn);
  107.     }
  108.  
  109.     ///////////////////////////////////////////////////////////////////////////
  110.     /**
  111.      * Send a SQL SELECT query to the database and get the query result.
  112.      *
  113.      * @see IConnector::select().
  114.      * @param string $query A SQL query to execute on a database.
  115.      * @param array $param An associated array of values to be used in the $query.
  116.      * @param array $map An array of type definitions for the <var>$param</var> values.
  117.      * @param array $options An associated array of options, see the {@tutorial connector.pkg#options.element}.
  118.      * @throws Exception when $param doesn't match the type definition $map.
  119.      * @return array The query result as a table (array of associated arrays).
  120.      */
  121.  
  122.     public function select($query$param array()$map array()$options array())
  123.     {
  124.         $param TypeValidator::check($param$map);
  125.         $query $this->bind($query$param$stack$options);
  126.         $query $this->build($query$options);
  127.         $hash $this->getHash($query$stack$options);
  128.  
  129.         if($this->lookup(self::LOG_DEBUG$options))
  130.         {
  131.             $log1 "SqlsrvConnector debug: Select query in ";
  132.             $log2 $stack LB."Params: ".@implode(", "$stack"";
  133.             error_log($log1.$_SERVER["SCRIPT_FILENAME"].LB.$query.$log2);
  134.         }
  135.         if($this->getCache($hash$table$options))
  136.         {
  137.             return $table;
  138.         }
  139.         if($stmt sqlsrv_query($this->conn$query$stack))
  140.         {
  141.             do $table[$this->fetch($stmt$options);
  142.             while(sqlsrv_next_result($stmt));
  143.             sqlsrv_free_stmt($stmt);
  144.  
  145.             if(count($table== 1$table current($table);
  146.             $this->setCache($hash$table$options);
  147.             return $table;
  148.         }
  149.         if($this->lookup(self::LOG_ERROR$options))
  150.         {
  151.             $log1 "SqlsrvConnector error: Select query in ";
  152.             $log2 $stack LB."Params: ".@implode(", "$stack"";
  153.             error_log($log1.$_SERVER["SCRIPT_FILENAME"].LB.$query.$log2);
  154.             error_log("SqlsrvConnector error: ".sqlsrv_errors());
  155.         }
  156.         return false;
  157.     }
  158.  
  159.     ///////////////////////////////////////////////////////////////////////////
  160.     /**
  161.      * Send a SQL INSERT query to the database and get the IDENTITY ID
  162.      * generated from the last INSERT operation (if any).
  163.      *
  164.      * @see IConnector::insert().
  165.      * @param string $query A SQL query to execute on a database.
  166.      * @param array $param An associated array of values to be used in the $query.
  167.      * @param array $map An array of type definitions for the <var>$param</var> values.
  168.      * @param array $options An associated array of options, see the {@tutorial connector.pkg#options.element}.
  169.      * @throws Exception when $param doesn't match the type definition $map.
  170.      * @return int The IDENTITY ID of the last inserted row.
  171.      */
  172.  
  173.     public function insert($query$param array()$map array()$options array())
  174.     {
  175.         $param TypeValidator::check($param$map);
  176.         $query $this->bind($query$param$stack$options);
  177.         $query rtrim($query";").";".LB."SELECT SCOPE_IDENTITY();";
  178.  
  179.         if($this->lookup(self::LOG_DEBUG$options))
  180.         {
  181.             $log1 "SqlsrvConnector debug: Insert query in ";
  182.             $log2 $stack LB."Params: ".@implode(", "$stack"";
  183.             error_log($log1.$_SERVER["SCRIPT_FILENAME"].LB.$query.$log2);
  184.         }
  185.         if($stmt sqlsrv_query($this->conn$query$stack))
  186.         {
  187.             while(sqlsrv_next_result($stmt))
  188.             {
  189.                 $row sqlsrv_fetch_array($stmtSQLSRV_FETCH_NUMERIC);
  190.             }
  191.             sqlsrv_free_stmt($stmt);
  192.             $key is_array($rowcurrent($rownull;
  193.             return is_numeric($key? (int)$key $key;
  194.         }
  195.         else if($this->lookup(self::LOG_ERROR$options))
  196.         {
  197.             $log1 "SqlsrvConnector error: Insert query in ";
  198.             $log2 $stack LB."Params: ".@implode(", "$stack"";
  199.             error_log($log1.$_SERVER["SCRIPT_FILENAME"].LB.$query.$log2);
  200.             error_log("SqlsrvConnector error: ".sqlsrv_errors());
  201.         }
  202.         return false;
  203.     }
  204.  
  205.     ///////////////////////////////////////////////////////////////////////////
  206.     /**
  207.      * Send a SQL UPDATE query to the database and get the number of rows
  208.      * updates by the query.
  209.      *
  210.      * @see IConnector::update().
  211.      * @param string $query A SQL query to execute on a database.
  212.      * @param array $param An associated array of values to be used in the $query.
  213.      * @param array $map An array of type definitions for the <var>$param</var> values.
  214.      * @param array $options An associated array of options, see the {@tutorial connector.pkg#options.element}.
  215.      * @throws Exception when $param doesn't match the type definition $map.
  216.      * @return int The number of rows updates by the query.
  217.      */
  218.  
  219.     public function update($query$param array()$map array()$options array())
  220.     {
  221.         $param TypeValidator::check($param$map);
  222.         $query $this->bind($query$param$stack$options);
  223.  
  224.         if($this->lookup(self::LOG_DEBUG$options))
  225.         {
  226.             $log1 "SqlsrvConnector debug: Update query in ";
  227.             $log2 $stack LB."Params: ".@implode(", "$stack"";
  228.             error_log($log1.$_SERVER["SCRIPT_FILENAME"].LB.$query.$log2);
  229.         }
  230.         if($stmt sqlsrv_query($this->conn$query$stack))
  231.         {
  232.             $result sqlsrv_rows_affected($stmt);
  233.             sqlsrv_free_stmt($stmt);
  234.             return $result;
  235.         }
  236.         else if($this->lookup(self::LOG_ERROR$options))
  237.         {
  238.             $log1 "SqlsrvConnector error: Update query in ";
  239.             $log2 $stack LB."Params: ".@implode(", "$stack"";
  240.             error_log($log1.$_SERVER["SCRIPT_FILENAME"].LB.$query.$log2);
  241.             error_log("SqlsrvConnector error: ".sqlsrv_errors());
  242.         }
  243.         return false;
  244.     }
  245.  
  246.     ///////////////////////////////////////////////////////////////////////////
  247.     /**
  248.      * Send a SQL DELETE query to the database and get the number of rows
  249.      * deleted by the query.
  250.      *
  251.      * @see IConnector::delete().
  252.      * @param string $query A SQL query to execute on a database.
  253.      * @param array $param An associated array of values to be used in the $query.
  254.      * @param array $map An array of type definitions for the <var>$param</var> values.
  255.      * @param array $options An associated array of options, see the {@tutorial connector.pkg#options.element}.
  256.      * @throws Exception when $param doesn't match the type definition $map.
  257.      * @return int The number of rows deleted by the query.
  258.      */
  259.  
  260.     public function delete($query$param array()$map array()$options array())
  261.     {
  262.         $param TypeValidator::check($param$map);
  263.         $query $this->bind($query$param$stack$options);
  264.  
  265.         if($this->lookup(self::LOG_DEBUG$options))
  266.         {
  267.             $log1 "SqlsrvConnector debug: Delete query in ";
  268.             $log2 $stack LB."Params: ".@implode(", "$stack"";
  269.             error_log($log1.$_SERVER["SCRIPT_FILENAME"].LB.$query.$log2);
  270.         }
  271.         if($stmt sqlsrv_query($this->conn$query$stack))
  272.         {
  273.             $result sqlsrv_rows_affected($stmt);
  274.             sqlsrv_free_stmt($stmt);
  275.             return $result;
  276.         }
  277.         else if($this->lookup(self::LOG_ERROR$options))
  278.         {
  279.             $log1 "SqlsrvConnector error: Delete query in ";
  280.             $log2 $stack LB."Params: ".@implode(", "$stack"";
  281.             error_log($log1.$_SERVER["SCRIPT_FILENAME"].LB.$query.$log2);
  282.             error_log("SqlsrvConnector error: ".sqlsrv_errors());
  283.         }
  284.         return false;
  285.     }
  286.  
  287.     ///////////////////////////////////////////////////////////////////////////
  288.     /**
  289.      * Begins a transaction on the current connection.
  290.      * The current transaction includes all statements on the connection that
  291.      * were executed after the call to transaction() and before any calls
  292.      * to rollback() or commit().
  293.      *
  294.      * @see IConnector::transaction().
  295.      * @param array $options An associated array of options, see the {@tutorial connector.pkg#options.element}.
  296.      * @return bool true if the transaction was successfully begun, false otherwise.
  297.      */
  298.     
  299.     public function transaction($options array())
  300.     {
  301.         if($this->lookup(self::LOG_DEBUG$options))
  302.         {
  303.             $log "SqlsrvConnector debug: Begin transaction in ";
  304.             error_log($log.$_SERVER["SCRIPT_FILENAME"]);
  305.         }
  306.         if(sqlsrv_begin_transaction($this->conn))
  307.         {
  308.             return true;
  309.         }
  310.         else if($this->lookup(self::LOG_ERROR$options))
  311.         {
  312.             $log "SqlsrvConnector error: Begin transaction in ";
  313.             error_log($log.$_SERVER["SCRIPT_FILENAME"]);
  314.             error_log("SqlsrvConnector error: ".sqlsrv_errors());
  315.         }
  316.     }
  317.     
  318.     ///////////////////////////////////////////////////////////////////////////
  319.     /**
  320.      * Commits the current transaction on the current connection.
  321.      * The current transaction includes all statements on the connection that
  322.      * were executed after the call to transaction() and before any calls
  323.      * to rollback() or commit().
  324.      *
  325.      * @see IConnector::commit().
  326.      * @param array $options An associated array of options, see the {@tutorial connector.pkg#options.element}.
  327.      * @return bool true if the transaction was successfully committed, false otherwise.
  328.      */
  329.     
  330.     public function commit($options array())
  331.     {
  332.         if($this->lookup(self::LOG_DEBUG$options))
  333.         {
  334.             $log "SqlsrvConnector debug: Commit in ";
  335.             error_log($log.$_SERVER["SCRIPT_FILENAME"]);
  336.         }
  337.         if(sqlsrv_commit($this->conn))
  338.         {
  339.             return true;
  340.         }
  341.         else if($this->lookup(self::LOG_ERROR$options))
  342.         {
  343.             $log "SqlsrvConnector error: Commit in ";
  344.             error_log($log.$_SERVER["SCRIPT_FILENAME"]);
  345.             error_log("SqlsrvConnector error: ".sqlsrv_errors());
  346.         }
  347.     }
  348.     
  349.     ///////////////////////////////////////////////////////////////////////////
  350.     /**
  351.      * Rolls back the current transaction on the current connection.
  352.      * The current transaction includes all statements on the connection that
  353.      * were executed after the call to transaction() and before any calls
  354.      * to rollback() or commit().
  355.      *
  356.      * @see IConnector::rollback().
  357.      * @param array $options An associated array of options, see the {@tutorial connector.pkg#options.element}.
  358.      * @return bool true if the transaction was successfully rolled back, false otherwise.
  359.      */
  360.     
  361.     public function rollback($options array())
  362.     {
  363.         if($this->lookup(self::LOG_DEBUG$options))
  364.         {
  365.             $log "SqlsrvConnector debug: Rollback in ";
  366.             error_log($log.$_SERVER["SCRIPT_FILENAME"]);
  367.         }
  368.         if(sqlsrv_rollback($this->conn))
  369.         {
  370.             return true;
  371.         }
  372.         else if($this->lookup(self::LOG_ERROR$options))
  373.         {
  374.             $log "SqlsrvConnector error: Rollback in ";
  375.             error_log($log.$_SERVER["SCRIPT_FILENAME"]);
  376.             error_log("SqlsrvConnector error: ".sqlsrv_errors());
  377.         }
  378.     }
  379.     
  380.     ///////////////////////////////////////////////////////////////////////////
  381.     /**
  382.      * MSSQL unicode strings are converted to the special sqlsrv format.
  383.      *
  384.      * @param string $value The parameter to replace the query placeholder.
  385.      * @param array &$stack An array of values for use in parameterized queries.
  386.      * @param array $options An associated array of options.
  387.      * @return string A valid SQL string or placeholder.
  388.      */
  389.  
  390.     protected function castString($value$name&$stack$options array())
  391.     {
  392.         $charset $this->lookup(self::CHAR_DATABASE$options);
  393.         $parameterized $this->lookup(self::PARAM_QUERIES$options);
  394.  
  395.         if($parameterized && in_array($charsetself::$unicode))
  396.         {
  397.             $value $this->strStrip($value$options);
  398.             $value $this->strEncode($value$options);
  399.             $value array($valueSQLSRV_PARAM_INSQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY)SQLSRV_SQLTYPE_NVARCHAR('max'));
  400.             return $this->push($value$name$stack$options);
  401.         }
  402.         else
  403.         {
  404.             return parent::castString($value$name$stack$options);
  405.         }
  406.     }
  407.  
  408.     ///////////////////////////////////////////////////////////////////////////
  409.     /**
  410.      * Add a TOP x statement to a SQL SELECT query when the
  411.      * {@link IConnector::RESULT_LENGTH} is set.
  412.      *
  413.      * @param string $query A SQL query to execute on a database.
  414.      * @param array $options An associated array of options.
  415.      * @return string A modified SQL query.
  416.      */
  417.  
  418.     protected function build($query$options array())
  419.     {
  420.         $offset $this->lookup(self::RESULT_OFFSET$options0);
  421.         $length $this->lookup(self::RESULT_LENGTH$options);
  422.                         
  423.         if($length !== null)
  424.         {
  425.             $number $offset $length;
  426.             $pattern "/\A(SELECT)([\s]+(ALL|DISTINCT))?\b/i";
  427.             $query preg_replace($pattern"$0 TOP {$number}"$query);
  428.         }
  429.         return $query;
  430.     }
  431.  
  432.     ///////////////////////////////////////////////////////////////////////////
  433.     /**
  434.      * Fetch multiple rows of a query result.
  435.      *
  436.      * If the {@link IConnector::RESULT_LENGTH} or {@link IConnector::RESULT_OFFSET}
  437.      * options are set, some rows are omitted from the beginning and/or the end
  438.      * of the query result.
  439.      * If the {@link IConnector::RESULT_KEY_FIELD} option is set, the
  440.      * resulting table is an <b>associated</b> array of rows.
  441.      *
  442.      * @param resource $stmt A statement resource corresponding to an executed statement.
  443.      * @param array $options An associated array of options.
  444.      * @return array The query result as a table (array of associated arrays).
  445.      */
  446.  
  447.     protected function fetch($stmt$options array())
  448.     {
  449.         return in_array($this->lookup(self::CHAR_DATABASE$options)self::$unicode)
  450.             ? $this->fetchDoublebyte($stmt$options)
  451.             : $this->fetchSinglebyte($stmt$options);
  452.     }
  453.  
  454.     ///////////////////////////////////////////////////////////////////////////
  455.     /**
  456.      * Fetch multiple rows of a query result with single byte string encoding.
  457.      *
  458.      * If the {@link IConnector::RESULT_LENGTH} or {@link IConnector::RESULT_OFFSET}
  459.      * options are set, some rows are omitted from the beginning and/or the end
  460.      * of the query result.
  461.      * If the {@link IConnector::RESULT_KEY_FIELD} option is set, the
  462.      * resulting table is an <b>associated</b> array of rows.
  463.      *
  464.      * @param resource $stmt A statement resource corresponding to an executed statement.
  465.      * @param array $options An associated array of options.
  466.      * @return array The query result as a table (array of associated arrays).
  467.      */
  468.  
  469.     protected function fetchSinglebyte($stmt$options array())
  470.     {
  471.         $key $this->lookup(self::RESULT_KEY_FIELD$options);
  472.         $offset $this->lookup(self::RESULT_OFFSET$options0);
  473.         $length $this->lookup(self::RESULT_LENGTH$options);
  474.         $number is_null($lengthPHP_INT_MAX $length $offset;
  475.         $table array();
  476.         $index 0;
  477.  
  478.         while($row sqlsrv_fetch_array($stmtSQLSRV_FETCH_ASSOC))
  479.         {
  480.             if($number <= $indexbreak;
  481.             if($offset $index++continue;
  482.             $row $this->strDecode($row$options);
  483.             $key $table[$row[$key]] $row $table[$row;
  484.         }
  485.         return $table;
  486.     }
  487.  
  488.     ///////////////////////////////////////////////////////////////////////////
  489.     /**
  490.      * Fetch multiple rows of a query result with double byte string encoding.
  491.      *
  492.      * If the {@link IConnector::RESULT_LENGTH} or {@link IConnector::RESULT_OFFSET}
  493.      * options are set, some rows are omitted from the beginning and/or the end
  494.      * of the query result.
  495.      * If the {@link IConnector::RESULT_KEY_FIELD} option is set, the
  496.      * resulting table is an <b>associated</b> array of rows.
  497.      *
  498.      * WARNING: To override the default PHP types, binary strings are fetched
  499.      * with the sqlsrv_fetch/sqlsrv_get_field functions. This combination is
  500.      * REALLY slow. sqlsrv_fetch_array is about 100 times faster.
  501.      *
  502.      * @param resource $stmt A statement resource corresponding to an executed statement.
  503.      * @param array $options An associated array of options.
  504.      * @return array The query result as a table (array of associated arrays).
  505.      */
  506.  
  507.     protected function fetchDoublebyte($stmt$options array())
  508.     {
  509.         $key $this->lookup(self::RESULT_KEY_FIELD$options);
  510.         $offset $this->lookup(self::RESULT_OFFSET$options);
  511.         $length $this->lookup(self::RESULT_LENGTH$options);
  512.         $number is_null($lengthPHP_INT_MAX $length $offset;
  513.         $meta sqlsrv_field_metadata($stmt);
  514.         $table array();
  515.         $index 0;
  516.  
  517.         while(sqlsrv_fetch($stmt))
  518.         {
  519.             if($number <= $indexbreak;
  520.             if($offset $index++continue;
  521.             $len count($meta);
  522.             $row array();
  523.  
  524.             for($i 0$i $len$i++)
  525.             {
  526.                 $name $meta[$i]["Name"];
  527.                 $type $meta[$i]["Type"];
  528.  
  529.                 if($type <= -8)
  530.                 {
  531.                     $value sqlsrv_get_field($stmt$iSQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY));
  532.                     $value $this->strDecode($value$options);
  533.                     $row[$name$value;
  534.                 }
  535.                 else
  536.                 {
  537.                     $row[$namesqlsrv_get_field($stmt$i);
  538.                 }
  539.             }
  540.             $key $table[$row[$key]] $row $table[$row;
  541.         }
  542.         return $table;
  543.     }
  544.  
  545.     ///////////////////////////////////////////////////////////////////////////
  546. }
  547.  
  548. ?>

Documentation generated on Wed, 03 Jun 2009 12:41:56 +0200 by phpDocumentor 1.4.1