Do You PHP はてブロ

Do You PHPはてなからはてブロに移動しました

Avoiding the usage of global variables

グローバル変数は頭痛の種になりやすいですが、グローバル変数の使用を避ける1つの方法が解説されています。


The usage of global variables can easily become a big headache. Even being easy to access the data, global variables make us have to deal with a terrible problem: the possibility of a value's overwrite in some part of the code. It's even more possible for that to occur when we're working with more people in the same project.

ここ掲載されてるサンプルでは「Registry」クラスを用意して、通常だとグローバルスコープになる変数をRegistryクラスで管理させています。また、SingletonパターンとRegistryパターンを使っています。Registryパターンは、以下を参照ということで。

掲載されてるサンプルですが、E_STRICT出まくりです。。。以下、「static」を付けただけのコードですが、とりあえず。。。確かに、「グローバルスコープな変数」は使ってないですね。

<?php
/**
 * @see http://fernandobassani.com/php/design-patterns/avoiding-the-usage-of-global-variables/
 */
error_reporting(E_ALL|E_STRICT);
class Registry extends ArrayObject {
    private static $registry;
    const _NS_PREFIX = 'Registry::NS::%s';
    const DEFAULT_NAMESPACE = 'RegistryDefault';
    public static function getInstance() {
        if (is_null(self::$registry)) {
            $c = __CLASS__;
            return new $c;
        }
        return self::$registry;
    }
    public static function init() {
        if (is_null(self::$registry)) {
            self::$registry = self::getInstance();
            self::registerNamespace(self::DEFAULT_NAMESPACE);
            return true;
        }
        throw new Exception("The registry is already initialized");
    }
    public static function registerNamespace($namespace) {
        $fnamespace = self::getFormatedNamespace($namespace);
        if (!self::$registry->offsetExists($fnamespace)) {
            self::$registry->offsetSet($fnamespace, new ArrayObject);
            return $namespace;
        }
        throw new Exception("The namespace '{$namespace}' is already registered");
    }
    public static function add($key, $value, $namespace = self::DEFAULT_NAMESPACE) {
        $fnamespace = self::getFormatedNamespace($namespace);
        if (!self::$registry->offsetExists($fnamespace)) {
            throw new Exception("The namespace '{$namespace}' does not exist");
        }
        self::$registry->offsetGet(self::getFormatedNamespace($namespace))->offsetSet($key,$value);
    }
    public static function get($key, $namespace = self::DEFAULT_NAMESPACE) {
        $fnamespace = self::getFormatedNamespace($namespace);
        if (!self::$registry->offsetExists($fnamespace)) {
            throw new Exception("The namespace '{$namespace}' does not exist");
        }
        return self::$registry->offsetGet(self::getFormatedNamespace($namespace))->offsetGet($key);
    }
    public static function exists($key) {
        foreach (self::$registry as $namespace => $ArrayObject) {
            if (self::$registry->offsetGet($namespace)->offsetExists($key)) {
                return true;
            }
        }
        return false;
    }
    public static function drop($key, $namespace = self::DEFAULT_NAMESPACE) {
        return self::$registry->offsetGet(self::getFormatedNamespace($namespace))->offsetUnset($key);
    }
    public function dropAll() {
        self::$registry = null;
        self::init();
    }
    private static function getFormatedNamespace($namespace) {
        return sprintf(self::_NS_PREFIX, $namespace);
    }
}
Registry::init();
Registry::add('key1','value1');
Registry::add('key2','value2');
echo Registry::get('key2');
echo "\n";
$n = Registry::registerNamespace('myNamespace');
Registry::add('key1Ns1','value1Ns1',$n);
echo Registry::get('key1Ns1',$n);
echo "\n";
var_dump(Registry::exists('key1'));

Registryクラスのキモは、やっぱり

<?phpprivate static $registry;

です。というか、これが「すべて」じゃないですかね。後のメソッドとかはオマケみたいなもんだと思います。
ちょっと話は変わりますが、2007-07-30 - ます’s Diary - どうでもいい事100選id:masugataさんが

個人的には変数の名前空間対応もやって欲しいと思う。

と仰っていますが、個人的にも欲しいです:-) ただし、今のところ対応してなさそうです。たとえば、

<?php
/**
 * test01.php
 */
namespace test01;
$str = 'foo';
<?php
/**
 * test02.php
 */
namespace test02;
$str = 'bar';
<?php
/**
 * test.php
 */
require_once 'test01.php';
require_once 'test02.php';

echo test01::$str;
echo test02::$str;

とかできると、かなりステキなんですけどねぇ。。。もし、実現したら、グローバル変数を使っても、今よりは「頭痛の種」が減るんじゃないかと思いますが。