- Php exception in constructor
- Php exception in constructor
- Создание классов исключений
- PHP RFC: Constructor behaviour of internal classes
- Constructors returning null rather than throwing an exception
- Factory methods
- Constructors give warning, but are then in an unusable state
- Throwing Exceptions in PHP
- The Exception class
- Throwing custom PHP exceptions
- Writing to the error log
Php exception in constructor
A User defined Exception class can be defined by extending the built-in Exception class. The members and properties below, show what is accessible within the child class that derives from the built-in Exception class.
Example #1 The Built in Exception class
class Exception implements Throwable
protected $message = ‘Unknown exception’ ; // exception message
private $string ; // __toString cache
protected $code = 0 ; // user defined exception code
protected $file ; // source filename of exception
protected $line ; // source line of exception
private $trace ; // backtrace
private $previous ; // previous exception if nested exception
?php
public function __construct ( $message = » , $code = 0 , Throwable $previous = null );
final private function __clone (); // Inhibits cloning of exceptions.
final public function getMessage (); // message of exception
final public function getCode (); // code of exception
final public function getFile (); // source filename
final public function getLine (); // source line
final public function getTrace (); // an array of the backtrace()
final public function getPrevious (); // previous exception
final public function getTraceAsString (); // formatted string of trace
// Overrideable
public function __toString (); // formatted string for display
>
?>
If a class extends the built-in Exception class and re-defines the constructor, it is highly recommended that it also call parent::__construct() to ensure all available data has been properly assigned. The __toString() method can be overridden to provide a custom output when the object is presented as a string.
Note:
Exceptions cannot be cloned. Attempting to clone an Exception will result in a fatal E_ERROR error.
Example #2 Extending the Exception class
/**
* Define a custom exception class
*/
class MyException extends Exception
// Redefine the exception so message isn’t optional
public function __construct ( $message , $code = 0 , Throwable $previous = null ) // some code
?php
// make sure everything is assigned properly
parent :: __construct ( $message , $code , $previous );
>
// custom string representation of object
public function __toString () return __CLASS__ . «: [ < $this ->code > ]: < $this ->message > \n» ;
>
public function customFunction () echo «A custom function for this type of exception\n» ;
>
>
/**
* Create a class to test the exception
*/
class TestException
public $var ;
const THROW_NONE = 0 ;
const THROW_CUSTOM = 1 ;
const THROW_DEFAULT = 2 ;
function __construct ( $avalue = self :: THROW_NONE )
switch ( $avalue ) case self :: THROW_CUSTOM :
// throw custom exception
throw new MyException ( ‘1 is an invalid parameter’ , 5 );
break;
case self :: THROW_DEFAULT :
// throw default one.
throw new Exception ( ‘2 is not allowed as a parameter’ , 6 );
break;
default:
// No exception, object will be created.
$this -> var = $avalue ;
break;
>
>
>
// Example 1
try $o = new TestException ( TestException :: THROW_CUSTOM );
> catch ( MyException $e ) < // Will be caught
echo «Caught my exception\n» , $e ;
$e -> customFunction ();
> catch ( Exception $e ) < // Skipped
echo «Caught Default Exception\n» , $e ;
>
// Continue execution
var_dump ( $o ); // Null
echo «\n\n» ;
// Example 2
try $o = new TestException ( TestException :: THROW_DEFAULT );
> catch ( MyException $e ) < // Doesn't match this type
echo «Caught my exception\n» , $e ;
$e -> customFunction ();
> catch ( Exception $e ) < // Will be caught
echo «Caught Default Exception\n» , $e ;
>
// Continue execution
var_dump ( $o ); // Null
echo «\n\n» ;
// Example 3
try $o = new TestException ( TestException :: THROW_CUSTOM );
> catch ( Exception $e ) < // Will be caught
echo «Default Exception caught\n» , $e ;
>
// Continue execution
var_dump ( $o ); // Null
echo «\n\n» ;
// Example 4
try $o = new TestException ();
> catch ( Exception $e ) < // Skipped, no exception
echo «Default Exception caught\n» , $e ;
>
// Continue execution
var_dump ( $o ); // TestException
echo «\n\n» ;
?>
Php exception in constructor
PHP по умолчанию представляет ситуации, в которых автоматически генерируются ошибки и исключения, например, при делении на ноль. Но иногда возникает необходимость самим вручную сгенерировать исключение. Например:
class Person < private $name, $age; function __construct($name, $age) < $this->name = $name; $this->age = $age; > function printInfo() < echo "Name: $this->name
Age: $this->age"; > > $tom = new Person("Tom", -105); $tom->printInfo();
Здесь класс Person через конструктор получает имя и возраст пользователя. Однако в реальности мы можем передать любые значения, например, отрицательный возраст. Понятно, что это недействительное значение. Чтобы избежать подобной ситуации одним из решений является генерация исключения при получении невалидного значения.
Для генерации исключения применяется оператор throw , после которого указывается объект исключения.
Например, изменим выше определенный код класса Person:
class Person < private $name, $age; function __construct($name, $age) < if($age < 0) < throw new Exception("Недействительный возраст"); >$this->name = $name; $this->age = $age; > function printInfo() < echo "Name: $this->name
Age: $this->age"; > > $tom = new Person("Tom", -105); $tom->printInfo();
Теперь если возраст отрицательный, то будет генерироваться исключение типа Exception .
throw new Exception("Недействительный возраст");
В качестве параметра конструктор класса Exception получает сообщение об ошибке, которое будет выводиться при возникновении исключения.
В итоге при выполнении строки
throw new Exception("Недействительный возраст");
И в итоге в браузере мы увидем информацию об ошибке:
Fatal error: Uncaught Exception: Недействительный возраст in D:\localhost\hello.php:17 Stack trace: #0 D:\localhost\hello.php(26): Person->__construct('Tom', -105) #1 thrown in D:\localhost\hello.php on line 17
Поскольку вызов конструктора класса Person создает ситуацию, в которой потенциально может возникнуть исключение, то лучше вызов конструктора поместить в конструкцию try..catch :
class Person < private $name, $age; function __construct($name, $age) < $this->name = $name; if($age < 0) < throw new Exception("Недействительный возраст"); >$this->age = $age; > function printInfo() < echo "Name: $this->name
Age: $this->age"; > > try < $tom = new Person("Tom", -105); $tom->printInfo(); > catch(Exception $ex) < echo $ex ->getMessage(); >
Создание классов исключений
При генерации исключений мы можем полагаться на встроенные классы исключений, как в примере с классом Exception выше. Однако может возникнуть необходимость передать несколько больше информации при генерации или как-то по своему настроить поведение класса исключения. В этом случае мы можем создать свой класс исключения, заточенный под конкретные нужды:
class PersonInvalidAgeException extends Exception < function __construct($age) < $this ->message = "Недействительный возраст: $age. Возраст должен быть в диапазоне от 0 до 120"; > > class Person < private $name, $age; function __construct($name, $age) < $this->name = $name; if($age < 0) < throw new PersonInvalidAgeException($age); >$this->age = $age; > function printInfo() < echo "Name: $this->name
Age: $this->age"; > > try < $tom = new Person("Tom", -105); $tom->printInfo(); > catch(PersonInvalidAgeException $ex) < echo $ex ->getMessage(); >
Для примера здесь определен простенькй класс исключения, который унаследован от класса Exception. (В реальности чтобы создать класс исключения, достаточно реализовать интерфейс Throwable). В этом классе переопределяется конструктор, который в качестве параметра принимает недействительный возраст.
Выводимое значение в классе Exception хранится в свойстве message , соответственно в классе-наследнике PersonInvalidAgeException мы можем использовать это свойство для установки своего сообщения. В итоге при генерации исключения браузер выведет соответствующее сообщение об ошибке:
Недействительный возраст: -105. Возраст должен быть в диапазоне от 0 до 120
PHP RFC: Constructor behaviour of internal classes
Several internal classes in extensions that ship with PHP exhibit some highly surprising behaviour. This RFC has two aims:
i) to make the behaviour of these classes behave more consistently with the behaviour that most people would expect them to have.
ii) setting the behaviour that should be followed by internal classes for their constructors.
If other internal classes that are not listed in this RFC are found to have behaviour that is not consistent with the behaviour listed in this RFC they should be treated as bugs and fixed at the next appropriate release.
Constructors returning null rather than throwing an exception
when their constructor is called with parameters that cannot be used to create a valid instance of the class, the constructor returns null. This is inconsistent with classes which are declared in userland which are not capable of returning null value, but instead have to indicate an error by throwing an exception.
Having a different behaviour for internal classes is inconsistent and highly surprising when users encounter it:
$mf = new MessageFormatter(‘en_US’, »); if ($mf === null)
This RFC proposes setting a standard that internal classes should either return a valid instance of the class or throw an exception. Also for the classes listed to be changed in PHP 7 so that they follow this behaviour.
Factory methods
The classes IntlDateFormatter, MessageFormatter, NumberFormatter, ResourceBundle and IntlRuleBasedBreakIterator all have a static factory method that can create an instance of the class, as well as the constructor. The factory methods do not currently throw an exception when invalid arguments are passed to it. It is the position of this RFC that it is correct for those factory methods to return either null or a valid usable instance of the class.
PHP is a multi-paradigm programming language which supports writing code in either procedural or OO style. Having the factory methods behave like this is perfectly consistent with userland code.
class NumberFormatter < static function create($locale , $style , $pattern = null) < try < return new NumberFormatter($locale, $style, $pattern); >catch(NumberFormatterException $nfe) < trigger_error($nfe->getMessage(), E_USER_ERROR); return null; > > >
There is also a function `finfo_open` which is also a procedural way of instantiating an finfo object. This function would not be modified.
Constructors give warning, but are then in an unusable state
The constructors of several classes check the parameters that they are given and just give a warning when they are not acceptable. This leaves the object instantiated but in an unusable state.
getParameters()); //Fatal error: ReflectionFunctionAbstract::getParameters(): Internal error: Failed to retrieve the reflection object in..
This RFC proposes that this behaviour should be changed so that the constructor should throw an exception if the class cannot be created in a usable state i.e. convert the current warning to an appropriate exception.
The list of classes that show this behaviour is:
Throwing Exceptions in PHP
Throwing a generic PHP exception is almost as simple as it sounds. All it takes is to instantiate an exception object—with the first parameter of the Exception constructor being the error message—and then, «throw» it.
throw new Exception('Exception message')
The most important thing to take note of is the message. Defined in the constructor, and accessed via the getMessage() method, the message is the human-readable error that can often be related to the end user.
The Exception class
To more effectively utilize exceptions within your application, it is important to understand how to create and throw your own PHP exception. But before we get into throwing custom PHP exceptions, let’s first take a look at what an exception is under the hood, and how to define your own, starting with the global Exception class that all PHP exceptions stem from:
As we can see from the definition above, every built-in exception includes a few configurable properties: an exception message, an exception code, the source filename of the exception, and the line number of the exception in the source file. This information is what is used to create a human-readable—and diagnosable—exception object.
Throwing custom PHP exceptions
At their core, every exception is an extension of the global Exception class. What this means is that creating a custom exception, in its most basic form, requires only a class that extends the built-in Exception class.
namespace Custom; class Exception extends Exception
With this custom exception created, we can then throw it as we would any other exception:
throw new CustomException('Exception message');
The advantage to inheriting from the built-in Exception class is that we can extend the core functionality of default exceptions. By overriding class properties like code, file, line, and message or the __toString() method, we can coerce the exception data into a format we can work with.
namespace Custom; class Exception extends Exception < protected $details; public function __construct($details) < $this->details = $details; parent::__construct(); > public function __toString() < return 'I am an exception. Here are the deets: ' . $this->details; > >
Writing to the error log
Outside of exception throwing, PHP also supports writing directly to the error log (more about that in Where are PHP Errors Logged?). The aptly named error_log function can be used to write raw messages to the error log without interrupting program execution. This is useful when you need to track debug data or keep track of caught and handled PHP exceptions.
bool error_log ( string $message [, int $message_type = 0 [, string $destination [, string $extra_headers ]]] )
Depending on the $message_type , error_log generally only requires one parameter: a message. While the other parameters can be used to direct where the error message should go, the most common use case is to use the default options to write directly to PHP’s system logger.