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
Related
This is more just for documentation, since I've already solved the issue, but it was subtle and difficult enough to debug that I thought it would be useful in the public sphere.
The issue was that I had a try/catch block in an object method that just wasn't working. The reduced example is in two files, which look like this:
TestClass.php:
<?php
//TestClass.php
namespace MyTest;
class TestClass {
public function __construct() {
\e("Initializing object");
try {
\e("Trying object exception");
\throwTestException("Failing gracefully in object");
\e("After exception");
} catch (Exception $e) {
\e($e->getMessage());
}
\e("After object init exception");
}
}
?>
Main.php:
<?php
//Main.php
function e($str) { echo "\n$str"; }
function throwTestException($msg) {
throw new RuntimeException($msg);
}
require "TestClass.php";
e("Beginning");
try {
e("First try");
throwTestException("Failing gracefully first");
e("After exception");
} catch (Exception $e) {
e($e->getMessage());
}
e("Ending");
e('');
e('Beginning object test');
new \MyTest\TestClass();
e('Ending object test');
?>
The expected result on loading Main.php was this:
Beginning
First try
Failing gracefully first
Ending
Beginning object test
Initializing object
Trying object exception
Failing gracefully in object
After object init exception
Ending object test
What I actually got was something like this:
Beginning
First try
Failing gracefully first
Ending
Beginning object test
Initializing object
Trying object exception
Fatal Error: Uncaught Exception: Failing gracefully in object......
As you can see, the exception was not being caught. I tried all sorts of things and just couldn't figure out why it wasn't being caught. And then.... (See answer below)
I realized it was a namespace issue. Because I had declared TestClass within the namespace MyTest, and throwTestException in the global namespace, my reference to Exception within the class method was tacitly resolving to \MyTest\Exception and thus NOT matching the actual exception being thrown, \RuntimeException. And since I wasn't actually trying to instantiate the exception from within the namespace, no "Unknown Class" errors emerged to reveal what was happening.
The solution, then, was simply to properly resolve the exception class I was trying to catch:
catch(\Exception $e) { .... }
To be fair, this became obvious as I built my highly reduced example. It wasn't obvious initially because the exception I was expecting to catch was being generated by the class's superclass (which was the SQLite3 class). Thus, I didn't have to worry about the namespace when generating the exception, and all I was thinking about when catching it was to use the most general form of exception, Exception. And again, since I wasn't instantiating that exception -- only matching against it in a catch block --, I didn't get any notices that it was unresolved.
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
$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.
How to get a caller method name in an class constructor, get_called_class() gives me the name of an extended class which was instantiated but how can I get the name of a method which was called in that class?
I need this for a production state so debug_backtrace() is not a good solution.
Why do you need this? If you have any consideration for other coders on the project and standards, find a solution which does not require the constructor to know about how it was called. If all other solutions fail, define a static factory method and make the constructor private for more control over instantiation.
It looks like there is not way to get it without debug_backtrace(). Still you can create your our function for this. Example from manual:
function get_caller_method()
{
$traces = debug_backtrace();
if (isset($traces[2]))
{
return $traces[2]['function'];
}
return null;
}
There is no automatic way of doing this other than the slow/ugly debug_backtrace, or passing the name as an argument to the constructor.... but you shouldn't really be building these kinds of dependencies into your classes.
If you don't want to use the debug_backtrace() function, you can also throw and catch an exception, to get a backtrace.
But that's also ugly... Anyway:
try
{
throw new Exception();
}
catch( Exception $e )
{
print_r( $e->getTrace() );
}
I don't know why you need the caller. Maybe you can fix this by using a different logic.
It should be better than getting a backtrace...
EDIT
The exception does not need to be thrown. Sorry for that part.
So you can simply use:
$e = new Exception();
print_r( $e->getTrace() );
But once again, you shouldn't need to use this!
I'm having a little trouble organizing my error messages for two interacting classes. One object has states that are 'error-ish', where something went wrong, or happened unexpectedly, but the situation is still salvageable. I don't want to use exceptions, 1. because they only have a single string for the message, and 2. because I want to access the object after the error. At the very least, I want to use some of its get() methods to construct a useful error message after the exception!
Ultimately I have two messages I want to communicate: one to myself as the coder, that something went wrong. This string would have the technical details of the file, line, function/method, arguments, and results. Obviously, I don't want to show this to the user, so there is another string of an error message that I want to show the user ("That email address was not found" kind of thing ).
So the thought occurs to me to build an error message array, which could use the error code from exceptions, or a status code, as keys for various messages. ( Though if I do this, where do I store the array of messages? ) Another option might be to make an error status object.
Is there anything like "error patterns", similar to design patterns?
Exceptions really are your best option, they do everything you asked. Even multiple messages are possible, since exceptions are just classes that you can extend. You could just pass the object that causes the exception to said exception.
<?php
class ExampleException extends Exception {
private $secondMessage;
private $objectThatCausedIt;
public function __construct( $secondMessage, $objectThatCausedIt ) {
parent::__construct(
"Descriptive message for developer",
// error code for this type of error
1000 );
$this->secondMessage = $secondMessage;
$this->objectThatCausedIt = $objectThatCausedIt;
}
public function getSecondMessage() {
return $this->secondMessage;
}
public function getObjectThatCausedIt() {
return $this->objectThatCausedIt;
}
}
class Example {
public function causeException() {
throw new ExampleException( "Second Message", $this );
}
}
Now you just use the class and wrap the call that might throw the exception in a try-catch block.
<?php
$example = new Example();
try {
$example->causeException();
}
catch ( ExampleException $e ) {
// kind of pointless here, just an illustration
// get object that caused it and do something with it.
dump_and_log_function( $e->getObjectThatCausedIt() );
// or just use $example, which is still "alive"
// and references the same instance
dump_and_log_function( $example );
}
Extending Exception has the benefit that you also get a stack backtrace. The backtrace contains information like in what file, line and function the exception occured. You can also access the error code, the message and more. I suggest you read the PHP documentation on Exceptions (http://php.net/manual/en/class.exception.php) for more information.
Regarding the error messages and logging, I usually use a singleton class for that. A singleton class has only one instance of itself (in case you didn't know.) Here is an extremely simple example:
<?php
class Log {
private $logFile;
private static $instance;
/* Log instances may only be constructed in Log::getInstance */
private function __construct() {
$this->logFile = fopen( "path/to/log/file", "a" );
}
public logMessage( $message ) {
fwrite( $this->logFile, $message );
}
public static getInstance() {
if ( !self::$instance ) self::$instance = new self();
return self::$instance;
}
}
Now going back to the exception handling throw-catch block, you can change it to something like this:
<?php
$example = new Example();
try {
$example->causeException();
}
catch ( ExampleException $e ) {
// log developer message and backtrace
Log::getInstance()->logMessage( $e->getMessage() );
Log::getInstance()->logMessage( $e->getTraceAsString() );
// or more compact by casting to string
Log::getInstance()->logMessage( (string)$e );
// and now print error for users
echo "<p>An Error has occured: {$e->getSecondMessage()}</p>";
}
Instead of echoing the error message right away, you could make the Log class have a property with an array of messages. Then you could list them later in a view script all at once. You could also make the logMessage method store the messages in the session so they could be displayed after a refresh (just don't forget to clear the messages from the session or they will be displayed over and over again ;-).
I don't want to use exceptions, 1.
because they only have a single string
for the message, and 2. because I want
to access the object after the error.
At the very least, I want to use some
of its get() methods to construct a
useful error message after the
exception!
You can create your own exception classes and you can extend PHP's exception classes, adding the support you need. However, possibly you can have two settings, developer and client, where client errors goto the display and developer errors don't instead going to a log file or something.
Which, translates into two custom exception types (though you could have many more, I'm saying two distinct base classes).