Design question / PHP:
I have a class with methods.
I would like to call to an external function anytime when any of the methods within the class is called.
I would like to make it generic so anytime I add another method, the flow works with this method too.
Simplified example:
<?php
function foo()
{
return true;
}
class ABC {
public function a()
{
echo 'a';
}
public function b()
{
echo 'b';
}
}
?>
I need to call to foo() before a() or b() anytime are called.
How can I achieve this?
Protect your methods so they're not directly accessible from outside the class, then use the magic __call() method to control access to them, and execute them after calling your foo()
function foo()
{
echo 'In pre-execute hook', PHP_EOL;
return true;
}
class ABC {
private function a()
{
echo 'a', PHP_EOL;
}
private function b($myarg)
{
echo $myarg, ' b', PHP_EOL;
}
public function __call($method, $args) {
if(!method_exists($this, $method)) {
throw new Exception("Method doesn't exist");
}
call_user_func('foo');
call_user_func_array([$this, $method], $args);
}
}
$test = new ABC();
$test->a();
$test->b('Hello');
$test->c();
Related
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();
Is it possible in PHP 5.X to have a method in a class that is executed whenever a classes method is called, prior to the called function? I need this because i want to do some dynamic validation for the parameters used in the called function.
class MyClass {
protected function preHook(){ echo "You are about to call a method."; }
public function methodA() { echo "You called methodA."; }
}
$obj = new MyClass();
$obj->methodA();
// Output: "You called methodA."
// Desired output: "You are about to call a method.You called methodA"
Also keep in mind the following:
The "methodA" needs to be public because reflection is used in the code in order to detect the method.
As mentioned in comment this is impossible as __call magic method gets called only if method with given name is not defined:
http://php.net/manual/en/language.oop5.magic.php
However, maybe one of the following hackish solutions will solve your issue.
SOLUTION 1
will require changing all your methods names:
class MyClass {
public function __call($name, $arguments){
echo "You are about to call $name method.";
return call_user_func_array(array($this, '_real_' . $name), $arguments);
}
private function _real_methodA() { echo "You called methodA."; }
}
$obj = new MyClass();
$obj->methodA();
SOLUTION 2
this will require a 'wrapper' classes:
class MyClass {
public function methodA() { echo "You called methodA."; }
}
class MyClassWrapper {
public function __construct(){
$this->myClass = new MyClass();
}
public function __call($name, $arguments){
echo "You are about to call $name method.";
return call_user_func_array(array($this->myClass, $name), $arguments);
}
}
$obj = new MyClassWrapper();
$obj->methodA();
SOLUTION 3
Third approach would be to apply decorator pattern and create one wrapper class.
class Decorator
{
protected $_instance;
public function __construct($instance)
{
$this->_instance = $instance;
}
public function __call($method, $args)
{
print 'do your stuff here';
return call_user_func_array(array($this->_instance, $method), $args);
}
}
$obj = new Decorator(new MyClass);
$obj->methodA();
SOLUTION 4
mix of solution 1 and use reflection and "runkit_method_rename" to rename all methods
http://docs.php.net/manual/en/function.runkit-method-rename.php
runkit is experimental so this is rather hardcore.
class MyClass {
public function __call($name, $arguments){
echo "You are about to call $name method.";
return call_user_func_array(array($this, '_real_' . $name), $arguments);
}
private function methodA() { echo "You called methodA."; }
}
$reflection = new ReflectionClass('MyClass');
$methods = $reflection->getMethods();
foreach ($methods as $method) {
runkit_method_rename('MyClass', $method->name , '_real_' . $method->name);
}
$obj = new MyClass();
$obj->methodA();
In the end i modified the dispatcher to call the prehook (using __call in order to keep it hidden from reflection but still be public) before the actual method is called.
Given class A that extends class B, how can I have calls to class A's __call function override the matching function inherited from the parent?
Consider this simplified example:
class A
{
public function method_one()
{
echo "Method one!\n";
}
}
class B extends A
{
public function __call($name, $args)
{
echo "You called $name!\n";
}
}
$b = new B();
$b->method_one();
When I run it, I get the output Method one!. I WANT to get the output You called method_one!.
So, how do I have the subclass's magic method override the parent classes defined method?
I need to extend the object, because I need access to a protected method in A, but I want to channel all public methods into my own __call handler. Is there any way to do this?
So, I found a way to do it, which involves making an intermediate class to expose the protected method I need, while still using __call for the public ones. This works, but I really don't like the idea of extending a class just to expose a protected method... Still, someone might find it useful, so thought I'd share:
class A
{
public function method_one()
{
echo "Method one!\n";
}
protected function protected_method()
{
echo "Accessible!\n";
}
}
class A_accessor extends A
{
public function publicise()
{
$args = func_get_args();
return call_user_func_array(array($this, array_shift($args)), $args);
}
}
class B
{
private $A;
public function __construct()
{
$this->A = new A_accessor();
}
public function __call($name, $args)
{
echo "You called $name!\n";
}
public function protected_method()
{
return $this->A->publicise('protected_method');
}
}
$b = new B();
$b->method_one();
$b->protected_method();
This is the answer I actually used, based on Mark Baker's comment.
By having an object of the class whose methods I want access to as a variable, I can use ReflectionMethod to access any of its methods just as if I was extending it, but with __call still catching everything else. So any methods I want to pass through, I can pass through with something like this:
public function __call($name, $args)
{
$method = new ReflectionMethod($this->A, $name);
$method->setAccessible(true);
return $method->invokeArgs($this->A, $args);
}
Or in my case, with the full class like this:
class B
{
private $A;
public function __construct()
{
$this->A = new A();
}
public function __call($name, $args)
{
echo "You called $name!\n";
}
public function protected_method()
{
$method = new ReflectionMethod($this->A, 'protected_method');
$method->setAccessible(true);
return $method->invoke($this->A, $args);
}
}
try this
class A1
{
protected function method_one()
{
echo "Method one!\n";
}
}
class B1
{
private $A;
public function __construct()
{
$this->A = new A1;
}
public function __call($name, $args)
{
$class = new ReflectionClass($this->A);
$method = $class->getMethod($name);
$method->setAccessible(true);
//return $method;
echo "You called $name!\n";
$Output=$method->invokeArgs($this->A, $args);
$method->setAccessible(false);
return $Output;
}
}
$a = new B1;
$a->method_one("");
Is there a way to call all the methods from a class once the class is initialized? For example, lets say I have a class named todo and once I make an instance of a todo class, all the methods/functions inside it will be executed, without calling it in the constructor?
<?php
class todo
{
function a()
{
}
function b()
{
}
function c()
{
}
function d()
{
}
}
$todo = new todo();
?>
In here I created an instance of a class todo so that the methods a, b, c, d will be executed. Is this possible?
This outputs 'abc'.
class Testing
{
public function __construct()
{
$methods = get_class_methods($this);
forEach($methods as $method)
{
if($method != '__construct')
{
echo $this->{$method}();
}
}
}
public function a()
{
return 'a';
}
public function b()
{
return 'b';
}
public function c()
{
return 'c';
}
}
I think you can use iterator. All methods will be called in foreach PHP Iterator
Use a __construct() method (as you mentioned), which is called on object instantiation. Anything else would be unfamiliar and unexpected (to have random methods instantly executed not by the constructor).
Your class code looks like you're using PHP4, if that's the case, name your constructor the same as the class name.
Like this? I use this pattern to register meta data about classes sometimes.
<?php
class todo {
public static function init() {
self::a();
self::b();
self::c();
self::d();
}
function a()
{
}
function b()
{
}
function c()
{
}
function d()
{
}
}
todo::init();
There isn't any way that I can think of, short of putting it into the constructor as you suggest in your question:
<?php
class todo
{
public function __construct()
{
$this->a();
$this->b();
$this->c();
$this->d();
}
function a()
{
}
function b()
{
}
function c()
{
}
function d()
{
}
}
$todo = new todo();
?>
I copied and pasted below class from php.net...
I thought it will be usefull because methods are not called using objects, instead using get_class_methods():
class myclass {
function myclass()
{
return(truenter code heree);
}
function myfunc1()
{
return(true);
}
function myfunc2()
{
return(true);
}
}
$class_methods = get_class_methods('myclass');
foreach ($class_methods as $method_name) {
echo "$method_name\n";
}
I'm trying to call a static magic function (__callStatic) from a member of its child class. Problem being, it goes to the non-static __call instead.
<?php
ini_set("display_errors", true);
class a
{
function __call($method, $params)
{
echo "instance";
}
static function __callStatic($method, $params)
{
echo "static";
}
}
class b extends a
{
function foo()
{
echo static::bar();
// === echo self::bar();
// === echo a::bar();
// === echo b::bar();
}
}
$b = new b();
echo phpversion()."<br />";
$b->foo();
?>
Output:
5.3.6
instance
How can I make it display "static"?
If you remove the magic method '__call', your code will return 'static'.
According to http://php.net/manual/en/language.oop5.overloading.php "__callStatic() is triggered when invoking inaccessible methods in a static context".
What I think is happening in your code is that,
You are calling static method from a non-static context.
The method call is in non-static context, so PHP searches for the magic method '__call'.
PHP triggers the magic method '_call' if it's exists. Or, if it's not exists it will call '_callStatic'.
Here is a possible solution:
class a
{
static function __callStatic($method, $params)
{
$methodList = array('staticMethod1', 'staticMethod2');
// check if the method name should be called statically
if (!in_array($method, $methodList)) {
return false;
}
echo "static";
return true;
}
function __call($method, $params)
{
$status = self::__callStatic($method, $params);
if ($status) {
return;
}
echo "instance";
}
}
class b extends a
{
function foo()
{
echo static::staticMethod1();
}
function foo2()
{
echo static::bar();
}
}
$b = new b();
echo phpversion()."<br />";
$b->foo();
$b->foo2();
In PHP there are the reserved words self and parent for accessing static methods from within a class and/or instantiated object. parent refers to inherited methods from the parent class.
class b extends a
{
function foo()
{
echo parent::bar();
}
}
EDIT: Uhm, that doesn't do the trickā¦ (using PHP 5.3.5)
$b = new b();
$b->foo(); // displays: instance
a::bar(); // displays: static
2nd EDIT: Ha, it works only, if you omit the __call()-method in class a.
class a
{
static function __callStatic($method, $params)
{
echo "static";
}
// function __call($method, $params)
// {
// echo "instance";
// }
}
class b extends a
{
function foo()
{
echo parent::bar();
}
}
$b = new b();
$b->foo(); // displays: static
a::bar(); // displays: static