PHP function error and success pattern - php

What is the best way to return errors from a PHP function, when the function has executed normally?
Example
public function login($user, $pw){
if(!$this->verifyUser($user))
// return error about invalid user
}
else if (!$this->verifyPw($pw)){
// return error about invalid pw
}
else {
// return OK
}
}
Caller - Return response as JSON for web UI
public function doLogin($user,$pw){
$res = $this->login($user, $pw);
return json_encode($res);
}
On one hand I could understand returning results as an array, but I feel like this does not make sense for such low level functions. Perhaps they should return error codes and then caller must lookup the error code string?

Assuming you are in an object, you basically have three major options:
store errors in something like $this->errors array and return false
have some kind of error-collector as a dependency for object, where you
call $this->collector->addError('blah blah'); and return false
throw an exception
For the first two approaches, you will have to check the return value, and based on that, pull the list of errors. But both of those options have the benefit of being able to collect multiple errors.
The exception approach is a bit lighter on coupling, but you can only get one error.
As for what to actually return, I would recommend going with error code + description string. But that string would not be returned by your class. Instead your error should be registered using some "placeholder", that later is translated:
$this->errors[] = [
'code' => 52,
'msg' => 'authentication.login.invalid-password',
];
When you pull the errors from your object, it would be basically a list of entries like this, And then you just run them through your translation service.
In a case of exception, that same information would reside in $e->getCode() and $e->getMessage(), when your object throws InvalidPassword exception.

For an API response the answer from tereško would be along the correct lines.
For a DOM response you can do the following:
I have used a response code only in the past for something so simple:
http://php.net/manual/en/function.http-response-code.php with code 401
public function login($user, $pw) {
header_remove(); # Clear all previous headers.
if( !$this->verifyUser($user) || !$this->verifyPw($pw) ){
http_response_code(401);
exit;
}
http_response_code(200);
exit;
}
jQuery:
$.ajax({
.......
statusCode: {
200: function() {
window.location.href = '/';
},
401: function() {
alert( "Login Failed" );
}
}
});

Related

Returning useful error messages with PHP

I don't understand how to properly create and return useful error messages with PHP to the web.
I have a class
class Foo {
const OK_IT_WORKED = 0;
const ERR_IT_FAILED = 1;
const ERR_IT_TIMED_OUT = 3;
public function fooItUp(){
if(itFooed)
return OK_IT_WORKED;
elseif(itFooedUp)
return ERR_IT_FAILED;
elseif(itFooedOut)
return ERR_IT_TIMED_OUT;
}
}
And another class that uses this class to do something useful, then return the result to the user. I am just wondering where I put the string value for all my error messages.
class Bar {
public function doFooeyThings(stuff){
$res = $myFoo->fooItUp();
// now i need to tell the user what happened, but they don't understand error codes
if($res === Foo::OK_IT_WORKED)
return 'string result here? seems wrong';
elseif ($res === Foo::ERR_IT_FAILED)
return Foo::ERR_IT_FAILED_STRING; // seems redundant?
elseif($res === Foo:ERR_IT_TIMED_OUT)
return $res; // return number and have an "enum" in the client (js) ?
}
}
You should avoid returning error states whenever possible. Use exceptions instead. If you've never used exceptions before you can read about them here
There multiple ways you can utilize exceptions in your example. You could create custom exceptions for every error or for every category of error. More on custom exceptions here or you could create an instance of the default Exception class supplying it the error messages as strings.
The code below follows the second approach:
class Foo {
const OK_IT_WORKED = 0;
const ERR_IT_FAILED = 1;
const ERR_IT_TIMED_OUT = 3;
public function fooItUp(){
if(itFooed)
return OK_IT_WORKED;
else if(itFooedUp)
throw new Exception("It failed")
else if(itFooedOut)
throw new Exception("Request timed out");
}
}
I'm sure you can think of some more elegant messages than the ones I used. Anyway, you can then go ahead and handle those exceptions on the caller method using try/catch blocks:
class Bar {
public function doFooeyThings(stuff){
try
{
$res = myFoo->fooItUp();
}
catch(Exception $e)
{
//do something with the error message
}
}
}
Whatever exception is thrown from fooItUp will be "caught" by the catch block and handled by your code.
Two things you should also consider are:
It's best not to show your users detailed information about errors because those information could be used by users with malicious intent
Ideally you should have some kind of global exception handling
One solution is to use exceptions in conjunction with set_exception_handler().
<?php
set_exception_handler(function($e) {
echo "Error encountered: {$e->getMessage()}";
});
class ErrorMessageTest
{
public function isOk()
{
echo "This works okay. ";
}
public function isNotOkay()
{
echo "This will not work. ";
throw new RuntimeException("Violets are red, roses are blue!! Wha!?!?");
}
}
$test = new ErrorMessageTest();
$test->isOk();
$test->isNotOkay();
The set_exception_handler() method takes a callable that will accept an exception as its parameter. This let's you provide your own logic for a thrown exception in the event it isn't caught in a try/catch.
Live Demo
See also: set_exception_handler() documentation

How implement error in Rest API?

I want to know which is the way to implement error in RestAPI, actually if a method in my classes generate an exception I return this ...
if(mysqli_connect_errno()) {
throw new Exception("Can't connect to db.");
}
... but this is a bad practice 'cause an API should be return a json.
So my idea is create a class called Errors and, in each class, when an error is fired I simply call the relative error number for display the json error.
Someone have another idea?
Maybe something like so :
<?php
try {
// Do your stuff
if(mysqli_connect_errno()) {
throw new Exception("Can't connect to db.");
}
} catch (Exception $e) {
echo json_encode(array("success" => false, "message" => $e->getMessage()));
return;
}
I think #Gwendal answer is good but it's no enough just to return a json response, you also have to return the proper http code:
<?php
try {
// Do your stuff
} catch (Exception $e) {
header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
echo json_encode(array("success" => false, "message" => $e->getMessage()));
return;
}
I think you're in the right path. There are a couple of concerns that you're dealing with in here. First one is error handling, whilst the second one is error formatting.
Error handling can be done in several ways, and throwing exceptions is one of them. In order to find out when something bad happened, you'll need to wrap your exceptions within a try/catch block:
try {
//logic
if(mysqli_connect_errno()) {
throw new Exception("Can't connect to db.");
}
//more logic
} catch (Exception $e) {
//handle the error here
}
If you're following this route, I'd suggest you to be more specific in your exceptions, so you can better build your responses in your API. It's not the same having the DB down than to not being able to find a resource, for instance:
try {
//logic
if(mysqli_connect_errno()) {
throw new DBException("Can't connect to db.");
}
if(is_null($entity)) {
throw new ResourceNotFoundException("Entity could not be found");
}
//more logic
} catch (DBException $e) {
//handle DB error here
} catch (ResourceNotFoundException $e) {
//handle resource not found error here
}
Now for the formatting part, the normal response in REST APIs are JSON responses. One way to go about it, would be to create a specific class whose sole responsibility would be to transforms your response into a valid JSON:
...
} catch (DBException $e) {
return $this->JSONResponse->format("Sorry we could not complete your request", 500);
} catch (ResourceNotFoundException $e) {
return $this->JSONResponse->format("The resource you were looking for could not be found", 404);
}
As you can see, different errors have different status codes. The implementation of the class is quite trivial:
class JSONResponse {
public function format($message, $statusCode) {
return json_encode(['message' => $message, 'code' => $statusCode]);
}
}
This does not change the status code of the response though, which is essential to good REST API design. You'll need to set the appropriate status code by using this function.
You can find a more robust and flexible implementation of this class in the Symfony HTTPFoundation Component, which extends from the normal Response class.
My RESTful API always returns a JSON of this structure:
[
'resource' : [],
'code' : [
'id' : int,
'msg' : string
],
'meta' : [],
'log' : []
]
If I return data, the data is always in resource and code['id'] is always 0 (which represents 'OK'). When an error occours, I return an empty resource and some error code. Also I provide some extra information via meta and can log some actions via log which helps me a lot with debugging.
This might also help you with future issues, for example if you want to split an answer into pages so the client should request data via GET /path/to/resource/page/:page or want to notice the client that a certain request path is deprecated.

Confused about how to improve this function..?

I've got the following function in my controller that handles preparing and loading my home page.
public function index()
{
// GetBalance
$current_balance = $this->PayPal->getBalance();
if(Session::has('errors'))
{
return Redirect::to('error');
}
// TransactionSearch
$params = array(
'number_of_days' => 1
);
$recent_history = $this->PayPal->transactionSearch($params);
if(Session::has('errors'))
{
return Redirect::to('error');
}
// Make View
$data = array('current_balance' => $current_balance, 'recent_history' => $recent_history);
return View::make('index')->with('data', $data);
}
As you can see, I'm making 2 different calls to the PayPal API through my model, and after each one I'm checking for an error. When errors do occur I flash the error messages and redirect to an error page accordingly.
I'd like to improve upon that so I don't have to keep using this same snippet of code over and over again when I'm making a bunch of calls prior to loading a view.
if(Session::has('errors'))
{
return Redirect::to('error');
}
I tried moving this to its own function...
public function errorCheck()
{
if(Session::has('errors'))
{
return Redirect::to('error');
}
}
Then, I thought I could just do this within my index function...
// GetBalance
$current_balance = $this->PayPal->getBalance();
$this->errorCheck();
That doesn't work, though, I guess because errorCheck() is simply returning a value and not actually triggering the redirect, so I just end up at my home page with an error because none of the data it expects exists (since the API calls failed).
Any info on what I need to do here so that my errorCheck() function simply triggers the redirect when it should would be greatly appreciated.
Thanks!
The only way I can think of to avoid this is via the use of exceptions.
With your errorCheck() method you could try this:
// GetBalance
$current_balance = $this->PayPal->getBalance();
return $this->errorCheck();
but that's not what you want... it's going to exit your method after the first call. What you really need is a way to catch an error wherever it occurs and handle it - and that's what exceptions do.
I'm going to assume your PayPal class is a third-party package and you can't rewrite it to throw exceptions. Given that assumption, what you can do is this:
Rewrite your errorCheck() method like so:
public function errorCheck()
{
if(Session::has('errors'))
{
throw new Exception("Problem with Paypal!");
}
}
Then wrap all your Paypal access code in a try/catch block:
public function index()
{
try {
// GetBalance
$current_balance = $this->PayPal->getBalance();
$this->errorCheck();
// TransactionSearch
$params = array(
'number_of_days' => 1
);
$recent_history = $this->PayPal->transactionSearch($params);
$this->errorCheck();
// Make View
$data = array('current_balance' => $current_balance, 'recent_history' => $recent_history);
return View::make('index')->with('data', $data);
} catch(Exception $e) {
return Redirect::to('error');
}
}
Each time you call errorCheck() it will check for an error and throw an exception. In that case, execution will immediately jump to the catch block and redirect to the error page.
A better solution would be to throw the exceptions closer to the source of the error, ie. somewhere in the Paypal class when the error occurs. The idea here is that the exception includes a lot of useful information telling you what happened, like a stack trace. In the code I've given, the stack trace is going to show that the exception was thrown in the errorCheck() method which, while true, is not really helpful. If the exception could be thrown somewhere in the Paypal class, it would give you a better indication of what really went wrong.
While throwing an error is definitely the way to go, I'd say you go a step further and generalize the redirect. Instead of doing that try catch block every time the PayPal API is called, you can use App::error to do the redirect globally.
Create an exception class somewhere appropriate:
class PayPalApiException extends Exception {}
Then in your start/global.php add this (before the other App::error call):
App::error(function(PayPalApiException $exception)
{
return Redirect::to('error');
});
Then your code in the controller can become much simpler:
public function index()
{
$current_balance = $this->PayPal->getBalance();
$this->errorCheck();
$recent_history = $this->PayPal->transactionSearch([
'number_of_days' => 1
]);
$this->errorCheck();
$data = compact('current_balance', 'recent_history');
return View::make('index')->with('data', $data);
}
protected function errorCheck()
{
if (Session::has('errors'))
{
throw new PayPalApiException("Problem with Paypal!");
}
}
Why do you even need to check for the error twice? It doesnt seem to be related to each call? i.e. if doesnt seem to matter if the balance call fails, because you dont use the result in the transaction search.
I'd just do this
public function index()
{
// GetBalance
$current_balance = $this->PayPal->getBalance();
// TransactionSearch
$recent_history = $this->PayPal->transactionSearch(array('number_of_days' => 1));
if(Session::has('errors'))
{
return Redirect::to('error');
}
else
{
return View::make('index')->with('current_balance', $current_balance)
->with('recent_history', $recent_history);
}
}

How do you correctly validate worldpay's callback response

I am having trouble validating callbacks from WorldPay for an e-commerce website.
According to the documentation WorldPay should POST a parameter named "transStatus" with a value of "Y" for successful transactions.
This seems straightforward enough so I have written a simple validation function that looks for this value:
private function validateRequest() {
if ($_POST['transStatus'] == "Y"){
return true;
} else {
throw new Exception("Transaction failed");
die();
}
}
}
And when the callback page is run this function is called with a try like so:
try {$this->validateRequest()}
catch (Exception $e) {
mail("email#address.com", $e->getMessage(), $e->getTraceAsString());
throw new Exception("Could not validate payment.")
die();
}
Unfortunately when I test this WorldPay processes the payment successfully but the order is not completed by the rest of my code. I have checked my log files but am unable to see any exceptions or errors.
What is the best approach to take from here? How should I proceed in resolving this issue?
Here's how I resolved this in case anyone encounters a similar problem and stumbles across this in the future:
When I checked the contents of $_POST I realized that it was receiving "Y\n" instead of simply "Y", which it was expecting. Here's what I replaced the code with
if (strpos($_POST['transStatus'], 'Y') !== FALSE) { /* Order is verified */ } else { /* Order is not verified */ }
As you can, now the code checks if 'Y' is found anywhere in the response. This works because there are only three possible responses worldpay will send: 'Y', 'N', 'C'.

Using additional data in php exceptions

I have php code that execute python cgi and I want to pass python trace (returned from cgi) as extra data to php exception how can I do this and how can I get that value from catch(Exception e) { (It should check if that extra value exesit or not).
I have code like this:
$response = json_decode(curl_exec($ch));
if (isset($response->error)) {
// how to send $response->trace with exception.
throw new Exception($response->error);
}
return $response->result;
and I use json-rpc library that should return that data to the user:
} catch (Exception $e) {
//catch all exeption from user code
$msg = $e->getMessage();
echo response(null, $id, array("code"=>200, "message"=>$msg));
}
Do I need to write new type of exception or can I do this with normal Exception? I would like to send everything that was thrown in "data" =>
You need to extend Exception class:
<?php
class ResponseException extends Exception
{
private $_data = '';
public function __construct($message, $data)
{
$this->_data = $data;
parent::__construct($message);
}
public function getData()
{
return $this->_data;
}
}
When throwing:
<?php
...
throw new ResponseException($response->error, $someData);
...
And when catching:
catch(ResponseException $e) {
...
$data = $e->getData();
...
}
Dynamic Property (not recommended)
Please note that this will cause deprecation error in PHP 8.2 and will stop working in PHP 9 according to one of the PHP RFC https://wiki.php.net/rfc/deprecate_dynamic_properties
As the OP asking about doing this task without extending Exception class, you can totally skip ResponseException class declaration. I really not recommend do it this way, unless you've got really strong reason (see this topic for more details: https://softwareengineering.stackexchange.com/questions/186439/is-declaring-fields-on-classes-actually-harmful-in-php)
In throwing section:
...
$e = new Exception('Exception message');
$e->data = $customData; // we're creating object property on the fly
throw $e;
...
and when catching:
catch(Exception $e) {
$data = $e->data; // Access data property
}
September 2018 edit:
As some of readers found this answer useful, I have added a link to another Stack Overflow question which explains the downsides of using dynamically declared properties.
Currently, your code converts the response text directly into an object without any intermediate step. Instead, you could always just keep the serialized (via JSON) text it and append it to the end of the Exception message.
$responseText = curl_exec($ch);
$response = json_decode($responseText);
if (isset($response->error)) {
throw new Exception('Error when fetching resource. Response:'.$responseText);
}
return $response->result;
Then you could just recover everything after "Response:" in your error log and optionally de-serialize it or just read it.
As an aside, I would also not count on the server sending JSON, you should verify that the response text was actually parseable as JSON and return a separate error for that if it isn't.

Categories