PHPにおけるvoidとは何か

PHPの文脈では`void`はいくつかの異なる意味で利用されます。

## そもそもvoidってなんだ

> **void**（ボイド）は、プログラミング言語において、「何もない」といった意味の型などに使われる名前である。プリミティブとしてキーワードないし予約語であることも多い。

*([void (コンピュータ) - Wikipedia](https://ja.wikipedia.org/wiki/Void_(%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF))[^1]より引用)*

C言語では「引数を受け取らず、引数を返さない」関数のプロトタイプを `void f(void);` のように記述できますね。

## PHPマニュアルにおける`void`

[PHP: 本ドキュメントにおける疑似的な型および変数 - Manual](http://php.net/manual/ja/language.pseudo-types.php)に記載があります。

> ### void
>
> 返り値の型が `void` である場合は、 返り値に意味がないことを表します。パラメータ一覧で `void` が使用されている場合は、 その関数がパラメータを受け付けないことを表します。

具体的には以下の通りです。

> ### header
> (PHP 4, PHP 5, PHP 7)
>
> header — 生の HTTP ヘッダを送信する
>
> #### 説明
> ```php
> void header ( string $string [, bool $replace = true [, int $http_response_code ]] )
> ```

*([PHP: header - Manual](http://php.net/manual/ja/function.header.php)より引用)*

これは、`header()`の実行結果に何の値も返さないことを意味します。

つまり、 `$result = header($str);` や `var_dump(header($str));` のような実行は無意味です。無意味ではありますが、これらの返り値は実際には[NULL](http://php.net/manual/ja/language.types.null.php)になります。

> ### get_defined_vars
> (PHP 4 >= 4.0.4, PHP 5, PHP 7)
>
> get_defined_vars — 全ての定義済の変数を配列で返す
>
> #### 説明
>
> ```php
> array get_defined_vars ( void )
> ```

*([PHP: get_defined_vars - Manual](http://php.net/get_defined_vars)より引用)*

今度は引数が `( void )` です。これは特別な引数を期待するわけではなく、リストの中身が*void*、つまり空なので、**引数には何も受け取らない**ことを意味します。このような関数に対して引数を渡しても多くの場合エラーにはなりませんが、無意味です。

## PHPDocにおける`void`

PHPマニュアルと同じく[`@return`タグ](https://docs.phpdoc.org/references/phpdoc/tags/return.html)などで利用できる擬似的な型です。

引数を返さない関数は、PHPDocの型注釈記法では以下のように書きます。

```php
/**
 * @return void
 */
function hoge()
{
    echo "hoge";
}
```

後述する型宣言とは異なりPHPDocは**実行時には何の影響も及ぼしません**。実際ただのコメント行なので、PHP 7.1未満でも当然利用することができます。

## PHP7.1の型宣言における`void`

PHP7から[返り値に型宣言が書ける](http://php.net/manual/ja/functions.returning-values.php#functions.returning-values.type-declaration)ようになりましたが、PHP7.1ではさらに機能が強化され、`void`キーワードが書けるようになりました。

```php
function hoge(): void
{
    echo "hoge";
}
```

この型宣言は、**値を返すとエラーになる**ことが特徴です。

```php
<?php

function a(): void
{
    echo "a";
    if (false) {
        return true;
    }
}

a();
// Fatal error: A void function must not return a value in /in/ZidNI on line 7
```

この特徴は、実際に実行されない処理であっても`return`に返り値を書くだけでエラーとして捕捉できるので安心です。

以下のように引数のない`return`は問題ありません。

```php
function b(): void
{
    if (foo()) {
        return;
    }

    bar();
    buz();

    return; // 省略可能
}
```

## ツールのサポート

PhpStormは返り値が`void`である関数の返り値を利用しようとすると、わかりやすくハイライトされます。

<img width="278" alt="PhpStorm: 'void' function 'a' result used" src="https://qiita-image-store.s3.amazonaws.com/0/4121/d0fcda9c-8732-d63c-0c79-44b4acafc7f1.png">


また、これは[Phan](https://github.com/phan/phan)のような単体の静的解析ツールでも検出できます。

```
funcs.php:8 PhanTypeVoidAssignment Cannot assign void return value
```

## PHPDocと型宣言、どちらで書くか

PHPDoc形式の型注釈`@return void`と、PHP 7.1形式の型宣言`: void`のどちらで書くかは、「そのコードベースがPHP 7.1以降専用か否か」だけで選んで支障ありません。

さきほど紹介した二つのツール、PhpStormとPhanは、この二種類の表記を同じように見做しますので、どちらで書いても同じです。`: void`型宣言は特別なツールを追加で導入しなくても、PHP 7.1があれば定義時に間違って返り値を返してしまったことを簡単に検出できるのでおすすめです。

二種類の形式を両方書くことも可能ですが、**冗長なだけなので両方書くメリットはありません**。どちらかだけを書けば大丈夫です。金輪際PHP7.1未満でコードを動かすことはないと明らかならば、`: void`型宣言に統一した方が簡潔になります。

広く利用されたいライブラリなどでPHP7.1未満をサポートしたいならば、PHPDoc形式の`@return void`で書いた方が無難です。

## どうでもいいこと

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr">PHPクイズ: この中で、Syntax Errorが検出される一番上の行はどれ？？ <a href="https://t.co/zwHXYaYuti">pic.twitter.com/zwHXYaYuti</a></p>&mdash; nya-n (@tadsan) <a href="https://twitter.com/tadsan/status/964159346601963520?ref_src=twsrc%5Etfw">February 15, 2018</a></blockquote>

```php
<?php

namespace
{
    const void = null; // 1

    goto void;

    function void(     // 2
        $void = void   // 3
    ): void            // 4
    {
        var_dump(void);
        return void;   // 5
    }
}

namespace void
{
    void:
        void(void);
}
```

`void`はPHP7.1で擬似的な型名として[その他の予約語](http://php.net/manual/ja/reserved.other-reserved-words.php)に追加されました。

> PHP 7.0.0 以降は、これらのキーワードを プロパティや定数の名前として使えるようになりました。 また、クラスやインターフェイスそしてトレイトのメソッド名としても使えるようになりました

その前の、PHP7.0でメソッドとしての使用が許可された[キーワードのリスト](http://php.net/manual/ja/reserved.keywords.php)とは異なり、「その他の予約語」はクラス名(トレイト・インターフェイスを含む)として利用できないだけで、関数名・メソッド名・名前空間・定数名・gotoのラベルなどのシンボルとして、問題なく定義することができます。

そんなことをわざわざやっても特にメリットはないので、PHPおもしろ知識として~~留めておいてください……~~ 忘れて問題ないです。

<img width="375" alt="void, void, void, void, void, void, void, void, void, void, void, void..." src="https://qiita-image-store.s3.amazonaws.com/0/4121/f7946045-7e92-9d98-a795-b9b95b2bddbe.png">


## [追記] PHPにvoidキャスト/演算子はない

> また、JavaScriptなど、言語によっては、void演算子が存在する。JavaScriptのvoid演算子は、オペランドの式を評価してその値を得る計算をおこなうが、その値は捨てられ、値を返さない。C言語における、値を捨てることを明示するためのvoid型へのキャスト（たとえば、`(void)printf("Hello");` のように書く）に似ている。

PHPには、この用途の`(void)`はありません。その代りと言ってはなんですが、`(unset)`キャストが存在します。これは、どんな値をも`null`にキャストします。

```php
Psy Shell v0.8.17 (PHP 7.1.14 — cli) by Justin Hileman
>>> (unset)[]
=> null
>>> (unset)fopen('php://stdout', 'w');
=> null
>>> $a = "AAAAA";
=> "AAAAA"
>>> (unset)$a
=> null
>>> $a
=> "AAAAA"
>>> unset($a)
>>> $a
PHP Notice:  Undefined variable: a on line 1
```

注意したいのは、`(unset)`キャストはどんな値が来ても`null`を返しますが。変数を未定義状態に戻すわけではありません。それは`unset($v)`です。

こんなの何の訳に立つのか？ ふつうに `null` って書けばいいじゃね？　なんでこんなキャストがあるの？

さあ、わかりません……。むかしそうやってぼやいてたら誰かに納得のいく説明をしてもらった気がしますが、僕はもう覚えてないので、きっとどうでもいい感じのことです。

## 脚注

[^1]: [2017-04-02 19:51:47版](https://ja.wikipedia.org/w/index.php?title=Void_(%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF)&oldid=63593116)