一、引用变量的考察点
引用变量的本质是多个变量名指向同一内存地址,而非拷贝数据,核心考察点围绕其定义、使用场景及特殊行为展开。
1、基础定义与语法
引用符号 :通过 &
符号声明引用,需区分 "声明引用" 和 "传递引用" 两种场景。
- 声明引用:
$a = 10; $b = &$a;
($a
和$b
指向同一内存,修改$b
会同步改变$a
)。 - 注意:
$b = &$a
后,若再执行$b = 20
,是修改共同指向的内存值,而非改变引用关系。
禁止重复引用 :同一变量不能重复声明为其他变量的引用,否则会触发 Notice
警告(如 $a = &$b; $a = &$c;
不合法)。
2、PHP引用变量的原理
PHP 内部通过两个关键结构实现变量管理:
- 符号表(Symbol Table) :存储变量名及其对应的「指针」(指向内存块的引用)。
- 内存块(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
和$b
的refcount
均为 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 方法(如GET
、POST
、PUT
、DELETE
等)。
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/jpeg
、application/pdf
)。$_FILES['avatar']['type']
可能返回image/png
。 -
tmp_name
:文件上传到服务器后,临时存储的路径(PHP 自动生成)。脚本执行结束后会自动删除,需用move_uploaded_file()
永久保存。 -
size
:上传文件的大小(单位:字节),204800
表示 200KB。 -
error
:文件上传的错误代码(0 表示无错误)0
:上传成功1
:文件大小超过php.ini
中upload_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
:单次请求允许上传的最大文件数量。
安全注意事项
- 永远不要信任客户端数据(
name
、type
可被篡改),必须在服务器端验证。 - 使用
move_uploaded_file()
而非copy()
或rename()
,防止非上传文件被操作。 - 上传目录应设置严格权限(禁止执行脚本,如
chmod 0644
)。 - 重命名上传文件(如用
uniqid()
),避免文件名冲突或恶意文件名(如../evil.php
)。 - 限制允许的文件类型和大小,避免超大文件或恶意脚本上传。
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 的操作系统(如Linux
或WINNT
)PHP_SAPI
:服务器应用程序编程接口(如cli
或fpm-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 的预定义常量本质上是内核在初始化阶段预先注册到常量表中的值,其运行机制可分为以下几个步骤:
-
常量注册阶段 PHP 内核启动时(如
php -f script.php
执行或 FPM 进程初始化),会通过内部函数(如zend_register_standard_constants
)将预定义常量注册到全局常量表(zend_constants
哈希表)中。 例如,PHP_VERSION
的值在编译 PHP 时就已确定,内核会将其字符串值(如"8.2.10"
)与常量名绑定并存储。 -
动态常量的实时计算 对于魔术常量(如
__LINE__
、__FILE__
),其值并非固定,而是在代码编译或执行时动态生成:- 当 PHP 解析器(Zend Engine)扫描代码时,会在遇到魔术常量的位置实时计算其值(如记录当前行号、文件路径)。
- 例如,
__LINE__
的值由解析器在处理代码行时递增计数器获得,并替换为具体数值。
-
常量访问机制 当代码中使用预定义常量时,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 > 2
为true
,根据||
的 "一真则真" 规则,最终结果为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、控制循环流程
break
和 continue
是控制循环流程的核心关键字,但作用和使用场景截然不同。以下从 核心区别 、使用方法 、典型案例 三方面详细整理:
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
)、类 / 接口、array
、callable
等。 - 严格模式:默认宽松模式(自动类型转换),开启
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 Monday
、2024-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 字节)。
获取字符串字节长度 strlen
和 mb_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"
,true
和 1
)。
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