PHPonTraxTest
[ class tree: PHPonTraxTest ] [ index: PHPonTraxTest ] [ all elements ]

Source for file DB.php

Documentation is available at DB.php

  1. <?php
  2. /**
  3.  *  File for mock DB class
  4.  *
  5.  *  This file has the same name as the file holding the {@link }
  6.  *  http://pear.php.net/package/DB PEAR DB class}.
  7.  *  To use the mock DB, put this file in the PHP include path ahead of
  8.  *  the PEAR library, so that any class which requires DB.php will
  9.  *  load this version.
  10.  *
  11.  * (PHP 5)
  12.  *
  13.  * @package PHPonTraxTest
  14.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  15.  * @copyright (c) Walter O. Haas 2006
  16.  * @version $Id: DB.php 198 2006-04-20 16:20:30Z haas $
  17.  * @author Walt Haas <haas@xmission.com>
  18.  */
  19.  
  20. require_once 'PEAR.php';
  21. require_once 'PHPUnit2/Framework/Assert.php';
  22.  
  23. /**
  24.  * The code returned by many methods upon success
  25.  */
  26. define('DB_OK'1);
  27.  
  28. /**
  29.  * Unkown error
  30.  */
  31. define('DB_ERROR'-1);
  32.  
  33. /**
  34.  * Syntax error
  35.  */
  36. define('DB_ERROR_SYNTAX'-2);
  37.  
  38. /**
  39.  * Tried to insert a duplicate value into a primary or unique index
  40.  */
  41. define('DB_ERROR_CONSTRAINT'-3);
  42.  
  43. /**
  44.  * An identifier in the query refers to a non-existant object
  45.  */
  46. define('DB_ERROR_NOT_FOUND'-4);
  47.  
  48. /**
  49.  * Tried to create a duplicate object
  50.  */
  51. define('DB_ERROR_ALREADY_EXISTS'-5);
  52.  
  53. /**
  54.  * The current driver does not support the action you attempted
  55.  */
  56. define('DB_ERROR_UNSUPPORTED'-6);
  57.  
  58. /**
  59.  * The number of parameters does not match the number of placeholders
  60.  */
  61. define('DB_ERROR_MISMATCH'-7);
  62.  
  63. /**
  64.  * A literal submitted did not match the data type expected
  65.  */
  66. define('DB_ERROR_INVALID'-8);
  67.  
  68. /**
  69.  * The current DBMS does not support the action you attempted
  70.  */
  71. define('DB_ERROR_NOT_CAPABLE'-9);
  72.  
  73. /**
  74.  * A literal submitted was too long so the end of it was removed
  75.  */
  76. define('DB_ERROR_TRUNCATED'-10);
  77.  
  78. /**
  79.  * A literal number submitted did not match the data type expected
  80.  */
  81. define('DB_ERROR_INVALID_NUMBER'-11);
  82.  
  83. /**
  84.  * A literal date submitted did not match the data type expected
  85.  */
  86. define('DB_ERROR_INVALID_DATE'-12);
  87.  
  88. /**
  89.  * Attempt to divide something by zero
  90.  */
  91. define('DB_ERROR_DIVZERO'-13);
  92.  
  93. /**
  94.  * A database needs to be selected
  95.  */
  96. define('DB_ERROR_NODBSELECTED'-14);
  97.  
  98. /**
  99.  * Could not create the object requested
  100.  */
  101. define('DB_ERROR_CANNOT_CREATE'-15);
  102.  
  103. /**
  104.  * Could not drop the database requested because it does not exist
  105.  */
  106. define('DB_ERROR_CANNOT_DROP'-17);
  107.  
  108. /**
  109.  * An identifier in the query refers to a non-existant table
  110.  */
  111. define('DB_ERROR_NOSUCHTABLE'-18);
  112.  
  113. /**
  114.  * An identifier in the query refers to a non-existant column
  115.  */
  116. define('DB_ERROR_NOSUCHFIELD'-19);
  117.  
  118. /**
  119.  * The data submitted to the method was inappropriate
  120.  */
  121. define('DB_ERROR_NEED_MORE_DATA'-20);
  122.  
  123. /**
  124.  * The attempt to lock the table failed
  125.  */
  126. define('DB_ERROR_NOT_LOCKED'-21);
  127.  
  128. /**
  129.  * The number of columns doesn't match the number of values
  130.  */
  131. define('DB_ERROR_VALUE_COUNT_ON_ROW'-22);
  132.  
  133. /**
  134.  * The DSN submitted has problems
  135.  */
  136. define('DB_ERROR_INVALID_DSN'-23);
  137.  
  138. /**
  139.  * Could not connect to the database
  140.  */
  141. define('DB_ERROR_CONNECT_FAILED'-24);
  142.  
  143. /**
  144.  * The PHP extension needed for this DBMS could not be found
  145.  */
  146. define('DB_ERROR_EXTENSION_NOT_FOUND',-25);
  147.  
  148. /**
  149.  * The present user has inadequate permissions to perform the task requestd
  150.  */
  151. define('DB_ERROR_ACCESS_VIOLATION'-26);
  152.  
  153. /**
  154.  * The database requested does not exist
  155.  */
  156. define('DB_ERROR_NOSUCHDB'-27);
  157.  
  158. /**
  159.  * Tried to insert a null value into a column that doesn't allow nulls
  160.  */
  161. define('DB_ERROR_CONSTRAINT_NOT_NULL',-29);
  162.  
  163. /**
  164.  * Identifiers for the placeholders used in prepared statements.
  165.  * @see prepare()
  166.  */
  167.  
  168. /**
  169.  * Indicates a scalar (<kbd>?</kbd>) placeholder was used
  170.  *
  171.  * Quote and escape the value as necessary.
  172.  */
  173. define('DB_PARAM_SCALAR'1);
  174.  
  175. /**
  176.  * Indicates an opaque (<kbd>&</kbd>) placeholder was used
  177.  *
  178.  * The value presented is a file name.  Extract the contents of that file
  179.  * and place them in this column.
  180.  */
  181. define('DB_PARAM_OPAQUE'2);
  182.  
  183. /**
  184.  * Indicates a misc (<kbd>!</kbd>) placeholder was used
  185.  *
  186.  * The value should not be quoted or escaped.
  187.  */
  188. define('DB_PARAM_MISC',   3);
  189.  
  190. /**
  191.  * The different ways of returning binary data from queries.
  192.  */
  193.  
  194. /**
  195.  * Sends the fetched data straight through to output
  196.  */
  197. define('DB_BINMODE_PASSTHRU'1);
  198.  
  199. /**
  200.  * Lets you return data as usual
  201.  */
  202. define('DB_BINMODE_RETURN'2);
  203.  
  204. /**
  205.  * Converts the data to hex format before returning it
  206.  *
  207.  * For example the string "123" would become "313233".
  208.  */
  209. define('DB_BINMODE_CONVERT'3);
  210.  
  211. /**
  212.  * Fetchmode constants
  213.  */
  214. define('DB_FETCHMODE_DEFAULT'0);
  215. define('DB_FETCHMODE_ORDERED'1);
  216. define('DB_FETCHMODE_ASSOC'2);
  217. define('DB_FETCHMODE_OBJECT'3);
  218.  
  219. /**
  220.  * For multi-dimensional results, make the column name the first level
  221.  * of the array and put the row number in the second level of the array
  222.  *
  223.  * This is flipped from the normal behavior, which puts the row numbers
  224.  * in the first level of the array and the column names in the second level.
  225.  */
  226. define('DB_FETCHMODE_FLIPPED'4);
  227.  
  228. /**
  229.  * Old fetch modes.  Left here for compatibility.
  230.  */
  231. define('DB_GETMODE_ORDERED'DB_FETCHMODE_ORDERED);
  232. define('DB_GETMODE_ASSOC',   DB_FETCHMODE_ASSOC);
  233. define('DB_GETMODE_FLIPPED'DB_FETCHMODE_FLIPPED);
  234.  
  235. /**
  236.  * The type of information to return from the tableInfo() method.
  237.  *
  238.  * Bitwised constants, so they can be combined using <kbd>|</kbd>
  239.  * and removed using <kbd>^</kbd>.
  240.  *
  241.  * @see tableInfo()
  242.  */
  243. define('DB_TABLEINFO_ORDER'1);
  244. define('DB_TABLEINFO_ORDERTABLE'2);
  245. define('DB_TABLEINFO_FULL'3);
  246.  
  247. /**
  248.  * The type of query to create with the automatic query building methods.
  249.  * @see autoPrepare(), autoExecute()
  250.  */
  251. define('DB_AUTOQUERY_INSERT'1);
  252. define('DB_AUTOQUERY_UPDATE'2);
  253.  
  254. /**
  255.  * Portability Modes.
  256.  *
  257.  * Bitwised constants, so they can be combined using <kbd>|</kbd>
  258.  * and removed using <kbd>^</kbd>.
  259.  *
  260.  * @see setOption()
  261.  */
  262.  
  263. /**
  264.  * Turn off all portability features
  265.  */
  266. define('DB_PORTABILITY_NONE'0);
  267.  
  268. /**
  269.  * Convert names of tables and fields to lower case
  270.  * when using the get*(), fetch*() and tableInfo() methods
  271.  */
  272. define('DB_PORTABILITY_LOWERCASE'1);
  273.  
  274. /**
  275.  * Right trim the data output by get*() and fetch*()
  276.  */
  277. define('DB_PORTABILITY_RTRIM'2);
  278.  
  279. /**
  280.  * Force reporting the number of rows deleted
  281.  */
  282. define('DB_PORTABILITY_DELETE_COUNT'4);
  283.  
  284. /**
  285.  * Enable hack that makes numRows() work in Oracle
  286.  */
  287. define('DB_PORTABILITY_NUMROWS'8);
  288.  
  289. /**
  290.  * Makes certain error messages in certain drivers compatible
  291.  * with those from other DBMS's
  292.  *
  293.  * + mysql, mysqli:  change unique/primary key constraints
  294.  *   DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
  295.  *
  296.  * + odbc(access):  MS's ODBC driver reports 'no such field' as code
  297.  *   07001, which means 'too few parameters.'  When this option is on
  298.  *   that code gets mapped to DB_ERROR_NOSUCHFIELD.
  299.  */
  300. define('DB_PORTABILITY_ERRORS'16);
  301.  
  302. /**
  303.  * Convert null values to empty strings in data output by
  304.  * get*() and fetch*()
  305.  */
  306. define('DB_PORTABILITY_NULL_TO_EMPTY'32);
  307.  
  308. /**
  309.  * Turn on all portability features
  310.  */
  311. define('DB_PORTABILITY_ALL'63);
  312.  
  313. /**
  314.  *  Mock DB class for testing
  315.  *
  316.  *  This class is a mock version of the
  317.  *  {@link http://pear.php.net/package/DB PEAR DB class}.  It is
  318.  *  intended to provide the same interface as the real DB class, plus
  319.  *  a small database sufficient to test software.
  320.  */
  321.  
  322. class DB {
  323.  
  324.     /**
  325.      * Create a new DB object for the specified database type but don't
  326.      * connect to the database
  327.      *
  328.      * @param string $type     the database type (eg "mysql")
  329.      * @param array  $options  an associative array of option names and values
  330.      * @return object  new DB object.  A DB_Error object on failure.
  331.      * @see DB_common::setOption()
  332.      *  @todo Implement mock DB::factory
  333.      */
  334.     public function &factory($type$options false)
  335.     {
  336. //        if (!is_array($options)) {
  337. //            $options = array('persistent' => $options);
  338. //        }
  339. //
  340. //        if (isset($options['debug']) && $options['debug'] >= 2) {
  341. //            // expose php errors with sufficient debug level
  342. //            include_once "DB/{$type}.php";
  343. //        } else {
  344. //            @include_once "DB/{$type}.php";
  345. //        }
  346. //
  347. //        $classname = "DB_${type}";
  348. //
  349. //        if (!class_exists($classname)) {
  350. //            $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
  351. //                                    "Unable to include the DB/{$type}.php"
  352. //                                    . " file for '$dsn'",
  353. //                                    'DB_Error', true);
  354. //            return $tmp;
  355. //        }
  356. //
  357. //        @$obj =& new $classname;
  358. //
  359. //        foreach ($options as $option => $value) {
  360. //            $test = $obj->setOption($option, $value);
  361. //            if (DB::isError($test)) {
  362. //                return $test;
  363. //            }
  364. //        }
  365. //
  366. //        return $obj;
  367.     }
  368.  
  369.     /**
  370.      * Create a new DB object including a connection to the specified database
  371.      *
  372.      * @param mixed $dsn      the string "data source name" or array in the
  373.      *                          format returned by DB::parseDSN()
  374.      * @param array $options  an associative array of option names and values
  375.      * @return object  new DB object.  A DB_Error object on failure.
  376.      * @uses DB::parseDSN(), DB_common::setOption(), PEAR::isError()
  377.      *  @todo Implement mock DB::connect
  378.      */
  379.     function &connect($dsn$options array())
  380.     {
  381.         $dsninfo DB::parseDSN($dsn);
  382.         $type $dsninfo['phptype'];
  383.  
  384.         // only support MySQL at the moment
  385.         PHPUnit2_Framework_Assert::assertEquals($type,'mysql');
  386.         @$obj =new DB_mysql;
  387.  
  388.         foreach ($options as $option => $value{
  389.             $test $obj->setOption($option$value);
  390.             if (DB::isError($test)) {
  391.                 return $test;
  392.             }
  393.         }
  394.  
  395. //        $err = $obj->connect($dsninfo, $obj->getOption('persistent'));
  396. //        if (DB::isError($err)) {
  397. //            $err->addUserInfo($dsn);
  398. //            return $err;
  399. //        }
  400. //
  401.         return $obj;
  402.     }
  403.  
  404.     /**
  405.      * Return the DB API version
  406.      *
  407.      * @return string  the DB API version number
  408.      */
  409.     function apiVersion()
  410.     {
  411.         return '1.7.6';
  412.     }
  413.  
  414.     /**
  415.      * Determines if a variable is a DB_Error object
  416.      *
  417.      * @param mixed $value  the variable to check
  418.      * @return bool  whether $value is DB_Error object
  419.      */
  420.     function isError($value)
  421.     {
  422.         return is_a($value'DB_Error');
  423.     }
  424.  
  425.     /**
  426.      * Determines if a value is a DB_<driver> object
  427.      *
  428.      * @param mixed $value  the value to test
  429.      * @return bool  whether $value is a DB_<driver> object
  430.      *  @todo Implement mock DB::isConnection
  431.      */
  432.     function isConnection($value)
  433.     {
  434. //        return (is_object($value) &&
  435. //                is_subclass_of($value, 'db_common') &&
  436. //                method_exists($value, 'simpleQuery'));
  437.     }
  438.  
  439.     /**
  440.      * Tell whether a query is a data manipulation or data definition query
  441.      *
  442.      * @param string $query  the query
  443.      * @return boolean  whether $query is a data manipulation query
  444.      */
  445.     function isManip($query)
  446.     {
  447.         $manips 'INSERT|UPDATE|DELETE|REPLACE|'
  448.                 . 'CREATE|DROP|'
  449.                 . 'LOAD DATA|SELECT .* INTO|COPY|'
  450.                 . 'ALTER|GRANT|REVOKE|'
  451.                 . 'LOCK|UNLOCK';
  452.         if (preg_match('/^\s*"?(' $manips ')\s+/i'$query)) {
  453.             return true;
  454.         }
  455.         return false;
  456.     }
  457.  
  458.     /**
  459.      * Return a textual error message for a DB error code
  460.      *
  461.      * @param integer $value  the DB error code
  462.      * @return string  the error message or false if the error code was
  463.      *                   not recognized
  464.      *  @todo Implement mock DB::errorMessage
  465.      */
  466.     public function errorMessage($value)
  467.     {
  468.         static $errorMessages;
  469.         if (!isset($errorMessages)) {
  470.             $errorMessages array(
  471.                 DB_ERROR                    => 'unknown error',
  472.                 DB_ERROR_ACCESS_VIOLATION   => 'insufficient permissions',
  473.                 DB_ERROR_ALREADY_EXISTS     => 'already exists',
  474.                 DB_ERROR_CANNOT_CREATE      => 'can not create',
  475.                 DB_ERROR_CANNOT_DROP        => 'can not drop',
  476.                 DB_ERROR_CONNECT_FAILED     => 'connect failed',
  477.                 DB_ERROR_CONSTRAINT         => 'constraint violation',
  478.                 DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
  479.                 DB_ERROR_DIVZERO            => 'division by zero',
  480.                 DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found',
  481.                 DB_ERROR_INVALID            => 'invalid',
  482.                 DB_ERROR_INVALID_DATE       => 'invalid date or time',
  483.                 DB_ERROR_INVALID_DSN        => 'invalid DSN',
  484.                 DB_ERROR_INVALID_NUMBER     => 'invalid number',
  485.                 DB_ERROR_MISMATCH           => 'mismatch',
  486.                 DB_ERROR_NEED_MORE_DATA     => 'insufficient data supplied',
  487.                 DB_ERROR_NODBSELECTED       => 'no database selected',
  488.                 DB_ERROR_NOSUCHDB           => 'no such database',
  489.                 DB_ERROR_NOSUCHFIELD        => 'no such field',
  490.                 DB_ERROR_NOSUCHTABLE        => 'no such table',
  491.                 DB_ERROR_NOT_CAPABLE        => 'DB backend not capable',
  492.                 DB_ERROR_NOT_FOUND          => 'not found',
  493.                 DB_ERROR_NOT_LOCKED         => 'not locked',
  494.                 DB_ERROR_SYNTAX             => 'syntax error',
  495.                 DB_ERROR_UNSUPPORTED        => 'not supported',
  496.                 DB_ERROR_TRUNCATED          => 'truncated',
  497.                 DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
  498.                 DB_OK                       => 'no error',
  499.             );
  500.         }
  501.  
  502. //        if (DB::isError($value)) {
  503. //            $value = $value->getCode();
  504. //        }
  505. //
  506. //        return isset($errorMessages[$value]) ? $errorMessages[$value]
  507. //                     : $errorMessages[DB_ERROR];
  508.     }
  509.  
  510.     /**
  511.      * Parse a data source name
  512.      *
  513.      * @param string $dsn Data Source Name to be parsed
  514.      * @return array an associative array with the following keys:
  515.      *   + phptype:  Database backend used in PHP (mysql, odbc etc.)
  516.      *   + dbsyntax: Database used with regards to SQL syntax etc.
  517.      *   + protocol: Communication protocol to use (tcp, unix etc.)
  518.      *   + hostspec: Host specification (hostname[:port])
  519.      *   + database: Database to use on the DBMS server
  520.      *   + username: User name for login
  521.      *   + password: Password for login
  522.      *  @todo Implement mock DB::parseDSN
  523.      */
  524.     public function parseDSN($dsn)
  525.     {
  526.         $parsed array(
  527.             'phptype'  => false,
  528.             'dbsyntax' => false,
  529.             'username' => false,
  530.             'password' => false,
  531.             'protocol' => false,
  532.             'hostspec' => false,
  533.             'port'     => false,
  534.             'socket'   => false,
  535.             'database' => false,
  536.         );
  537.  
  538.         if (is_array($dsn)) {
  539.             $dsn array_merge($parsed$dsn);
  540.             if (!$dsn['dbsyntax']{
  541.                 $dsn['dbsyntax'$dsn['phptype'];
  542.             }
  543.             return $dsn;
  544.         }
  545.  
  546.         // Find phptype and dbsyntax
  547.         if (($pos strpos($dsn'://')) !== false{
  548.             $str substr($dsn0$pos);
  549.             $dsn substr($dsn$pos 3);
  550.         else {
  551.             $str $dsn;
  552.             $dsn null;
  553.         }
  554.  
  555.         // Get phptype and dbsyntax
  556.         // $str => phptype(dbsyntax)
  557.         if (preg_match('|^(.+?)\((.*?)\)$|'$str$arr)) {
  558.             $parsed['phptype']  $arr[1];
  559.             $parsed['dbsyntax'!$arr[2$arr[1$arr[2];
  560.         else {
  561.             $parsed['phptype']  $str;
  562.             $parsed['dbsyntax'$str;
  563.         }
  564.  
  565.         if (!count($dsn)) {
  566.             return $parsed;
  567.         }
  568.  
  569.         // Get (if found): username and password
  570.         // $dsn => username:password@protocol+hostspec/database
  571.         if (($at strrpos($dsn,'@')) !== false{
  572.             $str substr($dsn0$at);
  573.             $dsn substr($dsn$at 1);
  574.             if (($pos strpos($str':')) !== false{
  575.                 $parsed['username'rawurldecode(substr($str0$pos));
  576.                 $parsed['password'rawurldecode(substr($str$pos 1));
  577.             else {
  578.                 $parsed['username'rawurldecode($str);
  579.             }
  580.         }
  581.  
  582.         // Find protocol and hostspec
  583.  
  584.         if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|'$dsn$match)) {
  585.             // $dsn => proto(proto_opts)/database
  586.             $proto       $match[1];
  587.             $proto_opts  $match[2$match[2false;
  588.             $dsn         $match[3];
  589.  
  590.         else {
  591.             // $dsn => protocol+hostspec/database (old format)
  592.             if (strpos($dsn'+'!== false{
  593.                 list($proto$dsnexplode('+'$dsn2);
  594.             }
  595.             if (strpos($dsn'/'!== false{
  596.                 list($proto_opts$dsnexplode('/'$dsn2);
  597.             else {
  598.                 $proto_opts $dsn;
  599.                 $dsn null;
  600.             }
  601.         }
  602.  
  603.         // process the different protocol options
  604.         $parsed['protocol'(!empty($proto)) $proto 'tcp';
  605.         $proto_opts rawurldecode($proto_opts);
  606.         if ($parsed['protocol'== 'tcp'{
  607.             if (strpos($proto_opts':'!== false{
  608.                 list($parsed['hostspec'],
  609.                      $parsed['port']explode(':'$proto_opts);
  610.             else {
  611.                 $parsed['hostspec'$proto_opts;
  612.             }
  613.         elseif ($parsed['protocol'== 'unix'{
  614.             $parsed['socket'$proto_opts;
  615.         }
  616.  
  617.         // Get dabase if any
  618.         // $dsn => database
  619.         if ($dsn{
  620.             if (($pos strpos($dsn'?')) === false{
  621.                 // /database
  622.                 $parsed['database'rawurldecode($dsn);
  623.             else {
  624.                 // /database?param1=value1&param2=value2
  625.                 $parsed['database'rawurldecode(substr($dsn0$pos));
  626.                 $dsn substr($dsn$pos 1);
  627.                 if (strpos($dsn'&'!== false{
  628.                     $opts explode('&'$dsn);
  629.                 else