mirror of
https://github.com/dg/dibi.git
synced 2025-07-09 16:46:29 +02:00
update to 0.6
This commit is contained in:
@ -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('#\*\*.+?\*\*#', '<strong style="color:red">$0</strong>', $sql);
|
||||
|
||||
echo '<pre>', $sql, '</pre>';
|
||||
echo '<pre class="dibi">', $sql, '</pre>';
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,101 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* dibi - Database Abstraction Layer according to dgx
|
||||
* --------------------------------------------------
|
||||
*
|
||||
* This source file is subject to the GNU GPL license.
|
||||
*
|
||||
* @author David Grudl aka -dgx- <dave@dgx.cz>
|
||||
* @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
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -58,6 +58,9 @@ class DibiException extends Exception
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Checks result state
|
||||
*/
|
||||
function is_error($var)
|
||||
{
|
||||
return ($var === FALSE) || ($var instanceof Exception);
|
||||
|
@ -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).'**';
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 "<br>\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 "<br>\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
76
examples/date.type.demo.php
Normal file
76
examples/date.type.demo.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
require_once '../dibi/dibi.php';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Pseudotype for UNIX timestamp representation
|
||||
*/
|
||||
class TDateTime 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['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',
|
||||
));
|
||||
|
||||
|
||||
?>
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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',
|
||||
));
|
||||
|
@ -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);
|
||||
|
@ -1,3 +1,6 @@
|
||||
<style>
|
||||
pre.dibi { padding-bottom: 10px; }
|
||||
</style>
|
||||
<pre>
|
||||
<?php
|
||||
|
||||
@ -9,35 +12,37 @@ dibi::connect(array(
|
||||
'driver' => '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);
|
||||
|
||||
?>
|
||||
|
@ -1 +1 @@
|
||||
Dibi Version 0.5b
|
||||
Dibi Version 0.6
|
||||
|
Reference in New Issue
Block a user