Dynamic static method call in PHP? - php

Please could someone experienced in PHP help out with the following. Somewhere in my code, I have a call to a public static method inside a non-instantiated class:
$result = myClassName::myFunctionName();
However, I would like to have many such classes and determine the correct class name on the fly according to the user's language. In other words, I have:
$language = 'EN';
... and I need to do something like:
$result = myClassName_EN::myFunctionName();
I know I could pass the language as a parameter to the function and deal with it inside just one common class but for various reasons, I would prefer a different solution.
Does this make any sense, anyone? Thanks.

Use the call_user_func function:
http://php.net/manual/en/function.call-user-func.php
Example:
call_user_func('myClassName_' . $language . '::myFunctionName');

I think you could do:
$classname = 'myClassName_' . $language;
$result = $classname::myFunctionName();
This is called Variable Functions

I would encapsulate the creation of the class you need in a factory.
This way you will have a single entry point when you need to change your base name or the rules for mapping the language to the right class.
class YourClassFactory {
private $_language;
private $_basename = 'yourclass';
public YourClassFactory($language) {
$this->_language = $language;
}
public function getYourClass() {
return $this->_basename . '_' . $this->_language;
}
}
and then, when you have to use it:
$yourClass = $yourClassFactoryInstance->getYourClass();
$yourClass::myFunctionName();

As temuri said, parse error is produced, when trying '$className::functionName' :
Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM ...
In my case (static method with 2 arguments), best solutions is to use call_user_func_array with 2 arrays (as suggested by nikc.org):
$result = call_user_func_array(array($className, $methodName), array($ard1, $arg2));
BR

although i think the way you deal is a very bad idea, i think i may have a solution
$className = 'myClassName_'.$language;
$result = $className::myFunctionName();
i think this is what you want

You can easily do next:
<?php
class B {
public static $t = 5;
public static function t($h) {
return "Works!" . $h;
}
}
$g = 't';
$class = 'B';
echo $class::$g('yes'); //Works! Yes
And it will works fine, tested on PHP 5.2 >=

As far as i could understand your question, you need to get the class name which can be done using get_class function. On the other hand, the Reflection class can help you here which is great when it comes to methods, arguments, etc in OOP way.

Solutions like:
$yourClass::myFunctionName();
will not work. PHP will produce parse error.
Unfortunately, the only way is to use very slow call_user_func().

I know it's an old thread, but as of PHP 5.3.0 you should be using forward_static_call
$result = forward_static_call(array('myClassName_EN', 'myFunctionName'));
Using your $language variable, it might look like:
$result = forward_static_call(array('myClassName_' . $language, 'myFunctionName'));

Related

Is it possible to get the list of the class alias in php as array?

example
namespace Foo;
use Test\One;
use Test\Two;
use Test\Three;
class Sample
{}
How can I get the aliases (USE) as an array?
example of what I am looking to get
$test = [Test\One, Test\Two, Test\Tree];
Does anybody have any suggestions without scanning the file?
or is there a PHP function that will return the list aliases as an array?
Any help will be very appreciated.
Assuming I have the following class and the file is located and named as following file name and location src/Foo.php
namespace Foo;
use Test\One;
use Test\Two;
use Test\Three;
class Sample
{}
now I can scan this file
with this function, I can scan that class and get the result expected.
<?php
use \SplFileObject;
class Scanner
{
public static function getUseAliases()
{
$className = new SplFileObject("src/Foo.php");
$use = [];
while (!$className->eof())
{
$alias = explode("use ", $className->fgets());
if(!empty($alias[1]))
{
$use[] = trim($alias[1]);
}
}
$className = null; //Unset the file to prevent memory leaks
print_r($use);//will print my expected output [Test\One, Test\Two, Test\Three]
}
}
I think there should be a better way to get the same results and this is why I posted my current solution. Please let me know your thoughts.
You can try to use this class:
https://gist.github.com/Zeronights/7b7d90fcf8d4daf9db0c

Alternative for eval() -PHP

i am trying to implement URL mapping in PHP. I have a json file which stores the url and functions which is to execute when that link is requested. I was using eval() but then i came across this
Kepp the following Quote in mind:
If eval() is the answer, you're almost certainly asking the wrong
question. -- Rasmus Lerdorf, BDFL of PHP
now i am thinking is their any other(better) way to do it.
My json file looks like this.
{
"bw/":"main()",
"bw/login":"login()"
}
and my loadPage function look like this.
function loadPage($url){ //$url = 'bw/'
$str = file_get_contents('urls.json');
$this->link = json_decode($str, true);
$url = ltrim($url,"/");
$key = $this->link[$url];
eval("$key;");
}
EDIT:
i defined $this->link in my code
A slight tweak to your JSON to allow you to call the function dynamically would make it easier, just remove the brackets so it would look like...
{
"bw/":"main",
"bw/login":"login"
}
and then call it using...
function loadPage($url){ //$url = 'bw/'
$url = ltrim($url,"/");
$key = $this->link[$url];
$key();
}
A little better way is changing eval() to:
if (function_exists($key)) {
return $key();
}
return default();
and you might create a function "default" to show an error 404 or default page when function doesn't exists.

how to include script of variables in a class? undefined variable

got a script which has string variables that represent data fields like they are in the database. because this project is a complete mess this is a stage in cleaning it up and not having to rewrite the field name in numerous locations.
so one script 'DataKeys.php' will have variables set to field names.
//results from query1
$keyField1 = 'field1';
$keyField2 = 'field2';
these two vars above is only a snippet of a much longer list.
I want to access this file and use these vars when I am formatting the data to be more friendly for the front end. this script is being accessed in a class however the fields, $keyField1, defined in the script is not being found in the class. I did have the actual string there but I think single access point would be best so when I make future changes I don't need search the whole project.
class DataFormatter {
//put your code here
public function __construct() {
$documentRoot = filter_input(INPUT_SERVER, "DOCUMENT_ROOT");
include ($documentRoot . '/database/values/DataKeys.php');
}
public function cleanData($data){
if (is_null($data) || empty($data))
{
return;
}
foreach($data as $row){
$field1Value = $row[$keyField1];
unset($row[$keyField1]);
}
}
}
I also tried moving the include outside the class definition.
$documentRoot = filter_input(INPUT_SERVER, "DOCUMENT_ROOT");
include ($documentRoot . '/database/values/DataKeys.php');
The error that is being reported is :
Undefined variable: keyField1
SOULTION
Maybe not the optimal way but I took the include statement and placed it inside the function. The code above is just a demo of what I was trying to achieve not the actual code I am using.
the 2 variables are available just after the "include".
you can for example, put the 2 values in properties of the object
include ...;
$this->keyField1 = $keyField1;
$this->keyField2 = $keyField2;
You have to assign DataKeys.php to class member.
class DataFormatter {
private $keyField1;
private $keyField2;
public function __construct($filename) {
include $filename;
$this->keyField1 = $keyField1;
$this->keyField2 = $keyField2;
}
}
$dataFormatter = new DataFormatter(filter_input(INPUT_SERVER, 'DOCUMENT_ROOT') . '/database/values/DataKeys.php');

Is it possible to get the statements within a method in PHP?

function mainFunction() {
functionA(5, "blah");
functionB("ok", "whatever");
}
How to write a function GetFunctions that returns the functions within mainFunction?
How to call them with the parameters given in mainFunction?
How to call them as follows?
foreach (GetFunctions(mainFunction) as $function) {
print "Calling function $function: ";
call($functionA); // called with parameters(5, "blah")
}
Working in PHP 5.2.8
EDIT: OK, here's a more complete explanation. I tried to keep it simple to make it easy to understand, but apparently that wasn't a good idea.
The goal is to call each assertion within a given static method. I am writing a testing framework. Each assertion returns true or false.
I am calling the methods as follows.
$methods = get_class_methods('LibraryTests');
foreach ($methods as $method) {
if ( StartsWith($method, 'Test') ) {
print "calling: " . $method . ": ";
call_user_func('LibraryTests::' . $method);
}
}
The above code calls each method within the class, but I want to call each assertion individually and track the result (true/false). CallAssertion is supposed to call each assertion (such as TestUnit::AssertEqual(GetFormattedHour(5), "5 PM");). This is the method that I am asking about.
Here is the class:
class LibraryTests extends TestUnit {
static $success = 0;
static $failure = 0;
static $total = 0;
static function CallAssertion($assertion) {
self::$total += 1;
if ($assertion) { self::$success += 1; }
else { self::$failure += 1; }
}
static function TestGetFormattedHour() {
TestUnit::AssertEqual(GetFormattedHour(5), "5 PM");
TestUnit::AssertEqual(GetFormattedHour(16), "4 PM");
}
So, the question is, how to write CallAssertion?
You can't.
Instead, create a class and use reflection to get its methods.
Regardless, you'll want to figure out why this is necessary and see if there is an entirely different approach you can use.
(If this is for debugging purposes, you can use debug_backtrace to inspect but its purpose is not for calling functions as you have described in your question.)
Hmm, what problem are you actually trying to solve. To me it sounds like you're trying to inspect the call stack at runtime. If so, I'd suggest just using debug_backtrace() (src).
I wouldn't suggest using that function in production as much though, as it's a rather heavy hit on your code.
One possibility would be to do a file_get_contents on the PHP file that contains main_function, then go through it to parse out main_function and the functions it calls. Of course, I don't know your situation so that might not work.
You can do this with:
http://php.net/manual/en/function.token-get-all.php
Probably a bad idea, but good luck!

Is it possible for PHP to write additional PHP functions like a Lisp macro?

I'm writing a unit testing platform and I want to be able to dynamically generate a function based off of each function in the web service I am testing. The dynamic function would be generated with default(correct) values for each argument in the web service and allow them to be easily traded out with incorrect values for error testing.
$arrayOfDefVals = array(123, 'foo');
testFunctionGenerator('function1', $arrayOfDefVals);
//resulting php code:
function1Test($expectedOutput, $arg1=123, $arg2='foo')
{
try
{
$out = function1($arg1, $arg2);
if($expectedOutput === $out)
return true;
else
return $out;
}
catch ($e)
{
return $e;
}
}
This would allow me to quickly and cleanly pass one bad argument, or any number of bad arguments, at a time to test all of the error catching in the web service.
My main question is:
Is this even possible with php?
If it's not possible, is there an alternative?
EDIT: I'm not looking for a unit test, I'm trying to learn by doing. I'm not looking for advice on this code example, it's just a quick example of what I would like to do. I just want to know if it's possible.
I would not try that first as PHP has not build-in macro support. But probably something in that direction:
function function1($param1, $param2)
{
return sprintf("param1: %d, param2: '%s'\n", $param1, $param2);
}
/* Macro: basically a port of your macro as a function */
$testFunctionGenerator = function($callback, array $defVals = array())
{
$defVals = array_values($defVals); // list, not hash
return function() use ($callback, $defVals)
{
$callArgs = func_get_args();
$expectedOutput = array_shift($callArgs);
$callArgs += $defVals;
return $expectedOutput == call_user_func_array($callback, $callArgs);
};
};
/* Use */
$arrayOfDefVals = array(123, 'foo');
$function1Test = $testFunctionGenerator('function1', $arrayOfDefVals);
var_dump($function1Test("param1: 456, param2: 'foo'\n", 456)); # bool(true)
Probably this is helpful, see Anonymous functions­Docs, func_get_args­Docs, the Union array operator­Docs and call_user_func_array­Docs.
Well, for starters, you can set default parameters in functions:
function function1Test($expectedOutput, $testArg1=123, $testArg2='foo') {
...
}
Beyond that, I'm not really sure what you're trying to achieve with this "function generator"...
Read about call_user_func and func_get_args
This example from the manual should get you on the right track:
<?php
call_user_func(function($arg) { print "[$arg]\n"; }, 'test'); /* As of PHP 5.3.0 */
?>
If it's a function you have file access to (i.e., it's not a part of the PHP standard library and you have permissions to read from the file), you could do something like this:
Assume we have a function like this located in some file. The file will have to be included (i.e., the function will have to be in PHP's internal symbol table):
function my_original_function($param1, $param2)
{
echo "$param1 $param2 \n";
}
Use the ReflectionFunction class to get details about that function and where it's defined: http://us2.php.net/manual/en/class.reflectionfunction.php.
$reflection = new ReflectionFunction('my_original_function');
Next, you can use the reflection instance to get the path to that file, the first/last line number of the function, and the parameters to the function:
$file_path = $reflection->getFileName();
$start_line = $reflection->getStartLine();
$end_line = $reflection->getEndLine();
$params = $reflection->getParameters();
Using these, you could:
read the function out of the file into a string
rewrite the first line to change the function name, using the known function name as a reference
rewrite the first line to alter the parameter defaults, using $params as a reference
write the altered function string to a file
include the file
Voila! You now have the new function available.
Depending on what it is you're actually trying to accomplish, you could also potentially just use ReflectionFunction::getClosure() to get an closure copy of the function, assign it to whatever variable you want, and define the parameters there. See: http://us.php.net/manual/en/functions.anonymous.php. Or you could instantiate multiple ReflectionFunctions and call ReflectionFunction::invoke()/invokeArgs() with the parameter set you want. See: http://us2.php.net/manual/en/reflectionfunction.invokeargs.php or http://us2.php.net/manual/en/reflectionfunction.invoke.php

Categories