У вас включен AdBlock или иной блокировщик рекламы.

Пожалуйста, отключите его, доход от рекламы помогает развитию сайта и появлению новых статей.

Спасибо за понимание.

В другой раз
DevGang блог о програмировании
Авторизоваться

Менеджер контекста в PHP 

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

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

Одним из лучших шаблонов в Python является Context Manager. В Python определенные объекты и функции могут быть заключены в блок with, используемый для предоставления определенного контекста к коду, выполняющемуся внутри него. Например, открытие файлов становится невероятно простым:

with open('output.txt', 'w') as f:
    f.write('Hello world!')

Прелесть этого шаблона заключается в автоматической очистке ресурсов. Вам не нужно управлять указателями файлов вручную. Вам не нужно инициализировать и отключать сеанс базы данных. Менеджер контекста обрабатывает вещи для вас автоматически.

Менеджер контекста базы данных может управлять созданием и закрытием соединения с самой базой данных (или с используемым пулом соединений). Или его можно использовать для переноса транзакции и автоматической фиксации результатов в конце блока. В любом случае, вам не нужно управлять контекстом самостоятельно.

PHP не имеет такого шаблона изначально. Я думаю, что добавление конструкции with было бы огромной прибавкой для языка, но мои навыки кодирования на более низком уровне немного устарели, чтобы написать быстрое подтверждение концепции. Вместо этого я могу использовать генераторы и циклы для имитации аналогичных функций с существующей системой.

Приведенный выше пример Python использовал функцию open() для демонстрации управления контекстом применительно к файлам. Мы можем сделать нечто подобное в PHP с помощью следующей вспомогательной функции:

function open($file, $mode = 'r')
{
   $f = fopen($file, $mode);
   yield $f;   fclose($f);
}

Поскольку эта функция является генератором, мы можем использовать ее изначально внутри цикла foreach.

foreach(open('output.txt', 'w') as $file) {
   fwrite($file, 'Hello, world!');
}

Поскольку генератор возвращает только один yield , мы получаем только один элемент для итерации в нашем цикле foreach. Цикл также автоматически вернется к генератору после итерации, что дает нам возможность очистить наш контекст и область видимости.

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

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

try {
   $dbh = new PDO($dsn, $user, $pass, $options);
} catch (Exception $e) {
   die("Unable to connect: " . $e->getMessage());
}

try {  
   $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
   $dbh->beginTransaction();
   $dbh->exec("insert into users (id, first, last) values (5, 'Eric', 'Mann')");
   $dbh->exec('insert into authors (id) values (5)');
   $dbh->commit();
} catch (Exception $e) {
   $dbh->rollBack();
   echo "Failed: " . $e->getMessage();
}

Та же самая операция, использующая шаблон менеджера контекста, была бы намного проще:

function transaction()
{
   $dbh = new PDO($dsn, $user, $pass, $options);
   $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
   $dbh->beginTransaction();   yield $dbh;   try {
       $dbh->commit();
   } catch (Exception $e) {
       $dbh->rollBack();
   }
}

// Теперь используем транзакцию
foreach(transaction() as $dbh) {
   $dbh->exec("insert into users (id, first, last) values (5, 'Eric', 'Mann')");
   $dbh->exec('insert into authors (id) values (5)');
}

Это сложнее, чем в первом примере. Суть в том, что код установки и разрыва соединения с базой данных и транзакции происходит вне вашей бизнес-логики. Здесь функция transaction() может находиться в отдельном файле или пространстве имен от остальной части вашего кода, что обеспечивает чистоту логики выполнения и избавляет от необходимости беспокоиться о настройке / завершении работы.

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

Это шаблон, который будет иметь значение в вашем коде? Это, конечно, отличается от того, как многие из нас пишут PHP сегодня. Как еще можно использовать эту модель? Было бы более разумно, если бы мы расширили язык с помощью нашей собственной реализации with?

#PHP