在本文中,我们将探索使用 PHP 类进行面向对象编程的基础知识。
我们将从类和对象的介绍开始,我们将在本文的后半部分讨论一些高级概念,如继承和多态性。
什么是面向对象编程 (OOP)?
面向对象编程,通常称为 OOP,是一种帮助我们以易于长期维护和扩展的方式开发复杂应用程序的方法。 在 OOP 的世界中(在 PHP 中创建对象),现实世界中的实体,如 Person、Car 或 Animal 被视为对象。 在面向对象的编程中,我们通过使用对象与我们的应用程序进行交互。 这与我们主要与函数和全局变量交互的过程编程形成对比。
在 OOP 中,有一个“类”的概念,用于将现实世界的实体建模或映射到数据(属性)和功能(方法)的模板。 “对象”是一个类的实例,我们可以创建同一个类的多个实例。 例如,只有一个 Person 类,但许多 person 对象可以是该类的实例——dan、zainab、hector 等。
该类定义属性。 例如,对于 Person 类,我们可能有姓名、年龄和电话号码。 然后每个人对象都会有自己的这些属性值。
我们还可以在类中定义方法,这些方法允许我们操纵对象属性的值并对对象执行操作。 例如,可以定义一个将对象信息保存到数据库的保存方法。
让我们继续深入学习如何在 PHP 中创建对象。
什么是 PHP 类?
首先我们需要了解什么是 PHP 类。 类是代表现实世界实体的模板,它定义实体的属性和方法。 在本节中,我们将讨论典型 PHP 类的基本结构。
理解新概念的最佳方式是举个例子。 因此,让我们看一下以下代码片段中的 Employee 类,它代表员工实体。
class Employee
{
private $first_name;
private $last_name;
private $age;
public function __construct($first_name, $last_name, $age)
{
$this->first_name = $first_name;
$this->last_name = $last_name;
$this->age = $age;
}
public function getFirstName()
{
return $this->first_name;
}
public function getLastName()
{
return $this->last_name;
}
public function getAge()
{
return $this->age;
}
}
第一行的 class Employee
语句定义了 Employee 类。 然后,我们继续声明属性、构造函数和其他类方法。
PHP 中的类属性
我们可以将类属性视为用于保存有关对象信息的变量。 在上面的例子中,我们定义了三个属性——first_name 、last_name 和 age。 在大多数情况下,类属性是通过实例化对象访问的。
这些属性是私有的,这意味着它们只能在 PHP 类中访问。 这是最安全的属性访问级别。 我们将在本文后面讨论 PHP 中类属性和方法的不同访问级别。
PHP 类的构造函数
构造函数是一种特殊的类方法,当我们实例化对象时会自动调用它。 我们将在接下来的几节中看到如何实例化对象,但现在我们只需要知道构造函数用于在创建对象时初始化对象属性。
我们可以通过定义 __construct
方法来定义构造函数。
PHP 类的方法
我们可以将 PHP 中的类方法视为执行与对象关联的特定操作的函数。 在大多数情况下,它们用于访问和操作对象属性并执行相关操作。
在上面的示例中,我们定义了 getLastName
方法,它返回与对象关联的姓氏。
以上就是对 PHP 类结构的简要介绍。 在下一节中,我们将看到如何实例化 Employee 类的对象。
什么是 PHP 中的对象?
在上一节中,我们讨论了 PHP 中类的基本结构。 现在,当你想使用一个类时,你需要实例化它,最终的结果是一个对象。 所以我们可以将类视为蓝图,而对象是我们可以使用的实际事物。
在我们刚刚在上一节中创建的 Employee 类的上下文中,让我们看看如何实例化该类的对象。
$objEmployee = new Employee('Bob', 'Smith', 30);
echo $objEmployee->getFirstName(); // print 'Bob'
echo $objEmployee->getLastName(); // prints 'Smith'
echo $objEmployee->getAge(); // prints '30'
当你想要实例化任何类的对象及其类名时,你需要使用 new
关键字,你将取回该类的一个新对象实例。
如果一个类定义了 __construct
方法并且它需要参数,则在实例化对象时需要传递这些参数。 在我们的例子中,Employee 类构造函数需要三个参数,因此我们在创建 $objEmployee
对象时传递了这些参数。 正如我们之前讨论的,__construct
方法在对象被实例化时被自动调用。
接下来,我们在 $objEmployee
对象上调用类方法来打印在对象创建期间初始化的信息。 当然,我们可以创建同一个类的多个对象,如以下代码片段所示。
$objEmployeeOne = new Employee('Fql', 'Jiyik', 30);
echo $objEmployeeOne->getFirstName(); // prints 'Fql'
echo $objEmployeeOne->getLastName(); // prints 'Jiyik'
echo $objEmployeeOne->getAge(); // prints '30'
$objEmployeeTwo = new Employee('Opw', 'Jiyik', 34);
echo $objEmployeeTwo->getFirstName(); // prints 'Opw'
echo $objEmployeeTwo->getLastName(); // prints 'Jiyik'
echo $objEmployeeTwo->getAge(); // prints '34'
下图是 Employee 类及其一些实例的图形表示。
简而言之,类是我们可以用来创建结构化对象的蓝图。
封装
在上一节中,我们讨论了如何实例化 Employee 类的对象。 值得注意的是,$objEmployee
对象本身将类的属性和方法包装在一起。 换句话说,它向程序的其余部分隐藏了这些细节。 在 OOP 的世界里,这被称为数据封装。
封装是 OOP 的一个重要方面,它允许我们限制对对象的某些属性或方法的访问。 这将我们带到另一个讨论主题:访问级别。
访问级别
在类中定义属性或方法时,可以将其声明为具有以下三种访问级别之一—— public
、private
或 protected
。
public 访问
当我们将属性或方法声明为 public
时,可以从类之外的任何地方访问它。 可以从代码中的任何位置修改公共属性的值。
让我们看一个例子来理解 public
访问级别。
class Person
{
public $name;
public function getName()
{
return $this->name;
}
}
$person = new Person();
$person->name = 'jiyik';
echo $person->getName(); // prints 'jiyik'
正如我们在上面的示例中看到的,我们已将 name 属性声明为公共。 因此,我们可以从类之外的任何地方设置它,就像我们在这里所做的那样。
private 访问
当我们将属性或方法声明为私有时,只能从类中访问它。 这意味着我们需要定义 getter
和 setter
方法来获取和设置该属性的值。
同样,让我们修改前面的示例以了解私有访问级别。
class Person
{
private $name;
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}
$person = new Person();
$person->name = 'jiyik'; // 抛出错误
$person->setName('jiyik');
echo $person->getName(); // prints 'jiyik'
如果我们尝试从类外部访问私有属性,它将抛出致命错误无法访问私有属性 Person::$name
。 因此,我们需要使用 setter
方法设置私有属性的值,就像我们使用 setName
方法所做的那样。
我们可能希望将财产设为私有,这有充分的理由。 例如,如果该属性发生变化,也许应该采取一些行动(例如更新数据库或重新呈现模板)。 在这种情况下,我们可以定义一个 setter 方法并在属性更改时处理任何特殊逻辑。
protected 访问
最后,当我们将属性或方法声明为受保护时,定义它的同一个类和继承相关类的类都可以访问它。 我们将在下一节讨论继承,所以我们稍后会回到受保护的访问级别。
继承
继承是面向对象编程范例的一个重要方面,它允许我们通过扩展其他类来继承它们的属性和方法。 被继承的类称为父类,继承其他类的类称为子类。 当你实例化一个子类的对象时,它也继承了父类的属性和方法。
让我们看看下面的截图来理解继承的概念。
在上面的例子中,Person
类是父类,Employee
类扩展或继承了 Person 类,因此称为子类。
让我们尝试通过一个真实的例子来理解它是如何工作的。
class Person
{
protected $name;
protected $age;
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
private function callToPrivateNameAndAge()
{
return "{$this->name} is {$this->age} years old.";
}
protected function callToProtectedNameAndAge()
{
return "{$this->name} is {$this->age} years old.";
}
}
class Employee extends Person
{
private $designation;
private $salary;
public function getAge()
{
return $this->age;
}
public function setAge($age)
{
$this->age = $age;
}
public function getDesignation()
{
return $this->designation;
}
public function setDesignation($designation)
{
$this->designation = $designation;
}
public function getSalary()
{
return $this->salary;
}
public function setSalary($salary)
{
$this->salary = $salary;
}
public function getNameAndAge()
{
return $this->callToProtectedNameAndAge();
}
}
$employee = new Employee();
$employee->setName('Jiyik');
$employee->setAge(30);
$employee->setDesignation('Software Engineer');
$employee->setSalary('30K');
echo $employee->getName(); // prints 'Jiyik'
echo $employee->getAge(); // prints '30'
echo $employee->getDesignation(); // prints 'Software Engineer'
echo $employee->getSalary(); // prints '30K'
echo $employee->getNameAndAge(); // prints 'Jiyik is 30 years old.'
echo $employee->callToPrivateNameAndAge(); // produces 'Fatal Error'
这里需要注意的是,Employee 类已经使用了 extends
关键字来继承 Person 类。 现在,Employee 类可以访问 Person 类的所有声明为 public
或 protected
的属性和方法。 (它不能访问声明为 private
的成员。)
在上面的示例中,$employee 对象可以访问 Person 类中定义的 getName 和 setName 方法,因为它们被声明为公共的。
接下来,我们使用 Employee 类中定义的 getNameAndAge
方法访问了 callToProtectedNameAndAge
方法,因为它被声明为受保护的。 最后,$employee
对象无法访问 Person 类的 callToPrivateNameAndAge
方法,因为它被声明为私有的。
另一方面,我们可以使用 $employee
对象来设置 Person 类的 age 属性,就像我们在 Employee 类中定义的 setAge
方法中所做的那样,因为 age 属性被声明为受保护的。
这就是对继承的简要介绍。 它可以帮助我们减少代码重复,从而促进代码的可重用性。
多态性
多态性是面向对象编程领域的另一个重要概念,它指的是根据对象的数据类型以不同方式处理对象的能力。
例如,在继承的上下文中,如果子类想要改变父类方法的行为,它可以覆盖那个方法。 这称为方法覆盖。 让我们快速通过一个真实世界的例子来理解方法覆盖的概念。
class Message
{
public function formatMessage($message)
{
return printf("<i>%s</i>", $message);
}
}
class BoldMessage extends Message
{
public function formatMessage($message)
{
return printf("<b>%s</b>", $message);
}
}
$message = new Message();
$message->formatMessage('Hello World'); // prints '<i>Hello World</i>'
$message = new BoldMessage();
$message->formatMessage('Hello World'); // prints '<b>Hello World</b>'
如大家所见到的,我们通过在 BoldMessage 类中覆盖 formatMessage
方法来更改它的行为。 重要的是消息的格式根据对象类型而不同,无论它是父类还是子类的实例。
注意
:一些面向对象的语言也有一种方法重载,可以让你定义多个具有相同名称但参数数量不同的类方法。 这在 PHP 中不直接支持,但是有一些解决方法可以实现类似的功能。
结论
现在我们了解了使用 PHP 类进行面向对象编程的基础知识。 面向对象编程是一个庞大的主题,我们只是触及了它的复杂性的表面。 我确实希望本篇文章能帮助大家开始了解 OOP 的基础知识,并能激励大家继续学习更高级的 OOP 知识。