PHP 中的面向对象编程


我们可以想象我们的宇宙是由不同的物体组成的,如太阳、地球、月亮等。同样,我们可以想象我们的汽车是由不同的物体组成的,如车轮、方向盘、齿轮等。同样,存在面向对象的编程概念,它假设一切都是对象,并且使用不同的对象实现软件。

面向对象的概念

在详细介绍之前,让我们定义与面向对象编程相关的重要术语。

  • - 这是程序员定义的数据类型,包括本地函数和本地数据。您可以将类视为创建同一类型(或类)对象的许多实例的模板。

  • 对象- 由类定义的数据结构的单个实例。您定义一个类一次,然后创建属于该类的许多对象。对象也称为实例。

  • 成员变量- 这些是在类内部定义的变量。这些数据对于类的外部是不可见的,并且可以通过成员函数访问。一旦对象被创建,这些变量就被称为对象的属性。

  • 成员函数- 这些是在类内部定义的函数,用于访问对象数据。

  • 继承- 当通过继承父类的现有函数来定义类时,称为继承。这里子类将继承父类的全部或部分成员函数和变量。

  • 父类- 由另一个类继承的类。这也称为基类或超类。

  • 子类- 从另一个类继承的类。这也称为子类或派生类。

  • 多态性- 这是一个面向对象的概念,其中相同的函数可以用于不同的目的。例如,函数名称将保持不变,但它采用不同数量的参数并且可以执行不同的任务。

  • 重载- 一种多态性,其中部分或全部运算符根据其参数的类型具有不同的实现。类似地,函数也可以通过不同的实现来重载。

  • 数据抽象- 隐藏(抽象)实现细节的任何数据表示。

  • 封装- 指的是我们将所有数据和成员函数封装在一起形成一个对象的概念。

  • 构造函数- 指的是一种特殊类型的函数,只要类中有对象形成,就会自动调用该函数。

  • 析构函数- 是指一种特殊类型的函数,每当对象被删除或超出范围时就会自动调用。

定义 PHP 类

在 PHP 中定义新类的一般形式如下 -

<?php
   class phpClass {
      var $var1;
      var $var2 = "constant string";
      
      function myfunc ($arg1, $arg2) {
         [..]
      }
      [..]
   }
?>

这是每行的描述 -

  • 特殊形式class,后跟要定义的类的名称。

  • 一组包含任意数量的变量声明和函数定义的大括号。

  • 变量声明以特殊形式var开头,后跟常规的 $ 变量名称;它们也可能初始分配给一个常量值。

  • 函数定义看起来很像独立的 PHP 函数,但对于类来说是本地的,并且将用于设置和访问对象数据。

例子

这是一个定义 Books 类型类的示例 -

<?php
   class Books {
      /* Member variables */
      var $price;
      var $title;
      
      /* Member functions */
      function setPrice($par){
         $this->price = $par;
      }
      
      function getPrice(){
         echo $this->price ."<br/>";
      }
      
      function setTitle($par){
         $this->title = $par;
      }
      
      function getTitle(){
         echo $this->title ." <br/>";
      }
   }
?>

变量$this是一个特殊变量,它引用同一个对象,即。本身。

在 PHP 中创建对象

一旦定义了类,您就可以创建任意数量的该类类型的对象。以下是如何使用new运算符创建对象的示例。

$physics = new Books;
$maths = new Books;
$chemistry = new Books;

这里我们创建了三个对象,这些对象是相互独立的,各自存在。接下来我们将了解如何访问成员函数和过程成员变量。

调用成员函数

创建对象后,您将能够调用与该对象相关的成员函数。一个成员函数只能处理相关对象的成员变量。

下面的示例展示了如何通过调用成员函数来设置三本书的标题和价格。

$physics->setTitle( "Physics for High School" );
$chemistry->setTitle( "Advanced Chemistry" );
$maths->setTitle( "Algebra" );

$physics->setPrice( 10 );
$chemistry->setPrice( 15 );
$maths->setPrice( 7 );

现在您调用另一个成员函数来获取上面示例中设置的值 -

$physics->getTitle();
$chemistry->getTitle();
$maths->getTitle();
$physics->getPrice();
$chemistry->getPrice();
$maths->getPrice();

这将产生以下结果 -

Physics for High School
Advanced Chemistry
Algebra
10
15
7

构造函数

构造函数是特殊类型的函数,每当创建对象时都会自动调用它。因此,我们通过构造函数初始化许多东西,充分利用这种Behave。

PHP 提供了一个名为__construct()的特殊函数来定义构造函数。您可以将任意数量的参数传递到构造函数中。

以下示例将为 Books 类创建一个构造函数,并将在创建对象时初始化书籍的价格和标题。

function __construct( $par1, $par2 ) {
   $this->title = $par1;
   $this->price = $par2;
}

现在我们不需要单独调用set函数来设置价格和标题。我们只能在对象创建时初始化这两个成员变量。检查下面的示例 -

$physics = new Books( "Physics for High School", 10 );
$maths = new Books ( "Advanced Chemistry", 15 );
$chemistry = new Books ("Algebra", 7 );

/* Get those set values */
$physics->getTitle();
$chemistry->getTitle();
$maths->getTitle();

$physics->getPrice();
$chemistry->getPrice();
$maths->getPrice();

这将产生以下结果 -

  Physics for High School
  Advanced Chemistry
  Algebra
  10
  15
  7

析构函数

与构造函数一样,您可以使用函数__destruct()定义析构函数。您可以在析构函数中释放所有资源。

遗产

PHP 类定义可以选择使用 extends 子句从父类定义继承。语法如下 -

class Child extends Parent {
   <definition body>
}

继承的效果是子类(或子类或派生类)具有以下特征 -

  • 自动拥有父类的所有成员变量声明。

  • 自动具有与父级相同的所有成员函数,(默认情况下)其工作方式与这些函数在父级中的工作方式相同。

以下示例继承 Books 类并根据需求添加更多功能。

class Novel extends Books {
   var $publisher;
   
   function setPublisher($par){
      $this->publisher = $par;
   }
   
   function getPublisher(){
      echo $this->publisher. "<br />";
   }
}

现在,除了继承的函数之外,Novel 类还保留了两个附加的成员函数。

函数重写

子类中的函数定义会覆盖父类中同名的函数定义。在子类中,我们可以修改从父类继承的函数的定义。

在以下示例中, getPrice 和 getTitle 函数被重写以返回一些值。

function getPrice() {
   echo $this->price . "<br/>";
   return $this->price;
}
   
function getTitle(){
   echo $this->title . "<br/>";
   return $this->title;
}

公众会员

除非另有指定,否则类的属性和方法都是公共的。也就是说,可以在三种可能的情况下访问它们 -

  • 从声明它的类外部

  • 从声明它的类中

  • 从实现声明它的类的另一个类中

到目前为止,我们将所有成员视为公共成员。如果您希望限制类成员的可访问性,那么您可以将类成员定义为privateprotected

私人会员

通过将成员指定为私有,可以限制其对声明它的类的可访问性。私有成员不能从继承声明它的类的类中引用,也不能从类外部访问。

可以通过在成员前面使用private关键字将类成员设为私有。

class MyClass {
   private $car = "skoda";
   $driver = "SRK";
   
   function __construct($par) {
      // Statements here run every time
      // an instance of the class
      // is created.
   }
   
   function myPublicFunction() {
      return("I'm visible!");
   }
   
   private function myPrivateFunction() {
      return("I'm  not visible outside!");
   }
}

MyClass类被另一个类使用 extends 继承时, myPublicFunction() 将可见, $driver 也将可见。扩展类不会知道或访问 myPrivateFunction 和 $car,因为它们被声明为私有。

受保护会员

受保护的属性或方法可以在声明它的类以及扩展该类的类中访问。受保护的成员在这两种类别之外不可用。可以通过在成员前面使用protected关键字来使类成员受到保护。

这是 MyClass 的不同版本 -

class MyClass {
   protected $car = "skoda";
   $driver = "SRK";

   function __construct($par) {
      // Statements here run every time
      // an instance of the class
      // is created.
   }
   
   function myPublicFunction() {
      return("I'm visible!");
   }
   
   protected function myPrivateFunction() {
      return("I'm  visible in child class!");
   }
}

接口

定义接口是为了向实现者提供通用的函数名称。不同的实现者可以根据自己的需求实现这些接口。可以说,接口是开发人员实现的骨架。

从 PHP5 开始,可以定义一个接口,如下所示 -

interface Mail {
   public function sendMail();
}

然后,如果另一个类实现了该接口,如下所示 -

class Report implements Mail {
   // sendMail() Definition goes here
}

常数

常量有点像变量,因为它保存一个值,但实际上更像是函数,因为常量是不可变的。一旦声明了一个常量,它就不会改变。

声明一个常量很容易,就像在这一版本的 MyClass 中所做的那样 -

class MyClass {
   const requiredMargin = 1.7;
   
   function __construct($incomingValue) {
      // Statements here run every time
      // an instance of the class
      // is created.
   }
}

在这个类中,requiredMargin 是一个常量。它是用关键字 const 声明的,在任何情况下都不能将其更改为 1.7 以外的任何内容。请注意,常量名称不像变量名称那样以 $ 开头。

抽象类

抽象类是不能实例化的类,只能继承。您可以使用关键字abstract声明一个抽象类,如下所示:

当从抽象类继承时,父类声明中标记为抽象的所有方法都必须由子类定义;此外,这些方法必须以相同的可见性定义。

abstract class MyAbstractClass {
   abstract function myAbstractFunction() {
   }
}

请注意,抽象类内的函数定义也必须以关键字abstract 开头。在非抽象类中定义抽象函数是不合法的。

静态关键字

将类成员或方法声明为静态使它们无需实例化即可访问。声明为静态的成员无法使用实例化的类对象进行访问(尽管静态方法可以)。

尝试以下示例 -

<?php
   class Foo {
      public static $my_static = 'foo';
      
      public function staticValue() {
         return self::$my_static;
      }
   }
	
   print Foo::$my_static . "\n";
   $foo = new Foo();
   
   print $foo->staticValue() . "\n";
?>	

最终关键词

PHP 5 引入了final 关键字,它通过在定义前加上final 前缀来防止子类重写方法。如果类本身被定义为final,那么它就不能被扩展。

以下示例导致致命错误:无法覆盖最终方法 BaseClass::moreTesting()

<?php

   class BaseClass {
      public function test() {
         echo "BaseClass::test() called<br>";
      }
      
      final public function moreTesting() {
         echo "BaseClass::moreTesting() called<br>";
      }
   }
   
   class ChildClass extends BaseClass {
      public function moreTesting() {
         echo "ChildClass::moreTesting() called<br>";
      }
   }
?>

调用父构造函数

我们不是为子类编写一个全新的构造函数,而是通过显式调用父类的构造函数来编写它,然后执行子类实例化所需的任何操作。这是一个简单的例子 -

class Name {
   var $_firstName;
   var $_lastName;
   
   function Name($first_name, $last_name) {
      $this->_firstName = $first_name;
      $this->_lastName = $last_name;
   }
   
   function toString() {
      return($this->_lastName .", " .$this->_firstName);
   }
}
class NameSub1 extends Name {
   var $_middleInitial;
   
   function NameSub1($first_name, $middle_initial, $last_name) {
      Name::Name($first_name, $last_name);
      $this->_middleInitial = $middle_initial;
   }
   
   function toString() {
      return(Name::toString() . " " . $this->_middleInitial);
   }
}

在此示例中,我们有一个父类 (Name),它有一个二参数构造函数,还有一个子类 (NameSub1),它有一个三参数构造函数。NameSub1 的构造函数通过使用 :: 语法显式调用其父构造函数(传递其两个参数)然后设置一个附加字段来运行。类似地,NameSub1 根据其重写的父函数定义其非构造函数 toString() 函数。

注意- 可以使用与类名称相同的名称来定义构造函数。它在上面的例子中被定义。