人工智能
用 Kiro 构建可重复的 PHP 到 Java 迁移引擎当你的团队将一个成熟的 PHP 应用迁移到 Java 时,你将获得更强的类型安全性、更广泛的库支持以及更可预测的扩展能力。多年来用 Laravel、Symfony 或 Doctrine 构建的 PHP 代码库,会在一些并不显眼的地方承载业务规则——从 Eloquent trait 到 XML 映射文件——因此,一个结构上相似的 Java 移植版本可能编译得很干净,但行为仍然不同。
本文展示如何利用 Kiro将 PHP 到 Java 的迁移从一次性的盲目冒险,转变为可度量、可重复的过程。Kiro 是一个 AI 集成开发环境(IDE),它将 AI 代理与你的代码库配对,让你在环参与多步骤工程任务。该方法的核心是一个可复用的引擎:一套共享方法论、一个机械化的发现扫描器,以及一个八阶段工作流。该引擎将原始 PHP 应用视为规范,并且在 parity harness 验证通过之前,拒绝宣布 Java 移植版本正确。
前提条件
在开始之前,你需要:
- 熟悉 PHP(Laravel/Eloquent 或 Symfony/Doctrine)。Java 知识(Spring Boot、JPA)有助于你审查生成的输出,但引擎会为你生成地道的 Java,因此你不需要自己用 Java 重写应用。
- 一个可访问 Skills 和 Steering 文件的 Kiro 工作区。
- 运行发现扫描器的机器上安装 PHP 8.0 或更高版本,以及用于生成的 Spring Boot 项目的 Java 21 和 Maven。
- 对迁移规划(发现、翻译和验证)有基本理解。
本文你将学到什么
你从一个开放的代码仓库开始,因此可以自己运行引擎,而不是只阅读相关内容。引擎所知道的一切都存在于一小组 skill 文件中,借助这些文件,你可以用不止一种方式运行同一套方法论。
运行引擎的两种方式:Kiro 和 AWS Transform
这里的一切都打包为 skill 文件:带版本控制的 Markdown 能力包,它们编码了迁移方法论(php-to-java-migration)和发现扫描器工作流(php-model-discovery)。由于这些知识存在于文件中,而不是任何单一工具中,你可以用两种方式运行同一套方法论。
方法 1:Kiro,交互式且开发者参与其中
Kiro 将 AI 代理带入你的编辑器,逐步驱动迁移。将仓库根目录作为 Kiro 工作区打开,当你要求 Kiro 迁移一个 PHP 应用时,这些 skill 会自动激活。随后 Kiro 会带着明确的审批关卡走完八阶段工作流,因此你可以审查发现清单、批准规则手册,并在下一阶段开始前签核每个阶段。本文中的每个示例都使用这种模式。
方法 2:AWS Transform,代理式且面向组合规模
AWS Transform 是 AWS 用于大规模迁移和现代化的代理式 AI 服务,它现在与 Kiro 原生集成。通过该集成,你可以将管理交互式 Kiro 会话的同一批 skill 文件交给 AWS Transform,因此“先发现后翻译”、作为裁判的 parity harness,以及“修复循环,而不是修复代码”都适用于自主运行。
每种路径的适用场景
- 控制与自主性: 先用 Kiro 处理代码:在一个应用上运行 skill,阅读生成的 Java,并运行测试。当你想迁移更多应用时,AWS Transform 会以代理方式运行同一套方法论,并在 skill 定义的检查点进行审查。
- 规模与新颖性: Kiro 一次处理一个应用,适合首次出现的框架。AWS Transform 以并行任务运行许多应用。
- 共享反馈循环: 两条路径读取相同的 skill 文件,因此在 Kiro 中做出的每一次规则手册修复都会改进下一次 AWS Transform 运行,反之亦然。
解决方案概览:一个共享大脑,多个项目
该引擎围绕一个原则组织:一个共享大脑,多个项目。要打开它,请在本地克隆仓库,然后在 Kiro 中打开一个文件夹并选择仓库根目录(包含 .kiro/ 的文件夹)。Kiro 会自动加载 skill 和 steering,更完整的步骤见“入门”。
如下图所示,工作区将共享方法论层与使用它的各个项目分开。顶部是每个项目都会使用的全局 .kiro/ 层。其下是各项目文件夹,每个文件夹都有自己的 PHP 源代码、Java 输出和规范子文件夹。
图 1:工作区分为共享的全局方法论层,以及各项目的代码和规范文件夹
如下图所示,这些相同的共享 skill 文件可以用两种不同方式运行。相同的八阶段、parity 优先流水线既可以由 Kiro 驱动(交互式,一次一个应用),也可以由 AWS Transform 驱动(代理式,组合规模),两者都会将 PHP 源代码转换为经 parity 验证的 Spring Boot Java。
图 2:相同的共享 skill 文件驱动两条执行路径:Kiro(交互式,一次一个应用)和 AWS Transform(代理式,组合规模)。两者都运行相同的八阶段 parity 优先流水线,将 PHP 源代码转换为经 parity 验证的 Spring Boot Java
在 .kiro/ 下有一个共享的全局层,包含两个 Skill:php-model-discovery(扫描器)和 php-to-java-migration(八阶段操作手册),以及 core-workflow.md Steering 文件。每个项目文件夹都在 .kiro/specs/ 下拥有自己的 PHP 源代码、Java 输出和规范子文件夹,因此改进一次方法论,每个项目都能继承这一收益。
先发现后翻译
最重要的单条规则会在其他一切之前生效:在扫描器运行之前,不要读取、猜测或手工翻译模型或实体文件。
如下图所示,扫描器以单次遍历、无依赖的方式工作。它对源 token 应用三种发现策略,并输出一个 JSON 清单,任何有歧义的内容都会进入审查标记队列,由人工解决。
图 3:扫描器对源 token 运行三种发现策略,并输出一个带有审查标记队列的单一 JSON 清单
扫描器是 discover-models.php,一个单文件、无依赖的 PHP 命令行工具,它不会执行目标代码。它只把源文本作为 token 读取,因此无论依赖是否安装,它都能针对你选择的应用运行。它在每次扫描中运行三种策略:Eloquent 模型发现(沿类继承链向上追溯到 Illuminate\Database\Eloquent\Model)、Doctrine 属性检测(#[ORM\Entity] 属性)以及 ORM XML 映射(外部 *.orm.xml 文件)。输出是一个单一的 JSON 清单。一个 models[] 数组承载每个字段、类型转换、关系和可见性规则,并且每个条目上都会出现一个 flags[] 数组。扫描器会标记歧义,而不是猜测。例如,由运行时表达式构建的 $fillable 列表会变成一条用平实英语表述的标记,而该标记列表就是人工审查队列。
parity 优先流水线
阶段 0 是准备阶段:它运行扫描器并汇总清单。接下来的三个阶段则集中投入人类专业知识。阶段 1 在编写 Java 之前构建 parity harness,针对 PHP 原版和 Java 移植版重放请求/响应配对。该 harness 必须在可正常工作的 PHP 上通过,并在你故意破坏它时失败,因为漏掉注入式破坏的 harness 是不可信的。阶段 2 播种规则手册和差距清单(RULEBOOK.md、GAP_INVENTORY.md),阶段 3 在提交到整个应用之前,用代表性文件对其进行压力测试。
图 4:人类判断被前置到准备性的阶段 0 和阶段 1–3。阶段 4–8 主要是自我驱动的循环,由 parity harness 担任裁判
前四个阶段集中投入人类专业知识。阶段 0 运行扫描器并汇总清单。阶段 1 在编写 Java 之前构建 parity harness,针对 PHP 原版和 Java 移植版重放请求/响应配对。该 harness 必须在可正常工作的 PHP 上通过,并在你故意破坏它时失败,因为漏掉注入式破坏的 harness 是不可信的。阶段 2 播种规则手册和差距清单(RULEBOOK.md、GAP_INVENTORY.md),阶段 3 在提交到整个应用之前,用代表性文件对其进行压力测试。
| 阶段 | 名称 | 交付物 |
| 0 | 评估与发现 | 模型清单 + 清单汇总 + 切换选择 |
| 1 | 构建裁判 | 可在 PHP 和 Java 上运行的 parity harness |
| 2 | 规则手册 + 差距清单 | RULEBOOK.md、GAP_INVENTORY.md |
| 3 | 对规则手册进行压力测试 | 经过加固的规则手册,已用代表性文件验证 |
| 4 | 脚手架 | 可编译的 Spring Boot 项目 |
| 5 | 翻译数据模型 | JPA 实体 + schema 基线 |
| 6 | 翻译 I/O 层 | 控制器、安全、模板可响应 |
| 7 | 转换业务逻辑 | 逻辑已转换,测试通过 |
| 8 | 证明 parity 并切换 | parity 报告 + 切换检查清单 |
阶段 4–8 主要是自我驱动的循环:阶段 4 搭建项目脚手架,阶段 5 根据清单翻译数据模型,阶段 6 翻译 I/O 层(路由变成控制器,中间件变成过滤器,验证变成带注解的数据传输对象),阶段 7 分批转换业务逻辑,阶段 8 针对 harness 证明 parity 并切换。两条原则支配着这个循环。第一,PHP 代码就是规范:当裁判同意时,移植才算完成,或者每个不匹配都被记录为已批准的变更。第二,你要修复的是循环,而不是代码,即修订规则手册并重新生成,而不是手工打补丁。
演练:从 Eloquent 到 Spring Boot
laravel-eloquent-sample 项目是一个可运行、有边界的示例:一个开源 Laravel 应用的只读切片,其中 Book、Chapter、Page 和 User 模型已迁移到基于 Java 21 的 Spring Boot 3.4.1,使用 Maven 和内存 H2 数据库。
从 PHP 到 Spring Boot 的映射是机械且可追溯的,如下图所示。左侧的每个清单条目都映射到右侧的一个 Java 构造,从而生成应用切片的层化运行时架构。
图 5:左侧是由清单驱动的映射。右侧是生成的应用切片的层化运行时架构
这些映射是机械的,并且可以追溯到清单:PHP 命名空间变成 Java 包,Eloquent 模型变成 JPA @Entity,has Many 和 belongs to 关系变成 @OneToMany 和 @ManyToOne,Eloquent 查询作用域变成 Spring Data 仓库方法。这种纪律体现在序列化上:html、markdown 和 text 带有 @JsonIgnore,因为清单将它们记录在 Page 的 $hidden 列表中,而 Book 的 description 则有意不这样做。
规范坦率地指出,37 个模型中只有 4 个被移植,而且最重要的是,这个切片从未构建 parity harness,它称之为“最大的单一差距”。记录其不足之处本身就是设计的一部分。
入门
完整的迁移引擎可在 示例 PHP 到 Java 迁移仓库.
要使用它:
- 克隆仓库并将其根目录作为你的 Kiro 工作区打开(打开一个文件夹并选择包含 .kiro/ 的文件夹)。除了用于扫描器的 PHP 8.0+ 以及带 Maven 的 Java 21 之外,无需安装任何东西。
- 将 PHP 源代码获取到项目的 source-php/ 文件夹中。README 固定了用于 parity 的确切上游提交,以便任何人都能重现基线。
- 让 Kiro 迁移应用。php-to-java-migration skill 会自动激活,先运行扫描器,在 .kiro/specs/ 下生成新的规范,并引导你完成审批关卡。
- 对于代理式运行,将 AWS Transform 指向相同的 skill 文件,这样它的代理就能在你的组合范围内执行相同的方法论。
该方法论会自动检测 Laravel、Symfony、CodeIgniter 和纯 PHP,并且扩展是增量的:为新框架添加一行检测配置和一个 references/<framework>-to-spring.md 指南。由于 Kiro 和 AWS Transform 读取相同的文件,改进它们一次就能同时升级两个引擎。
清理
如果你在本次演练期间部署了示例 Spring Boot 应用:
- 停止正在运行的 Spring Boot 应用(内存中的 H2 数据库是临时的,无需单独清理)。
- 如果不再需要,请从工作区中删除生成的 Java 项目文件夹。
- 发现扫描器只生成一个 JSON 清单文件。如果你不打算引用发现扫描器的 JSON 清单,请从工作目录中删除它。
- 迁移引擎本身不会预置任何 AWS 资源,因此没有需要取消预置的云资源。
结论
我们展示了如何用 Kiro 构建一个可重复的 PHP 到 Java 迁移引擎,将大规模重写转变为一个可度量、可累积的过程。关键原则如下:
- 先发现,后翻译: 机械扫描器会清点每个模型、字段和关系,因此你无需猜测结构。
- 先构建判定器,再写代码: 一致性校验框架在编写第一行 Java 代码之前就定义了 完成 的标准。
- 修复循环,而非代码: 当同一个错误在多个文件中出现时,应修改规则手册并重新生成,而不是手工修补。
- 一个共享大脑: 方法论只编写一次,在各项目间共享,并随着每次迁移不断改进。
这些内容并非 PHP 或 Java 所特有。同样的形态适用于其他迁移场景,即旧系统是唯一完整的规范。克隆 示例 PHP 到 Java 迁移仓库, 将其作为 Kiro 工作区打开,并探索为其提供支持的 Skills 和 Steering 能力,或者将相同的 skill 文件带到 AWS Transform。有关 AWS 上语言迁移模式的更多信息,请参阅 AWS 规范性指导 文档。