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

Вопросы:

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

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

Author: Tomas, 2011-07-27

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

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

 1
Author: MitMaro, 2011-07-27 18:38:37

Как указал Вриккен в комментарии к вашему вопросу, вы вводите глобальное состояние в свое приложение. Цитируя Мартина Фаулера о глобальном состоянии (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 в классы, которые должны быть экспертами по информации.

 4
Author: Gordon, 2017-05-23 10:34:20

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

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

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

В объекте Настройки вы можете использовать магические методы (__вызов, __получение), чтобы получите доступ к различным настройкам, таким как Настройки::getSettings()->getconfigdirectory(). Этот волшебный метод удалит слово "получить" из вызова и попытается предоставить ресурс с заданным именем (в данном случае параметр с именем "configdirectory").

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

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

 1
Author: marcelog, 2011-07-27 18:02:44