Is this how you would pass a value ("username" in example below) to a custom exception? The question being Would I use __construct()? Is using a custom exception for checking whether an important variable is set an overkill?
class customException extends Exception {
public function __construct($e) {
parent::__construct($e); // makes sure variable is set? [1]
$this->e = $e;
}
public function redirect_user() {
if($this->e === "username") {
header("Location: ");
}
}
}
class testing {
public function test() {
try {
if(!isset($_POST['username'])) {
throw new customException("username");
}
}
catch(customException $e) {
$e->redirect_user();
}
}
}
[1] http://php.net/manual/en/language.exceptions.extending.php#example-266
On a side note, what's the purpose of parent::__construct($e)? Wouldn't that be redundant?
There is no reason for your constructor at all. I would suggest using $this->getMessage() to access the value you are trying to set to $this->e.
So do something like this:
class customException extends Exception {
public function redirect_user() {
if($this->getMessage() === "username") {
print_r("Header");
}
}
}
It is much more straightforward and only extends the functionality of the base class rather than unnecessarily overriding the constructor.
That being said, I personally don't like the thought of using exceptions to execute application flow logic like you are doing. To me, custom Exceptions are useful to interface with custom logging systems, or to be able to log aspects of your application's state that are not available via the default Exception class.
Related
How can I see if an exception is currently in flight, i.e. the stack is unwinding?
In the example below how would you implement isExceptionInFlight()?
<?php
class Destroyer
{
function __destruct() {
if (isExceptionInFlight()) {
echo 'failure';
} else {
echo 'success';
}
}
}
function isExceptionInFlight() {
// ?????
}
function createAndThrow()
{
$var = new Destroyer;
throw new \Exception;
}
createAndThrow();
The purpose of this would be to implement D's scope statement, which is available as a library in multiple other languages. This allows you to get rid of nested try-catch blocks, which in turn makes it easier to do transactions with rollbacks correctly.
Addendum1:
I've looked around in the Zend PHP Engine and executor_globals.exception seems to be what I'm looking for (https://github.com/php/php-src/blob/master/Zend/zend_globals.h). However this value is always nullptr when I inspect it during __destruct(). Any idea where I should look next?
Addendum2:
Inspecting executor_globals.opline_before_exception has led to some progress. However it is not reset to nullptr when the exception has been caught.
Addendum3:
I've found the following code (line 135)
/* Make sure that destructors are protected from previously thrown exceptions.
* For example, if an exception was thrown in a function and when the function's
* local variable destruction results in a destructor being called.
*/
old_exception = NULL;
if (EG(exception)) {
if (EG(exception) == object) {
zend_error_noreturn(E_CORE_ERROR, "Attempt to destruct pending exception");
} else {
old_exception = EG(exception);
EG(exception) = NULL;
}
}
zend_call_method_with_0_params(&obj, object->ce, &destructor, ZEND_DESTRUCTOR_FUNC_NAME, NULL);
if (old_exception) {
if (EG(exception)) {
zend_exception_set_previous(EG(exception), old_exception);
} else {
EG(exception) = old_exception;
}
}
This seems to actively PREVENT me from doing what I want, and explains why executor_globals.exception is always nullptr.
Although I don't recommend, I have implemented it in the past. My approach was (simply put) like this:
Implement custom Exception class
class MyException extends Exception {
public static $exceptionThrown = false;
public function __construct($your parameters) {
self::$exceptionThrown = true;
}
}
Now, every exception should be your own exception implementation instead of default Exception.
class Destroyer {
public function __destruct() {
if(MyException::exceptionThrown() {
Database::rollback();
} else {
Database::commit();
}
}
}
Well, it is technically possible, but would this break the MVC architecture?
I'm not sure whether this type of communication is recommended between both controller and model. I will describe it using a simple example and two ways of doing it:
OPTION 1 (model throws exception and controller catches it):
class Controller {
private $model;
public function save($data) {
try {
$this->model->save($data);
} catch (Exception $e) {
// handle exception
}
}
}
class Model {
public function save($data) {
// Call to internal function to save data in BD
if (! $this->_save($data)) throw new Exception('Error saving data');
}
}
OPTION 2 (the controller handles the exception completely):
class Controller {
private $model;
public function save($data) {
try {
if (! $this->model->save($data)) throw new Exception('Error saving data');
} catch (Exception $e) {
// handle exception
}
}
}
class Model {
public function save($data) {
// Call to internal function to save data in BD
if (! $this->_save($data)) return false;
}
}
**
EDIT after some responses:
**
These are other ways to solve it based on your suggestions. I hope not to get things too complicated.
OPTION 3 (model handles the exception completely, as Ray said. KingCrunch also suggested to better do it in the model)
class Controller {
private $model;
public function save($data) {
if (! $this->model->save($data)) {
// possible action: redirect to the form with an error message
}
}
}
class Model {
public function save($data) {
try {
if (! $this->_save($data)) throw new Exception('Error saving data');
} catch (Exception $e) {
// handle exception
return false;
}
return true;
}
}
OPTION 4 (controller gets a custom child exception thrown by the model, as shiplu.mokadd.im said.)
class Controller {
private $model;
public function save($data) {
try {
$this->model->save($data);
} catch (Exception $e) {
if ($e instanceof ValidationException) {
// handle validation error
}
elseif ($e instanceof DBStorageException) {
// handle DB error
}
}
}
}
class Model {
public function save($data) {
if (! $this->_validate($data)) {
throw new ValidationException ('Validation error');
}
if (! $this->_save($data)) {
throw new DBStorageException ('Storage error');
}
}
}
Model can throw Exception and Controller or View should catch it. Otherwise you never know if everything is working properly down there. So use the first option. But make sure you are throwing properly abstracted Exception that is meaningful to the controller and View.
To illustrate the above bold line see these two throw statements which are used inside a model.
throw new Exception('SQL Error: '.$mysqli->error()); // dont use it
throw new DuplicateFieldException('Duplicate username'); // use this
The second example does not show internal error. Rather it hides it. Controller should never know whats happening inside.
In your code your tied a single model to a single controller. Controller does not represent a single model. It uses model. And it can use any number of model. So dont tie up a single model with a controller with variable like private $model.
Definitely first option. Some words:
It's the job of a Controller to ... well, control. This means, that it should take care, that at least an useful error message appears. Other parts of the application may do it before, when they are able to handle the exceptional case. That includes the model itself: If it is able to handle it, it should do it.
save() means "save". Don't misuse the return value for status information. When the method is not able to save() it is an exception and when a method doesn't have to give you something, then it shouldn't give you something.
I prefer option 3.
The Model should catch the exception, try to resolve it, if not percolate it up to the controller but only if it's something the controller could address and recover from. In this case, (some kind of DB save failure) catching it in the model returning false should be adequate resolution for the save error and provide enough for the Controller to know something went wrong when saving.
The controller should not need to worry about implementation details on how the model implements saving.
I am starting an application built on zend framework; this application should be able to make use of multiple data sources other than a database; a webservice for example.
I have been reading on how to structure my model so as to allow for this scenario. I have come across various concepts that seem to provide a solution to this (DataMapper Pattern, Service Pattern, Adapter Layer, etc). However, I am still confused on how to put this all together into a reusable and scalable codebase.
I have worked with zend framework before and will normally work with a mysql table. If for example, I have a Users table...I simple have a Users class in my model that contains business logic of the the user domain and a User class extending Zend_Db_Table_Row_Abstract representing a row in the user table.
How best do I organize my model and code base such that i can still call $users->fetchAll() and get a collection of user objects regardless of what my datasource is?
It basically works the same as you did before, just that instead of a Zend_Db_Table_Gateway you use a My_UserGateway_Whatever, e.g. create an interface first:
interface UserGateway
{
/**
* #return array
* #throws UserGatewayException
*/
public function findAll();
}
We dont want Exceptions from the concrete Gateways to appear in the consuming code, so we add the UserGatewayException as a catch all:
class UserGatewayException extends RuntimeException
{}
Then add a class implementing that interface:
class My_UserGateway_Webservice implements UserGateway
{
public function findAll()
{
try {
// insert logic to fetch from the webservice
return $userData;
} catch (Exception $e) {
throw new UserGatewayException($e->getMessage(), $e->getCode, $e);
}
}
// … more code
}
Likewise, if you want to use a Database source, you can write an adapter for the Zend_Db_* classes, e.g.
class My_UserGateway_Database implements UserGateway
{
private $tableDataGateway;
public function __construct(Zend_Db_Table_Abstract $tableDataGateway)
{
$this->tableDataGateway = $tableDataGateway;
}
public function findAll()
{
try {
return $this->tableDataGateway->select()->blah();
} catch (Exception $e) {
throw new UserGatewayException($e->getMessage(), $e->getCode, $e);
}
}
// … more code
}
If you need another Data Provider, make sure they implement that interface, so you can rely on the findAll method to be there. Make your consuming class depend on the interface, e.g.
class SomethingUsingUsers
{
private $userGateway;
public function __construct(UserGateway $userGateway)
{
$this->userGateway = $userGateway;
}
public function something()
{
try {
$users = $this->userGateway->findAll();
// do something with array of user data from gateway
} catch (UserGatewayException $e) {
// handle Exception
}
}
// … more code
}
Now, when you create SomethingUsingUsers you can easily inject one or the other Gateway into the constructor and your code will work regardless of which Gateway you used:
$foo = SomethingUsingUsers(
new My_UserGateway_Database(
new Zend_Db_Table('User')
)
)
or, for the Webservice:
$foo = SomethingUsingUsers(
new My_UserGateway_Webservice(
// any additional dependencies
)
)
I'm trying to redefine exception handler for a couple of my controllers in Zend (RESTful).
This is my piece of code:
abstract class RestController extends Zend_Rest_Controller
{
public function init()
{
set_exception_handler(array($this, 'fault'));
}
public function fault($exception = null, $code = null)
{
echo $exception->getMessage();
}
}
But for some reason Zend uses default template/error handling and my fault function didnt execute.
Btw, I'm using module architecture. that controller is from rest module.. Zend's default error handler is from default module.
This is an interesting question. I'm not totally sure at the moment so I'm going to research this one a bit and see what I come up with. For now there are workarounds that aren't too ghetto either. One way would be to create an abstract controller from which to extend all of your controllers in your rest module.
abstract class RestAbstractController extends Zend_Rest_Controller
{
final public function __call($methodName, $args)
{
throw new MyRestException("Method {$methodName} doesn't exist", 500);
}
}
// the extends part here is optional
class MyRestException extends Zend_Rest_Exception
{
public function fault($exception = null, $code = null)
{
echo $exception->getMessage() . ' ' . __CLASS__;
exit;
}
}
class RestController extends RestAbstractController
{
// method list
}
Also, I found this interesting article: http://zend-framework-community.634137.n4.nabble.com/Dealing-with-uncatched-exceptions-and-using-set-exception-handler-in-Zend-Framework-td1566606.html
Edit:
Somewhere in your bootstrap file you will need to add this:
$this->_front->throwExceptions(true);
$ex = new MyRestException();
set_exception_handler(array($ex, 'fault'));
The first line there should effectively turn off Zend's exception handling, the only thing missing is a control structure to determine if the current request is for your REST service or not. NOTE The reason this had to go in the Bootstrap.php file was that your call to set_exception_handler() in the init() function was never reached because Zend Framework threw the exception first. Placing that in the bootstrap file will counter that.
Finally solved the problem by myself :)
From Zend documentation:
Zend_Controller_Front::throwExceptions()
By passing a boolean TRUE value to this method, you can tell the front
controller that instead of aggregating exceptions in the response
object or using the error handler plugin, you'd rather handle them
yourself
So, correct solution is this:
abstract class RestController extends Zend_Rest_Controller
{
public function init()
{
$front = Zend_Controller_Front::getInstance();
$front->throwExceptions(true);
set_exception_handler(array($this, 'fault'));
}
public function fault($exception = null, $code = null)
{
echo $exception->getMessage();
}
}
We just have to add
$front = Zend_Controller_Front::getInstance();
$front->throwExceptions(true);
before set_exception_handler to make it work.
If I want to create a new object that needs certain informations like a product id or something like that but the input is bad how can I elegant manage such a case?
class Product
{
function __construct($id)
{
if(is_invalid_id($id))
{ return false; }
}
}
If I initialize it this way I still get an object (since return inside a constructor doesn't return anything). Another way would be to use exceptions which I then can catch but that's kinda unelegant. 3rd option is to use a static function which then checks the input and then returns the object.
class Product
{
static function init($id)
{
if(is_invalid_id($id))
{ return false; }
return new self($id);
}
private function __construct($id)
{
$this->id = $id;
}
}
$product = Product::init($productId);
The problem here is when I try to extend the class. Either I have to create a init() method for every class I extend (even if it is the exact same code) or return new self() always returns an instance of the parent class.
Throw an exception. Not sure why you consider it unelegant. Man, things were unelegant before exceptions (FALSE, -1, null)
For the problem with self:: you might be able to use late static binding (PHP5.3+):
<?php
class Product
{
static function init($id)
{
if(false)
{ return false; }
$s = get_called_class();
return new $s($id);
}
private function __construct($id)
{
$this->id = $id;
}
function getId()
{
return "Product-$this->id";
}
}
class Headphones extends Product
{
function getId()
{
return "Headphones-$this->id";
}
}
$c1 = Product::init(1);
$c2 = Headphones::init(1);
printf("c1 is %s, c2 is %s\n", $c1->getId(), $c2->getId());
// Prints: c1 is Product-1, c2 is Headphones-1
?>
Your third option is the Factory Pattern.
As you've noticed, the downside is that each class that needs this kind of check generally needs it's own factory method.
I'll give you a non-standard workaround that's universally frowned upon by purists: the hybrid constructor
And it's even more evil than it sounds, because it's actually just a wrapper procedure:
function Product($i) {
$prod = new Product($i);
return $prod->valid() ? $prod : new InvalidProduct();
}
class Product {
function __construct() { ... }
}
class InvalidProduct extends Product implements Stub { }
$new_prod = Product("oops123"); // what will it be?
It simply verifies the object instantantly. If there is something wrong, and now here comes the trick, return a specific stub or NULL object. It might implement a few of the interfaces, but generally cause no side-effects or print an error message once it's inevitable. This concept basically hinges on the viability of carrying a stub object around. It's sometimes more sensible to the application logic to have such a specialized instance than to use decorative test logic.
And then there's the second alternative: just bail with an exception.