Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel 4: Why does my class autoload but global variables are not available

I've autoloaded a class, which is properly namespaced and PSR-0. I put it in app/lib/CI, and the class and it's filename are the same "DB". The class file itself includes a config file before the actual class:

require( 'config.php' );

class DB {
  // ...
}

The class is clearly autoloading, because when I call the static method connect it does display an error message from inside ::connect(). The problem is, global variables that are inside the included config.php are not available inside the class::method.

So, to be clear, the array $connection_settings is inside config.php, but even when using:

global $connection_settings;

$connection_settings is not set inside the connect method.

Something interesting is that even though the class is autoloaded, if I include the class from the top of my routes.php file, everything works normally. So what am I not doing right to get autoloading to work the way I consider "normal"?

like image 337
Brian Gottier Avatar asked Mar 28 '13 07:03

Brian Gottier


2 Answers

This has nothing to do with either Laravel or Composer, but rather with autoload mechanics. Phill Sparks' great answer already pointed out a subtle yet crucial difference of including files using autoload, and that's the key to understand and solve your problem:

The PHP Manual says:

Using global keyword outside a function is not an error. It can be used if the file is included from inside a function.

When you use autoload via spl_autoload_register(), the include happens inside the autoloader function. So any variables declared in the included file body does not have global scope, the same for your config.php vars.

So, while you can access such vars in DB.php body, they are not available inside its classes and functions. That's why using global just inside connect() does not work: because such vars are not really global in the first place!

So you need to "make" them global first, by using global in the same scope where they were declared (either at DB.php or config.php) and then use global again inside connect() to access such vars.

A simple example:

test.php:

<?php
spl_autoload_register(function ($class) {
    require(__DIR__.'/'.$class.'.php');
});

$foo = new Foo();
var_dump($foo->bar());

Foo.php:

<?php

global $foobar;
$foobar = "just a test";

class Foo
{
    function bar() {
        global $foobar;
        return $foobar;
    }
}

It works perfectly and prints just a test. The first global puts $foobar at global scope, the second one access it. Removing either will break the code, the first omission being more "evil", as it will print NULL with no errors.

like image 134
MestreLion Avatar answered Sep 23 '22 02:09

MestreLion


This is an issue with Composer rather than Laravel. Composer makes every effort not to pollute the global scope during autoloading (discussed briefly in #1297). If you want to force global variables then you should declare them as global in your config file, as well as in any function using them.

The PHP Manual says:

Using global keyword outside a function is not an error. It can be used if the file is included from inside a function.

The below code works for me (with Laravel 4b4 on PHP 5.4.13). Removing either global line breaks the code (in different ways).

config.php

global $connection_settings;
$connection_settings = array(/* ... */);

DB.php

require 'config.php';
class DB {
    static function connect()
    {
        global $connection_settings;
        // Do something with $connection_settings
    }
}
like image 30
Phill Sparks Avatar answered Sep 23 '22 02:09

Phill Sparks