mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
More binding tests and related changes
- Introduced abstract Statement class to hold common methods - Common methods currently consist of a date formatter and type caster - Moved binding tests to a trait for reuse with future drivers
This commit is contained in:
parent
1529fc367a
commit
0c410fcf50
8 changed files with 369 additions and 159 deletions
|
@ -291,16 +291,14 @@ class Database {
|
||||||
throw new Feed\Exception($url, $e);
|
throw new Feed\Exception($url, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->db->prepare("INSERT INTO newssync_feeds(url,title,favicon,source,updated,modified,etag,username,password) values(?,?,?)", "str", "str", "str", "str", "str", "str", "str", "str", "str")->run(
|
$this->db->prepare("INSERT INTO newssync_feeds(url,title,favicon,source,updated,modified,etag,username,password) values(?,?,?,?,?,?,?,?,?)", "str", "str", "str", "str", "datetime", "datetime", "str", "str", "str")->run(
|
||||||
$url,
|
$url,
|
||||||
$feed->title,
|
$feed->title,
|
||||||
// Grab the favicon for the Goodfeed; returns an empty string if it cannot find one.
|
// Grab the favicon for the Goodfeed; returns an empty string if it cannot find one.
|
||||||
(new PicoFeed\Reader\Favicon)->find($url),
|
(new PicoFeed\Reader\Favicon)->find($url),
|
||||||
$feed->siteUrl,
|
$feed->siteUrl,
|
||||||
// Convert the date formats to SQL date format before inserting.
|
$feed->date,
|
||||||
// FIXME: Dates should be formatted transparently by the driver's Statement wrapper, not here
|
$resource->getLastModified(),
|
||||||
$this->driver::formatDate($feed->date),
|
|
||||||
$this->driver::formatDate($resource->getLastModified()),
|
|
||||||
$resource->getEtag(),
|
$resource->getEtag(),
|
||||||
$fetchUser,
|
$fetchUser,
|
||||||
$fetchPassword
|
$fetchPassword
|
||||||
|
|
89
lib/Db/AbstractStatement.php
Normal file
89
lib/Db/AbstractStatement.php
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\NewsSync\Db;
|
||||||
|
|
||||||
|
abstract class AbstractStatement implements Statement {
|
||||||
|
|
||||||
|
abstract function runArray(array $values): Result;
|
||||||
|
abstract static function dateFormat(int $part = self::TS_BOTH): string;
|
||||||
|
|
||||||
|
public function run(...$values): Result {
|
||||||
|
return $this->runArray($values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rebind(...$bindings): bool {
|
||||||
|
return $this->rebindArray($bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rebindArray(array $bindings): bool {
|
||||||
|
$this->types = [];
|
||||||
|
foreach($bindings as $binding) {
|
||||||
|
$binding = trim(strtolower($binding));
|
||||||
|
if(!array_key_exists($binding, self::TYPES)) throw new Exception("paramTypeInvalid", $binding);
|
||||||
|
$this->types[] = self::TYPES[$binding];
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function cast($v, string $t) {
|
||||||
|
switch($t) {
|
||||||
|
case "date":
|
||||||
|
return $this->formatDate($v, self::TS_DATE);
|
||||||
|
case "time":
|
||||||
|
return $this->formatDate($v, self::TS_TIME);
|
||||||
|
case "datetime":
|
||||||
|
return $this->formatDate($v, self::TS_BOTH);
|
||||||
|
case "null":
|
||||||
|
case "integer":
|
||||||
|
case "float":
|
||||||
|
case "binary":
|
||||||
|
case "string":
|
||||||
|
case "boolean":
|
||||||
|
if($t=="binary") $t = "string";
|
||||||
|
$value = $v;
|
||||||
|
try{
|
||||||
|
settype($value, $t);
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
// handle objects
|
||||||
|
$value = $v;
|
||||||
|
if($value instanceof \DateTimeInterface) {
|
||||||
|
$value = $value->getTimestamp();
|
||||||
|
if($t=="string") $value = $this->formatDate($value, self::TS_BOTH);
|
||||||
|
settype($value, $t);
|
||||||
|
} else {
|
||||||
|
$value = null;
|
||||||
|
settype($value, $t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $value;
|
||||||
|
default:
|
||||||
|
throw new Exception("paramTypeUnknown", $type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function formatDate($date, int $part = self::TS_BOTH) {
|
||||||
|
// Force UTC.
|
||||||
|
$timezone = date_default_timezone_get();
|
||||||
|
date_default_timezone_set('UTC');
|
||||||
|
// convert input to a Unix timestamp
|
||||||
|
// FIXME: there are more kinds of date representations
|
||||||
|
if($date instanceof \DateTimeInterface) {
|
||||||
|
$time = $date->getTimestamp();
|
||||||
|
} else if(is_numeric($date)) {
|
||||||
|
$time = (int) $date;
|
||||||
|
} else if($date===null) {
|
||||||
|
return null;
|
||||||
|
} else if(is_string($date)) {
|
||||||
|
$time = strtotime($date);
|
||||||
|
if($time===false) return null;
|
||||||
|
} else if (is_bool($date)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
$time = (int) $date;
|
||||||
|
}
|
||||||
|
// ISO 8601 with space in the middle instead of T.
|
||||||
|
$date = date($this->dateFormat($part), $time);
|
||||||
|
date_default_timezone_set($timezone);
|
||||||
|
return $date;
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,23 +69,4 @@ Trait Common {
|
||||||
public function prepare(string $query, string ...$paramType): Statement {
|
public function prepare(string $query, string ...$paramType): Statement {
|
||||||
return $this->prepareArray($query, $paramType);
|
return $this->prepareArray($query, $paramType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function formatDate($date, int $precision = self::TS_BOTH): string {
|
|
||||||
// Force UTC.
|
|
||||||
$timezone = date_default_timezone_get();
|
|
||||||
date_default_timezone_set('UTC');
|
|
||||||
// convert input to a Unix timestamp
|
|
||||||
// FIXME: there are more kinds of date representations
|
|
||||||
if(is_int($date)) {
|
|
||||||
$time = $date;
|
|
||||||
} else if($date===null) {
|
|
||||||
$time = 0;
|
|
||||||
} else {
|
|
||||||
$time = strtotime($date);
|
|
||||||
}
|
|
||||||
// ISO 8601 with space in the middle instead of T.
|
|
||||||
$date = date(self::TS_FORMAT[$precision], $time);
|
|
||||||
date_default_timezone_set($timezone);
|
|
||||||
return $date;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -3,16 +3,6 @@ declare(strict_types=1);
|
||||||
namespace JKingWeb\NewsSync\Db;
|
namespace JKingWeb\NewsSync\Db;
|
||||||
|
|
||||||
interface Driver {
|
interface Driver {
|
||||||
const TS_TIME = -1;
|
|
||||||
const TS_DATE = 0;
|
|
||||||
const TS_BOTH = 1;
|
|
||||||
|
|
||||||
const TS_FORMAT = [
|
|
||||||
self::TS_TIME => 'h:i:sP',
|
|
||||||
self::TS_DATE => 'Y-m-d',
|
|
||||||
self::TS_BOTH => 'Y-m-d h:i:sP',
|
|
||||||
];
|
|
||||||
|
|
||||||
function __construct(\JKingWeb\NewsSync\RuntimeData $data, bool $install = false);
|
function __construct(\JKingWeb\NewsSync\RuntimeData $data, bool $install = false);
|
||||||
// returns a human-friendly name for the driver (for display in installer, for example)
|
// returns a human-friendly name for the driver (for display in installer, for example)
|
||||||
static function driverName(): string;
|
static function driverName(): string;
|
||||||
|
|
|
@ -3,7 +3,9 @@ declare(strict_types=1);
|
||||||
namespace JKingWeb\NewsSync\Db;
|
namespace JKingWeb\NewsSync\Db;
|
||||||
|
|
||||||
interface Statement {
|
interface Statement {
|
||||||
|
const TS_TIME = -1;
|
||||||
|
const TS_DATE = 0;
|
||||||
|
const TS_BOTH = 1;
|
||||||
const TYPES = [
|
const TYPES = [
|
||||||
"null" => "null",
|
"null" => "null",
|
||||||
"nil" => "null",
|
"nil" => "null",
|
||||||
|
@ -20,14 +22,16 @@ interface Statement {
|
||||||
"blob" => "binary",
|
"blob" => "binary",
|
||||||
"bin" => "binary",
|
"bin" => "binary",
|
||||||
"binary" => "binary",
|
"binary" => "binary",
|
||||||
"text" => "text",
|
"text" => "string",
|
||||||
"string" => "text",
|
"string" => "string",
|
||||||
"str" => "text",
|
"str" => "string",
|
||||||
"bool" => "boolean",
|
"bool" => "boolean",
|
||||||
"boolean" => "boolean",
|
"boolean" => "boolean",
|
||||||
"bit" => "boolean",
|
"bit" => "boolean",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
static function dateFormat(int $part = self::TS_BOTH): string;
|
||||||
|
|
||||||
function run(...$values): Result;
|
function run(...$values): Result;
|
||||||
function runArray(array $values): Result;
|
function runArray(array $values): Result;
|
||||||
function rebind(...$bindings): bool;
|
function rebind(...$bindings): bool;
|
||||||
|
|
|
@ -1,9 +1,20 @@
|
||||||
<?php
|
<?php
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
namespace JKingWeb\NewsSync\Db;
|
namespace JKingWeb\NewsSync\Db;
|
||||||
use JKingWeb\NewsSync\Db\DriverSQLite3 as Driver;
|
|
||||||
|
|
||||||
class StatementSQLite3 implements Statement {
|
class StatementSQLite3 extends AbstractStatement {
|
||||||
|
const BINDINGS = [
|
||||||
|
"null" => \SQLITE3_NULL,
|
||||||
|
"integer" => \SQLITE3_INTEGER,
|
||||||
|
"float" => \SQLITE3_FLOAT,
|
||||||
|
"date" => \SQLITE3_TEXT,
|
||||||
|
"time" => \SQLITE3_TEXT,
|
||||||
|
"datetime" => \SQLITE3_TEXT,
|
||||||
|
"binary" => \SQLITE3_BLOB,
|
||||||
|
"string" => \SQLITE3_TEXT,
|
||||||
|
"boolean" => \SQLITE3_INTEGER,
|
||||||
|
];
|
||||||
|
|
||||||
protected $db;
|
protected $db;
|
||||||
protected $st;
|
protected $st;
|
||||||
protected $types;
|
protected $types;
|
||||||
|
@ -19,8 +30,12 @@ class StatementSQLite3 implements Statement {
|
||||||
unset($this->st);
|
unset($this->st);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(...$values): Result {
|
public static function dateFormat(int $part = self::TS_BOTH): string {
|
||||||
return $this->runArray($values);
|
return ([
|
||||||
|
self::TS_TIME => 'h:i:sP',
|
||||||
|
self::TS_DATE => 'Y-m-d',
|
||||||
|
self::TS_BOTH => 'Y-m-d h:i:sP',
|
||||||
|
])[$part];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function runArray(array $values = null): Result {
|
public function runArray(array $values = null): Result {
|
||||||
|
@ -28,80 +43,21 @@ class StatementSQLite3 implements Statement {
|
||||||
$l = sizeof($values);
|
$l = sizeof($values);
|
||||||
for($a = 0; $a < $l; $a++) {
|
for($a = 0; $a < $l; $a++) {
|
||||||
// find the right SQLite binding type for the value/specified type
|
// find the right SQLite binding type for the value/specified type
|
||||||
$type = null;
|
|
||||||
if($values[$a]===null) {
|
if($values[$a]===null) {
|
||||||
$type = \SQLITE3_NULL;
|
$type = \SQLITE3_NULL;
|
||||||
} else if(array_key_exists($a,$this->types)) {
|
} else if(array_key_exists($a,$this->types)) {
|
||||||
$type = $this->translateType($this->types[$a]);
|
if(!array_key_exists($this->types[$a], self::BINDINGS)) throw new Exception("paramTypeUnknown", $this->types[$a]);
|
||||||
|
$type = self::BINDINGS[$this->types[$a]];
|
||||||
} else {
|
} else {
|
||||||
$type = \SQLITE3_TEXT;
|
$type = \SQLITE3_TEXT;
|
||||||
}
|
}
|
||||||
// cast values if necessary
|
// cast value if necessary
|
||||||
switch($this->types[$a]) {
|
$value = $this->cast($values[$a], $this->types[$a]);
|
||||||
case "null":
|
// re-adjust for null casts
|
||||||
$value = null; break;
|
if($value===null) $type = \SQLITE3_NULL;
|
||||||
case "integer":
|
// perform binding
|
||||||
$value = (int) $values[$a]; break;
|
$this->st->bindParam($a+1, $value, $type);
|
||||||
case "float":
|
|
||||||
$value = (float) $values[$a]; break;
|
|
||||||
case "date":
|
|
||||||
$value = Driver::formatDate($values[$a], Driver::TS_DATE); break;
|
|
||||||
case "time":
|
|
||||||
$value = Driver::formatDate($values[$a], Driver::TS_TIME); break;
|
|
||||||
case "datetime":
|
|
||||||
$value = Driver::formatDate($values[$a], Driver::TS_BOTH); break;
|
|
||||||
case "binary":
|
|
||||||
$value = (string) $values[$a]; break;
|
|
||||||
case "text":
|
|
||||||
$value = $values[$a]; break;
|
|
||||||
case "boolean":
|
|
||||||
$value = (bool) $values[$a]; break;
|
|
||||||
default:
|
|
||||||
throw new Exception("paramTypeUnknown", $type);
|
|
||||||
}
|
|
||||||
if($type===null) {
|
|
||||||
$this->st->bindParam($a+1, $value);
|
|
||||||
} else {
|
|
||||||
$this->st->bindParam($a+1, $value, $type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return new ResultSQLite3($this->st->execute(), $this->db->changes(), $this);
|
return new ResultSQLite3($this->st->execute(), $this->db->changes(), $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rebind(...$bindings): bool {
|
|
||||||
return $this->rebindArray($bindings);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function translateType(string $type) {
|
|
||||||
switch($type) {
|
|
||||||
case "null":
|
|
||||||
return \SQLITE3_NULL;
|
|
||||||
case "integer":
|
|
||||||
return \SQLITE3_INTEGER;
|
|
||||||
case "float":
|
|
||||||
return \SQLITE3_FLOAT;
|
|
||||||
case "date":
|
|
||||||
case "time":
|
|
||||||
case "datetime":
|
|
||||||
return \SQLITE3_TEXT;
|
|
||||||
case "binary":
|
|
||||||
return \SQLITE3_BLOB;
|
|
||||||
case "text":
|
|
||||||
return \SQLITE3_TEXT;
|
|
||||||
case "boolean":
|
|
||||||
return \SQLITE3_INTEGER;
|
|
||||||
default:
|
|
||||||
throw new Db\Exception("paramTypeUnknown", $binding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rebindArray(array $bindings): bool {
|
|
||||||
$this->types = [];
|
|
||||||
foreach($bindings as $binding) {
|
|
||||||
$binding = trim(strtolower($binding));
|
|
||||||
if(!array_key_exists($binding, self::TYPES)) throw new Db\Exception("paramTypeInvalid", $binding);
|
|
||||||
$this->types[] = self::TYPES[$binding];
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -4,10 +4,11 @@ namespace JKingWeb\NewsSync;
|
||||||
|
|
||||||
|
|
||||||
class TestDbStatementSQLite3 extends \PHPUnit\Framework\TestCase {
|
class TestDbStatementSQLite3 extends \PHPUnit\Framework\TestCase {
|
||||||
use Test\Tools;
|
use Test\Tools, Test\Db\BindingTests;
|
||||||
|
|
||||||
protected $c;
|
protected $c;
|
||||||
protected $s;
|
protected $s;
|
||||||
|
static protected $imp = Db\StatementSQLite3::class;
|
||||||
|
|
||||||
function setUp() {
|
function setUp() {
|
||||||
date_default_timezone_set("UTC");
|
date_default_timezone_set("UTC");
|
||||||
|
@ -28,53 +29,4 @@ class TestDbStatementSQLite3 extends \PHPUnit\Framework\TestCase {
|
||||||
function testConstructStatement() {
|
function testConstructStatement() {
|
||||||
$this->assertInstanceOf(Db\StatementSQLite3::class, new Db\StatementSQLite3($this->c, $this->s));
|
$this->assertInstanceOf(Db\StatementSQLite3::class, new Db\StatementSQLite3($this->c, $this->s));
|
||||||
}
|
}
|
||||||
|
|
||||||
function testBindMissingValue() {
|
|
||||||
$s = new Db\StatementSQLite3($this->c, $this->s);
|
|
||||||
$val = $s->runArray()->get()['value'];
|
|
||||||
$this->assertSame(null, $val);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testBindNull() {
|
|
||||||
$exp = [
|
|
||||||
"null" => null,
|
|
||||||
"integer" => null,
|
|
||||||
"float" => null,
|
|
||||||
"date" => null,
|
|
||||||
"time" => null,
|
|
||||||
"datetime" => null,
|
|
||||||
"binary" => null,
|
|
||||||
"text" => null,
|
|
||||||
"boolean" => null,
|
|
||||||
];
|
|
||||||
$s = new Db\StatementSQLite3($this->c, $this->s);
|
|
||||||
$types = array_unique(Db\Statement::TYPES);
|
|
||||||
foreach($types as $type) {
|
|
||||||
$s->rebindArray([$type]);
|
|
||||||
$val = $s->runArray([null])->get()['value'];
|
|
||||||
$this->assertSame($exp[$type], $val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testBindInteger() {
|
|
||||||
$exp = [
|
|
||||||
"null" => null,
|
|
||||||
"integer" => 2112,
|
|
||||||
"float" => 2112.0,
|
|
||||||
"date" => date('Y-m-d', 2112),
|
|
||||||
"time" => date('h:i:sP', 2112),
|
|
||||||
"datetime" => date('Y-m-d h:i:sP', 2112),
|
|
||||||
"binary" => "2112",
|
|
||||||
"text" => "2112",
|
|
||||||
"boolean" => 1,
|
|
||||||
];
|
|
||||||
$s = new Db\StatementSQLite3($this->c, $this->s);
|
|
||||||
$types = array_unique(Db\Statement::TYPES);
|
|
||||||
foreach($types as $type) {
|
|
||||||
$s->rebindArray([$type]);
|
|
||||||
$val = $s->runArray([2112])->get()['value'];
|
|
||||||
$this->assertSame($exp[$type], $val, "Type $type failed comparison.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
240
tests/lib/Db/BindingTests.php
Normal file
240
tests/lib/Db/BindingTests.php
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\NewsSync\Test\Db;
|
||||||
|
use JKingWeb\NewsSync\Db\Statement;
|
||||||
|
use JKingWeb\NewsSync\Db\Driver;
|
||||||
|
|
||||||
|
trait BindingTests {
|
||||||
|
|
||||||
|
function testBindMissingValue() {
|
||||||
|
$s = new self::$imp($this->c, $this->s);
|
||||||
|
$val = $s->runArray()->get()['value'];
|
||||||
|
$this->assertSame(null, $val);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindNull() {
|
||||||
|
$input = null;
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => null,
|
||||||
|
"float" => null,
|
||||||
|
"date" => null,
|
||||||
|
"time" => null,
|
||||||
|
"datetime" => null,
|
||||||
|
"binary" => null,
|
||||||
|
"string" => null,
|
||||||
|
"boolean" => null,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindTrue() {
|
||||||
|
$input = true;
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => 1,
|
||||||
|
"float" => 1.0,
|
||||||
|
"date" => null,
|
||||||
|
"time" => null,
|
||||||
|
"datetime" => null,
|
||||||
|
"binary" => "1",
|
||||||
|
"string" => "1",
|
||||||
|
"boolean" => 1,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindFalse() {
|
||||||
|
$input = false;
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => 0,
|
||||||
|
"float" => 0.0,
|
||||||
|
"date" => null,
|
||||||
|
"time" => null,
|
||||||
|
"datetime" => null,
|
||||||
|
"binary" => "",
|
||||||
|
"string" => "",
|
||||||
|
"boolean" => 0,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindInteger() {
|
||||||
|
$input = 2112;
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => 2112,
|
||||||
|
"float" => 2112.0,
|
||||||
|
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 2112),
|
||||||
|
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 2112),
|
||||||
|
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 2112),
|
||||||
|
"binary" => "2112",
|
||||||
|
"string" => "2112",
|
||||||
|
"boolean" => 1,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindIntegerZero() {
|
||||||
|
$input = 0;
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => 0,
|
||||||
|
"float" => 0.0,
|
||||||
|
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 0),
|
||||||
|
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 0),
|
||||||
|
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 0),
|
||||||
|
"binary" => "0",
|
||||||
|
"string" => "0",
|
||||||
|
"boolean" => 0,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindFloat() {
|
||||||
|
$input = 2112.0;
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => 2112,
|
||||||
|
"float" => 2112.0,
|
||||||
|
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 2112),
|
||||||
|
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 2112),
|
||||||
|
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 2112),
|
||||||
|
"binary" => "2112",
|
||||||
|
"string" => "2112",
|
||||||
|
"boolean" => 1,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindFloatZero() {
|
||||||
|
$input = 0.0;
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => 0,
|
||||||
|
"float" => 0.0,
|
||||||
|
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), 0),
|
||||||
|
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), 0),
|
||||||
|
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), 0),
|
||||||
|
"binary" => "0",
|
||||||
|
"string" => "0",
|
||||||
|
"boolean" => 0,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindAsciiString() {
|
||||||
|
$input = "Random string";
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => 0,
|
||||||
|
"float" => 0.0,
|
||||||
|
"date" => null,
|
||||||
|
"time" => null,
|
||||||
|
"datetime" => null,
|
||||||
|
"binary" => $input,
|
||||||
|
"string" => $input,
|
||||||
|
"boolean" => 1,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindUtf8String() {
|
||||||
|
$input = "é";
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => 0,
|
||||||
|
"float" => 0.0,
|
||||||
|
"date" => null,
|
||||||
|
"time" => null,
|
||||||
|
"datetime" => null,
|
||||||
|
"binary" => $input,
|
||||||
|
"string" => $input,
|
||||||
|
"boolean" => 1,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindBinaryString() {
|
||||||
|
// FIXME: This test may be unreliable; SQLite happily stores invalid UTF-8 text as bytes untouched, but other engines probably don't do this
|
||||||
|
$input = chr(233);
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => 0,
|
||||||
|
"float" => 0.0,
|
||||||
|
"date" => null,
|
||||||
|
"time" => null,
|
||||||
|
"datetime" => null,
|
||||||
|
"binary" => $input,
|
||||||
|
"string" => $input,
|
||||||
|
"boolean" => 1,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindIso8601DateString() {
|
||||||
|
$input = "2017-01-09T13:11:17";
|
||||||
|
$time = strtotime($input);
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => 2017,
|
||||||
|
"float" => 2017.0,
|
||||||
|
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), $time),
|
||||||
|
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), $time),
|
||||||
|
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
|
||||||
|
"binary" => $input,
|
||||||
|
"string" => $input,
|
||||||
|
"boolean" => 1,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindArbitraryDateString() {
|
||||||
|
$input = "Today";
|
||||||
|
$time = strtotime($input);
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => 0,
|
||||||
|
"float" => 0.0,
|
||||||
|
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), $time),
|
||||||
|
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), $time),
|
||||||
|
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
|
||||||
|
"binary" => $input,
|
||||||
|
"string" => $input,
|
||||||
|
"boolean" => 1,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindMutableDateObject($class = '\DateTime') {
|
||||||
|
$input = new $class("Noon Today");
|
||||||
|
$time = $input->getTimestamp();
|
||||||
|
$exp = [
|
||||||
|
"null" => null,
|
||||||
|
"integer" => $time,
|
||||||
|
"float" => (float) $time,
|
||||||
|
"date" => date(self::$imp::dateFormat(Statement::TS_DATE), $time),
|
||||||
|
"time" => date(self::$imp::dateFormat(Statement::TS_TIME), $time),
|
||||||
|
"datetime" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
|
||||||
|
"binary" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
|
||||||
|
"string" => date(self::$imp::dateFormat(Statement::TS_BOTH), $time),
|
||||||
|
"boolean" => 1,
|
||||||
|
];
|
||||||
|
$this->checkBinding($input, $exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBindImmutableDateObject() {
|
||||||
|
$this->testBindMutableDateObject('\DateTimeImmutable');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkBinding($input, array $expectations) {
|
||||||
|
$s = new self::$imp($this->c, $this->s);
|
||||||
|
$types = array_unique(Statement::TYPES);
|
||||||
|
foreach($types as $type) {
|
||||||
|
$s->rebindArray([$type]);
|
||||||
|
$val = $s->runArray([$input])->get()['value'];
|
||||||
|
$this->assertSame($expectations[$type], $val, "Type $type failed comparison.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue