掌握了PHP面向对象编程的基础后,就可以深入学习命名空间、类型转换、文档注释、序列化以及文件操作等重要概念。
1、命名空间(Namespace)
命名空间是PHP 5.3引入的重要特性,它解决了类名、函数名和常量名冲突的问题,使得大型项目的代码组织更加清晰。
命名空间的基本使用
<?php
// 定义命名空间
namespace App;
class User {
public $name = "基础用户";
}
// 子命名空间
namespace App\Model;
class User {
public $name = "模型用户";
}
// 更深层的命名空间
namespace App\Model\Database;
class Connection {
public function connect() {
echo "数据库连接成功";
}
}
// 在同一个文件中定义多个命名空间
namespace App\Controller {
class UserController {
public function index() {
echo "用户控制器";
}
}
}
namespace App\Service {
class UserService {
public function getUsers() {
return ["用户1", "用户2"];
}
}
}
?>
命名空间的访问方式
<?php
namespace App\Model;
class User {
public $name = "用户模型";
}
function getUserInfo() {
return "获取用户信息";
}
const VERSION = "1.0.0";
// 在其他文件中使用命名空间
namespace App\Controller;
// 1. 完全限定名称(从根命名空间开始)
$user1 = new \App\Model\User();
// 2. 限定名称(相对于当前命名空间)
// 假设当前在 App\Controller 命名空间中
$user2 = new \App\Model\User(); // 必须使用完全限定名
// 3. 非限定名称(不包含命名空间分隔符)
// 只能访问当前命名空间中的类
class HomeController {
// 这个类在 App\Controller 命名空间中
}
$controller = new HomeController(); // 非限定名称
?>
use关键字和别名
<?php
namespace App\Controller;
// 引入其他命名空间的类
use App\Model\User;
use App\Service\UserService;
use App\Model\Database\Connection;
// 使用别名避免冲突
use App\Model\User as ModelUser;
use App\Entity\User as EntityUser;
// 引入函数和常量
use function App\Helper\formatDate;
use const App\Config\DATABASE_HOST;
class UserController {
public function index() {
// 直接使用类名(已经通过use引入)
$user = new User();
// 使用别名
$modelUser = new ModelUser();
$entityUser = new EntityUser();
// 使用引入的函数和常量
echo formatDate(time());
echo DATABASE_HOST;
}
}
// 批量引入
use App\Model\{User, Product, Order};
use App\Service\{UserService, ProductService, OrderService};
?>
魔术常量和自动加载
<?php
namespace App\Model;
class User {
public function getNamespaceInfo() {
echo "当前命名空间: " . __NAMESPACE__ . "\n";
echo "当前类: " . __CLASS__ . "\n";
echo "当前方法: " . __METHOD__ . "\n";
}
}
// 手动加载类文件
if (!class_exists('App\Model\Product')) {
require_once 'Product.php';
}
// 自动加载机制
spl_autoload_register(function ($className) {
// 将命名空间转换为文件路径
$file = str_replace('\\', '/', $className) . '.php';
if (file_exists($file)) {
require_once $file;
}
});
// 现在可以直接使用类,无需手动引入
$user = new App\Model\User();
?>
Composer自动加载
现代PHP项目通常使用Composer来管理依赖和自动加载:
# 初始化Composer项目
composer init
# 在composer.json中配置PSR-4自动加载
{
"autoload": {
"psr-4": {
"App\\": "src/",
"Tests\\": "tests/"
}
}
}
# 更新自动加载
composer dump-autoload
<?php
// 引入Composer自动加载
require_once 'vendor/autoload.php';
// 现在可以直接使用所有通过Composer管理的类
use App\Model\User;
use App\Service\UserService;
$user = new User();
$service = new UserService();
?>
2、类型转换
PHP是弱类型语言,但有时需要强制转换数据类型。理解类型转换对于编写健壮的代码非常重要。
转换为字符串
<?php
// 方法1:使用strval()函数
$number = 123;
$float = 3.14;
$bool = true;
$str1 = strval($number); // "123"
$str2 = strval($float); // "3.14"
$str3 = strval($bool); // "1"
// 方法2:强制类型转换
$str4 = (string) $number; // "123"
$str5 = (string) $bool; // "1"
// 方法3:字符串连接
$str6 = $number . ""; // "123"
$str7 = $bool . ""; // "1"
// 布尔值转字符串的特殊情况
$trueStr = (string) true; // "1"
$falseStr = (string) false; // ""(空字符串)
echo "数字转字符串: '$str1'\n";
echo "布尔true转字符串: '$str3'\n";
echo "布尔false转字符串: '$falseStr'\n";
?>
转换为整数
<?php
// 方法1:使用intval()函数
$str = "123";
$float = 3.14;
$bool = true;
$int1 = intval($str); // 123
$int2 = intval($float); // 3(截断小数部分)
$int3 = intval($bool); // 1
// 方法2:强制类型转换
$int4 = (int) $str; // 123
$int5 = (integer) $float; // 3
// 特殊情况处理
$mixedStr = "123abc";
$int6 = intval($mixedStr); // 123(从开头解析数字)
$invalidStr = "abc123";
$int7 = intval($invalidStr); // 0(无法解析)
echo "字符串'123'转整数: $int1\n";
echo "浮点数3.14转整数: $int2\n";
echo "混合字符串'123abc'转整数: $int6\n";
echo "无效字符串'abc123'转整数: $int7\n";
?>
转换为浮点数
<?php
// 方法1:使用floatval()函数
$str = "3.14";
$int = 123;
$float1 = floatval($str); // 3.14
$float2 = floatval($int); // 123.0
// 方法2:强制类型转换
$float3 = (float) $str; // 3.14
$float4 = (double) $int; // 123.0
// 科学计数法
$scientific = "1.23e4"; // 12300
$float5 = floatval($scientific);
echo "字符串'3.14'转浮点数: $float1\n";
echo "整数123转浮点数: $float2\n";
echo "科学计数法'1.23e4'转浮点数: $float5\n";
?>
转换为布尔值
<?php
// 方法1:使用boolval()函数(PHP 5.5+)
$int = 0;
$str = "";
$array = [];
$bool1 = boolval($int); // false
$bool2 = boolval($str); // false
$bool3 = boolval($array); // false
// 方法2:强制类型转换
$bool4 = (bool) $int; // false
$bool5 = (boolean) $str; // false
// 假值(转换为false的值)
$falseValues = [
0, // 整数0
0.0, // 浮点数0.0
"", // 空字符串
"0", // 字符串"0"
null, // null值
false, // false本身
[] // 空数组
];
foreach ($falseValues as $value) {
$result = (bool) $value ? 'true' : 'false';
echo gettype($value) . " 值转布尔: $result\n";
}
// 真值示例
$trueValues = [1, -1, "hello", [1, 2, 3], new stdClass()];
?>
3、PHPDoc 文档注释
PHPDoc是PHP代码文档化的标准,它使用特殊的注释格式来描述代码的功能、参数、返回值等信息。
基本PHPDoc语法
<?php
/**
* 用户管理类
*
* 提供用户的增删改查等基本操作
*
* @author 张三 <zhangsan@example.com>
* @version 1.0.0
* @since 2024-01-01
* @package App\Model
*/
class UserManager {
/**
* 用户数据
*
* @var array
*/
private $users = [];
/**
* 数据库连接对象
*
* @var PDO|null
*/
private $connection;
/**
* 构造函数
*
* @param PDO $connection 数据库连接对象
* @throws InvalidArgumentException 当连接对象无效时抛出
*/
public function __construct(PDO $connection) {
if (!$connection) {
throw new InvalidArgumentException('数据库连接不能为空');
}
$this->connection = $connection;
}
/**
* 创建新用户
*
* @param string $name 用户姓名
* @param string $email 用户邮箱
* @param int $age 用户年龄
* @return int 返回新创建用户的ID
* @throws PDOException 数据库操作失败时抛出
* @throws InvalidArgumentException 参数验证失败时抛出
*
* @example
* $userManager = new UserManager($pdo);
* $userId = $userManager->createUser('张三', 'zhangsan@example.com', 25);
*/
public function createUser(string $name, string $email, int $age): int {
// 实现代码...
return 1;
}
/**
* 根据ID获取用户信息
*
* @param int $id 用户ID
* @return array|null 用户信息数组,不存在时返回null
*
* @deprecated 1.2.0 使用getUserById()方法替代
* @see getUserById()
*/
public function getUser(int $id): ?array {
// 实现代码...
return null;
}
/**
* 批量获取用户
*
* @param array $ids 用户ID数组
* @return array 用户信息数组
*
* @todo 添加缓存机制提高性能
* @link https://example.com/docs/user-api 相关API文档
*/
public function getUsers(array $ids): array {
// 实现代码...
return [];
}
}
?>
常用PHPDoc标签
<?php
/**
* 计算工具类
*
* @package App\Utils
* @author 李四 <lisi@example.com>
* @copyright 2024 My Company
* @license MIT License
* @version 2.1.0
*/
class Calculator {
/**
* 计算两个数的和
*
* @param int|float $a 第一个数
* @param int|float $b 第二个数
* @return int|float 两数之和
*
* @example
* $calc = new Calculator();
* $result = $calc->add(10, 20); // 返回 30
*/
public function add($a, $b) {
return $a + $b;
}
/**
* 计算数组平均值
*
* @param array<int|float> $numbers 数字数组
* @return float 平均值
* @throws InvalidArgumentException 当数组为空时
*
* @since 2.0.0
*/
public function average(array $numbers): float {
if (empty($numbers)) {
throw new InvalidArgumentException('数组不能为空');
}
return array_sum($numbers) / count($numbers);
}
}
?>
4、值传递与引用传递
理解PHP中参数传递的机制对于编写高效代码非常重要。
值传递(默认方式)
<?php
/**
* 值传递示例
*
* @param int $number 传入的数值
*/
function incrementByValue(int $number): void {
$number += 10;
echo "函数内部: $number\n";
}
$originalNumber = 5;
echo "调用前: $originalNumber\n"; // 5
incrementByValue($originalNumber); // 函数内部: 15
echo "调用后: $originalNumber\n"; // 5(原始值未改变)
// 对象的值传递(特殊情况)
class Counter {
public $count = 0;
public function increment() {
$this->count++;
}
}
function modifyObject(Counter $counter): void {
$counter->increment();
echo "函数内部count: {$counter->count}\n";
}
$myCounter = new Counter();
echo "调用前count: {$myCounter->count}\n"; // 0
modifyObject($myCounter); // 函数内部count: 1
echo "调用后count: {$myCounter->count}\n"; // 1(对象内容被修改了!)
?>
引用传递
<?php
/**
* 引用传递示例
*
* @param int &$number 通过引用传递的数值
*/
function incrementByReference(int &$number): void {
$number += 10;
echo "函数内部: $number\n";
}
$originalNumber = 5;
echo "调用前: $originalNumber\n"; // 5
incrementByReference($originalNumber); // 函数内部: 15
echo "调用后: $originalNumber\n"; // 15(原始值被修改了)
// 数组的引用传递
function addElement(array &$arr, $element): void {
$arr[] = $element;
}
$fruits = ['苹果', '香蕉'];
echo "添加前: " . implode(', ', $fruits) . "\n";
addElement($fruits, '橙子');
echo "添加后: " . implode(', ', $fruits) . "\n"; // 苹果, 香蕉, 橙子
// 实际应用:交换两个变量的值
function swap(&$a, &$b): void {
$temp = $a;
$a = $b;
$b = $temp;
}
$x = 10;
$y = 20;
echo "交换前: x=$x, y=$y\n";
swap($x, $y);
echo "交换后: x=$x, y=$y\n"; // x=20, y=10
?>
5、序列化
序列化是将PHP变量转换为可存储或传输的字符串格式的过程。
基本序列化操作
<?php
// 基本数据类型序列化
$data = [
'name' => '张三',
'age' => 25,
'skills' => ['PHP', 'JavaScript', 'MySQL'],
'active' => true
];
// 序列化
$serialized = serialize($data);
echo "序列化结果:\n$serialized\n\n";
// 反序列化
$unserialized = unserialize($serialized);
print_r($unserialized);
// JSON序列化(推荐用于数据交换)
$jsonString = json_encode($data, JSON_UNESCAPED_UNICODE);
echo "JSON序列化:\n$jsonString\n\n";
$jsonData = json_decode($jsonString, true);
print_r($jsonData);
?>
对象序列化
<?php
class User {
public $name;
public $email;
private $password;
public function __construct($name, $email, $password) {
$this->name = $name;
$this->email = $email;
$this->password = $password;
}
/**
* 序列化时调用,控制哪些属性被序列化
*/
public function __sleep(): array {
return ['name', 'email']; // 不序列化password
}
/**
* 反序列化时调用,进行必要的初始化
*/
public function __wakeup(): void {
// 可以在这里重新初始化一些属性
echo "对象被反序列化了\n";
}
public function getInfo(): string {
return "用户: {$this->name}, 邮箱: {$this->email}";
}
}
$user = new User('李四', 'lisi@example.com', 'secret123');
// 序列化对象
$serializedUser = serialize($user);
echo "序列化用户对象:\n$serializedUser\n\n";
// 反序列化对象
$unserializedUser = unserialize($serializedUser);
echo $unserializedUser->getInfo() . "\n";
// 文件存储示例
file_put_contents('user.dat', serialize($user));
$loadedUser = unserialize(file_get_contents('user.dat'));
?>
6、List解构赋值
List是PHP中一种方便的数组解构赋值方式,可以快速将数组元素赋值给多个变量。
基本list使用
<?php
// 基本用法
$userInfo = ['张三', 25, 'zhangsan@example.com'];
// 传统方式
$name = $userInfo[0];
$age = $userInfo[1];
$email = $userInfo[2];
// 使用list解构
list($name, $age, $email) = $userInfo;
echo "姓名: $name, 年龄: $age, 邮箱: $email\n";
// PHP 5.4+ 可以使用短语法
[$name, $age, $email] = $userInfo;
// 跳过某些元素
list($name, , $email) = $userInfo; // 跳过年龄
echo "姓名: $name, 邮箱: $email\n";
// 获取部分元素
list($name, $age) = $userInfo;
echo "姓名: $name, 年龄: $age\n";
?>
高级list用法
<?php
// 嵌套数组解构
$data = [
['产品A', 100],
['产品B', 200],
['产品C', 150]
];
foreach ($data as list($product, $price)) {
echo "产品: $product, 价格: ¥$price\n";
}
// 关联数组解构(PHP 7.1+)
$user = ['name' => '王五', 'age' => 30, 'city' => '北京'];
// 按键名解构
['name' => $userName, 'age' => $userAge] = $user;
echo "用户: $userName, 年龄: $userAge\n";
// 函数返回多个值
function getUserData() {
return ['李六', 28, 'developer'];
}
list($name, $age, $job) = getUserData();
echo "姓名: $name, 年龄: $age, 职业: $job\n";
// 交换变量值
$a = 10;
$b = 20;
list($a, $b) = [$b, $a];
echo "交换后: a=$a, b=$b\n";
// 处理CSV数据
$csvData = "张三,25,工程师\n李四,30,设计师\n王五,28,产品经理";
$lines = explode("\n", $csvData);
foreach ($lines as $line) {
list($name, $age, $position) = explode(",", $line);
echo "员工: $name, 年龄: $age, 职位: $position\n";
}
?>
7、文件操作
文件操作是Web开发中的常见需求,PHP提供了丰富的文件处理函数。
文件信息获取
<?php
$filename = 'example.txt';
// 创建测试文件
file_put_contents($filename, "这是一个测试文件\n第二行内容");
// 检查文件是否存在
if (file_exists($filename)) {
echo "文件存在\n";
// 获取文件信息
echo "是否为文件: " . (is_file($filename) ? '是' : '否') . "\n";
echo "是否为目录: " . (is_dir($filename) ? '是' : '否') . "\n";
echo "文件大小: " . filesize($filename) . " 字节\n";
echo "文件名: " . basename($filename) . "\n";
echo "文件路径: " . dirname($filename) . "\n";
echo "文件类型: " . filetype($filename) . "\n";
echo "修改时间: " . date('Y-m-d H:i:s', filemtime($filename)) . "\n";
echo "是否可读: " . (is_readable($filename) ? '是' : '否') . "\n";
echo "是否可写: " . (is_writable($filename) ? '是' : '否') . "\n";
}
// 获取文件扩展名
$pathInfo = pathinfo($filename);
echo "文件信息:\n";
echo "目录: " . $pathInfo['dirname'] . "\n";
echo "文件名: " . $pathInfo['filename'] . "\n";
echo "扩展名: " . ($pathInfo['extension'] ?? '无') . "\n";
?>
文件读写操作
<?php
$filename = 'data.txt';
// 写入文件(覆盖模式)
$content = "第一行数据\n第二行数据\n第三行数据";
$bytesWritten = file_put_contents($filename, $content);
echo "写入了 $bytesWritten 字节\n";
// 追加内容
$additionalContent = "\n第四行数据";
file_put_contents($filename, $additionalContent, FILE_APPEND);
// 读取整个文件
$fileContent = file_get_contents($filename);
echo "文件内容:\n$fileContent\n";
// 按行读取文件
$lines = file($filename, FILE_IGNORE_NEW_LINES);
echo "按行读取:\n";
foreach ($lines as $lineNumber => $lineContent) {
echo "第" . ($lineNumber + 1) . "行: $lineContent\n";
}
// 使用文件句柄进行操作
$file = fopen($filename, 'r');
if ($file) {
echo "\n逐行读取:\n";
while (($line = fgets($file)) !== false) {
echo "读取到: " . trim($line) . "\n";
}
fclose($file);
}
// 写入模式示例
$logFile = 'log.txt';
$log = fopen($logFile, 'a'); // 追加模式
if ($log) {
$timestamp = date('Y-m-d H:i:s');
fwrite($log, "[$timestamp] 系统启动\n");
fwrite($log, "[$timestamp] 用户登录\n");
fclose($log);
}
?>
文件操作实用函数
<?php
// 文件复制
$sourceFile = 'original.txt';
$targetFile = 'copy.txt';
file_put_contents($sourceFile, "原始文件内容");
if (copy($sourceFile, $targetFile)) {
echo "文件复制成功\n";
}
// 文件重命名/移动
$oldName = 'copy.txt';
$newName = 'renamed.txt';
if (rename($oldName, $newName)) {
echo "文件重命名成功\n";
}
// 删除文件
if (unlink($newName)) {
echo "文件删除成功\n";
}
// 目录操作
$dirName = 'test_directory';
// 创建目录
if (mkdir($dirName, 0755)) {
echo "目录创建成功\n";
}
// 创建多级目录
$nestedDir = 'parent/child/grandchild';
if (mkdir($nestedDir, 0755, true)) { // 第三个参数为true表示递归创建
echo "多级目录创建成功\n";
}
// 列出目录内容
$files = scandir('.');
echo "当前目录文件:\n";
foreach ($files as $file) {
if ($file != '.' && $file != '..') {
echo "- $file\n";
}
}
// 删除目录(只能删除空目录)
if (rmdir($dirName)) {
echo "目录删除成功\n";
}
// 递归删除目录函数
function deleteDirectory($dir) {
if (!is_dir($dir)) {
return false;
}
$files = scandir($dir);
foreach ($files as $file) {
if ($file != '.' && $file != '..') {
$filePath = $dir . '/' . $file;
if (is_dir($filePath)) {
deleteDirectory($filePath); // 递归删除子目录
} else {
unlink($filePath); // 删除文件
}
}
}
return rmdir($dir); // 删除空目录
}
// 使用递归删除函数
if (deleteDirectory('parent')) {
echo "递归删除目录成功\n";
}
?>
文件上传处理
<?php
// 处理文件上传
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['upload'])) {
$uploadFile = $_FILES['upload'];
// 检查上传是否成功
if ($uploadFile['error'] === UPLOAD_ERR_OK) {
$tmpName = $uploadFile['tmp_name'];
$originalName = $uploadFile['name'];
$fileSize = $uploadFile['size'];
$fileType = $uploadFile['type'];
// 安全检查
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
$maxSize = 2 * 1024 * 1024; // 2MB
if (!in_array($fileType, $allowedTypes)) {
echo "不允许的文件类型";
exit;
}
if ($fileSize > $maxSize) {
echo "文件太大";
exit;
}
// 生成安全的文件名
$extension = pathinfo($originalName, PATHINFO_EXTENSION);
$safeName = uniqid() . '.' . $extension;
$uploadDir = 'uploads/';
// 确保上传目录存在
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$targetPath = $uploadDir . $safeName;
// 移动上传文件
if (move_uploaded_file($tmpName, $targetPath)) {
echo "文件上传成功: $targetPath";
} else {
echo "文件上传失败";
}
} else {
echo "上传错误: " . $uploadFile['error'];
}
}
?>
<!-- 文件上传表单示例 -->
<!DOCTYPE html>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<form method="POST" enctype="multipart/form-data">
<div>
<label>选择文件:</label>
<input type="file" name="upload" accept="image/*">
</div>
<div>
<input type="submit" value="上传文件">
</div>
</form>
</body>
</html>
8、总结
本文全面介绍了PHP的进阶语法特性:
核心知识点回顾:
- 命名空间:解决命名冲突,组织代码结构
- 使用
namespace
定义命名空间 - 通过
use
关键字引入和使用别名 - 配合Composer实现自动加载
- 使用
- 类型转换:在弱类型语言中进行强制类型转换
- 字符串、整数、浮点数、布尔值之间的转换
- 理解转换规则和特殊情况
- PHPDoc文档注释:标准化代码文档
- 使用
@param
、@return
、@throws
等标签 - 提高代码可读性和可维护性
- 使用
- 参数传递:值传递与引用传递的区别
- 默认值传递不影响原变量
- 引用传递可以修改原变量
- 对象传递的特殊性
- 序列化:数据持久化和传输
serialize()
/unserialize()
用于PHP内部json_encode()
/json_decode()
用于跨语言交换
- List解构赋值:快速分解数组
- 简化数组元素赋值
- 支持嵌套和关联数组解构
- 文件操作:文件和目录的完整操作
- 文件信息获取和检查
- 读写操作和权限管理
- 目录创建和遍历
实践建议:
- 合理使用命名空间组织大型项目
- 重视代码文档,使用PHPDoc标准
- 注意内存使用,处理大文件时使用生成器
- 文件操作时考虑并发和异常处理
- 选择合适的序列化方式
- 优化文件I/O操作,减少磁盘访问