【PHP8.2】true型が使えるようになる

PHP8.2で[false型とnull型が単独で使えるようになった](https://qiita.com/rana_kualu/items/2ef15cfa2dea8ba0f061)わけですが、こうなるとtrue型が存在しないのが片手落ちになってしまうわけですよ。

```php
class Foo{
	public bool  $var1;
	public false $var2;
	public true  $var3; // ←これだけ非対応
}
```

ということでtrue型も実装しようというRFCが提出されました。

投票期間は2022/05/29から2022/06/12であり、賛成33反対0の全会一致で可決されました。
PHP8.2から使用可能になります。

以下は該当のRFC、[Add true type](https://wiki.php.net/rfc/true-type)の日本語訳です。


# PHP RFC: Add true type

## Introduction

PHPは、PHP8.2において[null型とfalse型を単独サポートする](https://wiki.php.net/rfc/null-false-standalone-types)ことを決めました。
しかし、falseの対偶であるtrueは型として存在しません。

true型を実装しなかった理由は[UNION型のRFC](https://wiki.php.net/rfc/union_types_v2)において説明されています。

:::note info
現在では、エラーや不正が起きた際の関数の戻り値はnullにすることが推奨されていますが、歴史的理由から多くの内部関数はfalseを返してきます。
Statisticsセクションで示すように、union型を返す内部関数は大部分がfalseを含んでいます。

一般的な例としてはint|falseを返すstrposなどです。
これを正しく表記するとint|boolですが、これは関数がtrueを返すこともあるという誤った印象を与えます。

そのため、このRFCにはfalseのみを表すfalse疑似型が含まれています。
trueのみを表すtrue疑似型は、それが必要となる歴史的理由が存在しないため、このRFCには含まれません。
:::

しかし、今となってはtrue型をサポートする意義があると考えています。

PHP8.0においてValueErrorが導入され、多くのE_WARNINGがエラーや例外に昇格しました。
これによって、これまで失敗時にfalseを返していた内部関数の多くが失敗時にエラーを返すようになり、falseを返さないことが多くなりました。

理想的には、これらの関数はvoidを返すべきかもしれませんが、それは大きなBCブレークになります。
またvoidはfalsyな値であるため、以下のような使い方ができなくなります。

```php
if (function_which_could_fail_before()) {
    // 成功した
} else {
    // 失敗した
}
```

従って、内部関数の返り値をtrue型にすれば、静的解析器などのツールは、上記コードが到達不能であることを検出できるでしょう。

もう一つの動機としては、LSPを満たしつつ、より正確な型情報を提供できることです。
例として、bool型の拡張で常にtrue型を返すメソッドなどです。

```php
class User {
    function isAdmin(): bool
}

class Admin extends User
{
    function isAdmin(): true
    {
        return true;
    }
}
```


## Proposal

現在、型宣言が許可されている全ての場所において、true型を使用可能にします。

```php
class Truthy {
    public true $truthy = true;

    public function foo(true $v): true { /* ... */ }
}
```

### Compile time redundancy checks

コンパイル時の重複チェック。

既存のfalse型と同様、UNION型における冗長な記述はコンパイルエラーになります。

```php
function test(): bool|true {} // エラー
```

これは`Fatal error: Duplicate type true is redundant in %s on line %d`のエラーになります。

### Compile time error for using true|false instead of bool

bool型のかわりに`true|false`のUNION型を書いた場合はエラーになります。

```php
function test(): false|true {} // エラー
```

これは`true|false`と`bool`のサブタイプ関係を定義しなければならないからです。
従って、本RFCでは`true|false`の使用を単に禁止することにします。

### Examples

内部関数やユーザランドでも、trueだけを返す関数は多く存在します。
例としては`array_sort()`・`mysqli_*()`・`SplFixedArray::setSize()`等です。
ユーザランドでのtrue型導入が有効な例としては、[composer](https://github.com/composer/composer/blob/main/src/Composer/Command/DiagnoseCommand.php#L204)等があります。


### Reflection

true型のリフレクションの動作は、他の型と同じです。

## Backward Incompatible Changes

互換性のない変更はありません。

## Proposed PHP Version

PHP8.2。

## Future Scope

この項目は、今後の展望であり、このRFCには含まれていません。

### Literal Types

リテラル型。

今回導入されたtrue型、および以前導入されたfalse型は、[TypeScript](https://www.typescriptlang.org/docs/handbook/advanced-types.html#string-literal-types)で言うところのリテラル型の一種です。
これは、特定の型に限定されたENUM型のようなものです。

```php
type ArrayFilterFlags = 0|ARRAY_FILTER_USE_KEY|ARRAY_FILTER_USE_BOTH;
array_filter(array $array, callable $callback, ArrayFilterFlags $flag): array;
```

ENUMではなくリテラル型を使用する利点は、中身のはっきりしないENUMではなく基本型の値を直接扱うことができることです。
そのため、互換性を壊すことなく後付けすることが可能です。

しかし、著者の思うところとしては、ENUMはリテラル型より優れています。
今回リテラル型であるtrueを追加する理由は、既にPHPエンジンにはfalse型が実装されているので実装のコストが極めて低いから、そしてfalse型があるのにtrue型が存在しないのはおかしいからということです。

### Allowing usage of true|false

`true|false`のUNION型を使用可能にする。

そのためには`true|false`型とbool型の間のサブタイピングを定義する必要があります。

### Type Aliases

型エイリアス。

このように型が複雑になってくると、型宣言を再利用する価値が出てくるでしょう。
これには2つの方法が考えられます。
ひとつはローカルエイリアスです。

```php
use int|float as number;

function foo(number $x) {}
```

この`number`はローカルからのみ見えるエイリアスであり、コンパイル時に元の`int|float`に展開されます。

もうひとつは型定義のエクスポートです。

```php
namespace Foo;
type number = int|float;
```

こちらは`\Foo\number`をどこからでも利用可能になります。


## Implementation

[プルリクエスト](https://github.com/php/php-src/pull/8326)。

# 感想

プルリクを見てみると、19ファイル300行弱というかなり小さな内容です。
しかもそのほとんどはテストコードであり、[実質的な変更](https://github.com/php/php-src/pull/8326/files#diff-85701127596aca0e597bd7961b5d59cdde4f6bb3e2a109a22be859ab7568b4d2)は11行でした。
マジかよ。

型の追加という、傍目にはとても大きな規模と思われる変更が、たったこれだけで済んでしまうんですね。
まあ流石に本体部分だけであり、たとえば[sort](https://www.php.net/manual/ja/function.sort.php)関数の返り値を`bool`から`true`にする、みたいなのは入ってないみたいですが、それでもびっくりですよ。
`due to the extremely low implementation complexity`ってのはこういう人たちにとってのって意味なんだろうなあと思っていたら文字どおりの意味だったでござる。

ということで、false型に続いてtrue型までもがPHPで使えるようになります。
もはやPHPには型がないなんて認識の人は古い人間なので相手にしないようにしましょう。

ちなみに、PHPのコードは型宣言を全て抜いても動きます。
そこまで互換を保ってるのは逆にびっくりですね。