Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Ещё один пример применения замыканий в PHP

Anna | 31.05.2014 | нет комментариев

На Прогре теснее было несколько статей с примерами применения замыканий в PHP. Некоторые из них были довольно абстрактными, некоторые нет. Я приведу ещё один метод использования замыканий в реальных условиях.

При добавлении нового функционала в один план на PHP без фреймворка, появилась надобность применения транзакций (применяется MySQL c InnoDB и PHP 5.4 с MYSQLi).

В плане по умолчанию autocommit установлен в true. Отключить его для каждого плана невозможно. Соответственно первой мыслью было перед выполнением SQL-запроса отключать autocommit, а позже всех действий (плюс commit либо rollback в конце), включать autocommit вновь.

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

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

public function save()
{
	$result = $this->db->update(...);
	//ошибка может быть не только из-за неверного запроса, но и в процессе валидации и пр.
	if (!$result) throw new Exception('Error while saving');
}

public function append_log()
{
	$result = $this->db->insert(...);
	if (!$result) throw new Exception('Error while append');
}

public function add()
{
	$this->db->autocommit(false);
	try {
		$this->save();
		$this->append_log();
        $this->db->commit();
	} catch (Exception $e) {
		$this->db->rollback();
	}	
	$this->db->autocommit(true);
}

Но здесь появляются две задачи:

  1. Писать такое в всяком способе не дюже хочется
  2. Что, если в каком-то из способов (save() либо append_log()) будет также исполняться несколько последовательных запросов, которые нужно объединить в транзакцию? Тогда придётся определять, отключали либо нет autocommit, и исполнять commit в зависимости от этого, так как если исполнитьcommit, родительские метаморфозы тоже будут сохранены.

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

public function transaction(callable $block)
{
	$exception = null;
	if ($need_to_off = $this->isAutocommitOn()) 
            $this->mysqli->autocommit(false);

	try {
		$block();
	} catch (Exception $e) {
		$exception = $e;
	}

	if ($need_to_off)
	{
		if ($exception == null) {
			$this->db->mysqli->commit();
		} else {
			$this->db->mysqli->rollback();
		}
        $this->mysqli->autocommit(true);
	}
	if ($exception) throw $exception;	
}

public function isAutocommitOn()
{
	if ($result = $this->db->mysqli->query("SELECT @@autocommit")) {
		$row = $result->fetch_row();
		$result->free();
	}
	return isset($row[0]) && $row[0] == 1;
}

Мы посылаем способу transaction() наш код внутри неизвестной функции. Если autocommit включен,transaction его отключает, после этого исполняет неизвестную функцию. В зависимости от итога делаетcommit либо rollback, а после этого снова включает autocommit. Если же autocommit теснее отключен, то легко выполняется неизвестная функция — об autocommit заботятся где-то в ином месте.

Пример применения:

public function save_all()
{
	$this->transaction(function(){
		$this->save();
		$this->append_log();
	});
}

P.S.: $this в замыканиях дозволено применять, начиная с PHP версии 5.4

 

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB