|
5 | 5 | use Clue\React\NDJson\Decoder; |
6 | 6 | use Evenement\EventEmitter; |
7 | 7 | use React\ChildProcess\Process; |
8 | | -use React\EventLoop\LoopInterface; |
9 | 8 | use React\Promise\Deferred; |
10 | 9 | use React\Promise\PromiseInterface; |
11 | 10 |
|
|
45 | 44 | */ |
46 | 45 | class Database extends EventEmitter |
47 | 46 | { |
48 | | - /** |
49 | | - * Opens a new database connection for the given SQLite database file. |
50 | | - * |
51 | | - * This method returns a promise that will resolve with a `Database` on |
52 | | - * success or will reject with an `Exception` on error. The SQLite extension |
53 | | - * is inherently blocking, so this method will spawn an SQLite worker process |
54 | | - * to run all SQLite commands and queries in a separate process without |
55 | | - * blocking the main process. |
56 | | - * |
57 | | - * ```php |
58 | | - * Database::open($loop, 'users.db')->then(function (Database $db) { |
59 | | - * // database ready |
60 | | - * // $db->query('INSERT INTO users (name) VALUES ("test")'); |
61 | | - * // $db->quit(); |
62 | | - * }, function (Exception $e) { |
63 | | - * echo 'Error: ' . $e->getMessage() . PHP_EOL; |
64 | | - * }); |
65 | | - * ``` |
66 | | - * |
67 | | - * The optional `$flags` parameter is used to determine how to open the |
68 | | - * SQLite database. By default, open uses `SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE`. |
69 | | - * |
70 | | - * ```php |
71 | | - * Database::open($loop, 'users.db', SQLITE3_OPEN_READONLY)->then(function (Database $db) { |
72 | | - * // database ready (read-only) |
73 | | - * // $db->quit(); |
74 | | - * }, function (Exception $e) { |
75 | | - * echo 'Error: ' . $e->getMessage() . PHP_EOL; |
76 | | - * }); |
77 | | - * ``` |
78 | | - * |
79 | | - * @param LoopInterface $loop |
80 | | - * @param string $filename |
81 | | - * @param ?int $flags |
82 | | - * @return PromiseInterface<Database> Resolves with Database instance or rejects with Exception |
83 | | - */ |
84 | | - public static function open(LoopInterface $loop, $filename, $flags = null) |
85 | | - { |
86 | | - $command = 'exec ' . \escapeshellarg(\PHP_BINARY) . ' ' . \escapeshellarg(__DIR__ . '/../res/sqlite-worker.php'); |
87 | | - |
88 | | - // Try to get list of all open FDs (Linux/Mac and others) |
89 | | - $fds = @\scandir('/dev/fd'); |
90 | | - |
91 | | - // Otherwise try temporarily duplicating file descriptors in the range 0-1024 (FD_SETSIZE). |
92 | | - // This is known to work on more exotic platforms and also inside chroot |
93 | | - // environments without /dev/fd. Causes many syscalls, but still rather fast. |
94 | | - // @codeCoverageIgnoreStart |
95 | | - if ($fds === false) { |
96 | | - $fds = array(); |
97 | | - for ($i = 0; $i <= 1024; ++$i) { |
98 | | - $copy = @\fopen('php://fd/' . $i, 'r'); |
99 | | - if ($copy !== false) { |
100 | | - $fds[] = $i; |
101 | | - \fclose($copy); |
102 | | - } |
103 | | - } |
104 | | - } |
105 | | - // @codeCoverageIgnoreEnd |
106 | | - |
107 | | - // launch process with default STDIO pipes |
108 | | - $pipes = array( |
109 | | - array('pipe', 'r'), |
110 | | - array('pipe', 'w'), |
111 | | - array('pipe', 'w') |
112 | | - ); |
113 | | - |
114 | | - // do not inherit open FDs by explicitly overwriting existing FDs with dummy files |
115 | | - // additionally, close all dummy files in the child process again |
116 | | - foreach ($fds as $fd) { |
117 | | - if ($fd > 2) { |
118 | | - $pipes[$fd] = array('file', '/dev/null', 'r'); |
119 | | - $command .= ' ' . $fd . '>&-'; |
120 | | - } |
121 | | - } |
122 | | - |
123 | | - // default `sh` only accepts single-digit FDs, so run in bash if needed |
124 | | - if ($fds && \max($fds) > 9) { |
125 | | - $command = 'exec bash -c ' . \escapeshellarg($command); |
126 | | - } |
127 | | - |
128 | | - $process = new Process($command, null, null, $pipes); |
129 | | - $process->start($loop); |
130 | | - |
131 | | - $db = new Database($process); |
132 | | - $args = array($filename); |
133 | | - if ($flags !== null) { |
134 | | - $args[] = $flags; |
135 | | - } |
136 | | - |
137 | | - return $db->send('open', $args)->then(function () use ($db) { |
138 | | - return $db; |
139 | | - }, function ($e) use ($db) { |
140 | | - $db->close(); |
141 | | - throw $e; |
142 | | - }); |
143 | | - } |
144 | | - |
145 | 47 | private $process; |
146 | 48 | private $pending = array(); |
147 | 49 | private $id = 0; |
148 | 50 | private $closed = false; |
149 | 51 |
|
150 | | - private function __construct(Process $process) |
| 52 | + /** |
| 53 | + * @internal see Factory instead |
| 54 | + * @see Factory |
| 55 | + * @param Process $process |
| 56 | + */ |
| 57 | + public function __construct(Process $process) |
151 | 58 | { |
152 | 59 | $this->process = $process; |
153 | 60 |
|
@@ -363,7 +270,8 @@ public function close() |
363 | 270 | $this->removeAllListeners(); |
364 | 271 | } |
365 | 272 |
|
366 | | - private function send($method, array $params) |
| 273 | + /** @internal */ |
| 274 | + public function send($method, array $params) |
367 | 275 | { |
368 | 276 | if (!$this->process->stdin->isWritable()) { |
369 | 277 | return \React\Promise\reject(new \RuntimeException('Database closed')); |
|
0 commit comments