Repox Repox - 1 month ago 14
PHP Question

Access variables from the global scope within a closure

I am well aware that globals are evil and this is an issue I will deal with later. It's not my codebase, but I've been assigned some cleaning up tasks.

Trying to smarten up a codebase, I decided to implement simple routing by using a package known as AltoRouter - I've worked with it before and it has worked fine for my purposes.

Now, the codebase is using a large amount of variables declared in the global scope. Usually, these variables are then fetched by using the

global
keyword. But for some reason, this doesn't work when I'm working inside a closure.

Consider this simple routing example:

<?php
require 'vendor/autoload.php';
$router = new AltoRouter();

$router->map('GET', '/shops/[i:id]', function($id) {

$_GET['shop_id'] = $id;
require 'go_to_shop.php';
});

$match = $router->match();

if( $match && is_callable( $match['target'] ) ) {
call_user_func_array( $match['target'], $match['params'] );
}


This calls my closure that sets a variable and requires a file.

This produces an error:


Fatal error: Call to a member function get() on null in
/vagrant/Core/CampaignHandler.php on line 71


Now, the code being called doing this is the following (line 70-71):

// Inside a method
global $serviceContainer;
$dispatcher = $serviceContainer->get("dispatcher");


The
$serviceContainer
is being declared by including a file early on:

$serviceContainer = new ServiceContainer();
$serviceContainer->set("dispatcher", new EventDispatcher());


Basically, if I move the contents of the closure outside of the closure, everything works perfectly - but as soon as I'm doing it from inside the closure, all variables accessed via the global scope is empty - and I have no idea as to why.

I've tried using
use
on the closure, this didn't work either.

I'm mostly looking for an explanation rather than a solution.

Answer

Globals are evil for a reason. You get the error because the global is not initialized at the time when function is being called. The mess of globals and requires is the exact issue and you are already trying to deal with it.

There is no problem to use globals in closure per se. This example works perfectly fine:

global.php:

<?php
class Foo {
    public function bar() { return 'bar';}
}

$foo = new Foo;

test.php:

<?php
require 'global.php';

$test = function($param) {
    global $foo;
    echo $param, $foo->bar();
}

call_user_func_array($test, ['baz']);

so php test.php outputs bazbar;