Implementing a try/catch block in php constructor function - php

I have a query with try-catch block. I am going to create object and if it fails it will throw error message or say redirect on some other page.
Code
function __construct() {
try{
$this->ServiceObj = AnyClass::getService('XXX');
}
catch{
return Redirect::to('/');
}
}
public function MyOwnFunction() {
$getValueofCode = $this->_ServiceObj->set($endPoint_Url); //it's only example method not want to set anything
}
Is this correct way to define and use the try catch block in a constructor? Can I use try & catch block in construction? If NO than is there a better way to achieve this?
Thanks in Advance!!!

You can definitely use try catch in a constructor, just don't forget to specify the exception class you want to catch like catch (\Exception $e). However Laravel will not process the returned redirect. (By definition constructors shouldn't even return something)
An easy fix for this would be calling send() on the redirect. This will return it immediately to the client and stop the app.
try{
$this->ServiceObj = AnyClass::getService('XXX');
}
catch(\Exception $e){
Redirect::to('/')->send();
}
But since you mentioned a login page, you might be better of using a before filter to check and redirect. Like the built in auth filter does.
A third solution would be App::error. In app/start/global.php you could do something like this:
App::error(function(FooBarException $exception)
{
return Redirect::to('/');
});
Of course this only works if your exception is specific enough (replace FooBarException with the exception you want to catch)

Related

Laravel catch exception and add message to a messagebag

I have a repository that throws an exception if it can't find a record in the database. Rather than redirect to another page I just want to display a warning alert as the record is not critical to the page but is an "exceptional event".
It's probably best to demonstrate with code:
// FxRateRepositoy
public function getRate(/** args **/)
{
$rate = FxRate::where(.... //query to get the rate
if (!rate)
throw new NonExistentCurrencyException(//message);
return $rate;
}
In my start/global.php I have a handler:
App::error(function(NonExistentCurrencyException $e)
{
Session::flash('alert', $e->getMessage());
return \\ ??
});
What to return? I must return a response or the exception continue uncaught. I want to continue to the intended page but with the alert flashed in the session. Is this possible without having to use try catch blocks in every place this method is called?
Ass an additional question, assuming this exception may be thrown multiple times in one request, what's the best way to accumulate alert messages and display them? I'm thinking something akin to the validation messageBag. Can I just use the global $errors variable or should I create a new, specific messagebag for this purpose?
The problem is that if you return nothing from App::error Laravel will display it's default error page. On the other side you can't return a response because you don't know what response it should be in the error handler.
I suggest you handle it in the controller itself.
You can catch the exception there and flash the message or don't throw an exception at all:
$rate = FxRate::where(.... //query to get the rate
if (!rate){
Session::flash('alert', 'Whoops');
}
Also the findOrFail() and firstOrFail methods might be of use. They throw an ModelNotFoundException if the query yields no results:
try {
$rate = FxRate::where(....)->firstOrFail()
// and so on
} catch (Illuminate\Database\Eloquent\ModelNotFoundException $e){
Session::flash('alert', 'Whoops');
}
As for a messages system, take a look at the laracasts/flash package

catch an exception an throw another one... is there any other way?

In Laravel framework whenever you try to fetch some data from your eloquent model, if an exception occurs, it'll throw aModelNotFoundException. In my project I need to catch this exception and redirect user to specific route.
the solution that I've ended up is like this:
try{
$foundUser = $this->user->whereId($id)->firstOrFail();
}catch(ModelNotFoundException $e){
throw new NonExistantUserException;
}
I know that I can put my redirect code inside the catch block, however I'm already catching these sort of exceptions in global.php:
App::error(function(NonExistantUserException $e)
{
return Redirect::back()->WithInput();
});
I want to know is there any way to say, for example no matter what kind of exception would occur inside try block I want to catch it as NonExistantUserException just for this try block!
I'm asking because catching an exception an throw another one. seems a bad practice to me.
Thanks in advanced.
Absolutely not bad practice and even common practice. But you should not simply discard the previous exception as it might be useful for debugging purposes.
<?php
try {
$foundUser = $this->user->whereId($id)->firstOrFail();
} catch (ModelNotFoundException $e) {
throw new NonExistentUserException(
"Could not find user for '{$id}'.",
null,
$e // Keep previous exception.
);
}
This ensures that you have the full chain of exceptions at your disposal. Other than that your approach looks good to me.

How to bypass Laravel Exception handling

I have a method that checks if a user has valid Session info. This is supposed to throw an Exception, Guzzle\Http\Exception\BadResponseException but when I try to catch it :
catch (Guzzle\Http\Exception\BadResponseException $e)
{
return false;
}
return true
Laravel doesn't get to this code and immediately starts it's own error handling. And ideas on how to bypass Laravels own implementation and use my own Catch.
EDIT: I just found out Laravel uses the same Exception handler as Symfony, so I also added the Symfony2 tag.
EDIT 2:
I sort of fixed the issue by disabling Guzzle exceptions and checking the return header manually. It's a bit of a short cut but in this case, it does the job. Thanks for the responses!
Actually this exception can be catched in Laravel, you just have to respect (and understand) namespacing:
If you have
namespace App;
and you do
catch (Guzzle\Http\Exception\BadResponseException $e)
PHP understands that you are trying to
catch (\App\Guzzle\Http\Exception\BadResponseException $e)
So, for it to work you just need a root slash:
catch (\Guzzle\Http\Exception\BadResponseException $e)
And it will work.
By default, the app/start/global.php file contains an error handler for all exceptions. However, you may specify more handlers if needed. Handlers are called based on the type-hint of the Exception they handle. For example, you may create a handler that only handles your BadResponseException instances, like
App::error(function(Guzzle\Http\Exception\BadResponseException $exception)
{
// Handle the exception...
return Response::make('Error! ' . $exception->getCode());
});
Also, make sure you have a well defined (BadResponseException) class. Read more on Laravel Documentation.
Instead of your code
catch (Guzzle\Http\Exception\BadResponseException $e)
{
return false;
}
return true
use this solution
catch (\Exception $e)
{
return false;
}
return true
to catch all possible exceptions thrown by Guzzle.
If you explicitly want to catch a BadResponseException you can also prepend your exception's class namespace with '\'.
catch (\Guzzle\Http\Exception\BadResponseException $e)
{
return false;
}
return true

Using headers in a class

I access a function in an included class Environment which can throw an Alert which needs to direct the users back to a page with some error information. What is the best way for me to reference the page so it can be used wherever I access the function from anywhere?
Is there any better way to do what I am trying to do?
public function checkEnvironment() {
try {
// If status is false
if (!$this->getStatus()) {
// Generate a new special exception with code
throw new Alert(6);
} else {
$connection = Gateway::checkInstance();
return $connection->getData('SELECT * FROM control_environment WHERE subdomain = ?', array($this->subdomain[0]));
}
} catch (Alert $alert) {
$_SESSION['error'] = $alert->getData();
if (!headers_sent()) {
header('Location: my/file/here.php');
exit;
}
}
}
You're badly abusing exceptions. There is absolutely no reason to put a single if statement in a try block, which can only throw one type of exception which is guaranteed to be caught immediately after that block. You've added nothing to your code but clutter, there is literally no advantage to doing this over simply doing an if/else with no exceptions/catching.
The point of exceptions is that you throw them up out of the current scope, to some place where they can actually be handled in a meaningful way.
Pick a real exception class, one that communicates something about the error that's occurred. Alert(6) doesn't tell anybody anything. Then, handle that (and probably many other) exceptions up above this, where you can be more sure that redirecting is the correct course of action. Your low-level database code shouldn't have any concept of browsers or http or redirection.
Your code also shouldn't have an else branch when the purpose of the if branch is to throw an exception. The else is redundant.
The whole function should look like this.
public function checkEnvironment() {
if (!$this->getStatus())
// Generate a new special exception with code
throw new StatusException;
$connection = Gateway::checkInstance();
return $connection->getData('SELECT * FROM control_environment WHERE subdomain = ?', array($this->subdomain[0]));
}

Error handling for functions in a catch statement

This is a piece of code that I was wondering if it should be refactored to make it more complied with the Clean Code practices.
This is a class which is responsible for refunding some orders made by customers.
class RefundServiceInvoker {
private $_orders;
public function refundOrder() {
$this->getOrdersFromDB(); //This function gets all orders from DB and sets $_orders
foreach ($this->_orders as $order) {
try {
$order->refund(); //Some lines may throw an exception when refunded due to some business logic (ex. the order was already shipped)
$this->updateOrderStatus('refunded')
} catch (Exception $e) {
$this->logError($e);
$this->sendMailToAdmin();
}
}
}
}
of course this code is highly simplified than my original code.
My main problem is if $order->refund(); throws an exception it will be caught and logged to DB then a mail is sent. However what if $this->logError($e); itself throws an exception? or what if the mail server was down and an exception was thrown?
Better what if the DB was down itself and $this->getOrdersFromDB(); throws an exception?
My first solution was to wrap everything in one big try{}catch{}:
public function refundOrder() {
try {
$this->getOrdersFromDB(); //This function gets all orders from DB and sets $_orders
foreach ($this->_orders as $order) {
$order->refund(); //Some lines may throw an exception when refunded due to some business logic (ex. the order was already shipped)
$this->updateOrderStatus('refunded')
} catch (Exception $e) {
$this->logError($e);
$this->sendMailToAdmin();
}
}
}
But that would mean if one order fails then all fails!! Should I put 2 try{}catch{} one for the whole function and the other for each order? But in this case too the functions in the catch might throw an exception that won't be caught.
Note:
The application is built using Zend framework 1.11.11.
Thanks in advance.
There is no magic bullet to solve such issues. If a function can throw and you care about it, you have to wrap try/catch around it -- simple as that.
To move into specifics: it's not really possible to evaluate the merits of this or that approach without having much more information about the architecture of your app, but here are some general suggestions:
Do check prerequisites before even calling refundOrder. Make sure that orders have been loaded successfully; make sure the orders you are going to operate on are all refundable (what's the purpose of trying to refund an order that is not refundable due to business logic? shouldn't the user/operator be notified of this before a refund is attempted?).
Do use more than one levels of try/catch, but perhaps not all inside refundOrder. The outer block is meant to catch any errors that were really unexpected, so what's the point in catching them inside refundOrder only? Don't you want to do that in other parts of you app as well? The innermost try/catch is necessary so that one non-refundable order won't kill all the process.
If you need to log and mail every exception, then you need something like this:
class RefundServiceInvoker {
private $_orders;
public function refundOrder() {
try {
$this->getOrdersFromDB();
foreach ($this->_orders as $order) {
try {
$order->refund();
} catch (MyBusinessException $e) {
# deals with the problematic $order without stopping the loop
$this->logError($e);
$this->sendMailToAdmin();
}
$this->updateOrderStatus('refunded');
}
} catch(Exception $e) {
# deals with unexpected bugs
$this->logError($e);
$this->sendMailToAdmin();
}
}
}
But you have to put a try/catch inside log and mail methods to prevent them to throw any exception when servers are offline, for example. If you don’t, the $orders loop will be stopped when log/mail fails.
If you need to log/mail only your business exception in refund() method, then you need something like this:
class RefundServiceInvoker {
private $_orders;
public function refundOrder() {
$this->getOrdersFromDB();
foreach ($this->_orders as $order) {
try {
$order->refund();
} catch (MyBusinessException $e) {
# deals with the problematic $order without stopping the loop
$this->logError($e);
$this->sendMailToAdmin();
}
$this->updateOrderStatus('refunded');
}
}
}
Any other exception will lead to an http 500 – internal server error page, which is usually what you want, because it’s an unexpected bug.
There’re other ways to handle this, but as you see, it depends on your needs.
As logError($e) and sendMailToAdmin() are probably functions that are used in last resort, typically in try/catch blocks, I would ensure that they never throw an Exception.
function logError($e)
{
try
{
//your logic that may throw an Exception here
}
catch {}
}
function sendMailToAdmin($e)
{
try
{
//your logic that may throw an Exception here
}
catch {}
}

Categories