i have the following logic in my model:
if ( $switch_obj->connect() ) {
if ( $data = $switch_obj->showIntAll() ) {
$switch_obj->disconnect();
return $data;
}
else {
$switch_obj->disconnect();
throw new Exception('Empty Data Set');
}
}
else {
throw new Exception('Connection');
}
This switch_obj that's being called has logic in it's constructor and destructor to increment / decrement counters respectively. (saved in a class called testclass).
So each time an object of type testclass is instantiated, a counter is increased. And then when destroyed, it's decremented.
However, I've just discovered a scenario that I'm not handling.
Fatal error: Call to undefined method testclass::showIntAll() in
/var/www/myapp/application/models/test_model.php on line 215
It's clear that I'm calling a method that doesn't exist, which I will resolve. But my question is this: in creating this error, i can see that the counter has already been incremented ... but not decremented because once this error is thrown, it never returns to the destructor method in my class.
How would I program for these types of scenarios? Obviously, in production, I won't get getting errors because of missing methods in testclass... but in case I do get an unexpected error where the testclass constructor is called and then it bombs, I'm just wondering what the best way is to handle this.
You might achieve something with register_shutdown_function. Your constructor could register a clean-up function which would get called if an error would occur. You'd have to be careful not to call the clean-up code twice though (once from the destructor and once from this registered function.
Not a pretty solution, but it could work :)
As far as i can remember destructors are not called on fatal errors
Related
Let's say I have this very basic class:
class MyClass {
private $_counters = [
'counter1' => 0,
];
public function plus($counter) {
$this->_counters[$counter]++;
}
}
Now there's a change to the code so that it doesn't break if the $counter does not exist:
public function plus($counter) {
if (array_key_exists($counter, $this->_counters)) {
$this->_counters[$counter]++;
}
}
How would I go about testing this for a possible regression that results in an "undefined index" error being thrown?
Would simply calling the function plus() with a non-existing $counter be enough? When all is good nothing happens but if a regression removed the array_key_exists() check for example, the "undefined index" would be thrown.
I'm quite new to unit testing so am I missing a better way to test this situation? It feels a bit weird to write a testcase without asserting something.
Create a test that calls plus() with something that doesn't exist like foo as you stated.
Though testing that an undefined index notice isn't thrown isn't really what you are testing. This specifies that your class contains all the counters in an array. This is an implementation detail of your class that your tests shouldn't care about.
You could write the class such that each of the counters is a property of the class instead. Really want you want to decide is what should happen when an counter that doesn't exist is used in the plus method.
Your tests should test for behaviors of the class. So for non-existent counters you will need to decide what should happen. An exception is thrown, a message logged or nothing at all.
$someClass sc=new SomeClass();
What I want to know is what will be in the variable sc if the constructor fails for some reason (like maybe not enough memory). I can' t find a straight answer?
With your example of memory issue, You get a fatal error and php ceases execution. You never get to the assignment of the varible $sc.
A constructor can fail for mainly two reasons:
Out of memory; not unique to objects, this causes a fatal error and your script won't continue.
An exception is thrown; your script will stop unless the exception is caught using a 'try-catch' clause.
try {
$sc = new SomeClass(); // exception is thrown inside the constructor
} catch (Exception $e) {
echo "Yikes, object didn't get created; error = {$e->getMessage()}\n";
}
first of all, the syntax for object initiation is incorrect, needs to be $sc = new SomeClass(); And you would get an memory exhaustion error, and variable $sc wouldn't be created.
The same thing that happens when any other type of code fails for some reason like not enough memory and such: the PHP runtime issues a fatal error and that's it. Object constructors are not special.
Are there situations in which this method would not be called?
I'm thinking to store a important variable into a persistent cache just before the cache object gets destroyed. This variable is used many times in the page so I wouldn't like to update the cache with it every time the variable changes...
Let's have a class:
class A {
public function __construct(){
echo "Construct\n";
}
public function __destruct(){
echo "Destruct\n";
}
}
And test code:
$test = new A();
die( "Dead\n"); // Will output Construct; dead; Destruct
$test = new A();
throw new Exception("Blah\n"); // Construct, Fatal error (no destruct)
$test = new A();
require_once( 'invalid_file.php'); // Construct, Fatal error (no destruct)
So basically: there are situations (fatal errors) when destructor won't be called.
Ah and this question has the same answer as this one: When will __destruct not be called in PHP? (+/-)
It is called as soon as there are no more references to that particular object, or during the shutdown sequence. The manual also states destructors are called when scripts are terminated with exit().
Aside from the issue pointed out by TimWolla, I am not aware of any problems with PHP destructors.
It seems there at least was a problem using Windows: https://github.com/WoltLab/WCF/blob/ff7e6ed381f2ccab7f51220f97087921133b2237/wcfsetup/install/files/lib/system/WCF.class.php#L122
I don't know whether this is still relevant.
consider this code:
class TestClass {
function __construct() {
set_error_handler(array($this,'error_handler'));
}
function error_handler($errno,$errstr,$errfile,$errline,$errcontext) {
trigger_error("\ncode: [$errno]\nfile: [$errfile -> $errline]\nSTR: [$errstr]",E_USER_ERROR);
}
}
count($nonExistentVariable);
//notice was thrown above but script carry on
//but here error handler is registered and...
$anInstance = new TestClass();
// rest of the code is affected by it
count($nonExistentVariable); // user error is thrown
So is there a way to make the error_handler function to fire only when error is encountered inside an instance and do not mess globally with other code ?
You could check the call stack in your error handler (via debug_backtrace()) and determine if it's coming from within the class you're interested in. If it's not coming from the class be sure to return FALSE and PHP will fall back on it's default handler.
http://php.net/manual/en/function.set-error-handler.php
In PHP, is it possible to get the file name of the script that instantiated an object?
For example I have a script called file.php that creates a new instance of class Class. The class has an Error object that, when triggered, returns some error information. I would like to show that file.php triggered the error.
The function debug_backtrace() may be of use? It shows the execution flow from bottom to top.
As Kai Sellgren said, the debug_backtrace() function can be used.
You can also throw and catch an exception to get a backtrace.
try
{
throw new Exception();
}
catch( Exception $e )
{
print_r( $e->getTrace() );
}
As getting a backtrace can take some time, you should benchmark both solutions to see which is the fastest.
And by the way, shoudn't your error object be a subclass of Exception? That's the reason why exceptions exists...
Any chance that error_get_last's returned array satisfies your need? The function 'returns an associative array describing the last error with keys "type", "message", "file" and "line". Returns NULL if there hasn't been an error yet. '
http://www.php.net/manual/en/function.error-get-last.php