Is this possible Im trying to do this with the extract() function since Im unable to get the method arguments in advance
class Test{
public function getData($id){
//use $id here
}
}
$class = 'Test'; //this is determined dymanically
$method = 'getData'; //this is also determined dynamically
$arguments = ['id'=>'1234'];
$test = new $class();
$test->{$method}(extract($arguments));
//this generates a warning Missing argument 1 for Test::getData(),
called
How can this be implemented?
EDIT
It appears I've simplified it too much, the code is intended to be the main deployment mechanism in a mini-framework Im developing so the method - getData is determined dynamically and therefore I cant know the arguments for each method in advance.
Thanks
extract is for assigning to variables. For each element of the associative array, it will assign to a variable in the current scope with that name. It returns the number of variables it assigned, not the value of any of the variables.
There's no reason to use extract in your case, just get the element of the array that you want:
$test->getData($arguments['id']);
I'm not sure why you're getting an error about a missing argument. It should pass 1 as the $id argument, since there's one element in the array.
If you don't know which elements the function needs, a better design would be to pass the whole $arguments array to the function, and let it use the parts it wants.
public function getData($args) {
$id = $args['id'];
// Use $id
}
...
$test->getData($arguments);
Just extract your array and pass $id
<?php
class Test{
public function getData($id){
echo $id;
}
}
$arguments = array('id'=>'1234');
extract($arguments);
$test = new Test();
$test->getData($id);
or
$arguments = array('id'=>'1234');
extract($arguments);
$test = new Test();
foreach($arguments as $key=>$value){
$test->getData($$key);
}
Ive found the solution using ReflectionMethod
$reflection = new \ReflectionMethod('Test', 'getData');
$pass = [];
foreach($reflection->getParameters() as $param){
//parse the method to get its arguments and filter through the sent arguments array
if(isset($args[$param->getName()])){
//check if the arguments exists and select them
$pass[] = $args[$param->getName()];
}
else{
$pass[] = $param->getDefaultValue();
}
}
//execute the resolved parameters
return $reflection->invokeArgs(new Test, $pass);
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.
public function get_entity_keyNumber($entity)
{
$this->session = Session::instance();
(int)$lastNumber = 0;
$user_data= array_keys($this->session->as_array());
$user_experience = array_filter($user_data,function($value){return strstr($value, $entity);});
if(!empty($user_experience))
{
$lastElement = end($user_experience);
(int)$lastNumber = substr($lastElement,-1);
$lastNumber++;
}
return $lastNumber;
}
this function is doing that it return me last character of last key in $user_data and Iam incrementing to it by casting it to Integer.
$user_data = array("experience0") like if i use this function:
(int)$lastNumber = get_entity_keyNumber("experience");
It will return me 1 and i will add another array to a session with name experience1 then experience2,experince3 so it cannot overwirte the keys in session
when iam using this function why is this throwing error:
Call to undefined function get_entity_keyNumber() or sometimes $entity variable undefined
Iam new to php and as well as to kohana
when using inline it works perfectly.
Try this:
$instance->get_entity_keyNumber("experience");
$instance means an instance of the class that the method get_entity_keyNumber belongs to.
In the class definition field, it'll be $this.
I'm sorry for not knowing about kohana.
I used private attribute in class with same name and assigned value of method parameter to it and used that class attribute using $this to filter array values, My OOP is weak :)
private $entity;
public function get_entity_keyNumber($entity)
{
$this->$entity =$entity;
$this->session = Session::instance();
(int)$lastNumber = 0;
$user_data= array_keys($this->session->as_array());
$user_experience = array_filter($user_data,function($value){return strstr($value, $this->entity);});
if(!empty($user_experience))
{
$lastElement = end($user_experience);
(int)$lastNumber = substr($lastElement,-1);
$lastNumber++;
}
return $lastNumber;
}
I can't quite understand why the output of this code is '1'.
My guess is that php is not behaving like most other OO languages that I'm used to, in that the arrays that php uses must not be objects. Changing the array that is returned by the class does not change the array within the class. How would I get the class to return an array which I can edit (and has the same address as the one within the class)?
<?php
class Test
{
public $arr;
public function __construct()
{
$this->arr = array();
}
public function addToArr($i)
{
$this->arr[] = $i;
}
public function getArr()
{
return $this->arr;
}
}
$t = new Test();
$data = 5;
$t->addToArr($data);
$tobj_arr = $t->getArr();
unset($tobj_arr[0]);
$tobj_arr_fresh = $t->getArr();
echo count($tobj_arr_fresh);
?>
EDIT: I expected the output to be 0
You have to return the array by reference. That way, php returns a reference to the array, in stead of a copy.
<?php
class Test
{
public $arr;
public function __construct()
{
$this->arr = array();
}
public function addToArr($i)
{
$this->arr[] = $i;
}
public function & getArr() //Returning by reference here
{
return $this->arr;
}
}
$t = new Test();
$data = 5;
$t->addToArr($data);
$tobj_arr = &$t->getArr(); //Reference binding here
unset($tobj_arr[0]);
$tobj_arr_fresh = $t->getArr();
echo count($tobj_arr_fresh);
?>
This returns 0.
From the returning references subpage:
Unlike parameter passing, here you have to use & in both places - to
indicate that you want to return by reference, not a copy, and to
indicate that reference binding, rather than usual assignment, should
be done
Note that although this gets the job done, question is if it is a good practice. By changing class members outside of the class itself, it can become very difficult to track the application.
Because array are passed by "copy on write" by default, getArr() should return by reference:
public function &getArr()
{
return $this->arr;
}
[snip]
$tobj_arr = &$t->getArr();
For arrays that are object, use ArrayObject. Extending ArrayObject is probably better in your case.
When you unset($tobj_arr[0]); you are passing the return value of the function call, and not the actual property of the object.
When you call the function again, you get a fresh copy of the object's property which has yet to be modified since you added 5 to it.
Since the property itself is public, try changing:
unset($tobj_arr[0]);
To: unset($t->arr[0]);
And see if that gives you the result you are looking for.
You are getting "1" because you are asking PHP how many elements are in the array by using count. Remove count and use print_r($tobj_arr_fresh)
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..);
I have a function that does something similar to this:
function load_class($name){
require_once('classes/'.$name.'.php');
return new $name();
}
what I want to do is modify it so I can do something like this
function load_class($name, $vars = array()){
require_once('classes/'.$name.'.php');
return new $name($array[0], $array[1]);
}
The general gist of it is.
I want to be able to pass in an array of values that, gets used as the parameters for the class.
I dont want to pass in the actual array.
is this possible?
Of course, it's called var args and you want to unpack them. http://php.net/manual/en/function.func-get-arg.php. Check out the examples... unpacking an array of arguments in php.
See Also How to pass variable number of arguments to a PHP function
if you are trying to load classes then you could use __autoload function
more information here
You can call functions this way with call_user_func_array, but in the case of a class constructor, you should use ReflectionClass::newInstanceArgs:
class MyClass {
function __construct($x, $y, $z) { }
}
$class = new ReflectionClass("MyClass");
$params = array(1, 2, 3);
// just like "$instance = new MyClass(1,2,3);"
$instance = $class->newInstanceArgs($params);
Your code might look like this:
function load_class($name, $vars = array()){
require_once('classes/'.$name.'.php');
$class = new ReflectionClass($name);
return $class->newInstanceArgs($vars);
}