Зачем нужны throw Exception?

флоппик

promotor fidei
Команда форума
Партнер клуба
Это, на самом деле, третий случай. Очень удобный для троллей и истеричек. Многопоточность на деле бывает очень редко, но выпячивается так, как будто это самый массовый кейс. Так что на самом деле всё наоборот - это с многопоточностью кейс гнилой.
Открою тебе страшную тайну — веб-сервер — МНОГОПОТОЧНЫЙ. Он выполняет скрипты одновременно. Ну, кроме девелоперской машины, ага.
 

Вурдалак

Продвинутый новичок
повторяю ещё раз, читай внимательно:
Повторять ты можешь сколько угодно, только ты не слышишь никого. Здесь вылезают недостатки PHP, о которых я тоже неоднократно говорил и упомянул в этом треде. Но обойти тоже можно на уровне фреймворка типа
PHP:
<?php

set_error_handler(function($errno, $errstr, $errfile, $errline)
{
	if (preg_match('/^unlink\((?<file>.+?)\): No such file or directory$/', $errstr, $match))
		throw new MissingFileException('No such file or directory: '.$match['file']);

	throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
});

class MissingFileException extends ErrorException { }

try {
	unlink('/etc/hosts');
} catch (MissingFileException $e) {
	echo $e->getMessage(), PHP_EOL;
}

try {
	unlink('XXX');
} catch (MissingFileException $e) {
	echo $e->getMessage(), PHP_EOL;
}
xxx@xxx:/xxx/www$ php exception.php
PHP Fatal error: Uncaught exception 'ErrorException' with message 'unlink(/etc/hosts): Permission denied' in /xxx/www/exception.php:14
Stack trace:
#0 [internal function]: {closure}(2, 'unlink(/etc/hos...', '/xxx/www...', 14, Array)
#1 /xxx/www/exception.php(14): unlink('/etc/hosts')
#2 {main}
thrown in /xxx/www/exception.php on line 14
Ты живёшь в своём уютненьком мире пэхапэ и очень агрессивно реагируешь на попытки изменить твоё представление о мире.
 

Фанат

oncle terrible
Команда форума
Повторять ты можешь сколько угодно, только ты не слышишь никого. Здесь вылезают недостатки PHP, о которых я тоже неоднократно говорил и упомянул в этом треде. Но обойти тоже можно на уровне фреймворка типа
PHP:
<?php

set_error_handler(function($errno, $errstr, $errfile, $errline)
{
	if (preg_match('/^unlink\((?<file>.+?)\): No such file or directory$/', $errstr, $match))
		throw new MissingFileException('No such file or directory: '.$match['file']);

	throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
});

class MissingFileException extends ErrorException { }

try {
	unlink('/etc/hosts');
} catch (MissingFileException $e) {
	echo $e->getMessage(), PHP_EOL;
}

try {
	unlink('XXX');
} catch (MissingFileException $e) {
	echo $e->getMessage(), PHP_EOL;
}
Ты живёшь в своём уютненьком мире пэхапэ и очень агрессивно реагируешь на попытки изменить твоё представление о мире.
Вооот! Наконец-то! :)
То, чего я от тебя и хотел!
А теперь скажи - ты всегда будешь писать такой код, когда тебе нужно будет удалить файл, которого может и не быть?
Или, всё-таки, только в тех случаях, когда он действительно нужен? ;-)

Кстати, очень большой респект тебе за то, что ты не только злопыхал, как некоторые, но и написал код.
 

Silentland

Новичок
Все ваши ошибки - ошибки модели. Обычно каждую проверку в модели выносят в отдельный метод. По-этому эта не читаемая херня, которую вы предлагаете добавить в язык, там абсолютно не нужна. Другой вопрос, что можно вместо исключений использовать обычный return с "специализированным ответом"
Т.е. в идеале нужно насоздавать методов ifFileBig(), ifOutOfSpace(), ifNumberExceed()? Судя по большому количеству споров и мнений, исключения — штука слаборазвитая, но к if-else-return-конструкциям не скатывался бы, раз уж развитие пошло в другом направлении... И вполне вероятно нечто типа goto $e->getLine() появится в ООП в скором будущем, конечно не в таком виде. Оцените
PHP:
try {
    ...
    throw new Exception('Исключение', 1);
    ...
} catch(Exception $e) {
    ...
    continue; //или repeat, чтобы повторить блок try
}
Насколько упростилась бы жизнь! И обработку любых ошибок можно было бы переложить на плечи throw ... catch
 

Вурдалак

Продвинутый новичок
А теперь скажи - ты всегда будешь писать такой код, когда тебе нужно будет удалить файл, которого может и не быть?
Нет, конечно, я же сказал, что это решение на уровне фреймворка.

Silentland, для тебя выделили специальную тему, фантазируй там, пожалуйста.
 

Silentland

Новичок
Silentland, для тебя выделили специальную тему, фантазируй там, пожалуйста.
Ага, вот придет на ваше место программист среднего уровня, ему скажут вот тут у нас куча старого кода, нужно его как-то поддерживать, пока пишется новая система. Посмотрит он на кучу самописных функций (мегакрутых и удобных, конечно) и пойдет искать другую компанию... Это к тому, что нужно учитывать направление развития. Если все идет к обработке любых ошибок с помощью throw ... catch, то незазорно будет и забор из них построить (всего-то на пару строчек больше), если нет, то нет. Вы-то наверное больше меня это все изучали, можете сказать к чему все идет.
 

Фанат

oncle terrible
Команда форума
Нет, конечно, я же сказал, что это решение на уровне фреймворка.
Это не решение на уровне фреймворка.
Это не может быть решение на уровне фреймворка.
Я таки невнимательно прочитал код. Меня интересовало только количество кода, а не его содержание.

На уровне фреймворка нельзя пропускать ошибки No such file or directory, разумеется. Это потому что будет ваще адъ и содомия, я думаю, автора такого фреймворка распнут на ближащем столбе :)
Такая проверка может быть только локальной.
Поэтому её надо делать не в хендлере, разумеется, а в кетче.

Но это всё не принципиально. Я подводил к тому, что затраты на обработку многопоточности настолько велики, что никто, будучи в здравом уме и твердой памяти, не будет её ставить на каждый чих.
Именно поэтому случай, когда "состояние может измениться после проверки и перед удалением" - отдельный, третий.

При этом с той точки зрения, с которой я писал свой пост (а там очевидная дихтоимя - штатная vs. исключительная ситуация), этот случай можно отнести к первому - ситуация здесь явно исключительная. Я же вообще не про файлы писал, а про исключительность ситуаций. То есть, в данном случае, в условиях race condition, исключения будут на месте. Я и не спорю с этим. Я просто о другом совсем говорил, я иллюстрировал различие ситуаций.
 

Фанат

oncle terrible
Команда форума
мммм...
Ну, я опять невнимательно прочитал. Только за анлинк, конечно, не распнут.
Но это всё равно кривизна какая-то. Есть случаи, когда таким грязным хаком не отбояришься.
Да то же самое чтение, а не удаление - не будешь же на уровне фреймворка давить ошибку No such file or directory при попытке прочитать файл?
 

Вурдалак

Продвинутый новичок
Это не решение на уровне фреймворка.
Это не может быть решение на уровне фреймворка.
О, начинается.

На уровне фреймворка нельзя пропускать ошибки No such file or directory, разумеется.
Ошибка может быть на пользовательском уровне, а сам обработчик будет находится во фреймворке.

Да то же самое чтение, а не удаление - не будешь же на уровне фреймворка давить ошибку No such file or directory при попытке прочитать файл?
Вообще, вот такой вброс в зал: можно ведь пойти ещё дальше и сделать аналог роутера в error_handler'е в том смысле, что это будет маппинг ошибок на классы-исключения, которые все будут extends ErrorException. Сохраняется BC и появляется возможность писать няшный хороший код.
weNeedToGoDeeper.jpg
 

Фанат

oncle terrible
Команда форума
Здесь я потерял нить. Не понимаю, о чем ты.

Вот кейс:
Есть динамически формируемый файл. Многопоточно.
Мы его хотим прочитать.
Между проверкой и чтением файл может быть удалён.
Поэтому мы не используем проверку на существование, а заключаем в трай-кетч, которому пофиг на ошибку No such file or directory - он просто попытается прочесть снова.

Рядом есть файл, который формируется не динамически. Системе не пофиг на ошибку No such file or directory, и пытаться читать его снова бессмысленно - никто его не создаст. Обычный случай чтения файла, который мы даже не заворачиваем в проверки и в трай-кетч.
Таких файлов у нас много, и это штатное поведение любой системы и нежелательно как-то менять код работы с этими файлами.

Как твой фреймворк будет обрабатывать эти два случая?
 

Вурдалак

Продвинутый новичок
Рядом есть файл, который формируется не динамически. Системе не пофиг на ошибку No such file or directory, и пытаться читать его снова бессмысленно - никто его не создаст. Обычный случай чтения файла, который мы даже не заворачиваем в проверки и в трай-кетч.
Я сейчас тебя не понимаю. Я говорю уже о случае, когда фреймворк выбрасывает на PHP-ошибки ErrorException. В таком случае ты не мог не заворачивать чтение файла в try .. catch.
 

Фанат

oncle terrible
Команда форума
Я сейчас тебя не понимаю. Я говорю уже о случае, когда фреймворк выбрасывает на PHP-ошибки ErrorException. В таком случае ты не мог не заворачивать чтение файла в try .. catch.
Не могу не заворачивать.
Потому что фреймворк не может реагировать на все ошибки одинаково.
Два файла, про которые я написал, требуют димаетрально противоположного подхода к одной и той же ошибке No such file or directory.
Первому должно быть на неё начхать.
Второй должен бить во все колокола, свистеть вор все свистки, дудеть во все вувузелы.

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

Вурдалак

Продвинутый новичок
Фанат, я не понимаю суть твоего вопроса. Всё ровно так же и останется, как ты и описал. Просто вместо менее конкретного ErrorException будет выбрасываться MissingFileException extends ErrorException в том случае, если не найден файл.

Потому что фреймворк не может реагировать на все ошибки одинаково.
В каком смысле? Фреймворку неизвестно в каком контексте вызывается файл. Задача ставить try .. catch или нет лежит на программисте.
 

Beavis

Banned
Пустой кетч - та же собака, только кода больше.
И точно так же, как и собака, пропустит ошибку с правами доступа.
А твой код что сделает в случае ошибки с правами доступа?

Вот мой код:
PHP:
if (file_exists($file)) {
    unlink($file);
}
 

Silentland

Новичок
Где принято размещать блок обработки ошибки (catch) ближе к месту появления ошибки или как можно дальше, в пределах класса/функции или сразу же за ними, непосредственно перед передачей ошибки другому скрипту (делегируется клиенту, например) или в другом месте. Какими принципами руководствоваться если есть альтернативы?
 

Фанат

oncle terrible
Команда форума
только ты не слышишь никого.
мда, пожалуй, ты прав.
сейчас ещё раз внимательно посмотрел на код.
Я действительно слишком увлекся своей идеей и невнимательно смотрел твои примеры.
Теперь понял.

С твоим кодом все три случая отработают корректно:
1. Файл, который вообще не при делах - его отсутствие породит исключение, которое будет отловлено где-то на уровне глобального еррор хендлера.
2. Файл, которого может не быть, и ошибка что его нет - породит исключение, которое будет отловлено сразу же здесь в кэтче.
3. Файл, которого может не быть, и ошибка прав доступа - породит исключение, которое будет отловлено где-то на уровне глобального еррор хендлера.

Спасибо за разъяснения и извини за тупость.

Теперь я хочу вернуться к своей идее.
В общем, все это работает корректно, но остаётся вопрос - стоит ли лепить эти исключения на каждый тип ошибки заранее?
А их ведь много будет. Блажайший пример - попытка создать каталог, который уже есть. Ошибка другая - новое исключение.
Вероятность того, что между проверкой и созданием вылезет ошибка - стремится к нулю.
Стоит ли захламлять код такими, срабатывающими раз в сто лет исключениями, или лучше просто поставить таки проверку?

Насколько я помню по прошлой дискуссии, ты считаешь - стоит. Что механизм исключений - гораздо шире просто обработки типовых ошибок. что ими можно обрабатывать и такие вот ситуации, которые и не ошибки - что исключение вполне может заменить file_exists. Правильно?
 

флоппик

promotor fidei
Команда форума
Партнер клуба
В общем, все это работает корректно, но остаётся вопрос - стоит ли лепить эти исключения на каждый тип ошибки заранее?
А их ведь много будет. Блажайший пример - попытка создать каталог, который уже есть. Ошибка другая - новое исключение.
Исключения наследуются, как и все классы в ООП. Ничто не мешает в общем случае поймать тебе FileOperationException (условно говоря внутри фреймворка, выше пользовательского кода) и оставив возможность конкретному программисту поймать FileAccessDeniedException, которое extends FileOperationException уже в его, пользовательском, коде. Исключения всплывают, и исключения наследуются.
 

Вурдалак

Продвинутый новичок
В общем, все это работает корректно, но остаётся вопрос - стоит ли лепить эти исключения на каждый тип ошибки заранее?
А их ведь много будет. Блажайший пример - попытка создать каталог, который уже есть. Ошибка другая - новое исключение.
Ну, это уже лежит задача на разработчиках фреймворка. Чем подробнее будет сделана иерархия исключений, тем лучше. Вообще всё уже придумано до нас. Вот, например, IOException, что ты видел в Symfony, по своей сути позаимствован откуда-то отсюда:
http://docs.oracle.com/javase/1.4.2/docs/api/java/io/IOException.html

Там же есть, кстати, и аналог моего MissingFileException, который extends IOException:
http://docs.oracle.com/javase/1.4.2/docs/api/java/io/FileNotFoundException.html

Это же очень удобно. Чем дальше мы от точки выброса exception'а, тем просто более общий класс исключений нужно будет брать. И никакого нарушения инкапсуляции, как некоторые тут говорят, не будет. Мы будет знать только то, что нам нужно. Вот, кстати, ещё в тему: в Java есть ключевое слово throws на уровне языка, позволяющее контролировать какие исключения может выбросить метод. Некоторые IDE типа phpStorm поддерживают соответствующий phpDoc @throws в PHP.
 

craz

Нестандартное звание
Я ПРАВ! все это только инструменты ребят! Все фреймворки, экзепшены. Все сделано, чтобы было удобно НАМ. А вы опять развели...
Нравиться самоутверждаться?)
 
Сверху