Lập trình hướng đối tượng – PHP(Phần 4)
$this
Nhìn chung, bạn có thể hiểu rằng $this dùng để tham chiếu đến đối tượng (object), còn self dùng để truy cập đến chính class. Tuy nhiên, có một vài điểm đặc trưng chi tiết hơn chúng ta sẽ bàn sâu hơn để thật sự hiểu rõ hơn về 2 khái niệm này.
Tôi sẽ đưa cho các bạn một ví dụ để dễ hình dung. Chúng ta có class Animal và một class khác Tiger kế thừa từ Animal. Class Tiger sẽ override phương thức whichClass() của class cha. Hãy cùng xem qua cài đặt của 2 class này, đầu tiên ta sẽ dùng $this:
class Animal { public function whichClass() { echo "I am an Animal!"; } public function sayClassName() { $this->whichClass(); } } class Tiger extends Animal { public function whichClass() { echo "I am a Tiger!"; } } $tigerObj = new Tiger(); $tigerObj->sayClassName();
Ta thấy rằng ở phương thức sayClassName() của class Animal chúng ta sử dụng từ khóa $this để gọi phương thức whichClass(). Khi ta khởi tạo đối tượng $tigerObj từ class Tiger và gọi phương thức sayClassName(), phương thức whichClass() của class Tiger sẽ được gọi chứ không phải phương thức whichClass() của class Animal. Do vậy kết quả ta nhận được sẽ là
I am a Tiger!
Lý do hết sức đơn giản, con trỏ $this luôn luôn tham chiếu đến đối tượng hiện tại (chính là object $tigerObj), và ta sẽ nhận được kết quả bằng việc gọi phương thức whichClass() của Tiger chứ không phải Animal. Nói một cách khác đây chính là ví dụ rất dễ hiểu về tính Đa hình (Polymorphism) trong PHP.
Sử dụng self thay cho $this
Hãy cùng thử thay đổi phương thức sayClassName() trong Animal và sử dụng từ khóa self thay cho $this:
class Animal { public function whichClass() { echo "I am an Animal!"; } public function sayClassName() { self::whichClass(); } } class Tiger extends Animal { public function whichClass() { echo "I am a Tiger!"; } } $tigerObj = new Tiger(); $tigerObj->sayClassName();
Về cơ bản ví dụ vẫn vậy chỉ thay đổi hết sức nhỏ. Ta sẽ thấy điều kỳ diệu xảy ra, kết quả chúng ta nhận được hoàn toàn khác với kết quả bên trên:
I am an Animal!
Điều gì đã xảy ra? Khi sử dụng self, bản thân nó sẽ biết mình phải gọi phương thức của chính Class chứa nó (tức là gọi hàm whichClass() của Animal). Như vậy việc sử dụng self thông qua ví dụ trên đã ngăn chặn tính đa hình bằng việc bỏ qua vtable
$this và self trong ngữ cảnh static function
Câu hỏi đặt ra: $this có sử dụng được trong static function không?
Hãy cùng tìm câu trả lời thông qua một ví dụ cài đặt sau:
class Animal { public static $name; public static function nameChange() { $this->name = "Programmer Interview"; } } $animalObj = new Animal(); $animalObj->nameChange();
Kết quả nhận được:
Lỗi này xảy ra vì chúng ta đang sử dụng con trỏ$this trong static function, tuy nhiên static function thực tế có thể được gọi mà không cần thông qua đối tượng được tạo ra từ class:
Animal::nameChange();
Nếu ta gọi kiểu này thì việc sử dụng con trỏ $this ở đây không hề có ý nghĩa do con trỏ $this tham chiếu đến đối tượng hiện tại mà với cách gọi trên không hề có một đối tượng nào chúng ta cần làm việc cùng. Trong ngữ cảnh này chúng ta sẽ gặp lỗi như trên.
Tiếp tục với việc thay sử dụng $this bằng self:
class Animal { public static $name; public static function nameChange() { self::$name = "Programmer Interview"; } } $animalObj = new Animal(); $animalObj->nameChange();
Đoạn code trên chạy ngon mà không hề có lỗi. Đây chính là lý do chính mà self được sử dụng – mục đích chính là truy cập vào thành phần tĩnh (static) trong class. Tiếp tục thực hiện một thay đổi nho nhỏ bằng việc không sử dụng static cho $name nữa:
class Animal { // $name is no longer a static variable.. public $name; public static function nameChange() { self::$name = "Programmer Interview"; } } $animalObj = new Animal(); $animalObj->nameChange();
Kết quả nhận được:
Fatal error: Access to undeclared static property: Animal::$name
Lỗi xảy ra do thành phần non-static sẽ không được phép truy cập trong một static function. Có thể hiểu đơn giản là static function có thể được gọi mà không cần đối tượng từ class, những thành phần non-static thì lại cần đối tượng của class.
Kết luận
Thông qua các ví dụ cụ thể hết sức thú vị, chúng ta đã hiểu hơn chút về self và $this cũng như cách sử dụng chúng. Ta sẽ tổng hợp lại thông qua bảng so sánh sau:
self | $this |
Tham chiếu đến Class hiện tại | Tham chiếu đến đối tượng (Object) hiện tại |
Dùng để gọi các hàm static và tham chiếu đến các thuộc tính static | Có thể dùng để gọi các hàm static |
Có thể dùng trong các hàm static (để truy cập đến các hàm hay thuộc tính static khác của Class) | Không nên dùng để gọi các thuộc tính static (vì sẽ không truy cập được mà lại tự tạo ra các thuộc tính non-static của đối tượng), nên dùng self trong trường hợp này. |
Khi được sử dụng sẽ ngăn chặn thể hiện của tính đa hình bằng việc bỏ qua vtable | Không thể sử dụng được trong các hàm static |
Hy vọng bài viết sẽ giúp các bạn hiểu hơn về self và $this.
Hàm __autoload()
Như đã được học từ trước thì các bạn cũng đã biết nếu muốn khởi tạo 1 class được khai báo trong một file khác thì chúng phải nhúng file đó vào trong file muốn khởi tạo thì mới có thể sử dụng được, nhưng nếu như chúng ta khởi tạo nhiều class trong một files mà các class đó được khai báo từ các file khác nhau thì sẽ phải include rất nhiều file. Điều đó sẽ khiến cho code của chúng ta trở lên rất rối và để khắc phục được điều đó thì chúng sẽ sử dụng hàm __autoload()
trong PHP
__autoload() là gì?
– __autoload()
là một hàm đặc biệt được PHP giới thiệu từ phiên bản 5.0, hàm __autoload()
này sẽ được gọi khi chúng ta khởi tạo một đối tượng không xác định được. Vì __autoload() là một hàm nên chúng ta sẽ khai báo nó như một hàm bình thường.
+Đầu tiên chúng ta khai báo hàm__autoload
và classConNguoi
trên cùng một file và đồng thời khởi tạo luôn class ConNguoi.
<?php //Khai báo hàm __autoload function __autoload($className) { echo 'Bạn vừa khởi tạo class: ' . $className; } //Khai báo class ConNguoi class ConNguoi { //Khai báo hàm khởi tạo public function __construct() { echo 'Class ConNguoi'; } } //Khởi tạo class ConNguoi $connguoi = new ConNguoi(); //Kết Quả: Class ConNguoi
+Như các bạn đã thấy thì hàm __autoload()
không được gọi khi chúng ta khởi tạo class được xác định. Giờ mình thử xóa bỏ đoạn khai báo class ConNguoi nhưng vẫn khởi tạo nó xem sao.
<?php //Khai báo hàm __autoload function __autoload($className) { echo 'Bạn vừa khởi tạo class: ' . $className; } //Khởi tạo class ConNguoi $connguoi = new ConNguoi(); //Kết Quả: //Bạn vừa khởi tạo class: ConNguoi // Fatal error: Class 'ConNguoi' not found
-Oh, Vậy là hàm __autoload()
đã được gọi. Như vậy câu nói trên là đúng nhé!
Autoload class thông thường.
VD1: Chúng ta có 3 file NguoiLon.php
, TreCon.php
và index.php
ở cùng cấp thư mục.
+File NguoiLon.php
, chứa class NguoiLon có nội dung như sau:
<?php class NguoiLon { public function __construct() { echo 'Class NguoiLon'; } }
+File TreCon.php
, chứa class TreCon có nội dung như sau:
<?php class TreCon { public function __construct() { echo 'Class TreCon'; } }
Nếu chúng ta sử dụng hàm __autoload()
thì sẽ chỉ phải khai báo ngắn gọn như sau:
<?php //khai báo hàm __autoload function __autoload($className) { //kiểm tra xem file tồn tại không if(file_exists($className . '.php')){ //Nếu tồn tại thì nhúng file vào. include_once $className . '.php'; } } //Khởi tạo 2 class $nguoilon = new NguoiLon(); //Kết Quả: Class NguoiLon $trecon = new TreCon(); //Kết Quả: Class TreCon
Autoload khi gọi phương thức tĩnh.
-Và hàm __autoload()
này cũng được gọi khi chúng ta gọi phương thức tĩnh không xác định.
VD: Chúng ta có 2 file ConNguoi.php
và một file index.php
ở cùng thư mục.
+File ConNguoi.php
, chứa class ConNguoi có nội dung như sau:
<?php class ConNguoi { private static $name = 'ConNguoi'; public function __construct() { } public static function getName() { echo static::$name; } }
+File index.php
, có nội dung như sau:
<?php //khai báo hàm __autoload function __autoload($className) { //kiểm tra xem file tồn tại không if(file_exists($className . '.php')){ //Nếu tồn tại thì nhúng file vào. include_once $className . '.php'; } } ConNguoi::getName(); //Kết Quả: ConNguoi
-Sau khi chạy file index.php thì như các bạn cũng đã thấy là nó vẫn chạy bình thường.
==>Theo như thống kê, nếu chúng ta sử dụng nhiều hơn 1 lần include (require) thì dùng __autoload() sẽ nhanh hơn, còn nếu chỉ include (require) 1 lần thì __autoload() chậm hơn 1 tí nhưng không đáng kể.