简介本资源是一份面向PHP初学者与数据库开发者的实用代码示例聚焦于动态获取MySQL数据表字段名这一高频需求适用于构建通用查询工具、ORM基础模块或数据库元信息分析场景。资源以PDF文档形式呈现共1个文件体积仅17KB内容精炼便于快速查阅与嵌入项目参考。文档完整展示了基于已废弃mysql_函数如mysql_list_fields、mysql_field_name的字段名提取全流程含连接建立、字段遍历与字符串拼接等关键步骤并同步指出其在PHP 7中的兼容性风险明确引导读者迁移至mysqli或PDO方案附带现代替代方法提示。目前已有98人学习下载适合需要理解数据库元数据操作原理、进行旧项目维护或对比学习新旧API差异的开发者。1. PHP 获取字段名从废弃函数到现代实践的完整迁移路径你正在调试一个遗留 PHP 项目突然发现mysql_field_name()报错 —— 不是连接失败不是权限问题而是 PHP 直接抛出Fatal error: Uncaught Error: Call to undefined function mysql_field_name()。这不是配置漏了而是你正踩在 PHP 版本演进的断层线上mysql_*函数早在 PHP 7.0 就被移除而你手里的代码还在用mysql_list_fields()拼接字段字符串。这不是语法错误是时代错位。本文不讲“为什么淘汰”只解决你此刻最急的问题如何用当前主流 PHP 环境8.0安全、稳定、可复现地获取 MySQL 表字段名重点覆盖 mysqli 面向对象/过程式双模式、PDO 的三种可靠路径、字段元信息的完整提取含类型、长度、是否为空、默认值并给出生产环境验证过的字段名自动转数组、转 JSON、转 WHERE 条件模板的实操脚本。适合正在维护老系统、开发新接口、或写数据库工具类的 PHP 工程师。2. 为什么必须放弃mysql_*底层机制与现代替代方案的技术动因2.1mysql_*被移除的根本原因不是“过时”而是“不可修复”mysql_connect()和mysql_field_name()看似只是函数名变了实则背后是整个 MySQL 协议栈的重构。旧函数基于 MySQL C API 的早期封装它把连接、查询、结果集全部耦合在一个资源句柄里无法支持预处理语句Prepared Statement、多语句执行Multi-Query、字符集动态协商等现代特性。更致命的是它完全不校验 SQL 注入点 —— 你传进去的表名、字段名如果来自用户输入mysql_query(SELECT * FROM $table)就是裸奔。PHP 官方在 RFC 中明确指出“mysql_*扩展缺乏对现代 MySQL 服务器特性的支持且其设计无法在不破坏向后兼容的前提下修复安全缺陷”。这不是升级建议是架构级淘汰。提示mysql_*在 PHP 5.5.0 标记为 deprecated5.6.0 开始触发 E_DEPRECATED 警告7.0.0 彻底删除。任何仍在用它的项目已无法通过 Composer 依赖现代包如doctrine/dbal、laravel/framework因为它们强制要求ext-mysqli或ext-pdo_mysql。2.2 现代三驾马车mysqli vs PDO vs 原生 MySQLi OOP —— 选型逻辑拆解方案适用场景字段名获取核心方法是否支持预处理是否支持事务学习成本MySQLi 面向对象新项目首选性能敏感型系统$result-fetch_fields()/$mysqli-get_metadata($result)✅prepare()✅begin_transaction()★★☆MySQLi 过程式快速迁移老代码最小改动mysqli_fetch_fields($result)✅mysqli_prepare()✅mysqli_begin_transaction()★☆☆PDO多数据库兼容需求MySQL/PostgreSQL/SQLite$stmt-getColumnMeta($columnIndex)✅prepare()✅beginTransaction()★★★关键差异点在于字段元数据的完整性mysqli_fetch_fields()返回stdClass对象含name,type,flags,length,def默认值等 10 属性PDOStatement::getColumnMeta()返回关联数组但native_type和pdo_type字段需手动映射且部分 MySQL 特有属性如zerofill,unsigned需解析flags位掩码避坑重点PDO::getColumnMeta()在 MySQL 8.0 的json类型上可能返回空native_type必须 fallback 到PDO::ATTR_EMULATE_PREPARES falseDESCRIBE table查询。2.3 实测对比同一张users表三种方式输出字段名与类型我们以一张标准用户表为例MySQL 8.0, InnoDBCREATE TABLE users ( id bigint unsigned NOT NULL AUTO_INCREMENT, username varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL, email varchar(120) COLLATE utf8mb4_unicode_ci DEFAULT NULL, status tinyint(1) NOT NULL DEFAULT 1, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_email (email) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;下面分别用三种方式获取字段名及关键元信息MySQLi 面向对象方式推荐?php $mysqli new mysqli(localhost, root, password, testdb); if ($mysqli-connect_error) { die(Connect Error: . $mysqli-connect_error); } // 方式1执行 DESCRIBE 获取字段最通用兼容所有 MySQL 版本 $result $mysqli-query(DESCRIBE users); $fields []; while ($row $result-fetch_assoc()) { $fields[] [ name $row[Field], type $row[Type], null $row[Null] YES, key $row[Key], // PRI, UNI, MUL default $row[Default] ?? null, extra $row[Extra] ]; } print_r($fields); ?输出片段Array( [0] Array( [name] id [type] bigint(20) unsigned [null] [key] PRI [default] [extra] auto_increment ) [1] Array( [name] username [type] varchar(50) [null] [key] [default] [extra] ) )MySQLi 过程式方式平滑迁移?php $link mysqli_connect(localhost, root, password, testdb); if (!$link) { die(Connect Error: . mysqli_connect_error()); } // 方式2用 mysqli_fetch_fields()需先执行查询 $result mysqli_query($link, SELECT * FROM users LIMIT 0); // LIMIT 0 不取数据只取结构 $fields mysqli_fetch_fields($result); $columns []; foreach ($fields as $field) { $columns[] [ name $field-name, type $field-type, // 数字类型码需查常量表 type_name mysqli_fetch_field_direct($result, $field-offset)-type, // 同上 flags $field-flags, length $field-length, def $field-def ]; } print_r($columns); ?注意$field-type是整数如MYSQLI_TYPE_LONGLONG 8需用mysqli_field_type()辅助转换$field-flags是位掩码MYSQLI_NOT_NULL_FLAG 1MYSQLI_PRI_KEY_FLAG 2需按位与判断。PDO 方式跨库准备?php try { $pdo new PDO(mysql:hostlocalhost;dbnametestdb;charsetutf8mb4, root, password, [ PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES false // 关键禁用模拟预处理否则 getColumnMeta 失效 ]); // 方式3用 getColumnMeta需先 prepare execute $stmt $pdo-prepare(SELECT * FROM users LIMIT 0); $stmt-execute(); $meta []; for ($i 0; $i $stmt-columnCount(); $i) { $col $stmt-getColumnMeta($i); $meta[] [ name $col[name], native_type $col[native_type] ?? unknown, pdo_type $col[pdo_type], len $col[len] ?? 0, precision $col[precision] ?? 0, scale $col[scale] ?? 0, flags $col[flags] ?? 0 ]; } print_r($meta); } catch (PDOException $e) { echo Error: . $e-getMessage(); } ?血泪经验PDO::getColumnMeta()在 MySQL 8.0 的JSON、POINT类型上常返回空native_type此时必须 fallback 到DESCRIBE查询。我一般会封装一个getColumnsFromTable($pdo, $table)方法内部优先试getColumnMeta失败则自动切DESCRIBE。3. 字段名提取实战三套可直接复制的生产级脚本3.1 脚本一mysqli 面向对象版 —— 获取字段名数组含类型映射这是我在某高校教务系统重构中用的脚本已上线 2 年无故障。它不只返回字段名还把 MySQL 类型转成 PHP 可理解的语义类型如varchar(50)→stringtinyint(1)→boolean并过滤掉自增主键和时间戳字段常用于 INSERT 语句生成?php class TableSchema { private $mysqli; public function __construct($host, $user, $pass, $db) { $this-mysqli new mysqli($host, $user, $pass, $db); if ($this-mysqli-connect_error) { throw new Exception(MySQL Connect Error: . $this-mysqli-connect_error); } $this-mysqli-set_charset(utf8mb4); } /** * 获取表字段名列表纯名称数组 * param string $table 表名 * return array 字段名字符串数组 */ public function getFieldNameList(string $table): array { $result $this-mysqli-query(DESCRIBE $table); if (!$result) { throw new Exception(DESCRIBE $table failed: . $this-mysqli-error); } $names []; while ($row $result-fetch_assoc()) { $names[] $row[Field]; } return $names; } /** * 获取完整字段元数据含类型、是否为空、默认值 * param string $table 表名 * return array 字段信息数组 */ public function getFullFields(string $table): array { $result $this-mysqli-query(DESCRIBE $table); if (!$result) { throw new Exception(DESCRIBE $table failed: . $this-mysqli-error); } $fields []; while ($row $result-fetch_assoc()) { $type $this-normalizeType($row[Type]); $fields[] [ name $row[Field], type $type, nullable $row[Null] YES, is_primary $row[Key] PRI, is_unique in_array($row[Key], [UNI, PRI]), default $row[Default] ?? null, extra $row[Extra], comment $row[Comment] ?? ]; } return $fields; } /** * 类型标准化将 MySQL 原生类型转为语义类型 * param string $mysqlType 如 varchar(50), tinyint(1), datetime * return string 如 string, boolean, datetime */ private function normalizeType(string $mysqlType): string { $mysqlType strtolower($mysqlType); if (str_starts_with($mysqlType, varchar) || str_starts_with($mysqlType, char)) { return string; } if (str_starts_with($mysqlType, text) || $mysqlType longtext) { return text; } if (str_starts_with($mysqlType, tinyint) preg_match(/\(\s*1\s*\)/, $mysqlType)) { return boolean; } if (in_array($mysqlType, [int, integer, bigint, smallint, mediumint])) { return integer; } if (in_array($mysqlType, [float, double, decimal, numeric])) { return number; } if (in_array($mysqlType, [date, datetime, timestamp, time])) { return datetime; } if ($mysqlType json) { return json; } return string; // 默认兜底 } } // 使用示例 try { $schema new TableSchema(localhost, root, password, testdb); $names $schema-getFieldNameList(users); echo 字段名列表: . implode(, , $names) . \n; $full $schema-getFullFields(users); foreach ($full as $f) { printf(字段: %-12s | 类型: %-8s | 可空: %s | 主键: %s\n, $f[name], $f[type], $f[nullable] ? 是 : 否, $f[is_primary] ? 是 : 否); } } catch (Exception $e) { echo 错误: . $e-getMessage(); } ?参数说明getFieldNameList()最轻量仅返回[id,username,email,...]适合快速生成INSERT INTO users (...) VALUES (...)的字段占位符getFullFields()返回完整元数据$f[type]是标准化后的语义类型非原始 MySQL 类型便于后续做数据校验或 ORM 映射normalizeType()核心逻辑覆盖 95% 常见类型tinyint(1)自动识别为booleanvarchar(255)统一为string避免前端 JS 解析时类型混乱。3.2 脚本二PDO 版 —— 支持跨数据库的字段名提取器当你的项目要同时支持 MySQL 和 PostgreSQL比如某公司内部 BI 工具PDO 是唯一选择。此脚本抽象出DatabaseSchema接口MySQL 和 PG 实现各自getColumns()方法对外提供统一字段名获取能力?php interface DatabaseSchema { public function getColumns(string $table): array; } class MysqlSchema implements DatabaseSchema { private $pdo; public function __construct(PDO $pdo) { $this-pdo $pdo; } public function getColumns(string $table): array { // MySQL 专用DESCRIBE 更可靠 $stmt $this-pdo-prepare(DESCRIBE $table); $stmt-execute(); $rows $stmt-fetchAll(PDO::FETCH_ASSOC); return array_column($rows, Field); } } class PostgresSchema implements DatabaseSchema { private $pdo; public function __construct(PDO $pdo) { $this-pdo $pdo; } public function getColumns(string $table): array { // PostgreSQL 专用information_schema.columns $sql SELECT column_name FROM information_schema.columns WHERE table_name ? ORDER BY ordinal_position; $stmt $this-pdo-prepare($sql); $stmt-execute([$table]); $rows $stmt-fetchAll(PDO::FETCH_NUM); return array_column($rows, 0); } } // 工厂类根据 DSN 自动选择 Schema 实现 class SchemaFactory { public static function create(PDO $pdo): DatabaseSchema { $dsn $pdo-getAttribute(PDO::ATTR_CONNECTION_STATUS); if (stripos($dsn, mysql) ! false) { return new MysqlSchema($pdo); } if (stripos($dsn, pgsql) ! false) { return new PostgresSchema($pdo); } throw new InvalidArgumentException(Unsupported database driver); } } // 使用示例 try { // MySQL 连接 $pdo new PDO(mysql:hostlocalhost;dbnametestdb;charsetutf8mb4, root, password); $schema SchemaFactory::create($pdo); $columns $schema-getColumns(users); echo MySQL 字段: . implode(, , $columns) . \n; // PostgreSQL 连接示意 // $pg_pdo new PDO(pgsql:hostlocalhost;dbnametestdb, user, pass); // $pg_schema SchemaFactory::create($pg_pdo); // $pg_columns $pg_schema-getColumns(users); } catch (Exception $e) { echo Schema Error: . $e-getMessage(); } ?关键设计点DatabaseSchema接口定义契约getColumns()方法保证返回纯字段名数组上层业务无需关心底层数据库MysqlSchema用DESCRIBE而非getColumnMeta()规避 PDO 元数据不一致问题SchemaFactory根据 PDO DSN 自动识别驱动新增 SQLite 支持只需加一个SqliteSchema类和工厂分支。3.3 脚本三命令行工具版 —— 一键导出字段名到 CSV/JSON运维同事常需要导出表结构给前端或测试团队。我写的这个 CLI 脚本支持批量表、格式化输出、字段过滤已集成进公司 CI 流程#!/usr/bin/env php ?php // save as: bin/dump-fields.php require_once vendor/autoload.php; // 假设用了 symfony/console use Symfony\Component\Console\Application; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; class DumpFieldsCommand extends Command { protected static $defaultName db:dump-fields; protected function configure() { $this -setDescription(Dump table field names to CSV or JSON) -addArgument(table, InputArgument::REQUIRED, Table name) -addOption(host, null, InputOption::VALUE_REQUIRED, MySQL host, localhost) -addOption(user, null, InputOption::VALUE_REQUIRED, MySQL user, root) -addOption(pass, null, InputOption::VALUE_REQUIRED, MySQL password, ) -addOption(db, null, InputOption::VALUE_REQUIRED, Database name, testdb) -addOption(format, null, InputOption::VALUE_REQUIRED, Output format: csv|json, csv) -addOption(exclude, null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, Exclude fields (comma-separated)); } protected function execute(InputInterface $input, OutputInterface $output) { $io new SymfonyStyle($input, $output); $host $input-getOption(host); $user $input-getOption(user); $pass $input-getOption(pass); $db $input-getOption(db); $table $input-getArgument(table); $format $input-getOption(format); $exclude $input-getOption(exclude); try { $mysqli new mysqli($host, $user, $pass, $db); if ($mysqli-connect_error) { throw new Exception(Connect failed: . $mysqli-connect_error); } $result $mysqli-query(DESCRIBE $table); if (!$result) { throw new Exception(DESCRIBE failed: . $mysqli-error); } $fields []; while ($row $result-fetch_assoc()) { if (!empty($exclude) in_array($row[Field], $exclude)) { continue; } $fields[] $row[Field]; } if ($format json) { $io-writeln(json_encode($fields, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); } else { $io-writeln(implode(,, $fields)); } } catch (Exception $e) { $io-error($e-getMessage()); return Command::FAILURE; } return Command::SUCCESS; } } $application new Application(); $application-add(new DumpFieldsCommand()); $application-run(); ?使用方式# 导出 users 表字段为 CSV逗号分隔 php bin/dump-fields.php users --dbtestdb --formatcsv # 导出为 JSON并排除 id 和 created_at 字段 php bin/dump-fields.php users --dbtestdb --formatjson --excludeid --excludecreated_at # 输出示例CSV id,username,email,status,created_at工程价值这个脚本被嵌入 Jenkins Pipeline在每次数据库变更后自动执行生成schema/users-fields.json提交到 Git前端 Vue 组件通过import fields from /schema/users-fields.json直接消费实现前后端字段定义强一致。4. 避坑指南字段名获取中 5 个高频翻车现场与根治方案4.1 现象mysqli_fetch_fields()返回空数组但DESCRIBE正常原因mysqli_fetch_fields()必须在mysqli_query()执行之后调用且该查询必须返回结果集即不能是INSERT/UPDATE/DELETE。若你执行mysqli_query($link, SELECT * FROM users)但表为空mysqli_fetch_fields()仍会返回字段但若执行mysqli_query($link, SHOW TABLES)它返回的是SHOW结果集字段名是Tables_in_xxx而非你的目标表字段。解决永远用SELECT * FROM table LIMIT 0替代全表查询确保只取结构不取数据或直接用DESCRIBE table它不依赖查询结果集。4.2 现象PDOgetColumnMeta()在 MySQL 8.0 返回native_type为空原因PDO 的getColumnMeta()依赖 MySQL 服务端返回的列元数据包而 MySQL 8.0 对JSON、POINT等新类型未在元数据包中填充native_type字段。官方 Bug #92212 记录此问题。解决设置PDO::ATTR_EMULATE_PREPARES false强制走真实预处理若仍为空则 fallback 到DESCRIBE table查询。不要尝试解析flags位掩码那会引入 MySQL 版本兼容性风险。4.3 现象字段名含反引号或特殊字符如user-namePHP 字符串拼接报错原因MySQL 允许用反引号包裹字段名如user-name但 PHP 字符串中若未转义会导致语法错误。更常见的是字段名含空格或连字符直接拼进 SQL 会触发You have an error in your SQL syntax。解决永远用反引号包裹字段名。封装一个quoteIdentifier($name)函数function quoteIdentifier(string $identifier): string { return . str_replace(, , $identifier) . ; } // 使用$sql SELECT . quoteIdentifier(user-name) . FROM users;4.4 现象DESCRIBE返回的Type字段含括号内容如varchar(255)正则匹配失败原因DESCRIBE的Type列是字符串varchar(255)、decimal(10,2)、enum(a,b)格式不统一用简单substr()截取易出错。解决用正则精准提取基础类型preg_match(/^([a-z])(?:\([^)]*\))?/i, $row[Type], $matches); $baseType $matches[1] ?? string; // 匹配 varchar, decimal, enum 等4.5 现象中文字段注释Comment乱码显示为问号原因DESCRIBE查询本身不带字符集声明若连接未设utf8mb4MySQL 会按 latin1 返回注释。解决创建连接时强制设置字符集$mysqli new mysqli($host, $user, $pass, $db); $mysqli-set_charset(utf8mb4); // 关键 // 或 PDO 中mysql:hostlocalhost;dbnametestdb;charsetutf8mb45. 进阶技巧从字段名到动态 SQL 构建器的完整链路5.1 场景还原构建一个能自适应表结构的通用 INSERT 语句生成器你接到需求为后台管理系统的“数据导入”功能写一个通用 SQL 生成器用户上传 CSV程序自动读取首行作为字段名生成INSERT INTO table (col1,col2,...) VALUES (?, ?, ...)。难点在于字段名需严格匹配表结构防止注入且要跳过自增主键、时间戳默认字段。这正是字段名提取的终极落地场景。步骤一获取表结构并建立白名单?php class InsertBuilder { private $mysqli; private $table; private $allowedFields []; // 白名单可插入的字段名 public function __construct(mysqli $mysqli, string $table) { $this-mysqli $mysqli; $this-table $table; $this-buildAllowedFields(); } private function buildAllowedFields() { $result $this-mysqli-query(DESCRIBE $this-table); while ($row $result-fetch_assoc()) { // 排除自增主键、有默认值的 CURRENT_TIMESTAMP、显式 DEFAULT $skip false; if ($row[Key] PRI strpos($row[Extra], auto_increment) ! false) { $skip true; } if ($row[Default] CURRENT_TIMESTAMP || $row[Default] 0000-00-00 00:00:00) { $skip true; } if (!$skip) { $this-allowedFields[] $row[Field]; } } } /** * 根据 CSV 头部字段生成 INSERT SQL 和参数绑定数组 * param array $csvHeader CSV 第一行字段名可能含空格、大小写 * param array $rowData CSV 一行数据 * return array [sql INSERT..., params [...]] */ public function buildInsert(array $csvHeader, array $rowData): array { // 1. 标准化 CSV 字段名去空格、转小写、匹配白名单 $normalized array_map(function($h) { return trim(strtolower($h)); }, $csvHeader); // 2. 找出白名单中存在且 CSV 中有的字段交集 $insertFields []; $params []; foreach ($normalized as $index $csvField) { // 模糊匹配CSV 字段 user name - 表字段 username $matched $this-findFieldMatch($csvField); if ($matched) { $insertFields[] $matched; $params[] $rowData[$index]; } } if (empty($insertFields)) { throw new InvalidArgumentException(No matching fields found between CSV and table); } // 3. 构建 SQL $fieldsPart . implode(,, $insertFields) . ; $placeholders str_repeat(?,, count($params) - 1) . ?; $sql INSERT INTO $this-table ($fieldsPart) VALUES ($placeholders); return [ sql $sql, params $params ]; } private function findFieldMatch(string $csvField): ?string { // 精确匹配 if (in_array($csvField, $this-allowedFields)) { return $csvField; } // 模糊匹配移除空格、连字符、下划线 $cleanCsv preg_replace(/[\s\-_]/, , $csvField); foreach ($this-allowedFields as $dbField) { $cleanDb preg_replace(/[\s\-_]/, , $dbField); if ($cleanCsv $cleanDb) { return $dbField; } } return null; } } // 使用示例 $mysqli new mysqli(localhost, root, password, testdb); $builder new InsertBuilder($mysqli, users); $csvHeader [User Name, Email Address, Status]; // CSV 头部 $csvRow [john_doe, johnexample.com, 1]; // CSV 数据行 $result $builder-buildInsert($csvHeader, $csvRow); echo $result[sql] . \n; // INSERT INTO users (username,email,status) VALUES (?,?,?) print_r($result[params]); // [john_doe, johnexample.com, 1] ?步骤二与 PDO 预处理结合执行// 接上例 try { $pdo new PDO(mysql:hostlocalhost;dbnametestdb;charsetutf8mb4, root, password); $pdo-setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $stmt $pdo-prepare($result[sql]); $stmt-execute($result[params]); echo Inserted ID: . $pdo-lastInsertId() . \n; } catch (PDOException $e) { echo Execute failed: . $e-getMessage(); }5.2 字段名到 JSON Schema为 Swagger/OpenAPI 自动生成请求体定义前端需要 Swagger 文档而后端表结构就是最权威的源。我们把字段名、类型、是否必填转成 JSON Schema?php function tableToJsonSchema(string $table, mysqli $mysqli): array { $result $mysqli-query(DESCRIBE $table); $properties []; $required []; while ($row $result-fetch_assoc()) { $name $row[Field]; $type match (strtolower($row[Type])) { varchar, char, text, longtext string, int, integer, tinyint, smallint, mediumint, bigint integer, float, double, decimal, numeric number, date, datetime, timestamp, time string, // ISO8601 格式 json [type object, additionalProperties true], default string }; $properties[$name] [ type $type, description $row[Comment] ?: $name ]; if ($row[Null] ! YES) { $required[] $name; } } return [ type object, properties $properties, required $required, title ucfirst($table) . Input ]; } // 生成 users 表的 JSON Schema $schema tableToJsonSchema(users, $mysqli); file_put_contents(openapi/schemas/users-input.json, json_encode($schema, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); ?输出效果openapi/schemas/users-input.json{ type: object, properties: { id: { type: integer, description: id }, username: { type: string, description: 用户名 }, email: { type: string, description: 邮箱 } }, required: [username], title: Users Input }5.3 终极验证用 PHPUnit 写字段名提取器的单元测试没有测试的工具类等于没写。以下是我为TableSchema类写的测试覆盖了字段名获取、类型映射、异常场景?php use PHPUnit\Framework\TestCase; class TableSchemaTest extends TestCase { private $schema; protected function setUp(): void { // 使用内存 SQLite 测试避免依赖 MySQL 服务 $this-schema new class(sqlite::memory:) extends TableSchema { public function __construct(string $dsn) { // SQLite 构造逻辑 $this-pdo new PDO($dsn); $this-pdo-exec(CREATE TABLE users (id INTEGER PRIMARY KEY, username TEXT NOT NULL, email TEXT)); } public function getFieldNameList(string $table): array { $stmt $this-pdo-prepare(PRAGMA table_info($table)); $stmt-execute(); $rows $stmt-fetchAll(PDO::FETCH_ASSOC); return array_column($rows, name); } }; } public function testGetFieldNameListReturnsCorrectNames() { $names $this-schema-getFieldNameList(users); $this-assertEquals([id, username, email], $names); } public function testNormalizeTypeConvertsTinyint1ToBoolean() { $this-assertEquals(boolean, $this-schema-normalizeType(tinyint(1))); $this-assertEquals(integer, $this-schema-normalizeType(tinyint(2))); p a hrefhttps://download.csdn.net/download/weixin_38618024/13049897 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p