PHP Nested call_user_func_array with classes - php

I have a complicated scenario based on an existing framework I'm using which is forcing me to deal with nested call_user_func_array calls. I've got a file with two functions:
function their_caller($options)
{
call_user_func_array('their_callback', $options);
}
function their_callback($options)
{
call_user_func_array($options[0], $options[1]);
}
Then I have another file with a class with and some public methods:
namespace FOO;
class MyClass
{
public function my_caller()
{
$operations = array(
array(
array($this, 'my_callback'),
'Hello World'
)
);
their_caller($operations);
}
public function my_callback($text)
{
print $text;
}
}
When I call my_caller() on a new instance of MyClass, it calls their_caller which then passes an array of arguments containing a reference to MyClass (as $this) as well as the method my_callback.
their_caller() then forwards the request to their_callback and passes $options along.
In their_callback I can debug $options[0] and see that it's an array containing a reference to MyClass and my_callback.
I call get_class_methods() on $options[0][0] in their_callback and it will show the list of methods, but for some reason, call_user_func_array($options[0], $options[1]); won't call my_callback on MyClass. I can even call $options[0][0]->my_callback('HELLO'); and it works.
Unfortunately, I can't modify their_caller or their_callback. They are part of the framework. Any ideas what's preventing it from working?

Note: Your function their_callback will receive two arguments.
Try this code snippet here
<?php
ini_set('display_errors', 1);
function their_caller($options)
{
call_user_func_array('their_callback', $options);
}
//first argument will receive callback
//second argument will receive the text which you want to send as argument.
function their_callback($callback,$option)
{
//call_user_func_array should expect second argument as array not string.
call_user_func_array($callback, array($option));
//here we have converted `$option` string(Hello World) to array.
}
class MyClass
{
public function my_caller()
{
$operations = array(
array(
array($this, 'my_callback'),
'Hello World'
)
);
call_user_func_array('their_caller', $operations);
}
public function my_callback($text)
{
print $text;
}
}
$object= new MyClass();
$object->my_caller();

Related

Call class method from inside array_map anonymous function

I am trying to call one of my object's methods from within an array_map anonymous function. So far I am receiving the expected error of:
Fatal error: Using $this when not in object context in...
I know why I am getting this error, I just don't know a way to achieve what I am trying to... Does anybody have any suggestions?
Here is my current code:
// Loop through the data and ensure the numbers are formatted correctly
array_map(function($value){
return $this->some_method($value,'value',false);
},$this->mssql->data[0]['results'][0]);
You can tell the function to "close over" the $this variable by using the "use" keyword
$host = $this;
array_map(function($value) use ($host) {
return $host->some_method($value,'value',false);
},$this->mssql->data[0]['results'][0]);
Also, you can call your map function from a class context and you will not receive any errors. Like:
class A {
public $mssql = array(
'some output'
);
public function method()
{
array_map(function($value){
return $this->mapMethod($value,'value',false);
},$this->mssql);
}
public function mapMethod($value)
{
// your map callback here
echo $value;
}
}
$a = new A();
$a->method();

Reverse engineering on a php function

I know it isn't actually the reverse engineering
I have this function:
<?=$this->get('translator')->trans('dashboard.actions', array(), 'front');?>
I want to understand how to insert a function (using include maybe) that gets called when using that code.
I know the function name needs to be trans, and it has 3 arguments, but how to define it? Let me put this in a more easy way: In a php script how do I print Hello world when using $this->get('translator')->trans('dashboard.actions', array(), 'front');
It's just a regular class method:
class Traslator{
public function trans($a, $b, $c){
return 'Hello world';
}
}
Now, you only need another class method called get() that returns an instance of the previous class:
class Foo{
public function get(){
return new Traslator;
}
}
Full code:
<?php
class Traslator{
public function trans($a, $b, $c){
return 'Hello world';
}
}
class Foo{
public function get(){
return new Traslator;
}
public function test(){
?>
<?=$this->get('translator')->trans('dashboard.actions', array(), 'front');?>
<?php
}
}
$f = new Foo;
$f->test();
$this references the current object instance, and that variable may not be re-assigned. Meaning, to trick PHP into doing what you want requires to add code before and after the line you mention.
By creating a single class, the result can be obtained.
Before the <?=... line
<?php
class C {
function get($s) {
return $this;
}
function trans($s, $a, $f) {
return 'Hello world';
}
function hw() {
?>
Then the line
<?=$this->get('translator')->trans('dashboard.actions', array(), 'front');?>
Then need, to end the class, and call the method that does what you want
<?php
}}
$i = new C();
$i->hw();
Put the 3 blocks of code into a file and call PHP on it, the <?=... line should show "Hello world".
$this->get('translator') return instance of class lets call it "Translator" - on that instance you call trans() method
so you would like to change instance of "Translator" to your extended version
class ExtTranslator extends Translator{
public function trans($params){
echo 'fdsfds'; // here you could add what you like
return parent::trans($params);
}
}
and yes - it's hard to help you cause I don't really know what are you trying to do - to work with that code you must first understand it...

how to get input parameters of any method of a class in PHP?

Let's say I have a class with 10 methods, each method has different parameters.
I want to log input parameters of all methods of said class without having to do edit each method to insert that logging code. Is there away to do that ?
Just wrap it with decorator with magic __call http://ideone.com/n9ZUD
class TargetClass
{
public function A($a, $b) {}
public function B($c, $d) {}
public function C($e, $f) {}
}
class LoggingDecorator
{
private $_target;
public function __construct($target)
{
$this->_target = $target;
}
public function __call($name, $params)
{
$this->_log($name, $params);
return call_user_func_array(array($this->_target, $name), $params);
}
private function _log($name, $params)
{
echo $name . ' has been called with params: ' . implode(', ', $params) . '<br>';
}
}
$target = new TargetClass();
$logger = new LoggingDecorator($target);
$logger->A(1, 2);
$logger->A(3, 4);
The only disadvantage of this approach is that you will lose the type of the decorated class, e.g. you won't be able to satisfy type hints with it. If that is a concern, distill the interface of TargetClass and implement it in the LoggingDecorator.
Not directly, no.
You could rename all your methods to have an underscore suffix, e.g.:
myFunction() -> _myFunction()
Then, write add the magic __call() method to intercept calls to the previous (unprefixed) methods. Then you would log the request and pass on all arguments to the original method.
It's kind of ugly and still requires a change to all your method names.
I might be over simplifying this - but you can retrieve all the arguments of a function using
func_get_args();
Returns an array comprising a function's argument list
http://www.php.net/manual/en/function.func-get-args.php
<?php
function foo() {
$args = func_get_args();
var_export($args);
}
foo('arg1', 'arg1');
?>
That would output something like this -
array (
0 => 'arg1',
1 => 'arg2',
)
There are a few notes to be added here - you should read the documentation link I provided - one "limitation" is -
Note:
This function returns a copy of the passed arguments only, and does not account for default (non-passed) arguments.

get function name that called function without arguments

I have a class admin_model with the private method admin_form()
There are a number of other public functions that call this method.
Is there a way to get the name of the function that called admin_form() from INSIDE admin_form()?
I could easily add an argument when calling admin_form() like:
$this -> admin_form(__FUNCTION__);
then inside admin_form($caller) I have the caller. It will be $caller
But i was hoping there was a way to do this WITHOUT passing arguments.
Any Ideas??
Cheers,
Alex
You can do this with debug_backtrace():
<?php
class admin_model {
public function foo() {
$this->admin_form();
}
public function bar() {
$this->admin_form();
}
private function admin_form() {
// Get the current call stack
$stack = debug_backtrace();
// Remove this call (admin_form()) from the stack
array_shift($stack);
// The calling method is now on the top of the stack
$lastCall = array_shift($stack);
$callingMethod = $lastCall['function'];
echo "admin_form() called by method: $callingMethod\n";
}
}
$model = new admin_model();
$model->foo();
$model->bar();
Output:
admin_form() called by method: foo
admin_form() called by method: bar
But, as others have pointed out, this is bad practice and you should rethink your design.
No matter,
For my requirement this functionality is useful rather that 'bad coding'.
Anyway the answer is:
$e = new Exception();
$trace = $e -> getTrace();
$caller = $trace[1]["function"];
This will get the function name of the caller.

Something like a callback delegate function in php

I would like to implement something similar to a c# delegate method in PHP. A quick word to explain what I'm trying to do overall: I am trying to implement some asynchronous functionality. Basically, some resource-intensive calls that get queued, cached and dispatched when the underlying system gets around to it. When the asynchronous call finally receives a response I would like a callback event to be raised.
I am having some problems coming up with a mechanism to do callbacks in PHP. I have come up with a method that works for now but I am unhappy with it. Basically, it involves passing a reference to the object and the name of the method on it that will serve as the callback (taking the response as an argument) and then use eval to call the method when need be. This is sub-optimal for a variety of reasons, is there a better way of doing this that anyone knows of?
(Apart from the observer pattern) you can also use call_user_func() or call_user_func_array().
If you pass an array(obj, methodname) as first parameter it will invoked as $obj->methodname().
<?php
class Foo {
public function bar($x) {
echo $x;
}
}
function xyz($cb) {
$value = rand(1,100);
call_user_func($cb, $value);
}
$foo = new Foo;
xyz( array($foo, 'bar') );
?>
How do you feel about using the Observer pattern? If not, you can implement a true callback this way:
// This function uses a callback function.
function doIt($callback)
{
$data = "this is my data";
$callback($data);
}
// This is a sample callback function for doIt().
function myCallback($data)
{
print 'Data is: ' . $data . "\n";
}
// Call doIt() and pass our sample callback function's name.
doIt('myCallback');
Displays: Data is: this is my data
I was wondering if we could use __invoke magic method to create "kind of" first class function and thus implement a callback
Sound something like that, for PHP 5.3
interface Callback
{
public function __invoke();
}
class MyCallback implements Callback
{
private function sayHello () { echo "Hello"; }
public function __invoke () { $this->sayHello(); }
}
class MySecondCallback implements Callback
{
private function sayThere () { echo "World"; }
public function __invoke () { $this->sayThere(); }
}
class WhatToPrint
{
protected $callbacks = array();
public function register (Callback $callback)
{
$this->callbacks[] = $callback;
return $this;
}
public function saySomething ()
{
foreach ($this->callbacks as $callback) $callback();
}
}
$first_callback = new MyCallback;
$second_callback = new MySecondCallback;
$wrapper = new WhatToPrint;
$wrapper->register($first_callback)->register($second_callback)->saySomething();
Will print HelloWorld
Hope it'll help ;)
But I'd prefer the Controller pattern with SPL for such a feature.

Categories