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

建構函式

建構函式是特殊型別的函式,每當建立物件時都會自動呼叫。因此,我們可以充分利用這種行為,透過建構函式初始化許多東西。

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

公有成員

除非你另行指定,否則類的屬性和方法都是公有的。也就是說,它們可以在三種情況下被訪問:

  • 從宣告它的類的外部

  • 從宣告它的類的內部

  • 從實現宣告它的類的另一個類中

到目前為止,我們看到的都是公有成員。如果你希望限制類成員的可訪問性,那麼你可以將類成員定義為私有受保護

私有成員

透過將成員指定為私有,你可以將其可訪問性限制在宣告它的類中。私有成員不能被繼承宣告它的類的類引用,也不能從類外部訪問。

可以使用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開頭。在非抽象類中,不允許有抽象函式定義。

Static關鍵字

將類成員或方法宣告為靜態,使其無需例項化類即可訪問。宣告為靜態的成員不能使用例項化的類物件訪問(儘管可以使用靜態方法)。

嘗試以下示例:

<?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";
?>	

Final關鍵字

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() 函式。

注意 - 建構函式可以用與類名相同的名稱定義。在上面的例子中定義了它。

廣告