Skip to content

5. Obiekty

5.1. Każda zmienna może stać się obiektem posiadającym atrybuty (exemple_14)


<?php

// każda zmienna może z założenia posiadać atrybuty
$obj1->attr1 = "un";
$obj1->attr2 = 100;
// wyświetla obiekt
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
// modyfikuje obiekt
$obj1->attr2+=100;
// wyświetla obiekt
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
// przypisuje wartość obiektu1 do obiektu2
$obj2 = $obj1;
// modyfikuje obiekt2
$obj2->attr2 = 0;
// wyświetla oba obiekty
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
print "objet2=[$obj2->attr1,$obj2->attr2]\n";
// przypisuje odwołanie do obiektu1 do obiektu3
$obj3 = &$obj1;
// modyfikuje obiekt3
$obj3->attr2 = 10;
// wyświetla oba obiekty
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
print "objet3=[$obj3->attr1,$obj3->attr2]\n";
// czy obiekt jest słownikiem?
print count($obj1)."\n";
while (list($attribut, $valeur) = each($obj1))
  print "obj1[$attribut]=$valeur\n";
// koniec
exit;
?>

Wyniki:

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

Komentarze

  • wiersz 4: zapis $obj->attr oznacza atrybut attr zmiennej $obj. Jeśli nie istnieje, zostaje utworzony, co sprawia, że zmienna $obj staje się obiektem z atrybutami.
  • wiersz 13: wyrażenie $obj2=$obj1, gdy $obj1 jest obiektem, stanowi kopiowanie obiektów przez odwołanie. Zatem $obj2 i $obj1 są odwołaniami do tego samego obiektu. Sam obiekt może zostać zmodyfikowany przez którekolwiek z tych odwołań.
  • wiersz 20: wyrażenie $obj3=&$obj1 przypisuje do $obj3 odwołanie (adres) zmiennej $obj1. Jeśli $obj1 jest obiektem, tak jak w tym przypadku, powyższe wyrażenie jest równoważne wyrażeniu $obj3=$obj1.
  • wiersze 28–29: pokazują, że obiektem można przeglądać jak słownikiem. Kluczami słownika są nazwy atrybutów, a wartościami słownika – wartości tych samych atrybutów.
  • wiersz 27: funkcja count może być zastosowana do obiektu, ale nie zwraca, jak można by się spodziewać, liczby atrybutów. Obiekt wykazuje zatem podobieństwa do słownika, ale nim nie jest.

5.2. Klasa Personne bez zadeklarowanych atrybutów (exemple_15)


<?php

class Personne {

  // atrybuty klasy
  // niezadeklarowane – mogą być tworzone dynamicznie
  
  // metoda
  function identité() {
    // z założenia wykorzystuje nieistniejące atrybuty
    return "[$this->prénom,$this->nom,$this->âge]";
  }
}

// test
// atrybuty są publiczne i mogą być tworzone dynamicznie
$p = new Personne();
$p->prénom = "Paul";
$p->nom = "Langevin";
$p->âge = 48;
// wywołanie metody
print "personne=" . $p->identité() . "\n";
// koniec
exit;
?>

Wyniki:

personne=[Paul,Langevin,48]

Komentarze

  • wiersze 3–13: definiują klasę Personne. Klasa to szablon, na podstawie którego tworzy się obiekty. Klasa to zbiór atrybutów i funkcji zwanych metodami. Nie ma obowiązku deklarowania atrybutów.
  • wiersze 9–12: metoda identité wyświetla wartości trzech atrybutów, które nie zostały zadeklarowane w klasie. Słowo kluczowe $this określa obiekt, do którego stosowana jest metoda.
  • wiersz 17: tworzy się obiekt $p typu Personne. Słowo kluczowe new służy do tworzenia nowego obiektu. Operacja ta zwraca odwołanie do utworzonego obiektu (czyli adres). Możliwe są różne zapisy: new Personne(), new Personne, new personne. W nazwie klasy nie ma znaczenia wielkość liter.
  • wiersze 18–20: w obiekcie $p tworzone są trzy atrybuty niezbędne dla metody identité.
  • wiersz 22: metoda identité klasy Personne jest stosowana na obiekcie $p. W kodzie (wiersze 9–11) metody identité metoda $this odwołuje się do tego samego obiektu co metoda $p.

5.3. Klasa „Osoba” z zadeklarowanymi atrybutami (exemple_16)


<?php

class Personne {

  // atrybuty klasy
  var $prénom;
  var $nom;
  var $âge;

  // metoda
  function identité() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
}
// test
// atrybuty są publiczne
$p = new Personne();
$p->prénom = "Paul";
$p->nom = "Langevin";
$p->âge = 48;
// wywołanie metody
print "personne=" . $p->identité() . "\n";
// koniec
exit;
?>

Wyniki:

personne=[Paul,Langevin,48]

Komentarze

  • wiersze 6–8: atrybuty klasy są jawnie zadeklarowane za pomocą słowa kluczowego var.

5.4. Klasa Personne z konstruktorem (exemple_17)

Poprzednie przykłady przedstawiały nietypowe klasy Personne, jakie można było znaleźć w PHP 4. Nie zaleca się naśladowania tych przykładów. Przedstawiamy teraz klasę Personne zgodną z dobrymi praktykami z PHP 5:


<?php

class Personne {

// atrybuty klasy
  private $prénom;
  private $nom;
  private $âge;

// metody pobierające i ustawiające
  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;
  }

// konstruktor
  function __construct($prénom, $nom, $âge) {
    // przechodzimy przez set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

// metoda toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

// test
// utworzenie obiektu osoby
$p = new Personne("Paul", "Langevin", 48);
// tożsamość tej osoby
print "personne=$p\n";
// zmiana wieku
$p->setAge(14);
// tożsamość osoby
print "personne=$p\n";
// koniec
exit;
?>

Wyniki:

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

Komentarze

  • wiersze 3–48: klasa Personne
  • wiersze 6–8: atrybuty prywatne (private) klasy. Atrybuty te są widoczne wyłącznie wewnątrz klasy. Inne dostępne słowa kluczowe to:
  • public: sprawia, że atrybut staje się atrybutem publicznym, widocznym spoza klasy
  • protected: sprawia, że atrybut staje się atrybutem chronionym, widocznym wewnątrz klasy oraz w klasach od niej pochodnych.
  • Ponieważ atrybuty są prywatne, nie można uzyskać do nich dostępu spoza klasy. Nie można zatem napisać następującego kodu:
$p=new Personne() ;
$p->nom="Landru" ;

Znajdujemy się tutaj poza klasą Personne. Ponieważ atrybut nom jest prywatny, wiersz 2 jest nieprawidłowy. Aby zainicjować pola prywatne obiektu $p, istnieją dwa sposoby:

  • (ciąg dalszy)
    • użycie publicznych metod set i get (nazwy tych metod mogą być dowolne) z wierszy 11–33. Można wtedy napisać:
$p=new Personne() ;
$p->setNom("Landru") ;
  • (ciąg dalszy)
    • użycie konstruktora z wierszy 36–41. W takim przypadku należy napisać:
$p=new Personne("Michel","Landru",44) ;

Powyższy zapis automatycznie wywołuje metodę klasy Personne o nazwie __construct.

  • Wiersz 54: ten wiersz wyświetla osobę $p w postaci ciągu znaków. W tym celu wykorzystywana jest metoda klasy Personne o nazwie __toString.

5.5. Klasa Personne z kontrolą poprawności w konstruktorze (exemple_18)

Konstruktor klasy jest odpowiednim miejscem do sprawdzenia, czy wartości inicjalizacyjne obiektu są poprawne. Jednak obiekt może być również inicjalizowany za pomocą metod set lub ich odpowiedników. Aby uniknąć umieszczania tych samych sprawdzeń w dwóch różnych miejscach, umieści się je w metodach set. Jeśli wartość inicjalizacyjna obiektu okaże się nieprawidłowa, zostanie zgłoszony wyjątek. Oto przykład:


<?php

class Personne {

// atrybuty klasy
  private $prénom;
  private $nom;
  private $âge;
  
// metody pobierające i ustawiające
  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) {
    // imię musi być niepuste
    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) {
    // nazwisko nie może być puste
    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) {
    // wiek musi być prawidłowy
    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];
    }
  }

// konstruktor
  function __construct($prénom, $nom, $âge) {
    // korzystamy z metod set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // metoda toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
}

// test
// utworzenie obiektu Osoba
$p = new Personne("Paul", "Langevin", 48);
// tożsamość tej osoby
print "personne=$p\n";
// błędne utworzenie obiektu „Osoba”
try {
  $p = new Personne("xx", "yy", "zz");
} catch (Exception $e) {
  print $e->getMessage();
}
// koniec
exit;
?>

Wyniki:

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

Komentarze

  • wiersze 23–30: inicjalizacja atrybutu prénom i sprawdzenie wartości inicjalizacyjnej
  • wiersz 25: użycie wyrażenia regularnego. Imię to ciąg jednego lub kilku znaków, po którym mogą następować lub przed którym mogą występować spacje.
  • wiersz 26: jeśli nazwisko jest puste, zgłaszany jest wyjątek; w przeciwnym razie imię jest zapisywane (wiersz 28)
  • wiersz 66: użycie konstruktora klasy Personne
  • wiersz 68: użycie metody __toString klasy Personne
  • wiersze 70–74: obsługa ewentualnego wyjątku zgłoszonego przez konstruktor klasy Personne.

5.6. Dodanie metody pełniącej rolę drugiego konstruktora (exemple_19)

W PHP 5 nie ma możliwości posiadania wielu konstruktorów z różnymi parametrami, które pozwalałyby na tworzenie obiektu na różne sposoby. Można zatem używać metod pełniących rolę konstruktora:


<?php

class Personne {

// atrybuty klasy
  private $prénom;
  private $nom;
  private $âge;
  
// metody pobierające i ustawiające
  public function getPrénom() {
    return $this->prénom;
  }

….

// konstruktor
  function __construct($prénom, $nom, $âge) {
    // przechodzimy przez set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // metoda
  public function initWithPersonne($p) {
    // inicjuje bieżący obiekt z osobą $p
    $this->__construct($p->prénom, $p->nom, $p->âge);
  }

  // metoda toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

// test
// utworzenie obiektu Osoba
$p = new Personne("Paul", "Langevin", 48);
// tożsamość tej osoby
print "personne=$p\n";
// utworzenie drugiej osoby
$p2 = new Personne("Laure", "Adeline", 67);
// inicjalizacja pierwszej osoby wartościami drugiej
$p->initWithPersonne($p2);
// weryfikacja
print "personne=$p\n";
// koniec
exit;
?>

Wyniki:

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

Komentarze

  • wiersze 26–29: metoda initWithPersonne pozwala przypisać do bieżącego obiektu wartości atrybutów innego obiektu Personne. W tym przypadku wywołuje ona konstruktor __construct, ale nie jest to obowiązkowe.

5.7. Tablica obiektów typu Personne (exemple_20)

Poniższy przykład pokazuje, że można tworzyć tablice obiektów.


<?php

class Personne {

// atrybuty klasy
  private $prénom;
  private $nom;
  private $âge;
  
// metody pobierające i ustawiające
  public function getPrénom() {
    return $this->prénom;
  }

...

// konstruktor
  function __construct($prénom, $nom, $âge) {
    // przechodzimy przez set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // metoda
  public function initWithPersonne($p) {
….
  }

  // metoda toString
  function __toString() {
    ...
  }

}

// test
// tworzenie tablicy obiektów typu Personne
$groupe = array(new Personne("Paul", "Langevin", 48), new Personne("Sylvie", "Lefur", 70));

// tożsamość tych osób
for ($i = 0; $i < count($groupe); $i++)
  print "groupe[$i]=$groupe[$i]\n";
// koniec
exit;
?>

Wyniki:

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

Komentarze

  • wiersz 39: utworzenie tabeli zawierającej 2 osoby
  • wiersz 42: przeglądanie tabeli
  • wiersz 43: $groupe[$i] jest obiektem typu Personne. Do jego wyświetlenia służy metoda __toString.

5.8. Tworzenie klasy pochodnej od klasy Osoba (exemple_21)


<?php

class Personne {

// atrybuty klasy
  private $prénom;
  private $nom;
  private $âge;
  
// metody pobierające i ustawiające
  public function getPrénom() {
    return $this->prénom;
  }
...

// konstruktor
  function __construct($prénom, $nom, $âge) {
    // korzystamy z metod set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // metoda
  public function initWithPersonne($p) {
    // inicjuje bieżący obiekt za pomocą osoby $p
    $this->__construct($p->prénom, $p->nom, $p->âge);
  }

  // metoda toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

// klasa wywodząca się z klasy Osoba
class Enseignant extends Personne {

  // atrybuty
  private $discipline;   // nauczana dyscyplina

  // metody getter i setter

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

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

  // konstruktor

  public function __construct($prénom, $nom, $âge, $discipline) {
    // atrybuty klasy nadrzędnej
    parent::__construct($prénom, $nom, $âge);
    // inne atrybuty
    $this->setDiscipline($discipline);
  }

  // przeładowanie funkcji __toString klasy nadrzędnej
  public function __toString() {
    return "[" . parent::__toString() . ",$this->discipline]";
  }

}

// test
// utworzenie tabeli obiektów typu „Osoba” i pochodnych
$groupe = array(new Enseignant("Paul", "Langevin", 48, "anglais"), new Personne("Sylvie", "Lefur", 70));

// tożsamość tych osób
for ($i = 0; $i < count($groupe); $i++)
  print "groupe[$i]=$groupe[$i]\n";
// koniec
exit;
?>

Wyniki:

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

Komentarze

  • wiersze 3–35: klasa Personne
  • wiersz 38: klasa Enseignant wywodzi się (rozszerza) z klasy Personne. Klasa pochodna Enseignant dziedziczy atrybuty i metody po swojej klasie nadrzędnej.
  • wiersz 41: klasa Enseignant dodaje nowy, własny atrybut discipline
  • wiersz 55: konstruktor klasy Enseignant przyjmuje 4 parametry
    • 3 do zainicjowania klasy nadrzędnej (imię, nazwisko, wiek)
    • 1 do własnej inicjalizacji (przedmiot)
  • wiersz 57: klasa pochodna ma dostęp do metod i konstruktorów swojej klasy nadrzędnej za pomocą słowa kluczowego parent ::. Tutaj przekazujemy parametry (imię, nazwisko, wiek) do konstruktora klasy nadrzędnej.
  • wiersz 64: metoda __toString klasy pochodnej wykorzystuje metodę __toString klasy nadrzędnej.
  • wiersz 71: tablica zawierająca obiekt Enseignant oraz obiekt Personne.
  • wiersz 74: pętla przechodzi przez elementy tablicy
  • wiersz 75: zostanie wywołana metoda __toString każdego elementu $groupe[$i]. Klasa Personne posiada metodę __toString. Klasa Enseignant posiada dwie metody: metodę swojej klasy nadrzędnej oraz własną. Można się zastanawiać, która z nich zostanie wywołana. Wykonanie kodu pokazuje, że wywołano metodę klasy Enseignant. Zawsze tak jest: gdy wywoływana jest metoda na obiekcie, szuka się jej w następującej kolejności: w samym obiekcie, w jego klasie nadrzędnej, jeśli taka istnieje, następnie w klasie nadrzędnej klasy nadrzędnej itd. Poszukiwanie kończy się, gdy tylko metoda zostanie znaleziona.

5.9. Tworzenie drugiej klasy pochodnej klasy Personne (exemple_22)

Poniższy przykład tworzy klasę Etudiant wywodzącą się z klasy Personne:


<?php

class Personne {

// atrybuty klasy
  private $prénom;
  private $nom;
  private $âge;

// metody pobierające i ustawiające
  public function getPrénom() {
    return $this->prénom;
  }
...

// konstruktor
  function __construct($prénom, $nom, $âge) {
    // korzystamy z metod set
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // metoda
  public function initWithPersonne($p) {
    // inicjuje bieżący obiekt za pomocą osoby $p
    ...
  }

  // metoda toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }

}

// klasa pochodna klasy „osoba”
class Enseignant extends Personne {

  // atrybuty
  private $discipline;   // nauczana dyscyplina

  // metody getter i setter
...

  // konstruktor

  public function __construct($prénom, $nom, $âge, $discipline) {
    // atrybuty nadrzędne
    parent::__construct($prénom, $nom, $âge);
    // inne atrybuty
    $this->setDiscipline($discipline);
  }

  // przeładowanie funkcji _identité__toString klasy nadrzędnej
  public function __toString() {
    return "[" . parent::__toString() . ",$this->discipline]";
  }

}

// klasa pochodna od klasy „Osoba”
class Etudiant extends Personne {

  // atrybuty
  private $formation;   // nauczany przedmiot

  // metody getter i setter

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

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

  // konstruktor

  public function __construct($prénom, $nom, $âge, $formation) {
    // atrybuty klasy nadrzędnej
    parent::__construct($prénom, $nom, $âge);
    // inne atrybuty
    $this->setFormation($formation);
  }

  // przeładowanie funkcji __toString klasy nadrzędnej
  public function __toString() {
    return "[" . parent::__toString() . ",$this->formation]";
  }

}

// test
// utworzenie tablicy obiektów typu „osoba” i pochodnych
$groupe = array(new Enseignant("Paul", "Langevin", 48, "anglais"), new Personne("Sylvie", "Lefur", 70), new Etudiant("Steve", "Boer", 23, "iup2 qualité"));

// tożsamość tych osób
for ($i = 0; $i < count($groupe); $i++)
  print "groupe[$i]=$groupe[$i]\n";
// koniec
exit;
?>

Wyniki:

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

5.10. Interfejsy (exemple_23)

Interfejs to struktura definiująca prototypy metod. Klasa implementująca interfejs definiuje kod wszystkich metod tego interfejsu.


<?php

// interfejs
interface IExemple {
  function ajouter($i, $j);

  function soustraire($i, $j);
}

// implementacja 1 interfejsu
class Classe1 implements IExemple {

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

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

}

// implementacja 2 interfejsu
class Classe2 implements IExemple {

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

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

}

// funkcja
function calculer($a, $b, IExemple $interface) {
  print $interface->ajouter($a, $b)."\n";
  print $interface->soustraire($a, $b)."\n";
}

// ------------------- main
// utworzenie 2 obiektów typu Klasa1 i Klasa2
$c1 = new Classe1();
$c2 = new Classe2();
// wywołanie funkcji „oblicz”
calculer(4, 3, $c1);
calculer(14, 13, $c2);
?>

Wyniki

17
21
127
201

Komentarze

  • wiersze 4–8: interfejs IExemple definiuje dwie metody: funkcję ajouter (wiersz 5) oraz funkcję soustraire (wiersz 7). Interfejs nie definiuje kodu tych metod. Zrobią to klasy implementujące ten interfejs.
  • wiersz 11: klasa Classe1 implementuje interfejs IExemple. W związku z tym definiuje ona kod dla metod ajouter (wiersz 13) i soustraire (wiersz 17).
  • wiersze 24–34: to samo dotyczy klasy Classe2
  • wiersz 37: metoda calculer przyjmuje trzy parametry: dwie liczby całkowite $a i $b oraz obiekt $interface implementujący interfejs IExemple. Można by było zapisać funkcję w postaci calculer($a, $b, $interface), nie wskazując typu *IExemple* parametru $interface. Dzięki temu interpreter PHP może sprawdzić, czy rzeczywisty parametr jest rzeczywiście obiektem implementującym interfejs IExemple.
  • wiersze 38–39: użycie metod ajouter i soustraire obiektu $interface. Ponieważ obiekt ten implementuje interfejs IExemple, wiadomo, że posiada on te dwie metody.
  • wiersze 44–45: utworzenie dwóch obiektów różnych typów, ale oba implementujących interfejs IExemple.
  • wiersz 47: przekazujemy do funkcji calculer obiekt $c1 typu Classe1, który implementuje interfejs IExemple.
  • wiersz 48: następuje przejście do funkcji calculer, obiekt $c2 typu Classe2 implementujący interfejs IExemple.