diff --git a/dibi/dibi.php b/dibi/dibi.php index 35f90b13..b7365ac3 100644 --- a/dibi/dibi.php +++ b/dibi/dibi.php @@ -14,7 +14,7 @@ * @license GNU GENERAL PUBLIC LICENSE * @package dibi * @category Database - * @version 0.5b $Revision$ $Date$ + * @version 0.6 $Revision$ $Date$ */ @@ -31,10 +31,12 @@ require_once dirname(__FILE__).'/libs/resultset.php'; require_once dirname(__FILE__).'/libs/parser.php'; require_once dirname(__FILE__).'/libs/exception.php'; -// support -require_once dirname(__FILE__).'/libs/date.type.demo.php'; +// required since PHP 5.1.0 +if (function_exists('date_default_timezone_set')) + date_default_timezone_set('Europe/Prague'); // or 'GMT' + /** @@ -374,7 +376,7 @@ class dibi $sql = preg_replace('#\*\*.+?\*\*#', '$0', $sql); - echo '
', $sql, '
'; + echo '
', $sql, '
'; } diff --git a/dibi/libs/date.type.demo.php b/dibi/libs/date.type.demo.php deleted file mode 100644 index f43bed70..00000000 --- a/dibi/libs/date.type.demo.php +++ /dev/null @@ -1,101 +0,0 @@ - - * @link http://texy.info/dibi/ - * @copyright Copyright (c) 2005-2006 David Grudl - * @license GNU GENERAL PUBLIC LICENSE - * @package dibi - * @category Database - * @version $Revision$ $Date$ - */ - - -// security - include dibi.php, not this file -if (!defined('DIBI')) die(); - - -// required since PHP 5.1.0 -// todo: -if (function_exists('date_default_timezone_set')) - date_default_timezone_set('Europe/Prague'); // or 'GMT' - - - -/** - * Pseudotype for UNIX timestamp representation - */ -class TDate implements IDibiVariable -{ - /** - * Unix timestamp - * @var int - */ - protected $time; - - - - public function __construct($time = NULL) - { - if ($time === NULL) - $this->time = time(); // current time - - elseif (is_string($time)) - $this->time = strtotime($time); // try convert to timestamp - - else - $this->time = (int) $time; - } - - - - /** - * Format for SQL - * - * @param object destination DibiDriver - * @param string optional modifier - * @return string - */ - public function toSQL($driver, $modifier = NULL) - { - return date( - $driver->formats['date'], // format according to driver's spec. - $this->time - ); - } - - - - public function getTimeStamp() - { - return $this->time; - } - - -} - - - - -/** - * Pseudotype for datetime representation - */ -class TDateTime extends TDate -{ - - public function toSQL($driver, $modifier = NULL) - { - return date( - $driver->formats['datetime'], // format according to driver's spec. - $this->time - ); - } - -} - -?> \ No newline at end of file diff --git a/dibi/libs/exception.php b/dibi/libs/exception.php index 5c6806af..d576206a 100644 --- a/dibi/libs/exception.php +++ b/dibi/libs/exception.php @@ -58,6 +58,9 @@ class DibiException extends Exception +/** + * Checks result state + */ function is_error($var) { return ($var === FALSE) || ($var instanceof Exception); diff --git a/dibi/libs/parser.php b/dibi/libs/parser.php index fc42ae5c..33cdb195 100644 --- a/dibi/libs/parser.php +++ b/dibi/libs/parser.php @@ -43,133 +43,21 @@ class DibiParser { $sql = ''; $this->driver = $driver; - $this->modifier = 0; + $this->modifier = false; $this->hasError = false; $command = null; - $lastString = null; + //$lastString = null; foreach ($args as $index => $arg) { $sql .= ' '; // always add simple space - - // array processing (with or without modifier) - if (is_array($arg)) { - // determine type: set | values | list - if ($this->modifier) { - $type = $this->modifier; - $this->modifier = false; - } else { - // autodetect - if (is_int(key($arg))) - $type = 'L'; // LIST - else { - if (!$command) - $command = strtoupper(substr(ltrim($args[0]), 0, 6)); - - $type = $command == 'UPDATE' ? 'S' : 'V'; // SET | VALUES - } - } - - // build array - $vx = $kx = array(); - switch ($type) { - case 'S': // SET - foreach ($arg as $k => $v) - $vx[] = $this->driver->quoteName($k) . '=' . $this->formatValue($v); - - $sql .= implode(', ', $vx); - break; - - case 'V': // VALUES - foreach ($arg as $k => $v) { - $kx[] = $this->driver->quoteName($k); - $vx[] = $this->formatValue($v); - } - - $sql .= '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')'; - break; - - case 'L': // LIST - foreach ($arg as $k => $v) - $vx[] = $this->formatValue($v); - - $sql .= implode(', ', $vx); - break; - - case 'N': // NAMES - foreach ($arg as $v) - $vx[] = $this->driver->quoteName($v); - - $sql .= implode(', ', $vx); - break; - - default: - $this->hasError = true; - $sql .= "**Unknown modifier %$type**"; - } - - continue; - } - - - - // after-modifier procession - if ($this->modifier) { - if ($arg instanceof IDibiVariable) { - $sql .= $arg->toSql($this->driver, $this->modifier); - $this->modifier = false; - continue; - } - - if (!is_scalar($arg) && !is_null($arg)) { // array is already processed - $this->hasError = true; - $this->modifier = false; - $sql .= '**Unexpected '.gettype($arg).'**'; - continue; - } - - switch ($this->modifier) { - case "s": // string - $sql .= $this->driver->escape($arg, TRUE); - break; - case 'T': // date - $sql .= date($this->driver->formats['date'], is_string($arg) ? strtotime($arg) : $arg); - break; - case 't': // datetime - $sql .= date($this->driver->formats['datetime'], is_string($arg) ? strtotime($arg) : $arg); - break; - case 'b': // boolean - $sql .= $arg ? $this->driver->formats['TRUE'] : $this->driver->formats['FALSE']; - break; - case 'i': - case 'u': // unsigned int - case 'd': // signed int - $sql .= (string) (int) $arg; - break; - case 'f': // float - $sql .= (string) (float) $arg; // something like -9E-005 is accepted by SQL - break; - case 'n': // identifier name - $sql .= $this->driver->quoteName($arg); - break; - default: - $this->hasError = true; - $sql .= "**Unknown modifier %$this->modifier**"; - } - - $this->modifier = false; - continue; - } - - // simple string means SQL - if (is_string($arg)) { + if (is_string($arg) && !$this->modifier) { // double string warning // (problematic with dibi::queryStart & dibi::queryAdd // if ($lastString === $index-1) // trigger_error("Is seems there is error in SQL near '$arg'.", E_USER_WARNING); - - $lastString = $index; +// $lastString = $index; // speed-up - is regexp required? $toSkip = strcspn($arg, '`[\'"%'); @@ -196,10 +84,17 @@ class DibiParser continue; } + // array processing without modifier - autoselect between SET or VALUES + if (is_array($arg) && !$this->modifier && is_string(key($arg))) { + if (!$command) + $command = strtoupper(substr(ltrim($args[0]), 0, 6)); + + $this->modifier = ($command == 'INSERT' || $command == 'REPLAC') ? 'V' : 'S'; + } // default processing - $sql .= $this->formatValue($arg); - + $sql .= $this->formatValue($arg, $this->modifier); + $this->modifier = false; } // for @@ -214,8 +109,77 @@ class DibiParser - private function formatValue($value) + private function formatValue($value, $modifier) { + // array processing (with or without modifier) + if (is_array($value)) { + + $vx = $kx = array(); + switch ($modifier) { + case 'S': // SET + foreach ($value as $k => $v) { + list($k, $mod) = explode('%', $k.'%', 3); // split modifier + $vx[] = $this->driver->quoteName($k) . '=' . $this->formatValue($v, $mod); + } + + return implode(', ', $vx); + + case 'V': // VALUES + foreach ($value as $k => $v) { + list($k, $mod) = explode('%', $k.'%', 3); // split modifier + $kx[] = $this->driver->quoteName($k); + $vx[] = $this->formatValue($v, $mod); + } + + return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')'; + + default: // LIST + foreach ($value as $v) + $vx[] = $this->formatValue($v, $modifier); + + return implode(', ', $vx); + } + } + + + // with modifier procession + if ($modifier) { + if ($value instanceof IDibiVariable) + return $value->toSql($this->driver, $this->modifier); + + if (!is_scalar($value) && !is_null($value)) { // array is already processed + $this->hasError = true; + return '**Unexpected '.gettype($value).'**'; + } + + switch ($modifier) { + case "s": // string + return $this->driver->escape($value, TRUE); + case 'b': // boolean + return $value ? $this->driver->formats['TRUE'] : $this->driver->formats['FALSE']; + case 'i': + case 'u': // unsigned int + case 'd': // signed int + return (string) (int) $value; + case 'f': // float + return (string) (float) $value; // something like -9E-005 is accepted by SQL + case 'D': // date + return date($this->driver->formats['date'], is_string($value) ? strtotime($value) : $value); + case 'T': // datetime + return date($this->driver->formats['datetime'], is_string($value) ? strtotime($value) : $value); + case 'n': // identifier name + return $this->driver->quoteName($value); + case 'p': // preserve as SQL + return (string) $value; + default: + $this->hasError = true; + return "**Unknown modifier %$modifier**"; + } + } + + + + // without modifier procession if (is_string($value)) return $this->driver->escape($value, TRUE); @@ -232,7 +196,7 @@ class DibiParser return $value->toSql($this->driver); $this->hasError = true; - return '**Unsupported type '.gettype($value).'**'; + return '**Unexpected '.gettype($value).'**'; } diff --git a/dibi/libs/resultset.php b/dibi/libs/resultset.php index 4718b400..19825853 100644 --- a/dibi/libs/resultset.php +++ b/dibi/libs/resultset.php @@ -284,10 +284,10 @@ abstract class DibiResult implements IteratorAggregate, Countable } if ($type == self::FIELD_DATE) - return new TDate($value); // !!! experimental + return strtotime($value); // !!! not good if ($type == self::FIELD_DATETIME) - return new TDateTime($value); // !!! experimental + return strtotime($value); // !!! not good return $value; } diff --git a/examples/connect.php b/examples/connect.php index c5b9d22b..290b9a0b 100644 --- a/examples/connect.php +++ b/examples/connect.php @@ -2,38 +2,46 @@ require_once '../dibi/dibi.php'; -// use two connections: -// first connection to mysql + +// connects to mysql $state = dibi::connect(array( 'driver' => 'mysql', 'host' => 'localhost', 'username' => 'root', - 'password' => '***', + 'password' => 'xxx', // change to real password! 'database' => 'test', 'charset' => 'utf8', -), 1); +)); -if ($state instanceof Exception) { - echo $state; -} - -if (!dibi::isConnected()) { - die(); -} - - - -// second connection to odbc +/* connects to ODBC dibi::connect(array( 'driver' => 'odbc', 'username' => 'root', 'password' => '***', 'database' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq=C:\\Database.mdb', -), 3); +)); +*/ -echo dibi::isConnected(); +// check status +if (!dibi::isConnected()) { + echo 'dibi::isConnected(): Not connected'; + echo "
\n"; +} + + +// or checked status this way +if (is_error($state)) { + + // $state can be FALSE or Exception + if ($state instanceof Exception) + echo $state; + else + echo 'FALSE'; + + echo "
\n"; +} diff --git a/examples/date.type.demo.php b/examples/date.type.demo.php new file mode 100644 index 00000000..fb2d065b --- /dev/null +++ b/examples/date.type.demo.php @@ -0,0 +1,76 @@ +time = time(); // current time + + elseif (is_string($time)) + $this->time = strtotime($time); // try convert to timestamp + + else + $this->time = (int) $time; + } + + + + /** + * Format for SQL + * + * @param object destination DibiDriver + * @param string optional modifier + * @return string + */ + public function toSQL($driver, $modifier = NULL) + { + return date( + $driver->formats['datetime'], // format according to driver's spec. + $this->time + ); + } + + + +} + + + +// connects to mysqli +dibi::connect(array( + 'driver' => 'mysqli', + 'host' => 'localhost', + 'username' => 'root', + 'password' => 'xxx', // change to real password! + 'charset' => 'utf8', +)); + + + +// generate and dump SQL +dibi::test(" +INSERT INTO [test]", array( + 'A' => 12, + 'B' => NULL, + 'C' => new TDateTime(31542), // using out class + 'D' => 'any string', +)); + + +?> \ No newline at end of file diff --git a/examples/fetch.php b/examples/fetch.php index e3074b35..a17b050c 100644 --- a/examples/fetch.php +++ b/examples/fetch.php @@ -10,7 +10,7 @@ dibi::connect(array( 'driver' => 'mysqli', 'host' => 'localhost', 'username' => 'root', - 'password' => '***', + 'password' => 'xxx', // change to real password! 'database' => 'test', 'charset' => 'utf8', )); @@ -22,6 +22,7 @@ if (!dibi::isConnected()) $res = dibi::query('SELECT * FROM table'); + // fetch a single value $value = $res->fetchSingle(); diff --git a/examples/log.sql b/examples/log.sql index ba7c7fb6..bf36bfdd 100644 --- a/examples/log.sql +++ b/examples/log.sql @@ -1,14 +1,14 @@ Successfully connected to DB 'mysql' SELECT * FROM `nucleus_item` WHERE `inumber` = 38; --- Result: object(DibiMySqlResult) rows: 1 --- Takes: 4.994 ms +-- Result: Query error: Table 'test.nucleus_item' doesn't exist +-- Takes: 1.357 ms SELECT * FROM `nucleus_item` WHERE `inumber` < 38; --- Result: object(DibiMySqlResult) rows: 29 --- Takes: 135.842 ms +-- Result: Query error: Table 'test.nucleus_item' doesn't exist +-- Takes: 2.013 ms SELECT * FROM `*nucleus_item` WHERE `inumber` < 38; --- Result: Query error: Can't find file: '.\dgx\*nucleus_item.frm' (errno: 22) --- Takes: 121.454 ms +-- Result: Query error: Can't find file: '.\test\*nucleus_item.frm' (errno: 22) +-- Takes: 75.413 ms diff --git a/examples/logging.php b/examples/logging.php index 702f3a30..77aefe7a 100644 --- a/examples/logging.php +++ b/examples/logging.php @@ -4,6 +4,7 @@ require_once '../dibi/dibi.php'; +// enable logging to this file dibi::$logfile = 'log.sql'; @@ -12,7 +13,7 @@ dibi::connect(array( 'driver' => 'mysql', 'host' => 'localhost', 'username' => 'root', - 'password' => '***', + 'password' => 'xxx', // change to real password! 'database' => 'test', 'charset' => 'utf8', )); diff --git a/examples/metatypes.php b/examples/metatypes.php index 8d95dc2f..a1e602ca 100644 --- a/examples/metatypes.php +++ b/examples/metatypes.php @@ -9,15 +9,16 @@ dibi::connect(array( 'driver' => 'mysql', 'host' => 'localhost', 'username' => 'root', - 'password' => '***', + 'password' => 'xxx', // change to real password! 'database' => 'test', 'charset' => 'utf8', )); -$res = dibi::query('SELECT * FROM [nucleus_item] WHERE [inumber] <> %i', 38); - $res = dibi::query('SELECT * FROM [nucleus_item] WHERE [inumber] <> %i', 38); +if (is_error($res)) + die('SQL error'); + // auto-convert this field to integer $res->setType('inumber', DibiResult::FIELD_INTEGER); diff --git a/examples/sql-builder.php b/examples/sql-builder.php index 2a37d1ca..427358bc 100644 --- a/examples/sql-builder.php +++ b/examples/sql-builder.php @@ -1,3 +1,6 @@ +
  'mysqli',
     'host'     => 'localhost',
     'username' => 'root',
-    'password' => '***',
-    'database' => 'test',
+    'password' => 'xxx',  // change to real password!
     'charset'  => 'utf8',
 ));
 
 
+
 $arr1 = array(1, 2, 3);
 $arr2 = array('one', 'two', 'three');
 $arr3 = array(
-    'a' => 'one',
-    'b' => 'two',
-    'c' => 'three',
+    'col1' => 'one',
+    'col2' => 'two',
+    'col3' => 'three',
 );
 $arr4 = array(
-    'A' => 12,
-    'B' => NULL,
-    'C' => new TDateTime(31542),
-    'D' => 'string',
+    'a' => 12,
+    'b' => NULL,
+    'c%T' => time(),  // modifier 'T' means datetime
+    'd' => 'any string',
 );
+$arr5 = array('RAND()', 'ANY SQL');
 
-dibi::test(
-"
+
+dibi::test("
 SELECT *
-FROM [test]
-WHERE ([test.a] LIKE %T", '1995-03-01', "
-  OR [b1] IN (", $arr1, ")
-  OR [b2] IN (", $arr2, ")
-  OR [b3] IN (%N", $arr3, ")
-  OR [b4] IN %V", $arr4, "
+FROM [db.table]
+WHERE ([test.a] LIKE %D", '1995-03-01', "
+  OR [b1] IN (", $arr1, ")   
+  OR [b2] IN (%s", $arr1, ")
+  OR [b3] IN (", $arr2, ")
+  OR [b4] IN (%n", $arr3, ")
+  OR [b4] IN (%p", $arr5, ")
   AND [c] = 'embedded '' string'
   OR [d]=%d", 10.3, "
   OR [true]=", true, "
@@ -45,4 +50,17 @@ WHERE ([test.a] LIKE %T", '1995-03-01', "
   OR [null]=", NULL, "
 LIMIT 10");
 
+
+// dibi detects INSERT or REPLACE command
+dibi::test("INSERT INTO [test]", $arr4);  
+
+
+// dibi detects UPDATE command
+$n = 123;
+dibi::test("UPDATE [test] SET", $arr4, " WHERE [id]=%n", $n);  
+
+
+// array with modifier %d - means strings
+dibi::test("UPDATE [test] SET%s", $arr4, " WHERE [id]=%n", $n);  
+
 ?>
diff --git a/version.txt b/version.txt
index 4123ea9a..af97075f 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-Dibi Version 0.5b
+Dibi Version 0.6