глобальные настройки приложения 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 не работают, потому что я могу установить любое значение из любого места на любом этапе процесса подачи заявки)
Вопросы:
Рекомендуется ли хранить глобальные настройки таким образом? Каковы недостатки этого метода? Может быть, есть гораздо лучший способ сделать это?
Спасибо за ваши ответы
3 answers
Ваш класс 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);
Как упоминал марселог в другом ответе, вы можете использовать класс начальной загрузки для обработки ввода конфигурации, а также других объектов в ваш класс 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();
Это очень простые примеры, и ничто не мешает вам писать магические геттеры и сеттеры, заставлять загрузчик вызывать любой метод, начинающийся с инициализации, и т. Д.
Как указал Вриккен в комментарии к вашему вопросу, вы вводите глобальное состояние в свое приложение. Цитируя Мартина Фаулера о глобальном состоянии (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 или Фабричные узоры. Я просто использовал самую простую возможную вещь. Это (надеюсь) легче понять таким образом. Вы можете проверить http://silex-project.org / для микроструктуры, использующей более сложный, но аналогичный подход.
Основное преимущество этого подхода заключается в том, что каждый компонент получит то, что ему нужно, с самого начала Внедрение зависимостей . Это упростит модульное тестирование вашего кода, потому что его намного проще имитировать зависимости и добейтесь тестовой изоляции.
Еще одно преимущество заключается в том, что вы разделяете график построения и график совместной работы, поэтому вы не смешиваете эти обязанности (как если бы вы использовали Одноэлементный или иным образом помещали ключевое слово new
в классы, которые должны быть экспертами по информации.
Можете ли вы опубликовать еще немного кода? просто чтобы показать, как вы получаете доступ к этим настройкам.
В любом случае, вы могли бы создать класс Boostrap. Этот класс начальной загрузки сделает все необходимое для создания рабочей среды для вашего приложения (таким образом, переместив код начальной загрузки из приложения и настроек в этот класс).
Он также может создавать экземпляр объекта настроек, который должен быть одноэлементным.
В объекте Настройки вы можете использовать магические методы (__вызов, __получение), чтобы получите доступ к различным настройкам, таким как Настройки::getSettings()->getconfigdirectory(). Этот волшебный метод удалит слово "получить" из вызова и попытается предоставить ресурс с заданным именем (в данном случае параметр с именем "configdirectory").
Это похоже на то, что делает Zend Framework в своих классах Zend_Application, Zend_Bootstrap и Zend_Config, вы можете проверить их, чтобы получить некоторые идеи.
В качестве примечания, я не понимаю (концептуально говоря), почему приложение следует расширить настройки. Приложение должно иметь некоторые настройки, но это сильно отличается от их расширения.