PHP SoapClient timeout error handler - php

I am calling some web services, using SoapClient. I am looking for a mechanism which will help me to display some errors to user, whenever web services goes offline or down.
As I have to wait for some time(15 sec) before displaying any errors to user. I am adding connection_timeout in SoapClient like this, for timeout.
$this->client = new SoapClient($clienturl,array('trace' => 1,
'exceptions'=> 1,
'connection_timeout'=> 15)); //$clienturl is webservice url
Also in top section of page, I have added this line,
ini_set("default_socket_timeout", 15); // 15 seconds
After specific timeout interval I am getting different SOAP-ERROR like this,
SOAP-ERROR: Parsing WSDL: Couldn't load from $clienturl
So I am looking for an error handler which will handle these SOAP-ERROR so as to display those in human-readable format to user like "Server is down, Try again after some time." Or Is there any way to handle timeout errors?

You can put it in a try/catch
try {
$time_start = microtime(true);
$this->client = new SoapClient($clienturl,array('trace' => 1,
'exceptions'=> 1,
'connection_timeout'=> 15
));
} catch (Exception $e) {
$time_request = (microtime(true)-$time_start);
if(ini_get('default_socket_timeout') < $time_request) {
//Timeout error!
} else {
//other error
//$error = $e->getMessage();
}
}

This is what I am using for soapClien connection in php
set_error_handler('error_handler');
function connectSoapClient($soap_client){
while(true){
if($soap_client['soap_url'] == ''){
trigger_error("Soap url not found",E_USER_ERROR);
sleep(60);
continue;
}
try{
$client = #new SoapClient($soap_client['soap_url'],array("trace" => 1,"exceptions" => true));
}
catch(Exception $e){
trigger_error("Error occured while connection soap client<br />".$e->getMessage(),E_USER_ERROR);
sleep(60);
continue;
}
if($client){
break;
}
}
return $client;
}
function error_handler($errno, $errstr, $errfile, $errline){
if($errno == E_USER_ERROR){
$error_time = date("d-m-Y H:i:s");
$errstr .= "\n
############################### Error #########################################\n
Error No: $errno
Error File: $errfile
Line No: $errline
Error Time : $error_time \n
##############################################################################
";
mail($notify_to,$subject,$errstr);
}
}

Related

Microsoft Graph API (Calendar) Intermittent 503 Errors

This code sometimes works, but frequently runs for ~20s then fails with the "503 Service Unavailable" message when I call getPage(). The authentication/token request seems to be working fine.
I can't seem to identify any pattern of when it fails/succeeds. I don't believe it's a throttling error, as there is no "Retry-After" header returned, and the script only runs once per day at night with <200 records returned. I've also tried removing the $filter and changing parameter order as described here, with no clear benefit.
Can someone please help find the cause here? Happy to share any additional info. Any help is much appreciated, thanks!
<?php
define('DEBUG', true);
session_start();
// set config vars
$ms_url_base = "https://login.microsoftonline.com/d3523db7-f84a-4a24-a815-cd4ba4691c9c";
$ms_client_id = '<client id>';
$ms_redirect_uri = "https://example.com/path";
$ms_scope = "calendars.readwrite user.read";
$ms_client_secret = '<secret>';
$ms_auth_endpoint = '/oauth2/v2.0/authorize';
$ms_token_endpoint = '/oauth2/v2.0/token';
$query_numdays = 100;
if (DEBUG) error_reporting(E_ALL);
date_default_timezone_set("America/Detroit");
require_once __DIR__.'/vendor/autoload.php';
use Microsoft\Graph\Graph;
use Microsoft\Graph\Model;
class EventMod extends \Microsoft\Graph\Model\Event {
// custom functions here
public function getNextLink() {
parent::getNextLink();
}
}
class ResponseMod extends \Microsoft\Graph\Http\GraphResponse {}
// authorization
$provider = new Stevenmaguire\OAuth2\Client\Provider\Microsoft([
'clientId' => $ms_client_id,
'clientSecret' => $ms_client_secret,
'redirectUri' => $ms_redirect_uri,
'urlAuthorize' => $ms_url_base.$ms_auth_endpoint,
'urlAccessToken' => $ms_url_base.$ms_token_endpoint,
'urlResourceOwnerDetails' => 'https://graph.microsoft.com/v2.0/me',
]);
if (!isset($_GET['code'])) {
$options = ['scope' => $ms_scope, 'aud' => 'Graph'];
$authUrl = $provider->getAuthorizationUrl($options);
$_SESSION['oauth2state'] = $provider->getState();
header('Location: '.$authUrl);
exit;
} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) {
unset($_SESSION['oauth2state']);
exit('Invalid state');
} else {
try {
$token = $provider->getAccessToken('authorization_code', ['code' => $_GET['code']]);
} catch (League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
exit ("Get access token exception: ".$e->getMessage());
}
if (DEBUG) {
echo 'Access Token: ' . $token->getToken() . "<p>";
echo 'Refresh Token: ' . $token->getRefreshToken() . "<p>";
echo 'Expired in: ' . $token->getExpires() . "<p>";
echo 'Already expired? ' . ($token->hasExpired() ? 'expired' : 'not expired') . "<p>";
}
// start calendar query
$start=new DateTimeImmutable("yesterday 0:0:1");
$end = $start->add(new DateInterval("P".$query_numdays."D"));
$url='/me/calendarview'
.'?startdatetime='.$start->format('c')
.'&enddatetime='.$end->format('c')
.'&$filter=isOrganizer+eq+false'
.'&$select=subject,responseStatus,start,categories'
.'&$orderby=start/dateTime';
$graph = new Graph;
$graph->setAccessToken($token->getToken());
$data = array();
try {
$iterator = $graph->createCollectionRequest("GET", $url)
->setReturnType(EventMod::class)
->addHeaders(["Prefer" => 'outlook.timezone="America/Detroit"'])
->setPageSize(25);
do {
$page = $iterator->getPage(); /*************** THIS IS WHERE THE EXCEPTION HAPPENS ************/
if (DEBUG) echo "<pre>".print_r($page, true)."</pre>";
$data = array_merge($data, $page);
} while (!$iterator->isEnd());
}
catch (\Microsoft\Graph\Exception\GraphException $e) {
if (DEBUG) echo "GraphException Message: ".$e->getMessage();
exit;
}
catch (Exception $e) {
if (DEBUG) echo "Unk Exception getting data: ".$e->getMessage();
exit;
}
if (DEBUG) print_r($data);
}
?>
composer.json
{
"require": {
"microsoft/microsoft-graph": "^1.29",
"stevenmaguire/oauth2-microsoft": "^2.2"
}
}
I played with your above API call and i ended up noticing the issue - in the case of having bigger larger dates and lot of data at my end (not with smaller time window or less records). It tells me that the failure is due to the client timeout. We need to understand that Calendar view is an expensive operation that too when you deal with calendars and filters added to it. So i went ahead in this scenario, reduce/minimize time window for calendar view by client so smaller segments of time are scanned for matching calendar events. It helped me to get the records as i expected and make use of effective usage of Calendarview API call too.
Apparently the issue was not in the code at all. I discovered that the 503 error was thrown only when a specific date's events were being read by the API. One of the events was rather large and I deleted it; after this the script works well.
This particular event has 16536 attendees listed, which I believe was the source of the error. Still unexplained is why the error was intermittent. I was eventually able to get graph explorer to successfully read the event, so perhaps the bug is in the Microsoft Graph SDK for PHP. I will post there to see if the devs want to capture this error condition.

How to use exceptions for queries?

I want to handle errors in MySQL queries as exceptions.
For example, if I am inserting a record but a column is not in the table then it will show me an error.
Is there any way to handle it so that the user won't be able to see the error?
CI has no good support for exceptions.You need to do is setup proper exception handling.
Now all your database errors will automatically throw exceptions. And as a bonus you have good exception handling in your entire CI application.
Register a custom errorhandler that transforms PHP errors into exceptions, for instance put this in top of your config/config.php
function my_error_handler($errno, $errstr, $errfile, $errline)
{
if (!(error_reporting() & $errno))
{
// This error code is not included in error_reporting
return;
}
log_message('error', "$errstr #$errfile::$errline($errno)" );
throw new ErrorException( $errstr, $errno, 0, $errfile, $errline );
}
set_error_handler("my_error_handler");
Register an uncaught exception handler, put something like this in your config/config.php
function my_exception_handler($exception)
{
echo '<pre>';
print_r($exception);
echo '</pre>';
header( "HTTP/1.0 500 Internal Server Error" );
}
set_exception_handler("my_exception_handler");
Set a termination handler:
function my_fatal_handler()
{
$errfile = "unknown file";
$errstr = "Fatal error";
$errno = E_CORE_ERROR;
$errline = 0;
$error = error_get_last();
if ( $error !== NULL )
{
echo '<pre>';
print_r($error);
echo '</pre>';
header( "HTTP/1.0 500 Internal Server Error" );
}
}
register_shutdown_function("my_fatal_handler");
Set a custom assert handler that converts asserts into exceptions, put something like this in your config/config.php:
function my_assert_handler($file, $line, $code)
{
log_message('debug', "assertion failed #$file::$line($code)" );
throw new Exception( "assertion failed #$file::$line($code)" );
}
assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_WARNING, 0);
assert_options(ASSERT_BAIL, 0);
assert_options(ASSERT_QUIET_EVAL, 0);
assert_options(ASSERT_CALLBACK, 'my_assert_handler');
Use wrappers like this in your controllers
public function controller_method( )
{
try
{
// normal flow
}
catch( Exception $e )
{
log_message( 'error', $e->getMessage( ) . ' in ' . $e->getFile() . ':' . $e->getLine() );
// on error
}
}
You can tune and customize the whole thing to your likings!
Hope this helps.
You will also need to intercept the CI show_error method. Place this in application/core/MY_exceptions.php:
class MY_Exceptions extends CI_Exceptions
{
function show_error($heading, $message, $template = 'error_general', $status_code = 500)
{
log_message( 'debug', print_r( $message, TRUE ) );
throw new Exception(is_array($message) ? $message[1] : $message, $status_code );
}
}
And leave in application/config/database.php this setting on FALSE to have database errors converted into exceptions.
$db['default']['db_debug'] = TRUE;

What is the proper way to capture a "Response too large to return error" in Google BigQuery API for PHP?

In the Google BigQuery web interface, if I run a query that returns a response that is too large, I receive the message:
Error: Response too large to return. Consider setting allowLargeResults to true in your job configuration.
How can I capture this error message in the Google BigQuery API interface when running a query that does not have allowLargeResults set in the job configuration?
In respective BigQuery API - Jobs: get this info is in status.errorResult
Respectivelly in
reason property: responseTooLarge
message property: Response too large to return. Consider setting allowLargeResults to true in your job configuration. For more details, ...
You can also check status.errors for more details for all errors encountered during job execution
We use this fragment to handle errors, and it helped out:
We have also when we place the job, and later when we check the status of the job in a loop, as that error popups up when the job is done.
try {
try {
$job = $bq->jobs->insert(PROJECT_ID, $job);
} catch (Google_IO_Exception $e) {
$this->e('Exception: ' . $e->getMessage(), 'red');
$this->e('Strace: ' . $e->getTraceAsString());
if ($e->getMessage() == 'SSL connect error') {
$this->clearTokenFile();
$this->releaseJob();
}
return false;
}
$status = new Google_Service_Bigquery_JobStatus();
$status = $job->getStatus();
if (0 != $status->count()) {
$err_res = $status->getErrorResult();
$this->e($err_res->getMessage(), 'red');
return false;
}
} catch (Google_Service_Exception $e) {
$this->e('Exception: ' . $e->getMessage(), 'red');
return false;
}
on and insertAll we have, here pay attention to the reason field:
try {
$resp = new Google_Service_Bigquery_TableDataInsertAllResponse();
$resp = $bq->tabledata->insertAll($project_id, $dataset_id, static::tableId(), $request);
$errors = new Google_Service_Bigquery_TableDataInsertAllResponseInsertErrors();
$errors = #$resp->getInsertErrors();
if (!empty($errors)) {
$error_msg = "\r\nRequest Headers: \r\n" . json_encode($client->request->getRequestHeaders()) . "\r\nResponse Headers: \r\n" . json_encode($client->request->getResponseHeaders()) . "\r\nRequest Body:\r\n" . $client->request->getPostBody() . "\r\nResponse Body:\r\n" . $client->request->getResponseBody() . "\r\n";
if (is_array($errors)) {
foreach ($errors as $eP) {
$arr = $eP->getErrors();
$line = $eP->getIndex();
if (is_array($arr)) {
foreach ($arr as $e) {
switch ($e->getReason()) {
case "stopped":
break;
case "timeout":
$failed_lines[] = $line;
$last_reason = $e->getReason();
$error_msg.= sprintf("Timeout on line %s, reason: %s, msg: %s\r\n", $line, $e->getReason(), $e->getMessage());
break;
default:
$error_msg.= sprintf("Error on line %s, reason: %s, msg: %s\r\n", $line, $e->getReason(), $e->getMessage());
break;
}
}
} else {
$error_msg.= json_encode($arr) . "\r\n";
}
}
$this->setErrorMessage($error_msg);
} else {
$this->setErrorMessage($errors);
}
//print_r($errors);
//exit;
$success = false;
}
return $ret;
} catch (Google_Service_Exception $e) {
$this->setErrors($e->getErrors())->setErrorMessage($e->getMessage());
throw $e;
}
Answering my own question: Here's a summary of what resolved it for me. In summary, I couldn't get it to throw an error for Synchronous Queries, but was able to get it to throw for Asynchronous Queries.
Here is a sample query with a response too large to return:
$query = "SELECT * FROM [publicdata:samples.github_timeline] LIMIT 1000000;"
Synchronous Queries
Running a synchronous query with jobs.query:
try {
$query_request = new Google_Service_Bigquery_QueryRequest();
$query_request->setQuery($query);
$res = $this->gbq_service->jobs->query($this->_project_id, $query_request);
return $res;
} catch (Exception $e){
echo $e->getMessage());
}
The response I receive is:
{
"cacheHit": null,
"jobComplete": false,
"kind": "bigquery#queryResponse",
"pageToken": null,
"totalBytesProcessed": null,
"totalRows": null
}
So in this case, I still don't receive the desired "response too large to return".
Asynchronous Queries
If I run the job as an asynchronous query, the job status is eventually set to DONE, and on checking the results, an error is thrown with message:
{
"error": "Error calling GET https://www.googleapis.com/bigquery/v2/projects/.../queries/job_2VICoK6yX0YMM_zRkJ10hT9mom8?timeoutMs=1000000&maxResults=100: (403) Response too large to return. Consider setting allowLargeResults to true in your job configuration. For more information, see https://cloud.google.com/bigquery/troubleshooting-errors",
}
Telling me that I need to save the results as a table and set allowLargeResults to true.
So, the short answer is- run your queries as asynchronous queries.
Update
I've contacted google and they mentioned it may be a bug in the php api. They have said they will forward it to the php gbq API people.

Slim ignoring try catch block

I am using Slim to code a REST API, I have come upon a situation where I need to check if the date time entered by user is valid and thus came up with this code
$app->post('/test', function() use($app)
{
verifyRequiredParams(array('d_string'));
$response = array();
$d_string = $app->request->post('d_string');
try {
$datetime = datetime::createfromformat('d M Y H:i:s', $d_string);
$output = $datetime->format('d-M-Y H:i:s');
}
catch (Exception $e) {
$response["error"] = true;
$response["message"] = $e->getMessage();
echoRespnse(400,$response);
}
$response["error"] = false;
$response["message"] = "Converted Date";
$response['output'] = $output;
echoRespnse(200,$response);
});
It works fine when I enter a valid date time string like 11-Dec-2015 12:18 but if just for testing purpose I enter some random string, it gives 500 internal error instead of giving me any exception.
Why is it ignoring the try catch block???
Error Info
PHP Fatal error: Call to a member function format() on a non-object
DateTime::createFromFormat will not throw an exception if the provided time string is invalid, but will return a boolean false.
So you don't really need a try/catch block to accomplish this:
$datetime = \DateTime::createFromFormat('d M Y H:i:s', $d_string);
if (false === $datetime) {
// send your 400 response and exit
}
$output = $datetime->format('d-M-Y H:i:s');
// the rest of the code
If you really want to keep your try/catch block for various reasons, you can throw an exception yourself and catch it locally:
try {
$datetime = \DateTime::createFromFormat('d M Y H:i:s', $d_string);
if (false === $datetime) {
throw new \Exception('Invalid date.');
}
$output = $datetime->format('d-M-Y H:i:s');
} catch (\Exception $e) {
$response["error"] = true;
$response["message"] = $e->getMessage();
echoRespnse(400,$response);
}
But I don't see a really good reason to throw an exception just to catch it locally in this situation, so I would go with first solution.
If you want to show more detailed error messages, you can use DateTime::getLastErrors method.

Apache thrift: client timeout issues

I have some Apache Thrift (v.0.6.1) test application with perl-server and php-client.
The behaviour I cannot explain: If we call server-method with invalid argument we see the error in server-output, but php-client stays waiting the response infinitely.
Here are the sources of server:
sub new {
my $classname = shift;
my $self = {};
return bless($self,$classname);
}
sub DateToTimestamp
{
my ($self, $date) = #_;
my $result = CommonAPI::DateToTimestamp($date);
return $result;
}
eval {
my $handler = new RPCHandler;
my $processor = new RPCPerformanceTest::RPCPerformanceTestProcessor($handler);
my $serversocket = new Thrift::ServerSocket(9091);
my $forkingserver = new Thrift::ForkingServer($processor, $serversocket);
print "Starting the server...\n";
$forkingserver->serve();
print "done.\n";
}; if ($#) {
if ($# =~ m/TException/ and exists $#->{message}) {
my $message = $#->{message};
my $code = $#->{code};
my $out = $code . ':' . $message;
die $out;
} else {
die $#;
}
}
and client:
try {
$socket = new TSocket($server_host, $server_port);
$transport = new TBufferedTransport($socket, 1024, 1024);
$protocol = new TBinaryProtocol($transport);
$client = new RPCPerformanceTestClient($protocol);
$transport->open();
$start = microtime(true);
$result = $client->DateToTimestamp('071/26/2011 01:23:45');
var_dump($result);
} catch (Exception $e) {
echo 'Exception: <b>' . $e->getMessage() . '</b>';
}
Why is this happening? Is it my fault? Is it expected behavour?
The Thrift PHP library is a bit broken. You need to manually set the timeouts
E.g.
$socket = new TSocket('host', 9095);
$socket->setSendTimeout(60000);
$socket->setRecvTimeout(60000)
This happens often with protocols that do not supply message length: a client sends more data then the server expects and waits for the server to receive the data. The server receives some of the data, tries to parse it and fails. Now the server-side of the protocol is in errorneous state. If it continues to read the data, it may block. Most probably, the server-side has sent you some error response and is waiting at the same time for the client to receive the response, but that will never happen too.
This is my guess. The best strategy IMHO is to set a time-out for both client and server sockets.

Categories