Object Oriented Programming in PHP

Chúng ta có thể hình dung vũ trụ của mình được tạo thành từ các đối tượng khác nhau như mặt trời, trái đất, mặt trăng, v.v. Tương tự, chúng ta có thể hình dung chiếc xe của mình được tạo thành từ các đối tượng khác nhau như bánh xe, vô lăng, hộp số, v.v. Cũng theo cách đó, có các khái niệm lập trình hướng đối tượng, trong đó coi mọi thứ là một đối tượng và triển khai phần mềm bằng cách sử dụng các đối tượng khác nhau.

Object Oriented Concepts

Trước khi đi vào chi tiết, hãy định nghĩa các thuật ngữ quan trọng liên quan đến Lập trình Hướng đối tượng.

  • Class − Đây là một kiểu dữ liệu được định nghĩa bởi lập trình viên, bao gồm các hàm cục bộ cũng như dữ liệu cục bộ. Bạn có thể nghĩ về một lớp (class) như một mẫu để tạo ra nhiều thể hiện (instances) của cùng một loại (hoặc lớp) đối tượng.

  • Object − Một thể hiện cá nhân của cấu trúc dữ liệu được định nghĩa bởi một lớp. Bạn định nghĩa một lớp một lần và sau đó tạo nhiều đối tượng thuộc về nó. Các đối tượng cũng được gọi là thể hiện.

  • Member Variable − Đây là các biến được định nghĩa bên trong một lớp. Dữ liệu này sẽ không thể nhìn thấy từ bên ngoài lớp và có thể được truy cập thông qua các hàm thành viên. Những biến này được gọi là thuộc tính của đối tượng khi một đối tượng được tạo ra.

  • Member function − Đây là các hàm được định nghĩa bên trong một lớp và được sử dụng để truy cập dữ liệu của đối tượng.

  • Inheritance − Khi một lớp được định nghĩa bằng cách kế thừa các chức năng hiện có của lớp cha, thì điều này được gọi là kế thừa. Ở đây, lớp con sẽ kế thừa tất cả hoặc một vài hàm thành viên và biến của lớp cha.

  • Parent class − Một lớp được kế thừa bởi một lớp khác. Điều này cũng được gọi là lớp cơ sở hoặc lớp cha.

  • Child Class − Một lớp kế thừa từ một lớp khác. Điều này cũng được gọi là lớp con hoặc lớp dẫn xuất.

  • Polymorphism − Đây là một khái niệm lập trình hướng đối tượng, nơi cùng một hàm có thể được sử dụng cho nhiều mục đích khác nhau. Ví dụ, tên hàm sẽ giữ nguyên nhưng nó có thể nhận số lượng tham số khác nhau và thực hiện các nhiệm vụ khác nhau.

  • Overloading − một loại đa hình trong đó một số hoặc tất cả các toán tử có các triển khai khác nhau tùy thuộc vào loại của các đối số của chúng. Tương tự, các hàm cũng có thể được nạp chồng với các triển khai khác nhau.

  • Data Abstraction − Bất kỳ biểu diễn nào của dữ liệu mà trong đó các chi tiết thực hiện được ẩn đi (trừu tượng hóa).

  • Encapsulation − đề cập đến một khái niệm trong đó chúng ta đóng gói tất cả dữ liệu và các hàm thành viên lại với nhau để tạo thành một đối tượng.

  • Constructor − đề cập đến một loại hàm đặc biệt sẽ được gọi tự động mỗi khi có một đối tượng được tạo ra từ một lớp.

  • Destructor − đề cập đến một loại hàm đặc biệt sẽ được gọi tự động mỗi khi một đối tượng bị xóa hoặc ra khỏi phạm vi.

Defining PHP Classes

Cấu trúc chung để định nghĩa một lớp mới trong PHP như sau −

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

Dưới đây là mô tả cho từng dòng −

  • Dạng đặc biệt class , theo sau là tên của lớp mà bạn muốn định nghĩa.

  • Một tập hợp các dấu ngoặc nhọn bao gồm bất kỳ số lượng khai báo biến và định nghĩa hàm nào.

  • Các khai báo biến bắt đầu bằng dạng đặc biệt var , tiếp theo là tên biến theo quy ước $; chúng cũng có thể có một gán ban đầu cho một giá trị hằng.

  • Các định nghĩa hàm trông giống như các hàm PHP độc lập nhưng là cục bộ cho lớp và sẽ được sử dụng để thiết lập và truy cập dữ liệu của đối tượng.

Example

Dưới đây là một ví dụ định nghĩa một lớp loại Sách −

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

Biến $this là một biến đặc biệt và nó tham chiếu đến cùng một đối tượng, tức là chính nó.

Creating Objects in PHP

Khi bạn đã định nghĩa lớp của mình, bạn có thể tạo ra nhiều đối tượng tùy thích từ loại lớp đó. Dưới đây là một ví dụ về cách tạo đối tượng bằng cách sử dụng toán tử new .

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

Ở đây, chúng tôi đã tạo ra ba đối tượng và các đối tượng này độc lập với nhau và sẽ tồn tại riêng biệt. Tiếp theo, chúng ta sẽ xem cách truy cập vào hàm thành viên và xử lý các biến thành viên.

Calling Member Functions

Sau khi tạo các đối tượng của bạn, bạn sẽ có thể gọi các hàm thành viên liên quan đến đối tượng đó. Một hàm thành viên sẽ chỉ có thể xử lý biến thành viên của đối tượng liên quan.

Ví dụ dưới đây cho thấy cách thiết lập tiêu đề và giá cho ba cuốn sách bằng cách gọi các hàm thành viên.

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

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

Bây giờ bạn gọi các hàm thành viên khác để lấy các giá trị được thiết lập trong ví dụ trên −

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

Điều này sẽ tạo ra kết quả sau −

Physics for High School
Advanced Chemistry
Algebra
10
15
7

Constructor Functions

Hàm khởi tạo là một loại hàm đặc biệt được gọi tự động mỗi khi một đối tượng được tạo ra. Vì vậy, chúng ta tận dụng tối đa hành vi này bằng cách khởi tạo nhiều thứ thông qua các hàm khởi tạo.

PHP cung cấp một hàm đặc biệt gọi là __construct() để định nghĩa một hàm khởi tạo. Bạn có thể truyền vào bao nhiêu đối số tùy ý vào hàm khởi tạo.

Ví dụ sau sẽ tạo một hàm khởi tạo cho lớp Books và nó sẽ khởi tạo giá và tiêu đề cho cuốn sách vào thời điểm tạo đối tượng.

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

Bây giờ chúng ta không cần gọi hàm set riêng biệt để thiết lập giá và tiêu đề. Chúng ta có thể khởi tạo hai biến thành viên này ngay tại thời điểm tạo đối tượng. Kiểm tra ví dụ dưới đây −

$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();

Điều này sẽ tạo ra kết quả sau −

Physics for High School
Advanced Chemistry
Algebra
10
15
7

Destructor

Giống như một hàm khởi tạo, bạn có thể định nghĩa một hàm hủy bằng cách sử dụng hàm __destruct() . Bạn có thể giải phóng tất cả các tài nguyên bên trong một hàm hủy.

Inheritance

Các định nghĩa lớp trong PHP có thể tùy chọn kế thừa từ định nghĩa lớp cha bằng cách sử dụng từ khóa extends. Cú pháp như sau −

class Child extends Parent {
   <definition body>
}

Ảnh hưởng của kế thừa là lớp con (hoặc lớp con, hoặc lớp dẫn xuất) có các đặc điểm sau:

  • Tự động có tất cả các khai báo biến thành viên của lớp cha.

  • Tự động có tất cả các hàm thành viên giống như lớp cha, mà (mặc định) sẽ hoạt động giống như các hàm đó trong lớp cha.

Ví dụ sau kế thừa lớp Books và thêm nhiều chức năng dựa trên yêu cầu.

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

Bây giờ, ngoài các hàm kế thừa, lớp Novel giữ hai hàm thành viên bổ sung.

Function Overriding

Các định nghĩa hàm trong các lớp con ghi đè các định nghĩa có cùng tên trong các lớp cha. Trong một lớp con, chúng ta có thể sửa đổi định nghĩa của một hàm được kế thừa từ lớp cha.

Trong ví dụ sau, các hàm getPrice và getTitle được ghi đè để trả về một số giá trị.

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

Public Members

Trừ khi bạn chỉ định khác đi, các thuộc tính và phương thức của một lớp sẽ là công khai. Nói cách khác, chúng có thể được truy cập trong ba tình huống có thể xảy ra:

  • Từ bên ngoài lớp trong đó nó được khai báo.

  • Từ bên trong lớp mà nó được khai báo.

  • Từ bên trong một lớp khác thực hiện lớp mà nó được khai báo.

Cho đến nay, chúng ta đã thấy tất cả các thành viên là thành viên công khai. Nếu bạn muốn giới hạn khả năng truy cập của các thành viên trong một lớp, thì bạn định nghĩa các thành viên của lớp là private hoặc protected .

Private members

Bằng cách chỉ định một thành viên là private, bạn giới hạn khả năng truy cập của nó chỉ trong lớp mà nó được khai báo. Thành viên private không thể được tham chiếu từ các lớp kế thừa lớp mà nó được khai báo và không thể được truy cập từ bên ngoài lớp.

Một thành viên lớp có thể được đặt thành riêng tư bằng cách sử dụng từ khóa private trước thành viên đó.

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!");
   }
}

Khi lớp MyClass được kế thừa bởi một lớp khác sử dụng extends, myPublicFunction() sẽ có thể truy cập, cũng như $driver. Lớp mở rộng sẽ không có bất kỳ nhận thức hoặc quyền truy cập nào vào myPrivateFunction và $car, vì chúng được khai báo là private.

Protected members

Một thuộc tính hoặc phương thức được bảo vệ có thể truy cập trong lớp mà nó được khai báo, cũng như trong các lớp mở rộng lớp đó. Các thành viên được bảo vệ không có sẵn bên ngoài hai loại lớp đó. Một thành viên của lớp có thể được đặt thành protected bằng cách sử dụng từ khóa protected trước thành viên đó.

Dưới đây là phiên bản khác của 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!");
   }
}

Interfaces

Các interface được định nghĩa để cung cấp các tên hàm chung cho những người thực hiện. Các người thực hiện khác nhau có thể triển khai những interface đó theo yêu cầu của họ. Bạn có thể nói rằng, các interface là khung xương mà các nhà phát triển thực hiện.

Kể từ PHP5, có thể định nghĩa một giao diện, như sau −

interface Mail {
   public function sendMail();
}

Sau đó, nếu một lớp khác thực hiện giao diện đó, như thế này −

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

Constants

Một hằng số giống như một biến, vì nó giữ một giá trị, nhưng thực sự giống như một hàm vì một hằng số là không thay đổi. Khi bạn khai báo một hằng số, nó sẽ không thay đổi.

Việc khai báo một hằng số là dễ dàng, như được thực hiện trong phiên bản này của MyClass −

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

Trong lớp này, requiredMargin là một hằng số. Nó được khai báo bằng từ khóa const, và trong bất kỳ trường hợp nào cũng không thể thay đổi thành giá trị khác ngoài 1.7. Lưu ý rằng tên của hằng số không có dấu $ ở đầu, như các tên biến.

Abstract Classes

Một lớp trừu tượng là lớp không thể được khởi tạo, chỉ có thể được kế thừa. Bạn khai báo một lớp trừu tượng với từ khóa abstract , như sau −

Khi kế thừa từ một lớp trừu tượng, tất cả các phương thức được đánh dấu là trừu tượng trong khai báo lớp cha phải được định nghĩa bởi lớp con; ngoài ra, các phương thức này phải được định nghĩa với cùng một mức độ truy cập.

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

Lưu ý rằng các định nghĩa hàm bên trong một lớp trừu tượng cũng phải được đứng trước từ khóa abstract. Việc có các định nghĩa hàm trừu tượng bên trong một lớp không trừu tượng là không hợp pháp.

Static Keyword

Khai báo các thành viên hoặc phương thức của lớp là tĩnh (static) cho phép chúng có thể được truy cập mà không cần phải khởi tạo một đối tượng của lớp. Một thành viên được khai báo là tĩnh không thể được truy cập bằng một đối tượng đã được khởi tạo của lớp (mặc dù một phương thức tĩnh có thể).

Hãy thử ví dụ sau −

<?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 Keyword

PHP 5 giới thiệu từ khóa final, ngăn chặn các lớp con ghi đè một phương thức bằng cách thêm tiền tố final vào định nghĩa. Nếu lớp đó được định nghĩa là final thì nó không thể được mở rộng.

Ví dụ sau dẫn đến lỗi nghiêm trọng: Không thể ghi đè phương thức cuối cùng 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>";
      }
   }
?>

Calling parent constructors

Thay vì viết một constructor hoàn toàn mới cho lớp con, hãy gọi constructor của lớp cha một cách rõ ràng và sau đó thực hiện những gì cần thiết để khởi tạo lớp con. Dưới đây là một ví dụ đơn giản -

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

Trong ví dụ này, chúng ta có một lớp cha (Name), có một hàm khởi tạo với hai tham số, và một lớp con (NameSub1), có một hàm khởi tạo với ba tham số. Hàm khởi tạo của NameSub1 hoạt động bằng cách gọi hàm khởi tạo của lớp cha một cách rõ ràng bằng cách sử dụng cú pháp :: (truyền hai tham số của nó) và sau đó thiết lập một trường bổ sung. Tương tự, NameSub1 định nghĩa hàm toString() không phải là hàm khởi tạo của nó dựa trên hàm của lớp cha mà nó ghi đè.

NOTE − Một hàm khởi tạo có thể được định nghĩa với cùng tên như tên của một lớp. Nó được định nghĩa trong ví dụ trên.