I'm working on a logging class, and I'd like to add a convenience function which would log the arguments of the function from which it is called. A little convoluted, so here's some code:
function MyFunc($arg1, $arg2) {
global $oLog;
$oLog->logArgs();
}
So, I can get the name of the calling function (MyFunc) using debug_backtrace(). I'd be interested in getting the names and values of the arguments, preferably without having to add func_get_args() in the call to logArgs().
I know it's a tall order, but PHP continues to surprise me, so I'm putting it out there, just in case.
Thanks.
You can do this with reflection:
function logger()
{
$bt = debug_backtrace();
$previous = $bt[1];
if(empty($previous['class'])) {
$fn = new ReflectionFunction($previous['function']);
} else {
$class = new ReflectionClass($previous['class']);
$fn = $class->getMethod($previous['function']);
}
$parameters = $fn->getParameters();
//Get a parameter name with $parameters[$paramNum]->getName()
//Get the value from $previous['args'][$paramNum]
}
This particular implementation won't work with closures, but it will work with both global functions and class methods.
Related
Say I have a callable stored as a variable:
$callable = function($foo = 'bar', $baz = ...) { return...; }
How would I get 'bar'?
if (is_callable($callable)) {
return func_get_args();
}
Unfortunately func_get_args() is for the current function, is it possible to get a key value pair of arguments?
You can use reflection:
$f = new ReflectionFunction($callable);
$params = $f->getParameters();
echo $params[0]->getDefaultValue();
You may want to use get_defined_vars to accomplish this, this function will return an array of all defined variables, specifically by accessing the callable index from the output array.
I came across this question because I was looking for getting the arguments for a callable which is not just the function itself. My case is
class MyClass{
public function f(){
// do some stuff
}
}
$myclass = new MyClass();
$callable = array($myclass, "f);
This is a valid callback in php. In this case the solution given by #Marek does not work.
I worked around with phps is_callable function. You can get the name of the function by using the third parameter. Then you have to check whether your callback is a function or a (class/object) method. Otherwise the Reflection-classes will mess up.
if($callable instanceof Closure){
$name = "";
is_callable($callable, false, $name);
if(strpos($name, "::") !== false){
$r = new ReflectionMethod($name);
}
else{
$r = new ReflectionFunction($name);
}
}
else{
$r = new ReflectionFunction($callable);
}
$parameters = $r->getParameters();
// ...
This also returns the correct value for ReflectionFunctionAbstract::isStatic() even though the $name always uses :: which normally indicates a static function (with some exceptions).
Note: In PHP>=7.0 this may be easier using Closures. There you can do someting like
$closure = Closure::fromCallable($callable);
$r = new ReflectionFunction($closure);
You may also cause have to distinguish between ReflectionFunction and ReflectionMethod but I can't test this because I am not using PHP>=7.0.
I have a class that generates data based on a few things. I would like to format that data from the outside. So I am trying to pass a function into the class so that it would format that data. I have looked at many examples, but it seems this is unique.
Can anybody give an idea of how to do this? The following code gives an error.
<?php
class someClass {
var $outsideFunc; // placeholder for function to be defined from outside
var $somevar='Me'; // generated text
function echoarg($abc){
$outsideFunc=$this->outsideFunc; // bring the outside function in
call_user_func($outsideFunc,$abc); // execute outside function on text
echo $abc;
}
}
function outsidefunc($param){ // define custom function
$param='I am '.$param;
}
$someClass=new someClass();
$someClass -> outsideFunc = 'outsideFunc'; // send custom function into Class
$someClass -> echoarg($someClass->somevar);
$someClass -> outsidefunc = 'outsidefunc';
In PHP, function names are not case sensitive, yet object property names are. You need $someClass->outsideFunc, not $someClass->outsidefunc.
Note that good OOP design practice calls for the use of getter and setter methods rather than just accessing properties directly from outside code. Also note that PHP 5.3 introduced support for anonymous functions.
Yeah. You are right. Now there is no error. But it does not work either.
By default, PHP does not pass arguments by reference; outsidefunc() does not actually do anything useful. If you want it to set $param in the caller to something else, and do not want to just return the new value, you could change the function signature to look like this:
function outsidefunc(&$param) {
You would also need to change the way you call the function, as call_user_func() does not allow you to pass arguments by reference. Either of these ways should work:
$outsideFunc($abc);
call_user_func_array($outsideFunc, array(&$abc));
Why not pass your function as an argument?
<?php
class someClass {
public $somevar="Me";
public function echoarg($abc,$cb=null) {
if( $cb) $cb($abc);
echo $abc;
}
}
$someClass = new someClass();
$someClass->echoarg($someClass->somevar,function(&$a) {$a = "I am ".$a;});
i am not sure what exactly you are looking for, but what i get is, you want to pass object in a function which can be acheive by
Type Hinting in PHP.
class MyClass {
public $var = 'Hello World';
}
function myFunction(MyClass $foo) {
echo $foo->var;
}
$myclass = new MyClass;
myFunction($myclass);
OP, perhaps closures are what you're looking for?
It doesn't do EXACTLY what you're looking for (actually add function to class), but can be added to a class variable and executed like any normal anonymous function.
$myClass->addFunc(function($arg) { return 'test: ' . $arg });
$myClass->execFunc(0);
class myClass {
protected $funcs;
public function addFunc(closure $func) {
$this->funcs[] = $func;
}
public function execFunc($index) { $this->funcs[$index](); } // obviously, do some checking here first.
}
Consider the following code, which is a scheme of storing a callback function as a member, and then using it:
class MyClass {
function __construct($callback) {
$this->callback = $callback;
}
function makeCall() {
return $this->callback();
}
}
function myFunc() {
return 'myFunc was here';
}
$o = new MyClass(myFunc);
echo $o->makeCall();
I would expect myFunc was here to be echoed, but instead I get:
Call to undefined method MyClass::callback()
Can anyone explain what's wrong here, and what I can do in order to get the desired behaviour?
In case it matters, I am using PHP 5.3.13.
You can change your makeCall method to this:
function makeCall() {
$func = $this->callback;
return $func();
}
Pass it as a string and call it by call_user_func.
class MyClass {
function __construct($callback) {
$this->callback = $callback;
}
function makeCall() {
return call_user_func($this->callback);
}
}
function myFunc() {
return 'myFunc was here';
}
$o = new MyClass("myFunc");
echo $o->makeCall();
One important thing about PHP is that it recognises the type of a symbol with the syntax rather than the contents of it, so you need to state explicitly what you refer to.
In many languages you just write:
myVariable
myFunction
myConstant
myClass
myClass.myStaticMethod
myObject.myMethod
And the parser/compiler knows what each of the symbols means, because it's aware of what they refer to simply by knowing what's assigned to them.
In PHP, however, you need to use the syntax to let the parser know what "symbol namespace" you refer to, so normally you write:
$myVariable
myFunction()
myConstant
new myClass
myClass::myStaticMethod()
$myObject->method()
However, as you can see these are calls rather than references. To pass a reference to a function, class or method in PHP, combined string and array syntax is used:
'myFunction'
array('myClass', 'myStaticMethod')
array($myObject, 'myMethod')
In your case, you need to use 'myFunc' in place of myFunc to let PHP know that you're passing a reference to a function and not retrieving the value the myFunc constant.
Another ramification is that when you write $myObject->callback(), PHP assumes callback is a method because of the parentheses and it does not attempt to loop up a property.
To achieve the expected result, you need to either store a copy of/reference to the property callback in a local variable and use the following syntax:
$callback = $this->callback;
return $callback();
which identifies it as a closure, because of the dollar sign and the parentheses; or call it with the call_user_func function:
call_user_func($this->callback);
which, on the other hand, is a built-in function that expects callback.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Use a variable to define a PHP function
Use Variable as Function Name in PHP
I want to perform a conditional function call but I don't necessarily know what what the function will be, so that would be a long switch.
For example;
$userSelection = "calculator"; /* or "stocks" or whatever widget */
$widget->get_widget($userSelection);
public function __construct($userSelection){
/* pseudo code */
call function $userSelection();
}
public function calculator(){
/* Get Calculator */
}
Sure there is. This feature is called variable functions:
$functionName = "strlen";
$length = $$functionName("Hello world!");
The $$var(...) syntax is convenient, but it will only work with free functions. If you want to call a class method this way, you will need to use call_user_func or call_user_func_array (these functions can also handle the "free function" case).
Look at the call-user-func function. This allows you to call another function, e.g.
call_user_func('calculator')
call_user_func($userSelection);
http://php.net/manual/en/function.call-user-func.php
Take a look at this php functions:
call_user_func(): http://php.net/manual/de/function.call-user-func.php
call_user_func_array(): http://www.php.net/manual/de/function.call-user-func-array.php
create_function(): http://www.php.net/manual/de/function.create-function.php
There is also a direct (though ugly) execution syntax:
function some_func(args) {...}
$function_name='some_func';
$$function_name(args2);
You can use call_user_func() for that, like this:
$userSelection = "calculator";
call_user_func($userSelection[, $param1, $param2, ...]);
call_user_func_array($userSelection, $params);
If it's just a function you're after then using this should solve your problems
$function = "echo";
$$function "fooBar";
If it's a class method that you want to keep flexible use magic method __call() which will allow you to use method names that are not pre-defined.
__call() is triggered when invoking inaccessible methods in an object context.
i.e.
class Foo {
public function __call($name, $arguments) {
echo $name;
}
}
$foo = new Foo();
$foo->bar(); // will echo "bar"
PHP built-in function 'eval' can do everything, but beware of injection.
$var = "somefunction";
eval("$var();");
http://php.net/manual/en/function.eval.php
It's pretty simple if that's what you mean.
function calculator() {
echo 'foo';
}
$userSelection = "calculator";
if (function_exists($userSelection)) {
$userSelection();
}
Or within a class like in your example:
class widget {
public function __construct($userSelection) {
echo 'constructed widget<br>';
if (function_exists($userSelection)) {
$this->$userSelection();
}
}
public function calculator() {
echo 'bar';
}
}
$userSelection = "calculator";
$widget = new widget($userSelection);
Or from outside a class when the function is part of the class.
class widget {
public function calculator() {
echo 'bar';
}
}
$widget = new widget();
$userSelection = "calculator";
$widget->$userSelection();
I would work with if/else statements though to determine the function to be called just to be sure that only valid functions are executed (do you sanitize the user selection or do you just get it from a $_POST? The latter would be a very bad idea).
You can do following :
$var = 'abc';
switch ($var) {
case 'abc':
$result = $var('test param');
echo $result;
break;
default :
echo 'default';
break;
}
function abc($data) {
return $data;
}
I'm using PHPs create_function($args, $code) function to dynamically load a function definition from a database.
The way I'm attempting to implement it is as follows:
I have a class MyClass which has an instance variable myFunction. The constructor populates that instance variable with the result of a call to create_function. I'm hoping to dynamically create a function for the specific object (once instantiated) of this class, that can be called as $object->myFunction(arg1, arg2);
So my class looks like:
class MyClass {
public $myFunction = '';
public function __construct() {
$this->myFunction = //return function body from DB call.
}
}
I'm then trying to call this dynamic function from elsewhere in my program on the instantiated "MyClass" object by doing something like...
$object = new MyClass();
$object->myFunction(args..);
However I keep getting errors such as:
MyClass and its behaviors do not have a method or closure named myFunction.
When I run var_dump($object->myFunction) I get back "lambda_xx", which is a good sign meaning create_function is at least working.
Interesting Update on Works vs. Doesn't Work cases
It turns out that in my "other file" where I am doing the following:
$pm = Yii::app()->user->postMatching; //This is a PostMatching object made elsewhere
$c = $pm->findRelated;
foreach ($posts as $post) {
var_dump($c);
$postIds = $c($post, $limit);
//post to related mapping
$specificRelatedPostIds[$post->postId] = $postIds;
}
exit; // exiting for testing
This doesn't work, but if instead of pulling the object $pm from Yii::app()->user->postMatching I just create a new one:
$pm = new PostMatching();
$c = $pm->findRelated; //the anon function instance variable
$c(); // THIS WORKS NOW!
So naturally I var_dumped $pm and $c in both the "newly created" case and the case where I get it from Yii::app()->user->postMatching, and they are identical. The only thing that is different is the name of the anonymous function (as expected).
Does anyone have any idea why this might be the case? In both cases $pm IS an instantiated PostMatching object with that instance variable, I'm just unable to use the syntax to invoke it!
Just updated the above with newly discovered "Twists", thanks guys!
Maybe something along these lines can be useful:
class MyClass {
private $myFunction = '';
public function __construct() {
$this->myFunction = //return function body from DB call.
}
public function myFunction() {
$args = func_get_args();
return call_user_func_array($this->myFunction, $args);
}
}
That's due to parsing-related troubles that PHP has. This version should work:
$object = new MyClass();
$method = $object->myFunction;
$method(args..);
See it in action.
You can call the method like this:
call_user_func($object->myFunction, args..);