PHP
rfc
PHP7.3

PHP RFCがたくさん投票中

やたらめったら腰が重いことで有名なPHP RFCですが、なんとなく覗いてみたら突然6個のRFCが投票フェーズに入ってました。
今までせいぜい2個くらいしか見たことなかったのでびっくりだよ。

せっかくなので現在投票中のRFCを見てみます。
みんなも投票に参加してみよう。
なお、賛成反対数は2018/07/12時点での数です。

In voting phase

User-defined object comparison

ユーザ定義のオブジェクトで比較を定義できるようにしようというRFCです。
オブジェクトの比較は、現在の仕様では、
==は同じクラスのインスタンスであればtrue
===は同じインスタンスであればtrue
となっています

    $a = new \stdClass();
    $b = $a;
    $c = clone $a;

    var_dump($a==$b, $a===$b); // true, true
    var_dump($a==$c, $a===$c); // true, false

また不等号については明文化されていませんが、プロパティを順に見ていって最初に見つかった異なる値を比較するという、わりかし微妙な実装になっています。

class HOGE{
    private $a;
    private $b;
    public function __construct($a, $b){
        $this->a = $a;
        $this->b = $b;
    }
}

$a = new HOGE(10, 20);
$b = new HOGE(10, 30);
$c = new HOGE(11, 0);

var_dump($a>$b, $a<$b); // false, true 20<30
var_dump($a>$c, $a<$c); // false, true 10<11
var_dump($b>$c, $b<$c); // false, true 10<11

異なるオブジェクト同士の比較は基本的にfalseですが、宇宙船演算子は何故か1になります。

    $a = new \stdClass();
    $b = new \DateTime();

    var_dump( $a == $b, $a < $b, $a > $b ); // false, false, false
    var_dump( $a <=> $b ); // 1 ←?

日時クラスなど任意に大小比較を行いたいクラスはこのような仕様では困るので、独自の比較を行えるマジックメソッド__compareTo__equalsを追加しよう、という提案がされました。

class Example{
    /**
    * $a < $bであれば$a::__compareToが呼ばれて引数は$bになる
    * @param mixed 右辺
    * @return int 左辺が大きいなら1、小さいなら-1、等しいなら0
    */
    public function __compareTo($other): int{
        return $this <=> $other;
    }

    /**
    * 緩い比較`==`で呼ばれる
    * @param mixed 右辺
    * @return boolean $a == $bならtrueを返す
    */
    public function __equals($other): bool{
        return $this == $other;
    }
}

これによって<<=>>===!=<=>の挙動を操作することができるようになります。
===は同じインスタンスか否かなので変更の余地なしです。

これによって例えばnew \Metre(1) == new \Centimetre(100)がtrueになったり、new \Yen(10000) < new \Dollar(100)みたいな比較ができるようになります。
できるようになるといっても実際書いてみるとちょっと気持ち悪いので、普及するかは微妙な気がしますが。

2018/07/16まで投票中、賛成4反対11でおそらく却下されます。¯_(ツ)_/¯

iterable_to_array() and iterable_count()

PHP7.1で型宣言Iterableが追加されました。
これによって配列とイテレータをひとまとまりで操作できるようになりました。

しかし配列関数を使いたい場合など、うっかりイテレータをarray関数に突っ込むと死にます。
そこでIterableを安全に配列にするiterable_to_array関数を追加しましょう。

    // Iterableを配列にする
    $array = iterable_to_array($iterable);
    // これと同じ
    $array = is_array($iterable) ?: iterator_to_array($iterable);

    // Iterableの個数を求める
    $count = iterable_count($iterable);
    // これと同じ
    $count = is_array($iterable) ? count($iterable) : iterator_count($iterable);

いや、要らんだろこれ。

2018/07/16まで投票中、賛成8反対20でおそらく却下されます。

Class Friendship

protectedなメソッドとプロパティは、本来は自身か自身をextendしたクラスからしかアクセスすることはできません。
Class Friendshipは、友達には自分のprotectedを見せてもいいようにしようというRFCです。
元ネタのC++ではprivateにもアクセスできるのですが、議論の結果PHPではprotectedだけになったようです。

class A{
    friend B;

    protected $trueName = 'AAA';

    public function getBsTrueName(B $b){
        return $b->trueName;
    }
}

class B{
    protected $trueName = 'BBB';

    public function getAsTrueName(A $a){
        return $a->trueName;
    }
}

$a = new A();
$b = new B();

$b->getAsTrueName($a); // AAA
$a->getBsTrueName($b); // Fatal Error: Cannot access protected property

AはBのことを友達だと思っているのでBはA::trueNameが読めます。
BはAのことを友達だと思っていないのでAはB::trueNameを見ることができません。

これができて何がうれしいかというと、なんといってもテスト開発者ですね。
PHPUnitとかとにかくひたすらReflection山盛りですから、ここを「friendって書いとけ」で終わらせられるのは楽そうです。
普通の開発者にはあまり恩恵はなさそうです。

2018/07/13まで投票中、賛成6反対25でおそらく却下されます。

Deprecations for PHP 7.3

古い機能がDeprecateになり、PHP8で削除されます。

Undocumented mbstring function aliases

mbereg_search_getposって関数知ってる人いるでしょうか。
これはmb_ereg_search_getposのエイリアスで、実はmb_ereg**関数には全てmbereg_**というエイリアスが用意されています。
これらは一切ドキュメント化されていないし、意味もないので削除しましょう。

2018/07/16まで投票中、賛成34、反対1でおそらく受理されます。

String search functions with integer needle

strposstrstrなどの検索対象文字列に数値を与えると、コードポイントとして解釈されます。

    $str = "There are 97 apples";
    var_dump(strpos($str, "97")); // 10
    var_dump(strpos($str, 97));   // 6

あまりにわかりにくすぎるのでコードポイントの解釈をやめます。
PHP7.3でDeprecateとし、PHP8では単純に文字列として扱います。

2018/07/16まで投票中、賛成29、反対1でおそらく受理されます。

fgetss() function and string.strip_tags filter

fgetssという関数が……誰も知らないだろこんな関数。
fgetsしたあとstrip_tagsするという、誰得にも程がある関数です。
そもそもstrip_tags自体、多くのバグや機能自体の限界から運用に耐えられるようなものではなくなっています。
ということでfgetss、gzgetssSplFileObject::fgetssstring.strip_tagsストリームフィルタをPHP7.3でDeprecate、PHP8で削除します。

2018/07/16まで投票中、賛成18、反対4でおそらく受理されます。

Defining a free-standing assert() function

assertはPHP7から言語構造となり、ノーコストで呼び出しができるようになりました。
zend.assertions=-1をセットすると完全に無視されます。

この設定、実はグローバルでないassert関数も影響を受けます。

namespace TEST{
    function assert(){
        throw new \Exception('hoge');
    }
    assert(1==1);
}

zend.assertions=1であればUncaught Exception: hogeが発生しますが、-1のときは何も起こりません。

そこでPHP7.3ではassert関数定義をDeprecateとし、PHP8では関数定義自体を禁止します。
$this並のアンタッチャブルになるようです。
独自のassert関数を定義したい場合はassert_optionsを使いましょう。

なおassertメソッドは問題なく定義可能です。

2018/07/16まで投票中、賛成19、反対7でおそらく受理されます。

FILTER_FLAG_SCHEME_REQUIRED and FILTER_FLAG_HOST_REQUIRED flags

検証フィルタのFILTER_VALIDATE_URLフィルタに、FILTER_FLAG_SCHEME_REQUIREDとFILTER_FLAG_HOST_REQUIREDというフラグがあります。

実はこのふたつ、FILTER_VALIDATE_URLフィルタにデフォルトで入っている上に無効化する方法がありません。
それなのにこのようなフラグが存在することによって、正しいURLをネガティブ判定するうえにデフォルトでは入ってないと勘違いするようなことになり、良くありません。

そこでPHP7.3でこれらをDeprecateとし、PHP8で削除します。

2018/07/16まで投票中、賛成30、反対0でおそらく受理されます。

pdo_odbc.db2_instance_name php.ini directive

pdo_odbc.db2_instance_nameはPHP5.1.1で実装されたものの当時からずっと非推奨のままでした。
これはDB接続用の設定にもかかわらずグローバルで定義されるため、全てのDB接続設定がプロセス全体で書き換えられてしまいます。

そこでPHP7.3でこの設定をDeprecateとします。PHP8で削除とは書かれていません。

2018/07/16まで投票中、賛成28、反対0でおそらく受理されます。

Deprecate and Remove Case-Insensitive Constants

PHPの定数は大文字で書くのが一般的ですが、別に小文字でも書けます。
それはいいのですが、defineには大文字小文字を区別しないという謎すぎるオプションが存在します。
これを使うと簡単に定数定義変更できます。

define('hoge', 1, true);
var_dump(HOGE); // 1
define('HOGE', 2);
var_dump(HOGE); // 2

誰も得しないし実装も大変だしとてもよろしくないので、PHP7.3でこれらをDeprecateとし、PHP8でdefineの第三引数を削除します。

2018/07/16まで投票中、賛成34、反対0でおそらく受理されます。

array_key_first(), array_key_last() and array_value_first(), array_value_last()

先日紹介した配列の最後を求めるRFCです。

2018/07/16まで投票中。
array_key_first/lastは賛成17、反対11でおそらく受理されます。
array_value_first/lastは賛成14、反対13で拮抗しています。

PHP 7.3 Implemented

PHP7.3の新機能以降、早くも3個のRFCが受理されていたので、ついでに紹介してみます。

Make compact function reports undefined passed variables

賛成32、反対5で受理されました。

compact関数は、引数の変数名が存在しない場合、単に無視されます。

$array = compact('hoge'); // 何も起こらない
var_dump($array); // []

失敗したかどうかわかりにくいため、今後は存在しない変数名を渡された場合にE_WARININGが発生するようになります。

ちなみに、どうして無視する実装になっていたのかは、誰にもわからなかったらしいです。

Argon2 Password Hash Enhancements

賛成17、反対0で受理されました。

最近提出されたPHP RFCで紹介した、password_hash関数のアルゴリズムを追加するRFCです。

Deprecate and Remove image2wbmp

賛成14、反対0で受理されました。

GDのWBMP出力関数はimage2wbmpimagewbmpの2種類があります。
元々は異なる処理をしていたらしいのですが、PHP5.0.0以降は実質的に同じようです。
そのためimage2wbmpを削除し、imagewbmpに統一します。

といってもwbmp自体、使っている人がいるのか怪しいものですが。

感想

投票フェーズまで進むRFCは、これまでは数ヶ月にひとつとかそういうレベルだったのに、いきなり何かあったのでしょうかね?
とはいえ受理されそうなのは削除されて当然な謎機能ばかりで、使えそうなのはarray_key_first/lastくらいなのが残念。

他にもDistrust SHA-1とかThrow Exception on Attempt of Constant Redefinitionとか、速攻受け入れるべきRFCも色々あるので、今後も足を止めずにどんどん活動してほしいものですね。