
刚接触PHP的朋友十有八九都是被“世界上最好的语言”这个梗拉进来的。但真打开编辑器准备写代码时面对的基本就是标题里这几个东西基本语法、字符串、运算符、流程控制语句。这四个模块确实是PHP的地基地基打不牢后面学函数、学数组、学数据库操作、学框架全都会觉得别扭。这篇内容我打算换个讲法不整教科书那套“从入门到放弃”的枯燥罗列。我会把PHP基础拆成几条主线带你搞懂PHP脚本到底怎么组织、字符串在PHP里有哪些“玩法”、运算符怎么用才能少踩坑、流程控制怎么写才能逻辑清晰。每个模块我都会结合自己实际写代码时遇到的场景来说包括那些官方文档里不会明说、但你在开发中大概率会碰到的细节。适合刚入门还没建立起PHP手感的新手也适合断断续续学过、想系统把基础补齐的开发者。1. 环境准备与第一个PHP程序1.1 开发环境怎么搭才省心写PHP基础之前得先把代码跑起来。PHP开发环境的花样不少我按自己的使用经验给你分个类。第一类是集成环境代表就是phpStudy、XAMPP、MAMP这类。它们把Apache/Nginx、PHP、MySQL打包在一个面板里点一下就能启动特别适合新手。不过这类工具也有问题就是版本切换有时候不太灵活。我见过不少朋友用phpStudy装好了PHP 5.6后面项目要用PHP 8.0面板里找不到合适的版本折腾半天。所以后来我更推荐的方式是直接去PHP官网下载对应版本的压缩包手动配置环境变量。这样你本机能同时装好几个PHP版本项目需要哪个就用哪个命令行里指定路径就行。第二类是Docker方案。如果你已经接触过Docker用容器跑PHP会非常干净。项目里放一个docker-compose.ymlPHP、Nginx、MySQL三个容器一组新同事拉下来直接就能跑不用再装一堆环境。我这里给一个最简配置参考version: 3 services: php: image: php:8.2-fpm volumes: - ./www:/var/www/html nginx: image: nginx:latest ports: - 8080:80不过对刚学PHP的朋友来说我不建议一上来就碰Docker那又增加了一层学习成本。先用集成环境把你跑通后面有精力了再上容器。第三类也是我想重点提的就是PHP自带的开发服务器。你装好PHP之后进入项目目录执行一条命令php -S localhost:8000浏览器访问http://localhost:8000你的PHP代码就跑起来了。这条命令特别适合基础学习和本地调试不用装Apache、不用配虚拟主机写个测试脚本直接就能看结果。我平时做小实验、测某个函数返回什么值都是用这个方式几秒钟就能验证一个想法。说到Mac用户如果你用的是M4芯片的MacBook装PHP时可能会遇到一个报错类似PHP -v dyld[45472]: library not loaded: loader_path/../../../../opt/libffi/这个问题的原因很直接PHP某个扩展动态库链接不到libffi这个依赖库。大部分情况是因为你用Homebrew装的PHP升级系统或者升级Xcode Command Line Tools之后动态库缓存失效。解决办法也不复杂重新执行一下brew的链接修复命令让依赖重新关联。这些都是环境层面的问题花点时间解决一次后面就顺了。注意PHP内置的开发服务器php -S只能用于本地开发和学习生产环境绝对不能这么用。生产还是要靠Nginx或Apache配合PHP-FPM那才是正规军。1.2 第一个PHP程序怎么写环境准备好之后创建一个index.php文件先写一个最简单的脚本?php echo Hello PHP;你会注意到PHP代码必须放在 这两个标记之间。PHP解释器在执行文件时只把标记内部的内容当作PHP代码解析标记外面的内容会原样输出。这也带来了PHP一个很有特点的写法——在HTML里嵌入PHP。!DOCTYPE html html head title测试页/title /head body h1?php echo 这里是PHP输出的内容; ?/h1 /body /html这种把PHP代码直接嵌在HTML模板里的方式直到今天在很多PHP项目里依然随处可见。理解了这个特性你就明白为什么PHP在Web开发领域一直是“快速出活”的代表。文件名方面PHP文件通常以.php结尾服务器配置好之后访问.php文件就会交给PHP解释器处理。也有少数项目用.phtml或.php5后缀但那些都是历史遗留或特殊框架约定你正常开发用.php就对了。2. 基本语法核心2.1 PHP标签与注释的基础认知PHP标签除了标准的 方便得很。注释这块PHP支持三种风格?php // 单行注释这个最常用 # Shell风格注释也能用但PHP官方推荐用双斜杠 /* 多行注释 适合大段说明 */这里我想多聊两句注释的“度”。我见过两种极端一种是完全不写注释过两周自己看自己写的代码都发懵另一种是每一行都加注释结果代码本体反而被注释淹没。我个人习惯是函数和方法必须写注释说明用途和参数一块容易让人困惑的逻辑必须写注释说明思路其他简单的代码不用硬凑注释。写注释的目的是帮助读代码的人快速理解“为什么这么写”而不是复述“这里写了什么”。2.2 变量、数据类型与作用域PHP的变量有个醒目的标志就是美元符号$。变量名以$开头后面跟字母、数字、下划线且不能以数字开头。比如$name、$_age、$userName都是合法变量名而$1name、$my-name是非法变量名。另外PHP是大小写敏感的$name和$Name是两个完全不同的变量这个细节很坑新手经常在这里翻车。PHP是弱类型语言不需要声明变量的类型一个变量可以随时赋值字符串、数字、数组、对象。但这不代表PHP没有数据类型概念PHP 8里常见的类型还是这几个大类标量类型整型int、浮点型float、字符串string、布尔型bool复合类型数组array、对象object、可调用callable、迭代器iterable特殊类型资源resource、空值NULL搞清楚这些类型你在用var_dump()调试时就不会一脸懵。比如var_dump($var)会输出变量的类型和值这是排查问题最常用的函数之一。变量作用域是初学者的重灾区。PHP的作用域用一句话概括就是函数内部默认访问不到函数外部的变量。看这个例子?php $name 张三; function showName() { echo $name; // 报错$name未定义 } showName();这个代码会报未定义变量的错误因为在函数内部PHP默认不会自动获取全局作用域的变量。解决方案有几个?php // 方案一声明global $name 张三; function showName() { global $name; echo $name; } // 方案二函数参数传入 function showName2($name) { echo $name; } // 方案三使用超全局变量$GLOBALS function showName3() { echo $GLOBALS[name]; }三种方案里我最建议方案二也就是通过参数把值传进去。这样函数的输入输出清晰不依赖外部环境代码可读性和可测试性都更好。global虽然方便但用多了代码会变得难以追踪一个变量从哪里来的都搞不清楚。2.3 常量与魔术常量常量和变量不一样常量一旦定义就不能再改变。定义常量通常有两种方式?php // 方式一define()函数 define(APP_NAME, 我的应用); // 方式二const关键字 const APP_VERSION 1.0.0; // 调用var_dump或echo输出 echo APP_NAME; echo APP_VERSION;两者区别在于const是语言结构定义时必须给一个确定的值define()可以在条件判断里使用甚至可以用变量拼接常量名。常量还有一个特点就是全局生效不需要像变量那样通过global引入。你在一个函数里直接使用常量PHP就能正常识别非常适合存放配置类信息。PHP里还有一组特殊的“魔术常量”它们以双下划线开头和结尾。我挑几个常用的说一下LINE当前代码所在的行号FILE当前文件的完整路径和文件名DIR当前文件所在目录FUNCTION当前函数名CLASS当前类名这些魔术常量在调试和写日志时非常有用。比如你要记录日志想知道日志是哪个文件、哪一行输出的直接拼上__FILE__和__LINE__就行省去手动填写的麻烦。3. 字符串的完整操作指南3.1 字符串定界方式选型PHP里字符串有四种写法但多数人日常只会用到前两种单引号和双引号。它们的区别可以用一句话概括双引号会解析变量和转义字符单引号不会。?php $name 张三; echo 你好$name; // 输出你好张三 echo 你好$name; // 输出你好$name注意看双引号里的$name被解析成了变量值单引号里则是原样输出。所以当你写SQL、写正则表达式时用单引号会更安全因为不需要担心里面的$被当成变量解析。单双引号还有一个性能上的细微差别双引号因为要检查是否包含变量执行速度略慢但这个差异在Web应用中可以忽略不计不用过度优化。除了单双引号还有两种定界方式。heredoc写法用来定义大段的多行字符串?php $html HTML div classbox p这是一段HTML内容/p p当前用户$username/p /div HTML;nowdoc写法只需要把定界符用单引号包裹同样不会解析变量适合放那些不需要变量解析的模板内容。两种写法在生成多行文本时特别顺手比一行行拼接字符窜清爽太多。3.2 常用字符串函数速查PHP的字符串函数有上百个但实际开发中高频使用的就那十几个。我给你整理成一个速查表每个函数配合一个例子方便直接抄。函数作用示例输出结果strlen()获取字节长度strlen(你好)6UTF-8中一个中文占3字节mb_strlen()获取字符长度mb_strlen(你好)2strpos()查找子串首次出现位置strpos(hello world, world)6substr()截取子串substr(hello, 1, 3)ellstr_replace()字符串替换str_replace(a, b, banana)bbnbnbtrim()去除首尾空白trim( hi )hiexplode()按分隔符拆成数组explode(,, a,b,c)[a,b,c]implode()数组元素拼成字符串implode(-, [a,b])a-bstrtolower()转小写strtolower(PHP)phpstrtoupper()转大写strtoupper(php)PHPucfirst()首字母大写ucfirst(hello)Hellostrrev()字符串反转strrev(abc)cba这里必须强调一个经典坑在UTF-8编码下strlen()统计的是字节数而不是字符数。一个中文在UTF-8里占3个字节所以strlen(你好)的结果是6。要统计字符数必须用mb_strlen()。这也是为什么PHP中文技术圈一直在强调mbstring扩展的重要性。做用户输入长度校验时如果校验中文昵称长度用了strlen会把3字节当成3个字符来处理校验结果自然不对。跟字符串截取相关的还有另一个函数mb_substr()。substr()在处理中文时同样会按字节截取一个不小心就把中文字符截成乱码。所以只要文本可能包含中文就推荐使用mb_substr()。3.3 字符串拼接与类型转换的常见坑PHP的字符串拼接用点号.这个跟JavaScript的加号拼接完全不同。新手如果习惯了JavaScript的写法在PHP里用加号拼字符串会得到意想不到的结果?php $num 5; echo 共 . $num . 条 // 正确共5条 echo 共 $num 条 // 错误结果只是共本身其实是050为什么加号会输出5因为PHP会把加号两边的字符串尝试转成数字共转成数字是0条转成数字也是0所以整个表达式变成0505。这背后的机制就是PHP的“类型转换魔法”理解它你就理解了字符串转数字的坑。在PHP中当一个字符串和一个数字做算术运算时字符串会尽力转成数字。转的规则是从字符串开头取连续的数字部分如果开头不是数字整个值就是0。举例说明?php echo 12abc 3; // 1512abc转成12 echo abc12 3; // 3abc12转成0 echo 12.5px 1; // 13.5这个规则在做表单数据处理时非常危险。比如你接到一个用户传过来的字符串200元想把它转成数字直接转出来的结果是200没问题。但如果用户传的是大概200元转出来就是0。所以严谨的做法是先提取数字部分或者用类型转换函数明确处理。常用转换写法?php $str 123.45; $num (float)$str; // 强制类型转换 $num2 floatval($str); // 使用floatval函数 $num3 intval(123abc); // 输出123有必要提一下PHP和Java在MD5处理上的差异。很多项目做接口对接时PHP算出来的MD5和Java算出来的总对不上。这不是MD5算法的问题而是两边的输入编码不一样。PHP的md5()函数接收的是字符串如果你的字符串里包含中文PHP直接按内部编码计算而Java端如果先对字符串做了getBytes()但没有指定UTF-8就会用平台默认编码两边编码不一致MD5值自然不同。解决办法是确保两边都用UTF-8编码的字节序列去计算。4. 运算符体系详解4.1 算术运算符与赋值运算符算术运算符就是加减乘除那套。PHP里的运算符跟数学里的符号基本一致但有几个特别的地方。?php $a 10; $b 3; echo $a $b; // 13 echo $a - $b; // 7 echo $a * $b; // 30 echo $a / $b; // 3.3333333333333 echo $a % $b; // 1取余数 echo $a ** $b; // 1000PHP 5.6起支持的幂运算取余运算%常用于判断奇偶、分页计算、多维数组轮询等场景。比如要判断一个数是奇数还是偶数$num % 2 0就是偶数否则是奇数。这个写法比用函数简单直接。赋值运算符除了最基础的还有配合算术运算符的组合赋值。$a 5相当于$a $a 5$a . xx相当于$a $a . xx。组合赋值的写法更简洁也更直观。我在写循环累加时基本都这么写?php $total 0; for ($i 1; $i 100; $i) { $total $i; }4.2 比较运算符比较运算符是最容易踩坑的地方。基本的等于号、不等于号PHP里有两套宽松比较和严格比较。?php $a 0; $b 0; var_dump($a $b); // true宽松比较自动转换类型 var_dump($a $b); // false严格比较类型不同直接false $c abc; var_dump($c 0); // true又一个经典坑abc转数字等于0这个坑在实战里非常常见。比如你从表单接收到一个字符串0和判断一个数字0是否相等用是成立的但你从数据库查出来一个值是abc跟0用比较居然也是成立的这就直接导致逻辑错误。所以我的习惯是能用就不用类型不确定的地方一律用严格比较。同样的道理比较两个值是否不相等用!而不是!。比较运算符里还有一组比较大小这点和别的语言差不多。但PHP 8.0开始非数字字符串之间的比较规则变了两个字符串之间用和比较时默认按字典顺序比较而不再是之前那种隐式数字转换。如果你项目从PHP 7升级到PHP 8这块必须留意。4.3 逻辑运算符与三目运算符逻辑运算符主要就是四个与and或、或or或||、非!、异或xor。日常写判断条件最常见的就是和||。它们的运算规则是两边都为真结果才为真||只要一边为真结果就为真。逻辑运算符有个特性叫“短路求值”这是面试里常考的点。所谓短路就是第一个条件已经能决定整个表达式的值时后面的条件不会执行。看个例子?php $a 0; $b 5; var_dump($a $b); // false$b不会执行$b还是5 $c 1; var_dump($c || $b--); // true$b--不会执行短路特性在你写条件判断时可以帮助避免一些错误。比如要判断一个变量存在且长度大于3先用isset()判断慢变量是否存在再用strlen()判断长度。如果变量不存在strlen()就会报错但通过短路机制第一个条件为false后第二个条件不会执行就不会报错。这种写法比嵌套if要优雅。三目运算符是if/else的简化写法?php $age 20; $tip $age 18 ? 成年人 : 未成年人;语法是“条件 ? 值为真时取这个 : 值为假时取这个”。三目运算符在数组取值、默认值设置时特别方便。PHP 7还提供了一种更简洁的空合并运算符???php $username $_GET[username] ?? 游客;这行代码的意思是如果$_GET[username]存在且不为null就取这个值否则用游客。相比传统的isset判断这种写法简洁太多也避免了直接用$_GET[username]时因键不存在而报的未定义索引警告。4.4 错误抑制符与其他特殊运算符PHP有一个独特的错误抑制符把它放在表达式前面可以抑制这个表达式产生的错误信息。比如读一个不存在的文件?php $content file_get_contents(not_exist.txt);加上之后即使文件不存在也不会输出警告信息。但我必须劝你一句真的不能随便用。它会把错误信息全部吞掉导致问题发生时你完全不知道哪里出错排查问题的难度直线上升。我见过一些老代码满屏都是出问题时只能一行行注释去掉找原因效率极低。正确的做法是让错误暴露出来用合理的错误处理机制去应对而不是一味抑制。另外还有一个反引号运算符能让PHP执行系统命令?php $output ls -la;这个功能有自己的使用场景但同时也是安全风险高发区。如果你要执行系统命令命令里拼接了用户输入的内容攻击者就能通过注入特殊字符执行任意命令这就是命令注入漏洞。所以这个反引号我建议能不用就不用真要执行系统命令也要对参数做严格的过滤和校验。4.5 运算符优先级速查运算符优先级是个容易出乱子的地方。先看一个经典问题?php $a true || false false; var_dump($a); // true $b true || (false false); var_dump($b); // true因为的优先级高于||所以第一个表达式相当于true || (false false)结果就是true。类似这种因为优先级导致的逻辑误解在团队协作里经常引发争执。我整理了一份高频使用的运算符优先级表从高到低排列方便你对照优先级运算符说明高括号()永远优先高 --自增自减高!逻辑非高* / %算术乘除取余高 - .算术加减、字符串拼接中 比较中 ! !相等比较中逻辑与低||逻辑或更低? :三目运算符最低 - .赋值我的建议是如果表达式里有多层逻辑组合别依赖记忆优先级直接加括号。括号不仅让代码逻辑清晰还能防止其他人改代码时理解错。写清晰代码比写“看起来很高级”的代码更重要。5. 流程控制语句详解5.1 条件分支if/elseif/else与switch流程控制是程序逻辑的灵魂。PHP的条件分支和其他语言大同小异if后面接布尔表达式表达式为真时执行对应代码块。?php $score 85; if ($score 90) { echo 优秀; } elseif ($score 60) { echo 及格; } else { echo 不及格; }这里几个细节要注意elseif是一个单词PHP不支持else if分开写虽然写else if也不会报错但业界统一用elseif。另外if条件里的表达式判断尽量用严格比较避免类型自动转换带来的意外结果。当判断条件很多而且是根据同一个变量的不同值来分支时可以用switch替代一堆if/elseif?php $status 2; switch ($status) { case 1: echo 待审核; break; case 2: echo 已通过; break; case 3: echo 已拒绝; break; default: echo 未知状态; }switch的每个case结尾必须有break否则会继续执行下一个case的代码这就是常说的“坑”忘记了break代码就“穿透”到下一个分支。PHP 8.0还引入了match表达式可以视为switch的升级版它不需要break而且返回值?php $status 2; $text match ($status) { 1 待审核, 2 已通过, 3 已拒绝, default 未知状态, }; echo $text;match在PHP 8里更好用它天然是表达式能直接把结果赋给变量而且匹配用的是严格比较不会有switch那种的隐式转换。新项目如果PHP版本在8以上我建议优先考虑match。5.2 循环控制for、while与do-whilePHP提供三种循环语法虽然都能实现迭代功能但使用场景不同。for循环适合明确知道循环次数的场景?php for ($i 1; $i 10; $i) { echo $i . ; } // 输出 1 2 3 4 5 6 7 8 9 10for的语法拆成三段初始化表达式$i1、循环条件$i10、循环后表达式$i。中间循环条件的真假决定循环是否继续一旦条件为假循环结束。while循环适合条件不确定、靠状态控制的场景?php $count 0; while ($count 5) { echo $count . ; $count; }最典型的用法是遍历文件内容比如读取文件只要没读到文件末尾就一直读。再比如从数据库拉取数据结果集里还有记录就一直循环。do-while和while的差别在于do-while至少会执行一次循环体。这个特性适合那种“必须先做一次再判断要不要继续”的场景。但说实话日常开发里do-while用得比较少我主要是在写菜单多级展开之类的逻辑时偶尔用到。5.3 数组遍历利器foreachforeach是PHP里使用频率最高的循环结构没有之一。因为PHP的数组太灵活了关联数组和索引数组混在一个数组里都很常见而foreach可以同时遍历键和值?php $user [ name 张三, age 25, city 北京 ]; foreach ($user as $key $value) { echo $key . : . $value . PHP_EOL; }输出结果name: 张三 age: 25 city: 北京foreach遍历时$key是数组的键$value是对应的值。如果你只需要值也可以简写成foreach ($array as $value)。在模板渲染、数据库结果集输出的场景里foreach就是主力。这里必须强调一个foreach的重要特性循环内部对元素的修改不会影响原数组。看这个例子?php $arr [1, 2, 3]; foreach ($arr as $value) { $value $value * 2; } print_r($arr); // 输出还是 [1, 2, 3]要直接修改原数组有两种办法。一种是遍历键用键去修改原数组另一种是在foreach里用引用?php $arr [1, 2, 3]; foreach ($arr as $value) { $value $value * 2; } unset($value); print_r($arr); // 输出 [2, 4, 6]注意这个写法最后有个unset($value)这是必须的。因为$value是引用变量循环结束后它仍然指向数组的最后一个元素后面如果再对$value赋值就会意外修改数组的最后一个值。这个细节坑过不少人包括我自己。5.4 跳转控制break、continue与gotobreak和continue是循环体里的两个控制关键字。break用来终止整个循环continue用来跳过当前这次迭代、进入下一次迭代。看个例子?php for ($i 1; $i 10; $i) { if ($i % 2 0) { continue; // 偶数跳过本次循环 } if ($i 7) { break; // 到7就终止循环 } echo $i . ; } // 输出 1 3 5break后面可以跟数字表示跳出几层循环。嵌套两层循环时break 2会退出内层和外层两层循环。不过嵌套循环多了代码可读性急剧下降我更建议把内层循环封装成函数通过return来退出。goto是PHP 5.3开始支持的跳转语法允许直接跳到指定标签?php goto end; echo 这段不会执行; end: echo 跳到这里了;goto在C语言里是熟面孔但在PHP里我几乎不使用。它能实现的功能用条件判断和函数调用完全可以替代而且滥用goto会让代码执行路径变得不可预测排查问题非常痛苦。这个语法了解即可实战中能不用就不用。6. 常见问题与排查技巧实录6.1 环境配置类问题我把这几年在PHP学习群里经常看到的新手问题整理成了一个表格每一条都是真实踩过的坑现象可能原因解决办法浏览器访问.php文件变成下载服务器没配置PHP解析检查Apache/Nginx是否加载了PHP模块命令行执行php -v报找不到命令PHP没加入环境变量找到PHP安装目录配置PATH环境变量代码改了浏览器刷新没变化Opcache缓存了旧代码PHP-FPM模式下重启PHP服务或清缓存页面直接显示PHP源码文件后缀错误或服务器配置错误确认文件是.php后缀确认Nginx的location规则Windows下路径包含空格导致PHP启动失败环境变量路径没加引号给PHP安装路径加英文双引号环境问题最大的特点就是五花八门很多时候跟你的系统版本、PHP版本、服务器软件都有关。我的排查套路是先看错误日志Nginx的错误日志在logs/error.logPHP-FPM的错误日志在php的日志目录找到具体的报错信息再去搜索解决方案比盲目试各种方法高效得多。6.2 语法类问题语法错误通常会在执行时直接抛错。最常见的几类第一忘记写分号。PHP每条语句结尾必须有分号漏掉分号会报“unexpected token”之类的解析错误。好在错误信息会提示具体行号按图索骥就能找到。第二引号不匹配。双引号里又用了双引号没有转义导致字符串提前结束。解决办法是内外引号换成不同风格或者用转义字符。第三变量名大小写错误。$name和$Name是两个不同变量明明赋值的时候用的是$name使用的时候写成了$Name得到的值就是NULL。这种错误很难发现因为PHP不会报错只是默默返回NULL。有一种排查思路很好用——分段注释。当一长段代码报错但你找不到具体位置时先注释掉后半段看前半段是否正常如果前半段正常再逐步缩小范围定位到真正出错的代码。这个办法虽然笨但在一些逻辑复杂的代码块里反而最有效。6.3 业务逻辑踩坑记录最后分享几个PHP基础学习里的典型业务逻辑坑。第一个是字符串和数字比较的坑。前面已经详细说过PHP的在比较字符串和数字时会做类型转换所以“abc”等于0。如果你用做状态判断用户传的参数可能绕过你的判断逻辑。稳妥的做法是始终用做相等比较或者在比较前明确转换类型。第二个是foreach遍历数组时修改数组的坑。前面也强调过用引用修改数组元素时循环结束一定要unset($value)否则引用会残留。如果你是做批量数据处理这个残留引用会让你后面的代码莫名其妙修改原数组值。第三个是流程控制里if/else的嵌套过深。我见过有人写五层嵌套的if读代码的人得把每一层条件都拆开才能看懂。这种代码重构思路很简单把嵌套拆成多个独立的判断提前返回。比如?php // 不推荐的写法 if ($user) { if ($user[status] 1) { if ($user[role] admin) { // 具体逻辑 } } } // 推荐的写法 if (!$user || $user[status] ! 1 || $user[role] ! admin) { return; } // 具体逻辑这种“卫语句”写法让代码的阅读顺序从“层层进入”变成“提前排除”逻辑清晰很多。尤其是有多个前置条件时用卫语句能把各种异常出口提前挡在外面核心代码不嵌套读起来非常舒服。6.4 PHP版本升级带来的坑如果你要维护的都是PHP 7甚至PHP 5的老项目升级到PHP 8时有一堆地雷。我这里提几个最常见的PHP 8.0把很多“自动变成警告/通知”的错误级别提升为Error异常。比如给一个不存在的数组键赋值、调用不存在的方法以前都是E_NOTICE或E_WARNING代码还能继续跑到PHP 8直接抛Error程序直接崩。做升级前最好用静态分析工具扫一遍代码把这种问题提前抓出来。PHP 8.0开始字符串和数值的比较规则变了。前面提过非数字字符串与数字比较时如果字符串不是数字就不再做“转成0”的宽松处理而是直接比较字符串字典顺序。这个变化对业务判断影响很大尤其是那些依赖比较的旧代码升级后行为会完全不一样。对刚开始学PHP的人来说这些版本差异可以先了解不用记太深。但如果你将来要工作接项目这些就是实打实的生存技能。我对PHP基础这一块最深的体会是基础语法不是背诵的内容而是每天在代码里反复使用的东西。你可能今天写一个循环很吃力但写三十个循环之后for和foreach的边界就在你脑子里固化了。你可能今天还在被双引号和单引号搞晕但多写几个页面渲染场景自然就明白该在哪用哪个。这篇文章里的所有知识点都值得你打开编辑器亲自动手敲一遍。尤其是我标记过的那些“坑”它们不是吓唬你而是我真实踩过之后替你做的笔记。照着代码敲一遍再自己改一改参数、换一换逻辑这些基础知识才算真正长在你身上。