PHP-класс для работы с INI-файлами

Класс-помощник для работы с INI-файлами. Класс реализует шаблон проектирования "Строитель". В представленном классе идет реализация функционала для работы с INI-файлами. В качестве базового функционала представлено создание, добавление и чтение секций, добавление и удаление ключей и другой функционал.


Первым делом необходимо объявить переменные, которые отвечают за структуру файла, путь к файлу и тип сканирования:

/**
 * @var array Структура ini файла
*/
private array $structure = [];
/**
 * @var int Тип сканирования
*/
private int $scannerMode;
/**
 * @var string Путь к файлу
*/
private string $path;

В конструкторе метода укажем путь к файлу (для создания) и тип сканирования, а также вызовем метод получения структуры файла:

public function __construct(string $path, int $scannerMode = INI_SCANNER_TYPED) {
	$this->path = $path;
	file_put_contents($path, null, LOCK_EX);
	$this->scannerMode = $scannerMode;

	$this->setInitStructure();
}

Метод получения структуры файла выглядит следующим образом:

private function setInitStructure(): void {
	$this->structure = parse_ini_file($this->path, true, $this->scannerMode);
}

Метод parse_ini_file получает в качестве аргументов путь к файлу, флаг получения многомерного массива и типа сканирования. Мы ставим по умолчанию флаг многомерного массива как true, так как нам проще работать с такими массивами. Тип сканирования может быть:

При INI_SCANNER_RAW значения опций (ключ => значение) не будут обрабатываться. Также с версии PHP 5.6.1 завезли тип INI_SCANNER_TYPED. В этом режиме типы boolean, null и integer будут, по возможности, сохраняться. Строковые значения "true""on" и "yes" будут преобразованы в TRUE"false""off""no" и "none" в FALSE"null" преобразуется в NULL. Кроме этого, все числовые строки будут, по возможности, преобразованы к целым числам. Вроде как это лучше, чем остальные варианты, по этому используем его.

Вспомогательные методы

Напишем публичный метод получения всей структуры файла:

public function getStructure(): array {
	return $this->structure;
}

Чаще всего необходимо получать какое-то значение или всю секцию файла. Давайте напишем простые методы получения этих значений:

public function getSection(string $section): array {
	return $this->structure[$section];
}

public function getValue(string $section, string $key) {
	return $this->getSection($section)[$key];
}

С методами получения мы разобрались, теперь нам необходимо формировать файлы, чтобы записывать туда определенные значения. Можно сказать, что этот процесс называется динамическим конфигурированием. Для начала создадим метод добавления секции:

public function addSection(string $section): Ini {
	if (array_key_exists($section, $this->structure)) {
		throw new Exception("Секция {$section} уже существует");
	}

	$this->structure[$section] = [];

	return $this;
}

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

Теперь сделаем метод, который добавлял бы значения в определенные секцию:

public function addValues(string $section, array $values): Ini {
	$this->structure[$section] = array_merge($values, $this->structure[$section]);

	return $this;
}

Но не всегда надо добавлять значения, иногда нужно из переназначать. Напишем такой метод:

public function setValues(string $section, array $values): Ini {
  foreach ($values as $key => $value) {
  	$this->structure[$section][$key] = $value;
  }

  return $this;
}

С добавлением, назначением и чтением мы разобрались, теперь сделаем возможность удаления секции и ключей в секциях:

public function removeSection(string $section): Ini {
  unset($this->structure[$section]);

  return $this;
}

public function removeKeys(string $section, array $keys): Ini {
  foreach ($keys as $key) {
  	unset($this->structure[$section][$key]);
  }

  return $this;
}

Обратите внимание, что методы добавления и удаления реализуют шаблон проектирования "Строитель".

Осталось дело за малым - написать метод записи в файл:

public function write(): void {
  $iniContent = null;

  foreach ($this->structure as $section => $data) {
  	$iniContent .= "[{$section}]\n";

  foreach ($data as $key => $value) {
  	$iniContent .= "{$key}={$value}\n";
  }

  $iniContent .= "\n";
  }

	file_put_contents($this->path, $iniContent, LOCK_EX);
  $this->setInitStructure();
}

Надеюсь, что вам будет полезен данный помощник по работе с INI файлами в PHP. Если есть предложения по улучшению, то буду рад их выслушать.

@mepihin
21.09.2020 12:44 UTC
Первоисточник

Комментарии

@negasus
21.09.2020 07:53 UTC
+3
Главный вопрос, почему не оформили в виде composer пакета? Как предлагается этим пользоваться?
@mepihin
21.09.2020 08:21 UTC
-1
Вы можете скачать код этот и внедрить к себе в проект. Как минимум — это ознакомительная часть, может кому понадобиться для реализации своих идей. Можно было оформить в качестве пакета, но позже сделаю это.
21.09.2020 08:38 UTC
+5
Это так не работает. Никто не будет «качать и внедрять». Чтобы пользоваться, нужен composer пакет. Нужны тесты. Это прям минимум.

позже сделаю это.


почти всегда можно читать как «никогда». Сам это проходил)
21.09.2020 09:01 UTC
0
Откуда скачать-то, даже если желающий найдется? Ссылок на гитхаб не наблюдаю.

Ну и да — прежде чем писать свой велосипед, неплохо бы сделать хотя бы наколеночный обзор существующих решений (неужели в мире никому не нужно было работать с ini на php, учитывая, кхм, то, что php.ini — это INI файл?)
21.09.2020 09:17 UTC
+1

но php же не на php написан

@Insolita
21.09.2020 08:07 UTC
0

del

@mepihin
21.09.2020 08:21 UTC
0
Что имеете ввиду?
@BullDER
21.09.2020 09:49 UTC
0
Кажется проще использовать github.com/yosymfony/toml
@savostin
21.09.2020 12:46 UTC
+1
var $ini = new Ini('app.ini');
$ini->addSection('hello');
$ini->addSection('Hello'); // опа
$ini->addSection('hello]there'); // поломалось
$ini->addValues('hello', ['an=y'=>'va\nlue;']); // приветик
@mepihin
21.09.2020 13:04 UTC
-1
Вы не соблюдаете синтаксис работы с ini файлами. Написать проверку на ключи можно, а на значения нет смысла. В значение может быть и строка и цифра и символы. Этот момент был оставлен на отмашку пользователя класса.
21.09.2020 13:09 UTC
0
Если бы я хотел заморачиваться с соблюдением синтаксиса ini-файла, мне не нужен для этого класс. Это Вы не проверяете входные данные, хотя бы элементарно. Вы хотите сделать «удобно», но самую рутину «оставляете на отмашку пользователю класса».

В догонку: в ini-файле могут быть записи без секции.
23.09.2020 04:05 UTC
0
вроде секция все равно будет [DEFAULT]
23.09.2020 04:56 UTC
+3
Мне кажется, хорошие классы-хэлперы должны под капотом решать максимально возможное количество проблем, включая обработку ошибок и вопросы безопасности. Просто когда такие моменты оставлены на отмашку пользователя, это всё равно, что написать оболочку для mysqli, которая очень удобная в использовании, но под капотом заменяет в SQL-запросах знаки вопроса на значения соответстующих переменных — просто вставляя их как есть в запрос. Типа, моменты с экранированием значение переменных оставлены на отмашку пользователям класса.

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

Единственное, что мне в вашем коде нравится — это то, что вы открывающие фигурные скобки на той же строке пишете. А сама статья… я вам практически гарантирую, что впечатления от фидбэка у вас будут негативные, потому что формат статьи такой. Она практически из одного кода состоит. Его будут критиковать и тыкать вас в ошибки, причём если вы учтёте все замечания и обновите статью, критиковать вас продолжат, потому что обновление никто читать не будет — достаточно того, что в комментариях цитируется ваш старый, который вы уже поправили, код. Люди будут смотреть на эти старые цитаты и будут критиковать именно их.
23.09.2020 05:57 UTC
0
Единственное, что мне в вашем коде нравится — это то, что вы открывающие фигурные скобки на той же строке пишете


Что, если правильно помню, противоречит PSR-2, к сожалению.
23.09.2020 06:48 UTC
0
И спецификации PSR-12, которая пришла на смену PSR-2, это тоже противоречит, кстати. И повлиять на спецификацию вы никак не сможете, даже если вы в одиночку написали кода на PHP больше, чем было в тех исходниках, которые они брали на анализ для составления стандартов.
@Pullay
23.09.2020 08:25 UTC
0

Лучше место ini использовать yaml.

@mepihin
23.09.2020 08:25 UTC
0
Почему и чем?
@SbWereWolf
24.09.2020 22:23 UTC
0

Оформить пакет композера не сложно. Я делал это год назад и как то справился. Кажется надо просто в репозитории иметь composer.json где будут прописаны пути к классам.
Как пример https://packagist.org/packages/sbwerewolf/language-specific. И потом в жизни самому так удобней использовать. Ни какого копипаста. Подключил зависимостьти оно само работает. И само обновиться когда что то в исходниках поправишь.
По коду мелкое замечание. Не красиво использовать '\n' красиво это PHP_EOL.
И верное замечание про проверки. Если хочешь что бы таоим кодом пользовались, то сделвй его удобным.
Можно сделать фабрику что бы пользоаатель библиотеки мог подсунуть свою реализацию, но в своей реализации сделать проверки. Кому будет мало сможет твой класс пепеписать и подставить.
В порядке друкжеской критики.
Спасибо что поделился кодом.