Avoiding the usage of global variables
グローバル変数は頭痛の種になりやすいですが、グローバル変数の使用を避ける1つの方法が解説されています。
ここ掲載されてるサンプルでは「Registry」クラスを用意して、通常だとグローバルスコープになる変数をRegistryクラスで管理させています。また、SingletonパターンとRegistryパターンを使っています。Registryパターンは、以下を参照ということで。
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.
掲載されてるサンプルですが、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クラスのキモは、やっぱり
<?php : private 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;
とかできると、かなりステキなんですけどねぇ。。。もし、実現したら、グローバル変数を使っても、今よりは「頭痛の種」が減るんじゃないかと思いますが。