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. فئة Personne بدون سمات مُعلنة (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: يتم إنشاء السمات الثلاث اللازمة للطريقة identité في الكائن $p.
  • السطر 22: يتم تطبيق الأسلوب identité الخاص بالفئة Personne على الكائن $p. في كود (الأسطر 9-11) للطريقة identité، تشير $this إلى نفس الكائن الذي تشير إليه $p.

5.3. فئة Personne مع السمات المعلنة (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;

// مُستردات ومُعيّنات
  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. فئة «Personne» مع التحقق من الصحة في المنشئ (exemple_18)

يُعد منشئ الفئة المكان المناسب للتحقق من صحة قيم تهيئة الكائن. ولكن يمكن أيضًا تهيئة الكائن من خلال طرقه set أو ما يعادلها. لتجنب وضع نفس عمليات التحقق في مكانين مختلفين، سيتم وضعها في الطرق set. وإذا تبين أن إحدى قيم التهيئة للكائن غير صحيحة، فسيتم إثارة استثناء. وفيما يلي مثال على ذلك:


<?php

class Personne {

// سمات الفئة
  private $prénom;
  private $nom;
  private $âge;
  
// دالات الاسترجاع والتعيين
  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) {
    // نستخدم طرق التعيين
    $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;
  
// مُستردات ومُعيّنات
  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]";
  }

}

// اختبار
// إنشاء كائن Personne
$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. مصفوفة من كائنات «Personne» (exemple_20)

يوضح المثال التالي أنه يمكن وجود مصفوفات من الكائنات.


<?php

class Personne {

// سمات الفئة
  private $prénom;
  private $nom;
  private $âge;
  
// مُستردات ومُعيّنات
  public function getPrénom() {
    return $this->prénom;
  }

...

// منشئ
  function __construct($prénom, $nom, $âge) {
    // نمر عبر المتغيرات
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }

  // الطريقة
  public function initWithPersonne($p) {
….
  }

  // طريقة toString
  function __toString() {
    ...
  }

}

// اختبار
// إنشاء مصفوفة من كائنات «Personne»
$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: إنشاء جدول يضم شخصين
  • السطر 42: مسار الجدول
  • السطر 43: $groupe[$i] هو كائن من النوع Personne. تُستخدم الطريقة __toString لعرضه.

5.8. إنشاء فئة مشتقة من فئة Personne (exemple_21)


<?php

class Personne {

// سمات الفئة
  private $prénom;
  private $nom;
  private $âge;
  
// دالات الاسترجاع والتعيين
  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]";
  }

}

// فئة مشتقة من Personne
class Enseignant extends Personne {

  // السمات
  private $discipline;   // المادة التي يدرسها

  // أدوات getter و setter

  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]";
  }

}

// اختبار
// إنشاء جدول للكائنات "Personne" والمشتقات منها
$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 مشتقة (تتوسع) من الفئة 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;

// مُستردات ومُعيّنات
  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]";
  }

}

// فئة مشتقة من Personne
class Etudiant extends Personne {

  // السمات
  private $formation;   // المادة التي يدرسها

  // getter و setter

  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
// إنشاء كائنين من النوع 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 الواجهة IExemple. وبالتالي، فإنها تحدد كودًا للطرق ajouter (السطر 13) و soustraire (السطر 17).
  • الأسطر 24-34: الأمر نفسه بالنسبة للفئة Classe2
  • السطر 37: تقبل الدالة calculer ثلاثة معلمات: عددين صحيحين هما $a و $b، وكائن $interface الذي ينفذ واجهة IExemple. كان من الممكن كتابة الدالة calculer($a,$b,$interface) دون الإشارة إلى نوع المعلمة $interface، وهو IExemple. وبتحديد هذا النوع، نسمح للمترجم PHP بالتحقق من أن المعلمة الفعلية هي بالفعل كائن يُنفذ الواجهة IExemple.
  • السطران 38-39: استخدام الطريقتين ajouter و soustraire للكائن $interface. ونظرًا لأن هذا الكائن يُنفذ الواجهة IExemple، فإننا نعلم أنه يمتلك هاتين الطريقتين.
  • السطران 44-45: إنشاء كائنين من نوعين مختلفين ولكنهما ينفذان واجهة IExemple.
  • السطر 47: يتم تمرير الكائن $c1 من النوع Classe1، الذي ينفذ الواجهة IExemple، إلى الدالة calculer.
  • السطر 48: يتم الانتقال إلى الدالة calculer، حيث يقوم الكائن $c2 من النوع Classe2 بتنفيذ الواجهة IExemple.