Skip to content

Commit ff44b98

Browse files
committed
Split DatabaseInterface and ProcessIoDatabase implementation
This is done to achieve better separation of concerns in preparation for creating different database communication implementations for Windows support in the future.
1 parent 52273c7 commit ff44b98

File tree

8 files changed

+201
-174
lines changed

8 files changed

+201
-174
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ built on top of [ReactPHP](https://reactphp.org/).
99
* [Usage](#usage)
1010
* [Factory](#factory)
1111
* [open()](#open)
12-
* [Database](#database)
12+
* [DatabaseInterface](#databaseinterface)
1313
* [exec()](#exec)
1414
* [query()](#query)
1515
* [quit()](#quit)
@@ -33,7 +33,7 @@ $factory = new Clue\React\SQLite\Factory($loop);
3333

3434
$name = 'Alice';
3535
$factory->open('users.db')->then(
36-
function (Clue\React\SQLite\Database $db) use ($name) {
36+
function (Clue\React\SQLite\DatabaseInterface $db) use ($name) {
3737
$db->exec('CREATE TABLE IF NOT EXISTS foo (id INTEGER PRIMARY KEY AUTOINCREMENT, bar STRING)');
3838

3939
$db->query('INSERT INTO foo (bar) VALUES (?)', array($name))->then(
@@ -58,7 +58,7 @@ See also the [examples](examples).
5858

5959
### Factory
6060

61-
The `Factory` is responsible for opening your [`Database`](#database) instance.
61+
The `Factory` is responsible for opening your [`DatabaseInterface`](#databaseinterface) instance.
6262
It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage).
6363

6464
```php
@@ -71,14 +71,14 @@ $factory = new Clue\React\SQLite\Factory($loop);
7171
The `open(string $filename, int $flags = null): PromiseInterface<Database>` method can be used to
7272
open a new database connection for the given SQLite database file.
7373

74-
This method returns a promise that will resolve with a `Database` on
74+
This method returns a promise that will resolve with a `DatabaseInterface` on
7575
success or will reject with an `Exception` on error. The SQLite extension
7676
is inherently blocking, so this method will spawn an SQLite worker process
7777
to run all SQLite commands and queries in a separate process without
7878
blocking the main process.
7979

8080
```php
81-
$factory->open('users.db')->then(function (Database $db) {
81+
$factory->open('users.db')->then(function (DatabaseInterface $db) {
8282
// database ready
8383
// $db->query('INSERT INTO users (name) VALUES ("test")');
8484
// $db->quit();
@@ -91,17 +91,17 @@ The optional `$flags` parameter is used to determine how to open the
9191
SQLite database. By default, open uses `SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE`.
9292

9393
```php
94-
$factory->open('users.db', SQLITE3_OPEN_READONLY)->then(function (Database $db) {
94+
$factory->open('users.db', SQLITE3_OPEN_READONLY)->then(function (DatabaseInterface $db) {
9595
// database ready (read-only)
9696
// $db->quit();
9797
}, function (Exception $e) {
9898
echo 'Error: ' . $e->getMessage() . PHP_EOL;
9999
});
100100
```
101101

102-
### Database
102+
### DatabaseInterface
103103

104-
The `Database` class represents a connection that is responsible for
104+
The `DatabaseInterface` represents a connection that is responsible for
105105
comunicating with your SQLite database wrapper, managing the connection state
106106
and sending your database queries.
107107

examples/insert.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
use Clue\React\SQLite\Database;
3+
use Clue\React\SQLite\DatabaseInterface;
44
use Clue\React\SQLite\Factory;
55
use Clue\React\SQLite\Result;
66

@@ -10,7 +10,7 @@
1010
$factory = new Factory($loop);
1111

1212
$n = isset($argv[1]) ? $argv[1] : 1;
13-
$factory->open('test.db')->then(function (Database $db) use ($n) {
13+
$factory->open('test.db')->then(function (DatabaseInterface $db) use ($n) {
1414
$db->exec('CREATE TABLE IF NOT EXISTS foo (id INTEGER PRIMARY KEY AUTOINCREMENT, bar STRING)');
1515

1616
for ($i = 0; $i < $n; ++$i) {

examples/search.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22

3-
use Clue\React\SQLite\Database;
3+
use Clue\React\SQLite\DatabaseInterface;
44
use Clue\React\SQLite\Factory;
55
use Clue\React\SQLite\Result;
66

@@ -10,7 +10,7 @@
1010
$factory = new Factory($loop);
1111

1212
$search = isset($argv[1]) ? $argv[1] : 'foo';
13-
$factory->open('test.db')->then(function (Database $db) use ($search){
13+
$factory->open('test.db')->then(function (DatabaseInterface $db) use ($search){
1414
$db->query('SELECT * FROM foo WHERE bar LIKE ?', ['%' . $search . '%'])->then(function (Result $result) {
1515
echo 'Found ' . count($result->rows) . ' rows: ' . PHP_EOL;
1616
echo implode("\t", $result->columns) . PHP_EOL;

src/Database.php renamed to src/DatabaseInterface.php

Lines changed: 7 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,11 @@
22

33
namespace Clue\React\SQLite;
44

5-
use Clue\React\NDJson\Decoder;
6-
use Evenement\EventEmitter;
7-
use React\ChildProcess\Process;
8-
use React\Promise\Deferred;
5+
use Evenement\EventEmitterInterface;
96
use React\Promise\PromiseInterface;
107

118
/**
12-
* The `Database` class represents a connection that is responsible for
9+
* The `DatabaseInterface` represents a connection that is responsible for
1310
* communicating with your SQLite database wrapper, managing the connection state
1411
* and sending your database queries.
1512
*
@@ -42,52 +39,8 @@
4239
*
4340
* See also the [`close()`](#close) method.
4441
*/
45-
class Database extends EventEmitter
42+
interface DatabaseInterface extends EventEmitterInterface
4643
{
47-
private $process;
48-
private $pending = array();
49-
private $id = 0;
50-
private $closed = false;
51-
52-
/**
53-
* @internal see Factory instead
54-
* @see Factory
55-
* @param Process $process
56-
*/
57-
public function __construct(Process $process)
58-
{
59-
$this->process = $process;
60-
61-
$in = new Decoder($process->stdout, true, 512, 0, 16 * 1024 * 1024);
62-
$in->on('data', function ($data) use ($in) {
63-
if (!isset($data['id']) || !isset($this->pending[$data['id']])) {
64-
$this->emit('error', array(new \RuntimeException('Invalid message received')));
65-
$in->close();
66-
return;
67-
}
68-
69-
/* @var Deferred $deferred */
70-
$deferred = $this->pending[$data['id']];
71-
unset($this->pending[$data['id']]);
72-
73-
if (isset($data['error'])) {
74-
$deferred->reject(new \RuntimeException(
75-
isset($data['error']['message']) ? $data['error']['message'] : 'Unknown error',
76-
isset($data['error']['code']) ? $data['error']['code'] : 0
77-
));
78-
} else {
79-
$deferred->resolve($data['result']);
80-
}
81-
});
82-
$in->on('error', function (\Exception $e) {
83-
$this->emit('error', array($e));
84-
$this->close();
85-
});
86-
$in->on('close', function () {
87-
$this->close();
88-
});
89-
}
90-
9144
/**
9245
* Executes an async query.
9346
*
@@ -129,16 +82,7 @@ public function __construct(Process $process)
12982
* @param string $sql SQL statement
13083
* @return PromiseInterface<Result> Resolves with Result instance or rejects with Exception
13184
*/
132-
public function exec($sql)
133-
{
134-
return $this->send('exec', array($sql))->then(function ($data) {
135-
$result = new Result();
136-
$result->changed = $data['changed'];
137-
$result->insertId = $data['insertId'];
138-
139-
return $result;
140-
});
141-
}
85+
public function exec($sql);
14286

14387
/**
14488
* Performs an async query.
@@ -196,18 +140,7 @@ public function exec($sql)
196140
* @param array $params Parameters which should be bound to query
197141
* @return PromiseInterface<Result> Resolves with Result instance or rejects with Exception
198142
*/
199-
public function query($sql, array $params = array())
200-
{
201-
return $this->send('query', array($sql, $params))->then(function ($data) {
202-
$result = new Result();
203-
$result->changed = $data['changed'];
204-
$result->insertId = $data['insertId'];
205-
$result->columns = $data['columns'];
206-
$result->rows = $data['rows'];
207-
208-
return $result;
209-
});
210-
}
143+
public function query($sql, array $params = array());
211144

212145
/**
213146
* Quits (soft-close) the connection.
@@ -225,14 +158,7 @@ public function query($sql, array $params = array())
225158
*
226159
* @return PromiseInterface<void> Resolves (with void) or rejects with Exception
227160
*/
228-
public function quit()
229-
{
230-
$promise = $this->send('close', array());
231-
232-
$this->process->stdin->end();
233-
234-
return $promise;
235-
}
161+
public function quit();
236162

237163
/**
238164
* Force-close the connection.
@@ -249,44 +175,5 @@ public function quit()
249175
*
250176
* @return void
251177
*/
252-
public function close()
253-
{
254-
if ($this->closed) {
255-
return;
256-
}
257-
258-
$this->closed = true;
259-
foreach ($this->process->pipes as $pipe) {
260-
$pipe->close();
261-
}
262-
$this->process->terminate();
263-
264-
foreach ($this->pending as $one) {
265-
$one->reject(new \RuntimeException('Database closed'));
266-
}
267-
$this->pending = array();
268-
269-
$this->emit('close');
270-
$this->removeAllListeners();
271-
}
272-
273-
/** @internal */
274-
public function send($method, array $params)
275-
{
276-
if (!$this->process->stdin->isWritable()) {
277-
return \React\Promise\reject(new \RuntimeException('Database closed'));
278-
}
279-
280-
$id = ++$this->id;
281-
$this->process->stdin->write(\json_encode(array(
282-
'id' => $id,
283-
'method' => $method,
284-
'params' => $params
285-
), \JSON_UNESCAPED_SLASHES) . "\n");
286-
287-
$deferred = new Deferred();
288-
$this->pending[$id] = $deferred;
289-
290-
return $deferred->promise();
291-
}
178+
public function close();
292179
}

src/Factory.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44

55
use React\ChildProcess\Process;
66
use React\EventLoop\LoopInterface;
7+
use Clue\React\SQLite\Io\ProcessIoDatabase;
78

89
class Factory
910
{
1011
private $loop;
1112

1213
/**
13-
* The `Factory` is responsible for opening your [`Database`](#database) instance.
14+
* The `Factory` is responsible for opening your [`DatabaseInterface`](#databaseinterface) instance.
1415
* It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage).
1516
*
1617
* ```php
@@ -28,14 +29,14 @@ public function __construct(LoopInterface $loop)
2829
/**
2930
* Opens a new database connection for the given SQLite database file.
3031
*
31-
* This method returns a promise that will resolve with a `Database` on
32+
* This method returns a promise that will resolve with a `DatabaseInterface` on
3233
* success or will reject with an `Exception` on error. The SQLite extension
3334
* is inherently blocking, so this method will spawn an SQLite worker process
3435
* to run all SQLite commands and queries in a separate process without
3536
* blocking the main process.
3637
*
3738
* ```php
38-
* $factory->open('users.db')->then(function (Database $db) {
39+
* $factory->open('users.db')->then(function (DatabaseInterface $db) {
3940
* // database ready
4041
* // $db->query('INSERT INTO users (name) VALUES ("test")');
4142
* // $db->quit();
@@ -48,7 +49,7 @@ public function __construct(LoopInterface $loop)
4849
* SQLite database. By default, open uses `SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE`.
4950
*
5051
* ```php
51-
* $factory->open('users.db', SQLITE3_OPEN_READONLY)->then(function (Database $db) {
52+
* $factory->open('users.db', SQLITE3_OPEN_READONLY)->then(function (DatabaseInterface $db) {
5253
* // database ready (read-only)
5354
* // $db->quit();
5455
* }, function (Exception $e) {
@@ -58,7 +59,7 @@ public function __construct(LoopInterface $loop)
5859
*
5960
* @param string $filename
6061
* @param ?int $flags
61-
* @return PromiseInterface<Database> Resolves with Database instance or rejects with Exception
62+
* @return PromiseInterface<DatabaseInterface> Resolves with DatabaseInterface instance or rejects with Exception
6263
*/
6364
public function open($filename, $flags = null)
6465
{
@@ -107,7 +108,7 @@ public function open($filename, $flags = null)
107108
$process = new Process($command, null, null, $pipes);
108109
$process->start($this->loop);
109110

110-
$db = new Database($process);
111+
$db = new ProcessIoDatabase($process);
111112
$args = array($filename);
112113
if ($flags !== null) {
113114
$args[] = $flags;

0 commit comments

Comments
 (0)