Введение в PHP5

524
Введение в PHP5

Введение в PHP5

PHP5 ещё официально не вышел, но "рабочие" версии уже трудоспособны (равно как и нестабильны!), так что мы вполне можем начать изучение новых возможностей грядущего релиза PHP и попрактиковать с ними. В этой статье мы поговорим о трёх основных нововведениях в PHP5:

  • Новая объектная модель
  • Исключения
  • Пространства имён

Но сначала пара официальных заявлений:

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

PHP5 ещё не выпущен и мне неизвестно, когда это произойдёт, но уже сейчас вы можете потестировать и изучить новые возможности языка, скачав рабочую версию PHP5 с http://snaps.php.net и установив её. По этой ссылке вы можете найти готовые для установки Windows и Linux версии PHP5. Инсталляция проходит как у любого нормального релиза PHP, так что все бегом за новой игрушкой. У нас так же не обходится разработка сайта Винница без использования PHP5.

Новая объектная модель

В PHP5 объектную модель основательно подлатали и добавили много новых возможностей, благодаря чему PHP5 стал "чем-то" напоминать Java. В этой части нашей статьи будет описана эта новая объектная модель и приведено несколько небольших примеров, чтобы вам обозначить исходный рубеж для ваших экспериментов.

  • Конструкторы и деструкторы
  • Объекты как ссылки
  • Клонирование объектов
  • Дескрипторы Private, Public и Protected
  • Интерфейсы
  • Абстрактные классы
  • __call
  • __set and __get
  • Закрытые члены
Конструкторы и деструкторы

В PHP4 конструктор именуется так же как и сам класс, а деструкторы отсутствуют полностью.

В PHP5 конструктор класса именуется __construct, а деструктор - __destruct. 

Пример 1: Конструкторы и деструкторы

class foo 
  var 
$x

  function 
__construct($x) { 
    
$this->$x
  } 

  function 
display() { 
    print(
$this->x); 
  } 

  function 
__destruct() { 
    print(
"ну, пока, до скорого"); 
  } 


$o1 = new foo(4); 
$o1->display(); 
?> 

Как вы видите, деструктор вызывается перед самым уничтожением класса.

Объекты как ссылки

Как вам уже наверняка известно, в PHP4 переменные передаются в функции/методы по значению (передаётся копия), если в объявлении функции не поставлен символ '&', указывающий на то, что переменная должна передаваться как ссылка. В PHP5 объекты передаются всегда как ссылки. Присваивание объектов тоже происходит по ссылке. 

Пример 2: Объекты как ссылки

class foo 
  var 
$x

  function 
setX($x) { 
    
$this->$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: Клонирование объектов

class foo 
  var 
$x

  function 
setX($x) { 
    
$this->$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

class foo 
  private 
$x

  public function 
public_foo() { 
    print(
"Это открытый метод"); 
  } 

  protected function 
protected_foo() { 
    
$this->private_foo(); //Всё правильно, мы можем вызывать закрытые методы, потому что мы находимся в том же классе 
    
print("Это защищённый метод"); 
  } 

  private function 
private_foo() { 
    
$this->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: Интерфейсы

interface displayable 
  function 
display(); 


interface 
printable 
  function 
doprint(); 


class 
foo implements displayable,printable 
  function 
display() { 
    
// код 
  


  function 
doprint() { 
    
// код 
  


?> 

Использование интерфейсов полезно для более удобного чтения и понимания кода: прочитав объявление класса, мы увидим, что класс реализует интерфейсы displayable и printable; это означает, что класс должен иметь методы display() и doprint(). Как эти методы реализованы - значения не имеет, главное - уже из объявления класса, вы знаете, что можете вызывать эти методы.

Абстрактные классы

Абстрактным называется класс, который может использоваться только как базовый (то есть создавать объекты этого класса нельзя). Как и в любом нормальном базовом классе, в абстрактном классе вы можете определять методы и переменные.

В абстрактном классе также можно определять абстрактные методы: методы, которые не реализованы в абстрактном классе, но которые обязательно должны быть реализованы в производных классах. 

Пример 6: Абстрактные классы

abstract class foo 
  protected 
$x

  abstract function 
display(); 

  function 
setX($x) { 
    
$this->$x
  } 



class 
foo2 extends foo 
  function 
display() { 
    
// Код 
  


?> 

__call

С PHP5 вы можете реализовать в классе специальный метод __call(), как метод для "отлова" всех нереализованных в данном классе методов. Метод __call (если он определён) вызывается при попытке вызвать недоступный или несуществующий метод. 

Пример 7: __call

class foo 

  function 
__call($name,$arguments) { 
    print(
"Вызывали? Я -  $name!"); 
  } 


$x = new foo(); 
$x->doStuff(); 
$x->fancy_stuff(); 
?> 

Этот специальный метод может быть использован для реализации перегрузки методов: вы можете исследовать полученные аргументы и в зависимости от результата вызвать подходящий для данного случая закрытый метод, например: 

Пример 8: Перегрузка методов с помощью __call

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

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: указание типов

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: методы класса и переменные класса

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: Исключения

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\n"
    
// Какие-нибудь драконовские меры 

?> 

Как вы видите, "try" используется для обозначения блока, в котором находятся ошибки, обрабатываемые оператором "catch", стоящим в конце блока. В блоке "catch" вам нужно реализовать вашу собственную политику обработки ошибок. В итоге получаем удобочитаемый код и всего один блок обработки ошибок.

Исключения, определённые пользователем

Для обработки непредвиденных проблем в ваших программах вы можете определить ваши собственные исключения. Всё, что вам нужно - это просто дополнить (extend) класс Exception, определив конструктор класса и метод getMessage. 

Пример 13: Исключения, определённые пользователем

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: Пространство имён

namespace Math 

  class 
Complex 
    
//...код... 
    
function __construct() { 
      print(
"привет"); 
    } 
  } 


$m = new Math::Complex(); 
?> 

Обратите внимание на синтаксис использования именного пространства для обозначения класса, объект которого мы создаём. Пример практического применения: создание одноимённых классов в разных именных пространствах; при этом классы делают отличную друг от друга работу (имея одинаковый интерфейс).