跨版本PHP代码转换终极教程

自学咖网努力为各位打造免费分享知识与教程网站

理想情况下,我们所有的网站都应该使用PHP8.0(撰写本文时的最新版本),并且在新版本发布后立即更新。然而,开发人员通常需要使用以前的PHP版本,例如在为WordPress创建公共插件或使用阻碍升级Web服务器环境的遗留代码时。

在这种情况下,我们可能会放弃使用最新PHP代码的希望。但是有一个更好的选择:我们还是可以用PHP8.0写源代码,转换成之前的PHP版本,甚至是PHP7.1。

在本指南中,我们将教你如何转换PHP代码。

什么是运输?

传输PHP的优势

PHP转换器(PHP传输器)

转换成哪个PHP版本?

传输与反向传输

传输PHP示例

PHP转换的优缺点

如何转换PHP

优化转换过程

转换代码时要避免的陷阱

以及转换和持续集成。

测试转换代码

什么是运输?

Transpiling将源代码从一种编程语言转换成相同或不同编程语言的等效源代码。

Transpiling在Web开发中并不是一个新概念:客户端开发人员可能很熟悉Babel,它是JavaScript代码的转换器。

Babel将现代ECMAScript 2015+版本中的JavaScript代码转换为兼容旧浏览器的旧版本。例如,给定ES2015箭头功能:

[2, 4, 6].map((n) => n * 2);

…Babel将其转换为ES5版本:

[2, 4, 6].map(function(n) { return n * 2;});

什么是Transpiling PHP?

Web开发中一个潜在的新功能是转换服务器端代码的可能性,尤其是PHP。

PHP转换的工作方式与JavaScript转换相同:现代PHP版本的源代码被转换成旧PHP版本的等价代码。

下面是和之前一样的例子,PHP 7.4中的arrow函数:

$nums = array_map(fn($n) => $n * 2, [2, 4, 6]);

…可以转换成相当的PHP 7.3版:

$nums = array_map( function ($n) { return $n * 2;   }, [2, 4, 6]);

可以转换箭头函数,因为它们是语法糖,也就是生成现有行为的新语法。这是一种下垂的水果。

然而,有一些新的特性创造了一种新的行为,所以对于以前版本的PHP来说,没有等价的代码。这是PHP 8.0中引入的联合类型:

function someFunction(float|int $param): string|float|int|null{  // …}

在这些情况下,只要开发需要新功能,而不是生产需要新功能,转换仍然可以进行。然后,我们可以简单地从转换后的代码中完全删除这个特性,而不会产生严重的后果。

联合就是这样一个例子。此函数用于检查输入类型与其提供的值之间是否存在不匹配,这有助于防止错误。如果与类型有冲突,那么开发中就会出现错误,我们应该在代码到达生产环境之前捕捉并修复它。

因此,我们可以从生产代码中删除这个函数:

function someFunction($param){  // …}

如果在生产中仍然出现错误,则抛出的错误消息不会像使用联合类型时那样准确。但是,这种潜在的缺点可以通过首先使用联合类型来弥补。

传输PHP的优势

Transpiling使您能够使用最新版本的PHP编写应用程序,并生成一个在旧版本PHP运行的环境中也能工作的版本。

这对于为遗留内容管理系统(CMS)创建产品的开发人员尤其有用。比如WordPress官方仍然支持PHP5.6(虽然推荐PHP7.4+)。WordPress网站运行PHP 5.6到7.2版本的百分比为34.8%,而运行PHP版本(8.0除外)的网站百分比高达99.5%:

自学咖网努力为各位打造免费分享知识与教程网站

图片来源:WordPress

因此,面向全球受众的WordPress主题和插件很可能使用旧版本的PHP进行编码,以增加其可能的影响范围。多亏了transpiling,这些代码可以用PHP8.0编码,并且仍然可以针对更老的PHP版本发布,从而面对尽可能多的用户。

事实上,任何需要支持除最新版本之外的任何PHP版本的应用程序(即使在当前支持的PHP版本内)都可以从中受益。

Drupal就是这样。它需要PHP7.3。由于transpiling,开发人员可以用PHP8.0创建公共可用的Drupal模块,并用PHP7.3发布它们。

另一个例子是为由于某种原因不能在其环境中运行PHP8.0的客户机创建定制代码。尽管如此,由于transpiling,开发人员仍然可以使用PHP8.0来编写可交付成果,并在这些遗留环境中运行它们。

什么时候需要转换PHP(Transpile PHP)

PHP代码总是可以转换的,除非它包含一些在以前的PHP版本中不等价的PHP函数。

可能就是这种情况。PHP 8.0中引入了这个属性:

#[SomeAttr]function someFunc() {}#[AnotherAttr]class SomeClass {}

在前面使用arrow函数的例子中,代码是可以转换的,因为arrow函数是语法糖。相反,属性创造了全新的行为。PHP 7.4及以下版本也可以复制这种行为,但只能通过手动编码,即不能自动基于工具或流程(AI可以提供解决方案,但我们还没有)。

用于开发的属性,如#[Deprecated],可以像删除联合类型一样删除。但是,您不能删除在生产中修改应用程序行为的属性,也不能直接转换这些属性。

到目前为止,还没有transpiler可以接受带有PHP8.0属性的代码,并自动生成其等价的PHP7.4代码。所以,如果你的PHP代码需要使用属性,转换起来会很困难或者不可行。

可转换的PHP函数

以上是PHP7.1及以上版本的特性,目前可以转换。如果您的代码只使用这些特性,那么您可以确保您的转换应用程序将正常工作。否则,您需要评估转换后的代码是否会导致失败。

PHP 版本特征7.1所有都可以7.2– object type
– parameter type widening
– PREG_UNMATCHED_AS_NULL flag in preg_match7.3– Reference assignments in list() / array destructuring (Except inside foreach — #4376)
– Flexible Heredoc and Nowdoc syntax
– Trailing commas in functions calls
– set(raw)cookie accepts $option argument7.4– Typed properties
– Arrow functions
– Null coalescing assignment operator
– Unpacking inside arrays
– Numeric literal separator
– strip_tags() with array of tag names
– covariant return types and contravariant param types8.0– Union types
– mixed pseudo type
– static return type
– ::class magic constant on objects
– match expressions
– catch exceptions only by type
– Null-safe operator
– Class constructor property promotion
– Trailing commas in parameter lists and closure use lists

PHP转换器(PHP传输器)

目前有一个转换PHP代码的工具:Rector。

Rector是一个PHP重构工具,它根据可编程的规则转换PHP代码。我们输入源代码和要运行的规则集,Rector将转换代码。

Rector是通过Composer通过命令行操作安装在项目中的。执行时,Rector将输出转换前后代码的“差异”(添加为绿色,删除为红色):

自学咖网努力为各位打造免费分享知识与教程网站

来自Rector的“diff”输出

转换成哪个PHP版本?

要跨PHP版本转换代码,必须创建相应的规则。

今天,Rector库包含了从PHP8.0到7.1的大多数代码转换规则。因此,我们可以可靠地将PHP代码转换为7.1版本。

还有从PHP7.1到7.0和从7.0到5.6的转换规则,但这些规则并不详尽。完成这些代码的工作正在进行中,所以我们最终可能会将PHP代码转换为5.6版本。

传输与反向传输

反向移植类似于传输,但更简单。反向移植代码不一定依赖于语言的新特性。相反,只需从新版本语言中复制/粘贴/改编相应的代码,为旧版本语言提供相同的功能。

例如,PHP 8.0中引入了str_contains函数。PHP 7.4及以下版本的相同功能可以像这样轻松实现:

if (!defined(‘PHP_VERSION_ID’) || (defined(‘PHP_VERSION_ID’) && PHP_VERSION_ID < 80000)) { if (!function_exists('str_contains')) { /** * Checks if a string contains another * * @param string $haystack The string to search in * @param string $needle The string to search * @return boolean Returns TRUE if the needle was found in haystack, FALSE otherwise. */ function str_contains(string $haystack, string $needle): bool { return strpos($haystack, $needle) !== false; } }}

因为反向移植比传输简单,所以无论何时进行反向移植,我们都应该选择这种解决方案。

对于PHP8.0和7.1之间的范围,我们可以使用Symfony的polyfill库:

聚合填充PHP 7.1

聚合填充PHP 7.2

聚合填充PHP 7.3

聚合填充PHP 7.4

聚合填充PHP 8.0

这些库支持以下函数、类、常数和接口:

PHP 版本特征7.2功能:

spl对象标识

utf8_encode

utf8 _解码

功能:

PHP_FLOAT_*

PHP _ OS _系列

7.3功能:

array_key_first

array_key_last

hrtime

是_可数的

异常处理:

JsonException

7.4功能:

get _ mangled _ object _ vars

mb_str_split

密码_算法

8.0接口:

Stringable

类别:

值错误

UnhandledMatchError

功能:

过滤器_验证_布尔

功能:

fdiv

获取调试类型

孕_最后_错误_消息

str _包含

字符串开始于

字符串结束于

获取资源标识

传输PHP示例

让我们来看看几个转换PHP代码的例子,以及几个正在被完全转换的包。

PHP代码

匹配表达式是在PHP8.0中引入的。这段源代码:

function getFieldValue(string $fieldName): ?string{ return match($fieldName) { ‘foo’ => ‘foofoo’, ‘bar’ => ‘barbar’, ‘baz’ => ‘bazbaz’, default => null, };}

…将使用switch运算符转换到其等效的PHP版本7.4:

function getFieldValue(string $fieldName): ?string{ switch ($fieldName) { case ‘foo’: return ‘foofoo’; case ‘bar’: return ‘barbar’; case ‘baz’: return ‘bazbaz’; default: return null; }}

PHP 8.0中也引入了nullsafe操作符:

public function getValue(TypeResolverInterface $typeResolver): ?string{ return $this->getResolver($typeResolver)?->getValue();}

转换后的代码需要首先将操作的值赋给新变量,以避免执行操作两次:

public function getValue(TypeResolverInterface $typeResolver): ?string{ return ($val = $this->getResolver($typeResolver)) ? $val->getValue() : null;}

在PHP 8.0中,还引入了构造函数属性提升,允许开发人员编写更少的代码:

class QueryResolver{ function __construct(protected QueryFormatter $queryFormatter) { }}

当转换为PHP7.4时,将生成完整的代码:

class QueryResolver { protected QueryFormatter $queryFormatter; function __construct(QueryFormatter $queryFormatter) { $this->queryFormatter = $queryFormatter; }}

上面转换后的代码包含PHP7.4中引入的类型化属性。将代码向下转换为PHP7.3,并替换为docblocks:

class QueryResolver { /** * @var QueryFormatter */ protected $queryFormatter; function __construct(QueryFormatter $queryFormatter) { $this->queryFormatter = $queryFormatter; }}

PHP包

正在为生产转换以下库:

库/描述代码/注释Rector
PHP重建工具,使转换成为可能–源代码
–已转换代码
–笔记易于编码标准
具有PHP代码的工具遵守一组规则–源代码
–已转换代码
–笔记GraphQL API for WordPress
一个为WordPress提供GraphQL server的插件–源代码
–已转换代码
–笔记

PHP转换的优缺点

PHP转换的好处已经描述过了:它允许源代码使用PHP 8.0(即PHP的最新版本),PHP将被转换为更低的版本,以便在遗留应用程序或环境中运行。

这有效地使我们成为更好的开发人员,并生成更高质量的代码。这是因为我们的源代码可以使用PHP8.0的联合类型,PHP7.4的类型属性,以及添加到每个新版本PHP中的不同类型和伪类型(混合了PHP8.0和PHP7.2的对象),以及PHP的其他现代函数。

使用这些特性,我们可以更好地捕捉开发过程中的错误,并编写更易于阅读的代码。

现在,我们来看看缺点。

必须对其进行编码和维护。

Rector可以自动转换代码,但这个过程可能需要一些手动输入,使其与我们的特定设置一起工作。

第三方库也必须转换。

每当转换它们产生错误时,这就成了一个问题,因为我们必须深入研究它们的源代码,找出可能的原因。如果问题可以解决,项目是开源的,我们将需要提交一个拉请求。如果库不开源,我们可能会遇到障碍。

当代码无法转录时,Rector不会通知我们。

如果源代码包含PHP8.0属性或任何其他无法转换的特性,我们将无法继续。但是,Rector不会检查这个条件,所以我们需要手动检查。这对于我们自己的源代码来说可能不是什么大问题,因为我们已经很熟悉了,但是可能会成为第三方依赖的障碍。

调试信息使用转换代码而不是源代码。

当应用程序在生产中生成带有堆栈跟踪的错误消息时,行号将指向转换后的代码。我们需要将转换后的代码转换回原始代码,以便在源代码中找到对应的行号。

转换后的代码也必须带有前缀。

我们的pile transfer项目和其他一些库也安装在生产环境中,可以使用相同的第三方依赖项。这种第三方依赖将被转移到我们的项目中,其他库的原始源代码将被保留。因此,转录的版本必须通过PHP- Scope、Strauss或其他一些工具来避免潜在的冲突。

我们的Transpile项目和其他一些也安装在生产环境中的库可以使用相同的第三方依赖项。这个第三方依赖项将为我们的项目进行转换,并为其他库保留其原始源代码。所以翻译出来的版本一定要加上PHP Scope,Strauss或者其他工具的前缀,避免潜在的冲突。

持续集成(CI)期间需要转换

因为转换后的代码自然会覆盖源代码,所以我们不应该在开发计算机上运行转换过程,否则我们会冒副作用的风险。在CI运行时运行这个过程更合适(下面将详细描述)。

如何转换PHP(Transpile PHP)

首先,我们需要在开发项目中安装Rector:

composer require rector/rector –dev

然后,我们在项目的根目录下创建一个rector.php配置文件,其中包含了所需的规则集。为了将代码从PHP 8.0降级到7.1,我们使用以下配置:

use RectorSetValueObjectDowngradeSetList;use SymfonyComponentDependencyInjectionLoaderConfiguratorContainerConfigurator;return static function (ContainerConfigurator $containerConfigurator): void { $containerConfigurator->import(DowngradeSetList::PHP_80); $containerConfigurator->import(DowngradeSetList::PHP_74); $containerConfigurator->import(DowngradeSetList::PHP_73); $containerConfigurator->import(DowngradeSetList::PHP_72);};

为了确保进程按预期执行,我们可以在dry模式下运行Rector的process命令,传递要处理的位置(在本例中是src/ folder中的所有文件):

vendor/bin/rector process src –dry-run

为了执行转换,我们运行Rector的process命令,这将修改现有位置中的文件:

vendor/bin/rector process src

请注意:如果我们在开发计算机中运行rector进程,源代码将在src/下转换。但是,我们希望在不同的位置生成转换后的代码,以便在降级代码时不会覆盖源代码。因此,在持续集成期间运行流程是最合适的。

优化转换过程

要为生产生成转换可交付成果,只需为生产转换代码;可以跳过只用于开发的代码。这意味着我们可以避免转换所有的测试(对于我们的项目及其依赖项)和所有的开发依赖项。

关于测试,我们已经知道项目的测试在哪里——例如,在tests/文件夹下。我们还必须找出依赖项在哪里——例如,在它们的子文件夹tests/、test/和Test/(对于不同的库)下。然后,我们告诉Rector跳过处理这些文件夹:

return static function (ContainerConfigurator $containerConfigurator): void { // … $parameters->set(Option::SKIP, [ // Skip tests ‘*/tests/*’, ‘*/test/*’, ‘*/Test/*’, ]);};

关于依赖项,Composer知道哪些用于开发(条目下composer.json中的require-dev),哪些用于生产(条目下的require)。

为了从Composer中检索所有生产依赖项的路径,我们运行:

composer info –path –no-dev

该命令将生成一个依赖项列表,包括它们的名称和路径,如下所示:

brain/cortex /Users/leo/GitHub/leoloso/PoP/vendor/brain/cortexcomposer/installers /Users/leo/GitHub/leoloso/PoP/vendor/composer/installerscomposer/semver /Users/leo/GitHub/leoloso/PoP/vendor/composer/semverguzzlehttp/guzzle /Users/leo/GitHub/leoloso/PoP/vendor/guzzlehttp/guzzleleague/pipeline /Users/leo/GitHub/leoloso/PoP/vendor/league/pipeline

我们可以提取所有路径,并将它们输入到Rector命令中,然后该命令将处理项目的src/文件夹以及包含所有生产依赖项的文件夹:

$ paths=”$(composer info –path –no-dev | cut -d’ ‘ -f2- | sed ‘s/ //g’ | tr ‘n’ ‘ ‘)”$ vendor/bin/rector process src $paths

进一步的改进可以防止Rector处理已经使用目标PHP版本的依赖项。如果一个库是用PHP7.1(或以下任何版本)编写的,那么就没有必要把它转换成PHP7.1。

要做到这一点,我们可以得到一个需要PHP7.2及以上版本的库的列表,只和这些库打交道。我们将通过Composer的why-not命令获得所有这些库的名称,如下所示:

composer why-not php “7.1.*” | grep -o “S*/S*”

由于该命令不适用于-no-dev标志,为了只包括生产依赖项,我们首先需要删除开发依赖项并重新生成自动加载程序,执行该命令,然后再次添加它们:

$ composer install –no-dev$ packages=$(composer why-not php “7.1.*” | grep -o “S*/S*”)$ composer install

Composer的info – path命令以下列格式检索包的路径:

# Executing this command$ composer info psr/cache –path # Produces this response:psr/cache /Users/leo/GitHub/leoloso/PoP/vendor/psr/cache

我们对列表中的所有项目执行该命令,以获取要转换的所有路径:

for package in $packagesdo path=$(composer info $package –path | cut -d’ ‘ -f2-) paths=”$paths $path”done

最后,我们将这个列表提供给Rector(加上项目的src/文件夹):

vendor/bin/rector process src $paths

转换代码时要避免的陷阱

代码转换可以视为一门艺术,通常需要针对项目进行调整。我们来看看可能会遇到的一些问题。

链式规则并不总是被处理。

链式规则是指一个规则需要转换前一个规则生成的代码。

例如,库symfony/cache包含以下代码:

final class CacheItem implements ItemInterface{ public function tag($tags): ItemInterface { // … return $this; }}

从PHP 7.4转换到7.3时,function标签必须修改两次:

由于规则降级covariantreturntypedirector,返回类型ItemInterface必须首先转换为self。

然后,由于规则降级downgradeselftypedeclarationdirector,返回类型self必须被删除。

最终结果应该是:

final class CacheItem implements ItemInterface{ public function tag($tags) { // … return $this; }}

但是,Rector只输出中间阶段:

final class CacheItem implements ItemInterface{ public function tag($tags): self { // … return $this; }}

问题是,Rector并不能总是控制规则的应用顺序。

解决方案是确定哪些链规则是未处理的,并执行新的目录运行来应用它们。

为了识别链规则,我们对源代码运行了两次Rector,如下所示:

$ vendor/bin/rector process src$ vendor/bin/rector process src –dry-run

第一次,我们按预期运行Rector来执行转换。第二次,我们使用- dry-run标志来找出是否需要进行任何更改。如果是这样,该命令将退出并显示一个错误代码,并且“diff”输出将指示哪些规则仍然可以应用。这意味着第一次运行没有完成,一些链规则没有被处理。

自学咖网努力为各位打造免费分享知识与教程网站

使用–-模拟运行标志运行Rector。

一旦我们确定了尚未应用的链规则,我们就可以创建另一个目录概要文件——例如,rector-chained-rule.php将执行缺失的规则。这一次,我们不需要为src/下的所有文件处理一整套规则,但是我们可以对需要应用此规则的特定文件运行特定的缺失规则:

// rector-chained-rule.phpuse RectorCoreConfigurationOption;use RectorDowngradePhp74RectorClassMethodDowngradeSelfTypeDeclarationRector;use SymfonyComponentDependencyInjectionLoaderConfiguratorContainerConfigurator;return static function (ContainerConfigurator $containerConfigurator): void { $services = $containerConfigurator->services(); $services->set(DowngradeSelfTypeDeclarationRector::class); $parameters = $containerConfigurator->parameters(); $parameters->set(Option::PATHS, [ __DIR__ . ‘/vendor/symfony/cache/CacheItem.php’, ]);};

最后,我们通过在第二遍中输入- config来告诉Rector使用新的配置文件:

# First pass with all modifications $ vendor/bin/rector process src # Second pass to fix a specific problem $ vendor/bin/rector process –config=rector-chained-rule.php

编写器依赖关系可能不一致。

库可以为开发声明一个依赖项(即composer.json中的require-dev下),但它仍然可以为生产引用它的一些代码(如在src/而不是tests/下的一些文件上)。

通常,这不是问题,因为代码可能不会加载到生产环境中,所以应用程序上永远不会有错误。但是,当Rector处理源代码及其依赖项时,它会验证是否可以加载所有引用的代码。如果任何文件引用未安装库中的一段代码(因为它被声明只用于开发),Rector将抛出一个错误。

例如,Symfony缓存组件中的EarlyExpirationHandler类实现了Messenger组件中的MessageHandlerInterface接口:

class EarlyExpirationHandler implements MessageHandlerInterface{ //…}

但是symfony/cache声明symfony/messenger是开发的依赖。然后,当在依赖于symfony/cache的项目上运行Rector时,它将抛出一个错误:

[ERROR] Could not process “vendor/symfony/cache/Messenger/EarlyExpirationHandler.php” file, due to: “Analyze error: “Class SymfonyComponentMessengerHandlerMessageHandlerInterface not found.”. Include your files in “$parameters->set(Option::AUTOLOAD_PATHS, […]);” in “rector.php” config. See https://github.com/rectorphp/rector#configuration”.

这个问题有三种解决方案:

在目录配置中,跳过处理引用该代码段的文件:返回静态函数(容器配置器$容器配置器):void {//…$ parameters-> set(option::skip,[ __DIR__。/vendor/symfony/cache/Messenger/earlyexpirationhandler . PHP ‘,]);};

下载缺失的库并添加其路径,以便由Rector自动加载:返回静态函数(容器配置器$容器配置器):void {//…$ parameters–> set(Option::AUTOLOAD _ PATHS,[ __DIR__。/vendor/symfony/messenger ‘,]);};

使您的项目依赖于缺失的库进行生产: composer需要symfony/messenger

以及转换和持续集成。

如前所述,在我们的开发计算机中,运行Rector时必须使用- dry-run标志,否则,源代码将被转换后的代码覆盖。因此,在持续集成(CI)期间运行实际的转换过程更合适,我们可以启动临时运行程序来执行该过程。

执行转换过程的理想时间是为我们的项目生成发布时间。例如,下面的代码是GitHub Actions的工作流程,它创建了WordPress插件的发布:

name: Generate Installable Plugin and Upload as Release Asseton: release: types: [published]jobs: build: name: Build, Downgrade and Upload Release runs-on: ubuntu-latest steps: – name: Checkout code uses: actions/checkout@v2 – name: Downgrade code for production (to PHP 7.1) run: | composer install vendor/bin/rector process sed -i ‘s/Requires PHP: 7.4/Requires PHP: 7.1/’ graphql-api.php – name: Build project for production run: | composer install –no-dev –optimize-autoloader mkdir build – name: Create artifact uses: montudor/[email protected] with: args: zip -X -r build/graphql-api.zip . -x *.git* node_modules/* .* “*/.*” CODE_OF_CONDUCT.md CONTRIBUTING.md ISSUE_TEMPLATE.md PULL_REQUEST_TEMPLATE.md rector.php *.dist composer.* dev-helpers** build** – name: Upload artifact uses: actions/upload-artifact@v2 with: name: graphql-api path: build/graphql-api.zip – name: Upload to release uses: JasonEtco/upload-to-release@master with: args: build/graphql-api.zip application/zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

这个工作流程包含了通过GitHub操作发布WordPress插件的标准流程。将插件代码从PHP7.4转换到7.1的新步骤如下:

– name: Downgrade code for production (to PHP 7.1) run: | vendor/bin/rector process sed -i ‘s/Requires PHP: 7.4/Requires PHP: 7.1/’ graphql-api.php

总而言之,该工作流现在执行以下步骤:

从WordPress插件的资源库中查看其源代码,它是用PHP7.4编写的。

安装其编写器依赖项。

将代码从PHP7.4转换为7.1

将插件主文件头中的“需要PHP”条目从“7.4”修改为“7.1”

删除开发所需的依赖项。

创建。zip文件,并排除所有不必要的文件。

上传。zip文件作为发布资产(另外,也作为GitHub运营的神器上传)

测试转换代码

一旦代码被转换成PHP 7.1,我们怎么知道它运行良好?或者换句话说,我们怎么知道它已经被完全转换了,没有留下更高版本PHP代码的残渣?

类似于转储代码,我们可以在CI流程中实现解决方案。想法是用PHP 7.1搭建一个亚军环境,在翻堆代码上运行一个pad。如果有任何代码与PHP 7.1不兼容(例如,未转换的PHP 7.4中的typing属性),那么代码行将抛出一个错误。

Php的内衬很好用。PHP和Lint是并行的。我们可以将此库作为项目开发的依赖项来安装,或者让CI process将其作为独立的composer项目来安装:

一旦代码转换成PHP7.1,我们怎么知道它运行良好?或者换句话说,我们怎么知道它已经被完全转换了,没有留下更高版本PHP代码的残渣?

类似于转换代码,我们可以在CI流程中实现解决方案。想法是使用PHP7.1来设置运行时环境,并在转换后的代码上运行linter。如果任何代码与PHP7.1不兼容(例如,PHP7.4中未转换的类型化属性),那么linter将抛出一个错误。

PHP运行良好的Linter是PHP并行lint。我们可以将该库作为开发依赖项安装在我们的项目中,或者让CI process将其作为独立的Composer项目安装:

composer create-project php-parallel-lint/php-parallel-lint

只要代码包含PHP 7.2和更高版本,PHP Parallel Lint就会抛出以下错误:

Run php-parallel-lint/parallel-lint layers/ vendor/ –exclude vendor/symfony/polyfill-ctype/bootstrap80.php –exclude vendor/symfony/polyfill-intl-grapheme/bootstrap80.php –exclude vendor/symfony/polyfill-intl-idn/bootstrap80.php –exclude vendor/symfony/polyfill-intl-normalizer/bootstrap80.php –exclude vendor/symfony/polyfill-mbstring/bootstrap80.phpPHP 7.1.33 | 10 parallel jobs…………………………………………………… 60/2870 (2 %)…………………………………………………… 120/2870 (4 %)……………………………………………………… 660/2870 (22 %)………….X………………………………………. 720/2870 (25 %)…………………………………………………… 780/2870 (27 %)……………………………………………………… 2820/2870 (98 %)………………………………………….. 2870/2870 (100 %)Checked 2870 files in 15.4 secondsSyntax error found in 1 file————————————————————Parse error: layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/graphql-api.php:5553| ‘0.8.0’,54| __(‘GraphQL API for WordPress’, ‘graphql-api’),> 55| ))) {56| $plugin->setup();57| }Unexpected ‘)’ in layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/graphql-api.php on line 55Error: Process completed with exit code 1.

让我们为我们的CI工作流程添加绒毛。执行从PHP 8.0到7.1的代码转换并测试它的步骤是:

查看源代码

让环境运行PHP 8.0,让校长解释源代码。

将代码转换为PHP 7.1

安装PHP绒毛工具

将环境的PHP版本切换到7.1

在转储代码上运行liner

这个GitHub动作工作流程完成这项工作:

让我们将linter添加到CI的工作流中。将代码从PHP 8.0转换到7.1并进行测试的步骤如下:

查看源代码

让环境运行PHP8.0,这样委托人就可以解释源代码了。

将代码转换为PHP7.1

安装PHP linter工具

将环境的PHP版本切换到7.1

对转换后的代码运行linter。

此GitHub操作工作流执行以下任务:

name: Downgrade PHP testsjobs: main: name: Downgrade code to PHP 7.1 via Rector, and execute tests runs-on: ubuntu-latest steps: – name: Checkout code uses: actions/checkout@v2 – name: Set-up PHP uses: shivammathur/setup-php@v2 with: php-version: 8.0 coverage: none – name: Local packages – Downgrade PHP code via Rector run: | composer install vendor/bin/rector process # Prepare for testing on PHP 7.1 – name: Install PHP Parallel Lint run: composer create-project php-parallel-lint/php-parallel-lint –ansi – name: Switch to PHP 7.1 uses: shivammathur/setup-php@v2 with: php-version: 7.1 coverage: none # Lint the transpiled code – name: Run PHP Parallel Lint on PHP 7.1 run: php-parallel-lint/parallel-lint src/ vendor/ –exclude vendor/symfony/polyfill-ctype/bootstrap80.php –exclude vendor/symfony/polyfill-intl-grapheme/bootstrap80.php –exclude vendor/symfony/polyfill-intl-idn/bootstrap80.php –exclude vendor/symfony/polyfill-intl-normalizer/bootstrap80.php –exclude vendor/symfony/polyfill-mbstring/bootstrap80.php

请注意,Symfony的polyfill库中的几个bootstrap80.php文件必须从linter中排除(不需要转换)。这些文件包含PHP8.0,因此linter在处理它们时会抛出一个错误。但是,排除这些文件是安全的,因为只有在运行PHP 8.0或更高版本时,它们才会被加载到生产环境中:

if (PHP_VERSION_ID >= 80000) { return require __DIR__.’/bootstrap80.php’;}

总结

这篇文章教我们如何转换PHP代码,允许我们在源代码中使用PHP8.0,并为PHP7.1创建一个发行版,Transpiling由Rector(一个PHP重构工具)完成。

代码转换使开发人员更加有效和高效,因为我们可以更好地捕捉开发中的错误,并生成自然更容易阅读和理解的代码。

Transpiling还使我们能够将具有特定PHP需求的代码从CMS中分离出来。如果我们想使用最新版本的PHP来创建公开可用的WordPress插件或Drupal模块,而不严重限制我们的用户群,那么我们现在就可以这样做。

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » 跨版本PHP代码转换终极教程