43
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Declined】PHP8に入ることのできなかった機能たち

Last updated at Posted at 2022-02-28

PHP8では大量のRFCが受理され、多くの文法の拡充が行われました。
光あるなら陰もまたあり、すなわち華々しく活躍するRFCの傍らには、却下され消えゆくRFCもまた存在するということです。

ということでPHP8に入り込むことを目指したものの却下されたRFCを見てみることにしましょう。
採用されたRFCと却下されたRFCを見比べることで、PHPの今後の方向性とかがわかるかもしれませんよ。

Declined RFC

Dump results of expressions in php -a

賛成11、反対6で却下。

php -aのインタラクティブシェルは寡黙です。

$ php -a
Interactive shell

php > 1+1;
php > $a = 1;
php > $a;
php > var_dump($a);
int(1)

代入などの操作を行っても何も言いません。

これがたとえばJavaScriptコンソールであれば毎回最後の演算結果を出力します。

> 1+1;
<< 2
> a = 1;
<< 1
> console.log(a);
1

そんなわけでPHPコンソールも饒舌に、デフォルトで最後の演算結果を出力しよう、という提案。

また出力を制御する関数readline_interactive_shell_result_function()というちょっとどうかしてる名前の関数も導入しようという提案でしたが、却下されました。
おそらく演算結果の出力だけだったら通ってた気がしないでもない。

StackFrame class

賛成14、反対14で却下。

debug_backtraceはトレースの返り値が配列です。
そこでStackFrameクラスのインスタンスにしようという提案。
これによってメモリ省力化につながります。
RFCでは、巨大なテストスクリプトでメモリ消費量が390MBから192MBへ削減できたということでした。

しかし投票時点でもプルリクにメモリリークのバグがあり、テストスクリプトが公開されていなかったので検証もできないということで却下になりました。
そこらへんきちんと準備していたらおそらく通っていただろうに、惜しいことをしたものです。

Make constructors and destructors return void

賛成34、反対22で却下。

コンストラクタ、デストラクタの返り値にvoidを強制する提案。

マニュアルではコンストラクタ、デストラクタの返り値の型はvoidとなっています。
しかし、これを無視して値を返しても何も言われないし、むしろvoidを付けると逆に怒られます。

class HOGE{
    public function __construct(){
        return 1; // OK
    }
    public function __destruct():void{ // Fatal error: Method HOGE::__destruct() cannot declare a return type
    }
}

そこでマニュアルと挙動を合わせ、コンストラクタ、デストラクタは値を返さないようにし、voidを付けられるようにしようというものです。

正しくするといえばそうなのですが、これまでvoid以外を返していたメソッドが動かなくなるという非互換があったため反対が多くなったようです。

Rename T_PAAMAYIM_NEKUDOTAYIM to T_DOUBLE_COLON

賛成44、反対30で却下。

::はT_PAAMAYIM_NEKUDOTAYIMというよくわからない名前で知られています。
意味がわからないのでT_DOUBLE_COLONにしようという提案。

これ10年前からずっと言われているのですが、T_PAAMAYIM_NEKUDOTAYIMが導入されたのは20年以上前なので歴史の差では勝てません。
実はT_DOUBLE_COLONも大昔から定義されているのですが、これがどうなっているのかはよくわかりません。

Opcache optimization without any caching

賛成10、反対13で却下。

Opcacheの機能は最適化とキャッシュです。
これはセットになっていて、キャッシュせずに最適化だけ使用することはできません。

ということで最適化だけ単体で使えるようにしようという提案。

しかし、これを実行するにはoptimizerをopcacheからPHPコアに持っていかないといけないみたいな話になり、話が広がりすぎてしまいました。

Match expression

賛成6、反対28で却下。

match式の提案です。

$expressionResult = match ($condition) {
    1, 2 => foo(),
    3, 4 => bar(),
    default => baz(),
};

このRFCは当初からブロック構文を含んでいたのですが、メモリリークバグなどの問題があり紆余曲折の末に却下されました。

その後、ブロック構文を外すなど機能を制限してブラッシュアップされたMatch expression v2として提案・受理されPHP8.0から使えるようになりました

Type casting in array destructuring expressions

賛成6、反対26で却下。

型キャストの新文法の提案。

// これを
[$now, $future] = explode(',', '2020,2021');
$now = (int) $now;
$future = (int) $future;

// こうしたい
[(int) $now, (int) $future] = explode(',', '2020,2021');

// これを
$users = [ ['id'=>'1', 'name'=>'ore'], ['id'=>'2', 'name'=>'omae'] ];
foreach($users as ['id'=>$id, 'name'=>$name]){
	$id = (int)$id;
	echo "{$name}のIDは{$id}です";
}

// こうしたい
foreach($users as ['id'=>(int)$id, 'name'=>$name]){
	echo "{$name}のIDは{$id}です";
}

PHPの場合、CSVやDB等から取ってきた値は全てstring型なので、取り出す時点でint型にしておきたいなどの需要はあるでしょう。
そんなときにあれば便利かもしれませんが、それだけのために新構文を追加する説得力が出せずに却下されました。

Compact Object Property Assignment

賛成2、反対48で却下。

オブジェクトのプロパティに一括で値を投入したいという提案。

$foo->[
    a = 1,
    b = myfunc(),
    c = $foo->bar(),
];

驚きの反対率で却下されました。

$foo->a = 1;
$foo->b = myfunc();
$foo->c = $foo->bar();

正直、現状の書き方と比べて長さもあまり変わらないですね。

Userspace operator overloading

賛成38、反対28で却下。

ユーザランドでの演算子オーバーロードの提案。

ここで言う演算子オーバーロードは、PHPにおけるオーバーロードではなく一般的に言うところの演算子オーバーロードです。

PHPでは演算子を再定義することはできません。
+とか<とかの定義は不変ということです。
またPHP8以降、オブジェクトへの算術演算はTypeErrorになります。

しかしGMPDateTimeなど、独自の演算子オーバーロードが施されているオブジェクトも存在します。
ならばユーザランドでも同じようにオーバーロードできてもいいじゃない。

class Vector3(){
    public static function __add($lhs, $rhs) {
        // 演算子'+'の定義
    }
 
    public static function __mul($lhs, $rhs): ?Vector3 {
        // 演算子'*'の定義
    }
}

$a = new Vector3();
$b = new Vector3();

$x = $a + $b;
$y = 3 * $b;

と、このように各演算子に対してマジックメソッドで独自の処理を実装できるという機能です。
しかし-は実装できるが+が実装できない例など様々な問題が広がりまくった挙句却下となりました。

User Defined Operator Overloads

賛成21、反対24で却下。

さきほどのUserspace operator overloadingと似たような、演算子オーバーロードの提案です。
ほぼ同時期に出たというだけでオーサーは別人です。

class Collection {
    // 演算子'+'の実装
    operator +(Collection $other, OperandPosition $operandPos) {}
}

新しいキーワードoperatorを追加し、演算子を直接定義できるようになります。

わしゃこのRFCに100時間以上注ぎ込んでるんじゃよとのことでしたが、長い長い議論の果てに却下となりました。

Write-Once Properties

賛成23、反対23で却下。

一回だけ書き込みが可能で、書き込んだらその後は変更できなくなるプロパティです。

class Foo
{
    <keyword> public int $a;
    <keyword> public array $b;
    <keyword> public object $c;
 
    public function __construct()
    {
        $this->a = 1;
        $this->b = ["foo"];
    }
}
 
$foo = new Foo();

$foo->a = 2;              // EXCEPTION: 書き込み済なのでNG
var_dump($foo->b);        // publicなら読み込みはOK。
$foo->c = new stdClass(); // 一回目なので書き込める。

PHP8.1において、ほぼ同じ内容で受理されました。
違いはクラス外から書き込み可能か否かくらいで、どうして明暗が分かれたのかはよくわかりません。

declare(function_and_const_lookup='global')

賛成2、反対35で却下。

PHPの関数呼び出しは、
・まず現在の名前空間に関数があるか探す
・なかったらグローバル名前空間の関数を探す
と2段階になっていて効率がよくありません。

そこで常時グローバル名前空間を探すようにするディレクティブdeclare(function_and_const_lookup='global')を導入しようという提案。

declare(
    strict_types=1,
    function_and_const_lookup='global'
);

namespace MyNS;

use function OtherNS\my_function;
use const OtherNS\OTHER_CONST;

if (version_compare(PHP_VERSION, '8.0.5') >= 0) {
    // このversion_compareはグローバル関数であることが保証される
}

当初はuse function *;って書きたかったけど書けなかったからどうにかしたいって話から始まったようです。

Deprecate Backtick Operator (V2)

賛成11、反対26で却下。

バッククォートをE_DEPRECATEDにしようという提案。

バッククォートはシェル実行するという危険な機能にもかかわらず、わかりやすい目印がなく引用符"'と紛れやすいため安全ではありません。
そもそもshell_execと同一であるため、こちらに揃えた方がよいでしょう。

個人的にはさっさと消してしまっていいと思うのですが、しかしこのRFCが提出されたのはDeprecate short open tags, againと同時期です。
すなわち銀河に平和がもたらされた直後であり、やはりこちらもZeevが反対を表明しています。

このあたりの関係もあり、却下されたのだと思われます。

Object Initializer

賛成3、反対26で却下。

新しいオブジェクトイニシャライザ構文の提案。

// これを
$customer = new Customer();
$customer->id = 123;
$customer->name = "John Doe";

// こうしたい
$customer = new Customer {
  id = 123,
  name = "John Doe",
};

この例ではファクトリーメソッドを使うべきだろと突っ込まれていました。
まあそりゃそうだ。

Add array_group function

賛成0、反対19で却下。

関数array_groupがほしい、という提案。

$array = [
    ['id' => 1, 'name' => 'hassan'],
    ['id' => 2, 'name' => 'sara'],
    ['id' => 3, 'name' => 'selim'],
    ['id' => 4, 'name' => 'chris'],
    ['id' => 5, 'name' => 'sara'],
];

var_dump(array_group($array, null, 'name')); // ↓こんなふうになる
[
    'hassan' => [ ['id' => 1, 'name' => 'hassan'] ],
    'sara' => [ ['id' => 2, 'name' => 'sara'], ['id' => 5, 'name' => 'sara'], ],
    'selim' => [ ['id' => 3, 'name' => 'selim'] ],
    'chris' => [ ['id' => 4, 'name' => 'chris'] ],
];

キーが同じ要素をまとめたいという要望ですね。
意外と需要があるようで、Qiitaでも何度も再発明されています。

元々はarray_columnの引数で返り値を変えようという意見であり、これには全員がやめろと突っ込んだ結果新たに関数を作るようにRFCを変更したのですが、それでも結局却下されてしまいました。

PHP Namespace in core

賛成19、反対24で却下。

PHPプロジェクトは\PHPで始まる名前空間を予約しています
が、これがあんまり使われてないので言語コア機能は今後これを使っていこうぜという提案。

たとえば\PhpTokenではなく\PHP\PhpTokenにしようといったかんじです。
なお、既に実装されているものについてはこのRFCでは変更しません。

どうして却下されたかよくわからないのですが、MLによると『言語コア機能は』がひっかかった人が多いように見えます。

PHP Namespace Policy

賛成13、反対17で却下。

上のPHP Namespace in coreと同時期に作られたRFCです。
特に関連しているわけではなく、たまたま時期が被ったみたい。

PHPコア用に\PHP、拡張機能用に\Ext名前空間を予約します。
また名前空間の命名規則も規定します。
名前空間\Foo\Bar\Bazがあれば、Fooが企業名、Barがコンポーネント名となります。
なお、既に実装されているものについてはこのRFCでは変更しません。

規程までするのはやり過ぎと思われたのか、こちらも同様に拒否されました。

あと\Extを使うには申請しなきゃいけないとかPEARを思い出すような仕様があったのも原因かもしれません。

感想

そんなの却下されて当たり前だろというものから、どうしてこれが却下されたんだ?というものまで、様々なRFCが提案されています。
これらの却下されたRFCと、受理されたRFCを比べて今後のPHPの進む先を探ってみるのも楽しいかもしれませんね。

43
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?