Exception not handled in correct method using PhpUnit - php

I'm trying to create a library for a personal project using php sockets. For that I started using phpUnit, to learn and to write a (more or less) qualitative library.
When i don't provide the try/catch block in the testConnection method, php gives an error that the connection has timed out (wich is normal, 'cause the device isn't connected). But php should handle the exception in the execute method below, not in the testConnection method. And I can't seem to figure this out.
This is the error:
PHPUnit_Framework_Error_Warning : stream_socket_client(): unable to connect to tcp://x.x.x.x:* (A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.)
Testclass with method and try/catch that should not be there:
public function testConnection() {
$adu = new Adu();
$adu->setPort('AS0');
$adu->setData('?');
$command = new Command('x.x.x.x', *);
$command->setAduSent($adu);
try
{
$command->execute();
}
catch (Exception $e)
{
echo $e->getMessage();
}
}
This (the execute method) is where the Exception should be handled:
public function execute()
{
try {
$this->stream = $this->createStream($this->address, $this->port, $this->timeout);
}
catch(Exception $e) {
$this->logger->error('Exception (' . $e->getCode() . '): ' . $e->getMessage() . ' on line ' . $e->getLine(), $e);
}
$this->send($this->stream, $this->aduSent);
$this->aduReceived = $this->receive($this->stream);
}
private function createStream($address, $port, $timeout = 2)
{
$stream = stream_socket_client('tcp://' . $address . ':' . $port, $errorCode, $errorMessage, $timeout);
if(!$stream) {
throw new Exception('Failed to connect(' . $errorCode . '): ' . $errorMessage);
}
return $stream;
}
Solution
Because a try/catch won't catch errors/warnings I had to suppress the warnings triggered by the stream_socket_client. An then check if the return value is false or a stream object. If false, throw an appropriate Exception.
$stream = #stream_socket_client('tcp://' . $address . ':' . $port, $errorCode, $errorMessage, $timeout);

The stream_socket_client sentence produces a warning, not an Exception, and warnings are not captured by try / catch blocks.
But PHPUnit do capture warnings, and throws an Exception in that case, so an error is triggered. You can configure PHPUnit not to consider warnings as errors, although I would not recommend it. Your code should be warnings free. PHPUnit docs.

Related

PHP PDO DbLib - SQLSTATE[01002] Adaptive Server connection failed (severity 9)

in my PHP application I am suing PDO DbLib driver to connect to Microsoft SQL Server database and sometimes I am getting this PDO Exception:
SQLSTATE[01002] Adaptive Server connection failed (severity 9)
The weird thing is, that I am not getting this exception always, only sometimes. Sometimes I get that exception once per 10 executions, sometimes once per 50 executions of the application.
This is the database class constructor, which is initialising database connection:
function __construct($SQLHOST, $SQLPORT, $SQLDB, $SQLUSER, $SQLPWD, $SQLDRIVER)
{
try {
$pdo_connect = "dblib:version=8.0;host=" . $SQLHOST . ":" . $SQLPORT . ";dbname=" . $SQLDB . ";";
$this->db = new PDO($pdo_connect, $SQLUSER, $SQLPWD);
} catch (PDOException $e) {
$this->dead = true;
$this->error = "PDOException: " . $e->getMessage();
}
}
Any ideas what could be wrong?

ZF2 Pdo error "FATAL: sorry, too many clients already" when making successive connections to multiple databases

I'm porting a class from a non-frameworked php library to Zend Framework 2. This class has methods to run an input query on multiple databases and multiple servers. We have 5 servers, each with anywhere from 5 to over 100 PostgreSQL databases. It works fine with low level postgres calls, but using \Zend\Db\Adapter\Adapter seems to be causing problems because connections aren't being released. Here's the code:
foreach($params['databases'] as $database):
$db_conn['database'] = trim($database);
$dsn = "pgsql:dbname=" . $db_conn['database'] .
";host=" . $db_conn['hostname'] .
";port=" . $db_conn['port'];
$db = array(
'driver' => 'Pdo',
'dsn' => $dsn,
'username' => $db_conn['username'],
'password' => $db_conn['password'],
);
$adapter = new \Zend\Db\Adapter\Adapter($db);
$connection = null;
$connect_count = 0;
$connected = false;
while (!$connected && $connect_count < 5) {
try {
$connect_count ++;
$connection = $adapter->getDriver()->getConnection();
$connection->connect();
$connected = true;
} catch (RuntimeException $e) {
sleep(2);
echo("\n<br>RuntimeException {$connect_count}: {$db_conn['database']} {$e->getCode()} {$e->getMessage()}");
}
}
try {
if (self::checkTablesExist($args['tables_exist'], $connection)) {
$results[trim($database)] = #$connection->execute(self::$arguments['query']);
} else {
log::error(__FUNCTION__." Missing tables", $db_conn['hostname']." : ".$db_conn['database']." : ".join(",", $args['tables_exist']));
}
} catch (PDOException $e) {
echo('\n<br>PDOException : ' . $e->getCode() . " " . $e->getMessage());
} catch (RuntimeException $e) {
echo("\n<br>RuntimeException : {$db_conn['database']} {$e->getCode()} {$e->getMessage()}");
} catch (Exception $e) {
echo('\n<br>Exception : ' . $e->getCode() . " " . $e->getMessage());
}
$connection->disconnect();
endforeach;
Everything works fine until around the 50th database, then I start getting
"RuntimeException: 7 Connect Error: SQLSTATE[08006] [7] FATAL: sorry, too many clients already"
I added the loop with the sleep() statement, which helped slightly. Now it gets to the point where it throws exceptions and the first few it might only go through 2 or 3 times, but then it goes back to not making any connections for the next several databases.
So it seems like $connection->disconnect() isn't working at all, connections are still not being released, at least not in a timely manner. I could just skip using Zend Framework in that part of the code and it would work, but I wanted to do it "canonically", so it wouldn't just be messy like that.

PHP escape error # for production

I want to escape the error generated by fsockopen and it works like this.
if ($fp = #fsockopen($host,$port,$errCode,$errStr,$waitTimeoutInSeconds)) { //... }
but i have been trying other things to avoid # and I have not managed.
Is there a code that I can use that is equivalent to this?
I have tried as well something like this just for testing purposes:
try{
if ($fp = fsockopen($host,$port,$errCode,$errStr,$waitTimeoutInSeconds)) {
/...
}
//..
} catch (Exception $e){
echo 'Error';
}
It does not work.
Warning: fsockopen(): unable to connect to localhost:79 (A connection
attempt failed because the connected party did not properly respond after a period of time or established
connection failed because connected host has failed to respond.
Use set_error_handler() an convert all the errors to an exception you can catch afterwards:
set_error_handler(function($errno, $errstr, $errfile, $errline, array $errcontext) {
if(0 === error_reporting())
return false;
throw new PHPException($errno, $errstr, $errfile, $errline, $errcontext);
});
So now you can catch the PHP error:
try {
if ($fp = fsockopen($host,$port,$errCode,$errStr,$waitTimeoutInSeconds)) {
//...
}
//..
} catch (\Exception $e){
echo 'Connection failed: ' , $e->getMessage();
}
echo 'Don\'t worry... go on!';
You can disable notices and warnings in production systems (instead write to a log):
error_reporting(E_ERROR);
In development environments you'd probably want all errors, notices and warnings on though:
error_reporting(E_ALL);
check out the error reporting levels here: http://php.net/manual/en/errorfunc.configuration.php#ini.error-reporting
EDIT: To check for an error:
$fp = #fsockopen("www.example.com", 80, $errno, $errstr, 30);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
...
}
I think if you are handling failure scenarios then consciously suppressing a warning using # is ok.

new mysqli(): how to intercept an 'unable to connect' error?

I'm doing this (yes, I'm using wrong connection data, it's to force a connection error )
try {
$connection = new mysqli('localhost', 'my_user', 'my_password', 'my_db') ;
} catch (Exception $e ) {
echo "Service unavailable";
exit (3);
}
But PHP is doing this php_warning:
mysqli::mysqli(): (28000/1045): Access denied for user 'my_user'#'localhost' (using password: YES)
In the example I'm using wrong connection data to force a connection error, but in the real world the database could be down, or the network could be down... etc..
Question: Is there a way, without suppressing warnings, to intercept a problem with the database connection ?
You need to tell mysqli to throw exceptions:
mysqli_report(MYSQLI_REPORT_STRICT);
try {
$connection = new mysqli('localhost', 'my_user', 'my_password', 'my_db') ;
} catch (Exception $e ) {
echo "Service unavailable";
echo "message: " . $e->message; // not in live code obviously...
exit;
}
Now you will catch the exception and you can take it from there.
For PHP 5.2.9+
if ($mysqli->connect_error) {
die('Connect Error, '. $mysqli->connect_errno . ': ' . $mysqli->connect_error);
}
You'll want to set the Report Mode to a strict level as well, just as jeroen suggests, but the code above is still useful for specifically detecting a connection error. The combination of those two approaches is what's recommended in the PHP manual.
Check $connection->connect_error value.
See the example here: http://www.php.net/manual/en/mysqli.construct.php
mysqli_report(MYSQLI_REPORT_STRICT);, as described elsewhere, gives me an error and stops the script immediately. But this below seems to provide the desired output for me...
error_reporting(E_ERROR);
$connection = new mysqli('localhost', 'my_user', 'my_password', 'my_db') ;
error_reporting(E_ERROR | E_WARNING | E_PARSE);
if($connection->connect_errno)
{
// Database does not exist, you lack permissions, or some other possible error.
if(preg_match($connection->connect_error, "Access denied for user"))
{
print("Access denied, or database does not exist.");
}
else
{
print("Error: " . $connection->connect_error);
}
}
Attempting to catch this error with try..catch() will fail.

PDO exceptions not being caught

I'm new to using PDO and I'm finding that exceptions due to errors in the query statements are not being caught and displayed properly. The page output when this happens typically looks like:
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42000]: ...
I make PDO queries using the following function:
/**************************************************************************************************************
* Function: makeQuery *
* Desc: Makes a PDO query. *
* Pre conditions: The statement/query and an array of named parameters (may be empty) must be passed. *
* Post conditions: The PDO query is executed. Exceptions are caught, displayed, and page execution stopped. *
**************************************************************************************************************/
function makeQuery($stmt, $array, $errMsg = '')
{
try
{
$stmt->execute($array);
}
catch (PDOException $e)
{
print $errMsg != ''?$errMsg:"Error!: " . $e->getMessage() . "<br/>";
die();
}
}
Any idea why the exceptions are not being caught?
EDIT: This is how I create PDO objects:
function createPDO()
{
// MySQL connection details
$dbhost = '';
$dbuser = '';
$dbpass = '';
$dbname = '';
try
{
$db = new PDO("mysql:$dbhost=localhost;dbname=$dbname;charset=utf8", $dbuser, $dbpass, array(PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
}
catch (PDOException $e)
{
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
return $db;
}
Any chance the file you're catching the exception uses namespaces? In that case it should be:
catch (\PDOException $e)
Or add:
use PDOException;
On the top of the file
Are you sure every query is running through that function? Might look through your code to be 100% certain.
Another option is to define a global exception handler then just ask the thrown exception which line & file it came from:
http://php.net/manual/en/function.set-exception-handler.php
function exception_handler($exception)
{
echo "Uncaught exception: " . $exception->getMessage() . PHP_EOL;
echo "In file: " . $exception->getFile() . PHP_EOL;
echo "On line: " . $exception->getLine() . PHP_EOL;
}
set_exception_handler('exception_handler');
EDIT:
After some dialog w/ OP I'm now certain that the call to PDOStatement::prepare is where the Exception is being thrown, hence I'm recommending yet another wrapper function to the library:
function prepareStmt(PDO $oPdo, $sStmt, array $aDriverOptions=array())
{
try
{
return $oPdo->prepare($sStmt, $aDriverOptions);
}
catch(PDOException $e)
{
print "Error!: " . $e->getMessage() . "<br/>";
die();
}
}

Categories