Skip to content

5. Об’єкти

5.1. Будь-яка змінна може стати об’єктом, що має атрибути (exemple_14)


<?php

// будь-яка змінна за своєю суттю може мати атрибути
$obj1->attr1 = "un";
$obj1->attr2 = 100;
// виводить об’єкт
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
// змінює об’єкт
$obj1->attr2+=100;
// відображає об’єкт
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
// присвоює значення об’єкта1 об’єкту2
$obj2 = $obj1;
// змінює об’єкт2
$obj2->attr2 = 0;
// виводить обидва об’єкти
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
print "objet2=[$obj2->attr1,$obj2->attr2]\n";
// присвоює посилання на об’єкт1 об’єкту3
$obj3 = &$obj1;
// змінює об’єкт3
$obj3->attr2 = 10;
// відображає обидва об’єкти
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
print "objet3=[$obj3->attr1,$obj3->attr2]\n";
// чи є об’єкт словником?
print count($obj1)."\n";
while (list($attribut, $valeur) = each($obj1))
  print "obj1[$attribut]=$valeur\n";
// кінець
exit;
?>

Результати:

objet1=[un,100]
objet1=[un,200]
objet1=[un,0]
objet2=[un,0]
objet1=[un,10]
objet3=[un,10]
1
obj1[attr1]=un
obj1[attr2]=10

Коментарі

  • рядок 4: запис $obj->attr позначає атрибут attr змінної $obj. Якщо його не існує, він створюється, перетворюючи змінну $obj на об’єкт з атрибутами.
  • рядок 13: вираз $obj2=$obj1, коли $obj1 є об’єктом, є копіюванням об’єктів за посиланням. Отже, $obj2 та $obj1 є посиланнями на один і той самий об’єкт. Сам об’єкт може бути змінений за допомогою будь-якого з цих посилань.
  • рядок 20: вираз $obj3=&$obj1 присвоює $obj3 посилання (адресу) на змінну $obj1. Якщо $obj1 є об’єктом, як у цьому випадку, то попередній вираз еквівалентний $obj3=$obj1.
  • рядки 28–29: показують, що об’єкт можна обходити як словник. Ключами словника є імена атрибутів, а значеннями — значення цих самих атрибутів.
  • рядок 27: функцію count можна застосувати до об’єкта, але вона не повертає, як можна було б очікувати, кількість атрибутів. Отже, об’єкт має схожість зі словником, але не є ним.

5.2. Клас «Особа» без оголошених атрибутів (exemple_15)


<?php

class Personne {

  // атрибути класу
  // не оголошені — можуть створюватися динамічно
  
  // метод
  function identité() {
    // на перший погляд, використовує атрибути, яких не існує
    return "[$this->prénom,$this->nom,$this->âge]";
  }
}

// тест
// атрибути є загальнодоступними і можуть створюватися динамічно
$p = new Personne();
$p->prénom = "Paul";
$p->nom = "Langevin";
$p->âge = 48;
// виклик методу
print "personne=" . $p->identité() . "\n";
// кінець
exit;
?>

Результати:

personne=[Paul,Langevin,48]

Коментарі

  • рядки 3–13: визначають клас Personne. Клас — це шаблон, на основі якого створюються об’єкти. Клас — це сукупність атрибутів і функцій, які називаються методами. Оголошувати атрибути не обов’язково.
  • рядки 9–12: метод identité виводить значення трьох атрибутів, не оголошених у класі. Ключове слово $this позначає об’єкт, до якого застосовується метод.
  • рядок 17: створюється об’єкт $p типу Personne. Ключове слово new використовується для створення нового об’єкта. Ця операція повертає посилання на створений об’єкт (тобто адресу). Можливі різні варіанти запису: new Personne(), new Personne, new personne. У назві класу не враховується регістр.
  • рядки 18–20: у об’єкті $p створюються три атрибути, необхідні для методу identité.
  • рядок 22: метод identité класу Personne застосовується до об’єкта $p. У коді (рядки 9–11) методу identité $this посилається на той самий об’єкт, що й $p.

5.3. Клас «Особа» з оголошеними атрибутами (exemple_16)


<?php

class Personne {

  // атрибути класу
  var $prénom;
  var $nom;
  var $âge;

  // метод
  function identité() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
}
// тест
// атрибути є загальнодоступними
$p = new Personne();
$p->prénom = "Paul";
$p->nom = "Langevin";
$p->âge = 48;
// виклик методу
print "personne=" . $p->identité() . "\n";
// кінець
exit;
?>

Результати:

personne=[Paul,Langevin,48]

Коментарі

  • рядки 6–8: атрибути класу явно оголошені за допомогою ключового слова var.

5.4. Клас Personne з конструктором (exemple_17)

Попередні приклади демонстрували нестандартні класи Personne, подібні до тих, що містяться у PHP 4. Не рекомендується слідувати цим прикладам. Тепер ми представимо клас Personne, що відповідає найкращим практикам PHP 5:


<?php

class Personne {

// атрибути класу
  private $prénom;
  private $nom;
  private $âge;

// методи getter та setter
  public function getPrénom() {
    return $this->prénom;
  }

  public function getNom() {
    return $this->nom;
  }

  public function getAge() {
    return $this->âge;
  }

  public function setPrénom($prénom) {
    $this->prénom = $prénom;
  }

  public function setNom($nom) {
    $this->nom = $nom;
  }

  public function setAge($age) {
    $this->âge = $age;
  }

// конструктор
  function __construct($prénom, $nom, $âge) {
    // використовуємо методи set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

// метод toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

// тест
// створення об’єкта «особа»
$p = new Personne("Paul", "Langevin", 48);
// ідентифікаційні дані цієї особи
print "personne=$p\n";
// зміна віку
$p->setAge(14);
// ідентифікаційні дані особи
print "personne=$p\n";
// кінець
exit;
?>

Результати:

personne=[Paul,Langevin,48]
personne=[Paul,Langevin,14]

Коментарі

  • рядки 3–48: клас Personne
  • рядки 6–8: приватні (private) атрибути класу. Ці атрибути видимі лише всередині класу. Інші ключові слова, які можна використовувати:
  • public: робить атрибут публічним, видимим ззовні класу
  • protected: робить атрибут захищеним, видимим зсередини класу та його похідних класів.
  • Оскільки атрибути є приватними, доступ до них ззовні класу неможливий. Тому не можна написати такий код:
$p=new Personne() ;
$p->nom="Landru" ;

Тут ми знаходимося поза класом Personne. Оскільки атрибут nom є приватним, рядок 2 є некоректним. Щоб ініціалізувати приватні поля об’єкта $p, існує два способи:

  • (продовження)
    • використати публічні методи set та get (назви цих методів можуть бути будь-якими) у рядках 11–33. Тоді можна буде написати:
$p=new Personne() ;
$p->setNom("Landru") ;
  • (продовження)
    • використати конструктор у рядках 36–41. Тоді можна написати:
$p=new Personne("Michel","Landru",44) ;

Наведений вище запис автоматично викликає метод класу Personne під назвою __construct.

  • рядок 54: цей рядок відображає особу $p у вигляді символьного рядка. Для цього використовується метод класу Personne, який називається __toString.

5.5. Клас «Особа» з перевіркою правильності в конструкторі (exemple_18)

Конструктор класу — це ідеальне місце для перевірки правильності значень ініціалізації об’єкта. Однак об’єкт також можна ініціалізувати за допомогою його методів set або еквівалентних. Щоб уникнути дублювання одних і тих самих перевірок у двох різних місцях, їх слід розмістити в методах set. Якщо значення ініціалізації об’єкта виявиться неправильним, буде згенеровано виняток. Ось приклад:


<?php

class Personne {

// атрибути класу
  private $prénom;
  private $nom;
  private $âge;
  
// методи getter та setter
  public function getPrénom() {
    return $this->prénom;
  }

  public function getNom() {
    return $this->nom;
  }

  public function getAge() {
    return $this->âge;
  }

  public function setPrénom($prénom) {
    // ім'я має бути не порожнім
    if (!preg_match("/^\s*(.+?)\s*$/", $prénom, $champs)) {
      throw new Exception("Le prénom doit être non vide");
    } else {
      $this->prénom = $champs[1];
    }
  }

  public function setNom($nom) {
    // прізвище не може бути порожнім
    if (!preg_match("/^\s*(.+?)\s*$/", $nom, $champs)) {
      throw new Exception("Le nom doit être non vide");
    } else {
      $this->nom = $champs[1];
    }
  }

  public function setAge($âge) {
    // вік має бути дійсним
    if (!preg_match("/^\s*(\d+)\s*$/", $âge, $champs)) {
      throw new Exception("L'âge doit être un entier positif ou nul");
    } else {
      $this->âge = $champs[1];
    }
  }

// конструктор
  function __construct($prénom, $nom, $âge) {
    // використовуються методи set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // метод toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
}

// тест
// створення об’єкта «Особа»
$p = new Personne("Paul", "Langevin", 48);
// ідентифікаційні дані цієї особи
print "personne=$p\n";
// створення помилкового об’єкта «Особа»
try {
  $p = new Personne("xx", "yy", "zz");
} catch (Exception $e) {
  print $e->getMessage();
}
// кінець
exit;
?>

Результати:

personne=[Paul,Langevin,48]
L'âge doit être un entier positif ou nul

Коментарі

  • рядки 23–30: ініціалізація атрибута prénom та перевірка значення ініціалізації
  • рядок 25: використання регулярного виразу. Ім’я — це послідовність одного або кількох символів, за якими можуть йти або перед якими можуть стояти пробіли.
  • рядок 26: якщо прізвище порожнє, генерується виняток, інакше ім’я зберігається (рядок 28)
  • рядок 66: використання конструктора класу Personne
  • рядок 68: використання методу __toString класу Personne
  • рядки 70–74: обробка можливого винятку, згенерованого конструктором класу Personne.

5.6. Додавання методу, що виконує роль другого конструктора (exemple_19)

У PHP 5 неможливо мати кілька конструкторів з різними параметрами, що дозволяли б створювати об’єкт різними способами. Тому можна використовувати методи, що виконують роль конструктора:


<?php

class Personne {

// атрибути класу
  private $prénom;
  private $nom;
  private $âge;
  
// методи getter та setter
  public function getPrénom() {
    return $this->prénom;
  }

….

// конструктор
  function __construct($prénom, $nom, $âge) {
    // використовуємо set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // метод
  public function initWithPersonne($p) {
    // ініціалізує поточний об’єкт за допомогою особи $p
    $this->__construct($p->prénom, $p->nom, $p->âge);
  }

  // метод toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

// тест
// створення об’єкта «Особа»
$p = new Personne("Paul", "Langevin", 48);
// ідентифікаційні дані цієї особи
print "personne=$p\n";
// створення другої особи
$p2 = new Personne("Laure", "Adeline", 67);
// ініціалізація першого об’єкта значеннями другого
$p->initWithPersonne($p2);
// перевірка
print "personne=$p\n";
// кінець
exit;
?>

Результати:

personne=[Paul,Langevin,48]
personne=[Laure,Adeline,67]

Коментарі

  • рядки 26–29: метод initWithPersonne дозволяє присвоїти поточному об’єкту значення атрибутів іншого об’єкта Personne. Тут він використовує конструктор __construct, але це не є обов’язковим.

5.7. Масив об’єктів «Особа» (exemple_20)

Наступний приклад показує, що можна мати масиви об’єктів.


<?php

class Personne {

// атрибути класу
  private $prénom;
  private $nom;
  private $âge;
  
// методи getter та setter
  public function getPrénom() {
    return $this->prénom;
  }

...

// конструктор
  function __construct($prénom, $nom, $âge) {
    // використовуємо set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // метод
  public function initWithPersonne($p) {
….
  }

  // метод toString
  function __toString() {
    ...
  }

}

// тест
// створення масиву об’єктів «Особа»
$groupe = array(new Personne("Paul", "Langevin", 48), new Personne("Sylvie", "Lefur", 70));

// ідентифікація цих осіб
for ($i = 0; $i < count($groupe); $i++)
  print "groupe[$i]=$groupe[$i]\n";
// кінець
exit;
?>

Результати:

groupe[0]=[Paul,Langevin,48]
groupe[1]=[Sylvie,Lefur,70]

Коментарі

  • рядок 39: створення масиву з 2 осіб
  • рядок 42: обхід масиву
  • рядок 43: $groupe[$i] є об’єктом типу Personne. Для його відображення використовується метод __toString.

5.8. Створення класу, похідного від класу «Особа» (exemple_21)


<?php

class Personne {

// атрибути класу
  private $prénom;
  private $nom;
  private $âge;
  
// методи getter та setter
  public function getPrénom() {
    return $this->prénom;
  }
...

// конструктор
  function __construct($prénom, $nom, $âge) {
    // використовуємо set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // метод
  public function initWithPersonne($p) {
    // ініціалізує поточний об’єкт за допомогою особи $p
    $this->__construct($p->prénom, $p->nom, $p->âge);
  }

  // метод toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

// клас, похідний від «Особа»
class Enseignant extends Personne {

  // атрибути
  private $discipline;   // предмет, який викладає

  // геттери та сеттери

  public function getDiscipline() {
    return $this->discipline;
  }

  public function setDiscipline($discipline) {
    $this->discipline = $discipline;
  }

  // конструктор

  public function __construct($prénom, $nom, $âge, $discipline) {
    // атрибути батьківського класу
    parent::__construct($prénom, $nom, $âge);
    // інші атрибути
    $this->setDiscipline($discipline);
  }

  // перевантаження функції __toString батьківського класу
  public function __toString() {
    return "[" . parent::__toString() . ",$this->discipline]";
  }

}

// тест
// створення таблиці об’єктів «Особа» та похідних
$groupe = array(new Enseignant("Paul", "Langevin", 48, "anglais"), new Personne("Sylvie", "Lefur", 70));

// ідентифікація цих осіб
for ($i = 0; $i < count($groupe); $i++)
  print "groupe[$i]=$groupe[$i]\n";
// кінець
exit;
?>

Результати:

groupe[0]=[[Paul,Langevin,48],anglais]
groupe[1]=[Sylvie,Lefur,70]

Коментарі

  • рядки 3–35: клас Personne
  • рядок 38: клас Enseignant походить (extends) від класу Personne. Похідний клас Enseignant успадковує атрибути та методи свого батьківського класу.
  • рядок 41: клас Enseignant додає новий атрибут discipline, який є властивим лише йому
  • рядок 55: конструктор класу Enseignant приймає 4 параметри
    • 3 — для ініціалізації батьківського класу (ім’я, прізвище, вік)
    • 1 — для власної ініціалізації (предмет)
  • рядок 57: похідний клас має доступ до методів і конструкторів свого батьківського класу за допомогою ключового слова parent ::. Тут параметри (ім'я, прізвище, вік) передаються конструктору батьківського класу.
  • рядок 64: метод __toString похідного класу використовує метод __toString батьківського класу.
  • рядок 71: масив, що містить об’єкт Enseignant та об’єкт Personne.
  • рядок 74: виконується цикл по елементах масиву
  • рядок 75: буде викликано метод __toString кожного елемента $groupe[$i]. Клас Personne має метод __toString. Клас Enseignant має два методи: метод свого батьківського класу та власний. Можна задатися питанням, який із них буде викликано. Виконання показує, що було викликано метод класу Enseignant. Так відбувається завжди: коли метод викликається на об’єкті, його шукають у такому порядку: у самому об’єкті, у його батьківському класі, якщо такий є, потім у батьківському класі батьківського класу тощо… Пошук припиняється, щойно метод знайдено.

5.9. Створення другого класу, похідного від класу Personne (exemple_22)

У наведеному нижче прикладі створюється клас Etudiant, похідний від класу Personne:


<?php

class Personne {

// атрибути класу
  private $prénom;
  private $nom;
  private $âge;

// методи getter та setter
  public function getPrénom() {
    return $this->prénom;
  }
...

// конструктор
  function __construct($prénom, $nom, $âge) {
    // використовуємо set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // метод
  public function initWithPersonne($p) {
    // ініціалізує поточний об’єкт за допомогою особи $p
    ...
  }

  // метод toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

// клас, похідний від класу «особа»
class Enseignant extends Personne {

  // атрибути
  private $discipline;   // предмет, який викладає

  // геттери та сеттери
...

  // конструктор

  public function __construct($prénom, $nom, $âge, $discipline) {
    // атрибути батьківського класу
    parent::__construct($prénom, $nom, $âge);
    // інші атрибути
    $this->setDiscipline($discipline);
  }

  // перевантаження функції _identité__toString батьківського класу
  public function __toString() {
    return "[" . parent::__toString() . ",$this->discipline]";
  }

}

// клас, похідний від класу «Особа»
class Etudiant extends Personne {

  // атрибути
  private $formation;   // предмет, який викладає

  // геттери та сеттери

  public function getFormation() {
    return $this->formation;
  }

  public function setFormation($formation) {
    $this->formation = $formation;
  }

  // конструктор

  public function __construct($prénom, $nom, $âge, $formation) {
    // атрибути батьківського класу
    parent::__construct($prénom, $nom, $âge);
    // інші атрибути
    $this->setFormation($formation);
  }

  // перевантаження функції __toString батьківського класу
  public function __toString() {
    return "[" . parent::__toString() . ",$this->formation]";
  }

}

// тест
// створення масиву об’єктів типу «особа» та похідних
$groupe = array(new Enseignant("Paul", "Langevin", 48, "anglais"), new Personne("Sylvie", "Lefur", 70), new Etudiant("Steve", "Boer", 23, "iup2 qualité"));

// ідентифікація цих осіб
for ($i = 0; $i < count($groupe); $i++)
  print "groupe[$i]=$groupe[$i]\n";
// кінець
exit;
?>

Результати:

groupe[0]=[[Paul,Langevin,48],anglais]
groupe[1]=[Sylvie,Lefur,70]
groupe[2]=[[Steve,Boer,23],iup2 qualité]

5.10. Інтерфейси (exemple_23)

Інтерфейс — це структура, що визначає прототипи методів. Клас, що реалізує інтерфейс, визначає код усіх методів цього інтерфейсу.


<?php

// інтерфейс
interface IExemple {
  function ajouter($i, $j);

  function soustraire($i, $j);
}

// реалізація 1 інтерфейсу
class Classe1 implements IExemple {

  public function ajouter($a, $b) {
    return $a + $b + 10;
  }

  public function soustraire($a, $b) {
    return $a - $b + 20;
  }

}

// реалізація 2 інтерфейсу
class Classe2 implements IExemple {

  public function ajouter($a, $b) {
    return $a + $b + 100;
  }

  public function soustraire($a, $b) {
    return $a - $b + 200;
  }

}

// функція
function calculer($a, $b, IExemple $interface) {
  print $interface->ajouter($a, $b)."\n";
  print $interface->soustraire($a, $b)."\n";
}

// ------------------- main
// створення 2 об’єктів типів Classe1 та Classe2
$c1 = new Classe1();
$c2 = new Classe2();
// виклик функції «calculer»
calculer(4, 3, $c1);
calculer(14, 13, $c2);
?>

Результати

17
21
127
201

Коментарі

  • рядки 4–8: інтерфейс IExemple визначає два методи: функцію ajouter (рядок 5) та функцію soustraire (рядок 7). Інтерфейс не визначає код цих методів. Це роблять класи, що реалізують інтерфейс.
  • рядок 11: клас Classe1 реалізує (implements) інтерфейс IExemple. Отже, він визначає код для методів ajouter (рядок 13) та soustraire (рядок 17).
  • рядки 24–34: те саме стосується класу Classe2
  • рядок 37: метод calculer приймає три параметри: два цілі числа $a та $b і об’єкт $interface, що реалізує інтерфейс IExemple. Можна було б написати function calculer($a, $b, $interface) без вказівки типу IExemple для параметра $interface. Вказавши його, ми даємо змогу інтерпретатору PHP перевірити, чи дійсно фактичний параметр є об’єктом, що реалізує інтерфейс IExemple.
  • рядки 38–39: використання методів ajouter та soustraire об’єкта $interface. Оскільки цей об’єкт реалізує інтерфейс IExemple, відомо, що він має ці два методи.
  • рядки 44–45: створення двох об’єктів різних типів, які, однак, обидва реалізують інтерфейс IExemple.
  • рядок 47: об’єкт $c1 типу Classe1, що реалізує інтерфейс IExemple, передається до функції calculer.
  • рядок 48: відбувається передача до функції calculer, об’єкт $c2 типу Classe2 реалізує інтерфейс IExemple.