Link to home
Start Free TrialLog in
Avatar of elepil
elepil

asked on

PHP throw/catch question

My issue is why PHP is adding so much verbosity to my error message, and I'd like to know how to make PHP just pass the message I gave it, and no more, no less. Let me demonstrate with this code snippet:

function a() {
    try {
        $name = "Mary";
        $e = new Exception("INTENTIONAL ERROR FROM a()", 5); // Notice that my string message is very short, and I gave it an error code of 5
        throw new Exception($e); // Then I throw the exception
    } catch (Exception $e) {
        // But this next statement gives a loooooong message ,much more than I specified. How can I make it just display
        // what I specified, no more and no less?
        echo "I'm in a()'s catch block [[" . $e->getCode() . ']] : [[' . $e->getMessage() . ']]<br/>';
        throw new Exception($e);
    }
}

function b() {
    try {
        a();
    } catch(Exception $e) {
        echo "I'm in b()'s catch block [[" . $e->getCode() . ']] : [[' . $e->getMessage() . ']]<br/>';
    }
}

b();

Open in new window


I was expecting this:

I'm in a()'s catch block [[0]] : [[INTENTIONAL ERROR FROM a()]]
I'm in b()'s catch block [[0]] : [[INTENTIONAL ERROR FROM a()]]

Open in new window


Instead, I got:

I'm in a()'s catch block [[0]] : [[exception 'Exception' with message 'INTENTIONAL ERROR FROM a()' in C:\xampp\htdocs\newdimension\public\test\test.php:8 Stack trace: #0 C:\xampp\htdocs\newdimension\public\test\test.php(18): a() #1 C:\xampp\htdocs\newdimension\public\test\test.php(24): b() #2 {main}]]
I'm in b()'s catch block [[0]] : [[exception 'Exception' with message 'exception 'Exception' with message 'INTENTIONAL ERROR FROM a()' in C:\xampp\htdocs\newdimension\public\test\test.php:8 Stack trace: #0 C:\xampp\htdocs\newdimension\public\test\test.php(18): a() #1 C:\xampp\htdocs\newdimension\public\test\test.php(24): b() #2 {main}' in C:\xampp\htdocs\newdimension\public\test\test.php:9 Stack trace: #0 C:\xampp\htdocs\newdimension\public\test\test.php(18): a() #1 C:\xampp\htdocs\newdimension\public\test\test.php(24): b() #2 {main}]]

Open in new window


My experience with other languages do not do this to my messages. Or is there maybe some kind of software switch I need to tweak in the .ini files??
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

"Why" may be the wrong question here - probably it's better to ask "what" does PHP do, given a set of inputs and circumstances.  Or to put it another way, if you don't want to see messages, don't use echo to write the messages!  When you echo an object, the __toString() method gets called, and unless you override that method (or know what it does and like its behavior) you're going to get whatever the author of the class wants to give you.  The same is true of the class properties - their settings are only under your control if you extend the class and modify them.  There are many other things you can do with an Exception object besides write messages.

As always, var_dump() is our friend.  You can use it to inspect the properties of objects.

FWIW, I have never seen correct programming that throws the same exception from inside the block that catches the exception, so I would be inclined to redesign that code.

Now with that said, let's gather some code references that describe the objects you're dealing with:

The base class for all Exceptions:
http://php.net/manual/en/class.exception.php There are only four properties, and a handful of getter methods.

The base class constructor:
http://php.net/manual/en/exception.construct.php

There are many articles that detail the strategies for using Exceptions.  My friend Brandon Savage has an introductory here that is pretty good (this is stuff you already know, but expressed in PHP terms).
http://www.brandonsavage.net/exceptional-php-introduction-to-exceptions/

How to write and handle exceptional conditions?  Start with the premise that Exception is only a base class and consider it unusable without your extensions.  If the only thing you're going to throw is Exception, why not just use die()?  The reason I put it this way is that Exception by itself is kind of useless.  It's the base class without any meaning or recovery mechanisms, and if you can't recover from an Exception, it should not be an exceptional condition, it should be considered a fatal condition.  There is no more information unless your code adds the information by extending the Exception class and building an error object that gives useful details about your unique exceptional condition.

To give an example, consider the PDOException.  There are many reasons a database can "hiccup" and only some of them are fatal.  One of the recoverable PDO exceptions can arise when you try to INSERT a duplicate into a column marked UNIQUE.  In that case PDO will throw a PDOException with error number 1062.  You can catch this error number and retry the INSERT with different data.  That is a useful way of working with an exception.  Another useful way (for developers) would be to catch the error number and SQLSTATE, and translate that numeric information into href-links to the related documentation.  The exception handler that caught PDOException could display this information so the developer would immediately see a clickable link to the documentation that described corrective practices.

PHP offers exception chaining but it's a little lame - most of the built-in PHP exceptions do not know about the "previous" property.  And as many smart people have discovered, exception chains introduce the same cognitive load that we had with GOTO statements.  It's like multiple inheritance - just don't do that.

Here is a little piece of code that can show you how PHP Exceptions work.  It's descriptive (not prescriptive) and intended only to illustrate the logic and show the contents of the Exception object.  Try it a few different ways to see what it does.
http://iconoun.com/demo/try_throw_catch.php?q=0
http://iconoun.com/demo/try_throw_catch.php?q=1
http://iconoun.com/demo/try_throw_catch.php?q=2
http://iconoun.com/demo/try_throw_catch.php?q=3
<?php // demo/try_throw_catch.php

/**
 * Illustrate the logic of try{} throw{} catch{}
 *
 * Usage: try_throw_catch.php?q=1 (vary the value of q=)
 *
 * References:
 * http://php.net/manual/en/reserved.exceptions.php
 * http://php.net/manual/en/language.exceptions.php
 * http://php.net/manual/en/language.exceptions.extending.php
 */
error_reporting(E_ALL);
echo "<pre>";


Class ScrewUP
{
    // CONSTRUCTOR SETS A CODE PROPERTY FOR THE OBJECT
    public function __construct($q)
    {
        // CHOOSE AN INTEGER VALUE FROM ZERO TO THREE
        $this->code = 0;
        if (!empty($q)) $this->code = floor($q % 4);
    }

    // THIS WILL THROW SOME KIND OF EXCEPTION BASED ON THE CODE
    public function hiccup()
    {
        // THESE EXCEPTIONS WILL BE CAUGHT
        if ($this->code == 0) throw new Exception_zero('Hello');
        if ($this->code == 1) throw new Exception_one('World');

        // THIS WILL BE AN UNCAUGHT EXCEPTION: FATAL ERROR
        if ($this->code == 2) throw new Exception_two('Foobar');

        // THIS WILL BE AN UNEXTENDED EXCEPTION
        throw new Exception('Naked');
    }
}

Class Exception_Zero extends Exception
{
    public function __construct($x)
    {
        parent::__construct();
        // echo "Exception Zero: $x ";
    }
}

Class Exception_One extends Exception
{
    public function __construct($x)
    {
        parent::__construct();
        // echo "Exception One: $x ";
    }
}


// INSTANTIATE THE CLASS USING THE URL q= VARIABLE
$q = !empty($_GET['q']) ? $_GET['q'] : 0;
$x = new Screwup($q);

// RUN THE TRY/CATCH LOGIC TO THROW AN EXCEPTION
try
{
    $x->hiccup();
}
catch (exception_One $e)
{
    echo "I Have Caught Exception Number: $x->code ";
    echo PHP_EOL;
    var_dump($e);
}
catch (exception_Zero $e)
{
    echo "I Have Caught Exception Number Zero ";
    echo PHP_EOL;
    var_dump($e);
}
catch (exception $e)
{
    echo "I Have Caught an UnExtended Exception ";
    echo PHP_EOL;
    var_dump($e);
}

Open in new window

Avatar of elepil
elepil

ASKER

Ray,

[/When you echo an object, the __toString() method gets called

Your assessment is totally incorrect. I did not echo the object, I echoed specific properties (i.e., $e->getCode() and $e->getMessage()). Those do NOT invoke the Exception object's __toString() method.

If the only thing you're going to throw is Exception, why not just use die()

Because die() ends program execution, not something I want. Not all exceptions are irrecoverable.

The reason I put it this way is that Exception by itself is kind of useless

I'm sorry to say, but I could not disagree with you more. If you are not familiar with how to use Exceptions, please do not condemn the concept. There is a reason why Java, C#, and a whole host of other OOP languages use the try/catch construct as their primary (if not only) error handling mechanism that allows 'throwing' of exceptions up the call stack. To call it useless just emphasizes the fact that all you've used in your life is PHP.

Despite the length of your response, I do not have an answer to my question because I still do not know how to rectify the verbosity of Exceptions. You do understand I cannot give a good grade based on length of response. As it stands, it will be a 'C', but of course you can always change it later to an 'A'.
Avatar of Mark Brady
@elepil - Dude you really need to calm down. I have only read two of your questions and participated in one and in both cases you are being very argumentative. You need to look at the bigger picture here. Who really cares WHY a language does something. Why not find out the BEST way to handle those situations from real experts that do this for a living. This is a site of learning and understanding things we do not. If your goal is to better your knowledge and understanding of something then you have come to the right place. We WILL be able to help you learn. However you need to be a little more polite when someone spends time and energy to try to help you. I for one will not ever participate in anything you post. Your attitude to people trying to help you is dismal.
@elepil: You don't need to get emotional about this.  I know how to use exceptions, and I do not condemn the concept.  They are appropriate for exceptional conditions, but if you don't have a recovery path what would you do with the exception?  When you don't have a recovery path, by definition, you have an unrecoverable error.  Log it, and terminate the request with the appropriate response to the client.

Let me clarify this: "Exception by itself is kind of useless" because if you do not extend the Exception class and write the code that is appropriate to the anticipated exceptional condition, you're only going to get the kind of thing that you're seeing in the code you wrote above.  Please go back and look at the code sample I posted.  It shows how to extend Exception and deal with an Exceptional condition.  It also shows what will happen if your code doesn't have an Exception handler and an exception gets thrown.

To be honest, I didn't really read your code before.  I've never seen any use to throw Exception in the Exception handler, and I'm a little surprised that PHP even allows that.  I'll go back and try to make sense of the first code sample here.
Finally, suggesting Ray will abuse his power as a moderator by changing your grade from a C to an A is totally unacceptable and you owe him an apology.
Avatar of elepil

ASKER

Mark,

I have made many posts in this forum. This is the first time I've been irked enough to respond by the way administrators handled my post. If you choose not to participate in any future questions I post, no one is twisting your arm, and I do not appreciate the rashness of how you judge people.

Also, you are missing the point. This whole issue began because Ray changed the grade to my other post. This is NOT the first time he has done this, and I've let all past incidents slide. But this time, when it is blatantly clear that I do not feel I got the help I was asking for, and to insist that it should be an 'A' just defies my sense of fairness and objectivity.
Avatar of elepil

ASKER

Ray,

I know how to use exceptions, and I do not condemn the concept.

You had me fooled. You called it "useless".

but if you don't have a recovery path what would you do with the exception

And how do you know that? The purpose of throwing an Exception is because you want the exception handled somewhere else. When I make an ajax call to a PHP file, and something goes awry inside, you throw an Exception so that the ajax will properly pass it to the .fail handler where I would handle the error (e.g. by way of displaying it in the proper area on the screen). Of course there is always a recovery path. Who throws Exceptions without one?

I've never seen any use to throw Exception in the Exception handler, and I'm a little surprised that PHP even allows that

Again, you say you don't condemn it, yet you do.

I'll go back and try to make sense of the first code sample here.

Yes, I would greatly appreciate it if you did that. You have helped me many times in the past, so I do have respect for your knowledge and abilities. But once in a while, you do go off on a tangent, and while I do not desire to be confrontational, I had to say something when you say things that I feel are way off.
Avatar of elepil

ASKER

Mark,

Finally, suggesting Ray will abuse his power as a moderator by changing your grade from a C to an A is totally unacceptable and you owe him an apology.

You're telling me this, just right after Ray changed the grade I put on a post from 'B' to 'A'?
The comments in the code below are my attempt to annotate what is going on here, but I may still be confused because nobody would write code like this.  The problem of "built up" messages arises because of __toString().  It is called when a script uses an object as if it were a string.  This is the same thing as echo; it's just under the covers and out of your sight behind an abstraction layer of the Exception constructor and the PHP magic methods.  I think the basic problem is that the constructor for Exception expects a string in the first argument, and it got objects.  This is all documented in the man pages, so I put comments into the code at the top to give you the links to the man pages.  PHP has no type-hint for anything other than generic array and named object.  You can't type-hint a string.  The language, for better or worse, just assumes everyone has read the manual and used the code the right way.
http://iconoun.com/demo/temp_elepil.php
<?php // demo/temp_elepil.php

/**
 * SEE http://www.experts-exchange.com/Programming/Languages/Scripting/PHP/Q_28658286.html#a40731380
 * REF http://php.net/manual/en/language.exceptions.php
 *     http://php.net/manual/en/class.exception.php
 *     http://php.net/manual/en/exception.construct.php
 */
error_reporting(E_ALL);
echo '<pre>';

function a() {

    // THIS CODE BLOCK WILL THROW AN EXCEPTION
    try {
        // THIS VARIABLE IS NOT USED IN THE SCRIPT SO I COMMENTED IT OUT
        // $name = "Mary";

        // THIS ATTEMPTS UNDOCUMENTED BEHAVIOR.  WE DO NOT INSTANTIATE EXCEPTION OBJECTS, WE THROW THEM
        $e = new Exception("INTENTIONAL ERROR FROM a()", 5); // Notice that my string message is very short, and I gave it an error code of 5

        // SHOW WHAT IS PRESENT IN $e (NOT A STRING, AS CALLED FOR IN THE DOCUMENTATION)
        var_dump($e);
        echo PHP_EOL;

        // THIS WILL THROW AN EXCEPTION USING THE $e VARIABLE AS A STRING.  SINCE $e IS NOT A STRING, PHP WILL CALL __toString()
        throw new Exception($e); // Then I throw the exception

    // THIS CODE BLOCK WILL CATCH THE EXCEPTION THROWN IN THE TRY BLOCK ABOVE
    // THEN IT WILL THROW ANOTHER EXCEPTION -- BUT TO WHAT LOGICAL EFFECT?
    } catch (Exception $e) {
        // But this next statement gives a loooooong message ,much more than I specified. How can I make it just display
        // what I specified, no more and no less?

        // SHOW WHAT IS PRESENT IN $e NOW = AN EXCEPTION OBJECT
        var_dump($e);
        echo PHP_EOL;

        // THIS WILL PRINT OUT SOME INFORMATION FROM THE $e EXCEPTION OBJECT
        echo "I'm in a()'s catch block [[" . $e->getCode() . ']] : [[' . $e->getMessage() . ']]<br/>';

        // THIS WILL THROW A NEW EXCEPTION REPEATING THE SAME MISTAKE ABOVE - USING AN OBJECT IN PLACE OF THE EXPECTED STRING
        // PROBABLY THIS EXCEPTION WILL GET CAUGHT INSIDE OF THE b() FUNCTION
        throw new Exception($e);
    }
}

function b() {

    // THIS CODE BLOCK WILL TRY THE FUNCTION a() THAT WILL THROW AN EXCEPTION AND CATCH THE EXCEPTION INSIDE a()
    try {
        a();

    // THIS CODE BLOCK WILL CATCH THE LAST THROWN EXCEPTION AND ASSIGN IT TO $e
    } catch(Exception $e) {

        // SHOW WHAT IS PRESENT IN $e NOW = AN EXCEPTION OBJECT
	    var_dump($e);
	    echo PHP_EOL;

        echo "I'm in b()'s catch block [[" . $e->getCode() . ']] : [[' . $e->getMessage() . ']]<br/>';
    }
}

// THIS WILL START THE CASCADE OF TRY {} CATCH{} PROCESSES
b();

Open in new window

I hope this is helping.  If not, please post back with any questions.  And if we say, "don't do that," please understand that we have reasons for saying these things - we know most of the right ways to use PHP and if you can tell us what you want to do in plain, non-technical language, we can usually come up with a "best practices" approach to the problem.
ASKER CERTIFIED SOLUTION
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of elepil

ASKER

Ray,

The code snippet I provided was a sample quick-and-dirty to demonstrate my issue. I don't think you will appreciate it if I posted my actual application code because it would be too long and interspersed with things unrelated to my issue. Let me reconstruct my code scenario, but this time closer in context to how I'm actually using it in my application.

Let's say I have an .ajax() call on index.php, which is the page the user is interacting with by way of a form. When the form gets submitted, .ajax() will transfer control to a page indicated by the URL value, which let's say is "URL: loginAuthenticate.php".

If any problems occurs within loginAuthenticate.php, I obviously cannot handle the error there, hence the need to report it back to index.php. At this point, there are two ways of doing this:

1) I could echo a string back to index.php and let .ajax()'s .done() handler deal with it. But why let a handler that denotes a successful operation handle an error condition?

2) I could throw an Exception with the appropriate message and let .ajax()'s .fail() handler deal with it. I feel this is more appropriate because that is what .ajax()'s fail() handler is for.

Hence the need for the throwing of Exceptions. It is a mechanism to pass error information across call stack levels to where the error can actually be handled. Executing a die() is out of the question. To further hypothesize this scenario, imagine if loginAuthenticate.php has it's own .ajax() which calls another PHP page (which I would avoid doing), and something goes wrong there instead, then that page has to throw an Exception back to loginAuthenticate.php, which then has to throw the Exception back to index.php, because there is no way to gracefully handle the error condition other than in index.php. Do you now see the justification why throwing Exceptions is so important?

Now going back to your response, you said the mere throwing of an Exception calls the __toString() method. If PHP handles this anyway similar to other programming languages, it should not. It should merely be passing a reference to the Exception object from one stack element to the other, that's all.

In other languages, when I do: throw new Exception("Username/Password combination is invalid."), wherever this Exception object eventually lands, it's getMessage() method should simply display "Username/Password combination is invalid.". My problem with PHP is that a lot more is getting passed such that it becomes inappropriate to display the getMessage() text content in my index.php. Now I know there is a workaround, that I can go by the getCode() instead of the getMessage(), and within my .fail() handler will be a switch statement for the string messages based on the numeric error code, but that would be clumsy because I would no longer be using Exception handling the way it was intended. In short, I should be able to bank on the Exception object's getMessage() to reliably display the message I had given it when the error occurred, and that's not happening.

I appreciate your response, but I am still unclear how I can achieve what I'm trying to achieve. I do hope I made my issue clear though.
Avatar of elepil

ASKER

Ray,

Well, I believe you're right about throw new Exception($e) calling the __toString(). That is highly bizarre and does not happen in other languages, but I'm beginning to realize it it's a mistake to assume PHP to behave like other languages.

I took your sample code and did it the way you did, the message came across correctly. But when I created the exception object first, then passed it to the throw statement, it appended a lot of extra content to the message, and it definitely looks like what a __toString() method would do.

You get a very well-deserved 'A' this time. I thank you for your help.
OK, let me see if I understand this correctly.  When you speak of .ajax(), .done(), and .fail() are you talking about JavaScript?
Avatar of elepil

ASKER

Yes, jQuery to be exact, but the same principle holds if you just used regular JavaScript.