tags:

views:

92

answers:

5

Is it possible to define private variables in a PHP script so these variables are only visible in this single PHP script and nowhere else? I want to have an include file which does something without polluting the global namespace. It must work with PHP 5.2 so PHP namespaces are not an option. And no OOP is used here so I'm not searching for private class members. I'm searching for "somewhat-global" variables which are global in the current script but nowhere else.

In C I could do it with the static keyword but is there something similar in PHP?

Here is a short example of a "common.php" script:

$dir = dirname(__FILE__);
set_include_path($dir . PATH_SEPARATOR . get_include_path());
// Do more stuff with the $dir variable

When I include this file in some script then the $dir variable is visible in all other scripts as well and I don't want that. So how can I prevent this?

+5  A: 

The only way you're going to accomplish anything close to what you want is to wrap everything in that included file in a function, and call it. If the file needs to execute itself you could still do

<?php
run_myfile()
function run_myfile() {
...
}
?>

There is no generic way to make a variable scoped to only a file outside of namespaces, classes, or functions.

Erik
+4  A: 

There are a few things you could do to keep $dir out of subsequent files

Example 1

set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path());

This is the most obvious.

Example 2

$dir = dirname(__FILE__);
set_include_path($dir . PATH_SEPARATOR . get_include_path());
// work with $dir
unset($dir);

Just unset the variable after defining it and using it. Note this will unset any variable named $dir used prior to including this script.

Example 3

define('DIR_THIS', dirname(__FILE__));

set_include_path(DIR_THIS . PATH_SEPARATOR . get_include_path());

It is less likely I suppose to redefine a global constant like this.

Example 4

function my_set_include_path {
  $dir = dirname(__FILE__);
  set_include_path($dir . PATH_SEPARATOR . get_include_path());
  // Do more stuff with the $dir variable
  $my_other_var = 'is trapped within this function';
}

my_set_include_path();

You can define as many variables within that function and not affect the global namespace.

Conclusion

The first method is the easiest way to solve this problem, however because you want to use $dir again, it may not be ideal. The last example will at least keep that $dir (and any others defined in that function) out of the global namespace.

alex
your second example would still clobber any other globals with the name of $dir that the poster is using. Then again, there's a reason why Globals Are Evil.
Erik
@Erik Ah yes, I just realised that. But then again, this seems like a script that would be included during a bootstrap sequence, so it's probably less likely to clobber anything using `$dir` previously. I would suggest using the first example.
alex
+1 for example 1
zaf
Thanks. For now I stick with your first solution even if I have to use dirname(__FILE__) multiple times. I left a node in my head to simply use __DIR__ as soon as I can use PHP 5.3.
kayahr
+2  A: 

Well, I'm probably getting flailed for this, but you if you are totally desperate you could use a Registry for that. I've whipped up a small one that does without classes (since I assume from And no OOP is used here so I'm not searching for private class members. means you don't want to do it with OOP at all)

function &registry_get_instance()
{
    static $data = array();
    return $data;
}

The static $data variable inside is persisted inside the function scope, so you can call the function wherever you like and always get the same contents. The crucial point is returning by reference, e.g.

$registry = &registry_get_instance();     // get $data array by reference
$registry['foo'] = 'bar';                 // set something to $data
unset($registry);                         // delete global reference to $data
print_r(&registry_get_instance());        // show $data 

Obviously you'd still have $registry as a variable in the global scope when calling this method from the global scope. So, you could add some more functions to make the Registry more convenient to use, e.g. for setting data to the Registry:

function registry_set($key, $value) 
{
    $registry = &registry_get_instance();
    $registry[$key] = $value;
}

and for getting it out again:

function registry_get($key)
{
    $registry = &registry_get_instance();
    if(array_key_exists($key, $registry)) {
        return $registry[$key];
    } else {
        trigger_error(sprintf(
            'Undefined Index: %s', htmlentities($key)
        ), E_USER_NOTICE);
    }
}

and for checking if a key exists:

function registry_isset($key)
{
    $registry = &registry_get_instance();
    return array_key_exists($key, $registry);
}

which you could then use like:

registry_set('foo', 'bar');         // setting something to the registry
var_dump( registry_isset('foo') );  // check foo is in the registry now
echo registry_get('foo');           // prints 'bar'
echo registry_get('punt');          // raises Notice

You could populate the Registry from an include file with an additional method like this:

function registry_load_file($file)
{
    if(!is_readable(realpath($file))) {
        return trigger_error(sprintf(
            'File is not readable: %s', htmlentities($file)
        ), E_USER_WARNING);
    }
    $config = include $file;
    if(!is_array($config)) {
        return trigger_error(sprintf(
            'Expected file %s to return an array', htmlentities($file))
        , E_USER_WARNING);
    }
    $registry = &registry_get_instance();
    $registry += $config;
}

with the include file having to return an array:

// config.php
return array(
    'setting1' => 'something'
);

and then you can do

registry_load_from_file('config.php');  // add the contents of config to registry
print_r(registry_get_instance());       // show content of registry

Of course, this is now six functions in the global scope just for not having a global variable. Don't know if it's worth it, especially since I consider static in functions and all that reference stuff doubtful practice.

Take it as a proof of concept :)

Gordon
Somebody pass me my whipping stick.
symcbean
@symcbean hehe, yeah, I remember I left a comment below one of your answers complaining how using static was bad style. I still do, but from that I guess you're the one most entitled to whip me ;)
Gordon
A: 

Why not just put everything in a static class? Then you only have a single "variable" that could possibly conflict with the global namespace.

class MyClass {
    public static $myvar = 1;
    public static $myvar2 = "xyz";

    public static function myfunction() {
        self::$myvar++;
        self::$myvar2 = "abc";
    }
}
// References to class items, if needed
MyClass::myfunction();
MyClass::$myvar += 3;
Brent Baisley
His first example only had one variable that could conflict too, `$dir`.
alex
A: 

If the problem you are trying to is just:

$dir = dirname(__FILE__);
set_include_path($dir . PATH_SEPARATOR . get_include_path());
// Do more stuff with the $dir variable

Then the solution would be to change the include path relative to '.' in your ini settings. E.g. change:

include_path=includes:/usr/local/php

to

include_path=./includes:/usr/local/php

Note that a script does not come into scope except where you explicitly include/require it (both the _once check applies globally) however I would recommend strongly against calling include/require from within a function - its much more transparent having the includes/requires at the top of the script.

I think that the problem you are trying to solve is based on a false premise and you should look for another way of fixing it. If you want the code in an include file to behave differently depending on what includes it, then really you should seperate it out into 2 seperate files - or maybe even 3 - 2 for the different behaviours and 1 for the common.

C.

symcbean