#php #class #config #configuration-files
#php #класс #конфигурация #конфигурационные файлы
Вопрос:
Я много искал и читал о том, как наилучшим способом (с точки зрения кода) получить переменные конфигурации приложения в среде PHP. После этого я подвел итог двум более широко используемым способам управления файлами конфигурации и переменными.
Но я немного смущен этим, один из методов 1 использует статический класс. Другой, метод 2, использует создаваемый класс.
Первый из них хуже поддается модульному тестированию, чем второй. И это похоже на глобальную переменную. Не так ли?
Во-вторых, для использования созданного объекта требуется глобальная переменная.
Я попытаюсь объясниться сам.
Факты: — Настройки приложения хранятся в INI-файле. — В этом INI-файле есть разделы для поддержания переменных конфигурации. — У меня есть только один INI-файл. — Класс выполните некоторую проверку файла конфигурации. — Приведенные ниже примеры кода не являются полными, это всего лишь пример для иллюстрации моего вопроса.
Метод 1: использование статического класса
В этом методе используется статический класс конфигурации, он использует статический, потому что во всем приложении будет использоваться только один объект конфигурации.
Пример кода:
class Config
{
static private $data;
static public function load($configFile) {
self::$data = parse_ini_file($configFile, true, INI_SCANNER_RAW)
}
static public get($key) {
// code stuff to deal with sections and keys, but basically
return self::$data[$key];
}
}
В моем приложении я создаю статический объект один раз, используя этот код:
Config::load('/where/is/my/ini/file.ini');
В этом случае каждый раз, когда я хочу получить значение, я использую:
$host = Config::get('database.host');
function example()
{
echo Config::get('another.value');
}
Метод 2: использование нестабильного объекта
В этом сценарии я использую объект класса конфигурации.
Пример кода:
class Config {
private $data = array();
public function __construct($configFile) {
$this->data = parse_ini_file($configFile, true, INI_SCANNER_RAW)
}
public function get($key) {
// code stuff to deal with sections and keys, but basically
return $this->data[$key];
}
public function __get($key) {
return $this->get($key);
}
}
Чтобы использовать его, сначала нам нужно создать экземпляр объекта, а затем получить значение:
$settings = new Config('/where/is/my/ini/file.ini');
$host = $settings->get('database.host');
echo $settings->database->host;
Но когда мне нужно это значение внутри функции, мне нужно использовать глобальную переменную, которая, как мне кажется, совсем не подходит:
global $settings;
$settings = new Config('/where/is/my/ini/file.ini');
function example()
{
global $settings;
echo $settings->get('another.value');
}
Чего мне не хватает?
Заранее спасибо, что прочитали и ответили на мой вопрос.
Комментарии:
1. Если вы создаете новое приложение, почему бы не избавиться от любого ненужного глобального кода и не полагаться на надлежащую инъекцию depdendency (даже если это означает ручную передачу этого объекта конфигурации в функцию)?
2. Используйте объект конфигурации и используйте внедрение зависимостей, чтобы сделать его доступным. Если вы работаете в какой-либо среде, у вас, вероятно, уже есть решения DI. Вы всегда можете создать свой собственный, например, глобальный контейнер, pub / sub или внедрение отражения. Если вы не используете фреймворк, используйте компонент из другого фреймворка, а не пишите свой собственный, например, компонент конфигурации Symfony .
3. Спасибо @Rangad за ваш комментарий. Я начинающий разработчик ООП, поэтому большая часть моего кода не является OO. Я пытаюсь преобразовать некоторые его части, чтобы использовать OO-подход.
4. Спасибо @bishop за ваш комментарий. Если я не правильно понимаю, вы рекомендуете метод 2 , но как вы справляетесь с внутренним вызовом функции? Использование компонента конфигурации Symfony, похоже, является сложным решением для моего маленького приложения:-P
5. @PacoOrozco: «Как вы справляетесь?» Посредством внедрения зависимостей. Ваша конфигурация — это зависимость, и вам нужно внедрить ее в разные места. Существует множество шаблонов для внедрения зависимостей. Одним из таких шаблонов является «сохранить его в глобальных файлах», но это проблема по всем упомянутым вами причинам. Доступны другие шаблоны, но их становится все труднее реализовать. Хороший фреймворк избавит вас от всей этой работы. Даже если ваше приложение кажется «маленьким», создание на основе фреймворка упростит эту — и все другие — проблемы, с которыми вы столкнетесь по мере роста вашего приложения.
Ответ №1:
Просто вы также можете использовать php
файл, например, для сохранения ваших конфигураций, config.php
а затем вы можете использовать require
из любого места, чтобы получить его:
// config.php
<?php
return array(
'database' => 'mysql',
'pagination' => array(
'per_page' => 10
)
);
Затем используйте:
$config = require "path/to/config.php";
print_r($config);
Вы также можете использовать это с функцией, например:
function someFunc()
{
$config = require "path/to/config.php";
// Now use it
}
Вы можете создать класс для получения конфигураций, используя такой метод, как, например:
class Config {
static function get()
{
$config = require "path/to/config.php";
return $config;
}
}
Итак, вы можете использовать:
$config = Config::get();
Это просто еще одна простая идея, вы можете расширить ее, чтобы использовать ее как:
$perPage = Config::get('pagination.per_page');
Просто нужно добавить еще немного кода, чтобы он работал таким образом, попробуйте.
Обновить:
Кстати, я создал пакет с именем IConfig для своего собственного MVC Framework
еще в 2013
, он также доступен на Packagist. Вы можете использовать его или проверить его исходный код, возможно, вы получите лучшую идею и сможете создать лучшую. Но, вероятно, в Интернете есть и намного лучшие.
Комментарии:
1. Спасибо @werewolf-the-alpha, но я хочу использовать INI-файл. Я думаю, что это более удобно для пользователя, его легче генерировать из PHP.
Ответ №2:
Я думаю, что проблема, которую вы пытаетесь решить, на самом деле не специфична для PHP, и любой из двух описанных вами методов может быть жизнеспособным способом обработки глобальных конфигураций.
Сказав это, я согласен с Рангадом, решение заключается в использовании внедрения зависимостей. Проще всего, этот шаблон просто означает передачу зависимостей классу / объекту / функции в качестве аргументов. Например,
class Thing () {
private $host = '';
function __contructor($host) {
$this->host = $host;
}
function getHost()
{
echo $this->host;
}
}
$thing = new Thing( Config::get('database.host') );
$thing->getHost();
$thing2 = new Thing (Config::get('someOtherDatabase.host') );
$thing2.getHost();
Это инкапсулирует ваши классы. Теперь их можно использовать в тестах или даже других приложениях, если могут быть предоставлены необходимые зависимости.
Самое приятное в этом то, что вы можете использовать его с любым из предложенных вами параметров конфигурации и, возможно, с другими. Например, если вы ищете что-то простое, есть Pimple, контейнер для внедрения зависимостей PHP, написанный создателем фреймворка Symphoy PHP, который, на мой взгляд, отлично подходит для небольших проектов PHPhttp://pimple.sensiolabs.org /.