RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

PHPで型宣言してますか?

PHPは動的型付け言語に分類されます。
私たちは型を意識することなくプログラミングができます。
要するにPHP側でいい感じに型変換してくれるので好き勝手できます。
ただ、その暗黙の型変換が呼び起こすデメリットも存在します。
一例を挙げるなら想定外のバグが起こる可能性があるということでしょうか。

想定しない形で関数に値が渡ってしまったり、条件式が想定通りいかなかったり。
型を意識しないというのは保守性・コードの可読性といった場面でも悪影響を及ぼします。
特に複数人で開発している場合は引数の型からも製作者の意図が読めたりします。

過去のコードに今から全部型宣言をしようと言っているわけではありません。
もちろんしている方が好ましいとは思います。
ただ新規でコードを書くときは少し意識して書いてみるのもいかがでしょうかといったところです。

ということで、本記事ではPHPの関数の型宣言について書いていきたいと思います。

はじめに

そもそもPHPで関数書くときはどういった書き方をするでしょうか?
単純な足し算をする関数を例に見てみましょう。

<?php
public function add($a, $b) {
    return $a + $b;
}

大体こんな感じでしょうか。
私は最初にPHPの勉強を始めた時、情報が少なすぎて不安になった記憶があります。

例えばですがJavaだとこうなります。

public int add(int a, int b) {
    return a + b
}

ここで違いを見ていくと、PHPの方には引数と返り値の型が書いていないことがわかります。
ですので、PHPのadd関数は整数を渡しても少数を渡しても文字列を渡しても動きます。
結果はさておきとして。

Javaの方はint以外はエラーになります。
なぜなら、intで型の指定をしているからです。
この二つを比べてPHPの方が良くないと言いたいわけではありません。
私が言いたいのは、
この関数の引数にどういった値が渡ってきて、どういう結果を返すのか考慮できていますか?
ということです。
それが考慮ができているというのであれば問題はありません。
ただ完璧に考慮できているというなら、もうPHPでも型宣言しちゃえばいいよねという話です。

それでエラーになるというなら考慮パターンが漏れているということなので。

関数の型宣言

ここではPHPで関数を書くときにどのように型宣言できるか見ていきます。
先程の足し算をする関数をJavaの方と同じ形で書くとこうなります。

<?php
public function add(int $a, int $b) : int {
    return $a + $b;
}

引数の型宣言はJavaと同じです。
ちょっと違うのは返り値を書く位置です。
個人的にはJavaやCなどより見やすいと思っているので好きです。
このように書くと、Javaと同じように引数と返り値の型をチェックしてくれてエラーを出してくれます。
と・・・言いたいところなのですがこれが微妙に違います。
このままだと返り値の型が正しくなくても自動的に型変換して返してしまいます。
もちろん引数でも同じことが起こります。
じゃあ意味無いじゃないかと言いたいところですが、ちゃんとエラーにしてくれる設定がPHPには用意されています。

<?php
declare(strict_type=1)

このように呼び出し側のファイルにも定義側のファイルにも宣言するとエラーにしてくれます。
片方だけにするとデフォルトの緩い型チェックが優先されるので注意しましょう。

基本の型

そもそもPHPの型は皆さん何があるかご存知でしょうか?
実際全く知らなくてもコードは書けるので知らない方もいらっしゃるかも知れません。
ここではざっくりとどんな型があるのか見ていきます。
結構たくさん種類がありますが、最初はよくある型とクラス/インターフェイス名、mixed辺りを活用していければいいんじゃないかと思います。
※ここで紹介する以外にもresorceやEnumなども宣言に使えます。

よくある型

  • array
  • bool
  • float
  • int
  • string

これらは大体どんな言語にもある型なので、説明省略します。

クラス/インターフェイス

Javaなどと同じようにクラス名、インターフェイス名も宣言できます。
値はクラス、インターフェイスインスタンスでなければいけません。

self

クラスの内部でのみ使え、型宣言が行われたクラスと同じクラスのインスタンスでなければいけません。
よくthisと混同されるやつです。これも型宣言に使えます。

parent

型宣言が行われたクラスの親クラスのインスタンスでなければいけません。
オブジェクト指向と言いますか継承をきちんと使ってクラス設計しているとそれなりに出てきます。

callable

コールバック関数であることを示します。
PHPではcall_user_func()等を使用することでユーザ定義のコールバック関数をコールできます。

iterable

配列かTraversableインターフェイス(クラスの中身が foreach を使用してたどれるかどうかを検出する)を実装したオブジェクトでなければいけません。
簡単にいうとforeachで繰り返し可能な値ならOKという型です。

PHP 7.1.0 以降

object

objectでなければなりません。
new命令を使用して作ったものということです。

PHP 7.2.0 以降

mixed

あらゆる型を許容します。なんでも屋さんです。
厳密にはobject,resource,array,string,int,float,bool,nullのいずれかということになっています。

PHP 8.0.0 以降

Null を許容する型宣言

ここまでPHPで使用できる型をざっと見てきました。
じゃあこれからは型の宣言をしていこうねといきたいのですが、PHPで開発をしたことのある人ならこれどうしようとなるケースがあると思います。
この関数基本はある型の値が引数として来るんだけど、たまにnullも来るみたいなケースです。
PHPでは往々にして考えられるケースです。
そんな時はこのような書き方でカバーできます。

<?php
public function add10(?int $a) : ?int {
    if (is_null($a)) {
        return null;
    }

    return $a + 10;
}

型の前に?をつけることでnullかその型というような宣言ができます。
これは返り値でも同じように使うことができます。
例の場合だと引数はnullかintを許容。返り値もnullかintを許容という定義になります。

PHP 7.1.0 以降

union型

先程nullを許容する型宣言のやり方がありましたが、いやいや私はnullじゃなくてintかfloatを引数で受け取りたいんだよ。みたいなことも場合によってはあるでしょう。
そういった場合は|を使用して、int|floatのように書くことができます。

先程のnull許容型はint|nullなどの省略形と言えます。
他にもmixedはobject|resource|array|string|int|float|bool|nullということになります。

このunion型を使用するとエラーになるたびに、これをつなげていって許容する型を増やしていくみたいなことが可能ではあるのですが、あまりに多くなる場合は関数そのものを見直した方がいいかも知れません。

PHP 8.0.0 以降

交差型

交差型を使用すると、クラスやインタフェースとして宣言された型を複数同時に許容できます。
型1&型2のように&を使って宣言します。

PHP 8.1.0 以降

戻り値のみで使用できる型

void

関数が値を返さないことを示す型です。
これはJavaとかにもあるやつです。
明示的に書いてあげるだけでも他人が見たときにとても見やすいコードになると思います。

PHP 7.1.0 以降

never

関数が戻ってこないことを示す戻り値の型です。
関数の中で exit() がコールされるか、 例外がスローされるか、 無限ループに入るかのいずれかであることを意味します。
ここに来たらプログラム終了するということを基本的には示しています。

PHP 8.1.0 以降

static

値が、メソッドが呼び出されているクラスと同じインスタンスでないといけません。
selfと混同しないように気をつけましょう。

PHP 8.0.0 以降

おわりに

PHPにもこれだけの型が用意されているのがわかりました。
もちろんこれまで通り、型を宣言しなくてもPHPは動きます。
しかし、今やPHPは8.00,8.1.0のバージョンアップで交差型やunion型が追加され、静的型付け言語に匹敵する型宣言ができるようになっています。
特に複数人でそれなりの規模の開発をPHPで行っているという場合は、意識して型を使ってみてはいかがでしょうか。
PhpStormを使用すれば最初の一歩もかなり踏み出しやすかったりします。
今後PHPは、更に型について厳しくなっていくと予想されます。
賛否両論ありますが、動的型付け言語と静的型付け言語のいいとこ取りをしていければ最高なのかなと思ったりします。

最後までお読みいただきありがとうございました。


◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.