PHP8 揭秘(二)
十三、杂项
类构造函数属性提升
在以前的 PHP 版本中,定义一个简单的值对象所需的声明是冗长而重复的。
class Pants {
public float $x;
public float $y;
public float $z;
public function __construct(
float $x = 0.0,
float $y = 0.0,
float $z = 0.0,
) {
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
}
在这个新特性中,PHP 为实现一个新类提供了一个更优化的方法。
class Pants {
public function __construct(
public float $x = 0.0,
public float $y = 0.0,
public float $z = 0.0,
) {}
}
限制
不允许使用非抽象构造函数,这将导致错误。
// Error: Not a constructor.
function pants(private $x) {}
abstract class Pants {
// Error: Abstract constructor.
abstract public function __construct(private $x);
}
interface Pants {
// Error: Abstract constructor.
public function __construct(private $x);
}
如果使用 traits,类构造函数属性提升将被允许。提升的属性还必须使用可见性关键字之一(public、private、protected)。不支持使用var。
class Pants {
// Error: "var" keyword is not supported.
public function __construct(var $foo) {}
}
与普通属性声明相同的限制也适用于通过提升参数声明的属性。这意味着不可能两次声明同一个属性。
class Test {
public $prop;
// Error: Redeclaration of property.
public function __construct(public $prop) {}
}
可调用类型不可用,因为它们不支持作为属性类型。
class Test {
// Error: Callable type not supported for properties.
public function __construct(public callable $callback) {}
}
新的 fdiv()函数
fdiv()函数 将执行浮点除法,同时将除以零视为完全合法的操作,在这种情况下不会发出任何类型的诊断。相反,它将返回 IEEE-754 要求的INF/-INF/NAN结果。它镜像了现有的fmod()功能。—Nikita Popov
*目前,被零除会导致不可预测的行为。根据 PHP 8 中新的引擎警告,波波夫想在fmod()和intdiv()家族中加入fdiv()。
让我们看一些这种行为的例子。
$output = intdiv(1, 0);
Fatal error: Uncaught DivisionByZeroError:
$output = 1 % 0;
Fatal error: Uncaught DivisionByZeroError
----
actually dividing by zero does not behave the same.
$output = 1 / 0;
Warning: Division by zero.
fdiv将遵循 IEEE-754 语义并返回INF/-INF/NaN而不抛出关键错误。
总是为不兼容的方法签名生成致命错误
日期:2019-04-08
作者:尼基塔·波波夫
表决结果:39 票赞成、3 票反对
这是又一次错误升级。不兼容的方法签名总是会引发致命错误。以前的版本会抛出警告或致命错误,但是 PHP 8 把所有的错误都归为致命错误。
从负索引开始的数组
版本:0.4
日期:2017-04-20
作者:佩德罗·麦哲伦
表决结果:17 票赞成、2 票反对
任何第一个数字键有数字n的给定数组都将被隐式地分配下一个键为n+1(如果 n > = 0)或0(如果 n < 0)。PHP 8 对此不会有不同的处理,总是将下一个键指定为n+1,而不管第一个键的值。
在 ext/dom 中实现新的 DOM 生活标准 API
版本:0.3
日期:2019-09-15
作者:Benjamin eberhee(beber lei @ PHP . net),thomas weinert
表决结果:37 票赞成
最初的文档对象模型(DOM)是作为 HTML 和 XML 的接口而创建的,由 W3 在 2004 年建立,但已被 Web 超文本应用技术工作组(WHATWG)接管,并转化为生活标准。之所以采用这个标准,是因为新的 API 改进了数据的遍历和操作。这种采用也确保了随着标准的发展,PHP 支持也会发展。与标准本身有一些偏差,主要是因为它是为浏览器或 JavaScript 编写的,PHP 必须适应这些情况。
实施
<?php
interface DOMParentNode
{
/** access to the first child of this node that is a DOMElement */
public readonly ?DOMElement $firstElementChild;
/** access to the last child of this node that is a DOMElement */
public readonly ?DOMElement $lastElementChild;
/** counts all child nodes that are DOMElements */
public readonly int $childElementCount;
/** appends one or many nodes to the list of children behind the last child node */
public function append(...DOMNode|string|null $nodes) : void;
/** prepends one or many nodes to the list of children before the first child node */
public function prepend(...DOMNode|string|null $nodes) : void;
}
class DOMDocument implements DOMParentNode {}
class DOMElement implements DOMParentNode {}
class DOMDocumentFragment implements DOMParentNode {}
interface DOMChildNode
{
/** Returns the previous node in the same hierarchy that is a DOMElement or NULL if there is none */
public readonly ?DOMElement $previousElementSibling;
/** Returns the next node in the same hierachy that is a DOMElement or NULL if there is none */
public readonly ?DOMElement $nextElementSibling;
/** acts as a simpler version of $element->parentNode->removeChild($element); */
public function remove() : void;
/** add passed node(s) before the current node */
public function before(...DOMNode|string|null $nodes) : void;
/** add passed node(s) after the current node */
public function after(...DOMNode|string|null $nodes) : void;
/** replace current node with new node(s), a combination of remove() + append() */
public function replaceWith(...DOMNode|string|null $nodes) : void;
}
class DOMElement implements DOMChildNode {}
class DOMCharacterData implements DOMChildNode {}
生活标准包含一个中间特征(接口)DOMNonDocumentTypeChildNode,它定义了previousElementSibling和nextElementSibling属性。PHP 不允许接口声明属性;因此,该接口不可用,但是属性在实现DOMChildNode的每个类上都可用。另外两种方法,querySelector和querySelectorAll,也被排除在这个植入之外。这些是面向层叠样式表(CSS)选择的,已经有特定的库(PhpCss 或 Symfony CSS 选择器)可以更好地处理这一功能。
静态返回类型
日期:2020 年 1 月 8 日
作者:尼基塔·波波夫
实现: https://github.com/php/php-src/pull/5062
投票结果:54 票赞成
目前,使用静态特殊类名指的是调用方法的实际类。无论该方法是否被继承,这都适用。这被称为后期静态绑定 (LSB)。LSB 通过存储最后一个非转发调用中命名的类来工作。对于静态方法调用,这是显式命名的类(通常在 :: 运算符的左边);对于非静态方法调用,它是对象的类。转移呼叫是由self::、parent::或static::引入的静态呼叫,如果在等级结构中向上,还有forward_static_call()。
class A {
public function test(): self {}
}
class B extends A {
public function test(): static {}
}
class C extends B {}
class A {
public function test(): A {}
}
class B extends A {}
class C extends B {
public function test(): static {}
}
变量语法调整
这是为了更新 PHP 7 中的统一变量语法 RFC。
日期:2020 年 1 月 7 日
作者:尼基塔·波波夫
实现: https://github.com/php/php-src/pull/5061
表决结果:47 票赞成
PHP 中有四种主要类型的“取消引用”操作:
-
数组:
$foo[$bar], $foo{$bar} -
对象:
$foo->bar, $foo->bar() -
静态:
Foo::$bar, Foo::bar(), Foo::BAR -
通话:
foo()
插值和非插值字符串
目前,像"pants"这样的非插入字符串被认为是完全不可引用的;也就是说,像"pants"[0]或"pants"->shorts()这样的结构在语法上是合法的。插入的字符串如"pants$shorts"是不可区分的。
魔法、类和常规常量
像__PANTS__这样的魔法常量现在将被视为普通常量,并允许数组去引用。
PANTS[0] and __PANTS__[0]
类似地,使用PANTS{0}和PANTS->length().可以取消对类常量的引用
类常数可取消引用性
目前Foo::$bar::$baz是合法的,而Foo::BAR::$baz不是。PHP 8 将改变这一点,使Foo::BAR::$baz和Foo::BAR::BAZ成为合法的。
对 new 和 instanceof 的任意表达式支持
PHP 8 还将引入语法new (expr)和$x instanceof (expr).
添加字符串接口
版本:0.9
日期:2020 年 1 月 15 日
作者:尼古拉斯·格雷卡斯
表决结果:29 / 9
stringable 接口将为所有实现__toString()方法的类添加一个 stringable 接口。这个接口有两个用途。第一个是允许使用string|Stringable来表达string|object-with-__toString().,第二个是提供从 PHP 7 到 PHP 8 的正向升级路径。
获取调试类型
日期:2020 年 2 月 15 日
作者:马克·兰道尔
表决结果:42 票赞成、3 票反对
PHP 8 的这一新增功能不同于显而易见的兄弟get_type(),它返回本机类型名int而不是integer,同时还解析类名。这个函数的主要目的是在处理 PHP 在运行时无法处理的类型时,通过现有的基于参数类型的检查来代替更复杂的过程。特别是在处理数组中的参数类型时,这是非常有益的。
$pants = $arr['key'];
if (!($pants instanceof Hive)) {
throw new TypeError('Expected ' . Hive::class . ' got ' . (is_object($pants) ? get_class($pants) : gettype($pants)));
}
表 13-1 列出了gettype和get_debug_type的不同返回值。
表 13-1
gettype和get_debug_type的返回值
价值
|
get_debug_type()
|
gettype()
| | --- | --- | --- | | Zero | (同 Internationalorganizations)国际组织 | 整数 | | Zero point one | 漂浮物 | 两倍 | | 真实的 | 弯曲件 | 布尔 | | 错误的 | 弯曲件 | 布尔 | | "你好" | 线 | | | [ ] | 排列 | | | 空 | 空 | 空 | | 名为“Pants\Hive”的类 | 裤子\蜂巢 | 目标 | | 匿名班级 | 匿名类 | 目标 | | 一种资源 | 资源(xxx) | 资源 | | 封闭资源 | 资源(已关闭) | |
// PHP 8 would allow for
if (!($pants instanceof Hive)) {
throw new TypeError('Expected ' . Hive::class . ' got ' . get_debug_type($pants));
}
$pants>someHiveMethod();
这在 PHP 中很受欢迎,几乎没有争议。波波夫在投票时给出了更多的细节。
考虑到命名的讨论,我认为这里有一个重要的部分需要强调:对于匿名类,它只返回
"class@anonymous",而不是"class@anonymous\0SOME_RANDOM_UNIQUE_STRING_HERE"。因而这个功能肯定不能在一个::type下构造(这个应该返回全称),甚至get_canonical_type()似乎也不合适。
该函数特别适用于在错误信息或类似信息中包含类型名称,因此
get_debug_type()…—Nikita Popov
New preg_last_error_msg()
作者:尼科·奥尔加特
PHP 有一个标准的函数类,称为 Perl 兼容正则表达式(PCRE)。PCRE 函数允许 PHP 中正则表达式的标准使用。在以前的版本中,PHP 用户只能得到返回错误代码的preg_last_error()。使用preg_last_error_msg(),我们现在可以改变这一点:
<?php
preg_match('/(?:\D+|<\d+>)*[!?]/', 'foobar foobar foobar');
var_dump(preg_last_error()); // 2
var_dump(preg_last_error_msg()); // Backtrack limit was exhausted
添加 CMS 支持
日期:2020 年 5 月 13 日
作者:艾略特·李尔
不,这与 WordPress、Laravel、Drupal 或类似的软件无关!加密消息语法(CMS)是 PKCS#7 的较新版本。由 RSA Security,LLC 开发的 PKCS#7(公钥加密标准)是一种标准,其中数字签名和证书的生成和验证由公钥基础设施(PKI)管理。这是目前在电子邮件和物联网设备中使用的加密标准。
PKCS#7 和 OpenSSL 函数之间的关系如表 13-2 所示。
表 13-2
PKCS#7 和 OpenSSL 函数
|PKCS#7 函数
|
新 CMS 功能
| | --- | --- | | openssl_pkcs7_encrypt() | openssl_cms_encrypt() | | openssl_pkcs7_decrypt() | openssl_cms_decrypt() | | openssl_pkcs7_sign() | openssl_cms_sign() | | openssl_pkcs7_verify() | openssl_cms_verify() | | openssl_pkcs7_read() | openssl_cms_read() |
function openssl_cms_sign(string $infile, string $outfile, $signcert, $signkey, ?array $headers, int $flags = 0, int $encoding = OPENSSL_ENCODING_SMIME, ?string $extracertsfilename = null): bool {}
此函数使用 X.509 证书和密钥对文件进行签名。论据如下。
-
$infile:待签名文件的名称 -
$outfile:存放结果的文件的名称 -
$signcert:包含签名证书的文件的名称 -
$signkey:包含与$signcert关联的密钥的文件名 -
$headers:要包含在 S/MIME 输出中的标题数组 -
$flags:要传递给cms_sign()的标志 -
$encoding:输出文件的编码 -
$extracertsfilename:签名中包含的中间证书
function openssl_cms_verify(string $filename, int $flags = 0, string $signerscerts = UNKNOWN, array $cainfo = UNKNOWN, string $extracerts = UNKNOWN, string $content = UNKNOWN, string $pk7 = UNKNOWN, string $sigfile = UNKNOWN, $encoding = OPENSSL_ENCODING_SMIME ): bool {}
此函数使用指定的编码验证 CMS 签名,无论是附加的还是分离的。
以下是论点:
-
$filename:输入文件 -
$flags:将被传递给cms_verify的标志 -
$signercerts:签名者证书和可选的中间证书的文件 -
$cainfo:包含自签名认证机构证书的数组 -
$extracerts:包含附加中间证书的文件 -
$content:分离签名时指向内容的文件 -
$pk7:保存签名的文件 -
$encoding:支持的三种编码之一(PEM/DER/SMIME)。
如果成功,则返回TRUE,如果失败,则返回FALSE。
function openssl_cms_encrypt(string $infile, string $outfile, $recipcerts, ?array $headers, int $flags = 0, int $encoding = OPENSSL_ENCODING_SMIME, int $cipher = OPENSSL_CIPHER_RC2_40): bool {}
该函数根据传递给它的证书对一个或多个接收者的内容进行加密。
以下是论点:
-
$infile:要加密的文件 -
$outfile:输出文件 -
$recipcerts:要加密的收件人 -
$headers:使用 S/MIME 时要包含的标题 -
$flags:要传递给CMS_sign的标志 -
$encoding:要输出的编码 -
要使用的密码
如果成功,则返回值TRUE,如果失败,则返回值FALSE。
function openssl_cms_decrypt(string $infilename, string $outfilename, $recipcert, $recipkey = UNKNOWN, int $encoding = OPENSSL_ENCODING_SMIME): bool {}
这个函数解密一个 CMS 消息。以下是论点:
-
$infilename:包含加密内容的文件的名称 -
$outfilename:存放解密内容的文件名 -
$recipcert:包含接收方证书的文件的名称 -
$recipkey:包含 PKCS#8 密钥的文件的名称 -
$encoding:输入文件的编码。
该函数成功时返回TRUE,失败时返回FALSE。
function openssl_cms_read(string $infilename, &$certs): bool {}
该功能完全模拟openssl_pkcs7_read()。
还包括新的常数。
OPENSSL_ENCODING_CMS /* encoding is a CMS-encoded message */
OPENSSL_ENCODING_DER /* encoding is DER (Distinguished Encoding Rules) */
OPENSSL_ENCODING_PEM /* encoding is PEM (Privacy-Enhanced Mail) */
OPENSSL_CMS_DETACHED
OPENSSL_CMS_TEXT
OPENSSL_CMS_NOINTERN
OPENSSL_CMS_NOVERIFY
OPENSSL_CMS_NOCERTS
OPENSSL_CMS_NOATTR
OPENSSL_CMS_BINARY
OPENSSL_CMS_NOSIGS
对象上的 Allow ::class
日期:2020 年 1 月 9 日
作者:尼基塔·波波夫
投票结果:60 / 0
在 PHP 的早期版本中,常量::class允许访问完全限定的类名。此常数考虑了使用和当前可用的命名空间。
namespace 311\Albums;
use Grass\Roots;
use Roots\Music as Album;
class Pants {}
// `use` statement
echo Roots::class; // 'Grass\Roots"
// `use` X `as` Y
echo Album::class; // "Roots\Music"
// Current namespace
echo Albums::class; // "311\Albums\Pants"
以前版本的 PHP 在对象上使用::class时会抛出致命错误,但在 PHP 8 中,这种情况现在可以按预期执行。
$object = new Grass\Roots;
echo $object::class;
// "Grass\Roots"
如果$object是一个object,那么$object::class返回get_class($object)。否则它抛出一个TypeError异常。
$object = new stdClass;
var_dump($object::class); // "stdClass"
$object = null;
var_dump($object::class); // TypeError
基于对象的 token_get_all()替代
日期:2020 年 2 月 13 日
实现: https://github.com/php/php-src/pull/5176
表决结果:47 票赞成
Popov 提出的PhpToken::getAll()方法是对token_get_all()的替代,它将返回一个PhpToken对象的数组,而不是字符串和数组的混合。这主要有两个原因。首先,返回一个对象数组将使返回结构标准化。现在,开发人员只需要期待对象的数组,而不是单个字符串或数组的可能性。第二个也是最有益的是使用数组时内存使用的减少。
Default:
Memory Usage: 14.0MiB
Time: 0.43s (for 100 tokenizations)
TOKEN_AS_OBJECT:
Memory Usage: 8.0MiB
Time: 0.32s (for 100 tokenizations)
此外,还包含了方法getTokenName(),它主要用于调试目的。对于 ID 小于 256 的单字符令牌,它返回与 ID 对应的扩展 ASCII 字符。对于已知的令牌,它返回与token_name()相同的结果。对于未知令牌,它返回 null。
下面是新的类实现:
class PhpToken {
/** A T_* constant, or an integer < 256 representing a single-char token. */
public int $id;
/** The context of the token. */
public string $text;
/** starting line number (1-based) of the token. */
public int $line;
/** starting position (0-based) in the tokenized string. */
public int $pos;
/**
* Same as token_get_all(), but returning array of PhpToken.
* @return static[]
*/
public static function getAll(string $code, int $flags = 0): array;
final public function __construct(int $id, string $text, int $line = -1, int $pos = -1);
/** Get the name of the token. */
public function getTokenName(): ?string;
因为PhpToken::getAll()方法返回static[],所以扩展这个类很容易。
class thePhpToken extends PhpToken {
public function getLowerText() {
return strtolower($this->text);
}
}
$tokens = thePhpToken::getAll($code);
var_dump($tokens[0] instanceof thePhpToken); // true
$tokens[0]->getLowerText(); // works
抽象特征方法的验证
日期:2020 年 2 月 7 日
作者:尼基塔·波波夫
实现: https://github.com/php/php-src/pull/5068
投票结果:52 票对 0 票
PHP 中的特征类似于类,但是可以在多个实例中重用。例如:
<?php
trait CustomReturn {
function getFirstReturnType() { /*1*/ }
function getFirstReturnDesc() { /*2*/ }
}
class thisFakeMethod extends FakeMethod {
use CustomReturn;
/* ... */
}
class thisFakeFunction extends FakeFunction {
use CustomReturn;
/* ... */
}
?>
PHP 8 将总是根据独立于其来源的实现方法来验证抽象特征方法的签名。如果实现方法与抽象特征方法不兼容,就会产生致命错误。不相容的定义如下:
-
签名必须兼容,包括奇偶校验兼容、逆变参数类型兼容和协变返回类型兼容。
-
方法的静态性必须匹配。
此外,现在可以只在 traits 中声明抽象私有方法了。抽象私有方法在术语上是矛盾的,因为声明实现的方法从发布需求的类中是不可见的。然而,trait 提供了对抽象私有方法的明确访问,因为 trait 方法可以访问 using 类的私有方法。
抛出表达式
日期:2020 年 3 月 21 日
作者:Ilija Tovilo
实现: https://github.com/php/php-src/pull/5279
表决结果:46 票赞成,3 票反对
Throw与catch一起用于处理编程逻辑中的异常。被一个try块包围着,你可以抛出一个异常来捕获和处理,而不是破坏代码。但是Throw只在这类场景中使用过,不能作为表达式使用。这将允许使用带有箭头函数的throw、联合操作符和三元/elvis 操作符。
$pants = fn() => throw new Exception();
$pants = $nullyMcNull ?? throw new NullException();
$pants = $isFalse ?: throw new FalseException();
$pants = !empty($array)
? reset($array)
: throw new EmptyException();
$isWinning && throw new Exception();
$isWinning || throw new Exception();
$isWinning and throw new Exception();
$isWinning or throw new Exception();
优先级也将变得重要。throw语句之后的所有内容将被认为具有更高的重要性。也就是说,throw将采用最低的操作符优先级。这是有意义的,因为一般来说,throw应该在逻辑语句的末尾。
throw (static::wrongPasswordException());
throw ($cheeseIsFree ? new payForCheeseException() : new noCheeseException());
throw ($foreverAlone ?? new Exception());
throw ($lightning = new Exception());
throw ($promises ??= new words());
throw ($fu && $gazi ? new selling() : new buying());
需要断言特殊性的一个地方是短路操作符。如果选择这样做,必须使用括号来区分优先级。
$transistor || (throw new Exception('$transistor must be resistor') && $unplugged || (throw new Exception('$unplugged must be resistor')));
独立于区域设置的浮点到字符串转换
版本:1.0
日期:2020 年 3 月 11 日
作者:乔治·彼得·班亚,梅特·科 csis
实现: https://github.com/php/php-src/pull/5224
表决结果:42 票赞成,1 票反对
在 PHP 中将浮点数转换成字符串已经成为一个问题有一段时间了。出现此问题是因为不同的国家表示十进制字符(。或者,)不同地。例如,3.14 在美国是相当标准的,可以确定为圆周率值的开始。然而,如果你在波兰发展,你会使用“3,14”。这种差异的主要问题是,当使用float到string的转换时,由于地区不同,结果会不一致。这一增加将用“.”使这些问题标准化作为主要的小数占位符。
setlocale(LC_ALL, "pl_PL");
$f = 3.11;
(string) $f; // 3,11 would become 3.11
strval($f); // 3,11 would become 3.11
print_r($f); // 3,11 would become 3.11
var_dump($f); // float(3,11) would become float(3.11)
debug_zval_dump($f); // float(3,11) would become float(3.11)
settype($f, "string"); // 3,11 would become 3.11
implode([$f]); // 3,11 would become 3.11
xmlrpc_encode($f); // 3,11 would become 3.11
这种行为在 userland 并不是前所未有的。例如,PDO 扩展利用这一点来标准化浮点的字符串表示。在var_export、serialize和json_encode中也使用了语言环境独立性。然而,printf已经有了一个用%f指定非比例感知转换的选项,保持不变。
非捕获捕获
版本:0.9
日期:2020 年 4 月 5 日
作者:马克斯·塞姆尼克
实现: https://github.com/php/php-src/pull/5345
表决结果:48 票赞成,1 票反对
简而言之,我希望能够做到以下:
请分享你的想法!:)
这名男子的名字在纽约一名 2013-16 岁的男子家中被杀。
我看起来不错。
—斯塔斯·马利舍夫
try {
foo();
catch (SomeExceptionClass) {
bar();
}}
编程中的try / catch范例允许定义一个代码块并测试其错误(try),同时定义另一个代码块来处理所述错误(catch)。在 PHP 中,这是通过使用catch块中的一个变量来实现的,这个变量将自己分配给错误。
try {
foo();
} catch (weekendException $ex) {
die($ex->showMessage());
}
会变成
try {
foo();
} catch (weekendException) {
echo "This does not work on the weekend";
}
这种增加不再需要变量的说明。catch的意图很清楚,因此不需要任何其他细节。
始终可用的 JSON 扩展
版本:0.3
日期:2020 年 4 月 29 日
作者:泰森·安德烈
实现: https://github.com/php/php-src/pull/5495
票数:56 票/ 0 票
通过这次添加,PHP 团队迈出了将 JSON 从 PECL 扩展转变为 PHP 核心特性的第一步。目前,在 PHP 版本中禁用 JSON 的唯一方法是通过./configure --disable-json,这使得代码有可能要求 JSON 出现故障。在以前的 PHP 版本中禁用 JSON 的唯一原因是由于许可问题,这个问题已经解决了。
然而,这种改变会有向后兼容性的问题。脚本或命令行界面(CLI)指令中使用的--enable-json or --disable-json将需要更新,因为这些选项将不再存在。这同样适用于任何使用extension_loaded('json'),的代码,因为这将始终是true。
Zend . exception _ String _ param _ max _ len:getTraceAsString()中可配置的字符串长度
自 2003 年以来,两个非常有用的功能被限制为 15 字节的信息。Throwable->getTraceAsString()和Throwable->__toString()被期望将堆栈跟踪返回给急切地等待挤压他们代码中的 bug 的开发人员。直到 PHP 8,这些都返回了类似于"/path/to/the/vesion/file.php 1349 function(whe ...",的半有用的字符串,考虑到路径、URL 和 UUIDs 的使用,这些信息实际上是不够的。
这个问题的解决方案是一个.ini设置zend.exception_string_param_max_len,它允许将字符串的字节限制更改为从 0 到 1000000 的任何值。如果没有设置值,默认值将保持为 15。然而,与此相关的一个问题是,随着暴露数据的增加,无意中获得敏感数据的风险也会增加。
function badHTMLRenderingExample(string $secretCode, string $secretPassword) {
echo "<h1>Welcome AOL</h1>\n";
try {
process($secretCode);
} catch (Exception $e) {
// The output will include both $secretCode and $secretPassword.
// in PHP 7x, only 15 bytes would be displayed.
echo "ID: 10 T error, please feed the dev team: $e\n";
}
}
15 字节的默认设置将保持实际执行此操作的遗留代码的安全性,除非.ini设置被更改。
解包 ext/xmlrpc
版本:1.0
日期:2020 年 5 月 12 日
作者:克里斯托弗·m·贝克尔
通过 https://github.com/php/php-src/pull/5640 进行拆分
票数:50 / 0
似乎并没有强烈的需求去暗示人们应该尽快停止使用它。扩展部分(更重要的是,底层库)已经很多年没有维护了,搬到 PECL 不会在这方面有实质性的改变。
*——*Nikita Popov
目前,是 PHP 中一个不可避免的弊端。对于那些不了解的人来说,XML-RPC 是允许在系统之间使用 XML 的规范,因此是必要的。尽管 XML 本质上并不邪恶,但出于多种原因,目前这种移植的使用是邪恶的。第一,ext/xmlrpc依赖于被抛弃的libxmlrpc-epi。这种放弃可以通过滚动 xmlrpc-epi-dev 邮件列表中当前的垃圾邮件数量来验证。这是第一击。打击二是 PHP 目前实现的是 0.51 版本,但最新版本是 0.54。一个解决方案是控制库并继续更新和维护。这不是 PHP 团队的目标或宗旨。该提案已提交给 unbundle ext/xmlrpc,这意味着它将被视为通过 PECL 的第三方扩展,因此选择使用或不使用它成为最终用户的决定和责任。这里需要注意的是,团队并不认为ext/xmlrpc是无用的或者需要被废弃。API 和功能按预期工作。这仅仅是为这个库或 PHP 关于 XML 的下一步做准备的一步。
不要在 getMetadata()之外自动取消 Phar 元数据的序列化
版本:0.4
日期:2020 年 7 月 7 日
作者:泰森·安德烈
实现: https://github.com/php/php-src/pull/5855
票数:25 票/ 0 票
除了它吸引人的名字,PHP 8 的这一新增功能带来了一个急需的安全更新。快速搜索“php phar 流包装器漏洞”将返回几个影响 Drupal、WordPress、Prestashop 等的结果。基本上今天互联网上的大多数网站在 PHP 8 之前都是易受攻击的。正如 RFC 指出的,“由于对象实例化和自动加载,非序列化会导致代码被加载和执行,恶意用户可能会利用这一点。”
提案
当 PHP 打开 phar 文件时,不要自动解序列化元数据。仅当直接调用Phar->getMetadata()或PharFile->getMetadata()时,才使 PHP 不序列化元数据。
另外,添加一个数组$unserialize_options = [] parameter to both getMetadata()实现,默认为当前默认的unserialize()行为,比如允许任何类。(作为一个实现细节,如果$unserialize_options被设置为默认值以外的任何值,那么产生的元数据将不会被缓存,并且不会从缓存中返回值。例如,setMetadata(new stdClass())之后的getMetaData(['allowed_classes' => []])将可能触发内部的unserialize(['allowed_classes' => []])呼叫。
向后不兼容的更改
加载 phar 时,在元数据非序列化期间或之后触发的来自__wakeup()、__destruct()等的任何副作用都将停止发生,只有在直接调用getMetadata()时才会发生。*