LogoPhly, boy, phly
the weblog and site of Matthew Weier O'Phinney

Sunday, May 20. 2007

Globals, continued

Update: Sara has pointed out a flaw in my last case. The file 'loadFileWithGlobals.php' was incorrectly loading the wrong file -- it should be loading 'withGlobals2.php' (updated now). When it does, access to 'baz2' works as it should.

As I note to in my comment, however, I stand by my original rant: relying on globals for your applications is a bad practice, as it makes them difficult to integrate with other applications later. Developers using your application should not need to hunt down exactly when a global is first declared and explicitly push it into the global scope in order to get that application to integrate with others. Use other means, such as singletons or registries, to persist configuration within your applications.


In my last entry, I evidently greatly simplified the issue to the point that my example actually didn't display the behaviour I had observed. I'm going to show a more detailed example that shows exactly the behaviour that was causing issues for me.

First off, this has specifically to do with including files from within functions or class methods that then call on other files that define values in the global scope. In the original example, I show an action controller method that includes the serendipity bootstrap file, which in turn loads a configuration file that sets a multi-dimensional array variable in the global scope. Without first defining the variable in the global scope, this method of running serendipity fails.

Now, for the examples.

First, let's define six files. Four set variables, two by regular declaration, the other two by declaring using $GLOBALS. The other two files each load one of these and act on the variables set.


<?php
// File: withoutGlobals.php
$bar = 'baz';
?>

<?php
// File: withoutGlobals2.php
$bar2 = 'baz2';
?>

<?php
// File: withGlobals.php
$GLOBALS['baz'] = 'bat';
?>

<?php
// File: withGlobals2.php
$GLOBALS['baz2'] = 'bat2';
?>

<?php
// File: loadFileWithoutGlobals.php
include dirname(__FILE__) . '/withoutGlobals2.php';

echo 'Direct access to bar2: ', $bar2, "\n";
echo 'GLOBALS access to bar2: ', $GLOBALS['bar2'], "\n";
?>

<?php
// File: loadFileWithGlobals.php
include dirname(__FILE__) . '/withGlobals2.php';

echo 'Direct access to baz2: ', $baz2, "\n";
echo '$GLOBALS access to baz2: ', $GLOBALS['baz2'], "\n";
?>
 

Now, I'll define a class, MyFoo, that tries in a variety of ways to set and access global values:


<?php
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', true);

class MyFoo
{
    public function setGlobal()
    {
        $GLOBALS['foo'] = 'bar';
    }

    public function loadFileWithoutGlobals()
    {
        include dirname(__FILE__) . '/withoutGlobals.php';
    }

    public function loadFileWithGlobals()
    {
        include dirname(__FILE__) . '/withGlobals.php';
    }

    public function loadScriptThatCallsFileWithoutGlobals()
    {
        include dirname(__FILE__) . '/loadFileWithoutGlobals.php';
    }

    public function loadScriptThatCallsFileWithGlobals()
    {
        include dirname(__FILE__) . '/loadFileWithGlobals.php';
    }
}
 

Finally, we actually try a few cases:


<?php
$o = new MyFoo();

// Case 1; expect 'Foo: bar'
$o->setGlobal();
if (isset($foo)) {
    echo 'Foo: ', $foo, "\n";
} else {
    echo "Foo not set\n";
}

// Case 2; expect 'Bar not set'
$o->loadFileWithoutGlobals();
if (isset($bar)) {
    echo 'Bar: ', $bar, "\n";
} else {
    echo "Bar not set\n";
}

// Case 3; expect 'Baz: bat'
$o->loadFileWithGlobals();
if (isset($baz)) {
    echo 'Baz: ', $baz, "\n";
} else {
    echo "Baz not set\n";
}

// Case 4; expect failure
$o->loadScriptThatCallsFileWithoutGlobals();

// Case 5; expect failure
$o->loadScriptThatCallsFileWithGlobals();
 

Now, I was wrong about being able to declare globals using $GLOBALS; the first case, where I set 'foo', works fine. Case 2 works as I expect, too; since the variable was technically defined in the same scope as the method, it's not global. The third case, which I initially said didn't work in my last post, works as well; $baz is set correctly in the global scope.

Cases 4 and 5 are where things get interesting. In Case 4, direct access to $bar2 works because it's technically in the same scope as where it's defined. However, access to it via $GLOBALS fails, as expected, because it was not defined in the global scope.

In case 5, neither access works; direct access to $baz2 does not work, nor does access via $GLOBALS; in both cases, I get a notice indicating that the index is undefined. This was the exact situation that was causing issues for me, and precisely the sort of inconsistency that makes working with globals so frustrating. In the updated code, Case 5 works exactly as it should; $baz2 is in the global scope.

Posted by Matthew Weier O'Phinney in PHP at 12:23 | Comments (4) | Trackback (1)

Trackbacks
Trackback specific URI for this entry

PHP globals for the OOP developer
Update: I evidently simplified the issue too much, and have had several people rightly comment on the bogosity of the issue. However, there are still situations where $GLOBALS does not act as expected, and I outline these in my next e
Weblog: phly, boy, phly
Tracked: May 20, 12:58

Comments
Display comments as (Linear | Threaded)

You should always use define if you want true global value in all scopes.
#1 Richard (Link) on 2007-05-20 14:38 (Reply)
loadFileWithGlobals includes withGlobals.php which sets sets *baz* in the global scope, but then you only look for *baz2*

Not a bug.
#2 Sara Golemion (Link) on 2007-05-20 15:07 (Reply)
Nice catch, Sara. I modified it, and it works correctly.

So, in the end, the issue is finding exactly where the variable is first defined and intercepting it so that it sets it via the $GLOBALS superglobal. In the end, the solution I found is the easiest one -- as I'm already in the global scope and this allows me to pull it into my method via the global keyword so it's in the same scope as the application.

The issue with relying on globals is determining *when* they are first declared so that you can push them explicitly into the global scope so they can be easily accessed elsewhere. I stand my by earlier posts that relying on globals for your applications is a bad practice, as it makes them difficult to integrate with other applications later.
#2.1 Matthew Weier O'Phinney (Link) on 2007-05-20 17:10 (Reply)
Oh certainly. Globals are a curse upon the world and must be crushed from existence. ;-)
#2.1.1 Sara Golemion (Link) on 2007-05-20 19:29 (Reply)

Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

 
 
  • Home
  • Resume
  • Blog
  • Phly PEAR Channel
  • Contact Me
  • About this site

ZCE

Zend Education Advisory Board Member

Add to Technorati Favorites

Calendar

Back August '08 Forward
Mon Tue Wed Thu Fri Sat Sun
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

Quicksearch

Links

  • PHLY - PHp LibrarY
  • Paul M. Jones
  • Mike Naberezny
  • Shahar Evron
  • Planet PHP
  • Zend Where I now work
  • Garden.org Where I once worked

Archives

August 2008
July 2008
June 2008
Recent...
Older...

Categories

XML Linux
XML Personal
XML Aikido
XML Family
XML Programming
XML Perl
XML PHP

All categories

Syndicate This Blog

XML RSS 0.91 feed
XML RSS 1.0 feed
XML RSS 2.0 feed
ATOM/XML ATOM 0.3 feed
ATOM/XML ATOM 1.0 feed
XML RSS 2.0 Comments

Show tagged entries

xml best practices
xml books
xml conferences
xml dojo
xml dpc08
xml file_fortune
xml linux
xml mvc
xml pear
xml personal
xml php
xml programming
xml ubuntu
xml webinar
xml zendcon
xml zend framework
© 2004 - present, Matthew Weier O'Phinney
matthew-web <at> weierophinney.net