ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

phpMyAdmin 导出插件(Export Plugin)开发完全指南:从 README 模板到源码级实现

phpMyAdmin 导出插件(Export Plugin)开发完全指南:从 README 模板到源码级实现 数据库后端【免费下载链接】phpmyadminA web interface for MySQL and MariaDB项目地址https://gitcode.com/gh_mirrors/ph/phpmyadmin点击查看免费下载本指南以 phpMyAdmin 官方仓库中 src/Plugins/Export/README.md 为骨架完整讲解如何为 phpMyAdmin 编写自定义导出插件从类结构、属性properties与选项options定义、生命周期钩子方法到插件发现与依赖注入机制并结合仓库中 JSON、CSV、SQL 等官方插件的真实实现与单元测试帮助你写出第一个可用的自定义导出格式插件。一、导出插件体系概览插件是什么、放在哪里phpMyAdmin 的导出功能采用插件Plugin架构每种导出格式SQL、CSV、JSON、XML、YAML、PDF 等都是一个独立的类统一继承抽象基类PhpMyAdmin\Plugins\ExportPlugin定义于 src/Plugins/ExportPlugin.php并放在src/Plugins/Export/目录下。从当前仓库的目录结构可以确认官方插件包括ExportCodegen.phpCodeGen、ExportCsv.phpCSV、ExportExcel.phpExcel、ExportHtmlword.php、ExportJson.phpJSON、ExportLatex.php、ExportMediawiki.php、ExportOds.php、ExportOdt.php、ExportPdf.php、ExportPhparray.php、ExportSql.php、ExportTexytext.php、ExportToon.php、ExportXml.php、ExportYaml.php以及公共辅助类 src/Plugins/Export/Helpers/Pdf.php 与 src/Plugins/Export/Helpers/TableProperty.php。插件是如何被发现的插件不需要手工注册。从 src/Plugins.php 的源码可以看到发现机制Plugins::getPlugins(Export)使用FilesystemIterator扫描src/Plugins/Export/目录只挑选文件名以Export开头、扩展名为.php的文件拼接出类名PhpMyAdmin\Plugins\Export\ExportXxx校验类存在、是ExportPlugin的子类且isAvailable()返回true后通过容器实例化并注入依赖最后按getProperties()-getText()插件显示名不区分大小写排序。因此开发新插件的注册动作只有两步写一个继承ExportPlugin的类 把文件以Export*命名放进src/Plugins/Export/目录。这也是 README 模板要求任何新插件follow the structure presented here的根本原因。二、插件类骨架README 官方模板完整解析src/Plugins/Export/README.md 给出了一个插件类的基本模板。它的核心要点包括类名形如Export[Name]继承PhpMyAdmin\Plugins\ExportPlugin构造函数中调用setProperties()完成属性初始化通过可选的私有变量、initSpecificVariables()、getter/setter 维护插件内部状态实现exportHeader()、exportFooter()、exportDBHeader()、exportDBFooter()、exportDBCreate()、exportData()等生命周期方法可按需实现exportRoutines()、exportStructure()、getTableDefStandIn()、getTriggers()等可选方法。官方模板的完整骨架如下注释已精简结构未变?php /** * [Name] export plugin for phpMyAdmin */ declare(strict_types1); /** * Handles the export for the [Name] format */ class Export[Name] extends PhpMyAdmin\Plugins\ExportPlugin { /** * optional - declare variables and descriptions * * var VariableType */ private $myOptionalVariable; /** * optional - declare global variables and descriptions * * var VariableType */ private $globalVariableName; /** * Constructor */ public function __construct() { $this-setProperties(); } // optional - declare global variables and use getters later /** * Initialize the local variables that are used specific for export SQL * * return void * * global VariableType $global_variable_name * [..] */ protected function initSpecificVariables() { global $global_variable_name; $this-setGlobalVariableName($global_variable_name); } /** * Sets the export plugin properties. * Called in the constructor. * * return void */ protected function setProperties() { $exportPluginProperties new PhpMyAdmin\Properties\Plugins\ExportPluginProperties(); $exportPluginProperties-setText([name]); // the name of your plug-in $exportPluginProperties-setExtension([ext]); // extension this plug-in can handle // create the root group that will be the options field for // $exportPluginProperties // this will be shown as Format specific options $exportSpecificOptions new PhpMyAdmin\Properties\Options\Groups\OptionsPropertyRootGroup( Format Specific Options ); // general options main group $generalOptions new PhpMyAdmin\Properties\Options\Groups\OptionsPropertyMainGroup( general_opts ); // optional : // create primary items and add them to the group // type - one of the classes listed in libraries/properties/options/items/ // name - form element name // text - description in GUI // size - size of text element // len - maximal size of input // values - possible values of the item $leaf new PhpMyAdmin\Properties\Options\Items\RadioPropertyItem( structure_or_data ); $leaf-setValues( [ structure __(structure), data __(data), structure_and_data __(structure and data), ] ); $generalOptions-addProperty($leaf); // add the main group to the root group $exportSpecificOptions-addProperty($generalOptions); // set the options for the export plugin property item $exportPluginProperties-setOptions($exportSpecificOptions); $this-properties $exportPluginProperties; } /** * Outputs export header * * return bool Whether it succeeded */ public function exportHeader() { // implementation return true; } /** * Outputs export footer * * return bool Whether it succeeded */ public function exportFooter() { // implementation return true; } /** * Outputs database header * * param string $db Database name * param string $dbAlias Aliases of db * * return bool Whether it succeeded */ public function exportDBHeader($db, $dbAlias ) { // implementation return true; } /** * Outputs database footer * * param string $db Database name * * return bool Whether it succeeded */ public function exportDBFooter($db) { // implementation return true; } /** * Outputs CREATE DATABASE statement * * param string $db Database name * param string $dbAlias Aliases of db * * return bool Whether it succeeded */ public function exportDBCreate($db, $dbAlias ) { // implementation return true; } /** * Outputs the content of a table in [Name] format * * param string $db database name * param string $table table name * param string $sqlQuery SQL query for obtaining data * param array $aliases Aliases of db/table/columns * * return bool Whether it succeeded */ public function exportData( $db, $table, $sqlQuery, array $aliases [] ) { // implementation; return true; } // optional - implement other methods defined in PhpMyAdmin\Plugins\ExportPlugin.php: // - exportRoutines() // - exportStructure() // - getTableDefStandIn() // - getTriggers() // optional - implement other private methods in order to avoid // having huge methods or avoid duplicate code. Make use of them // as well as of the getters and setters declared both here // and in the PhpMyAdmin\Plugins\ExportPlugin class // optional: /* ~~~~~~~~~~~~~~~~~~~~ Getters and Setters ~~~~~~~~~~~~~~~~~~~~ */ /** * Getter description */ private function getMyOptionalVariable(): VariableType { return $this-myOptionalVariable; } /** * Setter description */ private function setMyOptionalVariable(VariableType $my_optional_variable): void { $this-myOptionalVariable $my_optional_variable; } /** * Getter description */ private function getGlobalVariableName(): VariableType { return $this-globalVariableName; } /** * Setter description */ private function setGlobalVariableName(VariableType $global_variable_name): void { $this-globalVariableName $global_variable_name; } }与当前仓库实现的差异说明README 模板是教学用的简化骨架当前仓库的 ExportPlugin.php 已经演进为更严格的现代 PHP 实现差异点包括命名空间与严格类型所有类位于PhpMyAdmin\Plugins\Export命名空间文件开头必须declare(strict_types1);构造器注入取代全局变量抽象基类的构造器src/Plugins/ExportPlugin.php以protected readonly属性注入Relation、OutputHandler、Transformations、DatabaseInterface、Config五个依赖模板中的global $global_variable_name写法在新代码中已被依赖注入取代构造器为final基类构造器是final public function __construct(...)子类不允许再定义自己的构造器初始化逻辑应放入setProperties()属性与setExportOptions()运行期选项方法返回void模板中生命周期方法返回bool新基类中这些方法返回void子类实现也不再返回布尔值必须实现的抽象方法新基类中setProperties()、exportData()、setExportOptions(ServerRequest, Export)是抽象方法src/Plugins/ExportPlugin.php、src/Plugins/ExportPlugin.php、src/Plugins/ExportPlugin.php任何新插件都必须实现。官方插件与个人插件的国际化差异README 明确说明官方插件Official plugins需要将str*消息定义在语言文件中而自用插件可以直接在插件里写文本。当前仓库的官方插件正是如此例如 ExportCsv.php 中所有选项文本都用__(Columns separated with:)这类可翻译函数包裹而自用插件可直接传入普通字符串。基类还提供了getTranslatedText()src/Plugins/ExportPlugin.php作为文本翻译钩子当前默认实现直接返回原文子类可覆盖它实现自定义翻译逻辑。三、属性系统setProperties() 与选项树详解setProperties()是每个插件的元数据注册表它组装一个ExportPluginProperties对象并赋给$this-properties。核心信息分为两层。插件级属性ExportPluginPropertiesPhpMyAdmin\Properties\Plugins\ExportPluginPropertiessrc/Properties/Plugins/ExportPluginProperties.php继承自PluginPropertyItemsrc/Properties/Plugins/PluginPropertyItem.php提供方法含义示例值setText()插件在导出格式下拉框中的显示名JSON、CSVsetExtension()导出文件的扩展名json、csvsetMimeType()导出文件的 MIME 类型application/json、text/comma-separated-valuessetOptions()格式专属选项的根组OptionsPropertyRootGroup实例setForceFile()是否强制将结果保存为文件默认false选项树的三层结构选项采用根组 → 主组 → 叶子项的三层树结构根组OptionsPropertyRootGroup(Format Specific Options)在导出界面中对应 Format specific options格式专属选项区块主组OptionsPropertyMainGroup(xxx_general_opts)将相关选项分组组名通常是插件名前缀 _general_opts叶子项具体的表单控件位于src/Properties/Options/Items/目录当前仓库提供 7 种叶子类对应表单主要用途RadioPropertyItem单选按钮如结构/数据/结构数据选择通过setValues()设置键值对TextPropertyItem文本框如 CSV 的分隔符、括字符支持setSize()、setLen()BoolPropertyItem复选框布尔开关选项HiddenPropertyItem隐藏字段承载structure_or_data等透传参数NumberPropertyItem数字输入数值型选项SelectPropertyItem下拉框枚举选项MessageOnlyPropertyItem纯文本仅展示说明文字模板注释中提到的type / name / text / size / len / values六个维度分别对应叶子类的类名、表单元素名getName()、GUI 描述getText()、文本框宽度getSize()、输入最大长度getLen()以及可选值getValues()。这些属性最终由各叶子类的getHtml()方法渲染成表单 HTML——例如 TextPropertyItem.php 会输出带text_前缀 id、size/maxlength属性的input默认值取自Plugins::getDefault()。对照真实插件ExportCsv 的 setProperties()以 ExportCsv.php 为例它注册了完整的选项集$exportPluginProperties-setText(CSV); $exportPluginProperties-setExtension(csv); $exportPluginProperties-setMimeType(text/comma-separated-values); $exportSpecificOptions new OptionsPropertyRootGroup(Format Specific Options); $generalOptions new OptionsPropertyMainGroup(csv_general_opts); $generalOptions-addProperty(new TextPropertyItem(csv_separator, __(Columns separated with:))); $generalOptions-addProperty(new TextPropertyItem(csv_enclosed, __(Columns enclosed with:))); $generalOptions-addProperty(new TextPropertyItem(csv_escaped, __(Columns escaped with:))); $generalOptions-addProperty(new TextPropertyItem(csv_terminated, __(Lines terminated with:))); $generalOptions-addProperty(new TextPropertyItem(csv_null, __(Replace NULL with:))); $generalOptions-addProperty(new BoolPropertyItem(csv_removeCRLF, __(Remove carriage return/line feed characters within columns))); $generalOptions-addProperty(new BoolPropertyItem(csv_columns, __(Put columns names in the first row))); $generalOptions-addProperty(new HiddenPropertyItem(csv_structure_or_data)); $exportSpecificOptions-addProperty($generalOptions); $exportPluginProperties-setOptions($exportSpecificOptions); return $exportPluginProperties;单元测试 tests/unit/Plugins/Export/ExportCsvTest.php 通过反射验证了这一结构properties必须是ExportPluginProperties实例getText()为CSV、getExtension()为csv、getMimeType()为text/comma-separated-values根组名必须是Format Specific Options主组名必须是csv_general_opts且第一个叶子项必须是名为csv_separator的TextPropertyItem。写自定义插件后完全可以仿照这套测试为属性结构补上同样的断言。四、生命周期钩子导出过程中被调用的方法phpMyAdmin 的导出引擎按阶段调用插件的方法基类src/Plugins/ExportPlugin.php给出了全部默认实现多为空实现插件按需覆盖。调用顺序与职责如下阶段方法职责导出开始exportHeader()输出文件头如 JSON 的[、SQL 的版本注释导出结束exportFooter()输出文件尾如 JSON 的]每个数据库exportDBHeader($db, $dbAlias)库级头如 JSON 的 database 对象每个数据库exportDBFooter($db)库级尾创建库exportDBCreate($db, $dbAlias)输出CREATE DATABASE语句每张表exportData($db, $table, $sqlQuery, $aliases)抽象方法必须实现输出表数据可选exportRoutines($db, $aliases)导出存储过程/函数可选exportEvents($db)导出事件可选exportRawQuery($db, $sqlQuery)导出原始查询结果可选exportStructure($db, $table, $exportMode, $aliases)导出表结构$exportMode取create_table、triggers、create_view、stand_in之一可选exportMetadata($db, $tables, $metadataTypes)导出配置存储中的元数据可选getTableDefStandIn($db, $view, $aliases)返回视图的占位 CREATE 定义以解决依赖返回字符串模板注释中点名的exportRoutines()、exportStructure()、getTableDefStandIn()、getTriggers()正是这些可选方法当前基类中触发器导出已并入exportStructure()的triggers模式。绝大多数插件只需实现exportData()一个抽象方法即可工作其余方法都有空默认实现。输出方式OutputHandler所有输出通过注入的OutputHandlersrc/Export/OutputHandler.php完成核心方法是addLine()——它负责内容缓冲、gzip 压缩等细节。以 ExportJson.php 为例public function exportHeader(): void { $data $this-encode([ type header, version Version::VERSION, comment Export to JSON plugin for phpMyAdmin, ]); if ($data false) { throw new ExportException(Failure during header export.); } $this-outputHandler-addLine([ . \n . $data . , . \n); } public function exportFooter(): void { $this-outputHandler-addLine(] . \n); }exportData() 实战流式查询与二进制处理exportData()是插件的核心。以 ExportJson.php 的doExportForQuery()为例可以看到标准写法用$this-dbi-query($sqlQuery, ConnectionType::User, DatabaseInterface::QUERY_UNBUFFERED)执行非缓冲查询避免大数据量时内存溢出用$this-dbi-getFieldsMeta($result)获取字段元数据配合别名数组把列名替换为别名逐行fetchRow()遇到GIS 类型、BLOB、二进制字符串charsetnr 63时转为0x十六进制输出用$this-outputHandler-addLine()逐行写出控制数组元素之间的逗号分隔。这段实现也印证了模板中$aliases参数的用途——它携带 db/table/column 的重命名映射。五、运行期选项读取setExportOptions() 与 structure_or_data模板把结构/数据/结构数据的单选项RadioPropertyItem(structure_or_data)作为示范选项。现代实现中structure_or_data这个通用选项由基类统一处理基类维护protected StructureOrData $structureOrDatasrc/Plugins/ExportPlugin.php默认StructureOrData::DatasetStructureOrData()src/Plugins/ExportPlugin.php按请求参数 → 配置值 → 默认值的优先级解析该选项枚举定义见 src/Export/StructureOrData.phpincludeStructure()/includeData()src/Plugins/ExportPlugin.php供导出引擎判断当前模式是否包含结构或数据。每个插件还必须实现抽象方法setExportOptions(ServerRequest $request, Export $exportConfig)把请求表单与配置PhpMyAdmin\Config\Settings\Export中的值读入插件状态。例如 ExportJson.phppublic function setExportOptions(ServerRequest $request, Export $exportConfig): void { $this-structureOrData $this-setStructureOrData( $request-getParsedBodyParam(json_structure_or_data), $exportConfig-json_structure_or_data, StructureOrData::Data, ); $this-prettyPrint $request-hasBodyParam(json_pretty_print); $this-unicode $request-hasBodyParam(json_unicode); }这些选项随后在编码时生效encode()src/Plugins/Export/ExportJson.php根据prettyPrint/unicode状态叠加JSON_PRETTY_PRINT/JSON_UNESCAPED_UNICODE标志位。同理ExportCsv.php 的setupExportConfiguration()会把\r、\n、\t转义序列解析为真实控制字符并把AUTO行终止符替换为\n。六、别名机制导出时重命名库、表、列模板中exportData()的$aliases参数以及exportDBHeader($db, $dbAlias)的$dbAlias参数背后是基类内置的别名工具方法src/Plugins/ExportPlugin.phpgetDbAlias($aliases, $db)取库别名无则返回原名getTableAlias($aliases, $db, $table)取表别名getColumnAlias($aliases, $db, $table, $column)取列别名getAlias($aliases, $id)在完整别名树db → tables → columns中递归搜索任意标识符的别名getRelationString($foreigners, $fieldName, $db, $aliases)结合外键信息通过注入的Relation调用searchColumnInForeigners()生成[外键表] ([外键列])的关联字符串供导出注释使用。别名数组的结构约定为[ db_name [ alias db_alias, tables [ table_name [ alias table_alias, columns [col_name col_alias], ], ], ], ]七、把插件从模板变成现实对照官方实现与测试综合上述机制一个可运行的现代插件需要满足文件命名为ExportMyformat.php置于src/Plugins/Export/目录扫描要求文件名以Export开头见 src/Plugins.php类位于PhpMyAdmin\Plugins\Export命名空间declare(strict_types1);继承ExportPlugin实现抽象方法setProperties()返回ExportPluginProperties、exportData()返回void、setExportOptions(ServerRequest, Export)按需覆盖exportHeader()、exportFooter()、exportDBHeader()、exportDBFooter()、exportDBCreate()与可选的exportRoutines()、exportStructure()、exportRawQuery()、exportMetadata()、getTableDefStandIn()通过$this-outputHandler-addLine()输出内容通过注入的$this-dbi执行查询使用getDbAlias()/getTableAlias()/getColumnAlias()处理别名编写与 tests/unit/Plugins/Export/ExportCsvTest.php 类似的单元测试用反射断言setProperties()产出的属性树text、extension、mimeType、根组名、主组名、叶子项类型与 name并用setExportOptions()验证请求参数到插件状态的映射。对照仓库内体量最大的 ExportSql.php约 2600 行可以看到一个生产级插件会在setProperties()中注册大量选项插入语法complete|extended|both|none、兼容模式NONE|ANSI|DB2|MAXDB|MYSQL323|MYSQL40|MSSQL|ORACLE|TRADITIONAL、是否十六进制输出二进制、是否 UTC 时间等在setExportOptions()中逐项读取然后在各钩子方法中组合输出——其整体结构与 README 模板完全同构只是规模更大。八、开发与验收清单放置位置src/Plugins/Export/ExportXxx.php文件名必须以Export开头且为.php否则不会被Plugins::getPlugins()扫描到src/Plugins.php必须实现setProperties()、exportData()、setExportOptions()三个抽象方法src/Plugins/ExportPlugin.php可选增强文件头/尾、库头/尾、建库语句、存储过程/事件/结构/原始查询导出输出纪律一律经由OutputHandler-addLine()不要直接echo否则会破坏缓冲与压缩流程大数据量查询使用DatabaseInterface::QUERY_UNBUFFERED非缓冲模式并逐行取数参考 ExportJson.php二进制与 GISBLOB/二进制字符串/GIS 字段应转成0x十六进制charsetnr 63判断见 ExportJson.php国际化官方插件用__()包裹文案并同步语言文件自用插件可直接写死文本测试参考 tests/unit/Plugins/Export/ExportCsvTest.php用反射断言属性树与选项读取逻辑。按照这套流程你完全可以从 README 模板出发对照官方 JSON/CSV 插件的实现快速产出一个结构合规、可被 phpMyAdmin 自动发现并出现在导出页格式列表中的自定义导出格式插件。赞分享数据库后端【免费下载链接】phpmyadminA web interface for MySQL and MariaDB项目地址https://gitcode.com/gh_mirrors/ph/phpmyadmin点击查看免费下载相关推荐X6 画布导出插件 Export 完全指南SVG / PNG / JPEG 导出 API 与源码级原理X6 画布导出插件 Export 完全指南SVG / PNG / JPEG 导出 API 与源码级原理 X6 是使用 SVG 与 HTML 进行渲染的 Jav前端图形学tsParticles 视频导出插件tsparticles/plugin-export-video完全指南从安装、录制参数到引擎导出原理tsParticles 视频导出插件tsparticles/plugin export video完全指南从安装、录制参数到引擎导出原理 tsParti前端Ente CLI 导出命令 ente export 完全指南从参数解析到源码级导出流程Ente CLI 导出命令 ente export 完全指南从参数解析到源码级导出流程 Ente 是一个端到端加密的云服务照片、Locker 与 Auth后端前端移动开发桌面应用密码学认证鉴权存储上一篇yudaocode/yudao-cloudKafka高吞吐消息处理架构深度解析下一篇终极Stack-on-a-Budget扩展指南5个简单步骤添加自定义服务类别创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表