PHP面向对象编程入门:类与对象的实战应用
Orion K Lv6

PHP面向对象编程入门:类与对象的实战应用

面向对象编程(OOP)是现代PHP开发的核心概念。作为一名从过程式编程转向面向对象编程的开发者,我深知这个转变的重要性和挑战性。今天我想分享一些关于PHP面向对象编程的基础知识和实用技巧。

什么是面向对象编程

面向对象编程是一种编程范式,它将现实世界的事物抽象为程序中的对象。每个对象都有自己的属性(数据)和方法(行为)。

面向对象的三大特性

  1. 封装:将数据和操作数据的方法封装在一起
  2. 继承:子类可以继承父类的属性和方法
  3. 多态:同一个接口可以有不同的实现

类的基本定义

1. 简单的类定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
class User {
// 属性(成员变量)
public $name;
public $email;
public $age;

// 方法(成员函数)
public function introduce() {
return "我是 {$this->name},今年 {$this->age} 岁";
}

public function setEmail($email) {
$this->email = $email;
}

public function getEmail() {
return $this->email;
}
}

// 创建对象(实例化)
$user = new User();
$user->name = "张三";
$user->age = 25;
$user->setEmail("zhangsan@example.com");

echo $user->introduce() . "\n";
echo "邮箱:" . $user->getEmail() . "\n";
?>

2. 构造函数和析构函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
class Product {
private $name;
private $price;
private $category;

// 构造函数:对象创建时自动调用
public function __construct($name, $price, $category = "未分类") {
$this->name = $name;
$this->price = $price;
$this->category = $category;
echo "产品 '{$this->name}' 已创建\n";
}

// 析构函数:对象销毁时自动调用
public function __destruct() {
echo "产品 '{$this->name}' 已销毁\n";
}

public function getInfo() {
return "产品:{$this->name},价格:¥{$this->price},分类:{$this->category}";
}

public function setPrice($price) {
if ($price > 0) {
$this->price = $price;
} else {
throw new InvalidArgumentException("价格必须大于0");
}
}

public function getPrice() {
return $this->price;
}
}

// 使用示例
$product1 = new Product("iPhone 14", 6999, "手机");
echo $product1->getInfo() . "\n";

$product2 = new Product("MacBook Pro", 12999);
echo $product2->getInfo() . "\n";

// 对象会在脚本结束时自动销毁
?>

访问修饰符

PHP提供了三种访问修饰符来控制属性和方法的可见性:

1. public(公有)

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class PublicExample {
public $publicProperty = "我是公有属性";

public function publicMethod() {
return "我是公有方法";
}
}

$obj = new PublicExample();
echo $obj->publicProperty . "\n"; // 可以直接访问
echo $obj->publicMethod() . "\n"; // 可以直接调用
?>

2. private(私有)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class PrivateExample {
private $privateProperty = "我是私有属性";

private function privateMethod() {
return "我是私有方法";
}

public function accessPrivate() {
// 在类内部可以访问私有成员
return $this->privateProperty . " - " . $this->privateMethod();
}
}

$obj = new PrivateExample();
echo $obj->accessPrivate() . "\n"; // 通过公有方法访问私有成员

// 以下代码会产生错误
// echo $obj->privateProperty; // 错误:无法访问私有属性
// echo $obj->privateMethod(); // 错误:无法调用私有方法
?>

3. protected(受保护)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class ParentClass {
protected $protectedProperty = "我是受保护的属性";

protected function protectedMethod() {
return "我是受保护的方法";
}
}

class ChildClass extends ParentClass {
public function accessProtected() {
// 子类可以访问父类的受保护成员
return $this->protectedProperty . " - " . $this->protectedMethod();
}
}

$child = new ChildClass();
echo $child->accessProtected() . "\n";

// 以下代码会产生错误
// echo $child->protectedProperty; // 错误:无法直接访问受保护属性
?>

实际应用示例:银行账户类

让我们通过一个银行账户的例子来理解面向对象编程的实际应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
<?php
class BankAccount {
private $accountNumber;
private $balance;
private $ownerName;
private $transactions = [];

public function __construct($accountNumber, $ownerName, $initialBalance = 0) {
$this->accountNumber = $accountNumber;
$this->ownerName = $ownerName;
$this->balance = $initialBalance;

$this->addTransaction("开户", $initialBalance);
}

// 存款
public function deposit($amount) {
if ($amount <= 0) {
throw new InvalidArgumentException("存款金额必须大于0");
}

$this->balance += $amount;
$this->addTransaction("存款", $amount);

return $this->balance;
}

// 取款
public function withdraw($amount) {
if ($amount <= 0) {
throw new InvalidArgumentException("取款金额必须大于0");
}

if ($amount > $this->balance) {
throw new Exception("余额不足");
}

$this->balance -= $amount;
$this->addTransaction("取款", -$amount);

return $this->balance;
}

// 转账
public function transfer(BankAccount $targetAccount, $amount) {
if ($amount <= 0) {
throw new InvalidArgumentException("转账金额必须大于0");
}

if ($amount > $this->balance) {
throw new Exception("余额不足");
}

$this->withdraw($amount);
$targetAccount->deposit($amount);

$this->addTransaction("转出到账户" . $targetAccount->getAccountNumber(), -$amount);
$targetAccount->addTransaction("从账户" . $this->accountNumber . "转入", $amount);
}

// 获取余额
public function getBalance() {
return $this->balance;
}

// 获取账户信息
public function getAccountInfo() {
return [
'accountNumber' => $this->accountNumber,
'ownerName' => $this->ownerName,
'balance' => $this->balance
];
}

// 获取账户号
public function getAccountNumber() {
return $this->accountNumber;
}

// 获取交易记录
public function getTransactions() {
return $this->transactions;
}

// 私有方法:添加交易记录
private function addTransaction($type, $amount) {
$this->transactions[] = [
'date' => date('Y-m-d H:i:s'),
'type' => $type,
'amount' => $amount,
'balance' => $this->balance
];
}

// 打印账户信息
public function printAccountInfo() {
$info = $this->getAccountInfo();
echo "账户号:{$info['accountNumber']}\n";
echo "户主:{$info['ownerName']}\n";
echo "余额:¥{$info['balance']}\n";
echo "交易记录:\n";

foreach ($this->transactions as $transaction) {
echo " {$transaction['date']} - {$transaction['type']}: ¥{$transaction['amount']} (余额: ¥{$transaction['balance']})\n";
}
}
}

// 使用示例
try {
// 创建两个银行账户
$account1 = new BankAccount("001", "张三", 1000);
$account2 = new BankAccount("002", "李四", 500);

echo "=== 初始状态 ===\n";
$account1->printAccountInfo();
echo "\n";
$account2->printAccountInfo();

echo "\n=== 张三存款500元 ===\n";
$account1->deposit(500);
echo "张三账户余额:¥" . $account1->getBalance() . "\n";

echo "\n=== 张三取款200元 ===\n";
$account1->withdraw(200);
echo "张三账户余额:¥" . $account1->getBalance() . "\n";

echo "\n=== 张三向李四转账300元 ===\n";
$account1->transfer($account2, 300);
echo "张三账户余额:¥" . $account1->getBalance() . "\n";
echo "李四账户余额:¥" . $account2->getBalance() . "\n";

echo "\n=== 最终状态 ===\n";
$account1->printAccountInfo();
echo "\n";
$account2->printAccountInfo();

} catch (Exception $e) {
echo "错误:" . $e->getMessage() . "\n";
}
?>

静态属性和方法

静态成员属于类本身,而不是类的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
class Counter {
private static $count = 0;
private $instanceId;

public function __construct() {
self::$count++;
$this->instanceId = self::$count;
echo "创建了第 {$this->instanceId} 个实例\n";
}

public static function getCount() {
return self::$count;
}

public function getInstanceId() {
return $this->instanceId;
}

public static function reset() {
self::$count = 0;
echo "计数器已重置\n";
}
}

// 使用静态方法
echo "当前实例数量:" . Counter::getCount() . "\n";

$obj1 = new Counter();
$obj2 = new Counter();
$obj3 = new Counter();

echo "当前实例数量:" . Counter::getCount() . "\n";
echo "obj2的实例ID:" . $obj2->getInstanceId() . "\n";

Counter::reset();
echo "重置后实例数量:" . Counter::getCount() . "\n";
?>

常量

类常量在类定义后不能被修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class MathConstants {
const PI = 3.14159;
const E = 2.71828;

public static function getCircleArea($radius) {
return self::PI * $radius * $radius;
}

public static function getCircleCircumference($radius) {
return 2 * self::PI * $radius;
}
}

echo "圆周率:" . MathConstants::PI . "\n";
echo "半径为5的圆的面积:" . MathConstants::getCircleArea(5) . "\n";
echo "半径为5的圆的周长:" . MathConstants::getCircleCircumference(5) . "\n";
?>

魔术方法

PHP提供了一些特殊的魔术方法,以双下划线开头:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
class MagicExample {
private $data = [];

// __get:访问不存在的属性时调用
public function __get($name) {
echo "尝试访问属性:$name\n";
return $this->data[$name] ?? null;
}

// __set:设置不存在的属性时调用
public function __set($name, $value) {
echo "设置属性 $name = $value\n";
$this->data[$name] = $value;
}

// __isset:使用isset()检查不存在的属性时调用
public function __isset($name) {
return isset($this->data[$name]);
}

// __unset:使用unset()删除不存在的属性时调用
public function __unset($name) {
unset($this->data[$name]);
}

// __toString:对象被当作字符串使用时调用
public function __toString() {
return "MagicExample对象,包含数据:" . json_encode($this->data);
}

// __call:调用不存在的方法时调用
public function __call($name, $arguments) {
echo "尝试调用方法:$name,参数:" . json_encode($arguments) . "\n";
return "方法 $name 不存在";
}

// __clone:克隆对象时调用
public function __clone() {
echo "对象被克隆了\n";
$this->data = array_map(function($item) {
return is_object($item) ? clone $item : $item;
}, $this->data);
}
}

$obj = new MagicExample();

// 测试__set和__get
$obj->name = "张三";
echo "姓名:" . $obj->name . "\n";

// 测试__isset
if (isset($obj->name)) {
echo "name属性存在\n";
}

// 测试__toString
echo $obj . "\n";

// 测试__call
$result = $obj->nonExistentMethod("参数1", "参数2");
echo "返回值:$result\n";

// 测试__clone
$obj2 = clone $obj;
?>

对象比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
class Person {
public $name;
public $age;

public function __construct($name, $age) {
$this->name = $name;
$this->age = $age;
}
}

$person1 = new Person("张三", 25);
$person2 = new Person("张三", 25);
$person3 = $person1;

// == 比较:比较对象的属性值
if ($person1 == $person2) {
echo "person1 == person2:相等\n";
}

// === 比较:比较对象的引用
if ($person1 === $person2) {
echo "person1 === person2:相等\n";
} else {
echo "person1 === person2:不相等\n";
}

if ($person1 === $person3) {
echo "person1 === person3:相等(同一个对象)\n";
}
?>

实用技巧和最佳实践

1. 使用getter和setter方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
class User {
private $email;
private $age;

public function setEmail($email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException("无效的邮箱地址");
}
$this->email = $email;
}

public function getEmail() {
return $this->email;
}

public function setAge($age) {
if ($age < 0 || $age > 150) {
throw new InvalidArgumentException("年龄必须在0-150之间");
}
$this->age = $age;
}

public function getAge() {
return $this->age;
}
}
?>

2. 方法链式调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
class QueryBuilder {
private $query = "";

public function select($fields) {
$this->query .= "SELECT $fields ";
return $this; // 返回自身,支持链式调用
}

public function from($table) {
$this->query .= "FROM $table ";
return $this;
}

public function where($condition) {
$this->query .= "WHERE $condition ";
return $this;
}

public function orderBy($field, $direction = "ASC") {
$this->query .= "ORDER BY $field $direction ";
return $this;
}

public function getQuery() {
return trim($this->query);
}
}

// 链式调用示例
$query = (new QueryBuilder())
->select("name, email")
->from("users")
->where("age > 18")
->orderBy("name")
->getQuery();

echo $query . "\n";
?>

3. 单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
class Database {
private static $instance = null;
private $connection;

// 私有构造函数,防止外部实例化
private function __construct() {
$this->connection = "数据库连接";
echo "数据库连接已建立\n";
}

// 防止克隆
private function __clone() {}

// 防止反序列化
private function __wakeup() {}

public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}

public function getConnection() {
return $this->connection;
}
}

// 使用单例
$db1 = Database::getInstance();
$db2 = Database::getInstance();

var_dump($db1 === $db2); // true,同一个实例
?>

常见错误和注意事项

1. 忘记使用$this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class Example {
private $value = 10;

public function wrongMethod() {
// 错误:直接使用变量名
// return $value; // 这会产生错误
}

public function correctMethod() {
// 正确:使用$this访问属性
return $this->value;
}
}
?>

2. 访问修饰符的误用

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class Parent {
private $privateVar = "私有变量";
protected $protectedVar = "受保护变量";
}

class Child extends Parent {
public function accessVars() {
// echo $this->privateVar; // 错误:无法访问父类私有变量
echo $this->protectedVar; // 正确:可以访问父类受保护变量
}
}
?>

3. 静态方法中使用$this

1
2
3
4
5
6
7
8
9
10
11
<?php
class Example {
private $instanceVar = "实例变量";
private static $staticVar = "静态变量";

public static function staticMethod() {
// echo $this->instanceVar; // 错误:静态方法中不能使用$this
echo self::$staticVar; // 正确:使用self访问静态变量
}
}
?>

总结

PHP面向对象编程的关键要点:

  1. 理解类和对象的关系:类是模板,对象是实例
  2. 合理使用访问修饰符:封装数据,控制访问权限
  3. 掌握构造函数和析构函数:对象的生命周期管理
  4. 善用静态成员:类级别的属性和方法
  5. 了解魔术方法:PHP提供的特殊功能
  6. 遵循最佳实践:使用getter/setter、方法链等
  7. 避免常见错误:正确使用$this、访问修饰符等

面向对象编程不仅仅是语法的改变,更是思维方式的转变。它让我们能够更好地组织代码,提高代码的可维护性和可扩展性。

通过不断练习和实际项目应用,你会发现面向对象编程的强大之处。记住,好的面向对象设计需要时间和经验的积累。

希望这篇文章能帮助你更好地理解PHP面向对象编程的基础概念!

本站由 提供部署服务