Author Topic: Implementing a throw/nothrow exception handling in PHP  (Read 2860 times)

Mike

  • Jackass In Charge
  • Posts: 11248
  • Karma: +168/-32
  • Ex Asshole - a better and more caring person.
Implementing a throw/nothrow exception handling in PHP
« on: June 17, 2009, 11:35:34 AM »
Lets say you write two classes, a parent (P) and a child (C) and expect that future children will be written, that uses exceptions for error handling.  As you start using the classes you realize that there are times when it'd be better if it just returned false instead of throwing an exception.  So you create a throw/nothrow toggle ability.

Ok some constraints:
You are lazy and don't want to have the throw/nothrow logic in every function.
You are lazy and don't want to have the throw/nothrow logic in every child class.
PHP 5+ because PHP 4 is dead and sucks donkey balls for OOP

Well I found a way.  It probably violates the natural order of things but meh :D

First I am going to go backwards and look at the child classes first.  First write the functions normally using exceptions and test normally.  After all the testing is done change the functions from public to protected.  Making it protected means that outside code can't access the function but internal code can.

Next the parent.  The first thing to realize that even when you are in a parent's method $this still refers to the child object.  The parent methods can access the child's public and protected methods but not its private methods.

In the parent class add the nothrow variable and toggling method(s).  Now the work horse is going to be the magic method __call().  __call is invoked whenever you call an inaccessible method on an object.  It takes two parameters: the name of the method called and an array of the parameters.  In __call() you can use call_user_func_array() to call the method and since you are doing it from inside the parent you can access the child's protected methods.  So using a simple if/else and try/catch you can implement the throw/nothrow functionality.

Now a catch I found was that if the method called is private or otherwise inaccessible to the parent you'd get infinite recursion.  So to counter then you can use reflection to get a list of the child methods and make a list of all the protected functions.  Then in __call you simply check to see if the method called is in the list.

Ok code time:
Code: [Select]
<?php

class p
{
        private 
$method_cache null;
        public function 
__construct()
        {
                
$ref = new ReflectionClass($this);
                
$methods $ref->getMethods();
                
$callable = array();

                foreach(
$methods AS $method)
                {
                        if (
$method->isProtected())
                        {
                                
$callable[] = $method->name;
                        }
                }
                
$this->method_cache $callable;
        }
        public function 
__call($name$arguments)
        {
                if (!
in_array($name$this->method_cache))
                        return 
false;

                if (
$this->throwErrors)
                        return 
call_user_func_array(array($this$name), $arguments);
                else
                {
                        try
                        {
                                return 
call_user_func_array(array($this$name), $arguments);
                        }
                        catch (
Exception $e)
                        {
                                
$this->setLastError($e);
                                return 
false;
                        }
                }
        }

}

class 
extends p
{
        protected function 
foo()
        {
                echo 
"foo\n";
        }

        private function 
bar()
        {
                echo 
"bar\n";
        }
}

$c = new c();

$c->foo();
$c->bar();

Perspective

  • badfish
  • Jackass In Charge
  • Posts: 4635
  • Karma: +64/-22
    • http://jeff.bagu.org
Re: Implementing a throw/nothrow exception handling in PHP
« Reply #1 on: June 21, 2009, 06:58:38 PM »
I´m not sure how php works under the hood, but using reflection like that can be slower than straight function calls in some languages.


Mike

  • Jackass In Charge
  • Posts: 11248
  • Karma: +168/-32
  • Ex Asshole - a better and more caring person.
Re: Implementing a throw/nothrow exception handling in PHP
« Reply #2 on: June 21, 2009, 07:31:07 PM »
Oh I have no doubt that this method would be slower then straight calling.  Thought it isn't use reflection like Java is, just magic functions.

But while it is slower it is still really faster compared to the HTTP request/response.

Mike

  • Jackass In Charge
  • Posts: 11248
  • Karma: +168/-32
  • Ex Asshole - a better and more caring person.
Re: Implementing a throw/nothrow exception handling in PHP
« Reply #3 on: June 22, 2009, 04:21:01 PM »
Just realized I never said what context I was using it :D

Ran the test using a public function ( for a baseline), a protected function without nothrow set, and a protected function with nothrow set:

Test                          Time
Public                        0.0529849529266
Protected                     0.289533138275
Protected Nothrow             0.29740691185

10000 calls for each

webwhy

  • Jackass IV
  • Posts: 608
  • Karma: +15/-10
Re: Implementing a throw/nothrow exception handling in PHP
« Reply #4 on: June 22, 2009, 10:41:49 PM »
Just my opinion:

Besides the performance hit, there are more serious issues:

1) it relies on inheritance.  What if you want to reuse your "throw/nothrow" behavior across inheritance trees.  Right now you have two options:  every class should inherit from the base class with desired logic OR you have to re implement the logic in the base class of each inheritance tree.  neither is very good imho.

2) it relies on language "magic" to work.  while it's fun to play with stuff like this, it would be completely confusing to a new programmer on the project.  your parent class is modifying the behavior of the child class, which is backwards and against the spirit of OOP.  I shouldn't have to read the implementation of the base class to understand how the child classes are working.  What if i don't have access to the source for the  parent class?  you can maybe document it, but where?  In every class file that inherits from this parent?  It's much simpler to use the language as intended, making it easily understood by all programmers that understand OOP.

In ruby this can be accomplished using a mixin, but nothing similar is available in PHP as far as I know.  Using aspects would also be a cleaner way to accomplish your goal.  A quick good search returned a few AOP libraries for PHP.  one seemed to allow the aspects to be weaved into the target classes at runtime (GAP), which would allow clients of the class to "weave" the desired behavior, and provide similar functionality to a ruby mixin.

Mike

  • Jackass In Charge
  • Posts: 11248
  • Karma: +168/-32
  • Ex Asshole - a better and more caring person.
Re: Implementing a throw/nothrow exception handling in PHP
« Reply #5 on: June 22, 2009, 11:01:50 PM »
Just my opinion:

Besides the performance hit, there are more serious issues:

1) it relies on inheritance.  What if you want to reuse your "throw/nothrow" behavior across inheritance trees.  Right now you have two options:  every class should inherit from the base class with desired logic OR you have to re implement the logic in the base class of each inheritance tree.  neither is very good imho.
Since there isn't a language construct to handle this that is pretty much the choices.  I choose this approach over the other to reduce code duplication.

Quote
2) it relies on language "magic" to work.  while it's fun to play with stuff like this, it would be completely confusing to a new programmer on the project. 
Totally agree.  Fortunately it wasn't that hard to explain it to the other programmers nor to document it.  The PHP docs do a good job of explaining __call() and with some explaination in the comments it is pretty clear.  I would expect that a PHP programmer that is used to the weird quarks of PHP could understand it pretty quick.

Quote
your parent class is modifying the behavior of the child class, which is backwards and against the spirit of OOP.  I shouldn't have to read the implementation of the base class to understand how the child classes are working.
Hmmm, yes and no.  Most of the time you need to drill down the inheritence tree to really know what a class is doing or how something is handled.  And it isn't uncommon for a parent to set some behavior that the child follow.  And there is nothing stopping the child from implementing its own __call() to handle the behavior.  Though the reflection stuff was a bit of a surprise but upon my reflection it kinda makes since.  PHP does late binding, uses references/pointers, and doesn't do class slicing.  So it makes since that even in the parent class it knows what the child class object is.  The reflection functions then work off of that object.



Quote
  What if i don't have access to the source for the  parent class?  you can maybe document it, but where?  In every class file that inherits from this parent?
If you don't have access to the source of the parent then you don't have a functioning script.  This is PHP afterall.

Quote
  It's much simpler to use the language as intended, making it easily understood by all programmers that understand OOP.
What is intended or not?  Is what I'm doing a side effect, a bug, a quark, or just a combination of choices made by the language developers?  And I've seen much more horrible things done in standard OOP then what I'm doing.  How many times have you seen a piece of code that goes through 10 levels of method calls to get a value?


webwhy

  • Jackass IV
  • Posts: 608
  • Karma: +15/-10
Re: Implementing a throw/nothrow exception handling in PHP
« Reply #6 on: June 23, 2009, 12:16:42 AM »
Quote
Fortunately it wasn't that hard to explain it to the other programmers nor to document it.  The PHP docs do a good job of explaining __call() and with some explaination in the comments it is pretty clear.  I would expect that a PHP programmer that is used to the weird quarks of PHP could understand it pretty quick.

while it might not be that hard to explain; the fact that you have to explain it is the problem.  code should be clear and obvious; unnecessary explanation is a waste of time.

Quote
Hmmm, yes and no.  Most of the time you need to drill down the inheritence tree to really know what a class is doing or how something is handled.  And it isn't uncommon for a parent to set some behavior that the child follow.  And there is nothing stopping the child from implementing its own __call() to handle the behavior.  Though the reflection stuff was a bit of a surprise but upon my reflection it kinda makes since.  PHP does late binding, uses references/pointers, and doesn't do class slicing.  So it makes since that even in the parent class it knows what the child class object is.  The reflection functions then work off of that object.

you are definitely modifying the explicit behavior declared in the child.  you are potentially changing the return type of the function and are always changing the declared visibility of the method.  it's not clear how a protected method in a class can be called from outside the class without reading the base class.  i can't remember the last time I had to read a base class implementation to understand how a completely unrelated subclass method worked.

Quote
If you don't have access to the source of the parent then you don't have a functioning script.  This is PHP afterall.
The parent could be obfusticated.  it can be difficult to read and still function.  regardless, my point was i shouldn't have to read the base class implementation to see how an unrelated subclass method works.

Quote
What is intended or not?  Is what I'm doing a side effect, a bug, a quark, or just a combination of choices made by the language developers?  And I've seen much more horrible things done in standard OOP then what I'm doing.  How many times have you seen a piece of code that goes through 10 levels of method calls to get a value?

I would call it a bug.  at the very least a side-effect.  reading the php documentation it's pretty obvious what is intended by protected visibility.  you are violating that intention.

It's creative though...


Mike

  • Jackass In Charge
  • Posts: 11248
  • Karma: +168/-32
  • Ex Asshole - a better and more caring person.
Re: Implementing a throw/nothrow exception handling in PHP
« Reply #7 on: June 23, 2009, 12:53:24 AM »
while it might not be that hard to explain; the fact that you have to explain it is the problem.  code should be clear and obvious; unnecessary explanation is a waste of time.
While a good goal I have found that to be a fantasy in all but the simplest of code.  Besides, even the fundamentals of OOP have to be explained at sometimes.  This is more like explaining some of the quarks about PHP.

Quote
you are definitely modifying the explicit behavior declared in the child.  you are potentially changing the return type of the function and are always changing the declared visibility of the method.  it's not clear how a protected method in a class can be called from outside the class without reading the base class.  i can't remember the last time I had to read a base class implementation to understand how a completely unrelated subclass method worked.
As far as the return type:  It is still in keeping with PHP's behavior.  There are a lot of functions that'll return a value of some type or false if there is an error.

Quote
The parent could be obfusticated.  it can be difficult to read and still function.  regardless, my point was i shouldn't have to read the base class implementation to see how an unrelated subclass method works.
And you really don't.  The only thing you'd need to know is that protected is accessible and that the function can return false.  Not a terribly difficult thing.

Quote
I would call it a bug.  at the very least a side-effect.  reading the php documentation it's pretty obvious what is intended by protected visibility.  you are violating that intention.
Am I?

Protected limits access to inherited and parent classes (and to the class that defines the item).
I'm not giving the public direct access to the functions.  So in that regard it is no different then 99% of the setX/getX functions written:
Code: [Select]
public function setX ($x)
{
  $this->x=$x;
}
public function getX()
{
  return $this->x;
}

Quote
It's creative though...
Thanks

I'm having fun discussing this.  The idea seems to counter OOP in C++ and Java and yet I find it strangely freeing.  I'm starting to really appreciate PHP's magic functions.  Something I just had click for me:

At work we have a GetValue($string) function that has a giant switch in it where 99% of them are the same crap.
Code: [Select]
switch($string)
{
case 'blah':
  $data = $this->GetStudentData();
  return $data['blah'];
default:
  return $string;

Seems like it'd be easier to have something like:
Code: [Select]
<?php

class Student
{
private $studentdata null;
private function GetStudentData()
{
echo __FUNCTION__"<br />\n";

return array(
'id' => 1,
'name' => 'RoD Sucks',
'dob' => '05/12/1985',
'parentname' => 'Found on roadside dead',
);
}

public function __get($string)
{
if (is_null($this->studentdata))
$this->studentdata $this->GetStudentData();

if (isset($this->studentdata[$string]))
return $this->studentdata[$string];

/* Handle other cases if need be */

return $string;
}
}

$s = new Student;

echo 
'Student name: '$s->name"<br />\n
DoB: "
$s->dob"<br />\n";

And now if someone adds something that'd be in the studentdata database and is loaded into that array (like I did today) automatically then the code doesn't have to be updated to reflect that.

Mike

  • Jackass In Charge
  • Posts: 11248
  • Karma: +168/-32
  • Ex Asshole - a better and more caring person.
Re: Implementing a throw/nothrow exception handling in PHP
« Reply #8 on: June 23, 2009, 01:17:10 AM »
You know what would solve the issue?  Function level inheritance.  Not sure how the code would look like but something like:
Code: [Select]
public function bar($para1, $para2, ...) extends NothrowToggle
{
}
public function baz()
{
}
And then bar would have the nothrow functionality but baz wouldn't.

Mike

  • Jackass In Charge
  • Posts: 11248
  • Karma: +168/-32
  • Ex Asshole - a better and more caring person.
Re: Implementing a throw/nothrow exception handling in PHP
« Reply #9 on: June 23, 2009, 09:35:53 AM »
Just had a thought:  I should use a naming convention for the functions in the child classes that'd be called in this manner.

Something like: nothrowable_funcname().  It'd still be called as funcname() to the public but __call would look for nothrowable_funcname.  That'd let me have protected functions for normal usage and make it obvious that these functions have a special operation.

webwhy

  • Jackass IV
  • Posts: 608
  • Karma: +15/-10
Re: Implementing a throw/nothrow exception handling in PHP
« Reply #10 on: June 23, 2009, 10:16:30 AM »
Quote
I should use a naming convention for the functions in the child classes that'd be called in this manner.

i like that better.  again the main problem i had with your idea is the misuse of protected methods.  using and excerpt from you own example
Code: [Select]
class c extends p
{
        protected function foo()
        {
                echo "foo\n";
        }

        private function bar()
        {
                echo "bar\n";
        }
}

$c = new c();

$c->foo();

your original idea allows for a client of the class to call a protected method 'foo' while the documentation is clear this is not what is intended for protected members of a class.

the naming convention allows you to use protected methods as intended by the language designers and OOP in general.  It's much better imho.  that's actually how a lot of AOP libraries do their dirty work

Mike

  • Jackass In Charge
  • Posts: 11248
  • Karma: +168/-32
  • Ex Asshole - a better and more caring person.
Re: Implementing a throw/nothrow exception handling in PHP
« Reply #11 on: June 23, 2009, 10:23:13 AM »
See this is why debates/discussions are good :D  It allows things to become better.

Too bad it isn't trivial (on the development and certainly not on the maintenance ends) to define your own visibility type.  Then instead of public or protected it could be something else that would demonstrate that this method's visibility is set such to enable this operation.