Об'єктно-орієнтоване програмування в PHP


Об'єктно-орієнтоване програмування в PHP

PHP (рекурсивний акронім словосполучення Hypertext Preprocessor – «препроцесор гіпертексту») – це скриптова мова програмування, яка використовується для розробки web-додатків (сценаріїв), що виконуються на боці web-сервера.
 

Ця мова була розроблена в 1994 році, її творцем є програміст Расмус Лердорф. Надалі PHP набув широкого поширення у всьому світі – безліч сторонніх розробників підключилися до роботи з вдосконалення мови. Таким чином, з початку створення цієї мови програмування вийшло вже декілька версій. Нові версії виходили щодекілька років. Так впродовж 2014 та 2015 років було розроблено нову основну версію PHP, яку було пронумеровано PHP 7. А вже 26 листопада 2020 року вийшла нова версія PHP - 8. Яка широко використовується і нині. У цій версії повністю реалізовані принципи об'єктно-орієнтованого програмування, перевагами якого (порівняно з процедурним стилем) є: висока мобільність, простота модифікування та супроводу програм, зниження ризику помилок під час програмування.

Метою написання даної статті є огляд основ об'єктно-орієнтованого програмування в мові програмування PHP.
Відповідно при даному огляді можна виділити такі завдання:
 

  1. розглянути поняття клас та об'єкт класу;
  2. вивчити принципи успадкування у PHP;
  3. ознайомитись із спеціальними методами для роботи з класами.

Клас як тип даних та об'єкти класу

Основними поняттями об'єктно-орієнтованого програмування є клас та об'єкт. Клас - це своєрідний тип даних, що визначається самим користувачем; шаблон, яким надалі створюватиметься об'єкт. Об'єкт – це певна сутність чи екземпляр класу, що має певний набір даних та методів. Дані описують властивості об'єкта і є його змінні, а методи – це функції, що належать об'єкту чи виконувані ним дії.
У мові PHP клас визначається за допомогою зарезервованого слова class, після якого необхідно вказати унікальне ім'я класу та тіло класу (список властивостей та методів) у фігурних дужках. Нові екземпляри класу створюються за допомогою ключового слова new, за яким слідує назва класу. Ім'ям класу та об'єкта може бути будь-яке не зарезервоване слово, яке має починатися з літери латинського алфавіту або символу підкреслення, за яким може йти будь-яка кількість літер, цифр або символів підкреслення.

Для отримання доступу до властивостей і методів об'єкта, використовують оператор -> (стрілка), а саме: спочатку вказують ім'я певного об'єкта, даний оператор, а потім назву властивості або методу, до якого необхідно звернутися.

Крім того, щоб звернутися до властивостей та методів всередині об'єкта, необхідно використовувати покажчик $this – це змінна, яка неявно є у кожному класі, є посиланням на поточний об'єкт класу.
Далі наведено приклад коду, у якому описано створення класу Person та екземпляра цього класу – об'єкта person. Для всіх майбутніх екземплярів класу встановлені властивості name (ім'я), age (вік) та методи setName(), setAge(), getName(), getAge(), які дозволяють заповнити відповідні поля об'єкта необхідними значеннями, а потім отримати їх за рахунок використання змінної-покажчика $this.

class Person {
public $name; 
public $age; 
public function setName($n){
$this->name = $n;
}
public function setAge($a){
$this->age = $a;
}
public function getName(){
return $this->name; 
}
public function getAge(){
return $this->age;
}
$person = new Person(); 
$person->setName('Maria');
$person->setAge('20');
echo 'name: '.$person->getName().', age: '.$person->getAge();
 

Підсумком виконання операції echo стане виведення рядка, що містить значення полів name та age.
Нерідко до створення об'єкта використовують спеціальний метод – конструктор (construct), який дозволяє виконати операції з ініціалізації тих чи інших властивостей об'єкта, і навіть викликати інші методи. Виклик конструктора відбувається автоматично щоразу під час створення екземпляра класу з допомогою оператора new.

Крім конструктора існує такий метод, як деструктор (destruct), який застосовується для видалення об'єкта та звільнення пам'яті, яку вони займають. Зазвичай його використовують для виконання різних службових функцій, наприклад закриття файлу, розрив з'єднання з базою даних і т.д. Деструктор може бути викликаний лише у двох випадках: коли всі посилання на об'єкт були закриті та після завершення роботи сценарію.
Нижче наведено приклад коду, що показує процес створення конструктора та деструктора для екземплярів класу Person; причому при завершенні роботи даного сценарію об'єкт person буде видалено.

class Person {
public $name; 
public $age;
public function __construct($n, $a) {
$this->name = $n;
$this->age = $a;
}
public function __destruct() {
echo ‘Об’єкт '.$this->name.' видалений <br />';
}
$person = new Person('Maria', '20');
 

Крім методів __construct() і __destruct(), починаючи з п'ятої версії PHP введені спеціальні модифікатори доступу – ця характеристика дозволяє контролювати, звідки саме до властивостей, методів і об'єктів можна отримувати доступ .
Область видимості може бути визначена одним із трьох ключових слів:
 

  1. public (загальнодоступні) – дозволяє звертатися до властивостей та методів як усередині, так і поза класом;
  2. protected (захищені) – вказує, що доступ можливий лише всередині самого класу або всередині його дочірніх класів;
  3. private (закриті) – у цьому разі елементи класу можеуть бути доступні лише усередині класу, у якому їх визначено.
     

На додаток до встановлення області видимості, елементи класу можуть бути оголошені як static (статичні). Статичні властивості та методи мають відношення не до окремого екземпляра, а до всього класу в цілому; таким чином, дані елементи класу доступні без створення об'єктів.

Принципи спадковості в PHP

Спадковість є одним із трьох найважливіших механізмів об'єктно-орієнтованого програмування, що дозволяє описати новий клас на основі вже існуючого; причому клас-спадкоємець (дочірній, похідний клас) успадковує властивості та методи суперкласу (батьківський або базовий клас). Крім того, класи-спадкоємці можуть продовжувати реалізацію деяких або всіх методів і властивостей батьківського класу, щоб забезпечити додаткові або функціональні можливості, що відрізняються.

Щоб клас міг успадкувати елементи іншого класу в PHP використовується оператор extends. У наступному прикладі показано створення дочірнього класу Student, який розширює клас Person за рахунок додавання властивості vuz (назва ВНЗ) та перевантаження методу showInfo(), який виводить рядок зі значеннями name і vuz, що належать деякому об'єкту класу Student.

class Person {
public $name; 
public $age;
public function __construct($n, $a) {
$this->name = $n;
$this->age = $a;
}
public function showInfo(){
echo 'name: '.$this->name. ', age: '.$this->age. '<br />';
}
class Student extends Person {
public $vuz;
function showInfo(){
echo $this->name. ' is student of '.$this->vuz. '<br />';
}
$person = new Person('Maria', '20');
$person->showInfo();
$stud = new Student('Ivan', '19');
$stud->vuz = 'NarFU';
$stud->showInfo();
 

Таким чином, якщо клас-нащадок містить властивість або метод, ім'я якого вже присутній в суперкласі, такий елемент буде перевизначеним, і при виклику цієї властивості або методу для об'єкта дочірнього класу будуть виконані саме ті функції, які описані в класі-нащадку.

Якщо виникає необхідність у тому, щоб підклас як звернувся до методів батьківського класу, а й реалізував нову функціональність без перевизначення даних методів, то цього використовують ключове слово parent разом із оператором дозволу видимості.

Далі наведено приклад, в якому показано реалізацію конструктора та методу showInfo() для класу-нащадка Student шляхом звернення та модифікування відповідних методів класу-батька Person.

class Person {
public $name; 
public $age;
public function __construct($n, $a) {
$this->name = $n;
$this->age = $a;
}
public function showInfo(){
echo '<br />name: '.$this->name.', age: '.$this->age;
}
class Student extends Person {
public $vuz;
function __construct($n, $a, $v){
parent::__construct($n, $a);
$this->vuz = $v;
}
function showInfo(){
parent::showInfo();
echo ', vuz: '.$this->vuz. '<br />';
}
$stud = new Student('Anna', '19', 'NarFU');
$stud->showInfo();
 

У результаті при викликі методу showInfo() для об'єкта stud буде виведено рядок, що містить значення поля vuz, а також значення полів name та age.
Також при проектуванні класів нерідко використовують класи-шаблони, у яких реалізовано лише часткову функціональність. Такі класи називаються абстрактними. Використання абстрактних класів дозволяє створити особливий суперклас, в якому визначені абстрактні характеристики, що несуть лише описовий зміст і не мають будь-якої функціональності. Проте, такі суперкласи можуть мати спадкоємців, у яких відбувається подальша реалізація абстрактних методів батька.

Клас вважається абстрактним, якщо його оголошення міститься ключове слово abstract. Крім того, якщо клас містить хоча б один абстрактний метод, весь клас повинен бути оголошений як абстрактний. Також у PHP передбачені деякі обмеження, які дозволяють уникнути помилок під час використання абстрактних класів. По-перше, абстрактний метод не можна викликати, якщо він не був перевизначений у похідному класі. По-друге, не можна створити екземпляр класу, який було оголошено абстрактним. І, по-третє, клас-спадкоємець обов'язково має реалізувати всі абстрактні методи класу-батька.
Нижче наведено приклад, що показує створення абстрактного класу Aperson, якому належить абстрактний метод showInfo(). Його спадкоємці – класи Student та Worker – реалізують даний метод, причому кожен по-своєму.

abstract class Aperson{
function __construct($n, $a){
$this->name = $n;
$this->age = $a;
}
abstract function showInfo();
}
class Student extends Aperson {
public $vuz;
function showInfo(){
echo $this->name. ' is student of '.$this->vuz.'<br />';
}
class Worker extends Aperson {
public $job;
function showInfo(){
echo $this->name.' works in '.$this->job.'<br />';
}
$stud = new Student('Ivan', '20');
$stud->vuz = 'NarFU';
$stud->showInfo();
$worker = new Worker('Olga', '30');
$worker->job = 'Gazprom';
$worker->showInfo();
 

Таким чином, при викликі методу showInfo() для екземпляра класу Student буде виведено рядок, що містить значення полів name і vuz, а для екземпляра класу Worker – рядок зі значеннями name та job.
У PHP існує таке поняття, як інтерфейс. Інтерфейс - це звичайний абстрактний клас, шаблон, що визначає функціональність (поведінку) всіх класів, які успадковують його. Як і у випадку з абстрактним класом, клас-спадкоємець, що реалізує інтерфейс, повинен містити в собі визначення всіх способів, заявлених в інтерфейсі У той же час є низка особливостей, що відрізняють інтерфейс від абстрактного класу. По-перше, інтерфейс не містить властивості екземплярів класу, лише їх методи. По-друге, всі методи, зазначені в інтерфейсі, є абстрактними, тому жоден метод не повинен бути описаний. І, по-третє, клас-спадкоємець може реалізовувати кілька інтерфейсів одночасно, таким чином забезпечується підтримка множинного успадкування.

Інтерфейс оголошують за допомогою ключового слова interface, а щоб клас реалізував інтерфейси, використовують слово implements, після якого вказують список імен інтерфейсів.
Далі наведено приклад коду, в якому показана реалізація інтерфейсу Iperson, що містить методи save() та info(), у класах Student та Worker.

interface Iperson {
public function save();
public function info();
}
class Student implements Iperson {
private $name;
private $vuz;
private $course;
public function __construct($n, $v, $c) {
$this->name = $n;
$this->vuz = $v;
$this->course = $c;
}
public function save() {
echo 'Student '.$this->name.' is saved<br />';
}
public function info () {
echo $this->name.' is student of '.$this->vuz;
}
class Worker implements Iperson {
private $name;
private $job;
private $career;
public function __construct($n, $j, $c) {
$this->name = $n;
$this->job = $j;
$this->career = $c;
}
public function save() {
echo 'Worker '.$this->name.' is saved<br />';
}
public function info () {
echo $this->name.' works as a '.$this->career.' in '.$this->job;
}
$stud = new Student('Ivan', 'NaftoGaz', '2');
$stud->info();
$worker = new Worker('Olga', 'UkrNafta', 'secretary');
$worker->info();
 

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

Спеціальні методи для роботи з класами

Крім спеціальних методів construct() і destruct(), конструктора і деструктора, у мові програмування PHP передбачено низку інших методів, призначених до роботи з класами, – так звані «магічні методи» (від англ. magic methods). Дані методи є досить гнучким інструментом – з допомогою програміст може істотно проводити поведінка об'єктів. До таких методів відносять:
 

  1. метод clone() разом із оператором clone дозволяє отримати точну копію клонованого об'єкта, тобто. всі значення властивостей об'єкта-клона будуть такими ж, як і у вихідного об'єкта. Якщо ж необхідно, щоб копія приймала власні значення деяких властивостей, для цього в описі класу перевизначають метод clone(). Наприклад,

class Person {
public $name; 
public $status;
function __construct($n, $s) {
$this->name = $n;
$this->status = $s;
}
function __clone(){
$this->status = ‘married’;
}
function info(){
echo 'name:'.$this->name.', status:'.$this->status.'<br/>';
}
$person1 = new Person('Alice','single');
$person1->info();
$person2 = clone $person1;
$person2->info();

У цьому випадку була створена копія об'єкта person1 – об'єкт person2, для якого метод __clone() перевизначив значення властивості status.
   2. метод toString() призначений для спрощення рядкового подання складного об'єкта, наприклад:

class Person {
public $name; 
function __construct($n) {
$this->name = $n;
}
function __toString(){
return $this->name;
}
$person1 = new Person('Alice');
echo $person1;

У цій ситуації виклик оператора echo не спричинить висновку фатальної помилки, т.к. при описі класу Person було визначено метод toString().
  3. реалізована в коді скрипта функція autoload() буде викликатись автоматично при використанні класів, які раніше не були визначені. Таким чином, ця функція – за допомогою операторів include(), include_once(), require() та require_once() – дозволяє завантажити файл, що містить визначення класу, до того, як виконання коду скрипта перерветься повідомленням про помилку.
  4. метод-одержувач get() буде викликаний у той час, коли відбудеться звернення до неіснуючому властивості об'єкта класу, а метод-установщик set() – коли неіснуючому властивості присвоюватиметься певне значення. Наприклад:

class Example {
function set($name, $val) {
echo 'Спроба присвоїти '.$val.' Змінною '.$name;
}
function get($name) {
echo 'Спроба звернутися до змінної '.$name;
}
$exam = новий Example();
$exam->number = 100;
echo $exam->txt;

Під час виконання цього коду буде виведено два рядки: «Спроба привласнити 100 змінної number» та «Спроба звернутися до змінної txt».
У свою чергу метод __call() викликається в той момент, коли відбувається звернення до неіснуючого методу. Наприклад:

class Example{
function __call($name, $params){
$string = implode(", ", $params);
echo 'Спроба викликати метод '.$name.' із параметрами: '.$string;
}
$exam = новий Example();
$exam->someMethod('param1','param2');

Цей скрипт виведе наступний рядок «Спроба викликати метод someMethod із параметрами: param1, param2».
Таким чином, методи get(), set і call() корисно використовувати у тих випадках, коли до початку виконання сценарію складно визначити весь список необхідних властивостей та методів. Зазвичай це відбувається під час роботи з Web-службами або контейнерними об'єктами.
   5. методи sleep() і wakeup безпосередньо пов'язані з функціями serialize та unserialize, а саме: метод sleep буде викликаний суворо перед тим, як об'єкт серіалізується (serialize), а метод __wakeup() – після того, як об'єкт десеріалізується (unserialize).
Зазвичай метод sleep() використовують для того, щоб повернути рядок – список полів класу, водночас деякі поля можуть бути виключені з рядкового представлення об'єкта. Метод wakeup() використовують для відновлення об'єкта або з'єднання з базою даних, яке було втрачено під час серіалізації.
Нижче наведено код скрипту, в якому показано приклад використання методів sleep() та wakeup().

class Person {
public $name;
public function __sleep() {
echo 'Виклик методу __sleep()…<br />';
return array('name');
}
public function __wakeup() {
echo 'Виклик методу __wakeup()…<br />';
}
$person1 = New Person();
$person1->name = 'Maria';
$str = serialize($person1);
echo 'Об'єкт переведений у рядок: '.$str. '<br />';
$person2 = unserialize($str);
echo 'Рядок переведено в об'єкт <br />';

У ході виконання даного прикладу було представлено основи об'єктно-орієнтованого програмування у мові PHP. При цьому було дано поняття клас та об'єкт класу, розглянуто правила оголошення класів та створення об'єктів класу, описано модифікатори доступу та їх призначення.

Крім того, були розглянуті основні конструкції мови, що використовуються при описі спадкування класів, були вивчені такі поняття, як абстрактні класи та інтерфейси, їх призначення та особливості використання.
У результаті виконання роботи було описано низку спеціальних «магічних» методів, які дозволяють проводити поведінка об'єктів під час виконання скрипта коду.

Вдалого навчання і розвитку у програмуванні. Даді буде…