尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

PHP P1工程师无伤通关:核心语法陷阱与CRUD实战指南

PHP P1工程师无伤通关:核心语法陷阱与CRUD实战指南 如果你玩过《忍者必须死3》应该能体会这种感觉跑酷地图看着不复杂但暗坑特别多。你以为只是一段普通砖墙贴脸跳过去才发现是电网一套连招直接残血。很多玩家不是“不会玩”而是“不背板”——不知道机制在哪里招式再熟练也白搭。把场景换成 PHP 工程师的学习和面试逻辑几乎一样。PHP 语法上手确实快变量以$开头、数组是个万能容器、函数一大堆这些都不难。真正让一个 P1 级别的新手“暴毙”的往往是一些看起来很小的坑两个字符串用比较为什么成立了数组里放了整型和字符串键名为什么顺序和你想的不一样一个接口明明写了返回 JSON前端却收到字符串本地跑得好好的项目换台电脑环境就崩这篇文章不会带你背一遍 PHP 手册。我会按“P1 无伤通关”的思路把 PHP 工程师起步阶段最常用、最容易踩坑、面试最常考的内容串一遍从环境搭建、核心语法陷阱到图书管理系统这种最常见的增删改查项目再到跨域、序列化、错误处理、生产部署注意事项。读完后你至少能获得一份“背板图”知道哪些地方容易被打知道该怎么躲以及被打掉半血之后怎么快速自救。1. P1 级 PHP 工程师到底考什么先说清楚一个判断P1 这个级别不是考你读过多少源码、做过多少高并发优化而是考你能不能独立交付一个功能完整、逻辑正确、基本安全的小项目。你可以用一句话概括 P1 的能力要求把需求变成页面、把页面里的数据正确存进数据库、再把数据库里的数据正确取出来展示给用户并且在这条链路里不掉进 PHP 常见陷阱。对照下来P1 阶段需要掌握的内容基本是这几块能力模块具体内容常见考核方式基础语法变量、运算符、数组、字符串、控制流笔试题、代码阅读题函数与面向对象函数定义、类、对象访问、命名空间项目代码质量表单与请求$_GET/$_POST/$_SERVER/ 请求头接口开发数据库操作PDO、预处理、增删改查图书管理系统等 CRUD 项目会话与认证Session / Cookie、登录态保持后台登录模块文件操作文件上传、读取、安全限制头像上传、Excel 导入环境与部署本地环境、线上 Nginx/Apache 配置让项目跑起来安全基础SQL 注入、XSS、CSRF、文件上传校验防御意识考察很多新手会忽略最后一行“安全基础”。但在实际工作中安全不是加分项而是及格线。P1 面试里不一定会直接问你“什么是 SQL 注入”但一定会看你写的 SQL 是拼字符串还是用预处理。这就是 P1 和“培训作业”的分水岭。所以这份“无伤攻略”的核心思路是用最小成本把最常见的面做好把最常见的坑填平杜绝硬伤。2. PHP 环境搭建先把“跑起来”这件事做稳环境问题属于那种看起来简单、实际能卡你一整天的坑。我见过不少新手花两个小时写代码用四个小时装环境最后发现是 PHP 版本装错了目录。2.1 Windows 环境方案如果你用的是 Windows最常见也最省事的方案是集成面板比如 PHPStudy、XAMPP、WampServer。这类工具把 Apache/Nginx、MySQL、PHP 打包在一起点一下就能启动。以 PHPStudy 为例基本操作流程是下载安装对应版本解压路径尽量不要带中文和空格。在面板中启动 Nginx或 Apache和 MySQL。在“软件管理”里安装你需要的 PHP 版本P1 阶段建议 PHP 8.0 以上。把项目代码放到网站根目录比如phpstudy_pro/WWW。访问http://localhost/项目名验证是否输出phpinfo()。下面是最常用的检测代码?php // 文件路径项目根目录/index.php phpinfo();如果你能看到 PHP 版本、扩展信息、配置路径说明环境已经通了。2.2 macOS 环境方案macOS 上很多新手会随手使用系统自带 PHP但系统版本往往偏旧而且和 Composer、扩展的兼容性容易出问题。更稳的做法是使用 Homebrew 安装指定版本或者用 PHPStudy 的 macOS 版本。这里有一个典型报错很多人搜索过dyld: Library not loaded: loader_path/../../../../opt/libffi/这个错通常是 PHP 二进制文件的动态链接库路径出现问题。解决思路不是硬猜而是先确认你当前执行的是哪个 PHPwhich php php -v然后查看 PHP 依赖信息otool -L $(which php)如果发现某个依赖路径不存在多半是因为你重装或升级过依赖库比如 libffi。此时最稳妥的修复方式是重新安装对应的 PHP 版本brew reinstall php8.2或者改用 PHPStudy 一键切换版本避免自己在命令行里管理动态库。2.3 用 Docker 打包 PHP 镜像如果是跟着团队项目走或者你要部署到服务器Docker 是更统一的环境方案。一个最小可用的 PHP 项目可以由下面的docker-compose.yml定义# 文件路径docker-compose.yml version: 3 services: php: image: php:8.2-fpm container_name: php-demo volumes: - ./www:/var/www/html ports: - 9000:9000 nginx: image: nginx:latest container_name: nginx-demo volumes: - ./www:/var/www/html - ./nginx/conf.d:/etc/nginx/conf.d ports: - 8080:80 depends_on: - php然后在./nginx/conf.d/site.conf里配置 Nginx让它把 PHP 请求转发给php容器server { listen 80; server_name localhost; root /var/www/html; index index.php index.html; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass php:9000; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }启动命令docker compose up -d然后访问http://localhost:8080如果看到Hello PHP之类的输出说明容器链路是通的。Docker 方案的好处是环境无污染、版本一致坏处是你需要额外理解 Nginx、FastCGI 这些概念。P1 阶段不要求你精通但至少要有“用容器跑起 PHP 项目”的能力。3. PHP 核心语法与最容易致命的基础陷阱这一节是本攻略的“背板图”。下面每个点单独拿出来都不难但组合在一起就是 P1 面试笔试题里的重灾区。3.1 弱类型比较和不是程度差异而是机制差异PHP 最出名也最让新手困惑的就是弱类型比较。表面上看是宽松比较是严格比较。但真正的关键是会发生类型转换。一个最常见的例子?php var_dump(0e123 0e456);很多新手觉得两个字符串内容明显不同结果应该输出false。但实际输出是true。原因是当两个字符串的格式都符合科学计数法表示的数字时PHP 会先把它们当数字比较。0e123和0e456都表示0 * 10 的 n 次方都等于 0所以二者相等。这就是经常在网上看到的“MD5 相等绕过”的根源如果代码写成if ($a $b)而$a和$b是来自用户输入、且经过 MD5 计算后的字符串那么某些经过精心构造的输入会产生 0e 开头的 MD5 值导致宽松比较返回真。正确的防御写法?php // 使用严格比较 if (md5($a) md5($b)) { // 业务逻辑 }在 P1 阶段你不需要深入研究密码学攻击只需要记住一条铁律涉及校验、鉴权、哈希比较时一律使用不要使用。3.2 数组键名的自动转型PHP 数组的键名会做隐式转换这个机制藏得很深。例如?php $arr []; $arr[1] php; $arr[1] java; var_dump($arr);结果不是两个键而是只有一个键1值被后写入的java覆盖。因为字符串数字键1会被转成整型1。再比如?php $key 08; $arr []; $arr[$key] hello; print_r($arr);这里08不是合法的十进制数字格式PHP 不会把它转成整数所以它会保留为字符串键。这类问题放到项目里最典型的表现是从接口得到的数据明明包含多个元素但你遍历时却发现某些键被覆盖了。排查思路是先var_dump()键名类型确认是否有隐式转换发生。实际开发中处理来自 JSON 的键时我会先明确键名类型再写入数组。3.3 从混合字符串中提取数字热搜词里那句[\1\,\2′\]怎么取出数字php本质是一个正则提取问题。假设你拿到一段 JSON 风格但又不完全规范的字符串?php $str [1,2′]; preg_match_all(/\d/, $str, $matches); print_r($matches);输出Array ( [0] Array ( [0] 1 [1] 2 ) )如果输入是规范的 JSON更推荐直接用json_decode()?php $str [1,2]; $data json_decode($str, true); print_r($data);只有字符串并不是标准 JSON 时才考虑用正则。我见过很多新手在任何场景都先想到正则结果把简单问题写复杂了。通用判断标准是数据格式稳定就用解析函数格式混乱再考虑正则。3.4 对象访问与数组合集$user-xx()和$arr[xx]热搜词里还有一条“$user-xx()中间的-和是什么意思”。其实这是把两个符号拆开看造成的误会。-是 PHP 中访问对象属性的符号不是减号再加大于号。写法是?php class User { public $name 忍者; } $user new User(); echo $user-name; // 输出忍者如果是调用对象方法?php class User { public function profile() { return P1 忍者; } } $user new User(); echo $user-profile(); // 输出P1 忍者而访问数组用$arr[key]或$arr-key后者仅限特殊对象式数组。初学阶段最容易混的就是从数据库查出来的是数组从 ORM 查出来的是对象两者访问语法完全不一样。建议先统一用var_dump()确认类型再做访问操作。3.5isset、empty、is_null三者的边界这三个函数是 P1 面试的高频题也是最容易记混的。表达式00null[]未定义变量isset($v)truetruetruefalsetruefalseempty($v)truetruetruetruetruetrueis_null($v)falsefalsefalsetruefalse报错/true实际项目中判断前端传的参数是否填写通常用?php $name $_POST[name] ?? ; if (empty($name)) { // 参数为空 }但要特别注意如果允许传合法值0使用empty()会把0也当成空值处理。搜索词里出现“php当前时间加一天”也常和日期默认值一起考?php $now date(Y-m-d H:i:s); $nextDay date(Y-m-d H:i:s, strtotime(1 day)); echo $nextDay;这里容易踩的坑不是函数本身而是时区。如果php.ini里没有设置时区date()会使用 UTC 时间导致和北京时间差 8 小时。开发环境建议在项目入口设置?php date_default_timezone_set(Asia/Shanghai);4. 以图书管理系统为例把增删改查做完整几乎所有 PHP P1 面试都会涉及一个 CRUD 项目。图书管理系统是很经典的选题因为它的边界清楚书目信息、借阅状态、简单后台。这里的价值不是项目本身多有创意而是你能不能在 CRUD 里展示出规范化写法。4.1 数据库设计先建一张简单的图书表CREATE DATABASE IF NOT EXISTS library DEFAULT CHARSET utf8mb4; USE library; CREATE TABLE IF NOT EXISTS book ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, title VARCHAR(100) NOT NULL COMMENT 书名, author VARCHAR(50) NOT NULL DEFAULT COMMENT 作者, status TINYINT NOT NULL DEFAULT 1 COMMENT 1 可借 0 已借出, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT图书表;之所以推荐utf8mb4是为了兼容中文表情符号和更广泛的中文字符集。搜索词里出现过“php序列化中文”其实也和编码有关如果数据库连接字符集不是 utf8mb4序列化中文再存储时容易出现乱码。4.2 用 PDO 连接 MySQLP1 阶段不建议使用mysqli_connect()这类面向过程的写法更推荐 PDO。PDO 的好处是统一数据库操作接口、支持预处理、能有效避免 SQL 注入。一个最小的 PDO 连接类可以这样写?php // 文件路径includes/Database.php class Database { private static ?PDO $pdo null; public static function connection(): PDO { if (self::$pdo null) { $host 127.0.0.1; $db library; $user root; $pass your_password; $charset utf8mb4; $dsn mysql:host$host;dbname$db;charset$charset; self::$pdo new PDO($dsn, $user, $pass, [ PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_ASSOC, ]); } return self::$pdo; } }使用单例是为了避免一个请求内多次创建数据库连接。注意在实际项目中数据库密码不要硬编码在类文件里应该通过环境变量或配置文件加载。4.3 图书查询列表?php // 文件路径index.php require includes/Database.php; $pdo Database::connection(); // 使用预处理避免 SQL 注入 $keyword $_GET[keyword] ?? ; $sql SELECT * FROM book WHERE title LIKE :keyword ORDER BY id DESC; $stmt $pdo-prepare($sql); $stmt-execute([:keyword % . $keyword . %]); $books $stmt-fetchAll(); foreach ($books as $book) { echo htmlspecialchars($book[title], ENT_QUOTES, UTF-8) . - . htmlspecialchars($book[author], ENT_QUOTES, UTF-8) . br; }这里有两个关键点使用prepare 参数绑定而不是拼接 SQL。输出到 HTML 时使用htmlspecialchars()转义防止 XSS。很多新手把重点放在“结果能不能显示”忽略了 XSS 转义。在真实生产环境这段没有转义的代码就是安全漏洞。4.4 新增图书表单处理?php // 文件路径add.php require includes/Database.php; if ($_SERVER[REQUEST_METHOD] POST) { $title trim($_POST[title] ?? ); $author trim($_POST[author] ?? ); if ($title ) { die(书名不能为空); } $pdo Database::connection(); $sql INSERT INTO book (title, author) VALUES (:title, :author); $stmt $pdo-prepare($sql); $stmt-execute([ :title $title, :author $author, ]); header(Location: index.php); exit; } ? form methodpost 书名input typetext nametitle 作者input typetext nameauthor button typesubmit添加/button /form这段代码展示了一个完整的 POST 处理生命周期获取输入、校验、写库、跳转。校验这一步很容易被忽略但生产环境必须有。4.5 为什么“图书管理系统”是 P1 必会因为这类项目把 Web 开发的主链路串起来了表单提交、数据库操作、列表渲染、搜索、跳转。你把这张图的每个环节都掌握好你就已经具备进入真实项目的底子。P1 面试官问图书管理系统不是期待你做出多惊艳的系统而是想看你是不是真的理解了流程闭环。5. 接口与前端配合JSON、跨域、签名P1 阶段会接触越来越多的前后端分离项目。后端不再直接输出 HTML而是输出 JSON 给前端调用。这个环节新手常见三连问接口返回数组还是对象前端请求跨域了怎么办要不要做秘钥加密5.1 返回 JSON 的正确姿势PHP 内置json_encode()可以非常方便地把数组转成 JSON?php $data [ code 0, msg success, data [ books [ [title PHP入门, author 张三], [title MySQL实战, author 李四], ] ] ]; header(Content-Type: application/json; charsetutf-8); echo json_encode($data, JSON_UNESCAPED_UNICODE);注意JSON_UNESCAPED_UNICODE这个参数。不加它中文会被转成\u5f20\u4e09形式虽然前端也能处理但肉眼调试非常痛苦。“接口返回数组对象”这类搜索词本质是在问json_encode时关联数组默认变成 JSON 对象索引数组默认变成 JSON 数组。如果你不想让空数组变成[]可以用JSON_FORCE_OBJECT具体看前端约定。5.2 跨域问题与 JSONP前后端分离项目中前端站点在http://localhost:3000后端接口在http://localhost:8080直接 AJAX 请求就会被浏览器同源策略拦截。搜索词里的“php跨域jsonp”就是在描述这个场景。JSONP 是老方案原理是动态加载脚本标签只能支持 GET 请求。后端输出一段 JavaScript 调用?php // 文件路径jsonp.php $callback $_GET[callback] ?? callback; $data [code 0, msg ok]; header(Content-Type: application/javascript; charsetutf-8); echo $callback . ( . json_encode($data, JSON_UNESCAPED_UNICODE) . );前端function callback(res) { console.log(res); } const script document.createElement(script); script.src http://localhost:8080/jsonp.php?callbackcallback; document.body.appendChild(script);现在更推荐的做法是后端启用 CORS 响应头?php header(Access-Control-Allow-Origin: *); header(Access-Control-Allow-Methods: GET, POST, OPTIONS); header(Access-Control-Allow-Headers: Content-Type, Authorization); if ($_SERVER[REQUEST_METHOD] OPTIONS) { http_response_code(204); exit; }但要注意*意味着任何网站都能请求你的接口。在真实项目中需要把Access-Control-Allow-Origin配置成允许的域名列表而不是无脑放开。5.3 前端秘钥加密的误区搜索词里“php 前端 秘钥加密”其实是一个典型误区。前端代码运行在用户浏览器里前端存放的任何秘钥本质上都等于公开。所谓“前端秘钥加密”只能提高破解门槛不能提供真正的安全边界。真正需要保密的信息必须放在后端。常见的做法是后端生成一个临时签名前端在请求时携带签名后端验签。一个简单的 HMAC 签名流程?php // 后端生成签名示意 $secret your-app-secret; $params [uid 1001, ts time()]; $sign hash_hmac(sha256, json_encode($params), $secret);不过 P1 阶段不需要掌握完整的开放平台签名体系你只需要建立正确的安全观念秘钥不要放在前端涉及钱、账户、权限的操作必须走后端校验。6. 序列化与反序列化理解机制比记住结果重要PHP 的serialize()和unserialize()常用于缓存、Session、接口数据传递。搜索词里“php反序列化”“php序列化中文”热度很高说明这个点困扰了很多人。6.1 序列化是干什么的序列化是把内存中的变量转成可存储、可传输的字符串格式。例如?php $data [name 忍者, level 1]; $str serialize($data); echo $str;输出类似a:2:{s:4:name;s:6:忍者;s:5:level;i:1;}反序列化就是用unserialize()把这个字符串恢复成数组或对象?php $arr unserialize($str); print_r($arr);中文在序列化结果里显示为原始中文字节所以搜索词里才有人说“序列化中文”有问题。只要存储介质和读取进程都使用 UTF-8基本不会出现乱码。如果乱码优先排查数据库连接字符集和文件编码。6.2 反序列化的安全边界unserialize()在接收到不可信的输入时可能触发对象自动加载和魔术方法调用进而造成代码执行或数据操纵。这不是 P1 必须深入攻击链的领域但你至少要知道两条防御规则不要对用户提交内容直接调用unserialize()。如果必须反序列化考虑使用 JSON 替代json_decode()在安全性上比unserialize()可控得多。搜索词里的“php框架漏洞靶场”“ctfshow web入门 php特性”这些内容往往是把这些安全特性当作 CTF 游戏题目来学习的。对 P1 工程师来说正确态度是知道有这类风险写代码时守住边界不主动制造风险。7. PHP 常见问题与排查思路不管准备得多充分开发过程中一定会遇到问题。下面的表格是我整理的 P1 高频问题排查清单。问题现象可能原因排查方式解决方案启动 PHP 服务后页面空白代码中有语法错误且display_errors关闭命令行执行php -l 文件.php检查语法打开display_errors或查看错误日志php -v报 dyld/lib 找不到PHP 二进制文件依赖库路径失效which php再查看依赖重装 PHP 或用集成环境切换版本POST 数据接收不到提交表单未设置name或请求编码不对打印var_dump($_POST)确认表单字段有name属性中文乱码数据库连接字符集不对执行SHOW VARIABLES LIKE character_set%PDO DSN 中指定charsetutf8mb4跨域请求被拦截后端未设置 CORS 头浏览器 Network 面板查看响应头后端加上Access-Control-Allow-Origin文件上传失败上传目录权限不足或超过upload_max_filesize查看php.ini配置和错误信息调整目录权限修改upload_max_filesize页面报 500 错误Nginx/PHP-FPM 通信失败查看 Nginx 错误日志和 PHP 日志确认fastcgi_pass指向正确地址使用比较哈希值通过弱类型比较发生类型转换检查比较运算符改为这些问题的共性排查思路是先看错误日志再查配置最后怀疑代码。很多新手一上来就改代码其实有一半问题出在运行环境上。P1 阶段培养“日志驱动排查”的意识会省掉大量时间。8. 最佳实践与工程建议最后这部分不是面试题但比面试题更重要。P1 和高级工程师之间除了技术深度最大的差距就是工程习惯。以下建议你可以直接用到项目里。8.1 安全底线的四条铁律第一所有数据库操作使用 PDO 预处理禁止拼接 SQL。这是 P1 阶段最容易守住的底线。第二所有输出到 HTML 的内容做转义。在页面显示用户输入时用htmlspecialchars()处理。第三文件上传使用白名单校验扩展名和 MIME 类型文件存储路径与执行目录隔离禁用上传目录的 PHP 执行权限。不要只判断文件后缀因为在某些中间件配置下php、php5、phtml都可能被执行。第四不要在生产环境开启display_errors。日常开发可以开但生产环境应该把错误记录到日志文件避免把路径、SQL 结构等敏感信息暴露给用户。8.2 项目结构从第一天就分层哪怕是图书管理系统也不要在一个index.php里写完所有逻辑。建议至少拆分成project/ ├── public/ # Web 根目录入口文件 │ └── index.php ├── includes/ # 公共函数、数据库连接 ├── src/ # 核心类与业务逻辑 ├── config/ # 配置文件 ├── storage/ # 上传文件、日志 └── vendor/ # Composer 依赖把入口文件放在public/下避免用户直接访问到代码文件。这是一个非常简单的调整但对安全性提升很大。8.3 学会使用 Composer 和 PHPStorm搜索词里出现“phpstorm配置php环境”“vscode php插件”说明很多新手在纠结编辑器。编辑器本身不影响技术能力但有几个配置值得先做好确定命令行 PHP 版本和 IDE 配置的 PHP 版本一致。安装 PHP 的语法检查和代码格式化插件。Composer 是 PHP 的依赖管理工具P1 阶段至少要会composer install和composer require。一个项目里有composer.json时不要手动去下载依赖包直接执行composer install如果是初次创建项目并引入依赖composer require phpunit/phpunit --dev8.4 日志、异常与错误处理P1 阶段就要建立“三层错误处理”意识第一层开发环境开启错误显示。第二层生产环境关闭错误显示但打开日志记录。第三层对可能抛出异常的代码比如数据库操作使用try/catch包裹并记录异常信息。一个简单的异常处理片段?php try { $pdo Database::connection(); $stmt $pdo-prepare(SELECT * FROM book WHERE id :id); $stmt-execute([:id 1]); $book $stmt-fetch(); } catch (PDOException $e) { error_log($e-getMessage()); exit(系统繁忙请稍后再试); }不要把$e-getMessage()直接回显给用户数据库连接信息可能藏在错误消息里。8.5 从前端拿到的数据永远默认不可信不管参数来自$_GET、$_POST还是$_COOKIE都要先校验再使用。校验包括类型是否正确、取值范围是否合法、长度是否超限。例如?php $id $_GET[id] ?? 0; $id filter_var($id, FILTER_VALIDATE_INT); if ($id false || $id 0) { http_response_code(400); exit(参数错误); }这样做的好处是既能防止一部分注入攻击也能让你的代码逻辑更早发现数据异常而不是在 SQL 执行到一半时爆炸。9. 总结与后续学习方向写到这里这份“忍者必须死3 PHP工程师P1无伤攻略”其实已经把地图的基本机制讲完了。你可以看到P1 级别的核心不是背下一堆函数而是做到三件事第一环境能稳定跑起来第二CRUD 链路完整且安全第三PHP 特性坑不踩、踩了知道怎么解。弱类型比较、数组键名自动转换、isset/empty/is_null的边界、PDO 预处理、JSON 跨域、反序列化风险这些就是新手地图上最密集的“雷区”。如果你现在正处于准备面试、刚进公司需要快速上手、或者带新项目但基础不牢的状态建议按顺序做三个练习第一个练习用 PHP 完成一个图书管理系统的增删改查强制自己使用 PDO 预处理和htmlspecialchars转义。第二个练习写一个小接口返回 JSON处理跨域请求然后从前端用fetch请求它。第三个练习把本地项目通过 Docker 跑起来体验一次从代码到容器启动的完整流程。下一步你可以继续深入三个方面面向对象把类和对象关系弄熟悉理解命名空间与自动加载。PHP-FPM 与 Nginx了解请求是如何从 Nginx 走到 PHP 进程的。常见框架Laravel 或 ThinkPHP 任选一个看看框架如何封装请求、数据库和模板。P1 只是起点不是终点。游戏里无伤通关不是因为你躲掉了所有攻击而是因为你知道攻击从哪里来。工程能力也一样提前把概念和边界弄清楚后面补坑的成本就能降到最低。希望这份攻略能帮你在 PHP 修炼之路上的每一次“跑酷”都打个漂亮的全胜。
返回列表