Skip to content

Commit

Permalink
First attempt at PreparedStatements
Browse files Browse the repository at this point in the history
  • Loading branch information
sbuberl committed Feb 23, 2019
1 parent 32b8dd8 commit 76c2383
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/Environment.php
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,18 @@ public function rollback()
}
}

public function prepare($query)
{
if(preg_match_all("/\?(?=[^']*(?:'[^']*'[^']*)*$)/", $query, $matches, PREG_OFFSET_CAPTURE)) {
$params = [];
foreach($matches as $match) {
$params[] = $match[0][1];
}
return new Query($this, $query, $params);
}
return false;
}

public function query($query)
{
$query = trim($query);
Expand Down
56 changes: 56 additions & 0 deletions src/Query.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace FSQL;

use FSQL\Environment;

class Query
{
private $environment;
private $query;
private $positions;
private $params;

public function __construct(Environment $environment, $query, array $positions)
{
$this->environment = $environment;
$this->query = $query;
$this->positions = $positions;
}

public function bind_param($types, ...$params)
{
$this->params = [];
$param = current($params);

$length = strlen($types);
for($i = 0; $i < $length; ++$i, $param = next($params)) {
$type = $types[$i];
switch($type) {
case 'i':
$this->params[] = (int) $param;
break;
case 'd':
$this->params[] = (float) $param;
break;
case 's':
case 'o':
$this->params[] = "'". addslashes((string) $param) . "'";
break;
}
}
return true;
}

public function execute()
{
$that = $this;
$count = 0;
$newQuery = preg_replace_callback(
"/\?(?=[^']*(?:'[^']*'[^']*)*$)/",
function($match) use ($that, &$count) { return $that->params[$count++]; },
$this->query
);
return $this->environment->query($newQuery);
}
}
77 changes: 77 additions & 0 deletions tests/EnvironmentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,36 @@

require_once __DIR__.'/BaseTest.php';

use FSQL\Database\CachedTable;
use FSQL\Environment;
use FSQL\ResultSet;

class EnvironmentTest extends BaseTest
{
private $fsql;

private static $columns = [
'personId' => ['type' => 'i', 'auto' => 0, 'default' => 0, 'key' => 'n', 'null' => 1, 'restraint' => []],
'firstName' => ['type' => 's', 'auto' => 0, 'default' => '', 'key' => 'n', 'null' => 1, 'restraint' => []],
'lastName' => ['type' => 's', 'auto' => 0, 'default' => '', 'key' => 'n', 'null' => 1, 'restraint' => []],
'city' => ['type' => 's', 'auto' => 0, 'default' => '', 'key' => 'n', 'null' => 1, 'restraint' => []],
];

private static $entries = [
[1, 'bill', 'smith', 'chicago'],
[2, 'jon', 'doe', 'baltimore'],
[3, 'mary', 'shelley', 'seattle'],
[4, 'stephen', 'king', 'derry'],
[5, 'bart', 'simpson', 'springfield'],
[6, 'jane', 'doe', 'seattle'],
[7, 'bram', 'stoker', 'new york'],
[8, 'douglas', 'adams', 'london'],
[9, 'bill', 'johnson', 'derry'],
[10, 'jon', 'doe', 'new york'],
[11, 'homer', null, 'boston'],
[12, null, 'king', 'tokyo'],
];

public function setUp()
{
parent::setUp();
Expand Down Expand Up @@ -86,4 +110,57 @@ public function testSelectSchema()
$this->assertEquals(trim($this->fsql->error()), "Schema {$dbName}.{$fakeSchema} does not exist");
$this->assertEquals($goodSchema, $this->fsql->current_schema()->name());
}

public function testPrepare()
{
$dbName = 'db1';
$passed = $this->fsql->define_db($dbName, parent::$tempDir);
$this->fsql->select_db($dbName);

$table = CachedTable::create($this->fsql->current_schema(), 'customers', self::$columns);
$cursor = $table->getWriteCursor();
foreach (self::$entries as $entry) {
$cursor->appendRow($entry);
}
$table->commit();

$expected = [
['stephen', 'king', 'derry'],
['bart', 'simpson', 'springfield'],
[null, 'king', 'tokyo'],
];

$stmt = $this->fsql->prepare("SELECT firstName, lastName, city FROM customers WHERE personId = ? OR lastName = ?");
$this->assertTrue($stmt !== false);
$stmt->bind_param('is', '5', 'king');
$result = $stmt->execute();
$this->assertTrue($result !== false);


$results = $this->fsql->fetch_all($result, ResultSet::FETCH_NUM);
$this->assertEquals($expected, $results);
}

public function testPrepareInject()
{
$dbName = 'db1';
$passed = $this->fsql->define_db($dbName, parent::$tempDir);
$this->fsql->select_db($dbName);

$table = CachedTable::create($this->fsql->current_schema(), 'customers', self::$columns);
$cursor = $table->getWriteCursor();
foreach (self::$entries as $entry) {
$cursor->appendRow($entry);
}
$table->commit();

$stmt = $this->fsql->prepare("SELECT firstName, lastName, city FROM customers WHERE lastName = ?");
$this->assertTrue($stmt !== false);
$stmt->bind_param('s', 'doe;delete from customers');
$result = $stmt->execute();
$this->assertTrue($result !== false);

$results = $this->fsql->fetch_all($result, ResultSet::FETCH_NUM);
$this->assertSame([], $results);
}
}

0 comments on commit 76c2383

Please sign in to comment.