尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

京东2018秋招PHP笔试题解析:从基础到安全实战

京东2018秋招PHP笔试题解析:从基础到安全实战 这套题我印象太深了。2018年那会儿正好是秋招最卷的时候京东的PHP开发工程师笔试题在圈子里传得很广我身边好几个朋友都刷过。后来我自己带团队面试也会翻出类似的题目来筛人。为什么呢因为这套题出的水平确实可以它不是单纯的八股文背诵而是把一个合格PHP工程师在日常开发中真正要碰的东西都串起来了——从字符串处理、数组操作到面向对象设计从网络协议到数据库优化甚至还有工程化能力和安全意识的考察。今天我就以这套题为引子把背后的核心知识点、我当时踩过的坑、还有后来面试别人时发现的高频失分点完整拆开讲一遍。这篇文章适合谁看两类人。第一类是准备PHP岗位校招或者社招的朋友你可以把它当作一份系统的复习提纲对照着查漏补缺第二类是已经工作了几年、想回头夯实基础的开发者这套题里的很多考点放在今天依然是面试的高频题而且和日常开发的联系非常紧密。1. 整体拆解京东2018秋招PHP笔试题到底在考什么1.1 题目类型与考察维度先说结论这套题的整体风格是“基础扎实 工程落地 安全意识”难度对校招生来说属于中等偏上但它几乎不考偏题怪题。整张卷子一般由选择题、填空题、简答题和编程题构成其中选择题覆盖面很广编程题则非常贴近实际业务场景。我把它拆成几个维度来看语言基础层字符串函数、数组函数、运算符优先级、类型比较、错误处理机制。这一层考察的是你对PHP手册的熟悉程度说白了就是平时写代码的基本功扎不扎实。面向对象层魔术方法、访问控制、静态成员、设计模式。这一层考察的是你写出来的代码能不能被维护、能不能被扩展。网络与协议层HTTP状态码、Session与Cookie、跨域问题、JSONP、接口安全。这一层考察的是你作为一个后端工程师对前端联调和网络通信的理解。数据存储层SQL语句、PDO预处理、索引原理、事务隔离级别、数据库优化。这一层考察的是你处理真实数据的能力。工程与安全层MVC框架理解、文件包含、反序列化漏洞、代码审计思维。这一层是拉分项很多人会在这里丢分。有意思的是这套题虽然已经是2018年的了但里面的核心考点放到2025年的今天依然不过时。原因很简单PHP语言的底层机制没有大变面试官考察的“思维方式”也始终没有变。1.2 备考定位为什么这套题值得反复刷我当时看这套题的最大感受是——它没有故意为难人的意思。对比一下其他大厂的题目有些确实会出“冷门函数默写”这种题但京东这套题更倾向于“给你一个业务场景让你选最合理的实现方式”。举个例子它不会直接问你array_map和foreach的区别而是给你一段统计用户订单金额的代码让你找出其中的性能问题并优化。这种出题思路非常务实它考察的不只是“你会不会用函数”而是“你在真实项目中能不能写出高效的代码”。所以如果你正在准备面试我强烈建议你不要只是把题库背一遍。正确的姿势是每做一道题就去想“这题考的是哪个知识点”、“这个知识点在我的项目里哪里用过”、“如果让我从零实现一遍我会怎么写”。带着这三个问题去刷题比盲目刷十套题都管用。2. 基础语法与字符串处理三分基础七分细节2.1 substr函数和它的兄弟们热搜词里出现了php substr函数用法这其实是PHP面试中出现频率最高的知识点之一。2018年这套题里有一道选择题问的是substr(Hello, 京东, 0, 6)的输出结果是什么。这题看似简单实际上埋了好几个坑。第一substr在英文字符和中文混排的情况下如果你用的是PHP 7.x之前的版本截取中文很可能出现乱码。因为早期的substr是按字节截取的一个中文字符在UTF-8编码下占3个字节你截取6个字节可能刚好把一个中文拦腰截断。到了PHP 7.x之后很多场景下推荐使用mb_substr它按字符截取就不会有这个问题。第二substr的第三个参数如果是负数表示从字符串末尾倒数的位置。这个知识点看着简单但笔试时很容易慌。比如substr(abcdef, 0, -2)的结果是abcd很多人在笔试环境下会直接答错。第三还有一个经常和substr一起考的变体是strstr、strpos、str_replace。比如strpos判断子串是否存在时如果子串出现在第0位返回的是0而0在布尔判断中是false。这就是经典的“判断子串存在性”的坑正确写法是strpos($haystack, $needle) ! false。我后来面试别人的时候特别喜欢问这类细节。因为一个候选人如果能讲清楚substr和mb_substr的差异以及strpos返回0的坑那说明他是真的在用PHP写业务而不是只背了面试题。2.2 运算符、类型比较和错误处理热搜词里还有php 运算符和php错误处理。这两块在京东2018的笔试题里也是重点。PHP的松散类型是它的特点也是它的坑。0 foo在PHP 7.x之前是true因为在非严格比较下字符串会被转换成数字0。到了PHP 8.0这种比较行为被修改了0 foo的结果变成了false。这种跨版本的行为差异恰恰是面试官爱考察的点——他可能不会直接问你版本差异而是给你一段代码让你说出输出结果。常见的类型比较题还有这些null false结果是true因为宽松比较下它们都算“空值”。abc 0在PHP 7.x及更早版本是true在PHP 8之后是false。false 0是true但false 0是false。所以我的建议是在笔试中只要题目涉及比较优先考虑和!除非题目明确要求宽松比较。这是最不容易失分的策略。错误处理这块重点要理解error_reporting、display_errors、log_errors三个配置项的区别和联系。还有异常与错误的区别错误Error是PHP本身的问题比如语法错误、致命错误异常Exception是程序员主动用throw抛出来的。PHP 7之后引入了Error类和Exception一样都实现了Throwable接口所以你可以用同一个try...catch块来捕获两者——但parse error这种编译期错误是捕获不到的这点笔试时容易混淆。实操中我建议在项目里保持display_errors Off、log_errors On同时设置一个统一的异常处理器把日志写到独立的文件中。这样既不会把堆栈信息暴露给用户又方便排查问题。这个思路如果能在简答题里写出来会很加分。3. 数组、类与设计模式写出能上生产的PHP3.1 数组的底层逻辑和排序热搜词里有一个php 类还有一个php 源码。数组和类这两块是PHP笔试的大头。先讲数组因为PHP的数组真的太特殊了——它既是数组又是字典底层实现是哈希表HashTable加上双向链表。你平时用array_push、array_pop、array_shift、array_unshift操作数组时可能没想过它们的性能差异。实际上array_shift和array_unshift需要重新索引所有元素是O(n)的操作而array_push和array_pop是O(1)的。如果在一个大循环里频繁使用array_shift性能会急剧下降。更优的做法是反转数组后用array_pop或者用SplQueue来处理队列场景。排序这块sort、asort、ksort、usort、array_multisort都是高频考点。它们的区别用一个表格就能说清楚函数排序依据是否保持键值关联典型场景sort值否键名被重排纯值列表排序asort值是按分数排名的关联数组ksort键名是按键名排序的字典usort自定义规则否按对象属性排序array_multisort多列可保持多维数组按多个字段排序京东2018年那道编程题我印象很深给一个二维数组要求先按age升序再按salary降序排列。很多人第一反应是写多层usort嵌套但标准解法是用array_multisort。思路是先把两列拎出来作为辅助数组然后一次性传入多个排序规则。这种题目考察的就是对PHP数组函数的熟悉程度如果平时只会在foreach里写冒泡排序这种题会做得很吃力。3.2 单例模式、魔术方法与类的底层理解类的考察点一般集中在魔术方法、访问控制、静态成员、设计模式这四块。魔术方法是PHP的特色__construct、__destruct、__get、__set、__call、__toString、__clone、__sleep、__wakeup都是高频考点。其中__sleep和__wakeup是序列化和反序列化时自动调用的这两个方法在PHP反序列化漏洞中经常出现笔试时如果能把“序列化的字段控制”和“反序列化后的资源恢复”联系起来讲会显得你不仅有知识框架还有安全意识。单例模式是设计模式里最常考的一个。它的核心就是三点构造函数私有化防止外部new、静态成员保存实例保证全局唯一、静态方法提供访问入口统一获取方式。写成代码大概是这样的class Database { private static ?Database $instance null; private \PDO $pdo; private function __construct(array $config) { $this-pdo new \PDO( mysql:host{$config[host]};dbname{$config[dbname]}, $config[user], $config[pass] ); } private function __clone() {} public static function getInstance(array $config): Database { if (self::$instance null) { self::$instance new self($config); } return self::$instance; } public function query(string $sql): bool|\PDOStatement { return $this-pdo-query($sql); } }注意这里的__clone也要私有化否则外部仍然可以克隆重生成一个副本单例就被破坏了。这个细节很多人会漏。设计模式方面除了单例工厂模式、观察者模式、策略模式也偶尔会考。我的建议是不需要把二十多种设计模式全背下来但至少要能画清楚两三种常用模式的类图并说明它们解决了什么问题。京东这套题的简答题里经常会让你“用设计模式重构一段代码”重点考察的是你有没有“面向接口编程”的意识。4. 网络、协议与安全笔试中最容易拉开差距的部分4.1 跨域与JSONP热搜词里php跨域jsonp是一个很显眼的组合。之所以有很多人搜这个是因为在实际前后端分离的开发中跨域问题几乎是每个PHP开发都会遇到的。要理解跨域先要理解同源策略浏览器只允许页面请求同协议、同域名、同端口的资源。当你的前端页面跑在http://a.com后端接口跑在http://b.com直接发AJAX请求就会被浏览器拦截。解决方案有好几种后端最常用的是设置CORS头header(Access-Control-Allow-Origin: https://a.com); header(Access-Control-Allow-Credentials: true); header(Access-Control-Allow-Methods: GET, POST, OPTIONS); header(Access-Control-Allow-Headers: Content-Type, Authorization);注意如果Access-Control-Allow-Origin要用通配符*那Access-Control-Allow-Credentials就不能同时设置为true因为带上Cookie的跨域请求不允许使用通配符。这个细节是跨域配置里最常见的坑之一。那JSONP是什么呢它是利用script标签不受同源策略限制的特点来实现的变通方案。后端返回的不是纯JSON而是一段JavaScript函数的调用// 输入callbackmyFunc // 输出myFunc({ code: 0, data: [...] }) $callback $_GET[callback] ?? callback; $data [code 0, data [name 京东]]; header(Content-Type: application/javascript); echo $callback . ( . json_encode($data) . );JSONP的缺点是只能支持GET请求安全性也相对较差所以现在主流方案是CORS。但笔试里它依然高频因为面试官想确认你是否真的理解“跨域解决方案的本质”。4.2 伪协议与文件包含热搜词里出现php伪协议这真的是一个又爱又恨的知识点。爱的是它非常实用恨的是它经常和安全漏洞绑定在一起。所谓伪协议就是PHP内置的一些流封装协议比如php://input、php://filter、data://、file://、phar://等。其中最常考的是php://filter。它可以用来对文件内容做过滤处理经典用法是读取PHP源文件并Base64编码// 读取 source.php 的内容并编码常用于代码审计和脱敏展示 echo file_get_contents(php://filter/readconvert.base64-encode/resourcesource.php);在笔试和安全题里面试官通常会把php://filter和文件包含函数include、require结合起来考察。比如你有一个index.php?pageabout.php如果page参数没有经过严格的过滤和校验攻击者就可以把参数改成php://filter/convert.base64-encode/resourceconfig.php从而读到数据库配置文件的内容。应对这类题的正确思路我从实际防御的角度总结了三点使用白名单机制限制文件包含的范围而不是黑名单过滤特殊字符。尽量不要让用户直接控制include的文件路径如果确实需要要检查真实路径是否在允许的目录内。对$_GET、$_POST参数永远保持怀疑不要信任任何输入。4.3 序列化与安全的博弈热搜词里的php序列化中文、php 序列化也是一个非常典型的考点。序列化serialize和反序列化unserialize在PHP中很常用但反序列化如果使用不当会引入严重的安全风险。先看序列化的技巧json_encode默认会把中文转成\uXXXX的形式不便于阅读和调试。如果希望保留中文字符可以加一个参数echo json_encode($data, JSON_UNESCAPED_UNICODE);serialize则不受这个问题影响它直接把中文以原始字符存储。但serialize出来的字符串结构比较复杂可读性不如JSON。所以如果只是普通的接口数据传递我一般用JSON如果要在PHP内部做缓存或者存储复杂对象结构我会考虑serialize。安全的重点在反序列化。PHP的反序列化漏洞核心原因是unserialize处理的数据如果来自用户输入攻击者可以构造恶意字符串让程序在反序列化的过程中自动触发某些魔术方法如__wakeup、__destruct进而执行恶意代码或发起攻击。这就是所谓的“PHP对象注入”和“Pop链”的起点。防御手段其实也很简单永远不要对用户输入直接调用unserialize。如果必须使用可以开启allowed_classes参数限制可反序列化的类例如unserialize($data, [allowed_classes false])。复杂业务场景下优先用JSON传输数据尽量不碰unserialize。这套题考这两个点其实是在提醒所有PHP开发者安全和业务不是两件事它是一个合格工程师写代码时必须内生的一部分。5. 数据库与性能优化从笔试到生产5.1 PDO预处理为什么能防SQL注入热搜词里有一串和数据库相关的词php 数据库pdo访问封装类下载、php 图书管理系统。这说明很多人在实际开发中都会用到PDO。PDO预处理是PHP面试绕不开的重点京东这套题也有一道关于预处理机制的简答题。解释一下预处理的原理。预处理分为两个阶段第一阶段是“准备”PDO把你的SQL语句模板发送给MySQLMySQL完成语句的解析和编译第二阶段是“执行”你再把参数传给MySQLMySQL用参数替换占位符并执行。因为SQL结构在准备阶段已经确定了参数值只能作为数据进行绑定不再参与SQL语句的拼接所以注入的恶意代码不会被当成SQL逻辑执行。用代码来演示一下$pdo new PDO(mysql:host127.0.0.1;dbnametest;charsetutf8mb4, root, password, [ PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_ASSOC, ]); $stmt $pdo-prepare(SELECT * FROM users WHERE email :email AND status :status); $stmt-execute([:email $email, :status 1]); $user $stmt-fetch();注意charsetutf8mb4一定要带上这是防注入的另一个关键细节——如果使用GBK这种多字节编码且没设置字符集就存在宽字节注入的风险。有了utf8mb4配合占位符绑定SQL注入基本可以杜绝。后面我还会提到php 图书管理系统这类场景题。很多人在做图书管理系统的时候会用拼接字符串的方式构造SQL这就是典型的危险写法。笔试时如果让你写一个用户登录的查询语句直接写预处理方式是最稳妥的。5.2 索引设计、慢查询和事务隔离数据库优化的部分一般是简答题或者案例分析题。常见考法“给一个订单表有字段order_id、user_id、amount、status、created_at请设计索引并简述原因。”我的回答思路是order_id是主键默认有主键索引用来保证唯一性。user_id是高频查询条件加普通索引加速“查某用户的历史订单”。status是低区分度的字段一般不加单列索引如果非要和user_id一起查可以建立联合索引(user_id, status)索引左前缀原则可以让这个索引同时服务user_id单字段查询和user_id status组合查询。created_at适合做范围查询如果业务上经常按时间统计订单量可以加索引但要注意范围查询后的索引失效问题。事务隔离级别也是经典考点。MySQL默认的REPEATABLE READ在InnoDB下通过MVCC解决了大部分幻读问题。笔试时如果遇到“如何防止并发下订单金额被覆盖”的问题标准思路是在更新语句里使用SELECT ... FOR UPDATE加锁或者在更新后锁定的机制或者更简单的——把关键业务放在数据库事务中配合唯一索引兜底。不过这里我补充一句笔试归笔试生产环境要慎用FOR UPDATE因为它容易造成锁等待和死锁。如果业务允许用“乐观锁”的方式即通过比对版本号或者状态字段来决定是否更新成功往往更实用$stmt $pdo-prepare(UPDATE orders SET status :new_status, version version 1 WHERE order_id :oid AND status :old_status); $stmt-execute([:new_status 2, :oid $orderId, :old_status 1]); if ($stmt-rowCount() 0) { // 说明订单状态已经被别人改过需要重新处理 }凡是SQL拼接的动态条件优先考虑用预处理绑定参数凡是涉及账户金额、订单状态等并发敏感的数据一定要考虑事务和防止超卖的手段。这套思路不但笔试能用生产环境也能用。6. 工程化与框架ThinkPHP 3.2.3背后的考点6.1 从MVC到路由框架的核心就那几件事热搜词里出现了一串thinkphp3.2.3的内容这很真实。2018年的时候ThinkPHP 3.2.3在国内中小型PHP项目中依然有大量的存量用户。即使是今天很多公司的老项目里还在跑着TP 3.2.x所以面试官拿它来考察你对框架原理的理解一点也不奇怪。我当时复习框架这部分时给自己列了一个清单入口文件做什么定义常量、加载框架引导文件、启动应用。MVC是怎么组织的M层负责业务数据和规则V层负责展示C层负责调度请求。三者职责清晰才能保证代码可控。路由是怎么实现的框架把URL解析成Module/Controller/Action三个部分然后通过反射机制调用对应的控制器方法。ORM模型解决了什么问题把数据库表映射成类把行记录映射成对象程序员用面向对象的语法操作数据库。依赖注入和控制反转如果你从不用容器你会发现同一个类在多个地方被new出来后续想替换一个实现就得满项目地改。控制反转是把创建对象的权利交给容器你只需要在注册的时候声明“这个接口对应哪个具体实现”。招聘方把ThinkPHP放进笔试题其实不是在替某个框架做背书而是在考察你有没有理解框架存在的意义——它解决的是代码复用、分层解耦、快速迭代的问题。6.2 场景题图书管理系统、题库系统和图片抓取说回热搜词里的php图书管理系统、php保存题库信息代码、php实现html内容图片抓取替换。这些听起来像大作业题目但它们背后对应的其实是笔试编程题的原型京东2018的卷子里就有类似的场景设计题。以图书管理系统为例这类题一般会要求你设计数据库表结构并手写核心查询。我的表设计思路是这样的book表book_id主键、title、author、publisher、isbn、stock、created_at。user表user_id、username、password_hash、email。borrow_record表record_id、user_id、book_id、borrow_at、return_at、status。核心查询是“查某本书当前的借阅状态”和“查某个用户当前未归还的图书数量”。前者关联book和borrow_record后者用COUNT加上status条件即可。如果你能在表设计时加上唯一索引(user_id, book_id, status)并且把只允许一条未归还记录的约束利用起来那就是加分项。再比如“图片抓取替换”这种题考察的是正则表达式或者DOM解析的能力。面试官一般不要求你用现成的爬虫框架核心是考察你能否从HTML中提取出图片地址并做替换。下面这种写法是我的常规答案$html img src/upload/a.jpg altaimg src/upload/b.png altb; // 第一步匹配所有 img 标签的 src 属性 preg_match_all(/img[^]src[\]([^\])[\]/i, $html, $matches); $oldUrls $matches[1]; // 第二步把相对路径替换为 CDN 绝对路径 $newUrls array_map(function ($url) { if (str_starts_with($url, http)) { return $url; } return https://cdn.example.com . $url; }, $oldUrls); // 第三步用 str_replace 批量替换 $html str_replace($oldUrls, $newUrls, $html);答题时注意一点要先把$oldUrls和$newUrls准备好再统一替换避免前一个替换影响后一个的匹配结果。这种程序里面的“先后顺序”思维也是面试官想观察的东西。7. 常见问题与避坑实录7.1 笔试和面试中的高频失分点结合我刷题和后来当面试官的经验我梳理了PHP笔试题里最容易失分的几个点类型比较的坑0、0、false、null之间的宽松比较结果让人防不胜防。遇到这种题默认用判断。字符串和数组函数记混strpos和array_search返回值设计不同implode和explode参数顺序相反笔试一紧张就容易记反。只写SELECT不会写EXPLAIN手写SQL之后如果被追问一句“这条SQL走没走索引”很多人就会卡壳。平时练习时一定要养成对SQL执行EXPLAIN的习惯至少能看懂type、key、rows三列。不会处理PHP错误和异常只会try...catch捕获Exception不知道Error类实现了Throwable导致捕获不到致命错误。代码风格不统一缩进混乱、变量命名用拼音、方法没有注释。笔试机评虽然不会因为风格扣分但面试官一眼就能看出你的工程素养。7.2 现场笔试的时间分配和答题策略这里分享一个我自己的做题顺序这套顺序后来也推荐给了不少学弟学妹先做简答题和编程题。这类题目分值高、需要思考时间长先把它们写完心里踏实。编程题动手前先在草稿纸上画一下数据结构想清楚再用代码写。再做选择题和填空题。因为它们有时候会考一些很细的边界知识放到后面做反而能因为前面编程题的思路而调用起来。不会的题先标记跳过。不要在一道题上死磕超过5分钟笔试的时间很宝贵。代码题一定要写注释。注释不一定要多但关键的步骤逻辑要清晰这能让面试官更快理解你的思路。关于编程题的答题卡还有个小技巧如果你的编程环境里支持本地运行写完代码后一定要跑一下边界用例。比如字符串处理题试试空字符串排序题试试只有一条记录的情况。这些小细节能帮你躲开很多隐藏的雷。7.3 复盘比刷题更重要刷完这套题我强烈建议你做一个动作把每一道错题对应的知识点整理成一份“错题清单”不只要记正确答案还要写清楚“我当时为什么选错了”以及“下次该怎么避免”。比如如果你错在array_multisort用法上就在错题本里写array_multisort接受数组引用和排序标志第一个排序条件优先后续条件依次作为“并列时候的二次排序依据”。还要写一个最小可运行示例确保自己真的看懂了。我更推荐用“费曼学习法”来检验自己是否真的掌握了把这道题讲给一个不懂PHP的朋友听如果你能用大白话讲清楚“为什么”那就说明你真的懂了。如果讲得磕磕绊绊那这道题你还得再看一遍。8. 从笔试到项目这套思路怎么落地说回热词里的thinkphp3.2.3、php源码、phpstorm怎么运行php项目这类实战话题。笔试只是入口最终目的还是让你能写出可以上线的代码。很多初学者会遇到一个情况笔试题能答对但真到公司里打开PhpStorm面对一台Linux服务器发现连一个完整的项目都跑不起来。这就是“应试”和“工程”之间的差距。所以我建议你在准备面试的同时一定要动手把一个完整的项目跑起来。步骤如下在自己电脑上装好PHP环境和MySQL或者直接用Docker起一个php:8.2-fpm和mysql:8.0的组合。用PhpStorm创建或导入一个已有的PHP项目配置好本地解释器路径。在项目里建一个index.php写一段PDO连接数据库、查询数据的代码通过浏览器访问验证。试着在项目里增加一个简单的接口返回JSON数据并用前端页面调用它。最后再想办法部署到一台服务器上配合Nginx配好PHP-FPM的转发规则。当你亲手把这段流程走通之后你再回头去看京东这套笔试题里的框架题、部署题、安全题会发现它们其实都在讲同一件事——一个PHP工程师的完整工作链路。另外如果你对源码感兴趣可以拆一个简单的框架或者自己写一个极简MVC。不用多复杂几十行代码都可以一个入口文件、一个路由器、一个控制器基类、一个模板渲染函数。写完之后你会对“框架为什么这样设计”有完全不一样的理解。很多你背过的知识点突然就串起来了。这套题虽然来自2018年但它的考察逻辑并没有过时。技术栈会迭代框架会更换但基础原理、工程思维、安全意识这些东西放在哪个时代都值钱。希望这篇复盘能帮你把知识点串成体系少走一些弯路。
返回列表