在本教程中,您将学习如何在PHP中以面向对象的方式编写代码。
面向对象编程(OOP)是一种基于类和对象概念的编程模型。与过程编程不同,过程编程的重点是编写对数据执行操作的过程或函数,而在面向对象编程中,重点是创建同时包含数据和函数的对象。
相对于常规或过程式编程,面向对象编程具有多个优势。最重要的列出如下:
它为程序提供了清晰的模块化结构。
它可以帮助您遵守“不要重复自己”(DRY)原则,从而使您的代码更易于维护,修改和调试。
它使得可以用更少的代码,更短的开发时间和高度的可重用性来创建更复杂的行为。
以下各节将描述类和对象在PHP中的工作方式。
提示:以过程编程风格编写的程序,意味着程序由一个或多个过程组成。 而过程是一组一起执行特定任务的编程语句。
提示:“不重复自己”(Don‘t Repeat Yourself,DRY)原则背后的思想是,通过抽象出应用程序常用的代码并将它们放在单个位置并重用它们而不是重复它们,从而减少代码的重复。
类和对象是面向对象编程的两个主要方面。 类是独立的变量和函数集合,它们协同工作以执行一个或多个特定任务,而对象是类的单个实例。
一个类充当模板或蓝图,可以从中创建许多单个对象。创建单个对象时,尽管每个对象的某些属性可能具有不同的值,但它们继承相同的通用属性和行为。
例如,将类视为房子的蓝图。蓝图本身不是房屋,而是房屋的详细计划。而对象就像是根据该蓝图建造的实际房屋。我们可以根据相同的蓝图建造几座相同的房屋,但是每座房屋内部可能具有不同的涂料,内饰和家庭,如下图所示。
可以使用class关键字声明一个类,后跟该类的名称和一对花括号({}),如以下示例所示。
让我们创建一个名为Recangle.php的PHP文件,并将以下示例代码放入其中,以便将我们的类代码与程序的其余部分分开。 然后,我们只需包含Recangle.php文件,就可以在需要的任何地方使用它。
<?php class Rectangle { //声明属性 public $length = 0; public $width = 0; //求周长的方法 public function getPerimeter(){ return (2 * ($this->length + $this->width)); } //求面积的方法 public function getArea(){ return ($this->length * $this->width); } } ?>
public关键字在上面的实例中的属性和方法之前,是一个访问修饰符,表示该属性或方法是可以从任何地方访问。我们将在本章稍后了解更多有关此内容的信息。
注意:从语法上讲,类中的变量称为属性,而函数称为方法。 此外,类名通常是用PascalCase(帕斯卡命名法)编写的,也就是说,每个连接的单词都以大写字母开头(例如,MyClass)。
创建另一个PHP文件名test.php,并将以下代码放入其中。
<?php //包含类定义 class Rectangle { //声明属性 public $length = 0; public $width = 0; //求周长的方法 public function getPerimeter(){ return (2 * ($this->length + $this->width)); } //求面积的方法 public function getArea(){ return ($this->length * $this->width); } } //从Rectangle类创建一个新对象 $obj = new Rectangle; //获取对象属性值 echo $obj->length; // 输出: 0 echo $obj->width; // 输出: 0 //设置对象属性值 $obj->length = 30; $obj->width = 20; //再次读取对象属性值以显示更改 echo $obj->length; // 输出: 30 echo "<br>"; echo $obj->width; // 输出: 20 //调用对象方法 echo $obj->getPerimeter(); // 输出: 100 echo "<br>"; echo $obj->getArea(); // 输出: 600 ?>测试看看
箭头符号(->)是一个OOP构造,用于访问给定对象的包含的属性和方法。而伪变量$this提供了对调用对象(即方法所属的对象)的引用。
当使用同一类的多个实例时,面向对象编程的真正威力就显而易见了,如以下示例所示:
<?php //包含类定义 class Rectangle { //声明属性 public $length = 0; public $width = 0; //求周长的方法 public function getPerimeter(){ return (2 * ($this->length + $this->width)); } //求面积的方法 public function getArea(){ return ($this->length * $this->width); } } //从Rectangle类创建多个对象 $obj1 = new Rectangle; $obj2 = new Rectangle; //调用两个对象的方法 echo $obj1->getArea(); //输出:0 echo $obj2->getArea(); //输出:0 //设置$obj1属性值 $obj1->length = 30; $obj1->width = 20; //设置$obj2属性值 $obj2->length = 35; $obj2->width = 50; //再次调用两个对象的方法 echo $obj1->getArea(); //输出:600 echo "<br>"; echo $obj2->getArea(); //输出:1750 ?>测试看看
如上例所示,在不同的对象上调用getArea()方法会使该方法对不同的数据集进行操作。 每个对象实例都是完全独立的,具有自己的属性和方法,因此即使它们属于同一类,也可以独立地对其进行操作。
为了简化面向对象的编程,PHP提供了一些魔术方法,当对象中发生某些操作时,这些方法会自动执行。
例如,每当创建新对象时,魔术方法__construct()(称为构造函数)都会自动执行。 同样,魔术方法__destruct()(称为析构函数)在对象被销毁时会自动执行。 销毁对象后,析构函数将清除分配给该对象的所有资源。
<?php class MyClass { // 构造函数 public function __construct(){ echo ' 类 "' . __CLASS__ . '" 已启动<br>'; } // 析构函数 public function __destruct(){ echo '类 "' . __CLASS__ . '" 已销毁<br>'; } } //创建一个新对象 $obj = new MyClass; //在文件末尾输出消息 echo "到达文件末尾。"; ?>测试看看
上面示例中的PHP代码将产生以下输出:
类 "MyClass" 已启动 到达文件末尾。 类 "MyClass" 已销毁
脚本结束时将自动调用析构函数。但是,要显式触发析构函数,可以使用PHP unset()函数销毁对象,如下所示:
<?php class MyClass { // 构造函数 public function __construct(){ echo ' 类 "' . __CLASS__ . '" 已启动<br>'; } // 析构函数 public function __destruct(){ echo '类 "' . __CLASS__ . '" 已销毁<br>'; } } //创建一个新对象 $obj = new MyClass; // 销毁对象 unset($obj); //在文件末尾输出消息 echo "到达文件末尾。"; ?>测试看看
现在,以上示例中的PHP代码将产生以下输出:
类 "MyClass" 已启动 类 "MyClass" 已销毁 到达文件末尾。
提示:脚本完成后,PHP会自动清除执行期间分配的所有资源,例如关闭数据库连接,销毁对象等。
注:__CLASS__是一个魔术常量,它包含它所在的类的名称。如果它发生在类之外,则为空。
类可以使用extends关键字继承另一个类的属性和方法。可扩展性的过程称为继承。这可能是使用面向对象的编程模型背后最强大的原因。
<?php //包含类定义 class Rectangle { //声明属性 public $length = 0; public $width = 0; //求周长的方法 public function getPerimeter(){ return (2 * ($this->length + $this->width)); } //求面积的方法 public function getArea(){ return ($this->length * $this->width); } } //根据现有的类定义一个新的类 class Square extends Rectangle { //方法来测试矩形是否也是正方形 public function isSquare(){ if($this->length == $this->width){ return true; // 正方形 } else{ return false; //不是正方形 } } } //从Square类创建一个新对象 $obj = new Square; // 设置对象属性值 $obj->length = 20; $obj->width = 20; // 调用对象方法 if($obj->isSquare()){ echo "正方形的面积是"; } else{ echo "矩形的面积是"; }; echo $obj->getArea(); ?>测试看看
上面示例中的PHP代码将产生以下输出:
正方形的面积是 400
正如您在上面的示例中看到的,尽管Square的类定义既没有显式包含getArea()方法,也没有显式包含$length和$width属性,但是Square类的实例可以使用它们,因为它们继承自父Rectangle类。
提示:由于子类是从父类派生的,因此也称为派生类,其父类称为基类。
在使用类时,您甚至可以使用可见性关键字来限制对其属性和方法的访问,以实现更好的控制。 有三个可见性关键字(从最可见到最不可见):public,protected,private,它们确定如何以及从何处访问和修改属性和方法。
public - 可以从类内部和外部的任何位置访问公共属性或方法。 这是PHP中所有类成员的默认可见性。
protected - 受保护的属性或方法只能从类本身或子类或继承类(即扩展该类的类)中访问。
private - 私有属性或方法只能从定义它的类中访问。甚至子类或继承的类也无法访问私有属性或方法。
以下示例将向您展示这种可见性实际上是如何工作的:
<?php //类定义 class Automobile { //声明属性 public $fuel; protected $engine; private $transmission; } class Car extends Automobile { // 构造函数 public function __construct(){ echo ' 类 "' . __CLASS__ . '" 已启动<br>'; } } //从Automobile类创建对象 $automobile = new Automobile; //尝试设置$automobile对象属性 $automobile->fuel = 'Petrol'; // ok $automobile->engine = '1500 cc'; // fatal error $automobile->transmission = 'Manual'; // fatal error //从Car类创建对象 $car = new Car; //尝试设置$car对象属性 $car->fuel = 'Diesel'; // ok $car->engine = '2200 cc'; // fatal error $car->transmission = 'Automatic'; // undefined ?>
除了可见性之外,属性和方法也可以声明为static(静态的),这使得它们无需类的实例即可访问。可以使用范围解析运算符(::)访问静态属性和方法,如下所示:ClassName::$property和ClassName::method()。
尽管可以使用静态方法,但不能通过该类的对象访问声明为静态的属性,如以下示例所示:
<?php //类定义 class HelloClass { //声明静态属性 public static $greeting = "Hello World!"; //声明静态方法 public static function sayHello(){ echo self::$greeting; } } //尝试直接访问静态属性和方法 echo HelloClass::$greeting; //输出:Hello World! HelloClass::sayHello(); //输出:Hello World! //尝试通过对象访问静态属性和方法 $hello = new HelloClass; echo $hello->greeting; // Strict Warning $hello->sayHello(); //输出:Hello World! ?>
上例中的关键字self表示“当前类别”。 它绝不能以美元符号($)开头,并且总是以::运算符(例如self :: $ name)开头。
self关键字不同于this关键字,它表示“当前对象”或“类的当前实例”。 此关键字始终以美元符号($)开头,后跟->运算符(例如$ this-> name)。
注意:由于静态方法可以在没有类实例(即对象)的情况下调用,因此在声明为static的方法中,伪变量$this不可用。
我们希望您现在已经了解了面向对象编程的基本概念。您将在PHP和MySQL数据库部分找到有关OOP的更多示例。