Бочонок
http://frontender.info
Приятного времени суток. Извините за задержку - потратил время на эксперименты, прочтения дополнительных материалов и осмысление результатов.
Примеры:
С моим классом:
На чистых исключениях:
На исключениях с пользовательскими исключениями:
Да. В принципе конструкция
try{}catch(){}
По объему не многим хуже конструкции
if(){}
Зато не надо сбрасывать ошибку, добавлять класс, возвращать false или делать проверку флага что бы поднятся вверх по стеку на каждом уровне.
Меня интересуют следующие вопросы:
1. Когда надо пользоватся наследованием класса Exception?
Это нужно для создания общих классов ошибок, насколько я понимаю.
Как широко надо это применять?
Насколько это применимо по вашему опыту?
И где держать определение наследующих классов?
В отдельном скрипте и рекваерить?
2. Если переопределяется ф-я обработки ошибок, сгенерированных интерпретатором, то стоит ли писать конструкции генерирующие пользовательское исключение?
(см. пример 3. Конструктор. Комментарий.)
Наверное стоит, что бы не зависеть от того переопределен перехватчик ошибок генерируемых интерпретатором или нет.
Что вы думаете?
3. Писать в каждом скрипте
Довольно некрасиво, как мне кажется.
Имеет ли смысл что бы этого избежать сократить свой класс до:
Что бы инициировать все это простым:
Что вы посоветуете? Как лучше сделать? Мне кажется что если это типовая операция, то сократить - стоит.
2atv:
Спасибо что подтолкнули меня к написанию примеров.
Раньше уже использовал исключения в "чистом" варианте... Но вот сел писать недавно и почему то стукнуло в голову, что try ужасно усложняют структуру кода и надо попробовать что то придумать. Придумал...
2zerkms:
Спасибо за статью и потраченное на спор со мной время. Это подтолкнуло меня перечитать еще раз http://ua.php.net/exceptions и хорошенько подумать
2whirlwind:
P.S.
Еще раз спасибо всем за то что потратили время на пояснения и комментарии.
Буду очень благодарен вам, если вы поможете мне окончательно разобратся в теме.
Примеры:
С моим классом:
PHP:
<?php
header("content-type: text/html; charset=utf-8");
class db_renamer{
//! Переменная, содержащая указатель для работы с базой данных
private $connector;
//! Переменная, обеспечивающая работу с ошибками
public $err;
//! Конструктор. Инициирует класс для работы с SMS.
/*!
@param user - имя пользователя
@param pass - пароль
@param host - хост
@param dbname - имя базы данных
@return false в случае ошибки и true в противном
*/
public function __construct($user,$pass,$host,$dbname){
$this->err = new error_class();
$this->err->clear_err();
if(
empty($user)||
empty($pass)||
empty($host)||
empty($dbname)
){
$this->err->set_err("Не все данные заданы");
return false;
}
if(!($this->connector=mysql_connect($host,$user,$pass))){
$this->err->set_err(mysql_error());
return false;
}
if(!mysql_select_db($dbname,$this->connector)){
$this->err->set_err(mysql_error());
return false;
}
return true;
}
//! Метод переименует имя файла и изменяет его вбазе данных
/*!
@param name - имя файла
@param new_name - новое имя файла
*/
public function do_it($name,$new_name){
$this->err->clear_err();
if(!file_exists($name)){
$this->err->set_err("Файл ".$name." не найден");
return false;
}
if(file_exists($new_name)){
$this->err->set_err("Файл ".$new_name." уже существует. Файл не был переименован.");
return false;
}
$query="
SELECT
COUNT(*) AS 'in_base'
FROM
file_collector
WHERE
name='".$name."'
";
if(!($result=mysql_query($query,$this->connector))){
$this->err->set_err(mysql_error());
return false;
}
if(!($row=mysql_fetch_assoc($result))){
$this->err->set_err(mysql_error());
return false;
}
if($row['in_base']==0){
$this->err->set_err("Запись о файле ".$name." не найдена в базе данных.");
return false;
}
if(!rename($name,$new_name)){
$this->err->set_err("Не удалось переименовать файл ".$name." в ".$new_name);
return false;
}
$query="
UPDATE
file_collector
SET
name='".$new_name."'
WHERE
name='".$name."'
LIMIT 1
";
if(!mysql_query($query,$this->connector)){
$this->err->set_err(mysql_error());
return false;
}
return true;
}
}
require_once("error_class.php");
$err = new error_class("err_overwork");
$renamer = new db_renamer('user','pass','localhost','renamer_db');
if($renamer->is_err()!=false){
$err->fire_err($renamer->is_err());
}
$renamer->do_it('some_file.txt','some_other_file.txt');
if($renamer->is_err()!=false){
//специфичекий обработчик ошибки
}
function err_overwork($error){
//общий обработчик ошибки
echo "<pre>";
var_dump($error);
die("</pre>");
}
?>
PHP:
<?php
header("content-type: text/html; charset=utf-8");
class db_renamer{
//! Переменная, содержащая указатель для работы с базой данных
private $connector;
//! Конструктор. Инициирует класс для работы с SMS.
/*!
@param user - имя пользователя
@param pass - пароль
@param host - хост
@param dbname - имя базы данных
@return false в случае ошибки и true в противном
*/
public function __construct($user,$pass,$host,$dbname){
if(
empty($user)||
empty($pass)||
empty($host)||
empty($dbname)
){
throw new Exception("Не все данные заданы",E_ERROR);
}
if(!($this->connector=mysql_connect($host,$user,$pass))){
throw new Exception(mysql_error(),E_ERROR);
}
if(!mysql_select_db($dbname,$this->connector)){
throw new Exception(mysql_error(),E_ERROR);
}
}
//! Метод переименует имя файла и изменяет его вбазе данных
/*!
@param name - имя файла
@param new_name - новое имя файла
*/
public function do_it($name,$new_name){
if(!file_exists($name)){
throw new Exception("Файл ".$name." не найден",E_ERROR);
}
if(file_exists($new_name)){
throw new Exception("Файл ".$new_name." уже существует. Файл не был переименован.",E_ERROR);
}
$query="
SELECT
COUNT(*) AS 'in_base'
FROM
file_collector
WHERE
name='".$name."'
";
if(!($result=mysql_query($query,$this->connector))){
throw new Exception(mysql_error(),E_ERROR);
}
if(!($row=mysql_fetch_assoc($result))){
throw new Exception(mysql_error(),E_ERROR);
}
if($row['in_base']==0){
throw new Exception("Запись о файле ".$name." не найдена в базе данных.",E_ERROR);
}
if(!rename($name,$new_name)){
throw new Exception("Не удалось переименовать файл ".$name." в ".$new_name,E_ERROR);
}
$query="
UPDATE
file_collector
SET
name='".$new_name."'
WHERE
name='".$name."'
LIMIT 1
";
if(!mysql_query($query,$this->connector)){
throw new Exception(mysql_error(),E_ERROR);
}
}
}
set_exception_handler("err_overwork");
set_error_handler("redirect_err",E_ALL);
$renamer = new db_renamer('user','pass','localhost','renamer_db');
try{
$renamer->do_it('some_file.txt','some_other_file.txt');
}catch(Exception $error){
//Специфический обработчик ошибки
}
restore_exception_handler();
restore_error_handler();
function err_overwork($error){
//общий обработчик ошибки
echo "<pre>";
var_dump($error);
die("</pre>");
}
function redirect_err($errno, $errstr, $errfile, $errline, $errcontext){
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
return true;
}
?>
PHP:
<?php
header("content-type: text/html; charset=utf-8");
class FSError extends Exception{};
class DBError extends Exception{};
class ArgError extends Exception{};
class db_renamer{
//! Переменная, содержащая указатель для работы с базой данных
private $connector;
//! Конструктор. Инициирует класс для работы с SMS.
/*!
@param user - имя пользователя
@param pass - пароль
@param host - хост
@param dbname - имя базы данных
@return false в случае ошибки и true в противном
*/
public function __construct($user,$pass,$host,$dbname){
if(
empty($user)||
empty($pass)||
empty($host)||
empty($dbname)
){
throw new ArgError("Не все данные заданы",E_ERROR);
}
//Насколько следующие строки c обработкой ошибок имеют смысл,
если в случае ошибки с mysql_connect в любом случае интерпретатором будет сгенерирован Warning,
который перехватывается через redirect_err?
if(!($this->connector=mysql_connect($host,$user,$pass))){
throw new DBError(mysql_error(),E_ERROR);
}
if(!mysql_select_db($dbname,$this->connector)){
throw new DBError(mysql_error(),E_ERROR);
}
}
//! Метод переименует имя файла и изменяет его вбазе данных
/*!
@param name - имя файла
@param new_name - новое имя файла
*/
public function do_it($name,$new_name){
if(!file_exists($name)){
throw new FSError("Файл ".$name." не найден",E_ERROR);
}
if(file_exists($new_name)){
throw new FSError("Файл ".$new_name." уже существует. Файл не был переименован.",E_ERROR);
}
$query="
SELECT
COUNT(*) AS 'in_base'
FROM
file_collector
WHERE
name='".$name."'
";
if(!($result=mysql_query($query,$this->connector))){
throw new DBError(mysql_error(),E_ERROR);
}
if(!($row=mysql_fetch_assoc($result))){
throw new DBError(mysql_error(),E_ERROR);
}
if($row['in_base']==0){
throw new FSError("Запись о файле ".$name." не найдена в базе данных.",E_ERROR);
}
if(!rename($name,$new_name)){
throw new FSError("Не удалось переименовать файл ".$name." в ".$new_name,E_ERROR);
}
$query="
UPDATE
file_collector
SET
name='".$new_name."'
WHERE
name='".$name."'
LIMIT 1
";
if(!mysql_query($query,$this->connector)){
throw new DBError(mysql_error(),E_ERROR);
}
}
}
set_exception_handler("err_overwork");
set_error_handler("redirect_err",E_ALL);
try{
$renamer = new db_renamer('user','pass','localhost','renamer_db');
}catch(DBError $error){
echo 'Специфический обработчик DBError';
throw $error;
}catch(ArgError $error){
echo 'Специфический обработчик ArgError';
//Специфический обработчик ошибки
}
try{
$renamer->do_it('some_file.txt','some_other_file.txt');
}catch(FSError $error){
echo 'Специфический обработчик FSError';
//Специфический обработчик ошибки
}catch(DBError $error){
echo 'Специфический обработчик DBError';
//Специфический обработчик ошибки
}
restore_exception_handler();
restore_error_handler();
function err_overwork($error){
//общий обработчик ошибки
echo 'Общий обработчик';
echo "<pre>";
var_dump($error);
die("</pre>");
}
function redirect_err($errno, $errstr, $errfile, $errline, $errcontext){
echo 'Ошибка сгенерирована интерпретатором и перенаправлена<br/>';
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
return true;
}
?>
try{}catch(){}
По объему не многим хуже конструкции
if(){}
Зато не надо сбрасывать ошибку, добавлять класс, возвращать false или делать проверку флага что бы поднятся вверх по стеку на каждом уровне.
Меня интересуют следующие вопросы:
1. Когда надо пользоватся наследованием класса Exception?
Это нужно для создания общих классов ошибок, насколько я понимаю.
Как широко надо это применять?
Насколько это применимо по вашему опыту?
И где держать определение наследующих классов?
В отдельном скрипте и рекваерить?
2. Если переопределяется ф-я обработки ошибок, сгенерированных интерпретатором, то стоит ли писать конструкции генерирующие пользовательское исключение?
(см. пример 3. Конструктор. Комментарий.)
Наверное стоит, что бы не зависеть от того переопределен перехватчик ошибок генерируемых интерпретатором или нет.
Что вы думаете?
3. Писать в каждом скрипте
PHP:
set_exception_handler("err_overwork");
set_error_handler("redirect_err",E_ALL);
restore_exception_handler();
restore_error_handler();
function redirect_err($errno, $errstr, $errfile, $errline, $errcontext){
echo 'Ошибка сгенерирована интерпретатором и перенаправлена<br/>';
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
return true;
}
Имеет ли смысл что бы этого избежать сократить свой класс до:
PHP:
//!Класс работы с ошибками
/*!
@ingroup error
*/
class error_class{
//! Флаг замены стандартной функции обработки ошибок
private $err_handler=false;
//! Конструктор
/*!
@param callback - CallBack функция, которая будет обрабатывать исключения.
@param err_redirect - Если задана callback функция и err_redirect установлен в true,
то пользовательской функции будет передана так же обработка ошибок, генерируемых интерпретатором. По умолчанию true.
@param level - определяет, какие генерируемые интерпретатором ошибки будут перехватыватся.
Возможные значения: [url]http://ua.php.net/manual/ru/errorfunc.constants.php[/url]
*/
public function __construct($callback=null,$err_redirect=true,$level=E_ALL){
if($callback==null){
throw new Exception("Не определена callback функция.",E_ERROR);
}
set_exception_handler($callback);
if($err_redirect){
$this->err_handler = true;
set_error_handler(array("error_class","redirect_err"),$level);
}
}
//! Метод, который перехватывает ошибку, сгенерированую интерпретатором и
вызывает исключение, которое будет обработано пользовательской функцией.
/*!
@param errno - уровень сгенерированой ошибки
@param errstr - текстовое описание ошибки
@param errfile - имя файла, содержащего код, вызвавший ошибку
@param errline - номер строки, которая содержит код, вызвавший ошибку
@param errcontext - массив, указывающий на активную символьную таблицу
в месте где возникла ошибка. Другими словами массив, содержащий все переменные,
которы существовали в тот момент, как была сгенерирована ошибка.
Пользовательская ф-я обработки ошибок не должна изменять контекст.
*/
public function redirect_err($errno, $errstr, $errfile, $errline, $errcontext){
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
return true;
}
//! Деструктор функции, который возвращает обработчики исключений и ошибок,
в предидущее их состояние, если они были изменены при запуске конструктора.
public function __destruct(){
restore_exception_handler();
if($this->err_handler){
restore_error_handler();
}
}
}
PHP:
new error_class('err_overwork');
function err_overwork($error){
}
2atv:
Спасибо что подтолкнули меня к написанию примеров.
Раньше уже использовал исключения в "чистом" варианте... Но вот сел писать недавно и почему то стукнуло в голову, что try ужасно усложняют структуру кода и надо попробовать что то придумать. Придумал...
2zerkms:
Спасибо за статью и потраченное на спор со мной время. Это подтолкнуло меня перечитать еще раз http://ua.php.net/exceptions и хорошенько подумать
2whirlwind:
Это не всегда возможно, или я не прав? Если не прав - поясните пожалуйста.Ошибки не надо обрабатывать. Ошибки надо не допускать.
Честно говоря - просто не понял что вы сказали. Приведите пожалуйста пример. Или хотя бы rtfmните меня.Все делается гораздо изящнее: без программирования ради программирования. Пишется декоратор делегирующий экземпляру класса БД в случае если кеш не содержит данных.
P.S.
Еще раз спасибо всем за то что потратили время на пояснения и комментарии.
Буду очень благодарен вам, если вы поможете мне окончательно разобратся в теме.