PHP Use() Function for Scope

PHP use() function for scope?

use is not a function, it's part of the Closure syntax. It simply makes the specified variables of the outer scope available inside the closure.

$foo = 42;

$bar = function () {
// can't access $foo in here
echo $foo; // undefined variable
};

$baz = function () use ($foo) {
// $foo is made available in here by use()
echo $foo; // 42
}

For example:

$array = array('foo', 'bar', 'baz');
$prefix = uniqid();

$array = array_map(function ($elem) use ($prefix) {
return $prefix . $elem;
}, $array);

// $array = array('4b3403665fea6foo', '4b3403665fea6bar', '4b3403665fea6baz');

Reference: What is variable scope, which variables are accessible from where and what are undefined variable errors?

What is "variable scope"?

Variables have a limited "scope", or "places from which they are accessible". Just because you wrote $foo = 'bar'; once somewhere in your application doesn't mean you can refer to $foo from everywhere else inside the application. The variable $foo has a certain scope within which it is valid and only code in the same scope has access to the variable.

How is a scope defined in PHP?

Very simple: PHP has function scope. That's the only kind of scope separator that exists in PHP. Variables inside a function are only available inside that function. Variables outside of functions are available anywhere outside of functions, but not inside any function. This means there's one special scope in PHP: the global scope. Any variable declared outside of any function is within this global scope.

Example:

<?php

$foo = 'bar';

function myFunc() {
$baz = 42;
}

$foo is in the global scope, $baz is in a local scope inside myFunc. Only code inside myFunc has access to $baz. Only code outside myFunc has access to $foo. Neither has access to the other:

<?php

$foo = 'bar';

function myFunc() {
$baz = 42;

echo $foo; // doesn't work
echo $baz; // works
}

echo $foo; // works
echo $baz; // doesn't work

Scope and included files

File boundaries do not separate scope:

a.php

<?php

$foo = 'bar';

b.php

<?php

include 'a.php';

echo $foo; // works!

The same rules apply to included code as applies to any other code: only functions separate scope. For the purpose of scope, you may think of including files like copy and pasting code:

c.php

<?php

function myFunc() {
include 'a.php';

echo $foo; // works
}

myFunc();

echo $foo; // doesn't work!

In the above example, a.php was included inside myFunc, any variables inside a.php only have local function scope. Just because they appear to be in the global scope in a.php doesn't necessarily mean they are, it actually depends on which context that code is included/executed in.

What about functions inside functions and classes?

Every new function declaration introduces a new scope, it's that simple.

(anonymous) functions inside functions

function foo() {
$foo = 'bar';

$bar = function () {
// no access to $foo
$baz = 'baz';
};

// no access to $baz
}

classes

$foo = 'foo';

class Bar {

public function baz() {
// no access to $foo
$baz = 'baz';
}

}

// no access to $baz

What is scope good for?

Dealing with scoping issues may seem annoying, but limited variable scope is essential to writing complex applications! If every variable you declare would be available from everywhere else inside your application, you'd be stepping all over your variables with no real way to track what changes what. There are only so many sensible names you can give to your variables, you probably want to use the variable "$name" in more than one place. If you could only have this unique variable name once in your app, you'd have to resort to really complicated naming schemes to make sure your variables are unique and that you're not changing the wrong variable from the wrong piece of code.

Observe:

function foo() {
echo $bar;
}

If there was no scope, what would the above function do? Where does $bar come from? What state does it have? Is it even initialized? Do you have to check every time? This is not maintainable. Which brings us to...

Crossing scope boundaries

The right way: passing variables in and out

function foo($bar) {
echo $bar;
return 42;
}

The variable $bar is explicitly coming into this scope as function argument. Just looking at this function it's clear where the values it works with originate from. It then explicitly returns a value. The caller has the confidence to know what variables the function will work with and where its return values come from:

$baz   = 'baz';
$blarg = foo($baz);

Extending the scope of variables into anonymous functions

$foo = 'bar';

$baz = function () use ($foo) {
echo $foo;
};

$baz();

The anonymous function explicitly includes $foo from its surrounding scope. Note that this is not the same as global scope.

The wrong way: global

As said before, the global scope is somewhat special, and functions can explicitly import variables from it:

$foo = 'bar';

function baz() {
global $foo;
echo $foo;
$foo = 'baz';
}

This function uses and modifies the global variable $foo. Do not do this! (Unless you really really really really know what you're doing, and even then: don't!)

All the caller of this function sees is this:

baz(); // outputs "bar"
unset($foo);
baz(); // no output, WTF?!
baz(); // outputs "baz", WTF?!?!!

There's no indication that this function has any side effects, yet it does. This very easily becomes a tangled mess as some functions keep modifying and requiring some global state. You want functions to be stateless, acting only on their inputs and returning defined output, however many times you call them.

You should avoid using the global scope in any way as much as possible; most certainly you should not be "pulling" variables out of the global scope into a local scope.

What is the scope of a PHP function defined within a PHP anonymous function?

Cracks knuckles

Technically the syntax is "correct" (it won't generate a fatal error) but the semantics of PHP render it effectively meaningless in its current form. Let's look at a few things first, namely how PHP handles the assignment of named functions to variables:

php > echo shell_exec("php -v");
PHP 5.4.16 (cli) (built: Oct 30 2018 19:30:51)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies

php > function speak($arg) {echo "{$arg}\n";}
php > function give($arg) {return $arg;}
php > $speak = speak(4);
4
php > $give = give(4);
php > var_dump($speak);
NULL
php > var_dump($give);
int(4)

The function itself is executed upon assignment and its return value (NULL or otherwise) is assigned to the variable. Since we're only assigning the return value of a function's execution, trying to use this variable as a function name has no use:

php > $speak(4);
php > $give(4);
php >

Let's contrast this to the assignment of an anonymous function (a.k.a. a 'Closure'):

php > $min = 1; $max = 6;
php > $checkName = function ($value) use ($min, $max) {
php { echo "value: {$value}\n";
php { echo "min: {$min}\n";
php { echo "max: {$max}\n";
php { };
php > var_dump($checkName);
object(Closure)#1 (2) {
["static"]=>
array(2) {
["min"]=>
int(1)
["max"]=>
int(6)
}
["parameter"]=>
array(1) {
["$value"]=>
string(10) "<required>"
}
}

Unlike some other languages, a closure is represented in PHP by an actual Object. Variables inside the 'use' clause are imported at the time the Closure was created; function parameters (i.e. $value) have their values captured when the Closure is called (hence why we see it noted as a required parameter and not a static value). The semantics of references within Closures aren't worth considering right now but if you want further reading, goat's answer to this question is a great start.

The major takeaway here is that the Closure's assignment to $checkName did not execute the Closure itself. Instead, $checkName becomes a sort of "alias" we can use to reference this function by name:

php > $checkName("hello stackoverflow");
value: hello stackoverflow
min: 1
max: 6
php >

Given how loose PHP is about the number of function parameters passed, a zero-parameter execution returns expected results:

php > $checkName();
value:
min: 1
max: 6
php >

Now let's take it another level deeper and define a function within a function:

php > function myOuterFunc($arg) {
php { function myInnerFunc($arg){
php { echo "{$arg}\n";
php { }
php { }
php > $myVal = myOuterFunc("Hello stackoverflow");
php > var_dump($myVal);
NULL
php >

By now this result should make sense. Functions do not execute unless explicitly called; just because we call myOuterFunc doesn't mean we execute any function code defined inside of it. That's not to say that we couldn't:

php > function myOuterFunc($arg) {
php { function myInnerFunc($arg){
php { echo "{$arg}\n";
php { }
php { myInnerFunc($arg);
php { }
php > $myVal = myOuterFunc("Hello stackoverflow");
Hello stackoverflow
php > var_dump($myVal);
NULL
php >

Which brings us back around to what is essentially your question: what about a named function inside of a Closure? Given what we've now discovered about function execution, we can generate a series of very predictable examples:

$min = 1; $max = 6;
$checkName = function ($value) use ($min, $max) {
function question(){echo "How are you\n";}
echo "value: {$value}\n";
echo "min: {$min}\n";
echo "max: {$max}\n";
};
php > $checkName("Hello stackoverflow");
value: Hello stackoverflow
min: 1
max: 6
php >

As expected, the named function's code inside the Closure is not executed because we have not explicitly called it.

php > $min = 1; $max = 6;
php > $checkName = function ($value) use ($min, $max) {
php { function question(){echo "How are you\n";}
php { echo "value: {$value}\n";
php { echo "min: {$min}\n";
php { echo "max: {$max}\n";
php { question();
php { };
php > $checkName("Hello stackoverflow");
value: Hello stackoverflow
min: 1
max: 6
How are you
php >

Explicitly calling the inner function works just fine, provided we define that function before we call it:

php > $min = 1; $max = 6;
php > $checkName = function ($value) use ($min, $max) {
php { echo "value: {$value}\n";
php { echo "min: {$min}\n";
php { echo "max: {$max}\n";
php { question();
php { function question(){echo "How are you\n";}
php { };
php > $checkName("Hello stackoverflow");
value: Hello stackoverflow
min: 1
max: 6
php >

php > $min = 1; $max = 6;
php > $checkName = function ($value) use ($min, $max) {
php { echo "value: {$value}\n";
php { echo "min: {$min}\n";
php { echo "max: {$max}\n";
php { function question(){echo "How are you\n";}
php { question();
php { };
php > $checkName("Hello stackoverflow");
value: Hello stackoverflow
min: 1
max: 6
How are you
php >

So to the point of your questions then:

  1. Yes it's legal and what you're attempting is possible but semantically meaningless in its current form.

  2. Any named functions inside the Closure definitely do not reside in the global namespace, they are within the scope of their defining Closure. FWIW, the term "members" typically refers to class variables (usually called "properties" in PHP). While Closures are an Object and let you duplicate the functionality of instance variables found within classes, they should not be confused or construed with classes in any way.

3/4) Not how you're trying to use it, no. Nothing outside of the Closure has any concept of the functions inside; if in calling your Closure code, said code performs operations using the inner functions, then they will see the light of day. But there is no immediate way to reference those inner functions as if they were defined outside of the Closure's scope.

In other words, the only way you'll get those inner functions to execute is if a) that code is specifically executed by the Closure's code and b) you execute said Closure code.

Hope this helps.

Passing Variables into PHP Function Scope

$response_text isn't in scope inside the anonymous function that you're creating:

function create_dynamic_function($function_name,$response_text){
global $function_list;
$function_list[$function_name] = function() use ($response_text) {
echo $response_text; // Want to Echo the Input $response_text, but it cannot find it
};
}

For reference, arguments passed to an anonymous function are values applied when that function is executed; arguments passed via use are values applied when the function is defined.

PHP Function Scope: How to extend?

Your code is a bit confusing because no matter which value $x has in the beginning $x will always be undefined (notice: undefined index).

function foo(){
return $x;
}

There is no $x and the outer scope will not be accessed.

function foo($x){
return $x;
}

or

function foo() use ($x) {
return $x;
}

or

function foo(){
global $x;
return $x;
}

... the outer scope will be accessed.

Access variables from parent scope in anonymous PHP function

Use the use keyword to bind variables into the function's scope.

function() use ($db) {

Closures may also inherit variables from the parent scope. Any such variables must be declared in the function header [using use].

http://www.php.net/manual/en/functions.anonymous.php

PHP function use(*) all

Closures should be very limited in what they do. 4-5 lines tops really.

In my mind, it is far better to define the variables being made available to it as closures are essentially unstructured.

I usually inline them like this though:

array_walk($this->products, function ($quantity, $product) use ($tax, &$total) {

});

The new operation of the use keyword might be a bit cumbersome, but it is better than global. This is a much more manageable can of worms.



Related Topics



Leave a reply



Submit