9

I know, that by its very definition, a fatal exception is supposed to kill the execution, and should not be suppressed, but here's the issue.

I'm running a script that scrapes, parses and stores in a DB about 10,000 pages. This takes a couple of hours, and in rare cases (1 in 1000) a page fails parsing and throws a fatal exception.

Currently, I'm doing this:

for ($i=0;$i<$count;$i++)   
        {
            $classObject = $classObjects[$i];           

            echo $i . "   :   " . memory_get_usage(true) . "\n";

            $classDOM = $scraper->scrapeClassInfo($classObject,$termMap,$subjectMap);           
            $class = $parser->parseClassInfo($classDOM);                    
            $dbmanager->storeClassInfo($class);         

            unset($classDOM,$class,$classObject);           
        }        

Can I do something like

for ($i=0;$i<$count;$i++)   
{
   $classObject = $classObjects[$i];            
   echo $i . "   :   " . memory_get_usage(true) . "\n";

   try
   {
      $classDOM = $scraper->scrapeClassInfo($classObject,$termMap,$subjectMap);         
      $class = $parser->parseClassInfo($classDOM);                  
      $dbmanager->storeClassInfo($class);           
      unset($classDOM,$class,$classObject);         
    }
    catch (Exception $e)
    {
       //log the error here
       continue;
    }
}

The code above doesn't work for fatal exceptions.

Would it be possible to do something like this: If I moved the main loop into a method, and then call the method from register_shutdown_function ?

Like this:

function do($start)
{
   for($i=$start;$i<$count;$i++)
   {
      //do stuff here
   }
}

register_shutdown_function('shutdown');

function shutdown()
{ 
   do();
}

This is the message that is output when execution stops:

Fatal error:  Call to a member function find() on a non-object in ...

I expect this above message when a page isn't parse-able by the method I am using. I'm fine with just skipping that page and moving on to the next iteration of the loop.

7
  • 3
    What is a fatal exception? How is your catch (Exception $e) not working? Commented Jan 12, 2012 at 17:11
  • 1
    Indeed..... Is someone throwing something that doesn't extend Exception?
    – Wrikken
    Commented Jan 12, 2012 at 17:14
  • ...in which case, can't you just do } catch ($e) {?
    – DaveRandom
    Commented Jan 12, 2012 at 17:16
  • 1
    I think he means Fatal error. What is the Fatal error message you recieve? This will give you a clue as to what needs to be done. May not be an exception at all.
    – webbiedave
    Commented Jan 12, 2012 at 17:16
  • @CharlesSprayberry: posted the message that gets output (see edit). I guess my wording was wrong. Its not a fatal exception but a fatal error.
    – Ayush
    Commented Jan 12, 2012 at 17:17

4 Answers 4

11

Fatal errors are fatal and terminate execution. There is no way around this if a fatal error occurs. However, your error:

Fatal error: Call to a member function find() on a non-object in ...

is entirely preventable. Just add a check to make sure you have an instance of the correct object, and if not, handle the error:

if ($foo instanceof SomeObject) {
    $foo->find(...);
} else {
    // something went wrong
}
2
  • Yes, this seems the best way to handle it rather than let it escalate to a fatal error
    – Ayush
    Commented Jan 12, 2012 at 17:25
  • @mfonda how to find what type is it ? Commented Feb 18, 2013 at 19:03
6

First, there is a distinct difference between exceptions and errors. What you encountered is an error, not an exception. Based on your message and the code you posted the problem is with something you haven't put into your question. What variable are you trying to call find() on? Well, that variable isn't an object. There is no way to trap fatal errors and ignore it, you must go find where you are calling find() on a non-object and fix it.

3
  • The html dom parser library I'm using returns an object (the DOM structure of the page), and I use that object->find() to find certain elements. When a page isn't parse-able, it doesn't return the object and thus object->find() throws a fatal error. I suppose I could put a is_object() check before calling find()
    – Ayush
    Commented Jan 12, 2012 at 17:23
  • 1
    @xbonez That would be the answer to your problem, since the DOM parser failing will not itself cause a fatal error. You could also use the instanceof operator as suggested by @mfonda below.
    – DaveRandom
    Commented Jan 12, 2012 at 17:25
  • 2
    @xbonez That is one option. Or you could implement the NullObject design pattern. Basically you setup an implementation that when you call $object->find() it returns an empty object that you can still call methods on. This will ensure that you always have an object to call a method on and you don't have to do those object checks. Commented Jan 12, 2012 at 17:27
1

Seems to me like the only possible way to "catch" a faltal error is with the registering a shutdown function. Remember to add all (or maybe groups of) queries into transactions and maybe roll them back if something fails, just to ensure consistency.

2
  • 1
    On a fatal error, I'm not looking to exit gracefully (clean up, rollback transactions and then exit). I'm looking to log the error and continue. Is that possible? I suppose there is the chance that it isn't.
    – Ayush
    Commented Jan 12, 2012 at 17:20
  • 1
    Check this link php.net/manual/en/function.register-shutdown-function.php , check the messages they made, some of them are related to your problem. But you should try to avoid erros as much as posible in your code
    – guiman
    Commented Jan 12, 2012 at 17:27
1

I have had a similar problem to this, and I found that using a is_object() call before the find() call allows you to avoid the fatal error.

Not the answer you're looking for? Browse other questions tagged or ask your own question.