Source for file OdbcConnector.php

Documentation is available at OdbcConnector.php

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

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