PHP 類別與物件 (Class & Object)
PHP 支援物件導向程式設計(Object-Oriented Programming, OOP)。類別是物件的藍圖,定義了物件的屬性和行為。
基本概念
- 類別 (Class):物件的模板或藍圖
- 物件 (Object):類別的實例
- 屬性 (Property):物件的資料
- 方法 (Method):物件的行為(函數)
定義類別
<?php
class User {
// 屬性
public string $name;
public int $age;
// 方法
public function greet(): string {
return "Hello, I'm {$this->name}";
}
}
// 建立物件(實例化)
$user = new User();
$user->name = "Alice";
$user->age = 25;
echo $user->greet(); // Hello, I'm Alice
?>
建構子 __construct()
建構子在建立物件時自動執行:
<?php
class User {
public string $name;
public int $age;
public function __construct(string $name, int $age) {
$this->name = $name;
$this->age = $age;
}
}
$user = new User("Alice", 25);
echo $user->name; // Alice
?>
建構子屬性提升 (PHP 8+)
<?php
class User {
public function __construct(
public string $name,
public int $age,
public string $email = ''
) {}
}
$user = new User("Alice", 25);
echo $user->name; // Alice
?>
存取修飾符
| 修飾符 | 類別內 | 子類別 | 外部 |
|---|---|---|---|
public | ✓ | ✓ | ✓ |
protected | ✓ | ✓ | ✗ |
private | ✓ | ✗ | ✗ |
<?php
class User {
public string $name; // 公開
protected string $email; // 受保護
private string $password; // 私有
public function __construct(string $name, string $email, string $password) {
$this->name = $name;
$this->email = $email;
$this->password = $password;
}
public function getEmail(): string {
return $this->email;
}
}
$user = new User("Alice", "alice@example.com", "secret");
echo $user->name; // Alice
// echo $user->email; // 錯誤:protected
// echo $user->password; // 錯誤:private
echo $user->getEmail(); // alice@example.com
?>
$this 關鍵字
$this 指向目前的物件實例:
<?php
class Counter {
private int $count = 0;
public function increment(): self {
$this->count++;
return $this; // 回傳自己,支援鏈式呼叫
}
public function getCount(): int {
return $this->count;
}
}
$counter = new Counter();
$counter->increment()->increment()->increment();
echo $counter->getCount(); // 3
?>
靜態成員
使用 static 關鍵字定義靜態屬性和方法:
<?php
class Counter {
private static int $count = 0;
public static function increment(): void {
self::$count++;
}
public static function getCount(): int {
return self::$count;
}
}
Counter::increment();
Counter::increment();
echo Counter::getCount(); // 2
?>
self vs $this
<?php
class Example {
private static string $staticProp = 'static';
private string $instanceProp = 'instance';
public function show(): void {
echo self::$staticProp; // 存取靜態屬性
echo $this->instanceProp; // 存取實例屬性
}
public static function staticShow(): void {
echo self::$staticProp;
// echo $this->instanceProp; // 錯誤:靜態方法中沒有 $this
}
}
?>
常數
<?php
class Status {
public const PENDING = 'pending';
public const ACTIVE = 'active';
public const COMPLETED = 'completed';
}
echo Status::PENDING; // pending
// 在物件中使用
$status = Status::ACTIVE;
?>
Getter 和 Setter
<?php
class User {
private string $name;
public function getName(): string {
return $this->name;
}
public function setName(string $name): void {
if (strlen($name) < 2) {
throw new InvalidArgumentException("名稱至少 2 個字元");
}
$this->name = $name;
}
}
$user = new User();
$user->setName("Alice");
echo $user->getName(); // Alice
?>
解構子 __destruct()
解構子在物件被銷毀時執行:
<?php
class FileHandler {
private $handle;
public function __construct(string $filename) {
$this->handle = fopen($filename, 'r');
}
public function __destruct() {
if ($this->handle) {
fclose($this->handle);
}
}
}
?>
唯讀屬性 (PHP 8.1+)
<?php
class User {
public function __construct(
public readonly string $id,
public string $name
) {}
}
$user = new User("123", "Alice");
echo $user->id; // 123
// $user->id = "456"; // 錯誤:唯讀屬性不能修改
$user->name = "Bob"; // OK
?>
型別宣告
<?php
class User {
public string $name;
public ?int $age = null;
public array $roles = [];
public function setAge(int $age): self {
$this->age = $age;
return $this;
}
public function getInfo(): array {
return [
'name' => $this->name,
'age' => $this->age
];
}
}
?>
魔術方法
<?php
class User {
private array $data = [];
// 存取不存在的屬性
public function __get(string $name): mixed {
return $this->data[$name] ?? null;
}
public function __set(string $name, mixed $value): void {
$this->data[$name] = $value;
}
public function __isset(string $name): bool {
return isset($this->data[$name]);
}
// 呼叫不存在的方法
public function __call(string $name, array $arguments): mixed {
return "呼叫了 $name 方法";
}
// 轉換為字串
public function __toString(): string {
return json_encode($this->data);
}
}
$user = new User();
$user->name = "Alice"; // 觸發 __set
echo $user->name; // 觸發 __get
echo $user->sayHello(); // 觸發 __call
echo $user; // 觸發 __toString
?>
物件複製
<?php
class User {
public string $name;
public array $settings;
public function __clone() {
// 深層複製
$this->settings = $this->settings;
}
}
$user1 = new User();
$user1->name = "Alice";
$user1->settings = ['theme' => 'dark'];
// 淺複製
$user2 = $user1; // 同一個物件
$user2->name = "Bob";
echo $user1->name; // Bob
// 深複製
$user3 = clone $user1;
$user3->name = "Charlie";
echo $user1->name; // Bob(不受影響)
?>
完整範例
<?php
class BankAccount {
private static int $totalAccounts = 0;
public function __construct(
private readonly string $accountNumber,
private string $owner,
private float $balance = 0
) {
self::$totalAccounts++;
}
public function deposit(float $amount): self {
if ($amount <= 0) {
throw new InvalidArgumentException("金額必須大於 0");
}
$this->balance += $amount;
return $this;
}
public function withdraw(float $amount): self {
if ($amount > $this->balance) {
throw new RuntimeException("餘額不足");
}
$this->balance -= $amount;
return $this;
}
public function getBalance(): float {
return $this->balance;
}
public static function getTotalAccounts(): int {
return self::$totalAccounts;
}
}
$account = new BankAccount("001", "Alice", 1000);
$account->deposit(500)->withdraw(200);
echo $account->getBalance(); // 1300
echo BankAccount::getTotalAccounts(); // 1
?>