Cool new
things in PHP
What’s already available
added in PHP ≤7.4, or polyfilled by MediaWiki
01
yield
public function provideValues(): iterable {
yield 'null' => [ null ];
yield 'empty string' => [ '' ];
foreach ( [ 0, 1, -1 ] as $number ) {
yield "int $number" => [ $number ];
yield "string $number" => [ (string)$number ];
}
}
/** @dataProvider provideValues */
public function testSomething( $value ): void { /* ... */ }
yield
...
public function msg( string $msg, ...$args ): Message {
return wfMessage( $msg, ...$args )
->inLanguage( $this->getTargetLanguage() )
->page( $this->getPage() );
}
$array1 = [ 2, 3, 4 ];
$array2 = [ 0, 1, ...$array1, 5 ];
// [ 0, 1, 2, 3, 4, 5 ]
includes/parser/Parser.php,
as of commit a4da635e8a,
GPL-2.0-or-later
...
array destructuring
[ $causeAction, $causeAgent ] = $this->getCause();
[ , $lag, $index ] = $this->loadBalancer->getMaxLag();
[
'tables' => $tables,
'fields' => $fields,
'joins' => $joins,
] = $revStore->getQueryInfo();
includes/Storage/DerivedPageDataUpdater.php,
includes/api/ApiQuerySiteinfo.php,
as of commit a4da635e8a,
GPL-2.0-or-later
array destructuring
static types / type declarations
class CreditsAction extends FormlessAction {
/** @var LinkRenderer */
private $linkRenderer;
/** @var UserFactory */
private $userFactory;
/**
* Convert a Message to a MessageValue
* @param Message $m
* @return MessageValue
*/
public function convertMessage( $m ) {
includes/actions/CreditsAction.php (edited),
includes/Message/Converter.php (edited),
as of commit c40084e898,
GPL-2.0-or-later
💩
static types / type declarations
class CreditsAction extends FormlessAction {
private LinkRenderer $linkRenderer;
private UserFactory $userFactory;
/** Convert a Message to a MessageValue */
public function convertMessage( Message $m ): MessageValue {
includes/actions/CreditsAction.php (edited),
includes/Message/Converter.php (edited),
as of commit c40084e898,
GPL-2.0-or-later
🤩
static types / type declarations
static types / type declarations
strict types
<?php // a.php
function f( string $a ) {
var_dump( $a );
}
<?php // b.php
require_once __DIR__ . '/a.php';
f( 1 );
# logs string(1) "1"
strict types
<?php // a.php
function f( string $a ) {
var_dump( $a );
}
<?php // b.php
declare( strict_types = 1 );
require_once __DIR__ . '/a.php';
f( 1 );
# TypeError: f(): Argument #1 ($a) must be of type string, int given
strict types
arrow functions
$messageKeys = array_map( fn( MessageSpecifier $m ) => $m->getKey(), $messages );
$database->method( 'newSelectQueryBuilder' )
->willReturnCallback( fn() => new SelectQueryBuilder( $database ) );
tests/phpunit/includes/api/ApiBaseTest.php,
tests/phpunit/unit/includes/PingbackTest.php,
as of commit a4da635e8a,
GPL-2.0-or-later
arrow functions
new operators
'cat_pages' => $countByType['page'] ?? 0,
'cat_subcats' => $countByType['subcat'] ?? 0,
'cat_files' => $countByType['file'] ?? 0
// If length is null, calculate and remember it (potentially SLOW!).
// This is for compatibility with old database rows that don't have the field set.
$this->mSize ??= $this->mSlots->computeSize();
return count( $a ) <=> count( $b );
includes/Category.php,
includes/Revision/RevisionStoreRecord.php,
includes/GlobalFunctions.php,
as of commit 5c9674df53,
GPL-2.0-or-later
new operators
new functions
public function isKeyGlobal( $key ) {
return str_starts_with( $key, self::GLOBAL_PREFIX );
}
if ( str_ends_with( MW_CONFIG_FILE, '.php' ) ) {
public static function isExternal( $username ) {
return str_contains( $username, '>' );
}
includes/libs/objectcache/BagOStuff.php,
includes/Setup.php,
includes/user/ExternalUserNames.php,
as of commit a4da635e8a,
GPL-2.0-or-later
new functions
What’s
coming
up
PHP ≥8.0, coming soon™ to a production near you!
02
Union types
public function getWikiId(): string|false;
public function getId( string|false $wikiId = self::LOCAL ): int;
public function createComment(
IDatabase $dbw,
string|Message|CommentStoreComment $comment,
array $data = null
) {
includes/page/PageReference.php (edited)
includes/page/PageIdentity.php (edited),
includes/CommentStore/CommentStoreBase.php (edited)
as of commit c40084e898,
GPL-2.0-or-later
Union types
never return type
public function dieReadOnly(): never {
$this->fail( 'Unexpected call to selectField' );
throw new LogicException( 'Ooops' ); // Can't happen, make analyzer happy
includes/api/ApiBase.php (edited),
tests/phpunit/includes/Revision/
RevisionRendererTest.php,
as of commit c40084e898,
GPL-2.0-or-later
never return type
Named arguments
self::$extensionJsonCache[$this->extensionJsonPath] = json_decode(
file_get_contents( $this->extensionJsonPath ),
true,
512,
JSON_THROW_ON_ERROR
);
tests/phpunit/integration/includes/
ExtensionJsonTestBase.php,
as of commit c40084e898,
GPL-2.0-or-later
💩
Named arguments
self::$extensionJsonCache[$this->extensionJsonPath] = json_decode(
file_get_contents( $this->extensionJsonPath ),
associative: true,
flags: JSON_THROW_ON_ERROR
);
🤩
tests/phpunit/integration/includes/
ExtensionJsonTestBase.php (edited),
as of commit c40084e898,
GPL-2.0-or-later
Named arguments
?->
return $this->user === null ? null : $this->user->getName();
return $this->user?->getName();
$revComment = $rev->getComment() === null ? null : $rev->getComment()->text;
$revComment = $rev->getComment()?->text;
includes/session/UserInfo.php,
(edited),
includes/actions/HistoryAction.php,
(edited),
as of commit c40084e898,
GPL-2.0-or-later
?->
Attributes
/**
* @return stdClass|array|false
*/
#[\ReturnTypeWillChange]
public function current();
includes/libs/rdbms/database/
resultwrapper/IResultWrapper.php,
as of commit c40084e898,
GPL-2.0-or-later
Attributes
Constructor property promotion
class CreditsAction extends FormlessAction {
private LinkRenderer $linkRenderer;
private UserFactory $userFactory;
public function __construct(
Article $article,
IContextSource $context,
LinkRenderer $linkRenderer,
UserFactory $userFactory
) {
parent::__construct( $article, $context );
$this->linkRenderer = $linkRenderer;
$this->userFactory = $userFactory;
}
includes/actions/CreditsAction.php (edited),
as of commit c40084e898,
GPL-2.0-or-later
🤩
Constructor property promotion
class CreditsAction extends FormlessAction {
public function __construct(
Article $article,
IContextSource $context,
private LinkRenderer $linkRenderer,
private UserFactory $userFactory
) {
parent::__construct( $article, $context );
}
includes/actions/CreditsAction.php (edited),
as of commit c40084e898,
GPL-2.0-or-later
🤯
Constructor property promotion
match expressions
switch ( $ext ) {
case 'gif':
return 'image/gif';
case 'png':
return 'image/png';
case 'jpg':
case 'jpeg':
return 'image/jpeg';
}
return 'unknown/unknown';
includes/StreamFile.php,
as of commit c40084e898,
GPL-2.0-or-later
💩
match expressions
return match ( $ext ) {
'gif' => 'image/gif',
'png' => 'image/png',
'jpg', 'jpeg' => 'image/jpeg',
default => 'unknown/unknown',
};
includes/StreamFile.php (edited),
as of commit c40084e898,
GPL-2.0-or-later
🤩
match expressions
enums
// Audience options for accessors
public const FOR_PUBLIC = 1;
public const FOR_THIS_USER = 2;
public const RAW = 3;
public function getUser( $audience = self::FOR_PUBLIC, Authority $performer = null ) {
// Audience options for accessors
public const AUDIENCE_PUBLIC = 1;
public const AUDIENCE_RAW = 2;
includes/Revision/RevisionRecord.php,
includes/user/CentralId/CentralIdLookup.php,
as of commit 973253a7ee,
GPL-2.0-or-later
😱
enums
enum RevisionAudience {
case ForPublic;
case ForThisUser;
case Raw;
}
enum CentralIdAudience {
case Public;
case Raw;
}
😌
enums
That’s all!
Enjoy writing nicer PHP code and look forward to an even brighter future :)