Skip to content

Commit 156a3c3

Browse files
authored
Merge pull request #141 from clue-labs/errors
2 parents fe3d492 + 705737b commit 156a3c3

File tree

11 files changed

+339
-75
lines changed

11 files changed

+339
-75
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@ This example runs a simple `SELECT` query and dumps all the records from a `book
3333

3434
```php
3535
$factory = new React\MySQL\Factory();
36-
37-
$uri = 'test:test@localhost/test';
38-
$connection = $factory->createLazyConnection($uri);
36+
$connection = $factory->createLazyConnection('user:pass@localhost/bookstore');
3937

4038
$connection->query('SELECT * FROM book')->then(
4139
function (QueryResult $command) {

examples/01-query.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
<?php
22

3+
// $ php examples/01-query.php
4+
// $ MYSQL_URI=test:test@localhost/test php examples/01-query.php "SELECT * FROM book"
5+
36
use React\MySQL\Factory;
47
use React\MySQL\QueryResult;
58

69
require __DIR__ . '/../vendor/autoload.php';
710

811
$factory = new Factory();
12+
$connection = $factory->createLazyConnection(getenv('MYSQL_URI') ?: 'test:test@localhost/test');
913

10-
$uri = 'test:test@localhost/test';
1114
$query = isset($argv[1]) ? $argv[1] : 'select * from book';
12-
13-
//create a lazy mysql connection for executing query
14-
$connection = $factory->createLazyConnection($uri);
15-
1615
$connection->query($query)->then(function (QueryResult $command) {
1716
if (isset($command->resultRows)) {
1817
// this is a response to a SELECT etc. with some rows (0+)

examples/02-query-stream.php

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

33
// $ php examples/02-query-stream.php "SHOW VARIABLES"
4+
// $ MYSQL_URI=test:test@localhost/test php examples/02-query-stream.php "SELECT * FROM book"
45

56
use React\MySQL\Factory;
67

78
require __DIR__ . '/../vendor/autoload.php';
89

910
$factory = new Factory();
11+
$connection = $factory->createLazyConnection(getenv('MYSQL_URI') ?: 'test:test@localhost/test');
1012

11-
$uri = 'test:test@localhost/test';
1213
$query = isset($argv[1]) ? $argv[1] : 'select * from book';
13-
14-
//create a lazy mysql connection for executing query
15-
$connection = $factory->createLazyConnection($uri);
16-
1714
$stream = $connection->queryStream($query);
1815

1916
$stream->on('data', function ($row) {

examples/11-interactive.php

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

3+
// $ php examples/11-interactive.php
4+
// $ MYSQL_URI=test:test@localhost/test php examples/11-interactive.php
5+
36
use React\MySQL\ConnectionInterface;
47
use React\MySQL\QueryResult;
58
use React\MySQL\Factory;
@@ -8,8 +11,7 @@
811
require __DIR__ . '/../vendor/autoload.php';
912

1013
$factory = new Factory();
11-
12-
$uri = 'test:test@localhost/test';
14+
$uri = getenv('MYSQL_URI') ?: 'test:test@localhost/test';
1315

1416
// open a STDIN stream to read keyboard input (not supported on Windows)
1517
$stdin = new ReadableResourceStream(STDIN);

examples/12-slow-stream.php

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

33
// $ php examples/12-slow-stream.php "SHOW VARIABLES"
4+
// $ MYSQL_URI=test:test@localhost/test php examples/12-slow-stream.php "SELECT * FROM book"
45

56
use React\EventLoop\Loop;
67
use React\MySQL\ConnectionInterface;
@@ -9,8 +10,8 @@
910
require __DIR__ . '/../vendor/autoload.php';
1011

1112
$factory = new Factory();
13+
$uri = getenv('MYSQL_URI') ?: 'test:test@localhost/test';
1214

13-
$uri = 'test:test@localhost/test';
1415
$query = isset($argv[1]) ? $argv[1] : 'select * from book';
1516

1617
//create a mysql connection for executing query

src/Factory.php

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,12 @@ public function createConnection($uri)
163163
}
164164

165165
$parts = parse_url($uri);
166+
$uri = preg_replace('#:[^:/]*@#', ':***@', $uri);
166167
if (!isset($parts['scheme'], $parts['host']) || $parts['scheme'] !== 'mysql') {
167-
return \React\Promise\reject(new \InvalidArgumentException('Invalid connect uri given'));
168+
return \React\Promise\reject(new \InvalidArgumentException(
169+
'Invalid MySQL URI given (EINVAL)',
170+
\defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : 22
171+
));
168172
}
169173

170174
$args = [];
@@ -187,9 +191,12 @@ public function createConnection($uri)
187191
$parts['host'] . ':' . (isset($parts['port']) ? $parts['port'] : 3306)
188192
);
189193

190-
$deferred = new Deferred(function ($_, $reject) use ($connecting) {
194+
$deferred = new Deferred(function ($_, $reject) use ($connecting, $uri) {
191195
// connection cancelled, start with rejecting attempt, then clean up
192-
$reject(new \RuntimeException('Connection to database server cancelled'));
196+
$reject(new \RuntimeException(
197+
'Connection to ' . $uri . ' cancelled (ECONNABORTED)',
198+
\defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
199+
));
193200

194201
// either close successful connection or cancel pending connection attempt
195202
$connecting->then(function (SocketConnectionInterface $connection) {
@@ -198,7 +205,7 @@ public function createConnection($uri)
198205
$connecting->cancel();
199206
});
200207

201-
$connecting->then(function (SocketConnectionInterface $stream) use ($authCommand, $deferred) {
208+
$connecting->then(function (SocketConnectionInterface $stream) use ($authCommand, $deferred, $uri) {
202209
$executor = new Executor();
203210
$parser = new Parser($stream, $executor);
204211

@@ -209,12 +216,27 @@ public function createConnection($uri)
209216
$command->on('success', function () use ($deferred, $connection) {
210217
$deferred->resolve($connection);
211218
});
212-
$command->on('error', function ($error) use ($deferred, $stream) {
213-
$deferred->reject($error);
219+
$command->on('error', function (\Exception $error) use ($deferred, $stream, $uri) {
220+
$const = '';
221+
$errno = $error->getCode();
222+
if ($error instanceof Exception) {
223+
$const = ' (EACCES)';
224+
$errno = \defined('SOCKET_EACCES') ? \SOCKET_EACCES : 13;
225+
}
226+
227+
$deferred->reject(new \RuntimeException(
228+
'Connection to ' . $uri . ' failed during authentication: ' . $error->getMessage() . $const,
229+
$errno,
230+
$error
231+
));
214232
$stream->close();
215233
});
216-
}, function ($error) use ($deferred) {
217-
$deferred->reject(new \RuntimeException('Unable to connect to database server', 0, $error));
234+
}, function (\Exception $error) use ($deferred, $uri) {
235+
$deferred->reject(new \RuntimeException(
236+
'Connection to ' . $uri . ' failed: ' . $error->getMessage(),
237+
$error->getCode(),
238+
$error
239+
));
218240
});
219241

220242
// use timeout from explicit ?timeout=x parameter or default to PHP's default_socket_timeout (60)
@@ -223,10 +245,11 @@ public function createConnection($uri)
223245
return $deferred->promise();
224246
}
225247

226-
return \React\Promise\Timer\timeout($deferred->promise(), $timeout, $this->loop)->then(null, function ($e) {
248+
return \React\Promise\Timer\timeout($deferred->promise(), $timeout, $this->loop)->then(null, function ($e) use ($uri) {
227249
if ($e instanceof TimeoutException) {
228250
throw new \RuntimeException(
229-
'Connection to database server timed out after ' . $e->getTimeout() . ' seconds'
251+
'Connection to ' . $uri . ' timed out after ' . $e->getTimeout() . ' seconds (ETIMEDOUT)',
252+
\defined('SOCKET_ETIMEDOUT') ? \SOCKET_ETIMEDOUT : 110
230253
);
231254
}
232255
throw $e;

src/Io/Connection.php

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,25 @@ public function close()
157157
}
158158

159159
$this->state = self::STATE_CLOSED;
160+
$remoteClosed = $this->stream->isReadable() === false && $this->stream->isWritable() === false;
160161
$this->stream->close();
161162

162163
// reject all pending commands if connection is closed
163164
while (!$this->executor->isIdle()) {
164165
$command = $this->executor->dequeue();
165-
$command->emit('error', [
166-
new \RuntimeException('Connection lost')
167-
]);
166+
assert($command instanceof CommandInterface);
167+
168+
if ($remoteClosed) {
169+
$command->emit('error', [new \RuntimeException(
170+
'Connection closed by peer (ECONNRESET)',
171+
\defined('SOCKET_ECONNRESET') ? \SOCKET_ECONNRESET : 104
172+
)]);
173+
} else {
174+
$command->emit('error', [new \RuntimeException(
175+
'Connection closing (ECONNABORTED)',
176+
\defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
177+
)]);
178+
}
168179
}
169180

170181
$this->emit('close');
@@ -189,7 +200,10 @@ public function handleConnectionError($err)
189200
public function handleConnectionClosed()
190201
{
191202
if ($this->state < self::STATE_CLOSEING) {
192-
$this->emit('error', [new \RuntimeException('mysql server has gone away'), $this]);
203+
$this->emit('error', [new \RuntimeException(
204+
'Connection closed by peer (ECONNRESET)',
205+
\defined('SOCKET_ECONNRESET') ? \SOCKET_ECONNRESET : 104
206+
)]);
193207
}
194208

195209
$this->close();
@@ -202,10 +216,13 @@ public function handleConnectionClosed()
202216
*/
203217
protected function _doCommand(CommandInterface $command)
204218
{
205-
if ($this->state === self::STATE_AUTHENTICATED) {
206-
return $this->executor->enqueue($command);
207-
} else {
208-
throw new Exception("Can't send command");
219+
if ($this->state !== self::STATE_AUTHENTICATED) {
220+
throw new \RuntimeException(
221+
'Connection ' . ($this->state === self::STATE_CLOSED ? 'closed' : 'closing'). ' (ENOTCONN)',
222+
\defined('SOCKET_ENOTCONN') ? \SOCKET_ENOTCONN : 107
223+
);
209224
}
225+
226+
return $this->executor->enqueue($command);
210227
}
211228
}

src/Io/Parser.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ protected function onSuccess()
307307
if ($command instanceof QueryCommand) {
308308
$command->affectedRows = $this->affectedRows;
309309
$command->insertId = $this->insertId;
310-
$command->warningCount = $this->warningCount;
310+
$command->warningCount = $this->warningCount;
311311
$command->message = $this->message;
312312
}
313313
$command->emit('success');
@@ -322,9 +322,10 @@ public function onClose()
322322
if ($command instanceof QuitCommand) {
323323
$command->emit('success');
324324
} else {
325-
$command->emit('error', [
326-
new \RuntimeException('Connection lost')
327-
]);
325+
$command->emit('error', [new \RuntimeException(
326+
'Connection closing (ECONNABORTED)',
327+
\defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103
328+
)]);
328329
}
329330
}
330331
}

0 commit comments

Comments
 (0)