Глобальные настройки приложения php

Я прочитал почти все вопросы, которые я нашел в 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 не работает, потому что я могу установить любое значение из любого места на любом этапе процесса приложения)

Вопросов:

Хорошая практика – хранить глобальные настройки, подобные этому. Какие недостатки этого метода? Может быть, есть намного лучший способ сделать это?

Спасибо за ответ

Класс Application не должен расширять Settings как нет никакой связи между этими двумя классами. Вместо этого вы должны использовать инъекцию зависимостей, чтобы включить настройки в класс 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 в другом ответе, вы можете использовать класс bootstrap для обработки вставки config, а также других объектов в свой класс Application .

Основной пример класса начальной загрузки:

 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(); 

Это очень простые примеры, и нет ничего, что мешает вам писать магические геттеры и сеттеры, имея вызов начальной загрузки любого метода, который начинается с init и т. Д.

Как указал Wrikken в комментарии к вашему вопросу, вы вводите Global State в свое приложение. Цитирование Мартина Фаулера по глобальному государству (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 или Factory. Я просто использовал самое простое . Это (надеюсь) проще понять. Вы можете проверить http://silex-project.org/ для микроструктуры, используя более сложный, но схожий подход.

Основное преимущество этого подхода заключается в том, что каждый компонент получит то, что ему нужно, с самого начала через Dependecy Injection . Это упростит модульный тест вашего кода, потому что его намного проще издеваться над зависимостями и достичь тестовой изоляции.

Еще одно преимущество заключается в том, что вы сохраняете график построения и график совместной работы отдельно, поэтому вы не смешиваете эту ответственность (например, с помощью Singleton или иным образом добавляете new ключевое слово в классы, которые должны быть информационными экспертами).

Можете ли вы опубликовать еще какой-нибудь код? просто чтобы показать, как вы получаете доступ к этим настройкам.

в любом случае, вы можете создать класс Boostrap. Этот класс bootstrap сделает все, что необходимо для создания рабочей среды для вашего приложения (таким образом, вытесняя код начальной загрузки из приложения и настроек в этот класс).

он также может создавать экземпляр объекта Settings, который должен быть одноточечным.

в объекте «Настройки» вы можете использовать магические методы (__call, __get) для доступа к различным настройкам, например Settings :: getSettings () -> getConfigDirectory (). Этот магический метод лишит слово «получить» от вызова и попытается дать ресурс с заданным именем (в данном случае параметр с именем «ConfigDirectory»).

Это похоже на то, что Zend Framework делает в своих классах Zend_Application, Zend_Bootstrap и Zend_Config, вы можете проверить их, чтобы получить некоторые идеи.

как примечание, я не вижу (концептуально говоря), почему приложение должно расширять настройки. Приложение должно иметь некоторые настройки, но это сильно отличается от их расширения.