Объектно-ориентированное программирование в PHP
Основные понятия ООП
ООП – методология программирования, основанная на представлении программ в виде совокупности объектов, каждый из которых является экземпляром конкретного класса.
Объект – обладающий именем набор данных (полей объекта), физически находящихся в памяти компьютера, и методов, имеющих доступ к ним и выполняющих операции над ними. Имя объекта используется для доступа к полям данных и методам, составляющим объект. Объект является экземпляром определенного класса. В классе дается обобщенное описание некоторого набора родственных реально существующих объектов. Объектно-ориентированное программирование основано на принципах:
− абстрагирования данных;
− инкапсуляции;
− наследования;
− полиморфизма;
− «позднего связывания».
Инкапсуляция (encapsulation) – принцип, объединяющий данные и код, манипулирующий этими данными, а также защищающий данные от прямого внешнего доступа и неправильного использования. Другими словами, доступ к данным класса возможен только посредством методов этого же класса.
Наследование (inheritance) – это процесс, посредством которого один объект может приобретать свойства другого. Точнее, объект может наследовать основные свойства объекта - предка и добавлять к ним свойства и методы, характерные только для него. Наследование бывает двух видов:
линейное – класс имеет один и только один суперкласс (предок);
множественное – класс может иметь любое количество предков.
Полиморфизм (polymorphism) – механизм, использующий одно и то же имя метода для решения двух или более похожих, но несколько отличающихся задач.
Целью полиморфизма, применительно к ООП, является использование одного имени для задания общих для класса действий. В более общем смысле, концепцией полиморфизма является идея "один интерфейс, множество методов".
Механизм «позднего связывания» в процессе выполнения программы определяет принадлежность объекта конкретному классу и производит вызов метода, относящегося к классу, объект которого был использован.
Классы и Объекты
Модель объектов PHP больше похожа на модель объектов Java, чем на модель C++. Класс представляет объединение переменных и функций-методов, работающих с этими переменными. Переменные класса определяются с ключевым словом var как в PHP4 или со спецификатором доступа private, public или protected как в PHP5. Методы определяются в классе с ключевым словом function и спецификатором доступа. Рассмотрим пример:
<?php
class Mycart
{//var $items=0; в PHP4
private $items=0; //переменные класса в PHP5
public function add ($n) // Добавить $n артикулов
{
$this->items += $n;
}
public function remove ( $n) // Изъять $n артикулов
{
if ($this->items > $n) {
$this->items -= $n;
return true; }
else { return false; }
}
public function show()
{
echo '$items=', $this->items ;
}
}//конец класса
$instance=new Mycart();
$instance->add(5);
$instance->remove(2);
$instance->show();
echo $instance->items ;//ошибка
?>
Вывод:
$items=3
Fatal error: Cannot access private property Mycart::$items in x:\home\localhost\www\my\pr41.php on line 25 – нет доступа к private переменной items
Здесь $instance представляет ссылку на объект класса, сам объект создается операцией new. Каждый объект получает собственный числовой идентификатор (handler), который используется при обращении к объекту.
Внутри определения метода класса вы не знаете, под каким именем объект будет доступен в программе: на момент написания класса неизвестно, как объект будет называться и какие объекты будут использоваться. Чтобы иметь возможность доступа к переменным и функциям внутри класса, можно использовать псевдопеременную $this, которая может читаться как 'ссылка на текущий объект'.
В PHP 5 введены спецификаторы доступа public, protected и private, которые позволяют указать степень доступа к свойствам и методам класса.
К общедоступным (public) свойствам и методам можно получить доступ без каких либо ограничений. Защищенные (protected) элементы класса доступны внутри класса, в котором они объявлены, и в производных (наследуемых) от него классах.
Частные (private) элементы доступны только методам в классе, в котором они объявлены.
Если не указывать ни один из спецификаторов, то по умолчанию элемент будет иметь уровень доступа public. Такой же уровень доступа по умолчанию получают свойства, для объявления которых использовалось устаревшее и не рекомендуемое к использованию в PHP 5 ключевое слово var, а спецификатор доступа не используется. Это сделано для переемственности с PHP4.
Конструкторы и деструкторы
В PHP допустимы только константные инициализаторы для переменных класса. Для инициализации переменных класса используется функция инициализации, которая вызывается автоматически при создании объекта класса. Такая функция называется конструктором. Метод-конструктор вызывается автоматически при каждом создании объекта. В PHP4 имя этой функции совпадает с именем класса. В PHP 5 конструктором класса является метод __construct(). При уничтожении объекта вызывается специальный метод __destruct() – деструктор класса.
<?php //pr21
class MyClass {
private $property;
function _construct() {
echo "Запущен конструктор";
}
function __destruct() {
echo "Запущен деструктор";
}
function MyClass($n=1) {
echo "Запущен другой конструктор";
$this->property=$n;
}
}
$obj = new MyClass(); // Выводит "Запущен конструктор"
unset($obj); // Выводит "Запущен деструктор"
$obj1 = new MyClass(10);
?>
Запущен конструктор Запущен деструктор Запущен конструктор Запущен деструктор
Конструкторы PHP4, имена которых совпадают с именами классов, будут работать с PHP5 без изменений кода. Для совместимости с предыдущей версией PHP 5 поступает следующем образом: если при создании объекта в классе не найдется конструктора __construct(), то PHP пытается выполнить метод, имя которого совпадает с именем класса.
В приведенном выше примере конструктор не перегружается, а вызывается первый конструктор.
В случае наследования классов конструктор базового класса при создании порожденного класса неявно не вызывается. Если необходимо вызвать конструктор или деструктор базового класса из порожденного класса, это нужно сделать явно, через указатель parent.
<?php //pr22
class MyClass {
function __construct() {
echo "конструктор базового класса";
}
function __destruct() {
echo " деструктор базового класса";
}
}
class DerivedClass extends MyClass {
function __construct() {
echo "конструктор порожденного класса";
parent::__construct();
}
function __destruct() {
echo " деструктор порожденного класса";
parent::__destruct();
}
}
$obj = new DerivedClass();
unset($obj);
?>
Вывод:
конструктор порожденного класса конструктор базового класса
деструктор порожденного класса деструктор базового класса
Рассмотрим еще один пример вывода даты/времени:
<?php
class Dateclass {
var $month=array( "Январ", "Феврал", "Март", "Апрел", "Ма", "Июн", "Июл","Август","Сентябр", "Октябр", "Декабр", "Январ");
var $day=array ( "Воскресенье", "Понедельник", "Вторник","Среда", "Четверг" , "Пятница" , "Суббота");
var $dnum; var $mnum; // Переменные класса
var $daym; var $year;
function Dateclass() { //Конструктор: инициализация переменных
$this->dnum = date("w");
$this->mnum = date("n");
$this->daym = date("d");
$this->year = date("Y");
}
function show(){// метод
$dnum =$this->dnum;
$mnum = $this->mnum;
$daym =$this->daym;
$year = $this->year;
$textday =$this->day[$dnum];
$monthm =$this->month[$mnum-1];
if ($mnum==3||$mnum==8)
{ $k="а"; }
else {$k="я";}
echo "Сегодня: $textday, $daym $monthm$k $year г.";
}
}
$obj=new Dateclass();// Создание объекта
$obj->show();//Вызов метода из объекта
?>
Результат:
Сегодня: Воскресенье, 05 Апреля 2009 г.
В PHP 5 объекты представляют объектные ссылки, присвоение объекта или его передача в качестве параметра функции происходит по ссылке, а не по значению. Если необходимо провести копирование объекта, то используется кланирование объекта. Объект копируется со всеми своими методами, свойствами и их значениями:
<?php //pr24
class MyClass{
public $property=1;
}
$obj1 = new MyClass;
$obj2=$obj1;//ссылка на тот же объект
echo $obj1->property; // Выводит 1
$obj2->property = 2;
echo $obj1->property; // Выводит 2
$obj3 = clone $obj1;//создание нового объекта
echo $obj3->property; // Выводит 2
$obj3->property = 3;
echo $obj1->property; // Выводит 2
?>
Для копирования объекта используется ключевое слово clone, которое вызывает метод __clone() и к которому нельзя обратиться непосредственно. Метод __clone() необязательно описывать в классе, однако его перегрузка, позволит изменить значения свойств копируемого объекта:
<?php
class MyClass{
var $property;
function __clone() {
$this->property = 2;
}
}
$obj1 = new MyClass;
$obj1->property = 1;
$obj2 = clone $obj1;
echo $obj1->property; // Выводит 1
echo $obj2->property; // Выводит 2
?>
Метод __clone() не может принимать никакие аргументы, однако позволяет обратиться к исходному объекту через указатель $this и получаемому в результате копирования объекту через указатель $that.
Наследование классов и интерфейсов
Класс может быть наследником только одного суперкласса и множества интерфейсов. Рассмотрим пример
<?php
class MyClass extend MySuperClass implements I1,I2,I3{
//реализация класса
}
?>
В вершине иерархии наследования обычно размещают абстрактные базовые классы, содержащие абстрактные методы и объявленные как абстрактные. Абстрактные методы имеют только объявление и не имеют реализации.
<?php
abstract class MySuperClass {
abstract public function abstrFunc();
}
class MyClass extends MySuperClass {
public function abstrFunc() {
echo “Be-Be-Be”;
}
}
$obj = new MyClass;
$obj->abstrFunc(); // Выводит Be-Be-Be
?>
Невозможно создать объект абстрактного класса, можно только определять производные классы от базового абстрактного класса и создавать объекты уже от производных классов. Стоит отметить, что абстрактные классы также могут содержать и обычные (не абстрактные) методы. Класс не может быть порожден от нескольких классов, в том числе и абстрактных, но зато может быть создан на основе любого числа интерфейсов.
Интерфейсами (interface) являются абстрактные классы, содержащие только абстрактные методы и вообще не имеющие никаких свойств. При этом в интерфейсе методы объявляются ключевым словом function без указания каких-либо спецификаторов, в том числе и abstract и нет тела функции.
<?php
interface Int1 {
function func1();
}
interface Int2 {
function func2();
}
class MyClass implements Int1, Int2 {
public function func1() {
echo 1;
}
public function func2() {
echo 2;
}
}
$obj = new MyClass;
$obj->func1(); // Выводит 1
$obj->func2(); // Выводит 2
?>
Таким образом, хотя множественное наследование классов и не поддерживается, однако можно создавать классы на основе нескольких интерфейсов.
В PHP 5 введена возможность определять методы класса и сами классы как финальные (final).
Метод, при определении которого используется ключевое слово final, не может быть переопределен в классах, производных от данного класса.
<?php
class MyClass {
final public function func() {
// Код метода
}
}
class MyClass1 extends MyClass {
// Следующий код вызывает ошибку
// переопределения финального метода
// базового класса MyClass
public function func() {
// Код метода
}
}
?>
Кроме этого, если final используется при определении самого класса, то порождение от него других классов становится невозможным.
<?php
final class MyClass {
// Код описания класса
}
// Следующий код вызывает ошибку
// порождения от финального класса
class MyClass1 extends MyClass {
// Код описания класса
}
?>
Если класс определен как final, то и все методы данного класса автоматически становятся финальными, таким образом, определять их явно как final уже нет необходимости.
В отличие от Java, определять свойства класса как финальные константы в PHP недопустимо. В PHP введен новый элемент класса – константа класса.
<?php
class MyClass {
const CONSTANT = "константа класса";
}
echo MyClass::CONSTANT; // Выводит "константа класса"
?>
В PHP 5 возможно объявление статических свойств класса.
<?php
class MyClass {
static $static = 1;
}
echo MyClass::$static; // Выводит 1
?>
В PHP используются статические свойства и методы классов. Статические свойства едины для всего класса и не могут принадлежать ни одному из объектов класса. Изменение такого свойства в одном из методов любого объекта приводит к его изменению для всех остальных объектов данного класса. Кроме этого, становится возможным обратиться к такому свойству из класса вне контекста объекта.
Статические методы классов, также как и статические свойства, принадлежат всему классу в целом. Это позволяет использовать такой метод без создания объекта такого класса.
<?php
class MyClass {
static function statFunc() {
echo "статический метод";
}
}
MyClass::statFunc(); // Выводит "статический метод"
?>
Однако в статическом методе становится невозможным использовать указатель $this, так как при вызове статического метода неизвестно в контексте какого объекта он вызывается, или такого объекта может и не существовать.
Специальное ключевое слово instanceof в PHP 5 позволяет определять является ли объект экземпляром определенного класса, или же экземпляром класса производного от какого-либо класса.
<?php
class MyClass { }
$obj1 = new MyClass();
if ($obj1 instanceof MyClass) {
echo "\$obj1 - объект класса MyClass";
}
class MyClass1 extends MyClass { }
$obj2 = new MyClass1();
if ($obj2 instanceof MyClass) {
echo "\$obj2 - объект класса, производного от MyClass";
}
interface Int { }
class MyClass2 implements Int { }
$obj3 = new MyClass2();
if ($obj3 instanceof Int) {
echo "\$obj3 - объект класса, созданного на основе интерфейса Int";
}
?>
Также с помощью instanceof можно определить является ли объект экземпляром класса, созданного на основе определенного интерфейса.
В PHP 5 введена возможность разыменования (dereferencing) объектов, которые возвращаются функциями.
<?php
class MyClass1 {
public function showClassName() {
echo "объект класса MyClass1";
}
}
class MyClass2 {
public function showClassName() {
echo "объект класса MyClass2";
}
}
function deref($obj) {
switch ($obj) {
case "MyClass1":
return new MyClass1();
case "MyClass2":
return new MyClass2();
}
}
deref("MyClass1")->showClassName(); // Выводит "объект
// класса MyClass1"
deref("MyClass2")->showClassName(); // Выводит "объект
// класса MyClass2"
?>
Данный механизм позволяет вызывать методы объектов, имена классов которых возвращаются пользовательскими функциями.
В PHP 5 имеется возможность производить уточнения типов классов (class type hints), которые передается методам в качестве параметров.
<?php
interface Int1 {
function func1(Int1 $int1);
}
interface Int2 {
function func2(Int2 $int2);
}
class MyClass implements Int1, Int2 {
public function func1(Int1 $int1) {
// Код метода
}
public function func2(Int2 $int2) {
// Код метода
}
}
$obj1 = new MyClass;
$obj2 = new MyClass;
$obj1->func1($obj2);
$obj1->func2($obj2);
?>
При этом уточнение типов классов производится не при компиляции, а только на этапе исполнения.
Магические методы
Функции, __construct, __destruct, __call, __callStatic, __get, __set, __isset, __unset, __sleep, __wakeup, __toString, __invoke, __set_state , __clone , имена которых начинаются с символа “_“ , являются магическими в PHP классах и имеют особую функциональность.
Методы доступа к свойствам объектов __get($name) и __set($name,$value) позволяют легко проводить динамическое назначение свойств объектам. В качестве параметров этим методам передаются имена свойств класса.
Метод __set() также получает и значение, которое устанавливается для свойства. Методы __get() и __set() вызываются только в том случае, если требуемого свойства вообще нет в классе.
<?php
class MyClass {
private $properties;
function __set($name, $value) {
echo "задание нового свойства $name = $value";
$this->properties[$name]=$value;
}
function __get($name) {
echo "чтение значения свойства ", $name;
return $this->properties[$name];
}
}
$obj = new MyClass;
$obj->property = 1; // Выводит "задание нового свойства property=1"
$a = $obj->property; // Выводит "чтение значения свойства property"
echo $a; // выводит 1;
?>
При вызове в PHP 5 несуществующего метода объекта автоматически вызывается специальный метод __call().
<?php
class MyClass {
function __call($name, $params) {
echo "Вызван метод $name с параметром $params[0]";
}
}
$obj = new MyClass;
echo $obj->method(1); // Выводит "Вызван метод method
// с параметром 1"
?>
В качестве параметров __call() принимает имя вызываемого метода и передаваемые этому методу параметры.
В PHP 5 псевдо-константа __METHOD__ возвращает имя класса и вызываемый метод.
<?php
class MyClass {
public function myMethod() {
echo "вызов метода ", __METHOD__;
}
}
$obj = new MyClass;
$obj->myMethod();//Выводит "вызов метода MyClass::myMethod"
function myFunction() {
echo "вызов функции ", __METHOD__;
}
myFunction(); // Выводит "вызов функции myFunction"
?>
При обращении к функции вне класса __METHOD__ возвращает только имя функции.
В PHP 5 введен еще один специальный метод класса - __toString().
<?php
class MyClass {
function __toString() {
return "вызван метод __toString()";
}
}
$obj = new MyClass;
echo $obj; // Выводит "вызван метод __toString()"
?>
Метод класса __toString() позволяет выполнить перегрузку преобразования объекта в строку.
Обработка ошибок
Начиная с PHP 5 введена современная схема обработки исключений. Конструкция try/catch/throw позволит весь код обработки ошибок локализовать в одном месте сценария.
<?php
try {
$fp = @fopen("file.txt", "w");
if (!$fp) throw new Exception("Невозможно открыть файл!");
// Запись данных в файл
fclose($fp);
}
catch (Exception $exception) {
echo "Ошибка в строке ", $exception->getLine();
echo $exception->getMessage(); // Выводит "Невозможно
// открыть файл"
}
?>
В конструкции можно использовать несколько блоков catch. Также возможно создание собственных классов исключений, производных от встроенного класса Exception.