In PHP, how do I find out if a class method was called via a method inherited from a trait?
Say I have a class myClass that uses the Psr\Log\LoggerTrait (see: PSR-3). I need to be able to find out if the method myClass::log() was called via a method from the Psr\Log\LoggerTrait, for example LoggerTrait::debug(), or if it was called directly from outside myClass.
All the methods are non-static.
This is related to a debugging package. I'm not trying to alter behavior based on the caller, I just need to be able to pass that information forward. And to be more precise, I need just the entry point, ie. just the last call outside of my package.
I'm looking at debug_backtrace() but it doesn't seem to offer any direct solutions. Is there some rational way of doing this?
Here's some code:
<?php
class myClass
{
use Psr\Log\LoggerTrait;
public function log($level, $message, array $context = array())
{
if (called_via_trait) {
...
} else {
...
}
}
}
$myObject = new myClass;
$myObject->log('debug', 'This is a direct call');
$myObject->debug('This is a call via a trait method');
You could use get_called_class to determine the class that called it.
trait Test {
public function doTest() {
echo get_called_class() . "\n";
}
}
class Some {
use Test;
public function myFunc() {
$this->doTest();
}
}
$some = new Some();
$some->myFunc(); // Outputs "Some" since Some uses Test
So in your case, inside your class, you could do something like
function test() {
if(get_called_class() == 'myClass') {
// You're in the myClass class
} else {
// You're not in the myClass class
}
}
Yes, you have to use debug_backtrace(); Please follow my example:
namespace Psr\Log;
class LoggerTrait{
public static function debug(){
return myClass::log();
}
}
class myClass{
public static function log(){
$trace = debug_backtrace();
if(isset($trace[1])){
echo'<br />Called by <b>'.$trace[1]['class'].'</b>. ';
} else {
echo'<br />Called by <b>'.$trace[0]['class'].'</b>. ';
}
if(isset($trace[1]['class']) && $trace[1]['class']!=get_class()){
echo'Called outside';
} else {
echo'Called inside';
}
//return get_class();
}
}
trait ExampleTrait {
public function doSay() {
echo LoggerTrait::debug();
echo myClass::log();
}
}
echo LoggerTrait::debug();
echo myClass::log();
echo ExampleTrait::doSay();
Related
I need your guidance, may be its a very basic question but i couldn't figure out this even after lots of google search. I could access variables of other classes through Eureka class but not their functions.
This is my case:
Class autoload function in index.php
<?php
function __autoload($class_name)
{
//$class_name = strtolower($class_name);
$path = "{$class_name}.php";
if (file_exists($path)) {
include ($path);
} else {
die("{$path} file could not be found!<br />");
}
}
?>
Base class, I want to access all other classes using this class's object on the html page:
<?php
class Eureka
{
public $some_var1;
public function get_all_settings()
{
echo 'get_all_settings was called from Eureka class.';
}
}
?>
Timezone class in timezone.php
<?php
class TimeZone
{
public $some_var2;
public function get_time_zone()
{
echo 'get_time_zone was called from TimeZone class.';
}
}
?>
Location class in location.php
<?php
class Location
{
public $some_var3;
public function set_location($location_name)
{
echo 'set_location was called from Location class.';
}
}
?>
html page index.php
<?php
$eureka = new Eureka;
//**How can I achieve this ????**
$eureka->TimeZone->get_time_zone();
//**OR**
$eureka->Location->set_location('some_location_name');
?>
e.g. when $eureka object calls 'TimeZone' class, it should be loaded and 'get_time_zone()' method called without any errors.
You'd have to do something like this, basically you to store the other classes in the first class and then access them. Can't pull them out of thin air after all.
<?php
class Eureka
{
protected $someVar1; //shouldn't really use public, instead use getters and setters
protected $TimeZone;
//should use dependacy injection
public function __construct(TimeZone $TimeZone = null){
if($TimeZone) $this->setTimeZone($timezone);
}
public function getTimeZone(){
return $this->TimeZone;
}
public function setTimeZone(TimeZone $TimeZone){
$this->TimeZone = $TimeZone;
}
public function getAllSettings()
{
echo 'get_all_settings was called from Eureka class.';
}
}
class TimeZone
{
protected $someVar2;
public function getTimeZone()
{
echo 'get_time_zone was called from TimeZone class.';
}
public function hello(){
echo "Hello";
}
}
$Eureka = new Eureka( new TimeZone() );
$Eureka->getTimeZone()->hello();
As far as your code style get_time_zone you can technically name them how you want. I personally try to follow the PSR-2 standard.
You shouldn't use public properties because a class should be a black box, it shouldn't expose it's implementation to the outside world. This will make it easier to refactor the class later and maintain it.
If you open it up to the rest of the code, you'll be searching in you're whole application looking where you used those properties. You could try to argue the same of methods but I would say consider this.
class Foo
{
protected $bar;
public function getBar()
{
return $this->bar;
}
}
$Foo = new Foo;
echo $Foo->getBar();
Then if you refactor it
class Foo
{
protected $baz; //renamed
public function getBar()
{
return $this->baz;
}
}
//no code change requred
$Foo = new Foo;
echo $Foo->getBar();
Where as if you do this
class Foo
{
public $bar;
}
$Foo = new Foo;
echo $Foo->bar;
And then change it
class Foo
{
public $baz; //changed
}
$Foo = new Foo;
echo $Foo->baz; //also required changing
This is a very simple tiny example, but these things can quickly get out of hand. And you really limit yourself by using public most novice coders look at protected as being more limited. When it's the other way around.
http://www.php-fig.org/psr/psr-2/
The magical __call() and __callStatic can pretty much handle any non existing method on the class, but is there a way to handle a non existing magical method on a class?!
Here's an example on why I need this:
I have a class called DoSomething:
class DoSomething{
public function ok(){
echo 'Something!';
}
}
I want to call this class as a function for a reason! which should call the __invoke function of that class:
$doSomething = new DoSomething();
$doSomething();
Normally by doing that, the class should look for the __invoke function, however in my case I don't to have that function declared on my class (DoSomething), instead I want to be able to call another function (such as the ok()) if the __invoke doesn't exist.
I was expecting something like this to work, but of course it didn't :)
public function __call($class, $arguments)
{
$object = IoC::resolve($class);
$object->ok(...$arguments);
}
The main goal is to use the class as a function, without having to declare the __invoke method. Handle the function does not exist error and call another function instead.
I think that would be really cool :D I appreciate suggestions or other solutions to achieve this.
Internal solution
Extract an abstract class
You could extract an abstract class and have your classes extend it:
<?php
abstract class Invokable
{
public function __invoke()
{
return $this->ok();
}
abstract public function ok();
}
class DoSomething extends Invokable
{
public function ok()
{
echo 'Something';
}
}
$doSomething = new DoSomething();
echo $doSomething();
For an example, see:
https://3v4l.org/m0ih8
Extract a trait
You could extract a trait and have your classes use it:
<?php
trait InvokableTrait
{
public function __invoke()
{
return $this->ok();
}
}
class DoSomething
{
use InvokableTrait;
public function ok()
{
echo 'Something';
}
}
$doSomething = new DoSomething();
echo $doSomething();
For an example, see:
https://3v4l.org/ftUfI
External Solution
Create a proxy
You could create a proxy (a decorator) that composes the object that is not invokable:
<?php
class InvokableDecorator
{
private $decorated;
public function __construct($decorated)
{
$this->decorated = $decorated;
}
public function __call($name, $arguments)
{
/**
* delegate to decorated object if the method exists
*/
if (method_exists($this->decorated, $name)) {
return $this->decorated->{$name}($arguments);
}
}
public function __invoke()
{
return $this->decorated->ok();
}
}
class DoSomething
{
public function ok()
{
echo 'Something';
}
}
$doSomething = new InvokableDecorator(new DoSomething());
echo $doSomething();
For an example, see:
https://3v4l.org/C3XEX
Create a handler
You could create a handler that takes care of determining this externally:
<?php
class Handler
{
public function handle($subject)
{
if (is_callable($subject)) {
return $subject();
}
if (method_exists($subject, 'ok')) {
return $subject->ok();
}
throw new \BadMethodCallException(sprintf(
'Unable to handle instance of "%s"',
get_class($subject)
));
}
}
class DoSomething
{
public function ok()
{
echo 'Something';
}
}
$handler = new Handler();
echo $handler->handle(new DoSomething());
For an example, see:
https://3v4l.org/E0NVs
I would like to add some logic before and after every time I'm using a method (doesn't really matter if is private, protected or public) in a class.
For example:
class Service
{
function test1() {
Log:start(__METHOD__);
someLogicInThere(); ....
Log:end(__METHOD__);
}
function test2() {
Log:start(__METHOD__);
someLogicInThere(); ....
Log:end(__METHOD__);
}
...
}
My idea is to finally have something like this:
/**
* #LogDecorate
*/
class Service
{
function test1() {
someLogicInThere();
}
function test2() {
someLogicInThere();
}
...
}
Use annotations is not important. There is any way to do that?
As your question-title already suggests, you could use the Decorator Pattern for this. I'm not quite sure though if a full-stack Decorator pattern is necessary here. If it's a really simple use-case, something like this could suffice.
What you could do, is extend the class and 'route' all calls to the extended class. Then add some logic before and after, and call the parent method in between. Something like this:
class Service {
function method1() {
doSomeFunkyStuff();
}
function method2() {
doSomeOtherFunkyStuff();
}
}
class DecoratedService extends Service {
function method1() {
Log::start(__METHOD__);
parent::method1();
Log::end(__METHOD__);
}
function method2() {
Log::start(__METHOD__);
parent::method2();
Log::end(__METHOD__);
}
}
$service = new DecoratedService();
$service->method1();
$service->method2();
Now you can either choose to use the original Service or use the DecoratedService. The funcionality is the same, and the DecoratedService will not have to be altered if the Service changes, assuming the method names won't change (which would actually be a bad thing).
But also look to the wiki page (or any other resource) to understand fully what's the intent of the Decorator Pattern. This (above) might not be the ideal solution for your problem.
EDIT a bit more automatic as requested, sir.
As you cannot change the visibility of methods, using the magic __call() doesn't work (as public or protected parent methods will be accessible from the child as well). But, what you can do, is create your own call method!
class DecoratedService extends Service {
function call($method) {
if(!method_exists(parent, $method)) {
return false; // OR:
throw Exception;
// OR handle this case some other way
}
Log::start(array(parent, $method));
call_user_func(array(parent, $method));
Log::end(array(parent, $method));
}
}
$service = new DecoratedService;
$service->call('method1');
I guess, it is a typicall case of Smart Reference Pattern (some mix of Proxy and Decorator Patterns).
class A {
function test1() {
echo 'TEST 1', PHP_EOL;
}
function test2() {
echo 'TEST 1', PHP_EOL;
}
}
class ProxyA {
protected $wrapped;
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function __call($name, $args) {
echo 'Log:start';
$this->wrapped->$name($args);
echo 'Log:end';
}
}
$proxy = new ProxyA(new A());
$proxy->test1();
But it works only for public methods.
Mixing Smart Reference with DecoratedService::call() method from #giorgio and #yceruto answer can cover all method, or just implement __call() twice:
class A {
public function test1() {
echo 'TEST 1', PHP_EOL;
}
private function test2() {
echo 'TEST 2', PHP_EOL;
}
public function __call($name, $args) {
if (method_exists($this, $name)) {
$this->$name($args);
}
}
}
class ProxyA {
protected $wrapped;
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function __call($name, $args) {
if (method_exists($this->wrapped, $name)) {
echo 'Log:start';
$this->wrapped->$name($args);
echo 'Log:end';
}
}
}
$proxy = new ProxyA(new A());
$proxy->test0(); // Nothing to do
$proxy->test1(); // Done
$proxy->test2(); // Done
Use of the magic __call method might make this easy for you:
class Service
{
public function test1() {
echo 'TEST 1', PHP_EOL;
}
protected function test2() {
echo 'TEST 2', PHP_EOL;
}
public function __call($method, $args) {
echo 'Some stuff before', PHP_EOL;
$returnValue = $this->$method($args);
echo 'Some stuff after', PHP_EOL;
return $returnValue;
}
}
$x = new Service();
$x->test2();
$x->test1();
Note that if the method is accessible from outside the class (like test1) then __call() won't be invoked; it only executes if the methods involved are protected or private; and doesn't trigger if they are invoked internally within the object either
Is there a way to call an inherited method, without specifying it's function name?
Something like:
class Child extends Parent {
function some_function(){
// magically inherit without naming the parent function
// it will call parent::some_function()
parent::inherit();
// other code
}
function another_function(){
// it will call parent::another_function()
$result = parent::inherit();
// other code
return $result;
}
}
I could think of a hack to do this using debug_backtrace(), get the last function where inherit() was called and access it's parent with the same function name. I was wondering if there's a nicer way instead of using debug functions which are clearly not meant for this.
You can use the magic __FUNCTION__ constant.
class A
{
function some_function()
{
echo 'called ' . __METHOD__;
}
}
class B extends A
{
function some_function()
{
call_user_func(array('parent', __FUNCTION__));
}
}
$b = new B;
$b->some_function(); // prints "called A::some_function"
Instead of
call_user_func(array('parent', __FUNCTION__));
you can also do
parent::{__FUNCTION__}();
Dirty, but:
class Adult {
function mummy(){
return 'Walk like an Egyptian';
}
function daddy(){
return 'Luke, I am your father';
}
}
class Child extends Adult {
function mummy(){
echo 'Mummy says: ';
$me = explode('::',__METHOD__)[1];
echo parent::$me();
}
function daddy(){
echo 'Daddy says: ';
$me = explode('::',__METHOD__)[1];
echo parent::$me();
}
}
$o = new Child();
$o->mummy();
$o->daddy();
EDIT
Actually giving you a parent method called inherit();
class Adult {
private function mummy(){
return 'Walk like an Egyptian';
}
private function daddy(){
return 'Luke, I am your father';
}
protected function inherit($method) {
$beneficiary = explode('::', $method)[1];
return $this->$beneficiary();
}
}
class Child extends Adult {
public function mummy() {
echo 'Mummy says: ',
parent::inherit(__METHOD__),
PHP_EOL;
}
public function daddy() {
echo 'Daddy says: ',
parent::inherit(__METHOD__),
PHP_EOL;
}
}
$o = new Child();
$o->mummy();
$o->daddy();
Dynamically calling functions:
static::$functionName();
In your case:
$func = __FUNCTION__;
parent::$func();
Note: the function name must be a string, if it's the actual function (not really relevant in this context) then it first needs to be converted to its string name first.
Other stuff that your question will probably lead you towards in the long run.
Check out late static binding it's what you're looking for.
Example taken from the linked page.
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who(); // Here comes Late Static Bindings
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test();
I have two classes:
class Init {
public function test() {
echo 1;
}
public static function loadSecond() {
// Load the class
}
}
class Second extends Init {
public function test2() {
echo 2;
}
}
I need to load Second class only by request. For example:
$init = new Init();
$init->test();
$second = $init::loadSecond();
$second->test2();
Replace // Load the class with
return new Second()
You just need to create a new object. (I do not know why you need it to be that way, but this should work for your purpose.
I'm not sure what you need to do.
If you mean to include a class whenever you need it automatically :
As Marcin Orlowski mentionned it, what you need is to use the autoloading function of PHP.
Basically it should look like :
function __autoload ($name)
{
include "/path/to/my/includes/" . $name . "inc.php";
}
If you mean creating an object from another class, you should probably do like
class Init {
public function test() {
echo 1;
}
public static function loadSecond() {
return new Second;
}
}
class Second extends Init {
public function test2() {
echo 2;
}
}
What you need is class autoloading feature implemented. It's basically supported by PHP, so follow the docs: http://php.net/manual/pl/language.oop5.autoload.php