I'm making a class that has several functions. There is one thing that makes me confused, which is when I call the function itself in its function:
in the following function, I don't get an error when I call the getChildKey function inside the function itself:
function getChildKey1($FPTree)
{
function getChildKey($FPTree){
$result = [];
if (isset($FPTree['child'])){
foreach ($FPTree['child'] as $key => $value){
$result[$key]=getChildKey($value);
}
}
if (empty($result)){
return 'gak ada array';
}
return $result;
}
$output = [];
foreach ($FPTree as $index => $child){
$output[$index]=getChildKey($child);
}
return $output;
}
and I get an error when I try to create a function similar to the getChildKey function, which calls the function itself in it:
function pathFinder($arr = [], $needle)
{
$path = '';
$paths = [];
foreach ($arr as $key => $value) {
if (is_array($value)) {
$path .= $key . "->";
pathFinder($value, $needle); //error here
} else {
if ($key === $needle) {
$paths[] = $path . $key;
}
}
}
return $paths; // return all found paths to key $needle
}
Why did it happen? what do I have to do so that the pathFinder function can call itself in it?
When you define a function, and call it that time (if function was found) php creates a scope which will be like a different "house" for all variables and functions that are declared/defined inside it. That's the reason we sometimes need to use the keyword global when we want to reference $var1 which is defined outside the function (and class if any) and we happen to have a variable with the same name declared in our function
CASE 1:
function A($args1)
{
//---- scope of A starts here
function B($args2)
{ return 'hello from B'; }
B(); // call B
//---- scope of A ends here
}
CASE 2:
function A($args1)
{
//---- scope of A starts here
A(); // call A
//but where is A?
//---- scope of A ends here
}
In 1st case the A function's block (anything between { }) is self-contained block meaning it has everything that any code inside it would need in order to run. So when B() is encountered php looks within the scope of A, whether any function named B is defined? Yes!
In 2nd case PHP does the same when it encounters A() but on not finding it there, it looks in the global scope ie. all code that is outside of your class to find a function named A. Since there isn't any it throws a not found exception
When you use $this->A() php knows where to look for this function ie.inside the same object where it was called from and it run perfectly
Related
I have function that uploads files to server:
function upload() {
$files = Input::file(self::$filename);
foreach ($files as $key => $file) {
// Validation
// Uploading
}
}
So, how to make this function as closure? That after loading it returns error or success information to output variable:
$results = function upload() {
}
In results should be result array of uploading files. It can be error or success state.
Since you're using self:: I assume you expect to run this function from inside a class. But you can't really do this - the function should be defined in the class to be able to use self::.
But really, you can just pass it as a parameter.
function upload($filename) {
$files = Input::file($filename); // note, self:: removed
foreach ($files as $key => $file) {
// Validation
// Uploading
}
return $result;
}
And when you need to use it inside your class, call it this way:
$result = upload(self::$filename);
For Converting a Function into a Closure in PHP, you can :
Since PHP 7 >= 7.1.0, you can use Closure::fromCallable()
$cl = Closure::fromCallable("upload");
$result = $cl();
Before (PHP >= 5.4.0), you can use ReflectionFunction::getClosure()
$reflexion = new ReflectionFunction('upload');
$cl = $reflexion->getClosure();
$result = $cl();
Another idea is to construct a new function that call your main function :
$cl = function (...$args) {
return upload(...$args);
};
Updated and Expanded
This answer addresses the OP's primary question of "How to Convert a Function into a Closure in PHP?" One way involves writing a nameless function and then assigning it to a variable. Since the OP's upload code is problematic, i.e. a method without a class, I therefore borrow the corrected code of #astax, modifying it for the purpose of illustrating the mechanics of how to create a closure in PHP, as follows:
<?php
$f = function( $filename ) {
$files = Input::file( $filename );
foreach ($files as $key => $file) {
// Validation
// Uploading
}
return $arrResults;
};
Note the trailing semicolon after the final brace. Also, the closure definition consists of an anonymous function. You may get the result as follows:
<?php
$arrResults = $f( $filename );
// or in a class:
$arrResults = $f( self::$filename );
Note: PHP does not have named closures, so the function needs to be anonymous. Usually it is assigned to a variable but PHP 7 provides more flexibility such that you may avoid setting a variable as the following two basic examples indicate:
<?php
echo (function() {
return 2 * 2;
})(),"\n",
(function(){
return function(){
return 3 * 3;
};
})()();
See live code.
This syntax is possible thanks to the support for function call chaining in PHP 7.
Note: if your code includes a return statement, when the closure executes, you may assign the return value to a variable.
Incidentally, two useful articles on PHP's closures: see this issue as well in this issue.
Lastly, note while other languages recognize a bound variable with a function as a closure and a nameless function as an anonymous function, in PHP support for each is done vis a vis the Closure class.
Caveat:
Care should be taken when creating a closure from the Closure object's fromCallable method since it evidently yields a more restricted closure as the following code demonstrates:
<?php
class A {
private $name;
public function __construct($name)
{
$this->name = $name;
}
}
$person = new A("Tim");
function test() {
return $this->name;
}
$f = function() {
return $this->name;
};
$cl = Closure::fromCallable( $f );
echo $cl->bindTo($person, 'A')();
echo "\n\nBut, Closure::fromCallable problematic:\n";
try {
$cl = Closure::fromCallable( "test" );
echo $cl->bindTo($person, 'A')();
} catch (Error $e) {
echo "Unable to bind closure from callable because: \n";
echo $e->getMessage();
}
see live code
The error message that results is as follows:
Warning: Cannot rebind scope of closure created by
ReflectionFunctionAbstract::getClosure() in /in/qso7b on line 26
Unable to bind closure from callable because: Function name must be a
string
I would create an array with ID posts from inside a function, and get him outside the class.
My code:
<?php
class cat_widget extends WP_Widget {
private $newHomePost = array();
function widget($args, $instance){
//...
foreach($img_ids as $img_id) {
if (is_numeric($img_id)) {
$this->setNewHomePost($newsCounter,$post->ID);
$newsCounter++;
//...
}
}
}
function setNewHomePost($num, $value){
$newHomePost[$num] = $value;
}
function getNewHomePost(){
return "ID: ".$this->newHomePost[0];
}
}
$testA = new cat_widget();
echo $testA->getNewHomePost();
?>
I receive on screen this resuld:
ID:
(without the id)
But if I insert inside setNewHomePost() an echo for the array, I'll obtain correctly the array but inside and not outside class.
function setNewHomePost($num, $value){
$newHomePost[$num] = $valore;
echo $newHomePost[0];
}
So seem that the array works fine inside the "function widget", but doesn't works outside it.
Can someone help me, please?
function setNewHomePost($num, $value){
$newHomePost[$num] = $value;
}
This creates a local variable named $newHomePost, setting a value at an index and returning. Once it returns, the local variable disappears. From the linked manual page:
Any variable used inside a function is by default limited to the local function scope.
You want to set the class member property newHomePost instead:
function setNewHomePost($num, $value) {
$this->newHomePost[$num] = $value;
}
Update
This is how you currently have the get method defined:
function getNewHomePost() {
return "ID: " . $this->newHomePost[0];
}
I suspect you're still fiddling with this and trying to get it to work. If you really want to just only ever return the 0'th index, try something like this instead:
function getNewHomePost() {
return isset($this->newHomePost[0]) ? $this->newHomePost[0] : null;
}
When building a class remember that you cannot make any assumptions about what order your public methods can be called from another object or calling code (even if the calling code itself exists inside of the class. The methods are public, meaning anything can call them). The code above assumes nothing in that you do not have to call addNewHomePost prior to getNewHomePost. I imagine if you look in your logs you may see a few Notice: Undefined index.. type errors.
Also be sure to check on the calling side:
$myClass = new cat_widget;
$myClass->setNewHomePost(0, 'my new home post!');
$homePost = $myClass->getNewHomePost();
echo $homePost ? $homePost : 'None';
I think a better getter method would probably look like this:
function getNewHomePost($i) {
return isset($this->newHomePost[$i]) ? $this->newHomePost[$i] : null;
}
I'd like to get the class/included variables/elements when I included a php file/class, somehow maybe I should try reflection to do that? If so, how?
For instance, I'd have a PHP class called foo.php:
<?php
class foo
{
public function bar()
{
return "foobar";
}
}
?>
then, in bar.php, I would like to:
<?php
class bar
{
public function foo()
{
$included_resources = include("foo.php"); // Notice, $included_resources is an array
if (($key = array_search("foo", $included_resources)) != false) // "foo" can be any variable or class name
return $included_resources[$key]->bar();
}
}
$helloworld = new bar();
echo $helloworld->foo();
?>
Result: a string value of "foobar" will be represented on the screen
First, store the declared variables in an array before including a file. Then do the include. Then store the declared variables in another array again. Then simply check the difference:
$declared_vars_before = get_defined_vars();
include 'another_file.php';
$declared_vars_after = get_defined_vars();
foreach ($declared_vars_after as $value) {
if (!in_array($value, $defined_vars_before)) {
echo $value . '<br>';
}
}
Same with classes, but use get_declared_classes instead of get_defined_vars.
How to access the variable in inner function of outer function variable?
I want to access $arr variable in the inner function.
<?php
function outer() {
$arr = array();
function inner($val) {
global $arr;
if($val > 0) {
array_push($arr,$val);
}
}
inner(0);
inner(10);
inner(20);
print_r($arr);
}
outer();
codepad link
This kind of "inner function" does not do what you probably expect. The global(!) function inner() will be defined upon calling outer(). This also means, calling outer() twice results in a "cannot redefine inner()" error.
As #hindmost pointed out, you need closures, functions that can get access to variables of the current scope. Also, while normal functions cannot have a local scope, closures can, because they are stored as variables.
Your code with Closures:
function outer() {
$arr = array();
$inner = function($val) use (&$arr) {
if($val > 0) {
array_push($arr,$val);
}
}
$inner(0);
$inner(10);
$inner(20);
print_r($arr);
}
outer();
Edited your code. You may want to refer to this
function outer() {
$arr = array();
function inner(&$arr,$val) { // pass array by reference
if($val > 0) {
array_push($arr,$val);
}
}
inner($arr,0);
inner($arr,10);
inner($arr,20);
print_r($arr);
}
outer();
You can pass arr by value, but you will not be able to print this
Array
(
[0] => 10
[1] => 20
)
First, do not use functions inside functions. Why? Because with this you'll get fatal error when triggering outer function second time (and that is because second run will invoke inner function declaration once again).
Second, if you need to do something with function result (as it stands from your code) - return that value. That is what is function intended to do - return some value. Another option may be using reference as a parameter (like in sort() functions family) - but in normal situation you should avoid such behavior. This is side-effect and this makes code less readable in general.
Third, do not use global variables. Because this binds your functions to your context, making your code totally unpredictable in terms of changing global context - and also impossible to scale to other contexts.
Functions in php are all global.
If you want to access the global $arr, then you have to make it global too in outer function.
function outer() {
global $arr;
$arr = array();
function inner($val) {
global $arr;
if($val > 0) {
array_push($arr,$val);
}
}
inner(0);
inner(10);
inner(20);
print_r($arr);
}
outer();
There is an better way of doing this.
function outer() {
$arr = array();
$inner = function ($val) use (&$arr) {
if($val > 0) {
array_push($arr, $val);
}
};
$inner(0);
$inner(10);
$inner(20);
print_r($arr);
}
outer();
Just put global to the outer function.
function outer() {
global $arr;
$arr = array();
function inner($val) {
global $arr;
if($val > 0) {
array_push($arr,$val);
}
}
inner(0);
inner(10);
inner(20);
print_r($arr);
}
Updated codepad.
I have a code problem which stems from the fact that I am using certain libraries of code I cannot change.
I use the following code to pass execution of any undefined methods to another class, and it works fine but it seems like a waste doubling up.
Any suggestions?
Basically I want to know if it's possible to pass an unknown number of parameters to a method (without using call_user_func_array(), just in case they need to be passed by reference). I am not asking how to use func_get_args(), rather the reverse.
Or should I just allow for a few more arguments in the first logic path (the list() code)?
class Foo {
__construct() {
$this->external = new ClassThatIHaveNoControlOver();
}
function bar($name) {
return 'Hi '.$name;
}
function __call($method, $arguments) {
if (count($arguments) < 3) {
// call_user_func_array won't pass by reference, as required by
// ClassThatIHaveNoControlOver->foobar(), so calling the function
// directly for up to 2 arguments, as I know that foobar() will only
// take 2 arguments
list($first, $second) = $arguments + Array(null, null);
return $this->external->$method($first, $second);
} else {
return call_user_func_array(array($this->external, $method), $arguments);
}
}
}
$foo = new Foo();
$firstName = 'Bob';
$lastName = 'Brown';
echo $foo->bar($firstName); // returns Hi Bob as expected
echo $foo->foobar($firstName, $lastName); // returns whatever
// ClassThatIHaveNoControlOver()->foobar() is meant to return
EDIT
Just to clarify, I know I can use this method to rejig the parameters as references, but that would mean passing everything as a reference, even if the method didn't require it - something I was trying to avoid, but seems unlikely at the moment.
As commented in the thread question post's comments this is an example and not necessarily (likely) best practice.
//Some vars
$foo = "shoe";
$bar = "bucket";
//Array of references
$arr = Array(&$foo, &$bar);
//Show that changing variable value affects array content
$foo = "water";
echo $arr[0];
//Sample function
function fooBar($a)
{
$a[0] = "fire";
}
//Call sample function
call_user_func("fooBar",$arr);
//Show that function changes both array contents and variable value by reference
echo $arr[0];
echo $foo;
Expanding a bit on the discussion, again not the most industry standard approach but it'll do the job.
function pushRefOnArray(&$arr, &$var, $key = false)
{
if(isset($key))
$arr[$key] = &$var;
else
$arr[] = &$var;
}
Essentially you can dynamically build your array and call pushRefToArray() any time you need to pass an item to be passed as reference rather than by value.
You could use something like this:
public function __call($method, $params = array()) {
switch (count($params)) {
case 0:
return $this->external->{$method}();
case 1:
return $this->external->{$method}($params[0]);
case 2:
return $this->external->{$method}($params[0], $params[1]);
case 3:
return $this->external->{$method}($params[0], $params[1], $params[2]);
default:
return call_user_func_array(array(&this->external, $method), $params);
}
}