从入门到放弃?一份让PHP学习持续正反馈的知识清单

一、引用变量的考察点

引用变量的本质是多个变量名指向同一内存地址,而非拷贝数据,核心考察点围绕其定义、使用场景及特殊行为展开。

1、基础定义与语法

引用符号 :通过 & 符号声明引用,需区分 "声明引用" 和 "传递引用" 两种场景。

  • 声明引用:$a = 10; $b = &$a;$a$b 指向同一内存,修改 $b 会同步改变 $a)。
  • 注意:$b = &$a 后,若再执行 $b = 20,是修改共同指向的内存值,而非改变引用关系。

禁止重复引用 :同一变量不能重复声明为其他变量的引用,否则会触发 Notice 警告(如 $a = &$b; $a = &$c; 不合法)。

2、PHP引用变量的原理

PHP 内部通过两个关键结构实现变量管理:

  1. 符号表(Symbol Table) :存储变量名及其对应的「指针」(指向内存块的引用)。
  2. 内存块(Zval Container) :存储变量的实际值,以及一个「引用计数(refcount)」属性。

引用变量的本质 :让多个变量名在符号表中指向同一个内存块,此时内存块的 refcount 会增加(记录有多少个变量引用它)。

php 复制代码
$name = 'stark张宇';
xdebug_debug_zval('name'); //(interned, is_ref=0)string 'stark张宇' (length=11)
$nick = &$name;
xdebug_debug_zval('name'); //(refcount=2, is_ref=1)string 'stark张宇' (length=11)

# 引用计数 refcount: 2 表示有 2 个变量共享该内存
# is_ref 是否为引用
# string 是类型
# stark张宇 是变量的值

3、引用与普通赋值的底层区别

普通赋值(值拷贝)

当执行 $b = $a 时:

  • $a 是简单类型(int、string 等)或数组,PHP 会复制一份新的内存块$b,此时 $a$brefcount 均为 1。
  • 修改 $b 只会改变其对应的新内存块,不影响 $a

引用赋值(共享内存)

当执行 $b = &$a 时:

  • 不会创建新内存块,而是让 $b 在符号表中直接指向 $a 对应的内存块,内存块的 refcount 从 1 变为 2。
  • 修改 $b 时,直接操作共享的内存块,因此 $a 的值会同步变化。

4.引用计数(refcount)的作用

内存块的 refcount 属性用于跟踪引用它的变量数量,是 PHP 垃圾回收的核心依据:

  • 当变量被赋值给新引用时,refcount 加 1。
  • 当变量被 unset() 或超出作用域时,refcount 减 1。
  • refcount 减为 0 时,PHP 会释放该内存块(销毁数据)。
php 复制代码
$a = 10;       // refcount=1
$b = &$a;      // refcount=2
unset($b);     // refcount=1(仅 $a 引用)
unset($a);     // refcount=0(内存被释放)

5.特殊场景:对象的「隐式引用」

PHP 中对象的赋值行为与普通变量不同:

  • 对象默认是引用传递 :执行 $obj2 = $obj1 时,$obj2 会直接指向 $obj1 对应的对象内存块(无需 &),refcount 加 1。
  • 这是因为对象通常占用内存较大,PHP 为优化性能,默认采用引用方式避免冗余拷贝。
  • 若需真正复制对象(而非引用),需使用 clone 关键字,此时会创建新内存块(refcount 独立)。
php 复制代码
class Person{
    public $name = 'stark张宇';
}
$person = new Person();

xdebug_debug_zval('person'); 
#(refcount=1, is_ref=0)
#object(Person)[1]
#  public 'name' => (refcount=0, is_ref=0)string 'stark张宇' (length=11)

$person2 = $person;
#(refcount=2, is_ref=0)
#object(Person)[1]
#  public 'name' => (refcount=0, is_ref=0)string 'stark张宇' (length=11)

xdebug_debug_zval('person');
$person2->name = '畅宝';

xdebug_debug_zval('person');
#(refcount=2, is_ref=0)
#object(Person)[1]
#  public 'name' => (refcount=0, is_ref=0)string '畅宝' (length=6)

PHP 引用变量的工作原理可概括为:通过符号表让多个变量名绑定到同一内存块,并通过引用计数管理内存生命周期。这种机制避免了数据的冗余拷贝,既能节省内存,又能实现变量间的同步修改,但需注意与对象默认引用行为的区别。

php 复制代码
// 使用更大的数据来测试
$data = str_repeat('测试写时复制机制', 10000);
var_dump(memory_get_usage());

$copy = $data;
var_dump(memory_get_usage()); // 内存变化很小,因为还没有实际复制

$copy = str_repeat('修改后的数据', 10000);
var_dump(memory_get_usage()); // 内存会明显增加,因为发生了实际复制

二、常量及数据类型考点

1、帮我整理PHP字符串的定义方式及各自区别

在 PHP 中,字符串的定义方式主要有四种,每种方式有其独特的语法和使用场景,下面为你详细整理:

1.1 单引号字符串(' '),使用单引号包裹字符串内容

  • 不会解析变量,变量会被当作普通字符串输出
  • 仅支持转义 '(单引号)和 \(反斜杠)两个转义字符
  • 解析速度较快,因为不需要处理变量和复杂转义
php 复制代码
$name = "PHP";
echo 'Hello $name'; // 输出:Hello $name
echo 'It's a test'; // 输出:It's a test

1.2. 双引号字符串(" "),使用双引号包裹字符串内容

  • 会解析字符串中的变量,变量值会被替换后输出
  • 支持更多转义字符,如 \n(换行)、\t(制表符)、"(双引号)等
  • 解析速度相对单引号稍慢
php 复制代码
$name = "PHP";
echo "Hello $name"; // 输出:Hello PHP
echo "Line 1\nLine 2"; // 输出两行文本

1.3 heredoc 语法(<<<)

使用 <<<标识符 开始,另起一行写内容,最后一行以 标识符; 结束

  • 语法类似双引号字符串,会解析变量和转义字符
  • 适合定义多行字符串,保持格式整洁
  • 结束标识符必须顶格写,前面不能有任何空格或字符
php 复制代码
$name = "PHP";
$str = <<<EOD
Hello $name
这是第二行
EOD;
echo $str;

1.4 nowdoc 语法(<<<' 标识符 ')

与 heredoc 类似,但标识符需要用单引号包裹(<<<'标识符'

  • 语法类似单引号字符串,不解析变量和转义字符
  • 适合定义包含大量特殊字符或代码片段的多行字符串
  • 结束标识符同样需要顶格写
php 复制代码
$name = "PHP";
$str = <<<'EOD'
Hello $name
这是第二行
EOD;
echo $str; // 输出时$name不会被解析

2. 浮点类型不能用于运算比较运算中

在 PHP 中,浮点类型(float)可以用于运算和比较,但由于浮点数的精度特性,直接进行比较可能会产生意外结果。这是因为计算机无法精确表示某些十进制小数(如 0.1),导致微小的精度误差。

解决方案:

php 复制代码
$a = 0.1 + 0.2;
$b = 0.3;

// 检查两个浮点数的差值是否在可接受的精度范围内
if (abs($a - $b) < 0.000001) {
    echo "相等";
} else {
    echo "不相等";
}

转换为整数比较(适用于金额等场景):

php 复制代码
// 处理金额时,先转换为分(整数)再计算
$a = 10 + 20; // 0.1元 + 0.2元 = 0.3元(转换为分)
$b = 30;      // 0.3元(转换为分)

var_dump($a == $b); // 输出 bool(true)

使用 bcmath 扩展(高精度数学运算):

php 复制代码
// 需要开启 bcmath 扩展
$a = bcadd('0.1', '0.2', 2); // 第三个参数指定小数位数
$b = '0.3';

var_dump(bccomp($a, $b) == 0); // 输出 bool(true)

浮点数可以用于运算和比较,但应避免直接使用 ===== 进行比较。推荐使用精度范围判断或高精度数学扩展(如 bcmath)来确保比较结果的准确性,尤其是在处理金融数据等对精度要求较高的场景。

3.在 PHP 中,以下七种情况会被视为 false(即 "假值"):

布尔值 false、整数 0、浮点数 0.0、空字符串 "" 和字符串 "0"、空数组 []null

php 复制代码
var_dump((bool)false);      // bool(false)
var_dump((bool)0);          // bool(false)
var_dump((bool)0.0);        // bool(false)
var_dump((bool)"");         // bool(false)
var_dump((bool)"0");        // bool(false)
var_dump((bool)[]);         // bool(false)
var_dump((bool)null);       // bool(false)

除上述情况外,所有其他值(如非空字符串、非零数字、资源类型、非空对象等)均为 "真值"(转换为布尔值时等于 true)。

4.全局数组及作用

PHP 提供了多个预定义的全局数组,这些数组包含了各种重要的信息,如服务器环境、请求数据、会话信息等。以下是 PHP 中常用的全局数组及其作用:

1、$_GET

用于获取通过 URL GET 方法传递的参数,PHP 本身没有明确限制 $_GET 传递数据的大小,但实际传递大小受两个关键因素制约:

Web 服务器配置:这是最主要的限制来源。

  • Apache:默认受 LimitRequestLine 配置影响,通常默认值约为 8190 字节(约 8KB)。
  • Nginx:默认受 client_header_buffer_size 配置影响,通常默认值约为 4KB

浏览器限制 :不同浏览器对 URL 长度有默认限制,间接决定了 $_GET 的最大传递量,多数浏览器限制在 2KB - 8KB 之间(例如 IE 曾限制为 2KB,现代浏览器普遍支持更大长度,但仍不建议超过 8KB)。

2、$_POST

用于获取通过 HTTP POST 方法提交的数据,参数不会显示在 URL 中,适合传输大量数据或敏感信息

3、$_REQUEST

包含了 GET、POST 和 $ COOKIE 中的所有数据,具体包含哪些数据取决于 php.ini 中的 request_order 配置

4、$_COOKIE

用于获取客户端的 Cookie 信息,Cookie 存储在客户端,可用于跟踪用户会话或存储用户偏好设置

php 复制代码
bool setcookie(string $name [, string $value = "" [, int $expire = 0 [, string $path = "" [, string $domain = "" [, bool $secure = false [, bool $httponly = false ]]]]])
  • 参数说明:

    • $name:Cookie 名称
    • $value:Cookie 值(默认空)
    • $expire:过期时间(时间戳,0 表示会话结束后失效)
    • $path:Cookie 生效的路径
    • $domain:Cookie 生效的域名
    • $secure:是否仅通过 HTTPS 传输
    • $httponly:是否仅允许 HTTP 协议访问(防止 JS 读取,增强安全性)
php 复制代码
# 1小时后过期,全站生效
setcookie('user', 'admin', time() + 3600, '/')

# 删除 Cookie(需将过期时间设为过去的时间戳)
setcookie('user', '', time() - 3600, '/');

Cookie 操作需注意:setcookie() 必须在 HTML 输出前调用,否则会报错。

5、$_SESSION

用于访问和操作当前用户的会话数据、数据存储在服务器端,需要先调用 session_start() 才能使用

php 复制代码
$_SESSION['user_id'] = 123;       // 存储数据
echo $_SESSION['user_id'];        // 读取数据

session_destroy() 销毁当前会话的所有数据(但不清除 $_SESSION 数组和会话 ID),通常需配合 unset($_SESSION) 彻底清除

php 复制代码
session_start();
session_destroy();
unset($_SESSION); // 清除内存中的会话数据

session_unset() 清除当前会话中的所有变量(仅清空 $_SESSION 数组,不销毁会话)

session_id() 获取或设置当前会话 ID

6、$_SERVER

$_SERVER 是 PHP 中非常重要的超全局数组,包含了服务器环境、请求头、脚本路径等关键信息。以下是其中最常用的重要数据项:

6.1. 脚本与路径相关

  • $_SERVER['PHP_SELF'] 当前执行脚本的路径(相对于网站根目录)
  • $_SERVER['SCRIPT_FILENAME'] 当前执行脚本的绝对路径
  • $_SERVER['DOCUMENT_ROOT'] 网站的根目录绝对路径(由服务器配置定义)

6.2. URL 与请求相关

  • $_SERVER['REQUEST_URI']当前请求的完整 URI(包含路径和查询参数)
  • $_SERVER['QUERY_STRING'] URL 中的查询参数部分(即 ? 后面的内容)。
  • $_SERVER['REQUEST_METHOD']当前请求使用的 HTTP 方法(如 GETPOSTPUTDELETE 等)。

6.3. 客户端信息

  • $_SERVER['REMOTE_ADDR']客户端的 IP 地址(可能是用户真实 IP 或代理 IP)。
  • $_SERVER['REMOTE_PORT']客户端连接到服务器时使用的端口号。
  • $_SERVER['HTTP_USER_AGENT']客户端的用户代理字符串(包含浏览器、系统等信息)。
  • $_SERVER['HTTP_REFERER']引导用户访问当前页面的前一页面 URL(可能被浏览器禁用或修改)。

6.4.服务器信息

  • $_SERVER['SERVER_ADDR'] 服务器的 IP 地址。
  • $_SERVER['SERVER_NAME']服务器的主机名(由服务器配置定义,通常是域名)。
  • $_SERVER['SERVER_PORT']服务器监听的端口号(默认 HTTP 为 80,HTTPS 为 443)。
  • $_SERVER['SERVER_SOFTWARE']服务器软件信息。

6.5. 协议与环境

  • $_SERVER['HTTPS']若请求通过 HTTPS 协议,则值为 on;否则为空或未定义。
  • $_SERVER['SERVER_PROTOCOL']客户端使用的 HTTP 协议版本。
  • $_SERVER['REQUEST_TIME'] 请求开始时的时间戳(可用于计算脚本执行时间)。

全部参数如下:

php 复制代码
array (size=34)
  'USER' => string 'apache' (length=6) // 运行 PHP 进程的系统用户(此处为 Apache 服务器的运行用户)
  'HOME' => string '/usr/share/httpd' (length=16) // 运行 PHP 进程的系统用户的主目录(Apache 用户的默认主目录)
  'HTTP_ACCEPT_LANGUAGE' => string 'zh-CN,zh;q=0.9' (length=14) // 客户端浏览器支持的语言,优先简体中文,其次中文(权重0.9)
  'HTTP_ACCEPT_ENCODING' => string 'gzip, deflate' (length=13) // 客户端支持的内容压缩编码方式(gzip和deflate)
  'HTTP_ACCEPT' => string 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7' (length=135) // 客户端可接受的MIME类型,优先HTML及相关文档类型
  'HTTP_USER_AGENT' => string 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36' (length=111) // 客户端浏览器标识:Windows 10系统,Chrome 139.0.0.0浏览器(基于WebKit内核)
  'HTTP_UPGRADE_INSECURE_REQUESTS' => string '1' (length=1) // 浏览器请求将HTTP链接自动升级为HTTPS(增强安全性)
  'HTTP_CONNECTION' => string 'keep-alive' (length=10) // 客户端要求保持长连接(减少连接建立开销)
  'HTTP_HOST' => string 'www.stark.com' (length=13) // 客户端请求的主机名(浏览器地址栏中的域名)
  'REDIRECT_STATUS' => string '200' (length=3) // 重定向后的HTTP状态码(200表示请求成功)
  'SERVER_NAME' => string 'stark.com' (length=9) // 服务器配置中定义的主机名(可能与客户端请求的HTTP_HOST不一致)
  'SERVER_PORT' => string '80' (length=2) // 服务器监听的端口(80为HTTP协议默认端口)
  'SERVER_ADDR' => string '192.168.31.142' (length=14) // 服务器的IP地址(局域网内地址)
  'REMOTE_PORT' => string '53519' (length=5) // 客户端连接服务器时使用的本地端口(随机分配)
  'REMOTE_ADDR' => string '192.168.31.1' (length=12) // 客户端的IP地址(局域网内地址)
  'SERVER_SOFTWARE' => string 'nginx/1.26.0' (length=12) // 服务器软件及版本(此处为Nginx 1.26.0)
  'GATEWAY_INTERFACE' => string 'CGI/1.1' (length=7) // 服务器使用的CGI协议版本
  'REQUEST_SCHEME' => string 'http' (length=4) // 请求使用的协议(http表示未加密连接)
  'SERVER_PROTOCOL' => string 'HTTP/1.1' (length=8) // 客户端使用的HTTP协议版本
  'DOCUMENT_ROOT' => string '/home/wwwroot/stark.com' (length=23) // 网站的根目录绝对路径(服务器配置的网站文件存放目录)
  'DOCUMENT_URI' => string '/index.php' (length=10) // 客户端请求的文档路径(相对于网站根目录)
  'REQUEST_URI' => string '/index.php' (length=10) // 完整的请求URI(包含路径,无查询参数)
  'SCRIPT_NAME' => string '/index.php' (length=10) // 执行脚本的路径(相对于网站根目录)
  'CONTENT_LENGTH' => string '' (length=0) // 请求体的长度(GET请求通常为空)
  'CONTENT_TYPE' => string '' (length=0) // 请求体的MIME类型(GET请求通常为空)
  'REQUEST_METHOD' => string 'GET' (length=3) // HTTP请求方法(此处为GET方法)
  'QUERY_STRING' => string '' (length=0) // URL中的查询参数(?后面的内容,此处为空)
  'PATH_TRANSLATED' => string '/home/wwwroot/stark.com' (length=23) // 被翻译后的文件系统路径(通常与DOCUMENT_ROOT一致)
  'PATH_INFO' => string '' (length=0) // 脚本路径后的额外路径信息(此处无额外路径)
  'SCRIPT_FILENAME' => string '/home/wwwroot/stark.com/index.php' (length=33) // 执行脚本的绝对路径(服务器上的实际文件路径)
  'FCGI_ROLE' => string 'RESPONDER' (length=9) // FastCGI角色(RESPONDER表示处理请求并返回响应)
  'PHP_SELF' => string '/index.php' (length=10) // 当前执行脚本的路径(相对于网站根目录,常用于表单提交)
  'REQUEST_TIME_FLOAT' => float 1758166341.6998 // 请求开始的精确时间戳(包含毫秒级精度)
  'REQUEST_TIME' => int 1758166341 // 请求开始的时间戳(秒级精度,可用于计算脚本执行耗时)

7、$_FILES

核心属性(每个上传文件的信息)

  • name:上传文件在客户端的原始文件名(包含扩展名)。$_FILES['avatar']['name'] 可能返回 head.jpg

  • type:客户端浏览器提供的文件 MIME 类型(如 image/jpegapplication/pdf)。$_FILES['avatar']['type'] 可能返回 image/png

  • tmp_name:文件上传到服务器后,临时存储的路径(PHP 自动生成)。脚本执行结束后会自动删除,需用 move_uploaded_file() 永久保存。

  • size:上传文件的大小(单位:字节),204800 表示 200KB。

  • error:文件上传的错误代码(0 表示无错误)

    • 0:上传成功
    • 1:文件大小超过 php.iniupload_max_filesize 限制
    • 2:文件大小超过表单中 MAX_FILE_SIZE 限制
    • 3:文件仅部分上传
    • 4:未上传任何文件
    • 6:缺少临时文件夹(服务器配置问题)
    • 7:文件写入失败(权限问题)

关键配置(php.ini)

  • file_uploads = On:是否允许文件上传(默认开启)。
  • upload_max_filesize = 2M:单个文件的最大上传大小。
  • post_max_size = 8M:POST 数据的总最大大小(包含所有表单数据)。
  • upload_tmp_dir = /tmp:临时文件存储目录(需服务器有写入权限)。
  • max_file_uploads = 20:单次请求允许上传的最大文件数量。

安全注意事项

  1. 永远不要信任客户端数据(nametype 可被篡改),必须在服务器端验证。
  2. 使用 move_uploaded_file() 而非 copy()rename(),防止非上传文件被操作。
  3. 上传目录应设置严格权限(禁止执行脚本,如 chmod 0644)。
  4. 重命名上传文件(如用 uniqid()),避免文件名冲突或恶意文件名(如 ../evil.php)。
  5. 限制允许的文件类型和大小,避免超大文件或恶意脚本上传。

8. <math xmlns="http://www.w3.org/1998/Math/MathML"> E N V 和 _ENV 和 </math>ENV和GLOBALS

  • 作用:包含了环境变量的信息,可能需要在 php.ini 中开启
  • php.ini 中,variables_order 决定了 PHP 解析环境变量的顺序,默认值通常包含 E(代表 $_ENV)。
  • 若值为 GPCS(不含 E),则 $_ENV 会为空,需修改为 EGPCS 启用

5.常量和常用的预定义常量

在 PHP 中,define()const都用于定义常量,但它们在实现原理使用场景特性上有显著区别,深入理解这些差异需要结合 PHP 的执行机制和符号表管理逻辑。

5.1、定义时机:编译时 vs 运行时

这是两者最核心的区别,源于 PHP 的执行流程(词法分析→语法分析→编译为 opcode→执行 opcode)。

const编译时定义 const是语言结构(language construct),在 PHP 编译阶段(生成 opcode 之前)就会被解析并写入符号表。

  • 它的定义在代码执行前就已确定,无法依赖运行时的条件(如if语句)。
  • 编译阶段会对const进行语法校验,例如不允许重复定义(直接报错)。

define():运行时定义 define()是函数,其执行发生在 PHP 运行阶段(opcode 执行时)。

  • 它的定义可以依赖运行时条件(如if (condition) { define(...) })。
  • 直到函数被调用时,常量才会被写入符号表,重复定义时可通过第三个参数$case_insensitive控制是否报错(默认不允许重复)。

性能差异:const的性能略优于define(),原因是:

  • const在编译期已写入符号表,访问时直接从符号表读取,无需运行时函数调用开销。
  • define()在运行时需执行函数调用,涉及参数解析、符号表动态写入等操作,开销略高。

5.2 常用的 PHP 预定义常量分类

  • 版本与环境相关

    • PHP_VERSION:当前 PHP 版本(如 8.2.10
    • PHP_OS:运行 PHP 的操作系统(如 LinuxWINNT
    • PHP_SAPI:服务器应用程序编程接口(如 clifpm-fcgi
    • PHP_EOL:跨平台的换行符常量
  • 错误与日志相关

    • E_ERROR:致命错误的错误级别(值为 1
    • E_WARNING:警告错误的错误级别(值为 2
    • E_PARSE:语法解析错误的错误级别(值为 4
  • 文件与路径相关

    • __FILE__:当前文件的完整路径(包含文件名)
    • __DIR__:当前文件所在目录的路径(等价于 dirname(__FILE__)
    • PHP_EXTENSION_DIR:PHP 扩展模块存放目录
  • 魔术常量(动态变化)

    • __LINE__:当前代码所在的行号
    • __FUNCTION__:当前函数名(类方法中则为方法名)
    • __CLASS__:当前类名(包含命名空间)
    • __NAMESPACE__:当前命名空间名称
    • __METHOD__: 当前方法名称

5.3 预定义常量的底层运行机制

PHP 的预定义常量本质上是内核在初始化阶段预先注册到常量表中的值,其运行机制可分为以下几个步骤:

  1. 常量注册阶段 PHP 内核启动时(如 php -f script.php 执行或 FPM 进程初始化),会通过内部函数(如 zend_register_standard_constants)将预定义常量注册到全局常量表(zend_constants 哈希表)中。 例如,PHP_VERSION 的值在编译 PHP 时就已确定,内核会将其字符串值(如 "8.2.10")与常量名绑定并存储。

  2. 动态常量的实时计算 对于魔术常量(如 __LINE____FILE__),其值并非固定,而是在代码编译或执行时动态生成

    • 当 PHP 解析器(Zend Engine)扫描代码时,会在遇到魔术常量的位置实时计算其值(如记录当前行号、文件路径)。
    • 例如,__LINE__ 的值由解析器在处理代码行时递增计数器获得,并替换为具体数值。
  3. 常量访问机制 当代码中使用预定义常量时,Zend Engine 会在全局常量表中查找对应的键(常量名),并直接返回其值,无需经过变量作用域检查,效率高于变量访问。

预定义常量由 PHP 内核直接管理,无需手动定义即可使用,大部分常量的值在 PHP 启动时确定,魔术常量则在代码解析 / 执行时动态生成,常量值不可修改(尝试修改会报 E_WARNING 错误),这是由内核通过常量表的只读属性保证的。

三、运算符考点

3.1 运算符优先级

  • 核心原则:优先级高的先执行,优先级相同按 "左结合"(除赋值、三元等右结合运算符)。
  • 高频优先级排序(从高到低): 算术运算符(++/-- > *///% > +/-) > 比较运算符(===/!== > ==/!=) > 逻辑运算符(! > && > ||) > 三元运算符 > 赋值运算符。
  • 面试题示例:echo 2 + 3 * 4 && 5 > 3 ? 'yes' : 'no'; 结果为 yes(计算顺序:3*4=12 → 2+12=14 → 5>3=true → 14&&true=true → 输出 yes)。
php 复制代码
$a = 0;
$b = 0;
if ($a = 3 > 0 || $b = 3 > 0) {
    xdebug_debug_zval('a'); // (refcount=0, is_ref=0)boolean true
    xdebug_debug_zval('b'); // (refcount=0, is_ref=0)int 0
    $a++;
    $b++;
    echo $a . PHP_EOL;
    echo $b . PHP_EOL;
}

按优先级计算 (优先级:关系运算符 > && > ||

  • && 优先级高于 ||,因此先计算 1 == 0 && 5 < 3,结果为 false
  • 再计算 3 > 2 || false,由于 3 > 2true,根据 || 的 "一真则真" 规则,最终结果为 true
php 复制代码
if( 3 > 2 || 1 == 0 && 5 < 3 ){
      
}

思考题 <math xmlns="http://www.w3.org/1998/Math/MathML"> a + + 和 + + a++ 和 ++ </math>a++和++a 有什么不同?

$a++ 先返回$a当前的值,然后再将$a的值加 1, 简单说:"先用后加"。

php 复制代码
$a = 5;
$b = $a++; // 先将$a的当前值(5)赋给$b,再将$a加1

echo $a; // 输出:6($a已被加1)
echo $b; // 输出:5($b得到的是$a递增前的值)

++$a 先将$a的值加 1,然后返回加 1 后的新值, 简单说:"先加后用"。

php 复制代码
$a = 5;
$b = ++$a; // 先将$a加1(变为6),再将新值(6)赋给$b

echo $a; // 输出:6($a已被加1)
echo $b; // 输出:6($b得到的是$a递增后的值)

四、流程控制考点

1、数组遍历 for foreach while list

流程控制是 PHP 逻辑执行的基础,核心考点围绕分支结构循环结构跳转语句及特殊语法展开,以下是高频考点梳理:

数组遍历

在 PHP 中,数组是核心数据结构,分为索引数组 (连续数字索引)和关联数组 (自定义键名)。遍历的本质是按规则访问数组中每个元素,不同遍历方式的差异体现在索引 / 键名依赖度指针控制代码简洁性场景适配性上。选择合适的遍历方式,能提升代码效率与可维护性。

  • for 循环:索引数组的 "精准控制" 遍历

for 循环依赖连续数字索引,通过 "初始化索引→判断索引范围→更新索引" 三步实现遍历,仅适用于索引数组(关联数组无连续数字索引,无法使用)。

php 复制代码
// 定义索引数组(默认索引从0开始)
$fruit = ['苹果', '香蕉', '橙子', '葡萄'];
// 关键:通过count()获取数组长度,控制循环边界
$length = count($fruit);

for ($i = 0; $i < $length; $i++) {
    echo "索引 {$i}:{$fruit[$i]}<br>";
}
// 输出:
// 索引 0:苹果
// 索引 1:香蕉
// 索引 2:橙子
// 索引 3:葡萄

逆序遍历:修改索引更新逻辑

php 复制代码
for ($i = $length - 1; $i >= 0; $i--) {
    echo "逆序索引 {$i}:{$fruit[$i]}<br>";
}

步长遍历:跳过部分元素(如每 2 个取 1 个)

php 复制代码
for ($i = 0; $i < $length; $i += 2) {
    echo "步长2索引 {$i}:{$fruit[$i]}<br>";
}
// 输出:索引 0:苹果、索引 2:橙子
  • foreach:数组遍历的 "简洁之王"

foreach 是 PHP 专为数组设计的遍历语法,无需关注索引 / 键名,自动迭代数组中所有元素,存在值遍历和引用遍历两种模式。

php 复制代码
// 仅遍历值(键名默认隐藏)
foreach ($user as $value) {
    echo "用户信息:{$value}<br>";
}

// 同时遍历键名和值(推荐,清晰明了)
foreach ($user as $key => $value) {
    echo "键名 {$key}:{$value}<br>";
}

加&表示引用原数组元素,遍历结束后需unset($value),避免后续变量污染。

php 复制代码
$score = [85, 92, 78];

// 加&表示引用原数组元素
foreach ($score as &$value) {
    $value += 5; // 所有分数加5分(直接修改原数组)
}

// 关键:遍历结束后需unset($value),避免后续变量污染
unset($value);

print_r($score); // 输出:Array ( [0] => 90 [1] => 97 [2] => 83 )

进阶用法:遍历对象与多维数组

php 复制代码
class Student {
    public $name = '李四';
    public $grade = '三年级';
}
$stu = new Student();
foreach ($stu as $key => $value) {
    echo "{$key}:{$value}<br>"; // 输出name:李四、grade:三年级
}
  • while 循环:指针控制的 "灵活遍历"

PHP 数组内置一个内部指针,默认指向数组第一个元素。while 循环通过current()(获取当前指针元素)、next()(指针后移)、each()(获取当前元素并指针后移)等函数控制指针,实现遍历。

php 复制代码
$color = ['红', '绿', '蓝'];
// 重置指针(确保从第一个元素开始,避免之前操作影响)
reset($color);

while ($current = current($color)) {
    echo "当前颜色:{$current}<br>";
    next($color); // 指针后移,否则会无限循环
}
// 输出:红、绿、蓝
  • do-while 循环:"至少执行一次" 的遍历

与 while 循环逻辑一致,唯一区别是先执行循环体,再判断条件。适用于 "无论数组是否为空,都需先执行一次逻辑" 的场景(如先展示表单,再判断是否有数组数据)。

即使数组为空,循环体也会执行一次,需在循环体内处理 "空数据" 场景,避免报错,同样需手动控制数组指针,防止无限循环。

php 复制代码
// 模拟空数组(无用户输入)
$userInput = [];
reset($userInput);

do {
    $current = current($userInput);
    if ($current) {
        echo "用户输入:{$current}<br>";
    } else {
        echo "暂无用户输入(但循环已执行1次)<br>";
    }
    next($userInput);
} while ($current); // 数组为空,条件不满足,循环终止
// 输出:暂无用户输入(但循环已执行1次)
  • list ():数组解构的 "辅助工具"

list()不是独立的遍历方式,而是数组元素解构赋值工具,常用于配合foreach/while快速提取数组元素(尤其适用于索引数组)。PHP 7.1 + 支持关联数组解构(通过键名匹配)。

基础示例 1:解构索引数组

php 复制代码
$info = ['王五', 30, '程序员'];
// list()变量顺序需与数组元素顺序一致
list($name, $age, $job) = $info;
echo "姓名:{$name},年龄:{$age},职业:{$job}";
// 输出:姓名:王五,年龄:30,职业:程序员

基础示例 2:配合 foreach 解构多维数组

php 复制代码
$employees = [
    ['赵六', 25, '产品经理'],
    ['孙七', 32, '测试工程师']
];

// 遍历同时解构,代码更简洁
foreach ($employees as list($name, $age, $job)) {
    echo "{$name}({$age}岁):{$job}<br>";
}
// 输出:
// 赵六(25岁):产品经理
// 孙七(32岁):测试工程师

进阶示例:PHP 7.1 + 关联数组解构

PHP 7.1 前,list()仅支持索引数组,且变量个数必须与数组元素个数一致(否则报错);

关联数组解构时,键名必须用字符串(如'title' => $title),且需确保键名在数组中存在(否则变量为null)。

php 复制代码
$book = [
    'title' => 'PHP核心编程',
    'author' => '李刚',
    'price' => 89
];

// 关联数组解构:键名必须匹配,顺序可任意
list('title' => $title, 'author' => $author) = $book;
echo "书名:{$title},作者:{$author}";
// 输出:书名:PHP核心编程,作者:李刚

2、控制循环流程

breakcontinue 是控制循环流程的核心关键字,但作用和使用场景截然不同。以下从 核心区别使用方法典型案例 三方面详细整理:

break break 用于立即结束循环,无论循环条件是否还满足,都会直接跳出循环体,执行循环后的代码。

基础用法(无参数) 适用于单重循环,直接终止当前循环:

php 复制代码
// foreach循环示例
$fruits = ['apple', 'banana', 'orange'];
foreach ($fruits as $fruit) {
    if ($fruit == 'banana') {
        break; // 遇到banana时,终止循环
    }
    echo $fruit . ' ';
}

continue 用于跳过当前循环中剩余的代码,直接进入下一次循环迭代(循环不会终止,继续执行下一轮)。

基础用法(无参数) 适用于单重循环,跳过当前次的剩余逻辑:

php 复制代码
// while循环示例
$i = 1;
while ($i <= 5) {
    $i++;
    if ($i == 3) {
        continue; // 当$i=3时,跳过本次剩余代码
    }
    echo $i . ' ';
}

进阶用法 break n 和 continue n 参数 n 为正整数,表示终止当前循环及外层的 n-1 层循环 (默认 n=1,即仅终止当前层),适用于嵌套循环

php 复制代码
// 定义一个二维关联数组:公司部门及其员工信息
$company = [
    '技术部' => [
        ['name' => '张三', 'position' => '开发工程师', 'status' => '在职'],
        ['name' => '李四', 'position' => '测试工程师', 'status' => '离职'],
        ['name' => '王五', 'position' => '架构师', 'status' => '在职']
    ],
    '市场部' => [
        ['name' => '赵六', 'position' => '市场专员', 'status' => '在职'],
        ['name' => '孙七', 'position' => '销售经理', 'status' => '在职'],
        ['name' => '周八', 'position' => '市场总监', 'status' => '离职']
    ],
    '人事部' => [
        ['name' => '吴九', 'position' => '招聘专员', 'status' => '在职'],
        ['name' => '郑十', 'position' => 'HR经理', 'status' => '在职']
    ]
];

break n 例子:

php 复制代码
echo "=== 演示 break n 的作用 ===".PHP_EOL;
// 需求:遍历部门和员工,找到第一个离职员工后终止所有循环
foreach ($company as $dept => $employees) {
    echo "正在检查 {$dept}:".PHP_EOL;
    foreach ($employees as $emp) {
        echo "- 员工 {$emp['name']}({$emp['position']}):{$emp['status']}".PHP_EOL;
        if ($emp['status'] === '离职') {
            echo "找到离职员工,使用 break 2 终止所有循环".PHP_EOL;
            break 2; // 终止当前循环和外层循环(共2层)
        }
    }
}

//=== 演示 break n 的作用 ===
//正在检查 技术部:
//- 员工 张三(开发工程师):在职
//- 员工 李四(测试工程师):离职
//找到离职员工,使用 break 2 终止所有循环

continue n 的作用

php 复制代码
echo "=== 演示 continue n 的作用 ===".PHP_EOL;
// 需求:遍历部门和员工,遇到离职员工时跳过当前部门的剩余员工,直接处理下一个部门
foreach ($company as $dept => $employees) {
    echo "正在检查 {$dept}:".PHP_EOL;
    foreach ($employees as $emp) {
        if ($emp['status'] === '离职') {
            echo "- 发现离职员工 {$emp['name']},使用 continue 2 跳过当前部门剩余员工".PHP_EOL;
            continue 2; // 跳过当前循环和外层循环的当前次,直接进入外层下一次循环
        }
        echo "- 员工 {$emp['name']}({$emp['position']}):{$emp['status']}".PHP_EOL;
    }
}

//=== 演示 continue n 的作用 ===
//正在检查 技术部:
//- 员工 张三(开发工程师):在职
//- 发现离职员工 李四,使用 continue 2 跳过当前部门剩余员工
//正在检查 市场部:
//- 员工 赵六(市场专员):在职
//- 员工 孙七(销售经理):在职
//- 发现离职员工 周八,使用 continue 2 跳过当前部门剩余员工
//正在检查 人事部:
//- 员工 吴九(招聘专员):在职
//- 员工 郑十(HR经理):在职

3、分支结构核心考点

PHP 分支结构是控制程序流程的基础,也是面试与笔试的高频考点,核心涵盖 if 系列分支switch-case 分支三元运算符模板替代语法 四大类。以下按模块梳理考点,包含基础语法、核心陷阱、典型例题,助力精准掌握。

3.1 if 系列分支(if /if-else/if-elseif-else)

if 系列是最常用的分支结构,用于根据条件执行不同代码块,核心考点集中在 条件判断的弱类型陷阱空值处理

高频陷阱'0'0 的判断差异

php 复制代码
$var1 = '0'; // 字符串0
$var2 = 0;   // 数值0

if ($var1) { echo 'true'; } else { echo 'false'; } // 输出:false('0'转布尔为false)
if ($var2) { echo 'true'; } else { echo 'false'; } // 输出:false(0转布尔为false)

// 严格比较(===)可区分类型
if ($var1 === 0) { echo 'true'; } else { echo 'false'; } // 输出:false(类型不同)

empty()isset() 在条件中的使用

  • empty($var):判断变量是否 "为空"(等价于 !isset($var) || $var == false),即使变量未定义也不报错。
  • isset($var):判断变量是否已定义且值不为 null,变量未定义时返回 false

if-elseif 的 "短路执行"

if-elseif顺序匹配 ,一旦某个条件为 true,执行对应代码块后,跳过后续所有 elseif 和 else,不重复判断。

陷阱案例:条件顺序错误导致逻辑失效

php 复制代码
$score = 90;
// 错误:>=60 先匹配,90会进入"及格",而非"优秀"
if ($score >= 60) {
    echo '及格';
} elseif ($score >= 80) {
    echo '优秀';
}

// 正确:按"从高到低"顺序判断
if ($score >= 80) {
    echo '优秀'; // 执行此句,后续跳过
} elseif ($score >= 60) {
    echo '及格';
}
php 复制代码
// 题目:输出以下代码结果
$num = '10';
if ($num > 5 && $num < 15) {
    echo 'A';
} elseif ($num == 10) {
    echo 'B';
} else {
    echo 'C';
}
// 解析:'10'与数值比较时自动转为10,满足$num>5且<15,输出A(elseif被跳过)

3.2 switch-case 分支

switch-case 适用于 "单个变量与多个值匹配" 的场景,核心考点是 穿透问题松散比较规则

switch 的 "松散比较"(==)

switch 内部使用 松散比较(不严格校验类型),仅判断值是否相等,不判断类型。

php 复制代码
$type = '5';
switch ($type) {
    case 5: // '5' == 5 → true
        echo '数值5';
        break;
    case '5':
        echo '字符串5';
        break;
}
// 输出:数值5(松散比较匹配第一个case)

break 的必要性(穿透问题)

若 case 代码块后无 break,程序会继续执行下一个 case 的代码块 (无论是否匹配),直到遇到 break 或结束。

php 复制代码
$num = 2;
switch ($num) {
    case 1:
        echo '1';
    case 2:
        echo '2'; // 执行
    case 3:
        echo '3'; // 无break,穿透执行
        break;
    default:
        echo 'default';
}
// 输出:23(case2无break,穿透到case3)

case 后的值类型

case 后的值可以是 常量、字面量或表达式(但不能是变量,PHP 8.0+ 支持变量),且多个 case 可共享一个代码块。

php 复制代码
$x = 3;
switch ($x) {
    case 1 + 1: // 表达式:2
        echo '2';
        break;
    case 2 + 1: // 表达式:3
        echo '3';
        break;
    // 多个case共享代码块
    case 4:
    case 5:
        echo '4或5';
        break;
}
// 输出:3

switch (true) 的特殊用法

switch(表达式) 中表达式为 true 时,case 后的值会被当作 "条件表达式" 判断(等价于 if-elseif),适用于多条件复杂判断。

php 复制代码
$age = 25;
switch (true) {
    case $age < 18:
        echo '未成年';
        break;
    case $age >= 18 && $age < 60: // 条件为true
        echo '成年'; // 执行
        break;
    case $age >= 60:
        echo '老年';
        break;
}
// 输出:成年

3.3 三元运算符

三元运算符是 if-else 的简化形式,核心考点是 优先级PHP 版本差异(结合性)

php 复制代码
// 完整形式:条件为true时返回"值1",否则返回"值2"
$result = 条件表达式 ? 值1 : 值2;

// 简写形式(PHP 5.3+):条件为true时返回"条件表达式结果",否则返回"值2"
$result = 条件表达式 ?: 值2; 
// 等价于:$result = 条件表达式 ? 条件表达式 : 值2;

三元运算符优先级 高于赋值运算符(=),低于逻辑运算符(&&、||) ,复杂表达式需加括号避免歧义。

php 复制代码
// 错误:优先级导致先执行 10>5 ? 10 : 5,再赋值给$a=... && 3
$a = 10 > 5 ? 10 : 5 && 3; 
// 等价于:$a = (10>5?10:5) && 3 → 10 &&3 → true

// 正确:加括号明确逻辑
$a = 10 > 5 ? 10 : (5 && 3); 
// 等价于:$a = true ?10 : true →10

嵌套三元的 "结合性"(版本差异)

  • PHP 5.x :三元运算符是 左结合(从左到右计算)。
  • PHP 7.0+ :三元运算符是 右结合(从右到左计算),与多数语言一致。
php 复制代码
// 代码:
$a = 1; $b = 2; $c = 3;
echo $a > $b ? $a : $b > $c ? $b : $c;

// PHP 5.x(左结合):
// 等价于:((1>2) ?1 :2) >3 ?2 :3 → (2>3) ?2:3 →3

// PHP 7.0+(右结合):
// 等价于:1>2 ?1 : (2>3 ?2:3) →1>2?1:3 →3?不对,换个明显案例:
$x=1;$y=2;$z=3;
echo $x==1 ? $y : $x==2 ? $z : $x;

// PHP5:((1==1)?2:1==2) ?3:1 → (2 ?3:1) →3
// PHP7+:1==1 ?2 : (1==2 ?3:1) →2(正确逻辑)

4、自定义函数考点

4.1 变量的作用域

在 PHP 中,作用域(Scope)决定了变量的可访问范围,而静态变量(Static Variables)则是一种特殊的变量类型,具有独特的生命周期和访问规则。下面详细整理这两部分内容:

局部作用域(Local Scope)

在函数内部声明的变量,仅在该函数内部可访问,函数执行结束后,局部变量会被销毁,再次调用函数时会重新初始化。

php 复制代码
function test() {
    $localVar = "我是局部变量"; // 局部作用域
    echo $localVar; // 正确:函数内部可访问
}

test(); // 输出:我是局部变量
echo $localVar; // 错误:函数外部无法访问局部变量(会报"Undefined variable"错误)

全局作用域(Global Scope)

在函数外部声明的变量,可在全局范围内(函数外部)访问,全局变量默认不能在函数内部直接访问,需通过 global 关键字或超全局数组 $GLOBALS 声明后才能使用。

php 复制代码
$globalVar = "我是全局变量"; // 全局作用域

// 方法1:使用global关键字
function test1() {
    global $globalVar; // 声明使用全局变量
    echo $globalVar; // 正确:可访问全局变量
}

// 方法2:使用$GLOBALS数组(超全局变量)
function test2() {
    echo $GLOBALS['globalVar']; // 正确:$GLOBALS无需声明即可访问
}

test1(); // 输出:我是全局变量
test2(); // 输出:我是全局变量
echo $globalVar; // 正确:全局范围内可直接访问

超全局作用域(Superglobal Scope)

PHP 预定义的一组特殊变量,在脚本的任何作用域中(包括函数、类内部)都可直接访问,无需声明。

常见超全局变量:

$_GET:获取 URL 参数

$_POST:获取 POST 请求数据

$_SESSION:会话变量

$_COOKIE:Cookie 变量

$_SERVER:服务器环境信息

$GLOBALS:包含所有全局变量的数组

php 复制代码
function test() {
    echo $_SERVER['PHP_SELF']; // 正确:直接访问超全局变量
}
test(); // 输出当前脚本的路径

4.2 静态变量(Static Variables)

静态变量是在函数或类中用 static 关键字声明的变量,具有特殊的生命周期。在函数内部用 static 声明的变量,其值在函数多次调用后会被保留(不会被销毁)。

特点:

  • 仅在函数第一次调用时初始化;
  • 作用域仍为局部(仅函数内部可访问);
  • 函数执行结束后不会被销毁,下次调用时沿用上次的值。
php 复制代码
function countCalls() {
    static $count = 0; // 静态变量,仅第一次调用时初始化
    $count++;
    echo "被调用了 $count 次<br>";
}

countCalls(); // 输出:被调用了 1 次
countCalls(); // 输出:被调用了 2 次
countCalls(); // 输出:被调用了 3 次

思考题:代码会执行什么

php 复制代码
$count = 5;
function getCount()
{
    static $count;
    return $count++;
}
echo $count.PHP_EOL;
++$count;
echo getCount().PHP_EOL;
echo getCount().PHP_EOL;

静态变量的调用

在类中用 static 声明的属性或方法,属于类本身而非实例。

特点:

  • 无需实例化类即可访问(通过 类名::成员名 访问);
  • 静态方法中不能使用 $this(因不属于具体实例);
  • 静态属性可被所有实例共享。
php 复制代码
class Counter {
    public static $total = 0; // 静态属性
    
    public static function increment() { // 静态方法
        self::$total++; // 用self访问当前类的静态成员
    }
}

// 无需实例化,直接访问静态成员
Counter::increment();
Counter::increment();
echo Counter::$total; // 输出:2

4.3 自定义函数参数

值传递:函数内部修改参数不会影响外部实参。

php 复制代码
// 正确:默认参数在右侧
function setName($name) {
    return "$name";
}
//可以赋值默认参数
function greet($name, $prefix = "Mr.") {
    return "$prefix $name";
}

引用传递:在参数前加&,函数内部修改会同步影响外部实参(需注意实参必须是变量,不能是常量或表达式)。

php 复制代码
function add(&$num)
{
    $num++;
}
$a = 10;
add($a);
echo $a; // 输出11(外部变量被修改)

可变参数(动态参数):PHP 5.6+ 支持用...声明可变参数,将多个实参打包为数组,处理参数数量不确定的场景。

php 复制代码
function sum(...$nums) {
    return array_sum($nums);
}
echo sum(1, 2, 3); // 输出6

类型声明(类型约束)

  • 限制参数的数据类型,支持标量类型(int/string/float/bool)、类 / 接口、arraycallable等。
  • 严格模式:默认宽松模式(自动类型转换),开启declare(strict_types=1)后严格匹配类型,不匹配则报错。
php 复制代码
// 标量类型声明
function multiply(int $a, int $b) {
    return $a * $b;
}
multiply(2, 3); // 正确,返回6
multiply("2", 3); // 宽松模式下自动转换为int,返回6;严格模式报错

5、系统内置函数

5.1 时间日期相关函数

date_default_timezone_set() - 设置默认时区, PHP 时间函数依赖时区配置,未设置时会报警告,需优先配置。

php 复制代码
// 设置时区为中国上海
date_default_timezone_set('Asia/Shanghai');

time() - 获取当前 Unix 时间戳 , 无参数,直接返回当前时间的秒级时间戳(整数)。

php 复制代码
$nowTimestamp = time();
echo $nowTimestamp; // 输出示例:1717245600(对应2024-06-01 12:00:00)

date() - 格式化时间戳为字符串, 将时间戳转换为人类可读的时间格式(如 2024-06-01 12:30:45)。

php 复制代码
// 格式化当前时间为 "年-月-日 时:分:秒"
echo date('Y-m-d H:i:s'); // 输出示例:2024-06-01 14:30:25

// 格式化指定时间戳(2024-01-01 00:00:00)
$timestamp = strtotime('2024-01-01');
echo date('Y年m月d日', $timestamp); // 输出:2024年01月01日

strtotime() - 字符串转时间戳,将人类可读的时间字符串(如 next Monday2024-06-01)转换为 Unix 时间戳。

php 复制代码
// 字符串转时间戳(绝对时间)
$timestamp1 = strtotime('2024-06-01 12:00:00');
echo $timestamp1; // 输出示例:1717245600

// 相对时间转时间戳(明天此时)
$timestamp2 = strtotime('+1 day');
echo date('Y-m-d H:i:s', $timestamp2); // 输出:2024-06-02 14:30:25(假设当前是6月1日)

// 基准时间的相对时间(2024-01-01 的前3天)
$base = strtotime('2024-01-01');
$timestamp3 = strtotime('-3 days', $base);
echo date('Y-m-d', $timestamp3); // 输出:2023-12-29

5.2 序列化与反序列函数

序列化:将复杂数据(数组、对象)转换为字符串,用于存储(如数据库、文件)或网络传输;反序列化则相反。

php 复制代码
$arr = ['name' => 'PHP', 'version' => '8.2'];
// 序列化数组
$serialized = serialize($arr);
echo $serialized;
// 输出结果(PHP特有格式):
// a:2:{s:4:"name";s:3:"PHP";s:7:"version";s:3:"8.2";}
// 解释:a=array(2个元素),s=string(长度),键值对对应

// 序列化字符串(来自上方示例)
$serialized = 'a:2:{s:4:"name";s:3:"PHP";s:7:"version";s:3:"8.2";}';

// 反序列化
$unserialized = unserialize($serialized);
print_r($unserialized);
// 输出结果:
// Array
// (
//     [name] => PHP
//     [version] => 8.2
// )

json转换:

将数据转换为 JSON 字符串(通用格式,支持跨语言,如 PHP→JS、PHP→Python)。

php 复制代码
$arr = ['name' => 'PHP', 'desc' => '中文描述', 'version' => 8.2];
// 普通序列化(中文会转义为\uXXXX)
$json1 = json_encode($arr);
echo $json1;
// 输出:{"name":"PHP","desc":"\u4e2d\u6587\u63cf\u8ff0","version":8.2}

// 保留中文 + 格式化输出
$json2 = json_encode($arr, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
echo $json2;
// 输出结果:
// {
//     "name": "PHP",
//     "desc": "中文描述",
//     "version": 8.2
// }
php 复制代码
$json = '{"name":"PHP","version":8.2}';

// 反序列化为对象(默认)
$obj = json_decode($json);
echo $obj->name; // 输出:PHP

// 反序列化为关联数组
$arr = json_decode($json, true);
echo $arr['version']; // 输出:8.2

5.3 字符串相关函数

字符串是 PHP 中最常用的数据类型,此类函数用于字符串的查找、截取、替换、转换等操作。需要注意的是,计算字符串的字节数(注意:中文在 UTF-8 编码下占 3 字节,GBK 下占 2 字节)。

获取字符串字节长度 strlenmb_strlen

php 复制代码
$str1 = 'PHP';
echo strlen($str1); // 输出:3(3个英文字母,每个1字节)

$str2 = 'PHP中文';
echo strlen($str2); // 输出:9(3 + 3*2,UTF-8下2个中文字符占6字节)

// UTF-8编码下统计字符数
echo mb_strlen($str2, 'UTF-8'); // 输出:5(3个英文 + 2个中文,共5个字符)

查找字符串 strpos() - 查找子串首次出现位置 , 返回子串在目标字符串中首次出现的索引 (从 0 开始),找不到返回 false

php 复制代码
$str = 'Hello PHP, PHP is good!';

// 查找 "PHP" 首次出现位置
$pos1 = strpos($str, 'PHP');
echo $pos1; // 输出:6(从0开始,"Hello " 占6个字符)

// 从索引 7 开始查找(跳过第一个PHP)
$pos2 = strpos($str, 'PHP', 7);
echo $pos2; // 输出:10

// 查找不存在的子串
$pos3 = strpos($str, 'Java');
if ($pos3 === false) {
    echo '子串不存在'; // 输出:子串不存在
}

截取字符串substr()mb_substr 从目标字符串中按字节截取部分内容,多字节字符(如中文)需注意编码问题。

php 复制代码
$str = 'Hello PHP!';

// 从索引2开始截取("llo PHP!")
echo substr($str, 2); // 输出:llo PHP!

// 从索引2开始,截取3字节("llo")
echo substr($str, 2, 3); // 输出:llo

// 从末尾第3个字符开始截取("P!")
echo substr($str, -3); // 输出:P!

// 从索引0开始,排除末尾2字节("Hello PH")
echo substr($str, 0, -2); // 输出:Hello PH

$str = 'PHP中文开发';
echo substr($str, 5, 2); //�� 这样就会产生字符串乱码 要使用mb_substr来解决这个问题

// UTF-8编码下,从第0个字符开始,截取3个字符("PHP")
echo mb_substr($str, 0, 3, 'UTF-8'); // 输出:PHP

// 从第3个字符开始,截取2个字符("中文")
echo mb_substr($str, 3, 2, 'UTF-8'); // 输出:中文

字符串替换str_replace 将目标字符串中的指定子串替换为新字符串,支持数组批量替换。

php 复制代码
$str = 'PHP is good, PHP is easy!';

// 单字符串替换(替换所有"PHP"为"Python")
$newStr1 = str_replace('PHP', 'Python', $str);
echo $newStr1; // 输出:Python is good, Python is easy!

// 数组批量替换("PHP"→"Python","good"→"great")
$search = ['PHP', 'good'];
$replace = ['Python', 'great'];
$newStr2 = str_replace($search, $replace, $str);
echo $newStr2; // 输出:Python is great, Python is easy!

// 统计替换次数
str_replace('PHP', 'Python', $str, $count);
echo $count; // 输出:2(共替换2次)

去除字符串首尾空白 trim 去除字符串开头和结尾 的空白字符(空格、制表符 \t、换行符 \n、回车符 \r 等),中间空白不变。

php 复制代码
$str = '  Hello PHP!  ';
// 去除首尾空格
echo trim($str); // 输出:Hello PHP!(首尾无空格)

// 自定义去除首尾的 "H" 和 "!"
$str2 = 'Hello PHP!';
echo trim($str2, 'H!'); // 输出:ello PHP

字符串分割为数组 explode 按指定分隔符将字符串分割成数组 (与 implode() 相反)。

php 复制代码
$str = 'PHP,Python,Java,C++';
// 按 "," 分割为数组
$arr1 = explode(',', $str);
print_r($arr1); // 输出:Array ( [0] => PHP [1] => Python [2] => Java [3] => C++ )

// 限制最多3个元素(剩余部分合并为最后一个元素)
$arr2 = explode(',', $str, 3);
print_r($arr2); // 输出:Array ( [0] => PHP [1] => Python [2] => Java,C++ )

// 排除最后1个元素
$arr3 = explode(',', $str, -1);
print_r($arr3); // 输出:Array ( [0] => PHP [1] => Python [2] => Java )

数组拼接为字符串 implode 将数组元素拼接成一个字符串 (与 explode() 相反,效率高于 join(),两者功能一致)。

php 复制代码
$arr = ['PHP', 'Python', 'Java'];

// 用 "," 连接数组元素
$str1 = implode(',', $arr);
echo $str1; // 输出:PHP,Python,Java

// 用 "|" 连接数组元素
$str2 = implode('|', $arr);
echo $str2; // 输出:PHP|Python|Java

// 无连接符拼接
$str3 = implode('', $arr);
echo $str3; // 输出:PHPPythonJava

5.4 数组相关函数

数组的键/值操作函数

array_keys 返回数组中所有的键名 , 返回包含所有键名的索引数组

php 复制代码
$array = ['name' => '张三', 'age' => 25, 'gender' => '男'];
print_r(array_keys($array));
// 输出:Array ( [0] => name [1] => age [2] => gender )

// 只返回值为25的键名
print_r(array_keys($array, 25));
// 输出:Array ( [0] => age )

array_values() 获取数组所有键名, 返回包含所有值的索引数组

php 复制代码
$array = ['name' => '张三', 'age' => 25, 'gender' => '男'];
print_r(array_values($array));
// 输出:Array ( [0] => 张三 [1] => 25 [2] => 男 )

array_key_exists() - 检查数组中是否存在指定的键名, 存在返回true,否则返回false

php 复制代码
$array = ['name' => '张三', 'age' => 25];
var_dump(array_key_exists('name', $array)); // bool(true)
var_dump(array_key_exists('gender', $array)); // bool(false)

in_array() - 检查值是否存在

php 复制代码
$os = ["Mac", "NT", "Irix", "Linux"];
if (in_array("Irix", $os)) {
    echo "找到 'Irix'"; // 会输出此内容
}

array_search() - 在数组中搜索给定的值,如果成功则返回首个相应的键名 ,失败返回false

c 复制代码
$array = ['name' => '张三', 'age' => 25, 'gender' => '男'];
echo array_search('张三', $array); // 输出:name
var_dump(array_search('女', $array)); // bool(false)

array_flip() 交换数组中的键名和对应的值,返回交换后的数组;若原数组中有重复的值,后面的值会覆盖前面的值(因为键名必须唯一);若原数组的值不是合法键名(如数组),会产生警告并返回null

php 复制代码
// 正常交换键值
$array = ['name' => '张三', 'age' => 25, 'gender' => '男'];
$flipped = array_flip($array);
print_r($flipped);
// 输出:Array ( [张三] => name [25] => age [男] => gender )

// 处理重复值(后面的值覆盖前面的)
$array = ['a' => 1, 'b' => 2, 'c' => 1];
$flipped = array_flip($array);
print_r($flipped);
// 输出:Array ( [1] => c [2] => b ) (原数组中两个值为1的元素,后面的'c'覆盖了前面的'a')

// 错误示例(值为数组,不是合法键名)
$array = ['a' => [1,2]];
$flipped = array_flip($array);
// 警告:array_flip(): Can only flip STRING and INTEGER values!
var_dump($flipped); // NULL

array_reverse() 返回一个元素顺序相反的数组,可选择是否保留原数组的键名 ,返回反转后的数组

php 复制代码
// 不保留键名(默认)
$array = ['a', 'b', 'c', 'd'];
$reversed = array_reverse($array);
print_r($reversed);
// 输出:Array ( [0] => d [1] => c [2] => b [3] => a ) (索引键被重新分配)

// 保留键名
$array = ['name' => '张三', 'age' => 25, 'gender' => '男'];
$reversed = array_reverse($array, true);
print_r($reversed);
// 输出:Array ( [gender] => 男 [age] => 25 [name] => 张三 ) (原键名被保留)

// 混合键名的情况
$array = [10 => 'a', 20 => 'b', 'c', 'd'];
$reversed = array_reverse($array, true);
print_r($reversed);
// Array ( [22] => d [21] => c [20] => b [10] => a )
// (原数组中'c'的键是20,'d'的键是22,反转后保留这些键名)
统计数组元素的个数和唯一数

count() 计算数组中的单元数目或对象中的属性个数,返回元素的数量 需要注意的是 计数模式,COUNT_NORMAL(默认)不递归计数,COUNT_RECURSIVE递归计数

php 复制代码
$array = ['a', 'b', ['c', 'd']];
echo count($array); // 输出:3
echo count($array, COUNT_RECURSIVE); // 输出:4

array_count_values 统计数组中所有的值出现的次数 返回一个关联数组,键是原数组的值,值是该值出现的次数

php 复制代码
$array = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
print_r(array_count_values($array));
// 输出:Array ( [apple] => 3 [banana] => 2 [orange] => 1 )

array_unique 移除数组中重复的值,返回去除重复值后的数组

php 复制代码
$array = ['apple', 'banana', 'apple', 'orange', 'banana'];
print_r(array_unique($array));
// 输出:Array ( [0] => apple [1] => banana [3] => orange )
回调函数处理数组的函数

array_filter 用回调函数过滤数组中的单元 , 返回过滤后的数组

php 复制代码
// 过滤出偶数
$array = [1, 2, 3, 4, 5, 6];
$result = array_filter($array, function($value) {
    return $value % 2 == 0;
});
print_r($result); // 输出:Array ( [1] => 2 [3] => 4 [5] => 6 )

array_walk 使用用户自定义函数对数组中的每个元素做回调处理,成功返回true,失败返回false

php 复制代码
$array = ['name' => '张三', 'age' => 25];
array_walk($array, function(&$value, $key) {
    if ($key == 'age') {
        $value = $value + 1; // 年龄加1
    }
});
print_r($array); // 输出:Array ( [name] => 张三 [age] => 26 )

array_map 为数组的每个元素应用回调函数,返回处理后的新数组

php 复制代码
// 将数组中每个元素乘以2
$array = [1, 2, 3, 4, 5];
$result = array_map(function($value) {
    return $value * 2;
}, $array);
print_r($result); // 输出:Array ( [0] => 2 [1] => 4 [2] => 6 [3] => 8 [4] => 10 )

array_reduce 用回调函数迭代地将数组简化为单一的值,返回简化后的结果

php 复制代码
// 计算数组元素的总和
$array = [1, 2, 3, 4, 5];
$sum = array_reduce($array, function($carry, $item) {
    return $carry + $item;
}, 0);
echo $sum; // 输出:15
根据数组的值对数组排序

sort 和 rsort 是对数组的值进行排列 第一个参数是必需的,指定需要排序的数组。后一个参数是可选的,给出了排序的方式,可以用以 下值改变排序的行为。

sort 按由小到大的升序对给定数组的值排序,成功返回true,失败返回false

php 复制代码
$array = [3, 1, 4, 2];
sort($array);
print_r($array); // 输出:Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 )

rsort 对数组的元素按照键值进行由大到小的逆向排序,成功返回true,失败返回false

php 复制代码
$array = [3, 1, 4, 2];
rsort($array);
print_r($array); // 输出:Array ( [0] => 4 [1] => 3 [2] => 2 [3] => 1 )

注意,下面是关于sort和rsort的第二个参数解释:

  • SORT_REGULAR : 是默认值,将自动识别数组元素的类型进行排序。
  • SORT_NUMERIC : 用于数字元素的排序。
  • SORT_STRING : 用于字符串元素的排序。
  • SORT_LOCALE_STRING : 根据当前的locale设置来把元素当做字符串比较。

asort 和 arsort 如果按值从大到小排序,可以使用 arsort 函数将保留原有键名和值的关系。

php 复制代码
$array = ['b' => 3, 'a' => 1, 'd' => 4, 'c' => 2];
sort($array);
print_r($array); //输出 Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 )

$array2 = ['b' => 3, 'a' => 1, 'd' => 4, 'c' => 2];
asort($array);
print_r($array);//输出 Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 )

arsort($array2);
print_r($array2);//输出 Array ( [d] => 4 [b] => 3 [c] => 2 [a] => 1 )
根据键名对数组排序

ksort 和 krsort 根据键名对数组排序 ksort 函数和krsort 函数实现了这个功能。ksort 函数按照键名对数组进行由小到大的排序,krsort 函数排序后为数组值保留原来的键。

php 复制代码
$array = ['b' => 3, 'a' => 1, 'd' => 4, 'c' => 2];
ksort($array);
print_r($array); // 输出:Array ( [a] => 1 [b] => 3 [c] => 2 [d] => 4 )

krsort($array);
echo "<br>";
print_r($array); //输出: Array ( [d] => 4 [c] => 2 [b] => 3 [a] => 1 )
自然排序

使用认知而不是使用计算规则,这种特性称为"自然排序"法,即数字从1到9的排序方法,字母从a到z的排序方法,短者优先。当创建模糊逻辑应用软件时这种排序方式非常有用。

natsort:按照 "自然排序" 算法对数组排序,区分大小写,且保持原数组的键值关联(不重置索引)。

php 复制代码
$files = [
    'file10.txt',
    'file2.txt',
    'file1.txt',
    'File5.txt' // 注意大写F,会影响排序位置
];

// 自然排序(区分大小写)
natsort($files);

print_r($files);
// 输出:
// Array (
//   [2] => file1.txt
//   [1] => file2.txt
//   [3] => File5.txt  // 大写字母ASCII值小于小写,排在前面
//   [0] => file10.txt
// )

natcasesort 功能与natsort相同(自然排序),但不区分大小写,同样保留键名关联。

php 复制代码
$files = [
    'file10.txt',
    'file2.txt',
    'file1.txt',
    'File5.txt' // 大写F不影响排序
];

// 自然排序(不区分大小写)
natcasesort($files);

print_r($files);
// 输出:
// Array (
//   [2] => file1.txt
//   [1] => file2.txt
//   [3] => File5.txt  // 忽略大小写,按数字顺序排在正确位置
//   [0] => file10.txt
// )

array_multisort对多个数组或多维数组进行排序,可指定排序顺序、排序类型(如数字 / 字符串),常用于关联数组的多字段排序。

支持按 "主数组" 排序,其他数组会跟随主数组的顺序调整(类似数据库的多字段排序)。

示例 1:排序多个关联数组

php 复制代码
// 三个关联数组(学生的姓名、年龄、成绩)
$names = ['Bob', 'Alice', 'David'];
$ages = [20, 19, 21];
$scores = [85, 92, 78];

// 按成绩降序排序(主数组为$scores),其他数组跟随调整
array_multisort($scores, SORT_DESC, $names, $ages);

print_r($scores); // [92, 85, 78]
print_r($names);  // ['Alice', 'Bob', 'David']
print_r($ages);   // [19, 20, 21]

示例 2:排序多维数组

php 复制代码
$students = [
    ['name' => 'Bob', 'age' => 20],
    ['name' => 'Alice', 'age' => 19],
    ['name' => 'David', 'age' => 21]
];

// 提取age列作为排序依据
$ages = array_column($students, 'age');

// 按年龄升序排序多维数组
array_multisort($ages, SORT_ASC, $students);

print_r($students);
// 输出(按age从小到大):
// Array (
//   [0] => Array ( [name] => Alice [age] => 19 )
//   [1] => Array ( [name] => Bob [age] => 20 )
//   [2] => Array ( [name] => David [age] => 21 )
// )
用户自定义排序

usort 使用用户自定义的比较函数 对数组的 进行排序。排序后会破坏原数组的键值关联,键会被重新索引为从 0 开始的数字。

php 复制代码
// 待排序的数组(值为关联数组)
$students = [
    ['name' => '张三', 'age' => 22],
    ['name' => '李四', 'age' => 18],
    ['name' => '王五', 'age' => 20]
];

// 自定义比较函数:按年龄升序排序
function compareAge($a, $b) {
    return $a['age'] - $b['age'];
}

// 使用usort排序
usort($students, 'compareAge');

// 输出结果(键已被重置为0、1、2)
print_r($students); 

uasort使用用户自定义的比较函数 对数组的 进行排序,且保持原数组的键值关联(不会重置键), 对关联数组按值的大小排序(保留原键)

php 复制代码
// 待排序的关联数组(键为姓名,值为分数)
$scores = [
    '张三' => 85,
    '李四' => 92,
    '王五' => 78
];

// 自定义比较函数:按分数升序排序
function compareScore($a, $b) {
    return $a - $b;
}

// 使用uasort排序(保留原键)
uasort($scores, 'compareScore');

// 输出结果(键仍为原姓名)
print_r($scores);

uksort使用用户自定义的比较函数 对数组的 进行排序,且保持键与值的关联(按键排序后,值跟随键移动),对数组按键的长度从小到大排序

php 复制代码
// 待排序的数组(键为字符串,值为任意内容)
$fruits = [
    'apple' => '苹果',
    'banana' => '香蕉',
    'pear' => '梨'
];

// 自定义比较函数:按键的长度升序排序
function compareKeyLength($a, $b) {
    return strlen($a) - strlen($b);
}

// 使用uksort排序(按键的长度排序,值跟随键)
uksort($fruits, 'compareKeyLength');

// 输出结果(键按长度排序:pear(4) < apple(5) < banana(6))
print_r($fruits);
/*
输出:
Array
(
    [pear] => 梨
    [apple] => 苹果
    [banana] => 香蕉
)
*/
拆分、合并、分解和结合

array_slice 从数组中取出一段,返回截取后的子数组。

php 复制代码
$array = ['a', 'b', 'c', 'd', 'e'];
print_r(array_slice($array, 1, 3)); // 输出:Array ( [0] => b [1] => c [2] => d )
print_r(array_slice($array, -3, 2)); // 输出:Array ( [0] => c [1] => d )

array_splice 选择数组中的一系列元素,删除它们并用其他值代替。如果提供了第四个参数,则之前选中的那些元素将被第四个参数指定的数组取代。最后生成的数组将会返回。

php 复制代码
$array = ['a', 'b', 'c', 'd', 'e'];
$removed = array_splice($array, 1, 2, ['x', 'y']);
print_r($removed); // 输出:Array ( [0] => b [1] => c )
print_r($array);   // 输出:Array ( [0] => a [1] => x [2] => y [3] => d [4] => e )

array_combine 创建一个数组,用一个数组的值作为其键名,另一个数组的值作为其值,返回合并后的数组,如果两个数组的元素数量不同则返回false

php 复制代码
$keys = ['name', 'age', 'gender'];
$values = ['张三', 25, '男'];
$array = array_combine($keys, $values);
print_r($array);
// 输出:Array ( [name] => 张三 [age] => 25 [gender] => 男 )

array_merge 合并数组

索引数组的数字键名在合并时会被视为 "位置标识",而非唯一标识。即使原数组有相同的数字键名,合并后也会按顺序保留所有元素,并重新从 0 开始分配索引。

php 复制代码
$arr1 = [0 => 'a', 1 => 'b']; // 索引数组(数字键名)
$arr2 = [1 => 'c', 2 => 'd']; // 与 arr1 有相同数字键名(1)

$result = array_merge($arr1, $arr2);
print_r($result);

/*Array
(
    [0] => a  // 来自 arr1[0]
    [1] => b  // 来自 arr1[1]
    [2] => c  // 来自 arr2[1](原键名 1 被重新分配为 2)
    [3] => d  // 来自 arr2[2](原键名 2 被重新分配为 3)
)*/

关联数组的字符串键名被视为 "唯一标识",若多个数组中存在相同的字符串键名,后面数组的键值会覆盖前面的

php 复制代码
$arr1 = ['name' => '张三', 'age' => 25]; // 关联数组(字符串键名)
$arr2 = ['age' => 30, 'gender' => '男']; // 与 arr1 有相同键名 'age'

$result = array_merge($arr1, $arr2);
print_r($result);
/*
Array
(
    [name] => 张三    // 来自 arr1(唯一键名,保留)
    [age] => 30       // 来自 arr2(覆盖 arr1 的 'age' => 25)
    [gender] => 男    // 来自 arr2(唯一键名,保留)
)*/

若数组中同时存在数字键和字符串键,合并时会分别应用上述规则:数字键追加并重新索引,字符串键重复则覆盖。

php 复制代码
$arr1 = [0 => 'a', 'name' => '张三'];
$arr2 = [0 => 'b', 'name' => '李四', 1 => 'c'];

$result = array_merge($arr1, $arr2);
print_r($result);

/*
Array
(
    [0] => a          // 来自 arr1[0](数字键,保留)
    [name] => 李四    // 来自 arr2(覆盖 arr1 的 'name')
    [1] => b          // 来自 arr2[0](数字键,追加并重新索引为 1)
    [2] => c          // 来自 arr2[1](数字键,追加并重新索引为 2)
)*/

array_merge() 只关注键名 ,不关心是否相同。即使多个元素的值完全一样,只要键名不同(或在索引数组中位置不同),都会被保留在合并结果中。

php 复制代码
$arr1 = ['a', 'b', 'c']; // 值为 'a'、'b'、'c'
$arr2 = ['a', 'd', 'b']; // 存在与 arr1 相同的值('a'、'b')

$result = array_merge($arr1, $arr2);
print_r($result);
/*
Array
(
    [0] => a  // 来自 arr1[0]
    [1] => b  // 来自 arr1[1]
    [2] => c  // 来自 arr1[2]
    [3] => a  // 来自 arr2[0](值与 arr1[0] 相同,但保留)
    [4] => d  // 来自 arr2[1]
    [5] => b  // 来自 arr2[2](值与 arr1[1] 相同,但保留)
)*/

如果 array_merge() 只接收一个索引数组参数,它会返回一个重新索引的数组(相当于重置索引)。

php 复制代码
$arr = [3 => 'a', 5 => 'b', 2 => 'c']; // 索引不连续的数组
$result = array_merge($arr);
print_r($result);
/*
Array
(
    [0] => a
    [1] => b
    [2] => c
)*/
交集和差集

array_diff():仅比较 "值" 的差集,比较多个数组的,返回在第一个数组中存在,但在其他所有数组中都不存在的值(不比较键名,只关注值)。

php 复制代码
$arr1 = ['a', 'b', 'c', 'd'];
$arr2 = ['c', 'd', 'e'];
$arr3 = ['b', 'f'];

// 计算 $arr1 中存在,且不在 $arr2 和 $arr3 中的值
$result = array_diff($arr1, $arr2, $arr3);
print_r($result);

/*Array
(
    [0] => a  // 仅在 $arr1 中存在
)*/

array_diff_assoc():比较 "键 + 值" 的差集 同时比较数组的键名和值,只有当 "键名和值都完全匹配" 时,才认为元素相同。返回在第一个数组中存在,且在后续数组中没有完全相同(键 + 值)的元素。返回差集数组,保留原键名。

php 复制代码
$arr1 = ['name' => '张三', 'age' => 25, 'gender' => '男'];
$arr2 = ['name' => '张三', 'age' => 30];
$arr3 = ['gender' => '女'];

// 比较键+值,返回 $arr1 中独有的(键+值)
$result = array_diff_assoc($arr1, $arr2, $arr3);
print_r($result);

/*Array
(
    [age] => 25        // $arr1 的 'age' 是 25,$arr2 的 'age' 是 30(键同值不同,保留)
    [gender] => 男     // $arr3 的 'gender' 是 女(键同值不同,保留)
)*/
# 关键:必须 "键名相同且值相同" 才视为重复,否则保留。

array_diff_key():仅比较 "键名" 的差集,仅比较数组的键名,忽略值。返回在第一个数组中存在,而在后续数组中不存在的键名对应的元素(无论值是否相同)。

php 复制代码
$arr1 = ['a' => 1, 'b' => 2, 'c' => 3];
$arr2 = ['b' => 10, 'd' => 20];
$arr3 = ['c' => 30];

// 比较键名,返回 $arr1 中独有的键名对应的元素
$result = array_diff_key($arr1, $arr2, $arr3);
print_r($result);
/*Array
(
    [a] => 1  // 键 'a' 仅在 $arr1 中存在(无论值如何)
)*/
# 关键:只看键名是否存在,不关心值。即使值相同,只要键名在其他数组中存在,就会被排除。

array_intersect : 计算多个数组的交集,返回所有数组中都存在的元素(仅基于值的比较)。

php 复制代码
$arr1 = ['a', 'b', 'c', 'd'];
$arr2 = ['c', 'd', 'e', 'f'];

// 计算交集:返回在 $arr1 和 $arr2 中都存在的值
$result = array_intersect($arr1, $arr2);
print_r($result);

/*Array
(
    [2] => c  // 在两个数组中都存在
    [3] => d  // 在两个数组中都存在
)*/

$arr1 = ['apple', 'banana', 'cherry', 'date'];
$arr2 = ['banana', 'cherry', 'date', 'elderberry'];
$arr3 = ['cherry', 'date', 'fig', 'grape'];

// 计算三个数组的交集:仅保留在三个数组中都存在的值
$result = array_intersect($arr1, $arr2, $arr3);
print_r($result);
/*
Array
(
    [2] => cherry  // 在三个数组中都存在
    [3] => date    // 在三个数组中都存在
)*/

array_intersect() 只关注元素的值,不关心键名。即使键名不同,只要值相同,就会被视为交集元素。

php 复制代码
$arr1 = ['name' => '张三', 'age' => 25, 'city' => '北京'];
$arr2 = ['user' => '张三', 'age' => 30, 'location' => '北京'];

// 比较值,忽略键名
$result = array_intersect($arr1, $arr2);
print_r($result);

/*Array
(
    [name] => 张三  // 虽然键名不同('name' vs 'user'),但值相同
    [city] => 北京  // 键名不同('city' vs 'location'),但值相同
)*/

比较时使用 == 而非 ===,即类型不同但值相等的元素会被视为相同(例如 1"1"true1)。

ini 复制代码
$arr1 = [1, 2, '3', true];
$arr2 = ['1', 2, 3, 1];

// 松散比较:1 == '1','3' == 3,true == 1
$result = array_intersect($arr1, $arr2);
print_r($result);

/*Array
(
    [0] => 1      // 1 == '1'($arr2 中有 '1')
    [1] => 2      // 2 == 2($arr2 中有 2)
    [2] => 3      // '3' == 3($arr2 中有 3)
    [3] => 1      // true == 1($arr2 中有 1)
)*/

array_intersect_assoc() 同时比较 "键名 + 值" 的交集,只有当元素的键名和值在所有数组中都完全相同时,才会被保留在结果中。

php 复制代码
$arr1 = [
    'name' => '张三',
    'age' => 25,
    'city' => '北京',
    'gender' => '男'
];

$arr2 = [
    'name' => '张三',  // 键和值都与 $arr1 相同
    'age' => 30,       // 键相同,值不同
    'city' => '北京',  // 键和值都与 $arr1 相同
    'hobby' => '读书'  // 键在 $arr1 中不存在
];

$arr3 = [
    'name' => '张三',  // 键和值都与 $arr1 相同
    'age' => 25,       // 键和值都与 $arr1 相同(但 $arr2 中值不同)
    'city' => '上海',  // 键相同,值不同
    'gender' => '男'   // 键和值都与 $arr1 相同
];

// 计算三个数组的交集(键+值都必须相同)
$result = array_intersect_assoc($arr1, $arr2, $arr3);
print_r($result);

/*Array
(
    [name] => 张三  // 仅 'name' 在三个数组中键和值完全相同
)*/

array_intersect_key():仅比较 "键名" 的交集,只要元素的键名在所有数组中都存在(无论值是否相同),就会被保留(值取自第一个数组)。

php 复制代码
$arr1 = [
    'a' => 10,
    'b' => 20,
    'c' => 30,
    'd' => 40
];
$arr2 = [
    'a' => 100,  // 键存在,值不同
    'b' => 200,  // 键存在,值不同
    'e' => 500   // 键在 $arr1 中不存在
];
$arr3 = [
    'a' => 1000, // 键存在,值不同
    'b' => 2000, // 键存在,值不同
    'c' => 3000, // 键存在,值不同
    'f' => 6000  // 键在 $arr1 中不存在
];

// 计算三个数组的交集(仅键名必须在所有数组中存在)
$result = array_intersect_key($arr1, $arr2, $arr3);
print_r($result);
/*Array
(
    [a] => 10  // 键 'a' 在三个数组中都存在(值取自 $arr1)
    [b] => 20  // 键 'b' 在三个数组中都存在(值取自 $arr1)
)*/
使用数组实现堆栈

array_push 将一个或多个单元压入数组的末尾(入栈),返回操作后数组的元素总数。

php 复制代码
$stack = ['a', 'b'];
$count = array_push($stack, 'c', 'd');
echo $count; // 输出:4
print_r($stack); // 输出:Array ( [0] => a [1] => b [2] => c [3] => d )

array_pop 弹出数组最后一个单元(出栈),返回弹出的元素,若数组为空则返回null

ini 复制代码
$stack = ['a', 'b', 'c', 'd'];
$last = array_pop($stack);
echo $last; // 输出:d
print_r($stack); // 输出:Array ( [0] => a [1] => b [2] => c )

array_shift 将数组开头的单元移出数组(出队),返回移出的元素,若数组为空则返回null

php 复制代码
$queue = ['a', 'b', 'c', 'd'];
$first = array_shift($queue);
echo $first; // 输出:a
print_r($queue); // 输出:Array ( [0] => b [1] => c [2] => d )

array_unshift 在数组开头插入一个或多个单元(入队),返回操作后数组的元素总数。

php 复制代码
$queue = ['c', 'd'];
$count = array_unshift($queue, 'a', 'b');
echo $count; // 输出:4
print_r($queue); // 输出:Array ( [0] => a [1] => b [2] => c [3] => d )
其他函数

array_rand 从数组中随机获取一个或多个键名。

php 复制代码
// 基础用法:随机获取1个键名
$fruits = ['apple', 'banana', 'cherry', 'date'];
$randomKey = array_rand($fruits);
echo "随机键名:$randomKey,对应值:{$fruits[$randomKey]}\n";
// 可能输出:随机键名:2,对应值:cherry(结果随机)

// 获取多个键名(如2个)
$randomKeys = array_rand($fruits, 2);
echo "随机2个键名:";
print_r($randomKeys);
// 可能输出:随机2个键名:Array ( [0] => 0 [1] => 3 )(结果随机)

// 关联数组示例
$user = ['name' => '张三', 'age' => 25, 'city' => '北京'];
$randomUserKey = array_rand($user);
echo "随机用户信息键名:$randomUserKey,值:{$user[$randomUserKey]}\n";
// 可能输出:随机用户信息键名:city,值:北京(结果随机)

//注意:返回的是 "键名" 而非 "值",需通过键名获取对应值;随机结果每次运行可能不同。

shuffle() 打乱数组元素顺序,成功返回 true,失败返回 false

php 复制代码
// 索引数组打乱
$numbers = [1, 2, 3, 4, 5];
shuffle($numbers);
echo "打乱后的索引数组:";
print_r($numbers);
// 可能输出:Array ( [0] => 3 [1] => 1 [2] => 5 [3] => 2 [4] => 4 )(顺序随机)

// 关联数组打乱(键名会被重置为数字)
$colors = ['red' => '红色', 'green' => '绿色', 'blue' => '蓝色'];
shuffle($colors);
echo "打乱后的关联数组(键名被重置):";
print_r($colors);
// 可能输出:Array ( [0] => 绿色 [1] => 红色 [2] => 蓝色 )(顺序随机,原字符串键名丢失)

array_sum 计算数组中所有数值类型元素的总和,非数值类型(如字符串、布尔值)会被自动转换为数值(转换失败则视为 0),所有元素的总和(整数或浮点数)。

php 复制代码
// 纯数字数组
$nums = [10, 20, 30, 40];
echo "总和:" . array_sum($nums); // 输出:100

// 包含浮点数和字符串数字
$mixed = [1.5, '2.5', 3, '4']; // '2.5' 和 '4' 会被转为数值
echo "\n混合数值总和:" . array_sum($mixed); // 输出:11(1.5+2.5+3+4)

// 包含非数值元素(自动转为0)
$withNonNum = [5, true, 'abc', null]; // true=1,'abc'=0,null=0
echo "\n含非数值的总和:" . array_sum($withNonNum); // 输出:6(5+1+0+0)

// 空数组
$empty = [];
echo "\n空数组总和:" . array_sum($empty); // 输出:0

//注意:字符串若无法转为数值(如 'abc')会被视为 0;布尔值 true=1,false=0。

range 创建一个包含指定范围元素的数组,返回包含范围元素的索引数组。

php 复制代码
// 数字范围(默认步长1)
$numRange = range(1, 5);
print_r($numRange); // 输出:Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 )

// 带步长的数字范围(步长2)
$stepRange = range(0, 10, 2);
print_r($stepRange); // 输出:Array ( [0] => 0 [1] => 2 [2] => 4 [3] => 6 [4] => 8 [5] => 10 )

// 字母范围(按ASCII码递增)
$letterRange = range('a', 'e');
print_r($letterRange); // 输出:Array ( [0] => a [1] => b [2] => c [3] => d [4] => e )

// 倒序范围(步长为负数)
$reverseRange = range(5, 1, -1);
print_r($reverseRange); // 输出:Array ( [0] => 5 [1] => 4 [2] => 3 [3] => 2 [4] => 1 )

五、正则表达式考点

正则表达式(Regular Expression,简称 regex)是一种用于匹配、查找和替换字符串模式的工具。在 PHP 中,正则表达式通过 PCRE 库(Perl Compatible Regular Expressions,兼容 Perl 的正则库) 实现,核心是一系列以 preg_ 开头的函数。

PHP 提供了多个用于处理正则的函数,覆盖「匹配」「替换」「分割」「过滤」等场景,以下是最常用的 5 个函数:

1、常用函数使用

preg_match 执行一个正则表达式匹配,只匹配一次,成功匹配返回 1,未匹配返回 0,错误返回 FALSE。

php 复制代码
$pattern = '/\bapple\b/';
$subject = 'I like apple and banana.';
$result = preg_match($pattern, $subject, $matches);

if ($result === 1) {
    echo "匹配成功:" . $matches[0]; // 输出:匹配成功:apple
} elseif ($result === 0) {
    echo "未找到匹配";
} else {
    echo "匹配过程中发生错误";
}

preg_match_all 执行一个全局正则表达式匹配,匹配所有符合条件的结果,成功返回完整匹配的次数,未匹配返回 0,错误返回 FALSE。

php 复制代码
$pattern = '/\b\w+berry\b/';
$subject = 'I like strawberry, blueberry and raspberry.';
$result = preg_match_all($pattern, $subject, $matches);

echo "找到 $result 个匹配:";
print_r($matches[0]);
// 输出:
// 找到 3 个匹配:
// Array ( [0] => strawberry [1] => blueberry [2] => raspberry )

preg_replace 执行一个正则表达式的搜索和替换,成功返回替换后的字符串或数组,发生错误时返回 NULL。

php 复制代码
$pattern = '/\bcat\b/';
$replacement = 'dog';
$subject = 'I have a cat. The cat is black.';
$result = preg_replace($pattern, $replacement, $subject, -1, $count);

echo "替换后的字符串:$result\n";
echo "替换次数:$count";
// 输出:
// 替换后的字符串:I have a dog. The dog is black.
// 替换次数:2

preg_replace_callback 用回调函数执行正则表达式的搜索和替换,成功返回替换后的字符串或数组,发生错误时返回 NULL。

php 复制代码
$pattern = '/\b(\d+)\b/';
$subject = 'Numbers: 10, 20, 30';

// 回调函数:将数字乘以2
$callback = function($matches) {
    return $matches[1] * 2;
};

$result = preg_replace_callback($pattern, $callback, $subject, -1, $count);

echo "替换后的字符串:$result\n";
echo "替换次数:$count";
// 输出:
// 替换后的字符串:Numbers: 20, 40, 60
// 替换次数:3

preg_split 用正则表达式分割字符串,成功返回一个数组,包含分割后的子串,失败返回 FALSE。

php 复制代码
$pattern = '/[\s,;]+/'; // 匹配空格、逗号、分号
$subject = 'apple, banana; orange grape';
$result = preg_split($pattern, $subject);

print_r($result);
// 输出:
// Array ( [0] => apple [1] => banana [2] => orange [3] => grape )

preg_grep 返回匹配模式的数组条目,返回一个数组,包含与模式匹配的元素。

php 复制代码
$pattern = '/^[A-Z]/'; // 匹配以大写字母开头的元素
$input = ['Apple', 'banana', 'Cherry', 'date'];
$result = preg_grep($pattern, $input);

print_r($result);
// 输出:
// Array ( [0] => Apple [2] => Cherry )

preg_quote 转义正则表达式字符,返回转义后的字符串。

php 复制代码
$str = 'Hello. How are you? 1+1=2';
$escaped = preg_quote($str);

echo "原始字符串:$str\n";
echo "转义后字符串:$escaped";
// 输出:
// 原始字符串:Hello. How are you? 1+1=2
// 转义后字符串:Hello. How are you? 1+1=2

preg_last_error 返回最后一个 PCRE 正则执行产生的错误代码,返回错误代码,可以与以下常量比较:

  • PREG_NO_ERROR:没有错误
  • PREG_INTERNAL_ERROR:内部错误
  • PREG_BACKTRACK_LIMIT_ERROR:回溯限制错误
  • PREG_RECURSION_LIMIT_ERROR:递归限制错误
  • PREG_BAD_UTF8_ERROR:错误的 UTF-8 数据
  • PREG_BAD_UTF8_OFFSET_ERROR:UTF-8 偏移量错误
  • PREG_JIT_STACKLIMIT_ERROR:JIT 栈限制错误(PHP7+)
php 复制代码
$pattern = '/[a-z'; // 不完整的模式
$subject = 'test';
preg_match($pattern, $subject);

$error = preg_last_error();
switch ($error) {
    case PREG_NO_ERROR:
        echo "没有错误";
        break;
    case PREG_INTERNAL_ERROR:
        echo "内部错误";
        break;
    default:
        echo "其他错误: $error";
}
// 输出:其他错误: 2

2、高级概念

后向引用:后向引用用于重复匹配之前捕获的内容,使用 \数字 表示引用第 n 个捕获组的内容。

php 复制代码
// 匹配重复的单词
$pattern = '/\b(\w+)\s+\1\b/';
$subjects = [
    'Hello hello world',
    'This is is a test',
    'PHP is great'
];

foreach ($subjects as $subject) {
    if (preg_match($pattern, $subject, $matches)) {
        echo "找到重复单词 '$matches[1]' 在: $subject\n";
    } else {
        echo "未找到重复单词在: $subject\n";
    }
}
// 输出:
// 找到重复单词 'Hello' 在: Hello hello world
// 找到重复单词 'is' 在: This is is a test
// 未找到重复单词在: PHP is great

贪婪模式与非贪婪模式

  • 贪婪模式 :默认情况下,量词是贪婪的,会匹配尽可能多的字符(*, +, ?, {n}, {n,}, {n,m}
  • 非贪婪模式 :在量词后添加 ? 可以启用非贪婪模式,匹配尽可能少的字符
php 复制代码
$html = '<div>内容1</div><div>内容2</div>';

// 贪婪模式
$pattern_greedy = '/<div>(.*)</div>/';
preg_match($pattern_greedy, $html, $matches_greedy);
echo "贪婪模式匹配: " . $matches_greedy[1] . "\n";

// 非贪婪模式
$pattern_non_greedy = '/<div>(.*?)</div>/';
preg_match($pattern_non_greedy, $html, $matches_non_greedy);
echo "非贪婪模式匹配: " . $matches_non_greedy[1];

// 输出:
// 贪婪模式匹配: 内容1</div><div>内容2
// 非贪婪模式匹配: 内容1
相关推荐
sunbin2 小时前
软件授权管理系统-整体业务流程图(优化版)
后端
一只专注做软件的湖南人2 小时前
京东商品评论接口(jingdong.ware.comment.get)技术解析:数据拉取与情感分析优化
前端·后端·api
长城20242 小时前
PHP的json_encode()函数了解
php·json_encode函数
TZOF2 小时前
TypeScript的新类型(二):unknown
前端·后端·typescript
xiaoye37082 小时前
Spring Boot 详细介绍
java·spring boot·后端
TZOF2 小时前
TypeScript的新类型(三):never
前端·后端·typescript
我不是混子2 小时前
如何实现数据脱敏?
java·后端
毕业设计制作和分享3 小时前
springboot523基于Spring Boot的大学校园生活信息平台的设计与实现
前端·vue.js·spring boot·后端·生活
花心蝴蝶.3 小时前
JVM 类加载
开发语言·jvm·后端