New exceptions mechanism

php.internals

Andi Gutmans

22 years ago
Hi, We rewrote the exception support. It fixes a few limitations and bugs in the old implementation, and allows exceptions to 'fire' much earlier than before. Generally, with the new mechanism, you're not supposed to touch EG(exception) directly, except for when you want to check whether there was an exception (you're not allowed to directly modify EG(exception)). - Generally, to throw an exception you need to use zend_throw_exception(). If you have special needs, and for some reason you wish to bypass zend_throw_exception(), you can use zend_throw_exception_internal(). Again, generally you shouldn't have to do that, though. - The new mechanism allows PHP to handle exceptions as soon as we return to the context of the execute loop. Exceptions still cannot be caught inside your extension code, as they can only be handled reliably in the context of execute(). For example, if an exception is thrown in a callback function that's called by array_walk(), it will only be caught when array_walk() returns. - Functions like array_walk() (typically functions that use zend_call_function() or call_user_function_ex()) should decide whether they want to abort when there is an exception, or ignore it. If you choose to abort, and let the exception propagate up, then you should simply return back as soon as you can. As soon as it reaches the execute loop - the exception will be handled (i.e., if there's a corresponding catch block it will be executed, or otherwise, the stack will unwind). If you choose to ignore it - you should call zend_clear_exception(TSRMLS_C). Note that you should make your choice and behave accordingly BEFORE making any further calls to PHP functions (or any code that may throw an exception). - To check whether there was an exception, you should still use the same method as before (check whether EG(exception) is not NULL), but again, you may not change it directly under any circumstances. Note - this (most probably) breaks the current implementation of set_exception_handler() so whoever is responsible for that piece of code please look into it, and ask us if you have any questions. Andi

Markus Fischer

22 years ago
I've a little issue with exception, however this isn't particular bound to the new mechanism and existed before. When calling a static method of a class from an objects method, the stack trace does not reveal the class name of the static class containing the static method. Example: $ cat test_exception.php <?php class StaticClass { function staticMethod() { throw new Exception("example"); } } class InstantiateMe { function method() { #throw new Exception("example"); StaticClass::staticMethod(); } } $obj = new InstantiateMe; $obj->method(); ?> Output: $ php test_exception.php Fatal error: Uncaught exception 'exception' with message 'example' in /home/mfischer/htdocs/php5/test_exception.php:4 Stack trace: #0 /home/mfischer/htdocs/php5/test_exception.php(11): InstantiateMe->staticMethod() #1 /home/mfischer/htdocs/php5/test_exception.php(16): InstantiateMe->method() #2 {main} thrown in /home/mfischer/htdocs/php5/test_exception.php on line 4 Of special interest, see #0 containing the line: InstantiateMe->staticMethod() instead of e.g. StaticClass::staticMethod() At least, saying 'InstantiateMe->' looks wrong to me as I'm calling 'StaticClass::' actually. - Markus

Stephane Drouard

22 years ago
== Quote from Markus Fischer (mfischer@gjat.josefine.at)'s article
> At least, saying 'InstantiateMe->' looks wrong to me as I'm calling > 'StaticClass::' actually.
You have to declare your method as static: <?php class StaticClass { static function staticMethod() { throw new Exception("example"); } } ?> Stephane

Markus Fischer

22 years ago
On Tue, Feb 03, 2004 at 04:22:14PM -0000, Stephane Drouard wrote :
> == Quote from Markus Fischer (mfischer@gjat.josefine.at)'s article > > At least, saying 'InstantiateMe->' looks wrong to me as I'm calling > > 'StaticClass::' actually. > > You have to declare your method as static: > > <?php > class StaticClass { > static function staticMethod() { > throw new Exception("example"); > } > } > ?>
Thank you. Though I've read the zend-engine-2.php page this must have slipped through. However, shouldn't the context be enough, from which a function is called, so the stack trace displays the information right? - Markus

Ferdinand Beyer

22 years ago
On 3 Feb 2004 at 17:34, Markus Fischer wrote:
> However, shouldn't the context be enough, from which a
function is
> called, so the stack trace displays the information right?
This is my guess: In your example staticMethod() is not regarded as a _static_ function. Instead, PHP runs it in the InstanciateMe-object's scope as if it would belong to the InstanciateMe class. If you try to access $this in staticMethod() it will certainly be the same as $obj. This behavior is needed to access overwritten methods from the parent class. In my opinion, it should not work for foreign classes, though.
-- Ferdinand Beyer <fb@fbeyer.com>

Markus Fischer

22 years ago
On Tue, Feb 03, 2004 at 06:12:41PM +0100, Ferdinand Beyer wrote :
> On 3 Feb 2004 at 17:34, Markus Fischer wrote: > > > However, shouldn't the context be enough, from which a > function is > > called, so the stack trace displays the information right? > > This is my guess: > In your example staticMethod() is not regarded as a _static_ > function. Instead, PHP runs it in the InstanciateMe-object's scope as > if it would belong to the InstanciateMe class. If you try to access $this > in staticMethod() it will certainly be the same as $obj. > > This behavior is needed to access overwritten methods from the > parent class. In my opinion, it should not work for foreign classes, > though.
Point taken. I can definitely live by the additional static keyword anyway. thanks, - Markus

Marcus Börger

22 years ago
Hello Markus, Tuesday, February 3, 2004, 6:19:59 PM, you wrote:
> On Tue, Feb 03, 2004 at 06:12:41PM +0100, Ferdinand Beyer wrote : >> On 3 Feb 2004 at 17:34, Markus Fischer wrote: >> >> > However, shouldn't the context be enough, from which a >> function is >> > called, so the stack trace displays the information right? >> >> This is my guess: >> In your example staticMethod() is not regarded as a _static_ >> function. Instead, PHP runs it in the InstanciateMe-object's scope as >> if it would belong to the InstanciateMe class. If you try to access $this >> in staticMethod() it will certainly be the same as $obj. >> >> This behavior is needed to access overwritten methods from the >> parent class. In my opinion, it should not work for foreign classes, >> though.
> Point taken. I can definitely live by the additional static keyword > anyway.
> thanks, > - Markus
You should enable E_STRICT severity by error_reporting: E_ALL|E_STRICT when developing new things. Besides from that the behavior is needed for BC reasons only.
-- Best regards, Marcus mailto:helly@php.net

Markus Fischer

22 years ago
On Tue, Feb 03, 2004 at 09:51:00PM +0100, Marcus Boerger wrote :
> > Point taken. I can definitely live by the additional static keyword > > anyway. > > You should enable E_STRICT severity by error_reporting: E_ALL|E_STRICT > when developing new things. Besides from that the behavior is needed > for BC reasons only.
Thanks for the explaination, makes sense. Still have to get used to the new E_STRICT ;) - Markus

Adam Bregenzer

22 years ago
On Tue, 2004-02-03 at 12:12, Ferdinand Beyer wrote:
> If you try to access $this in staticMethod() it will certainly be the > same as $obj.
I apologize if this is OT or just generally inappropriate for the internal list, but I had a question in the general list that is related to this. I was wondering if it was possible to retrieve the class name (i.e. class_name($this)) from within a static method. In php4 var_dump($this) in a static method returns NULL. Has this changed in php5, and if so what class name will it return when the static method is called from an extended class? Here is my post for reference: http://marc.theaimsgroup.com/?l=php-general&m=107575408628272&w=2 If there is another way to achieve what I am looking for I would be very appreciative if someone could point me in the right direction. Regards, Adam
-- Adam Bregenzer adam@bregenzer.net http://adam.bregenzer.net/

Stig S. Bakken

22 years ago
On Tue, 2004-02-03 at 22:47, Adam Bregenzer wrote:
> On Tue, 2004-02-03 at 12:12, Ferdinand Beyer wrote: > > If you try to access $this in staticMethod() it will certainly be the > > same as $obj. > > I apologize if this is OT or just generally inappropriate for the > internal list, but I had a question in the general list that is related > to this. I was wondering if it was possible to retrieve the class name > (i.e. class_name($this)) from within a static method. In php4 > var_dump($this) in a static method returns NULL. Has this changed in > php5, and if so what class name will it return when the static method is > called from an extended class? > > Here is my post for reference: > http://marc.theaimsgroup.com/?l=php-general&m=107575408628272&w=2 > > If there is another way to achieve what I am looking for I would be very > appreciative if someone could point me in the right direction.
Try __CLASS__. - Stig

Adam Bregenzer

22 years ago
Stig, On Tue, 2004-02-03 at 17:27, Stig S. Bakken wrote:
> Try __CLASS__.
Thank you for the reply. This does work for the class the method is defined in, however unfortunately it's not a solution for classes that inherit this method. Here's the sample code I used in my first post: class Foo { function getClassName() { // ??? } } class Bar extends Foo { } echo Foo::getClassName(); // returns 'foo' echo Bar::getClassName(); // returns 'bar' The solution I have right now that is inheritable, though very kludgy is this: class Foo { function someFunc($class_name) { return $class_name; } } class Bar extends Foo { function someFunc($class_name = NULL) { return parent::someFunc(isset($class_name) ? $class_name : __CLASS__); } } This does work, but I have to have this stub function copied into every class that inherits Foo, plus I have an extra parameter in there. Is there no better way? Thanks again, Adam
-- Adam Bregenzer adam@bregenzer.net http://adam.bregenzer.net/

Alan Knowles

22 years ago
It is a slightly annoying limitation.. - inherited static methods, cant guess their called class... I did look at a fix a while ago - it involved adding a pointer to the called class name string, to the exectuter globals, whenever a function is called in the engine... - then to retrieve it became problematic as - you cant really use a function as then it would assign the value again. - no-one was too keen to have 'dynamic' constants... Actually - I AFAIR someone said that it may be fixed in PHP5 back then :) - or suggested debug_backtrace.. (but I'm not sure if that is feasible) Regards Alan Adam Bregenzer wrote:

Adam Bregenzer

22 years ago
On Tue, 2004-02-03 at 20:14, Alan Knowles wrote:
> It is a slightly annoying limitation.. - inherited static methods, cant > guess their called class... > I did look at a fix a while ago - it involved adding a pointer to the > called class name string, to the exectuter globals, whenever a > function is called in the engine... - then to retrieve it became > problematic as > - you cant really use a function as then it would assign the value again. > - no-one was too keen to have 'dynamic' constants... > > Actually - I AFAIR someone said that it may be fixed in PHP5 back then > :) - or suggested debug_backtrace.. (but I'm not sure if that is feasible)
It would seem that having $this set in a static method would be the best way to solve this. What is interesting is that static variables in a method seem to be scoped to a class, even when inherited. For example: class Foo { function & getStaticVar($var) { static $static_variables; return $static_variables[$var]; } } class Bar extends Foo { } $foo_foo = & Foo::getStaticVar('foo'); $bar_foo = & Bar::getStaticVar('foo'); $foo_foo = 1; $bar_foo = 2; // And so (Foo::getStaticVar('foo') != Bar::getStaticVar('foo')) == TRUE I use this to have static variables without having to define getStaticVar in every class I use. Since this works it would seem there is some static context for classes somewhere. Is there a plan to implement static variables in php5? If so there would be good reason for $this to be set for static methods in a class. If this were true I could use class_name($this) in a static method to achieve what I am looking for. If there is no current effort to implement static variables I would be willing to make a go at it, if no one is opposed and there is still time to add it to php5...
-- Adam Bregenzer adam@bregenzer.net http://adam.bregenzer.net/

Stig S. Bakken

22 years ago
Hm, don't think debug_backtrace() can help you much. Reflection could work. - Stig On Wed, 2004-02-04 at 02:14, Alan Knowles wrote:
> It is a slightly annoying limitation.. - inherited static methods, cant > guess their called class... > I did look at a fix a while ago - it involved adding a pointer to the > called class name string, to the exectuter globals, whenever a > function is called in the engine... - then to retrieve it became > problematic as > - you cant really use a function as then it would assign the value again. > - no-one was too keen to have 'dynamic' constants... > > Actually - I AFAIR someone said that it may be fixed in PHP5 back then > :) - or suggested debug_backtrace.. (but I'm not sure if that is feasible) > > Regards > Alan > > Adam Bregenzer wrote: > > >Stig, > > > >On Tue, 2004-02-03 at 17:27, Stig S. Bakken wrote: > > > > > >>Try __CLASS__. > >> > >> > > > >Thank you for the reply. This does work for the class the method is > >defined in, however unfortunately it's not a solution for classes that > >inherit this method. Here's the sample code I used in my first post: > > > > > > > >class Foo { > > function getClassName() { > > // ??? > > } > >} > >class Bar extends Foo { > >} > > > >echo Foo::getClassName(); // returns 'foo' > >echo Bar::getClassName(); // returns 'bar' > > > >The solution I have right now that is inheritable, though very kludgy is > >this: > > > >class Foo { > > function someFunc($class_name) { > > return $class_name; > > } > >} > >class Bar extends Foo { > > function someFunc($class_name = NULL) { > > return parent::someFunc(isset($class_name) ? $class_name : __CLASS__); > > } > >} > > > > > > > >This does work, but I have to have this stub function copied into every > >class that inherits Foo, plus I have an extra parameter in there. Is > >there no better way? > > > >Thanks again, > >Adam > > > > > >
-- "Nearly all men can stand adversity, but if you want to test a man's character, give him power." - Abraham Lincoln

Ferdinand Beyer

22 years ago
On 3 Feb 2004 at 21:54, Adam Bregenzer wrote:
> Is there a plan to > implement static variables in php5?
--- Quote from ZEND_CHANGES --- * Member variables of classes can now be initialized. Example: <?php class foo { static $my_static = 5; public $my_prop = 'bla'; } print foo::$my_static; $obj = foo; print $obj->my_prop; ?> * Static Methods. The Zend Engine 2.0 introduces the 'static' keyword to declare a method static, thus callable from outside the object context. Example: <?php class Foo { public static function aStaticMethod() { // ... } } Foo::aStaticMethod(); ?> The pseudo variable $this is not available inside a method that has been declared static. ---------------------------------------------------
-- Ferdinand Beyer <fb@fbeyer.com>

Marcus Börger

22 years ago
Hello Andi, Tuesday, February 3, 2004, 2:01:12 PM, you wrote:
> Hi,
> We rewrote the exception support. It fixes a few limitations and bugs in > the old implementation, and allows exceptions to 'fire' much earlier than > before.
> Generally, with the new mechanism, you're not supposed to touch > EG(exception) directly, except for when you want to check whether there was > an exception (you're not allowed to directly modify EG(exception)).
> - Generally, to throw an exception you need to use > zend_throw_exception(). If you have special needs, and for some reason you > wish to bypass zend_throw_exception(), you can use > zend_throw_exception_internal(). Again, generally you shouldn't have to do > that, though.
> - The new mechanism allows PHP to handle exceptions as soon as we return to > the context of the execute loop. Exceptions still cannot be caught inside > your extension code, as they can only be handled reliably in the context of > execute(). For example, if an exception is thrown in a callback function > that's called by array_walk(), it will only be caught when array_walk() > returns.
> - Functions like array_walk() (typically functions that use > zend_call_function() or call_user_function_ex()) should decide whether they > want to abort when there is an exception, or ignore it. If you choose to > abort, and let the exception propagate up, then you should simply return > back as soon as you can. As soon as it reaches the execute loop - the > exception will be handled (i.e., if there's a corresponding catch block it > will be executed, or otherwise, the stack will unwind). > If you choose to ignore it - you should call > zend_clear_exception(TSRMLS_C). Note that you should make your choice and > behave accordingly BEFORE making any further calls to PHP functions (or any > code that may throw an exception).
> - To check whether there was an exception, you should still use the same > method as before (check whether EG(exception) is not NULL), but again, you > may not change it directly under any circumstances.
> Note - this (most probably) breaks the current implementation of > set_exception_handler() so whoever is responsible for that piece of code > please look into it, and ask us if you have any questions.
> Andi
The new exception implementation looks a bit nicer but needs some tweaks. Try this code: php -r 'reflection_class::export("xyz");' which dirctly segfaults. What happens is that the class xyz does not exist and reflection api throws an exception which results in a SEGV.
-- Best regards, Marcus mailto:helly@php.net

Zeev Suraski

22 years ago
At 00:29 05/02/2004, Marcus Boerger wrote:
>The new exception implementation looks a bit nicer but needs some >tweaks. Try this code:
A bit? It's a helluva lot nicer :)
>php -r 'reflection_class::export("xyz");' >which dirctly segfaults. What happens is that the class xyz does not exist >and reflection api throws an exception which results in a SEGV.
Yes, I think it's the same thing others are bumping into, but that's a very nice short reproducing script you got there :) We'll fix it tomorrow. Zeev

Zeev Suraski

22 years ago
I submitted a fix for this (as I suspected, it appears to be more of a bug in zend_call_function() than a bug in the new exceptions mechanism)... I'm not 100% sure about this fix as I didn't have time to investigate it too much, but it appears to be correct, and I wanted to allow you to go on testing :) I'll check into it further tomorrow. Zeev At 00:29 05/02/2004, Marcus Boerger wrote:

Andi Gutmans

22 years ago
Does this still segfault for you? It doesn't for me. Andi At 11:29 PM 2/4/2004 +0100, Marcus Boerger wrote:

Marcus Börger

22 years ago
Hello Andi, it works now fine :-) marcus Tuesday, February 10, 2004, 11:24:55 AM, you wrote: