php应用程序全局设置

时间:2011-07-27 17:13:26

标签: php settings global-variables

我已经阅读了StackOverflow上关于此主题的几乎所有问题,但找不到直接答案。

这是我的代码:

申请类

<?php
    class Application extends Settings {
        public function __construct($env, $cacheDir, $configFile) {
            self::$_env = $env;
            self::$_cacheDir = $cacheDir;
            self::$_config = $this->loadConfig($configFile) // reads configs from xml file into Config object
        }

        // other methods
    }
?>

设置类:

<?php
class Settings {
    protected static $_env = null;
    protected static $_cacheDir = null;
    protected static $_config = null;

    public static function getEnv() {
        return self::$_env;
    }

    public static function getCacheDir() {
        return self::$_cacheDir;
    }

    public static function getConfig() {
        return self::$_config;
    }
}
?>

我从代码中的任何位置访问设置,如下所示:

<?php
var_dump(Settings::getEnv());
?>

我想从许多不同的地方访问设置。所有值只能设置一次并且不能被覆盖(因此使用__set方法的注册表不起作用,因为我可以在应用程序进程的任何阶段从任何位置设置任何值)

问题:

存储像这样的全局设置是一种好习惯。这种方法的缺点是什么? 也许有更好的方法来做到这一点?

感谢您的回答

3 个答案:

答案 0 :(得分:4)

就像Wrikken在您的问题的评论中指出的那样,您正在将Global State引入您的应用程序。引用Martin Fowler关于全球状态(PoEAA,第482f页):

  

请记住,任何全球数据在被证明是无辜的之前总是有罪的。

简而言之就是:避免它。我留给你研究这个主题,因为这个问题的范围超出了它的详细讨论范围。

现在,换一个更好的选择

假设您将所有流量路由到index.php。然后,您可以简单地引导/构建在该文件中完成请求所需的所有组件。例如,像这样:

spl_autoload_register(
    function($className) {
        static $classMap = array(
            'request' => '/path/from/here/to/Request.php',
             … more mapping
        );
        require __DIR__ . $classMap[strtolower($className)];
    }
);

$config  = parse_ini_file(__DIR__ . '/path/from/here/to/config.ini');
foreach($config['env'] as $key => $val) {
    ini_set($key, $val);
}

$router = new Router;
$router->registerActionForRoute(
    '/product/list', 
    function($request, $response) use ($config) {
        return new ProductListAction(
            $request, $response
            new ProductMapper(
                new ProductGateway(
                    new MySqli($config['db']['host'], …),
                    new Cache($config['cache'], …)
                ),
                new ProductBuilder;
            )
        );
    }
);
$router->registerActionForRoute(…);
$router->execute(new Request($_GET, $_POST, $_SERVER), new Response);

当然,您更希望从单独的文件中包含自动加载器(因为您希望使用类似https://github.com/theseer/Autoload的内容自动生成它)。当然,您可以使用Builder或工厂模式替换路由器中的闭包。我刚刚使用了simplest thing possible。 (希望)这种方式更容易理解。您可以使用更复杂但相似的方法检查http://silex-project.org/微框架。

这种方法的主要好处是每个组件从开始到Dependecy Injection都能得到它所需要的。这样可以更容易地对代码进行单元测试,因为它更容易模拟依赖关系并实现测试隔离。

另一个好处是,您可以将构建图和协作者图分开,因此您不要混淆那些responsibility(就像使用Singleton或将new关键字放入类中一样应该是信息专家。

答案 1 :(得分:1)

你可以发布更多代码吗?只是为了说明你是如何访问这些设置的。

无论如何,你可以创建一个Boostrap类。这个引导类将为您的应用程序提供必要的工作环境(从而将引导代码从应用程序和设置移出到此类)。

它还可以实例化一个Settings对象,该对象应该是一个单例。

在Settings对象中,您可以使用魔术方法(__call,__ get)来访问不同的设置,例如Settings :: getSettings() - &gt; getConfigDirectory()。这个神奇的方法将从调用中删除“get”字,并尝试给出具有给定名称的资源(在这种情况下,名为“ConfigDirectory”的设置)。

这与Zend Framework在Zend_Application,Zend_Bootstrap和Zend_Config类中的作用类似,您可能需要查看它们以获得一些想法。

作为附注,我没有看到(从概念上讲)应用程序应该扩展设置的原因。应用程序应该有一些设置,但这与扩展它们完全不同。

答案 2 :(得分:1)

由于两个类之间没有关系,因此Application类不应扩展Settings。相反,您应该使用dependency injection将设置包含到Application类中。下面有一个例子,我建议阅读依赖注入。

class Settings {
    // public to simplify example, you can add setters and getters
    public $_env = null;
    public $_cacheDir = null;
    public $_config = null;
}

class Application {
    protected $config;

    public function setConfig($config) {
        $this->config = $config;
    }

}



$app = new Application();

$config = new Settings();

$config->_env = 'dev';
$config->_cacheDir = '/my/dir';
$config->_config = array(/* Config here */);

$app->setConfig($config);

正如marcelog在另一个答案中所提到的,您可以使用引导类来处理配置以及其他对象注入Application类。

bootstrap类的基本示例:

class Bootstrap {

    protected $application;

    public function __construct(Application $app) {
        $this->application = $app;
    }

    // connivence method
    public function init() {
        $this->initSettings();
    }

    public function initSettings() {
        $settings = new Settings();
        $settings->_env = 'dev';
        $settings->_cacheDir = '/my/dir';

        $config = array(); // load config from file here
        $settings->_config = config;
        $this->application->setSettings($settings);
    }

    // other init methods
}

$app = new Application();

$bootstrap = new Bootstrap($app);

$bootstrap->init();

这些都是非常基本的例子,没有什么能阻止你编写魔法getter和setter,让bootstrap调用任何以init开头的方法等等......