Skip to content

5. 对象

5.1. 任何变量都可以成为具有属性的对象(示例_14)


<?php
 
// any variable can have attributes by construction
$obj1->attr1 = "un";
$obj1->attr2 = 100;
// displays the
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
// modify object
$obj1->attr2+=100;
// displays the
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
// assigns the value of object1 to object2
$obj2 = $obj1;
// modify obj2
$obj2->attr2 = 0;
// displays both objects
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
print "objet2=[$obj2->attr1,$obj2->attr2]\n";
// assigns the reference from object1 to object3
$obj3 = &$obj1;
// modify obj3
$obj3->attr2 = 10;
// displays both objects
print "objet1=[$obj1->attr1,$obj1->attr2]\n";
print "objet3=[$obj3->attr1,$obj3->attr2]\n";
// is an object a dictionary?
print count($obj1)."\n";
while (list($attribut, $valeur) = each($obj1))
  print "obj1[$attribut]=$valeur\n";
// end
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 表示变量 $objattr 属性。如果该属性不存在,则会自动创建,从而使变量 $obj 成为一个具有属性的对象。
  • 第 13 行:当 $obj1 是一个对象时,表达式 $obj2 = $obj1 属于按引用复制对象。因此,$obj2$obj1 都是指向同一个对象的引用。可以通过任一引用修改该对象本身。
  • 第 20 行:表达式 $obj3=&$obj1 将变量 $obj1 的引用(地址)赋值给 $obj3。如果 $obj1 是一个对象(如本例所示),则上述表达式等同于 $obj3=$obj1
  • 第 28–29 行:演示了对象可以像字典一样进行遍历。字典的键是属性的名称,字典的值则是这些属性的值。
  • 第 27 行:count 函数可以应用于对象,但与预期不同,它不会返回属性的数量。因此,对象与字典有相似之处,但并非字典。

5.2. 一个未声明属性的 Person 类(example_15)


<?php
 
class Personne {
 
  // class attributes
  // undeclared - can be created dynamically
 
  // method
  function identité() {
    // a priori, uses non-existent attributes
    return "[$this->prénom,$this->nom,$this->âge]";
  }
}
 
// test
// attributes are public and can be created dynamically
$p = new Personne();
$p->prénom = "Paul";
$p->nom = "Langevin";
$p->âge = 48;
// method call
print "personne=" . $p->identité() . "\n";
// end
exit;
?>

结果

personne=[Paul,Langevin,48]

注释

  • 第 3–13 行:定义 Person 。类是一种用于创建对象的模板。类是由属性及称为方法的函数组成的集合。属性无需显式声明。
  • 第 9–12 行:identity 方法显示了三个未在类中声明的属性的值。关键字 $this 指代该方法所作用的对象。
  • 第 17 行:创建了一个类型为 Person 的对象 $p。使用关键字 new 来创建新对象。该操作返回对所创建对象的引用(即地址)。可以使用多种写法:new Person()new Personnew person。类名不区分大小写。
  • 第 18–20 行:在 $p 对象中创建了 identity 方法所需的三项属性。
  • 第 22 行:将 Person 类的 identity 方法应用于 $p 对象。在 identity 方法的代码(第 9–11 行)中,$this 指代与 $p 相同的对象。

5.3. 声明了属性的 Person 类(示例_16)


<?php
 
class Personne {
 
  // class attributes
  var $prénom;
  var $nom;
  var $âge;
 
  // method
  function identité() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
}
// test
// attributes are public
$p = new Personne();
$p->prénom = "Paul";
$p->nom = "Langevin";
$p->âge = 48;
// method call
print "personne=" . $p->identité() . "\n";
// end
exit;
?>

结果

personne=[Paul,Langevin,48]

注释

  • 第 6–8 行:类属性使用 var 关键字显式声明。

5.4. 带构造函数的 Person 类(示例_17)

前面的示例展示了在 PHP 4 中可能出现的非标准 Person 类。不建议照搬这些示例。下面我们将介绍一个遵循 PHP 5 最佳实践的 Person 类:


<?php
 
class Personne {
 
// class attributes
  private $prénom;
  private $nom;
  private $âge;
 
// getters and setters
  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;
  }
 
// manufacturer
  function __construct($prénom, $nom, $âge) {
    // we go through sets
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }
 
// method toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
 
}
 
// test
// creation of a person object
$p = new Personne("Paul", "Langevin", 48);
// identity of this person
print "personne=$p\n";
// we change the age
$p->setAge(14);
// identity of the person
print "personne=$p\n";
// end
exit;
?>

结果

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

注释

  • 第 3-48 行:Person
  • 第 6-8 行:类的私有属性。这些属性仅在类内部可见。其他可用的关键字包括:
  • public:使属性成为公共属性,可在类外部访问
  • protected:将属性设为受保护属性,可在类内部及其派生类中访问。
  • 由于这些属性是私有的,因此无法从类外部访问。因此,我们无法编写以下代码:
$p=new Personne() ;
$p->nom="Landru" ;

这里,我们处于 Person 类之外。由于 name 属性是私有的,第 2 行是错误的。要初始化 $p 对象的私有字段,有两种方法:

  • (待续)
    • 使用第 11–33 行中的公共 setget 方法(这些方法的名称可以是任意名称)。然后我们可以这样写:
$p=new Personne() ;
$p->setNom("Landru") ;
  • (续)
    • 使用第36至41行的构造函数。那么我们就会写成:
$p=new Personne("Michel","Landru",44) ;

上述代码会自动调用 Person 类的 __construct 方法。

  • 第 54 行:此行将 Person 对象 $p 作为字符串显示。为此,使用了 Person 类的 __toString 方法。

5.5. 在构造函数中进行有效性检查的 Person 类(示例_18)

类的构造函数是验证对象初始化值是否正确的合适位置。不过,对象也可以通过其 set 方法或等效方法进行初始化。为了避免在两个不同位置重复相同的检查,我们将这些检查放在 set 方法中。如果发现对象的初始化值不正确,将抛出异常。以下是一个示例:


<?php
 
class Personne {
 
// class attributes
  private $prénom;
  private $nom;
  private $âge;
 
// getters and setters
  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) {
    // first name must be non-empty
    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) {
    // name must be non-empty
    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) {
    // age must be valid
    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];
    }
  }
 
// manufacturer
  function __construct($prénom, $nom, $âge) {
    // we go through sets
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }
 
  // method toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
}
 
// test
// creation of a Personne object
$p = new Personne("Paul", "Langevin", 48);
// identity of this person
print "personne=$p\n";
// creation of an erroneous Person object
try {
  $p = new Personne("xx", "yy", "zz");
} catch (Exception $e) {
  print $e->getMessage();
}
// end
exit;
?>

结果

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

注释

  • 第 23-30 行:初始化 first_name 属性并验证初始化值
  • 第 25 行:使用正则表达式。名字是由一个或多个字符组成的序列,前后可能带有空格。
  • 第 26 行:如果姓氏为空,则抛出异常;否则,将名字存储起来(第 28 行)
  • 第 66 行:使用 Person 类的构造函数
  • 第 68 行:使用 Person 类的 __toString 方法
  • 第 70–74 行:处理 Person 类构造函数抛出的任何异常。

5.6. 添加一个充当第二个构造函数的方法(示例_19)

在 PHP 5 中,无法拥有多个具有不同参数的构造函数,从而无法以多种方式创建对象。因此,我们可以使用充当构造函数的方法:


<?php
 
class Personne {
 
// class attributes
  private $prénom;
  private $nom;
  private $âge;
 
// getters and setters
  public function getPrénom() {
    return $this->prénom;
  }
 
….
 
// manufacturer
  function __construct($prénom, $nom, $âge) {
    // we go through sets
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }
 
  // method
  public function initWithPersonne($p) {
    // initializes the current object with a person $p
    $this->__construct($p->prénom, $p->nom, $p->âge);
  }
 
  // method toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
 
}
 
// test
// creation of a Personne object
$p = new Personne("Paul", "Langevin", 48);
// identity of this person
print "personne=$p\n";
// creation of a second person
$p2 = new Personne("Laure", "Adeline", 67);
// initialization of the first with the values of the second
$p->initWithPersonne($p2);
// check
print "personne=$p\n";
// end
exit;
?>

结果

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

注释

  • 第 26-29 行:initWithPerson 方法允许您将另一个 Person 对象的属性值赋给当前对象。此处调用了 __construct 构造函数,但这并非强制要求。

5.7. Person 对象的数组 (example_20)

以下示例展示了您可以创建对象数组。


<?php
 
class Personne {
 
// class attributes
  private $prénom;
  private $nom;
  private $âge;
 
// getters and setters
  public function getPrénom() {
    return $this->prénom;
  }
 
...
 
// manufacturer
  function __construct($prénom, $nom, $âge) {
    // we go through sets
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }
 
  // method
  public function initWithPersonne($p) {
….
  }
 
  // method toString
  function __toString() {
    ...
  }
 
}
 
// test
// create an array of Personne objects
$groupe = array(new Personne("Paul", "Langevin", 48), new Personne("Sylvie", "Lefur", 70));
 
// identity of these persons
for ($i = 0; $i < count($groupe); $i++)
  print "groupe[$i]=$groupe[$i]\n";
// end
exit;
?>

结果

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

注释

  • 第39行:创建一个包含2人的数组
  • 第 42 行:遍历数组
  • 第 43 行:$group[$i] 是一个 Person 类型的对象。使用 __toString 方法来显示它。

5.8. 创建一个继承自 Person 类的子类(example_21)


<?php
 
class Personne {
 
// class attributes
  private $prénom;
  private $nom;
  private $âge;
 
// getters and setters
  public function getPrénom() {
    return $this->prénom;
  }
...
 
// manufacturer
  function __construct($prénom, $nom, $âge) {
    // we go through sets
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }
 
  // method
  public function initWithPersonne($p) {
    // initializes the current object with a person $p
    $this->__construct($p->prénom, $p->nom, $p->âge);
  }
 
  // method toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
 
}
 
// a class derived from Personne
class Enseignant extends Personne {
 
  // attributes
  private $discipline;   // d// subject taught
 
  // getter and setter
 
  public function getDiscipline() {
    return $this->discipline;
  }
 
  public function setDiscipline($discipline) {
    $this->discipline = $discipline;
  }
 
  // manufacturer
 
  public function __construct($prénom, $nom, $âge, $discipline) {
    // parent attributes
    parent::__construct($prénom, $nom, $âge);
    // other attributes
    $this->setDiscipline($discipline);
  }
 
  // overloads the __toString function of the parent class
  public function __toString() {
    return "[" . parent::__toString() . ",$this->discipline]";
  }
 
}
 
// test
// creation of an array of Personne and derived objects
$groupe = array(new Enseignant("Paul", "Langevin", 48, "anglais"), new Personne("Sylvie", "Lefur", 70));
 
// identity of these persons
for ($i = 0; $i < count($groupe); $i++)
  print "groupe[$i]=$groupe[$i]\n";
// end
exit;
?>

结果

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

注释

  • 第 3-35 行:Person
  • 第 38 行:Teacher 类继承自 Person派生类 Teacher 继承了其父类的属性和方法。
  • 第 41 行:Teacher 类添加了一个新的属性 subject,该属性是该类特有的
  • 第 55 行:Teacher 类的构造函数接受 4 个参数
    • 其中3个用于初始化父类(first_name、last_name、age)
    • 1 个用于自身初始化(subject)
  • 第 57 行:派生类可以通过关键字 parent:: 访问其父类的的方法和构造函数。在此,我们将参数(first_name、last_name、age)传递给父类的构造函数。
  • 第 64 行:派生类的 __toString 方法使用了父类的 __toString 方法。
  • 第 71 行:一个包含 Teacher 对象和 Person 对象的数组。
  • 第 74 行:我们遍历数组的元素
  • 第 75 行:将调用每个元素 $group[$i]__toString 方法。Person 类有一个 __toString 方法。Teacher 类有两个:父类的和它自己的。 人们可能会疑惑究竟会调用哪个方法。执行结果表明,被调用的正是 Teacher 类的方法。这种情况总是如此:当在对象上调用方法时,系统会按以下顺序进行搜索:首先在对象本身中查找,如果该对象有父类,则在其父类中查找,然后在父类的父类中查找,以此类推……一旦找到该方法,搜索即刻停止。

5.9. 创建一个从 Person 类派生的第二个类(example_22)

以下示例创建了一个从 Person 类派生的 Student 类:


<?php
 
class Personne {
 
// class attributes
  private $prénom;
  private $nom;
  private $âge;
 
// getters and setters
  public function getPrénom() {
    return $this->prénom;
  }
...
 
// manufacturer
  function __construct($prénom, $nom, $âge) {
    // we go through sets
    $this->setPrénom($prénom);
    $this->setNom($nom);
    $this->setAge($âge);
  }
 
  // method
  public function initWithPersonne($p) {
    // initializes the current object with a person $p
    ...
  }
 
  // method toString
  function __toString() {
    return "[$this->prénom,$this->nom,$this->âge]";
  }
 
}
 
// a class derived from personne
class Enseignant extends Personne {
 
  // attributes
  private $discipline;   // d// subject taught
 
  // getter and setter
...
 
  // manufacturer
 
  public function __construct($prénom, $nom, $âge, $discipline) {
    // parent attributes
    parent::__construct($prénom, $nom, $âge);
    // other attributes
    $this->setDiscipline($discipline);
  }
 
  // overloads the _identité__toString function of the parent class
  public function __toString() {
    return "[" . parent::__toString() . ",$this->discipline]";
  }
 
}
 
// a class derived from Personne
class Etudiant extends Personne {
 
  // attributes
  private $formation;   // d// subject taught
 
  // getter and setter
 
  public function getFormation() {
    return $this->formation;
  }
 
  public function setFormation($formation) {
    $this->formation = $formation;
  }
 
  // manufacturer
 
  public function __construct($prénom, $nom, $âge, $formation) {
    // parent attributes
    parent::__construct($prénom, $nom, $âge);
    // other attributes
    $this->setFormation($formation);
  }
 
  // overloads the __toString function of the parent class
  public function __toString() {
    return "[" . parent::__toString() . ",$this->formation]";
  }
 
}
 
// test
// creation of a table of person objects and derivatives
$groupe = array(new Enseignant("Paul", "Langevin", 48, "anglais"), new Personne("Sylvie", "Lefur", 70), new Etudiant("Steve", "Boer", 23, "iup2 qualité"));
 
// identity of these persons
for ($i = 0; $i < count($groupe); $i++)
  print "groupe[$i]=$groupe[$i]\n";
// end
exit;
?>

结果

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

5.10. 接口 (示例_23)

接口是一种定义方法原型的结构。实现接口的类会为该接口的所有方法编写代码。


<?php
 
// an interface
interface IExemple {
  function ajouter($i, $j);
 
  function soustraire($i, $j);
}
 
// interface implementation 1
class Classe1 implements IExemple {
 
  public function ajouter($a, $b) {
    return $a + $b + 10;
  }
 
  public function soustraire($a, $b) {
    return $a - $b + 20;
  }
 
}
 
// interface implementation 2
class Classe2 implements IExemple {
 
  public function ajouter($a, $b) {
    return $a + $b + 100;
  }
 
  public function soustraire($a, $b) {
    return $a - $b + 200;
  }
 
}
 
// function
function calculer($a, $b, IExemple $interface) {
  print $interface->ajouter($a, $b)."\n";
  print $interface->soustraire($a, $b)."\n";
}
 
// ------------------- hand
// creation of 2 objects of type Class1 and Class2
$c1 = new Classe1();
$c2 = new Classe2();
// call the calculate function
calculer(4, 3, $c1);
calculer(14, 13, $c2);
?>

结果

17
21
127
201

注释

  • 第 4–8 行:IExample 接口定义了两个方法:add 函数(第 5 行)和 subtract 函数(第 7 行)。该接口并未定义这些方法的代码,具体实现将由实现该接口的类来完成。
  • 第 11 行:Class1实现了 IExample 接口。因此,它为 add 方法(第 13 行)和 subtract 方法(第 17 行)定义了代码。
  • 第 24–34 行:Class2 类的情况与此相同。
  • 第 37 行:*calculer 方法接受三个参数:两个整数 *$a*$b*,以及一个实现 *IExample 接口的对象 *$interface。 我们本可以编写 function calculate($a, $b, $interface),而不指定 *$interface* 参数的 *IExample 类型。通过指定该类型,我们允许 PHP 解释器验证实际参数是否确实是一个实现了 *IExample 接口的对象。
  • 第 38–39 行:使用 $interface 对象的 addsubtract 方法。由于它实现了 IExample 接口,因此我们知道它拥有这两个方法。
  • 第 44–45 行:创建两个不同类型的对象,它们都实现了 IExample 接口。
  • 第 47 行:我们将类型为 Class1 且实现了 IExample 接口的对象 $c1 传递给 calculate 函数。
  • 第 48 行:将类型为 Class2 且实现了 IExample 接口的对象 $c2 传递给 calculate 函数。