ООП в PHP

Классы и объекты в PHP

Описание классов в PHP начинаются служебным словом class:

class Имя_класса {

// описание членов класса - свойств и методов для их обработки

}

//Для объявления объекта необходимо использовать оператор new:

Объект = new Имя_класса;

Данные описываются с помощью служебного слова var. Метод описывается так же, как и обыкновенная пользовательская функция. Методу также можно передавать параметры.

Подведем промежуточные итоги: объявление класса должно начинаться с ключевого слова class (подобно тому, как объявление функции начинается с ключевого слова function). Каждому объявлению свойства, содержащегося в классе, должно предшествовать ключевое слово var. Свойства могут относиться к любому типу данных, поддерживаемых в РНР, их можно рассматривать как переменные с небольшими различиями. После объявлений свойств следуют объявления методов, очень похожие на типичные объявления пользовательских функций.

По общепринятым правилам имена классов ООП начинаются с прописной буквы, а все слова в именах методов, кроме первого, начинаются с прописных букв (первое слово начинается со строчной буквы). Разумеется, вы можете использовать любые обозначения, которые сочтете удобными; главное — выберите стандарт и придерживайтесь его.

Пример класса на PHP:


// Создаем новый класс Coor:
class Coor {
// данные (свойства):
var $name;
var $addr;

// методы:
function Name() {
echo "John";
}

}
// Создаем объект класса Coor:
$object = new Coor;
?>

Доступ к класам и объектам в PHP

Мы рассмотрели, каким образом описываются классы и создаются объекты. Теперь нам необходимо получить доступ к членам класса, для этого в PHP предназначен оператор ->.

Приведем пример:


// Создаем новый класс Coor:
class Coor {
// данные (свойства):
var $name;

// методы:
function Getname() {
echo "

John

";
}

}

// Создаем объект класса Coor:
$object = new Coor;
// Получаем доступ к членам класса:
$object->name = "Alex";
echo $object->name;
// Выводит 'Alex'
// А теперь получим доступ к методу класса (фактически, к функции внутри класса):
$object->Getname();
// Выводит 'John' заглавными буквами
?>

Чтобы получить доступ к членам класса внутри класса, необходимо использовать указатель $this, которы всегда относится к текущему объекту. Модифицированный метод Getname():

function Getname() {
echo $this->name;
}

Таким же образом, можно написать метод Setname():

function Setname($name) {
$this->name = $name;
}

Теперь для изменения имени можно использовать метод Setname():

$object->Setname("Peter");
$object->Getname();

А вот и полный листинг кода:

// Создаем новый класс Coor:
class Coor {
// данные (свойства):
var $name;

// методы:
function Getname() {
echo $this->name;
}

function Setname($name) {
$this->name = $name;
}

}

// Создаем объект класса Coor:
$object = new Coor;
// Теперь для изменения имени используем метод Setname():
$object->Setname("Nick");
// А для доступа, как и прежде, Getname():
$object->Getname();
// Сценарий выводит 'Nick'
?>

//Указатель $this можно также использовать для доступа к методам, а не только для доступа к данным:

function Setname($name) {
$this->name = $name;
$this->Getname();
}

Инициализация объектов

Иногда возникает необходимость выполнить инициализацию объекта - присвоить его свойствам первоначальные значения. Предположим, имя класса Coor и он содержит два свойства:имя человека и город его проживания. Можно написать метод (функцию), который будет выполнять инициализацию объекта, например Init():

// Создаем новый класс Coor:
class Coor {
// данные (свойства):
var $name;
var $city;

// Инициализирующий метод:
function Init($name) {
$this->name = $name;
$this->city = "London";
}

}

// Создаем объект класса Coor:
$object = new Coor;
// Для инициализации объекта сразу вызываем метод:
$object->Init();
?>

Главное не забыть вызвать функцию сразу после создания объекта, либо вызвать какой-нибудь метод между созданием (оператор new) объекта и его инициализацией (вызовом Init).

Для того, чтобы PHP знал, что определенный метод нужно вызывать автоматически при создании объекта, ему нужно дать имя такое же, как и у класса (Coor):

function Coor ($name)
$this->name = $name;
$this->city = "London";
}

Метод, инициализирующий объект, называется конструктором. Однако, PHP не имеет деструкторов, поскольку ресурсы освобождаюся автоматически при завершении работы скриптов.

Обращение к элементам классов

Обращение к элементам классов осуществляется с помощью оператора :: "двойное двоеточие". Используя "двойное двоеточие", можно обращаться к методам классов.

При обращении к методам классов, программист должен использовать имена этих классов.

class A {
function example() {
echo "Это первоначальная функция A::example().
";
}
}

class B extends A {
function example() {
echo "Это переопределенная функция B::example().
";
A::example();
}
}

// Не нужно создавать объект класса A.
// Выводит следующее:
// Это первоначальная функция A::example().
A::example();

// Создаем объект класса B.
$b = new B;

// Выводит следующее:
// Это переопределенная функция B::example().
// Это первоначальная функция A::example().
$b->example();
?>

Наследование классов в PHP

Наследование - это не просто создание точной копии класса, а расширение уже существующего класса, чтобы потомок мог выполнять какие-нибудь новые, характерные только ему функции.

Итак, пусть у нас есть некоторый класс A с определенными свойствами и методами. Но то, что этот класс делает, нас не совсем устраивает — например, пусть он выполняет большинство функций, по сути нам необходимых, но не реализует некоторых других. Зададимся целью создать новый класс B, как бы "расширяющий" возможности класса A, добавляющий ему несколько новых свойств и методов. Сделать это можно двумя принципиально различными способами. Первый выглядит примерно так:

class A {
function TestA() { ... }
function Test() { ... }
}

class B {
var $a; // объект класса A
function B(параметры_для_A, другие_параметры){
$a = new A(параметры_для_A);
// инициализируем другие поля B
}
function TestB() { ... }
function Test() { ... }
}
?>

Поясним: в этой реализации объект класса B содержит в своем составе подобъект класса A в качестве свойства. Это свойство — лишь "частичка" объекта класса B, не более того. Подобъект не "знает", что он в действительности не самостоятелен, а содержится в классе B, поэтому не может предпринимать никаких действий, специфичных для этого класса. Мы хотели получить расширение возможностей класса A, а не нечто, содержащее объекты A. Что означает "расширение"? Лишь одно: мы бы хотели, чтобы везде, где допустима работа с объектами класса A, была допустима и работа с объектами класса B. Но в приведенном примере это совсем не так.

Итак, мы имеем некоторые проблемы:

1. Мы не видим явно, что класс B лишь расширяет возможности A, а не является отдельной сущностью;

2. Мы должны обращаться к "части A" класса B через $obj->a->TestA(), а к членам самого класса B как $obj->TestB(). Последнее может быть довольно утомительным, если, как это часто бывает, в B будет использоваться очень много методов из A и гораздо меньше — из B. Кроме того, это заставляет нас постоянно помнить о внутреннем устройстве класса B.

Теперь на практике рассмотрим, что же представляет собой наследование (или расширение возможностей) классов:

class B extends A {
function B(параметры_для_A, другие_параметры) {
$this->A(параметры_для_A);
// инициализируем другие поля B
}

function TestB() { ... }
function Test() { ... }
}
?>

Ключевое слово extends говорит о том, что создаваемый класс является лишь "расширением" класса A, и не более того. То есть B содержит те же самые свойства и методы, что и A, но, помимо них и еще некоторые дополнительные, "свои". Теперь "часть A" находится прямо внутри класса B и может быть легко доступна, наравне с методами и свойствами самого класса B. Например, для объекта $obj класса B допустимы выражения $obj->TestA() и $obj->TestB().

Итак, мы видим, что, действительно, класс B является воплощением идеи "расширение функциональности класса A". Обратите также внимание: мы можем теперь забыть, что B унаследовал от A некоторые свойства или методы — снаружи все выглядит так, будто класс B реализует их самостоятельно.

Немного о терминологии: родительский класс A принято называть базовым классом, а класс дочерний класс B — производным от A. Иногда базовый класс также называют суперклассом, а производный — подкласcом.

Рассмотрим еще один пример на PHP:

class Parent {
function parent_funct() { echo "Это родительская функция"; }
function test () { echo "Это родительский класс"; }
}

class Child extends Parent {
function child_funct() { echo "Это дочерняя функция"; }
function test () { echo "Это дочерний класс"; }
}

$object = new Parent;
$object = new Child;

$object->parent_funct(); // Выводит 'Это родительская функция'
$object->child_funct(); // Выводит 'Это дочерняя функция'
$object->test(); // Выводит 'Это дочерний класс'
?>

Дочерний класс (подкласс) Child наследует все методы и свойства суперкласса Parent.

Полиморфизм классов в PHP

Полиморфизм (многоформенность) является следствием идеи наследования. В общих словах, полиморфность класса — это свойство базового класса использовать функции производных классов, даже если на момент определения еще неизвестно, какой именно класс будет включать его в качестве базового и, тем самым, становиться от него производным.

Рассмотрим свойство полиморфности классов на основе следующего примера:

class A {
// Выводит, функция какого класса была вызвана
function Test() { echo "Test from A\n"; }
// Тестовая функция — просто переадресует на Test()
function Call() { Test(); }
}
class B extends A {
// Функция Test() для класса B
function Test() { echo "Test from B\n"; }
}
$a=new A();
$b=new B();
?>

Используем следующие следующие команды:

$a->Call(); // выводит "Test from A"
$b->Test(); // выводит "Test from B"
$b->Call(); // Внимание! Выводит "Test from B"!

Обратите внимание на последнюю строчку: вопреки ожиданиям, вызывается не функция Test() из класса A, а функция из класса B! Складывается впечатление, что Test() из B просто переопределила функцию Test() из A. Так оно на самом деле и есть. Функция, переопределяемая в производном классе, называется виртуальной.

Механизм виртуальных функций позволяет, например, "подсовывать" функциям, ожидающим объект одного класса, объект другого, производного, класса. Еще один классический пример — класс, воплощающий собой свойства геометрической фигуры, и несколько производных от него классов — квадрат, круг, треугольник и т. д. Базовый класс имеет виртуальную функцию Draw(), которая заставляет объект нарисовать самого себя. Все производные классы-фигуры, разумеется, переопределяют эту функцию (ведь каждую фигуру нужно рисовать по-особому). Также у нас есть массив фигур, причем мы не знаем, каких именно. Зато, используя полиморфизм, мы можем, не задумываясь, перебрать все элементы массива и вызвать для каждого из них метод Draw() — фигура сама "решит", какого она типа и как ее рисовать.

А вот еще один практический пример, показывающий свойство класса - полиморфизм:

class Base {
function funct() {
echo "Функция базового класса";
}
function base_funct() {
$this->funct();
}
}

class Derivative extends Base {
function funct() {
echo "Функция производного класса";
}
}

$b = new Base();
$d = new Derivative();

$b->base_funct();
$d->funct();
$d->base_funct();
// Скрипт выводит:

// Функция базового класса
// Функция производного класса
// Функция производного класса
?>

В рассмотренном примере функция base_funct() класса Base была перезаписана одноименной функцией класса Derivative.

Более подробно об ООП в РНР вы можете узнать тут: Сайт php.su

Святая святых РНР программиста: Сайт php.net