ООП в PHP

Матеріал з Вікі ЦДУ
Перейти до: навігація, пошук

В програмуванні на PHP в основному використовуються такі парадигми програмування як процедурне і об'єктно-орієнтоване програмування.

Розглянемо ці парадигми детальніше:

Процедурне програмування.

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

Об'єктно-орієнтовне програмування (ООП).

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

Якщо розглядати поняття об'єкту концептуально, то об'єкт є лише екземпляром певного класу об'єктів.

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

Кожен об'єкт має свій певний тип (клас), що об'єднає в собі наступні елементи:

  1. Властивості – певні параметри і характеристики об'єкту
  2. Методи – дії, що можна виконувати над даним об'єктом, чи які може виконувати він сам
  3. Події – повідомлення що виникають при зміні стану об'єкта


ООП функціонує за наступними принципами:

  1. Наслідування – це можливість породжувати один клас від іншого, при чому всі методи і властивості батьківського класу передаються дочірньому, дочірній клас в свою чергу може набувати нових методів і властивостей, яких не було в батьківському і також передавати їх в похідні від себе класи.
  2. Поліморфізм – це можливість дочірнього класу змінювати реалізацію тих чи інших дій батьківського класу.
  3. Інкапсуляція – це властивість об'єкта мати спеціальний інтерфейс (певний метод), через який і здійснюється взаємодія зовнішнього середовища з внутрішніми методами класу. При чому зовнішнє середовище може і не підозрювати про структуру і логіку  внутрішніх методів.

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

Вказівники і клонування.

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

Для створення копії об'єкта в PHP 5 використовується спеціальний метод __clone(). До цього методу неможна звернутись безпосередньо. Для безпосереднього використання використовується ключове слово clone.

Розглянемо приклад використання цього методу в PHP 5:

   <?php
   class SomeClass
   {
       var $name;
   }
   // створюємо клас  someObj1 і присвоюємо властивісті “name” значення “Object 1”
   $someObj1 = new SomeClass;
   $someObj1->name = 'Object 1';
   // копіюємо клас  someObj1 в  someObj2
   $someObj2 = clone $someObj1;
       echo $someObj1->name; // Виводить “Object 1”
       echo $someObj2->name; // Виводить “Object 1”
   // присвоюємо властивісті класу  someObj2 “name” значення “Object 2”
   $someObj2->name = 'Object 2';
       echo $someObj1->name; // Виводить “Object 1”
       echo $someObj2->name; // Виводить “Object 2”
   ?>

В PHP 4 для копіювання об'єкту замість $someObj2 = clone $someObj1 треба було б використати $someObj2 = $someObj1.

На перший погляд все зручніше виглядало в PHP 4, але не будемо поспішати з висновками. Метод __clone() не обов'язково описувати (перезавантажувати) в класі. Але якщо ми це зробимо, то зможемо наперед задати поведінку об'єкта під час його клонування.

Приклад:

   <?php
   class SomeClass
   {
       var $name;

       // перезавантаження методу __clone()
       function __clone()
       {
           $this->name = 'Copy';
       }
   }
       // створюємо клас  someObj1 і присвоюємо властивісті “name” значення “Original”
       $someObj1 = new SomeClass;
       $someObj1->name = 'Original';
       // створюємо клас  someObj2
       // автоматично властивості “name” присвоюється значення “Copy”
       $someObj2 = clone $someObj1;
           echo $someObj1->name; // Виводить “Original”
           echo $someObj2->name; // Виводить “Copy”
   ?>

Області видимості методів і властивостей класів. В PHP 5 введені специфікатори доступу до методів і властивостей класів:

   * public – доступний без обмежень
   * protected – тільки в середині класу, в якому вони оголошені і похідних від нього класах
   * private – тільки в середині класу, в якому вони оголошені


По замовчуванню виставляється тип доступу public.

Розглянемо приклад з методами і властивостями різних типів доступності.

   <?php
       // оголошуємо основний клас
   class SomeClass
   {
       private $privateName = 'Private Name';
       protected $protectedName = 'Protected Name';
       public $publicName = 'Public Name';

   private function PrivateFunction()
   {
       echo 'PrivateFunction()';
   }

   protected function ProtectedFunction()
   {
       echo 'ProtectedFunction()';
   }

   public function PublicFunction()
   {
       echo 'PublicFunction()';
   }

   public function GetPrivate()
   {
       echo $this->privateName;
   }

   public function GetProtected()
   {
       echo $this->protectedName;
   }

   public function GetPublic()
   {
       echo $this->publicName;
    }
   }


       // оголошуємо похідний клас від SomeClass
   class SomeClass1 extends SomeClass
   {
       public function GetPrivateFromParent()
       {
           echo $this->privateName;
       }
   }
   $someObj1 = new SomeClass;
   $someObj2 = new SomeClass1;
   // Доступ до private методів і властивостей
   echo $someObj1->privateName; //  Виводить помилку
   echo $someObj2->privateName; //  Виводить помилку
   $someObj1->PrivateFunction(); //  Виводить помилку
   $someObj2->PrivateFunction(); //  Виводить помилку
   $someObj1->GetPrivate(); //  Виводится 'Private Name'
   $someObj2->GetPrivate(); //  Виводится 'Private Name'*/
   $someObj2->GetPrivateFromParent(); //  Нічого не виводиться
   // Доступ до protected методів і властивостей
   echo $someObj1->protectedName; // Виводить помилку
   echo $someObj2->protectedName; // Виводить помилку
   $someObj1->ProtectedFunction(); // Виводить помилку
   $someObj2->ProtectedFunction(); // Виводить помилку
   $someObj1->GetProtected(); // Виводится 'Protected Name'
   $someObj2->GetProtected(); // Виводится 'Protected Name'
   // Доступ до public методів і властивостей
   echo $someObj1->publicName; // Виводиться 'Public Name'
   echo $someObj2->publicName; // Виводиться 'Public Name'
   $someObj1->PublicFunction(); // Виводиться 'PublicFunction()'
   $someObj2->PublicFunction(); // Виводиться 'PublicFunction()'
   $someObj1->GetPublic(); // Виводиться 'Public Name'
   $someObj2->GetPublic(); // Виводиться 'Public Name'
   ?>

Є ще також статичні (static) методи і властивості. Їхньою особливістю є те, що вони не належать певному об'єктові, вони єдині для цілого класу, і можуть викликатися без створення об'єкту. Зміна статичної властивості в одному з об'єктів класу призводить до його зміни для всіх об'єктів цього класу.

Приклад:

   <?php
     // оголошуємо основний клас
   class SomeClass
   {
       static $staticVar = 'Static Variable';

       static function StaticFunction()
       {
           echo 'StaticFunction()';
       }
   }
   echo SomeClass::StaticFunction(); // Виводить StaticFunction()
   echo SomeClass::$staticVar; // Виводить Static Variable
   ?>


Константи класу При описі класу в PHP 5 можна задавати властивості-константи (ключове слово const). Викликати константи можна і без створення об'єкту на основі класу, в якому оголошені константи.

Приклад:

   <?php
   class SomeClass
   {
         const SOME_CONSTANT = "SOME CONSTANT";
   }
   echo SomeClass::SOME_CONSTANT; // Виводить "SOME CONSTANT"
   ?>


Конструктори и деструктори.


Конструктор (__construct()) і деструктор (__destruct()) це методи що викликаються автоматично при створенні і знищенні об'єкту відповідно:

<?php class SomeClass{

 function __construct() {
   echo 'Create object';
 }

 function __destruct() {
   echo 'Destroy object';
 }

}

$someObj = new SomeClass; // Виводиться Create object unset($someObj); // Виводиться Destroy object ?> Абстрактні методи і класи. Абстрактні (abstract) методи і класи тільки оголошуються, клас який містить абстрактні методи повинен оголоситися як абстрактний. На основі абстрактного класу можна тільки створювати інші класи, а вже від них об'єкти. Абстрактний клас може містити і звичайні (не абстрактні) елементи.

Приклад: <?php abstract class SomeAbstractClass {

 // оголошення абстрактної функції
 abstract public function abstractFunction();

 // оголошення неабстрактної функції
 public function GeneralFunction() {

 }

}

class SomeClass extends SomeAbstractClass {

 // перевантаження абстрактного методу
 public function abstractFunction() {
   echo 'abstractFunction()';
 }

}

$someObj = new SomeAbstractClass; // Помилка створення об'єкта $someObj1 = new SomeClass; $someObj1->abstrFunc(); // Виводить 'abstractFunction()' ?> Інтерфейси. В PHP 5 немає множинного наслідування, тобто один клас не може бути створений на основі кількох інших класів. Але клас може бути створений на основі кількох інтерфейсів. Інтерфейс – це фактично абстрактний клас, який містить тільки абстрактні методи і не містить ніяких властивостей.

Оголошуються інтерфейси з використанням ключового слова interface, а всі функції оголошуються стандартно, з використанням ключового слова function.

Приклад: <?php // оголошення інтерфейсів interface InterfaceOne {

 function SomeFunctionOne();

}

interface InterfaceTwo {

 function SomeFunctionTwo();

}

// оголошення класу на основі інтерфейсів class SomeClass implements InterfaceOne, InterfaceTwo {

 public function SomeFunctionOne() {
   echo 'SomeFunctionOne()';
 }
 public function SomeFunctionTwo() {
   echo 'SomeFunctionTwo()';
 }

}

$object = new SomeClass; $object->SomeFunctionOne(); // Виводить 'SomeFunctionOne()' $object->SomeFunctionTwo(); // Виводить 'SomeFunctionTwo()' ?> Фінальні методи і класи. В PHP 5 є можливість задати таку властивість класу і методу як фінальний (final). На основі фінальних класів неможливо створити класи нащадки. Також не можна перевизначити фінальний метод в класах нащадках.

Приклад: <?php final class FinalClass {

}

class ClassWithFinalMethod {

 final public function FinalFunction() {
   echo 'FinalFunction()';
 }

}

// наступне оголошення класу викликає помилку class SomeClass1 extends FinalClass {

 // опис класу

}

// створюємо клас на основі класу з фінальним методом class SomeClass1 extends ClassWithFinalMethod {

 // наступне перевизначення методі викликає помилку
 public function FinalFunction() {
 }

} ?> Обробка винятків (помилок). Найцікавішим нововведенням в PHP 5 є методи для обробки винятків. Для цього використовуються конструкції try/catch/throw.

Розглянемо простий приклад використання цих методів: <?php

 try {
   // відкриваємо файл для читання
   $fp = @fopen("somefile.txt", "r");
     // якщо файл відсутній, створюємо виключення
     if (!$someFile)
       throw new Exception(" Помилка відкриття файлу!");
     
     fclose($someFile);
 } catch (Exception $exception) {
     // метод $exception->getLine() повертає номер рядка даного скрипта, в якому виникла помилка
     echo "Помилка в стрічці ", $exception->getLine();
     echo $exception->getMessage(); // Выводит "Помилка відкриття файлу!"
 }

?> Ключове слово instanceof. Метод instanceof дозволяє визначити походження об'єкта, його приналежність до певного класу, або чи є він нащадком якогось об'єкта. Також за допомогою instanceof можна визначити чи об'єкт екземпляром класу, створеного на основі певного інтерфейсу.

Приклад: <?php

 interface SomeInterfaceOne { }
 interface SomeInterfaceTwo { }
 class SomeClassTypeOne { }
 class SomeClassTypeTwo extends SomeClassTypeOne {}
 class SomeClassTypeThree implements SomeInterfaceOne, SomeInterfaceTwo {}

 $objOne = new SomeClassTypeOne;
 $objTwo = new SomeClassTypeTwo;
 $objThree = new SomeClassTypeThree;
 $clonedObj = clone $objThree;
 // наступний блок виводить: об'єкт $objOne належить до класу SomeClassTypeOne
 if($objOne instanceof SomeClassTypeOne)
   echo 'об\'єкт $objOne належить до класу SomeClassTypeOne';

 // наступний блок виводить: об'єкт $objTwo належить до класу SomeClassTypeTwo
 if($objTwo instanceof SomeClassTypeTwo)
   echo 'об\'єкт $objTwo належить до класу SomeClassTypeTwo';

 // наступний блок виводить: об'єкт $objThree належить до класу SomeClassTypeThree
 if($objThree instanceof SomeClassTypeThree)
   echo 'об\'єкт $objThree належить до класу SomeClassTypeThree';

 // наступний блок виводить: об'єкт $objTwo є екземпляром класу створеного на основі класу SomeClassTypeOne
 if($objTwo instanceof SomeClassTypeOne)
   echo 'об\'єкт $objTwo є екземпляром класу створеного на основі класу SomeClassTypeOne';

 // наступний блок виводить: об'єкт $objThree є екземпляром класу створеного на основі інтерфейсу SomeInterfaceOne
 if($objThree instanceof SomeInterfaceOne)
   echo 'об\'єкт $objThree є екземпляром класу створеного на основі інтерфейсу SomeInterfaceOne';

 // наступний блок виводить: об'єкт $objThree є екземпляром класу створеного на основі інтерфейсу SomeInterfaceTwo   
 if($objThree instanceof SomeInterfaceTwo)
   echo 'об\'єкт $objThree є екземпляром класу створеного на основі інтерфейсу SomeInterfaceTwo';

 // наступний блок виводить: об'єкта $clonedObj створений на основі об'єкту $objThree   
 if($clonedObj instanceof $objThree)
   echo 'об\'єкта $clonedObj створений на основі об\'єкту $objThree';

?> Функція __autoload() Функція __autoload() викликається в випадку коли створюється об'єкт на основі неіснуючого класу

Приклад: <?php

 function __autoload($class) {
   echo "спроба створити об'єкт невизначеного класу ", $class;
 }

 // наступна стрічка виводить:
 // спроба створити об'єкт невизначеного класу MyClass
 // Fatal error: Class 'MyClass' not found in C:\wamp\www\test.php on line 5
 $someObj = new SomeWrongClass;

 // наступна стрічка трохи практичніша
 // (екрануємо створення об'єту символом "@")
 // вона виводить:
 // спроба створити об'єкт невизначеного класу MyClass
 $someObj = @new SomeWrongClass;

?> Перевантаження доступу до властивостей об'єкту. Методи доступу __get() і __set() дозволяють динамічно визначати властивості об'єктів. __get() в якості параметра отримує ім'я властивості, а __set() окрім імені ще і нове значення властивості, яке відповідно і присвоює.

Приклад: <?php

 class SomeClass {
   private $classPropertys;
   function __set($name, $value) {
     echo "__set: присвоювання властивості $name = $value";
     $this->classPropertys[$name]=$value;
   }
  
   function __get($name) {
     echo "__get: читання властивості $name: ";
     echo $this->classPropertys[$name];
   }
 }

 $obj = new SomeClass;
 $obj->name = 'New Value'; // Виводить "__set: присвоювання властивості name=New Value"
 $value = $obj->name; // Виводить "__get: читання властивості name: New Value"

?> Перевантаження викликів методів класу. Якщо метод __call() описаний в певному класі, тоді він автоматично переловлює виклики до неіснуючих методів цього класу. В якості параметрів він отримує ім'я і параметри методу що викликається.

Приклад: <?php

 class SomeClass {
   function __call($name, $params) {
     echo "Помилка: викликано неіснуючий метод $name з параметром[ами]: ";
     foreach($params as $val) {
       echo $val.' | ';
     }
   }
 }

 $obj = new SomeClass;
 $obj->WrongMethodOne(1, 2, 'param'); // Виводить: 'Помилка: викликано неіснуючий метод WrongMethodOne з параметром[ами]: 1 | 2 | param |'
 $obj->WrongMethodTwo(123); // Виводить: 'Помилка: викликано неіснуючий метод WrongMethodOne з параметром[ами]: 123 |'

?>