Catch an exception in a shutdown function - php

I'm working on some PHP code that needs to obtain a database resource, mark it as locked for the duration of its run and mark it as not locked when done. The lock is obtained at startup by updating a value in the table, and I've registered a shutdown function to release the lock when the script terminates for any reason.
The problem I'm having is that the connection to the server (a Microsoft SQL database server) doesn't appear to be very reliable in the live environment, and that seems to be the usual reason for abnormal script termination. Obviously I can't unlock the database resource when I'm not connected but I at least want to be able to handle the failure to do that cleanly.
The following is a heavily cut down version of the main script.
try {
echo "Connecting to server...\n";
$obj_adaptor = get_remote_db ();
echo "Beginning sync...\n";
if (FALSE != ($bol_locked = $obj_adaptor -> lock_server_db ())) {
// Do work here
} else {
throw new RuntimeException ("Failed to obtain remote database lock!");
}
} catch (Exception $obj_ex) {
echo "Operation failed with an exception!\n\n";
echo "Details: \n";
echo $obj_ex -> getMessage () . PHP_EOL;
echo $obj_ex -> getTraceAsString () . PHP_EOL;
}
My shutdown function looks like this:
$arr_shutdown_func = function () {
global $bol_locked, $obj_adaptor;
if ($bol_locked) {
echo "Releasing lock...\n";
try {
echo $obj_adaptor -> unlock_server_db ()?
"Done\n":
"Failed to unlock database! Use force_remote_db_unlock script to force a database unlock\n";
} catch (PDOException $obj_ex) {
echo "Failed to unlock database! Use force_remote_db_unlock script to force a database unlock\n";
}
} else {
echo "No lock to release\n";
}
};
// Set up ctrl-c listener
if (function_exists ('pcntl_signal')) {
pcntl_signal (SIGINT, $arr_shutdown_func);
pcntl_signal (SIGTERM, $arr_shutdown_func);
// Register shutdown functions
register_shutdown_function ($arr_shutdown_func);
set_exception_handler ($arr_shutdown_func);
I was expecting in the event of the connection being lost for the script to attempt to release the database lock but failing, and echoing out the message about not being able to release the lock.
However, what I actually get is:
PHP Fatal error: Uncaught exception 'Zend_Db_Statement_Exception'
with message 'SQLSTATE[HY000]: General error: 20047 DBPROCESS is dead
or not enabled [20047] (severity 1) [(null)]' in
Zend/Db/Statement/Pdo.php:238
I've tried to find a definitive answer on whether or not it's possible to catch exceptions in a shutdown function but haven't had much luck so far so I'm still looking. However, if it's possible to deal with exceptions in a shutdown function I'd appreciate knowing what's wrong with the approach I'm applying.

I've just tested this out with a simple code and I don't get disconnected before shutdown function.
<?
mysql_connect('localhost','xxx','xxx') or die(mysql_error());
mysql_select_db('xxx');
register_shutdown_function("s");
function s() {
echo 'xxxx';
$qa = mysql_query('SELECT * FROM xxx') or die(mysql_error());
while($q = mysql_fetch_array($qa)) {
print_r($q);
}
}
?>
What version of PHP are you using?
PHP 5.5.3 (cli) (built: Aug 23 2013 08:41:45) Copyright (c) 1997-2013
The PHP Group Zend Engine v2.5.0, Copyright (c) 1998-2013 Zend
Technologies

Related

SQLSTATE[HY000] [2002] No such file or directory - PHP Daemon

I made a PHP daemon launcher (I execute an independent [which is an argument passed to the daemon] script through exec()) and the script that this PHP daemon runs uses a PDO wrapper that I made as well.
The thing is, when I run the script through the PHP daemon ($ php), my PDO wrapper can't connect and throws SQLSTATE[HY000] [2002] No such file or directory.
The daemon_script.php includes #!/usr/bin/php before the <?php opening tag.
Now, I've been searching here since yesterday and found a couple of approaches but none is my specific case and can't manage to make it work, so I thought you'd have an idea on what I'm doing wrong.
Thanks in advance.
Using:
PHP 7.0.21 (although intended to implement it with PHP 5)
MYSQL Ver 14.14 Distrib 5.6.34, for osx10.12 (x86_64)
Daemon's start() method:
/**
* Starts the script.
* Also receives a script and sets it, then it runs it.
*
* #param string $script Optional script to start.
* #throws Exception Could not init.
* #throws Exception Could not save pid.
* #return boolean
*/
public function start($script = '')
{
if ($script)
{
$this->setScript($script);
}
$initialized = false;
$daemon_command = 'php '.$this->script.' > script.log 2>&1 & echo $! &';
try
{
$daemon_pid = exec($daemon_command, $output);
if (!$daemon_pid)
{
throw new Exception('Could not initialize. Invalid script: '.$this->script);
}
if (!file_put_contents($this->pid, $daemon_pid))
{
exec('kill '.$daemon_pid);
throw new Exception('Could not save process id "'.$daemon_pid.'"… killing it.');
}
usleep(50000);
if (!($initialized = $this->checkPID($daemon_pid)))
{
file_put_contents($this->pid, null);
throw new Exception('Script died unexpectedly!');
}
}
catch (Exception $e)
{
$this->errors = array(
'code' => $e->getCode(),
'message' => $e->getMessage()
) + $this->_errors;
}
return $initialized;
}
You need to set the full path to PHP in your command in order for exec() to properly find it. Since php is located in /opt/local/bin/php, just add it like this:
$daemon_command = '/opt/local/bin/php '.$this->script.' > script.log 2>&1 & echo $! &';
As a note, cron often works the same way, because it doesn't access the same PATH variables that a command-line user does.

Messages not getting sent via ZMQ

Currently, i am trying a simple code of sending/receiving messages using ZMQ. The code is as below
/* Create new queue object, there needs to be a server at the other end */
$queue = new ZMQSocket(new ZMQContext(), ZMQ::SOCKET_REQ);
$queue->connect("tcp://127.0.0.1:5555");
/* Assign socket 1 to the queue, send and receive */
$retries = 5;
$sending = true;
/* Start a loop */
do {
try {
/* Try to send / receive */
if ($sending) {
echo "Sending message\n";
$queue->send("This is a message", ZMQ::MODE_NOBLOCK);
$sending = false;
} else {
echo "Got response: " . $queue->recv(ZMQ::MODE_NOBLOCK) . "\n";
echo 'Complete';
break;
}
} catch (ZMQSocketException $e) {
/* EAGAIN means that the operation would have blocked, retry */
if ($e->getCode() === ZMQ::ERR_EAGAIN) {
echo " - Got EAGAIN, retrying ($retries)\n";
} else {
die(" - Error: " . $e->getMessage());
}
}
/* Sleep a bit between operations */
usleep(5);
} while (--$retries);
When i run this script in console, my output is
Sending message
Got response:
Complete
I believe that though there are no errors thrown, but still my message is not actually sent. I also ran netstat command but i didn't found any process listening on port 5555. Ideally there should be one(current). But no exception is thrown while making connection.
Is there something which i am missing?
When you say no process is listening on port 5555, it probably means that your server is not up and running. Your client will not throw any errors even if there is no server, it just sets up and waits for the server to come online (with your particular setup here, at least).
In this case, since you're using non-blocking mode, it'll send your message on the first pass through the loop (why are you sending the message in the loop at all?), but it won't actually send anything because the server isn't there. Then it'll attempt to receive on the second pass through the loop, but since the socket isn't ready to receive it looks like it'll just fail silently, without throwing an exception. Since no exception is thrown, it gets to your break statement, quits the loop, and you're done.
First things first, your send call should happen before the loop, it's something you want to do only once.
Then, when you recv, store the result in a variable and test for emptiness. Leave the try/catch code. The result should be something like this:
/* Create new queue object, there needs to be a server at the other end */
$queue = new ZMQSocket(new ZMQContext(), ZMQ::SOCKET_REQ);
$queue->connect("tcp://127.0.0.1:5555");
/* Assign socket 1 to the queue, send and receive */
$retries = 5;
echo "Sending message\n";
$queue->send("This is a message", ZMQ::MODE_NOBLOCK);
/* Start a loop */
do {
try {
/* Try to receive */
$response = $queue->recv(ZMQ::MODE_NOBLOCK);
if (!empty($response)) {
echo "Got response: " . $response . "\n";
echo 'Complete';
break;
}
else {
echo "we probably haven't even sent the original request yet, retrying ($retries)\n";
}
}
catch (ZMQSocketException $e) {
/* EAGAIN means that the operation would have blocked, retry */
if ($e->getCode() === ZMQ::ERR_EAGAIN) {
echo " - Got EAGAIN, retrying ($retries)\n";
} else {
die(" - Error: " . $e->getMessage());
}
}
/* Sleep a bit between operations */
usleep(5);
} while (--$retries);
echo "END OF LINE";
... keep in mind this is a minor fix to the client code that makes it behave a little more rationally, I still believe your actual problem is that the server isn't running.
Please add the server code. You are showing the client code. Since the problem seems to be in the server, and you also state that the server is not visible with netstat, the problem is most probably there.

After php upgrade pcntl_fork causing "errno=32 Broken pipe"

I recently upgraded from php 5.4.26 to 5.4.28 after the upgrade I am getting this error
Notice: Unknown: send of 6 bytes failed with errno=32 Broken pipe in Unknown on line 0
When ever I run the following code:
<?php
$tasks = array(
'1' => array(),
'2' => array(),
);
ini_set('display_errors', true);
class RedisClass {
private $redis;
public function __construct()
{
$this->redis = new Redis();
$this->redis->connect('localhost', 6379);
}
}
$redis = new RedisClass();
foreach ($tasks as $index => $task)
{
$pid = pcntl_fork();
// This is a child
if($pid == 0)
{
echo "Running ".$index." child in ". getmypid() ."\n";
break;
}
}
switch($pid)
{
case -1 :
die('could not fork');
break;
case 0:
// do the child code
break;
default:
while (pcntl_waitpid(0, $status) != -1)
{
$status = pcntl_wexitstatus($status);
echo "Child completed with status $status\n";
}
echo "Child Done (says: ". getmypid() .")";
exit;
}
If I only fork one child then I do not get the PHP Notice. If I run any more than 1 child I get the PHP Notice for every child except the first child.
Does anyone have any clues as to what is going on here?
I am assuming it is trying to close the Redis connection multiple times but this is code I have been running for at least 4 months with out any issues.
It only starting displaying these notices after the upgrade to 5.4.28.
I have looked at the PHP change logs but I cannot see anything that I think may explain this issue.
Should I report this to PHP as a bug?
UPDATE:
Looks like it "may" be a Redis issue, I am using phpredis I tested the same code with a mysql connection instead of loading Redis and I do not get the error.
class MysqlClass {
private $mysqli;
public function __construct()
{
$this->mysqli = mysqli_init(); //This is not the droid you are looking for
$this->mysqli->real_connect('IP_ADDRESS',
'USER_NAME',
'PASSWORD');
}
}
$mysql = new MysqlClass();
The problem here is that you do not reconnect Redis in the child process. Like Michael had said, you do not have an active connection from the second child onwards. That mysql example should not work if you also make some queries.
I have had the exact problematic behaviour with the "MySQL server has gone away" error and also with Redis.
The solution is to create a new connection to MySQL and to Redis in the child. Make sure if you have a singletone that handles the MySQL/Redis connection to reset the instances ( that was also a problem for me ).

php get message from rabbitmq error

My amqp extension version is 1.0.1 & AMQP protocol version is 0-9-1
get messages from queue :
<?php
try {
$conn = new AMQPConnection() ;
$conn->setLogin('guest') ;
$conn->setPassword('guest') ;
$conn->connect() ;
if ($conn->isConnected()) {
$channel = new AMQPChannel($conn) ;
if ($channel->isConnected())
{
$queue = new AMQPQueue($channel) ;
$queue->setName('test_queue') ;
$queue->setFlags(AMQP_DURABLE | AMQP_AUTODELETE) ;
$queue->declare() ;
$messages = $queue->get(AMQP_AUTOACK) ;
print_r($messages->getBody()) ;
}
} else {
echo "connect failure ... " ;
}
$conn->disconnect() ;} catch (Exception $e) {
echo $e->getMessage() ;}?>
and it doesn't work ..
Server channel error: 406, message: PRECONDITION_FAILED - parameters for queue 'test_queue' in vhost '/' not equivalent
It seems to me that the queue already exists and it was declared (created) previously with different parameters in the vhost. Queues need to be declared exactly with the same parameters every time (or deleted and recreated with the desired parameters). Try deleting the queue via the management plugin (http://www.rabbitmq.com/management.html) and then running your script again
If your queue has already been created you don't need to create it (using 'declare' method) and bind with exchange once again. IMHO you shouldn't do it as a) these actions require administrative privileges b) it's enough to to it only once c) you might not have got administrative rights on production and your code would be broken.
I believe it's better to create and bind all required queues with management console or any other tool you like and then receive messages this way
// consider using connection more than once. that's only for illustration purposes.
$connection = new AMQPConnection([ put your credentials here ]);
$connection->connect();
if(!$connection->isConnected()) {
throw new Exception('Connection failed.');
}
$chnlObj = new AMQPChannel($connection);
$queObj = new AMQPQueue($chnlObj);
$queObj->setName('yourQueueName');
echo $queObj->get(AMQP_AUTOACK)->getBody();
// consider using connection more than once. that's only for illustration purposes.
$connection->disconnect();

Connecting to MQ using PHP/Linux

Is there anyone out there who successfully is using PHP/Linux to connect to MQ? For days I have desperately been trying to get this to work but to no avail. IF there's anyone out there who is doing this, how?
As I see it there's two extensions to use:
a) mqseries -> a thin wrapper to the C API
b) SAM 1.1.0 -> taking a more general approach
I've tried both but for a) I am able to do MQCONNX successfully, but not MQOPEN and for b) I am not getting past the MQCONN stage. I've tried both the 7 and the 6 Client. Our server is running 6.0.0.0.
Note; we've successfully been connecting to the server using .NET for years.
So this is what I've done so far:
Installed the MQClient from RPM packages
Installed IA94/XMS
Succesfully built the sam_xms.so extension and included that in my php.ini
Successfulyl built mqseries.so extension and included that as well.
Successfully been running the Samples from the mq client installation (amqsputc and amqsgetc) and been passing messages back and forth.
Using the mqseries PECL extension this is the error I get:
MQOPEN failed; CompCode:2 Reason:2044 Text:2044
Using the SAM PECL extension I get this instead:
<--SAMConnection.SetDebug()
-->SAMConnection.Connect()
-->SAMConnection.Create(proto=wmq:client)
SAMConnection.Create() get_cfg_var() ""
SAMConnection.Create() calling factory - SAM/sam_factory_xms.php
SAMConnection.Create() rc = SAMXMSConnection
<--SAMConnection.Create()
SAMConnection.Connect() connection created for protocol wmq:client
SAMConnection.Connect() connect failed (208) Unable to create XMS connection!
<--SAMConnection.Connect() rc=
-->SAMConnection.IsConnected()
SAMConnection.IsConnected() isconnected failed (208) Unable to create XMS connection!
<--SAMConnection.IsConnected() rc=
Connection failed (208) Unable to create XMS connection!
Neither of these errors generates anything in /var/mqm/errors...
Here's my sample code for mqseries:
mqseries_conn('MQED', $conn, $comp_code, $reason);
if ($comp_code !== MQSERIES_MQCC_OK) {
printf("<br>MQCONNX failed; Connx CompCode:%d Reason:%d Text:%s<br>\n", $comp_code, $reason, $reason);
exit;
}
else
{
printf("<br>MQCONNX successful: Connx CompCode:%d Reason:%d Text:%s<br>\n<br>", $comp_code, $reason, $reason);
}
$mqods = array('ObjectName'=>'MYPUTQUEUE');
mqseries_open($conn, $mqods,
MQSERIES_MQOO_INPUT_AS_Q_DEF | MQSERIES_MQOO_FAIL_IF_QUIESCING | MQSERIES_MQOO_OUTPUT,
$obj, $comp_code,$reason);
if ($comp_code !== MQSERIES_MQCC_OK) {
printf("<br><br>MQOPEN failed; CompCode:%d Reason:%d Text:%s<br><br>",
$comp_code,
$reason,
$reason);
}
else
{
printf("<br><br>MQOPEN successful; CompCode:%d Reason:%d Text:%s<br><br>",
$comp_code,
$reason,
$reason);
}
Here's the sample code using the SAM extension:
$conn->connect(SAM_WMQ_CLIENT, array(SAM_BROKER => 'MQED',SAM_TRANSACTIONS => SAM_MANUAL));
if($conn->isConnected())
{
$msg = new SAMMessage('MY MESSAGE');
$msg->header->SAM_REPLY_TO = 'MYGETQUEUE';
$correlid = $conn->send('MYPUTQUEUE', $msg);
if (!$correlid) {
// The Send failed!
echo "Send failed ($conn->errno) $conn->error";
} else {
$resp = $conn->receive('MYGETQUEUE', array(SAM_CORRELID => $correlid));
}
$conn->disconnect();
}
else
{
echo "Connection failed ($conn->errno) $conn->error";
}
Thank you everyone in advance!
Note, this is a continuation of the discussion on Trying to connect to MQ using PHP; almost there
Update #1: MQPUT1 works, but MQOPEN is still returning 2044.

Categories