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!
Related
I've been searching for an existing question that already asks this, but I wasn't able to find any questions that quite ask what I'm trying to figure out. The most similar question I could find was this: php 5.3 avoid try/catch duplication nested within foreach loop (code sandwich)
Okay so the place I work at has a web application with a PHP back end. We use an MVC type structure. I'm writing a controller that has multiple methods and in each of my methods I'm wrapping my code with identical try / catch code. In the catch, I pass the exception, a reference to the class, and a reference to the function to a method that builds an error message so that the error messages are formatted the same across the application. It looks something this:
class MyController {
public function methodA() {
try {
// code for methodA
} catch(Exception $e) {
$errorMessage = Tasks::buildErrorMessage($e, __CLASS__, __FUNCTION__);
throw new Exception($errorMessage);
}
}
public function methodB() {
try {
// code for methodB
} catch(Exception $e) {
$errorMessage = Tasks::buildErrorMessage($e, __CLASS__, __FUNCTION__);
throw new Exception($errorMessage);
}
}
public function methodC() {
try {
// code for methodC
} catch(Exception $e) {
$errorMessage = Tasks::buildErrorMessage($e, __CLASS__, __FUNCTION__);
throw new Exception($errorMessage);
}
}
}
So the buildErrorMessage function prevents each method from repeating the code that formats the error message, but there is something that really bothers me about have the same code spread through out every method in the class. I know that PHP doesn't support python-like decorator syntax, but just to demonstrate what I'm envisioning conceptually; I want the code to behave something more like this:
class MyController {
#DefaultErrorHandling()
public function methodA() {
// code for methodB
}
#DefaultErrorHandling()
public function methodB() {
// code for methodB
}
#DefaultErrorHandling()
public function methodC() {
// code for methodC
}
}
Where the #DefaultErrorHandling decorator would wrap each method in that standard try / catch. Is there a way I could achieve this behavior so I don't have to have all of these methods that have repeated code? Or am I thinking about error handling incorrectly?
Thanks to anyone who takes the time to answer this.
Have you looked at a writing a custom exception handler and using set_exception_handler?
What you are doing seems a bit like reinventing the wheel. Does the Exception not already have the info you are collecting in the trace? See: Exception::getTrace
Maybe buildErrorMessage does more? Anyway, I assume a custom exception handler is what you are after.
Not sure if there is a better way to solve this or not, but I created a logging class that formatted the log for me. Then just called this in my catch block.
To log the correct Class and Method, I the debug_backtrace() function. See this answer for more information.
Entry point that calls controller methods can wrap those calls with try / catch. That being said, if you are planning to use different type of error handlers on those methods then you can implement something in your base controller (or use trait) that keeps track of which handler should be invoked on each particular method. Something like
<?php
class MyController extends Controller
{
function __construct()
{
$this->setActionErrorHandler('function_name', 'handler');
}
}
Or just call it at the beginning of action method body. Keeping this type of configuration within class itself will help with readability. Not as neat as python example but better than somewhere in configuration files.
More generic error handlers can be implemented in php by using set_exception_handler mentioned by others.
I'm not really getting why there is such a requirement.
Consider the following method:
function m1()
{
$ent = new Entity;
...
try {
$ent->save();
} catch (QueryException $e) {
...
}
I've got to trigger an exception. Preferably with mockery. How do I do that?
P.S. I can't pass $ent into the method.
UPD Let me describe my particular case to confirm if I do need to trigger an exception. Here I'm trying to test controller's action that is triggered by payment system to notify that user has made a payment. In it I, among other things, store in database all the data coming from payment system in PaymentSystemCallback model, and link it to Order model, which is created before redirecting user to the payment system. So, it goes like this:
function callback(Request $request)
{
$c = new PaymentSystemCallback;
$c->remote_addr = $request->ip();
$c->post_data = ...;
$c->headers = ...;
...
$c->save();
$c->order_id = $request->request->get('order_id');
$c->save();
}
But if incorrect order_id comes in, foreign constraint fails, so I change it this way:
try {
$c->save();
} catch (QueryException $e) {
return response('', 400);
}
But it doesn't look good to handle any database exception this way, so I'm seeking for a way to rethrow the exception unless $e->errorInfo[1] == 1452.
And here's what I came up with:
/**
* #runInSeparateProcess
* #preserveGlobalState disabled
*/
function testExceptionOnSave()
{
$this->setUpState();
Mockery::mock('overload:App\PaymentSystemCallback')
->shouldReceive('save')
->andReturnUsing(function() {}, function() {
throw new QueryException('', [], new Exception);
});
$this->doRequest();
$this->assertBalanceDidntChange();
$this->assertNotProcessed();
$this->seeStatusCode(500);
}
I use #runInSeparateProcess because preceding tests trigger the same action, and therefore the class is loaded before mockery has a chance to mock it.
As for #preserveGlobalState disabled it doesn't work without it. As phpunit's documentation put it:
Note: By default, PHPUnit will attempt to preserve the global state from the parent process by serializing all globals in the parent process and unserializing them in the child process. This can cause problems if the parent process contains globals that are not serializable. See the section called “#preserveGlobalState” for information on how to fix this.
I deviate a little from what mockery's documentation says when I'm marking only one test to run in a separate process, since I need it only for one test. Not the whole class.
Constrictive criticism is welcome.
The easiest way around this is to call a factory method that creates a mock instance of your Entity. Something like:
function testSomething()
{
$ent = $this->getEntity();
...
try {
$ent->save();
} catch (QueryException $e) {
...
}
}
function getEntity()
{
$mock = $this->createMock(Entity::class);
$mock
->method('save')
->will($this->throwException(new QueryException));
return $mock;
}
Your method is not designed for test. Fix that. If you can't, then you have to monkey patch, which PHP does not support natively.
My recommended approach would be to have your test suite install its own priority autoloader. Have your test case register a mock class into that autoloader, associated with class name Entity. Your mock class will do its magic to throw an exception. If you're using PHP 7, you have access to anonymous classes, which makes fixtures easier: new class Entity {}.
Per the accepted answer, Mockery supports this autoloading trick using the overload: quantifier on mocked classes. This saves a lot of work on your part!
i am thinking hard about how to implement a good plugin-system in php. I read a lot about hooking systems and different ways to implement such things, but until now I did not find any approach or existing system that would fit my needs.
I want to have an object (let it be $plugins) in which all used plugins are available as sub-object ($plugin->$blog). So far, thats no problem. But how could I catch a non existant plugin?
If somewhere in the code I would call the plugin $blog by using $plugin->$blog->loadArticle or something similar and the plugin was not loaded by the plugin system, a fatal error would be thrown. Is it possible to catch and handle these errors within a debugging class?
Thanks for your thoughts! Please excuse my english... ;-)
Well, you can implement your Plugin object like this using magic __get()
class Plugins {
private $plugins = array();
public function __get($name) {
if (!isset($this->plugins[$name]) {
throw new PluginNotInstalledException(); //this can be catched with try / catch
}
return $this->plugins[$name];
}
... //methods addPlugin() etc..
}
Whenever you're setting your $blog property would be a good time to check whether the passed parameter is === NULL and throw an exception if that's the case. So you'll be sure that $plugin->blog isn't null.
It would be even better if your plugins have to implement an interface or inherit an abstract class. So then whenever you're setting a property you can check whether the passed parameter is instanceof that class/interface.
A simple approach would be to provide a getPlugin() method.
function getPlugin($plugin)
{
if(! isset($this->$plugin)) return false;
return $this->$plugin;
}
$blog = $plugins->getPlugin('blog');
if($blog === false)
{//error condition
}
I'm looking at Symfony2 classes and I found something like this
try {
return $this->parseSelectorGroup($stream);
} catch (\Exception $e) {
$class = get_class($e);
throw new $class(sprintf('%s at %s -> %s', $e->getMessage(), implode($stream->getUsed(), ''), $stream->peek()), 0, $e);
}
Why they use
$class = get_class($e);
throw new $class
such syntax. What is the purpose of that?
I'm not the original author of that line of code so I can only assume why this was done.
As the code shows, the standard message of the exception is being extended with additional information of the string handling class (getUsed, peek - sounds like a stream buffer/parser/scanner/peeker).
The Message is edited and then a new object of the same class is being thrown.
Generally: When you have a class name in a variable and want to create a new instance of that class, you can simply use:
$className = "ClassName";
$instance = new $className();
This is done here with the catched exceptions classname, see get_class.
Further Note: This code is conceptually broken because it uses a class with an undefined interface for it's constructor. It then makes use of the constructor function without knowing anything about it's constructor.
There might be some information hidden that such a case is not to be expected to happen though. So this is merely a further note. You should have concrete reasons to do something like that, so this has been done for a reason and there might be some design decision which is not documented by the some lines of code you've posted. So handle with care.
Is it possible to exit gracefully out of a constructor in php? Something to the effect of
class Foo {
function __construct()
{
$active = false;
if(!$active)
{
return false;
}
}
}
I'm trying to accomplish this because I want to check to see if any of the methods in the class should run based on a configuration file. I don't want to have to check the configuration file in every method to see if the methods should be running.
That depends on what you mean by "gracefully". If you want your constructor to fail you can throw an exception, or you can use the factory pattern:
class FooFactory {
function makeFoo() {
return $someConstraint ? null : new Foo();
}
}
Maybe you can elaborate a bit on what exactly it is you want to accomplish.
Make it blow up. There is no such thing as a graceful failure in a constructor. Perhaps in the calling code, but not in the constructor. Throw an exception and handle it appropriately.
I can smell the Proxy pattern coming your way. What you are trying to achieve is not having the constructor fail gracefully but not allowing methods to be called on an object based on some $active criterion.
This might point you in the right direction. Or maybe not (=>> I don't quite like the page I linked in, but it was the best I could find for PHP). Do give Proxy a read, from other sources too perhaps. Basically, your ProxyObject will have a reference to a real object that will execute methods. Your client code will be calling methods on the ProxyObject as if it were the real thing, and the ProxyObject would decide whether it's active or not, whether to pass the message on to the real thing or to return nothing or nulls or dummy values. Sounds good?
If the constructor does so much logic, then it's not well designed.
Leave it empty, pass the configuration to it via a setter method and let it fail there.
It is not OO programming:
$o = new myObject();
if (!is_object($o)) // then what???
If your constructor fails, you should throw an exception, not return a false value. Unless you're using a language like C in which exceptions in the construction of the object means it can never be deconstructed.
The reason for this is because an exception forces the program to deal with it, and it give you the bad data. If you return a value, say -1 or whatever, the program can continue silently and ignore that until it causes a problem later down the road. Throwing an exception prevents these silent bugs from entering the code. You may know that it returns false if it failed to construct properly, but your co-worker may not and may happily try to use an object he thought was what he constructed to find out that it's really a boolean.
I think N3rd suggested a very cool solution but here is another simpler method.
class Foo {
private $active = TRUE;
function __construct() {
$this->check ( $active );
//OR
//if ($this->check ( $active )) { do something }
}
function check($var) {
if (! $var) {
$this->active = FALSE;
return FALSE;
}
return TRUE;
}
}