Введение в PHP5
Автор: Данил Миронов
Источник: http://www.phpbuilder.com/columns/argerich20030411.php3
В этой статье мы поговорим о трёх
основных нововведениях в PHP5:
- Новая объектная модель
- Исключения
- Пространства имён
Но сначала пара официальных заявлений:
- Некоторые из приведённых в данной статье решений
воспроизводимы в PHP4, но, тем не менее, их описание
присутствует и здесь для большей удобочитаемости и
целостности всей статьи.
- Некоторые из описанных в данной статье особенностей в
конечном релизе PHP5 могут быть изменены.
Новая объектная
модель
В PHP5 объектную модель основательно подлатали и добавили
много новых возможностей, благодаря чему PHP5 стал "чем-то"
напоминать Java. В этой части нашей статьи будет описана эта
новая объектная модель и приведено несколько небольших
примеров, чтобы вам обозначить исходный рубеж для ваших
экспериментов.
- Конструкторы и деструкторы
- Объекты как ссылки
- Клонирование объектов
- Дескрипторы Private, Public и Protected
- Интерфейсы
- Абстрактные классы
- __call
- __set and __get
- Закрытые члены
Конструкторы и
деструкторы
В PHP4 конструктор именуется так же как и сам класс, а
деструкторы отсутствуют полностью.
В PHP5 конструктор класса именуется __construct, а
деструктор - __destruct.
Пример 1: Конструкторы и
деструкторы
<?php
class foo {
var $x;
function __construct($x)
{
$this->x = $x;
}
function display() {
print($this->x);
}
function __destruct() {
print("ну, пока, до скорого");
}
}
$o1 = new foo(4);
$o1->display();
?>
Как вы видите, деструктор вызывается перед самым
уничтожением класса.
Объекты как ссылки
Как вам уже наверняка известно, в PHP4 переменные
передаются в функции/методы по значению (передаётся копия),
если в объявлении функции не поставлен символ '&',
указывающий на то, что переменная должна передаваться как
ссылка. В PHP5 объекты передаются всегда как ссылки.
Присваивание объектов тоже происходит по ссылке.
Пример 2: Объекты как ссылки
<?php
class foo {
var $x;
function setX($x)
{
$this->x = $x;
}
function getX() {
return $this->x;
}
}
$o1 = new foo;
$o1->setX(4);
$o2 = $o1;
$o1->setX(5);
if($o1->getX() == $o2->getX()) print("Ох ты, Боже мой!");
?>
Клонирование
объектов
Если объекты присваиваются и передаются по ссылке, то вам
нужно как-то создавать и копии объектов. Для этого используйте
метод __clone.
Пример 3: Клонирование объектов
<?php
class foo {
var $x;
function setX($x)
{
$this->x = $x;
}
function getX() {
return $this->x;
}
}
$o1 = new foo;
$o1->setX(4);
$o2 = $o1->__clone();
$o1->setX(5);
if($o1->getX() != $o2->getX()) print("Копии взаимонезависимы");
?>
В программировании клонирование разрешено, так что всё
легально ;-)
Дескрипторы Private,
Public и Protected
В PHP4 все методы и переменные внутри объекта были доступны
извне, другими словами все методы и переменные всегда были
открытыми. В PHP5 вводится три дескриптора для осуществления
контроля над доступом к переменным и методам: Public,
Protected и Private.
- Public (открытый): Метод/переменная доступны из любого
места в коде.
- Private (закрытый): Закрытые методы или переменные
доступны только внутри класса.
- Protected (защищённый): Защищённые методы или переменные
доступны только внутри класса, где они были объявлены и из
его производных классов.
Пример 4: Public, protected and
private
<?php
class foo {
private $x;
public function public_foo() {
print("Это открытый метод");
}
protected function protected_foo() {
$this->private_foo(); //Всё правильно, мы можем вызывать
закрытые методы, потому что мы находимся в том же классе
print("Это защищённый метод");
}
private function private_foo() {
$this->x = 3;
print("Это закрытый метод");
}
}
class foo2 extends foo {
public function display() {
$this->protected_foo();
$this->public_foo();
//
$this->private_foo(); // Неправильно! В базовом
классе метод закрыт
}
}
$x = new foo();
$x->public_foo();
//$x->protected_foo(); //Неправильно,
защищённые методы могут вызываться только из того же класса
или его производных классов
//$x->private_foo(); //Неправильно,
закрытые методы могут быть вызваны только в классе, где они
были объявлены
$x2 = new foo2();
$x2->display();
?>
Совет разработчикам: Переменные класса всегда следует
делать закрытыми, прямой доступ к переменным - не очень
хорошая практика в ООП, лучше всего для доступа/изменения
переменных класса определять специальные методы.
Интерфейсы
Как вы знаете, PHP4 поддерживает наследование классов
синтаксисом "class foo extends parent". В PHP4 И в PHP5 класс
может наследовать только один класс, то есть множественное
наследование не поддерживается. Интерфейсом называется класс,
в котором не реализуется ни один метод, определяются только
названия методов и набор передаваемых им параметров.
Впоследствии классы могут 'реализовывать' сколь угодно много
интерфейсов, показывая тем самым, что тот или иной класс
реализует методы, определённые в интерфейсе.
Пример 5: Интерфейсы
<?php
interface displayable {
function display();
}
interface printable {
function doprint();
}
class foo implements displayable,printable {
function display() {
// код
}
function doprint() {
// код
}
}
?>
Использование интерфейсов полезно для более удобного чтения
и понимания кода: прочитав объявление класса, мы увидим, что
класс реализует интерфейсы displayable и printable; это
означает, что класс должен иметь методы display() и doprint().
Как эти методы реализованы - значения не имеет, главное - уже
из объявления класса, вы знаете, что можете вызывать эти
методы.
Абстрактные классы
Абстрактным называется класс, который может использоваться
только как базовый (то есть создавать объекты этого класса
нельзя). Как и в любом нормальном базовом классе, в
абстрактном классе вы можете определять методы и переменные.
В абстрактном классе также можно определять абстрактные
методы: методы, которые не реализованы в абстрактном классе,
но которые обязательно должны быть реализованы в производных
классах.
Пример 6: Абстрактные классы
<?php
abstract class foo {
protected $x;
abstract function display();
function setX($x)
{
$this->x = $x;
}
}
class foo2 extends foo {
function display() {
// Код
}
}
?>
__call
С PHP5 вы можете реализовать в классе специальный метод
__call(), как метод для "отлова" всех нереализованных в данном
классе методов. Метод __call (если он определён) вызывается
при попытке вызвать недоступный или несуществующий метод.
Пример 7: __call
<?php
class foo {
function __call($name,$arguments) {
print("Вызывали? Я
- $name!");
}
}
$x = new foo();
$x->doStuff();
$x->fancy_stuff();
?>
Этот специальный метод может быть использован для
реализации перегрузки методов: вы можете исследовать
полученные аргументы и в зависимости от результата вызвать
подходящий для данного случая закрытый метод, например:
Пример 8: Перегрузка методов с
помощью __call
<?php
class Magic {
function __call($name,$arguments) {
if($name=='foo') {
if(is_int($arguments[0])) $this->foo_for_int($arguments[0]);
if(is_string($arguments[0])) $this->foo_for_string($arguments[0]);
}
}
private function foo_for_int($x)
{
print("у, смотрите, целое число!");
}
private function foo_for_string($x)
{
print("у, смотрите, строка!");
}
}
$x = new Magic();
$x->foo(3);
$x->foo("3");
?>
__set и __get
Но это ещё не всё, теперь вы можете определить методы __set
и __get для "отлова" всех попыток изменения или доступа к
неопределённым (или недоступным) переменным.
Пример 9: __set и __get
<?php
class foo {
function __set($name,$val) {
print("Привет, вы попытались присвоить
значение $val переменной $name");
}
function __get($name) {
print("Привет, вы пытались обратиться к
$name");
}
}
$x = new foo();
$x->bar = 3;
print($x->winky_winky);
?>
Указание типов для
аргументов
В PHP5 вы сможете "сказать" методу, что он должен получить
в качестве аргумента объект определённого типа.
Пример 10: указание типов
<?php
class foo {
// код ...
}
class bar {
public function process_a_foo(foo $foo) {
// Ещё какой-нибудь код
}
}
$b = new bar();
$f = new foo();
$b->process_a_foo($f);
?>
Как вы заметили, перед именем аргумента теперь можно
поставить имя его класса, и таким образом PHP5 определит, что
переменная $foo должна быть класса foo.
Статические члены
класса
Статические члены и статические методы могут использоваться
для реализации того, что в ООП называется "методы класса" и
"переменные класса".
"Статическим методом класса" называют метод, который можно
вызвать без создания объекта этого класса.
"Переменной
класса" называют переменную, к которой можно обратиться без
создания объекта этого класса (и метод доступа при этом не
потребуется).
Пример 11: методы класса и
переменные класса
<?php
class calculator {
static public $pi = 3.14151692;
static public
function add($x,$y)
{
return $x + $y;
}
}
$s = calculator::$pi;
$result = calculator::add(3,7);
print("$result");
?>
Исключения
Исключения - это общепринятый подход к обработке ошибок и
неожиданных ситуаций в таких языках как Java и C++; в PHP5
перехват исключений реализован с помощью пары "try" - "catch".
Пример 12: Исключения
<?php
class foo {
function divide($x,$y)
{
if($y==0)
throw new Exception("деление на ноль
недопустимо");
return $x/$y;
}
}
$x = new foo();
try {
$x->divide(3,0);
} catch
(Exception $e) {
echo $e->getMessage();
echo "\n<br
/>\n";
// Какие-нибудь драконовские меры
}
?>
Как вы видите, "try" используется для обозначения блока, в
котором находятся ошибки, обрабатываемые оператором "catch",
стоящим в конце блока. В блоке "catch" вам нужно реализовать
вашу собственную политику обработки ошибок. В итоге получаем
удобочитаемый код и всего один блок обработки ошибок.
Исключения, определённые
пользователем
Для обработки непредвиденных проблем в ваших программах вы
можете определить ваши собственные исключения. Всё, что вам
нужно - это просто дополнить (extend) класс Exception,
определив конструктор класса и метод getMessage.
Пример 13: Исключения, определённые
пользователем
<?php
class WeirdProblem extends Exception {
private $data;
function WeirdProblem($data) {
parent::exception();
$this->data = $data;
}
function getMessage() {
return $this->data . "
вызвало какое-то странное исключение!";
}
}
?>
Потом, для возбуждения определённого вами исключения
используйте конструкцию throw new WeirdProblem($foo); если
исключение происходит внутри блока try{}, то PHP5 передаст
управление в "catch"-блок для обработки.
Пространства имён
В целях удобства классы и функции могут быть сгруппированы
в пространства имён (namespaces).
Примечение: разработчики отказались от поддрежки этой
возможности.
Пример 14: Пространство имён
<?php
namespace Math {
class Complex {
//...код...
function __construct() {
print("привет");
}
}
}
$m = new Math::Complex();
?>
Обратите внимание на синтаксис использования именного
пространства для обозначения класса, объект которого мы
создаём. Пример практического применения: создание одноимённых
классов в разных именных пространствах; при этом классы делают
отличную друг от друга работу (имея одинаковый интерфейс).