# I. Cơ bản về lập trình hướng đối tượng trong PHP
## 1. Abstraction - Tính trừu tượng hoá
* Mô tả các thực thể của đời sống thực trong mã nguồn phần mềm
* Ẩn đi các chi tiết triển khai của một thực thể và chỉ tập trung vào các khái niệm quan trọng.
* Abstraction giúp tạo ra các mô hình đơn giản và hiệu quả để làm việc với các đối tượng và giảm sự phức tạp của hệ thống.
* Abstraction mô tả một thực thể của đời sống thực qua các khái niệm **class, object, property, method, constructor, destructor**
```php
class Car {
// Property - Thuộc tính (dữ liệu)
public $brand;
public $model;
protected $started = false;
public function __construct($brand, $model) {
$this->model = $model;
$this->brand = $brand;
}
public function isStarted() {
return $this->started;
}
// Method - Phương thức (hành động)
public function start() {
$this->started = true;
echo("($this->brand, $this->model) started");
}
}
```
#### Tạo một đối tượng từ lớp:
```php
$myCar = new Car('Toyota', 'Camry');
$myCar2 = new Car('Toyota', 'Camry');
$myCar->model = "Camry"; # You can do it
$myCar->started = true;
$myCar->start();
```
> Chú ý từ khoá $this
## 2. Inheritance - Tính kế thừa
Lớp con có thể kế thừa từ lớp cha:
```php
class SportsCar extends Car {
// Thuộc tính mới
public $topSpeed;
public function __construct($brand, $model, $topSpd) {
parent::__construct($brand, $model);
$this->topSpeed = $topSpd;
}
// Sửa phương thức cũ
public function start() {
return parent::start() . ". Chúc bạn lái xe an toàn";
}
// Phương thức mới
public function displayInfo() {
return "Brand: $this->brand, Model: $this->model, Top Speed: $this->topSpeed";
}
}
$sportsCar = new SportsCar('Ferrari', '458 Italia', '210 mph');
$sportsCar->start();
$sportsCar->displayInfo();
```
> Chú ý từ khoá parent
## 3. Encapsulation - Tính đóng gói
Encapsulation giúp ẩn giấu chi tiết bên trong một lớp và chỉ cho phép truy cập thông qua các phương thức:
```php
class BankAccount {
private $balance;
public function __construct($initialBalance) {
$this->balance = $initialBalance;
}
public function getBalance() {
return $this->balance;
}
public function deposit($amount) {
$this->balance += $amount;
}
public function withdraw($amount) {
if ($amount <= $this->balance) {
$this->balance -= $amount;
} else {
echo "Insufficient funds";
}
}
}
$account = new BankAccount(1000);
echo $account->getBalance(); // 1000
$account->deposit(500);
$account->withdraw(200);
```
## 4. Polymorphism - Tính đa hình
Đa hình cho phép một phương thức có thể thực hiện khác nhau dựa trên đối tượng gọi nó:
```php
class Shape {
public function drawAt($x, $y) {
return "Do nothing at $x, $y";
}
}
class Circle extends Shape {
public function drawAt($x, $y) {
return "Draw a circle at $x, $y";
}
}
class Rectangle extends Shape {
public function drawAt($x, $y) {
return "Draw a rectange at $x, $y";
}
}
class Screen {
public function draw($shape, $x, $y) {
$shape->drawAt($x, $y);
}
}
$screen = new Screen();
$circle = new Circle();
$rect = new Rectangle();
echo $screen->draw($circle, 100, 100);
echo $screen->draw($rect, 200, 200);
```
# II. Một số khái niệm Nâng cao
## 1. Namespace
Namespace trong lập trình là một cách để nhóm các thành phần, như lớp, interface, hàm và hằng số, lại với nhau để giảm xung đột tên và tạo ra một cách tổ chức hợp lý cho mã nguồn.
### Tại sao cần sử dụng Namespace?
1. **Tránh Xung Đột Tên (Name Collisions):** Khi có nhiều đoạn mã nguồn khác nhau, có thể xảy ra xung đột tên giữa các lớp, hàm, hoặc hằng số. Sử dụng namespace giúp tránh xung đột này.
2. **Tổ Chức Mã Nguồn:** Namespace giúp tổ chức mã nguồn một cách có tổ chức hơn, đặc biệt là khi bạn làm việc với mã nguồn lớn hoặc với nhiều nhóm phát triển khác nhau.
### Cú Pháp Namespace:
```php
namespace MyNamespace;
// Khai báo lớp trong namespace
class MyClass {
// ...
}
// Khai báo hàm trong namespace
function myFunction() {
// ...
}
// Khai báo hằng số trong namespace
const MY_CONSTANT = 42;
```
> Chú ý: Từ khoá namespace phải đặt ở đầu file php và chỉ được sử dụng 1 lần trong file
### Sử Dụng Namespace:
```php
// Sử dụng các thành phần từ namespace
use MyNamespace\MyClass;
use function MyNamespace\myFunction;
use const MyNamespace\MY_CONSTANT;
// Tạo một đối tượng từ lớp trong namespace
$obj = new MyClass();
// Gọi hàm từ namespace
myFunction();
// Sử dụng hằng số từ namespace
echo MY_CONSTANT;
```
### Namespace Lồng Nhau:
Bạn cũng có thể lồng nhiều namespace vào nhau để tạo ra các tầng tổ chức phức tạp hơn.
```php
namespace Outer\Inner;
class MyClass {
// ...
}
// Sử dụng
$obj = new Outer\Inner\MyClass();
```
### Global Namespace:
Nếu không có namespace nào được xác định, thì các đối tượng, hàm, và hằng số sẽ thuộc về global namespace.
### Ví dụ Sử Dụng Namespace:
```php
// File: animals.php
namespace Animals;
class Dog {
public function bark() {
echo "Woof! Woof!";
}
}
// File: birds.php
namespace Birds;
class Sparrow {
public function sing() {
echo "Chirp! Chirp!";
}
}
// File: main.php
require 'animals.php';
require 'birds.php';
// Sử dụng các lớp từ các namespace khác nhau
use Animals\Dog;
use Birds\Sparrow;
$dog = new Dog();
$dog->bark();
$sparrow = new Sparrow();
$sparrow->sing();
```
Trong ví dụ trên, có hai namespace là `Animals` và `Birds`, mỗi namespace chứa một lớp. Trong file `main.php`, chúng ta sử dụng `use` để định nghĩa rõ ràng rằng `Dog` đến từ `Animals` và `Sparrow` đến từ `Birds`.
## 2. Trait
Trait là một tính năng trong PHP giúp tái sử dụng mã nguồn trong lập trình hướng đối tượng mà không cần sử dụng kế thừa đa nhiệm (multiple inheritance). Trait cho phép chèn một tập hợp các phương thức vào trong một lớp mà không cần sự kế thừa.
### Cú Pháp Trait:
```php
trait MyTrait {
public function method1() {
// ...
}
public function method2() {
// ...
}
}
```
### Sử Dụng Trait trong Lớp:
```php
class MyClass {
use MyTrait;
// Các phương thức khác của lớp
}
```
### Sử Dụng Nhiều Traits:
```php
trait Trait1 {
// ...
}
trait Trait2 {
// ...
}
class MyClass {
use Trait1, Trait2;
// ...
}
```
### Ưu Điểm của Trait:
1. **Tái Sử Dụng Mã Nguồn:** Trait giúp tái sử dụng mã nguồn một cách linh hoạt và dễ dàng.
2. **Giải Quyết Vấn Đề Đa Kế Thừa (Multiple Inheritance):** Trait giúp giải quyết vấn đề đa kế thừa mà không phải làm cho cấu trúc kế thừa trở nên phức tạp.
3. **Tăng Cường Phương Thức (Method Composition):** Bạn có thể sử dụng nhiều traits để xây dựng các phương thức tốt hơn.
### Ví dụ Sử Dụng Trait:
```php
trait Loggable {
public function log($message) {
echo "Logging: $message\n";
}
}
trait Timestampable {
public function timestamp() {
echo "Timestamp: " . date('Y-m-d H:i:s') . "\n";
}
}
class User {
use Loggable, Timestampable;
public function createUser() {
$this->log("User created");
$this->timestamp();
// Các công việc tạo user khác...
}
}
$user = new User();
$user->createUser();
```
Trong ví dụ này, `User` class sử dụng cả hai traits `Loggable` và `Timestampable`, cho phép nó có thể ghi log và lấy timestamp một cách dễ dàng.
### Mức độ ưu tiên
Khi một lớp sử dụng nhiều traits và có những phương thức giống nhau được định nghĩa trong các traits đó, có một quy tắc ưu tiên được áp dụng. Quy tắc này xác định thứ tự mà các traits được sử dụng được ưu tiên khi xảy ra trùng lặp phương thức.
#### Quy tắc Ưu Tiên Của Trait:
1. **Các Trait được Khai Báo Trước Có Ưu Tiên Cao Hơn:** Các trait được khai báo trước trong danh sách `use` sẽ có ưu tiên cao hơn so với các trait khai báo sau đó.
2. **Trùng Lặp Phương Thức Ở Các Trait:**
- Nếu một phương thức có tên giống nhau xuất hiện trong nhiều traits, trait được khai báo trước sẽ có ưu tiên.
- Nếu một lớp sử dụng các trait có các phương thức trùng tên, lớp sẽ sử dụng phương thức của trait được khai báo trước.
#### Ví dụ:
```php
trait Trait1 {
public function hello() {
echo "Hello from Trait1\n";
}
}
trait Trait2 {
public function hello() {
echo "Hello from Trait2\n";
}
}
class MyClass {
use Trait1, Trait2 {
hello as hello2; // Sử dụng hello từ Trait1, không sử dụng từ Trait2
}
}
$obj = new MyClass();
$obj->hello(); // Kết quả: Hello from Trait1
$obj->hello2(); // Kết quả: Hello from Trait1
```
Trong ví dụ này, `MyClass` sử dụng cả hai traits `Trait1` và `Trait2`, nhưng bằng cách sử dụng `insteadof`, nó chỉ sử dụng phương thức `hello` từ `Trait1` và không sử dụng phương thức `hello` từ `Trait2`.
## 3. Abstract Class
Một abstract class là một lớp trong lập trình hướng đối tượng mà không thể được khởi tạo (instantiated) trực tiếp. Abstract class thường chứa một hoặc nhiều phương thức abstract, đó là các phương thức chỉ được khai báo mà không có triển khai. Các lớp con của abstract class phải triển khai (implement) các phương thức abstract này.
```php
// Abstract class
abstract class Shape {
abstract public function drawAt($x, $y);
}
class Circle extends Shape {
private $radius;
private $color;
public function __construct($radius, $color) {
$this->radius = $radius;
$this->color = $color;
}
public function getArea() {
return 3.14 * $this->radius * $this->radius;
}
public function getColor() {
return $this->color;
}
}
$circle = new Circle(5, "red");
echo $circle->getArea(); // 78.5
echo $circle->getColor(); // red
```
### Điểm Chính của Abstract Class:
* Không Thể Khởi Tạo Trực Tiếp: Abstract class không thể được khởi tạo bằng từ khóa new. Nếu bạn cố gắng làm điều này, PHP sẽ sinh ra một lỗi.
* Phương Thức Abstract: Abstract class thường chứa ít nhất một phương thức abstract. Phương thức này chỉ được khai báo mà không có triển khai. Các lớp con phải cung cấp triển khai cho các phương thức abstract.
* Triển Khai Các Phương Thức Thông Thường: Abstract class cũng có thể chứa các phương thức thông thường (non-abstract), và chúng có thể có hoặc không có triển khai mặc định.
* Dùng như Interface: Abstract class có thể được sử dụng để định nghĩa một số phương thức mà tất cả các lớp con phải triển khai, giống như việc sử dụng interface.
## 4. Interface
Interface là một phần quan trọng của lập trình hướng đối tượng trong PHP. Một interface là một quy ước (bản thiết kế) chứa các phương thức mà các lớp khi thực hiện (implements) phải triển khai. Nó không thể được khởi tạo (instantiated) trực tiếp và không chứa bất kỳ thuộc tính nào.
```php
// Interface
interface IBanking {
public function getBalance();
public function deposit($amount);
public function withdraw($amount);
}
class TechcomBank implements IBanking {
public $balance;
public function getBalance() {
return $this->balance;
}
public function deposit($amount) {
# Do something with TCB Server
}
public function withdraw($amount) {
# Do something with TCB Server
}
}
class VietcomBank implements IBanking {
# ...
}
```
## 5. Static và final
Trong lập trình hướng đối tượng, `static` và `final` là hai từ khóa quan trọng có ý nghĩa đặc biệt khi định nghĩa lớp và phương thức.
### Static:
1. **Thuộc Tính Tĩnh (Static Properties):** Khi một thuộc tính được đánh dấu là `static`, nó thuộc về lớp chứ không phải là đối tượng cụ thể được tạo ra từ lớp đó. Các biến tĩnh giữ giá trị chung cho tất cả các đối tượng của lớp.
2. **Phương Thức Tĩnh (Static Methods):** Tương tự như thuộc tính tĩnh, phương thức tĩnh không thuộc về một đối tượng cụ thể mà thuộc về lớp. Chúng có thể được gọi mà không cần tạo đối tượng từ lớp.
3. **Ví dụ về Thuộc Tính và Phương Thức Tĩnh:**
```php
class MyClass {
public static $staticProperty = 0;
public static function staticMethod() {
echo "Static Method\n";
}
}
echo MyClass::$staticProperty; // 0
MyClass::staticMethod(); // Static Method
```
### Final:
1. **Lớp Final (Final Classes):** Khi một lớp được đánh dấu là `final`, nó không thể được kế thừa bởi các lớp con. Điều này ngăn chặn quá trình mở rộng và tạo ra các lớp con từ lớp `final`.
2. **Phương Thức Final (Final Methods):** Khi một phương thức được đánh dấu là `final`, nó không thể được đè (override) bởi các phương thức trong lớp con. Điều này giúp giữ nguyên chức năng của phương thức đó trong tất cả các lớp con.
3. **Ví dụ về Lớp và Phương Thức Final:**
```php
final class FinalClass {
// ...
}
class ChildClass extends FinalClass {
// Lỗi! Không thể kế thừa từ lớp final
}
class ParentClass {
final public function finalMethod() {
echo "Final Method\n";
}
}
class ChildClass extends ParentClass {
public function finalMethod() {
// Lỗi! Không thể đè (override) phương thức final
}
}
```