Posted at

HackでEnumsを使いこなそう!


PHPにも欲しいEnums

PHPにも欲しい!ということで近しい感覚で利用できる様なライブラリを使ったり、

作ったり、定数で頑張ったり・・・。

そんなEnumsがHackで利用できるのは多くのPHPerの方がご存知だと思います。

C#、C++、Javaといった言語で実装経験のある方は

おなじみですので、Hackですぐにそのまんまで使える様になります。

PHPでEnumsといえば、

myclabs/php-enum などを利用することが多いのではないでしょうか。

今回はEnumsについて早速みていきましょう。


Hack Enums

まずはじめに!

HackのEnumsは intとstringのみ 利用できるということをしっかりと覚えておきましょう。

それさえ忘れずにいればあとは利用していくだけです。


書き方

書き方は簡単です。

<?hh

namespace Acme\Enums;

enum Size: int {
SMALL = 0;
MEDIUM = 1;
LARGE = 2;
X_LARGE = 3;
}

enumsはclassではないため、PSR-4などにしたがってディレクトリはそどうしたら、

と思う方もいるかもしれません。

Hackでユニットテスト はじめてのHackTest#hh_autoload.json

でも触れた様にhhvm-autoload を利用するため

心配は無用です。

1ファイルに複数のEnumsやtype aliasなどを書き込んでいきましょう。

php環境でcomposerを動かすと、PHPにはない構文と機能となりますので、

忘れず hhvm でcomposerを動かすように・・。

これまでに紹介した様に、Hackではcomposer.jsonにあまり記述しませんが、

下記の通り登録されます。

PSR-4指定をcomposerやhh-autoloadに記述する必要はありません、

function map(): \Facebook\AutoloadMap\AutoloadMap {

/* HH_IGNORE_ERROR[4110] invalid return type */
return darray[
'class' =>
darray[
// 省略
'acme\\enums\\size' => 'src/types.php',
]
// 省略

これで型が保証される値を利用できる様になりました。

Enums自体は継承ができませんので、よく考えて利用しましょう。


値取得方法

Enumsの値を利用する場合は、定数利用と同じです。

<?hh

namespace Acme\Enums;

enum Language: string {
PHP = 'PHP';
HACK = 'Hack';
}

<?hh

require_once __DIR__ . '/../vendor/hh_autoload.php';

use type Acme\Enums\Language;

var_dump(Language::PHP, Language::HACK);

Enumsにはデフォルト値はありません。


Enumsのキャスト

Enumsの例としてAcme\Enums\Language を利用する例です。

<?hh // strict

namespace Acme;

use type Acme\Enums\Language;

final class Display {

public function render(string $l): string {
return $l;
}

public function renderEnum(Language $l): string {
return (string) $l;
}
}

このクラスのメソッドにEnumsを使う場合は次の通りです。

$d = new \Acme\Display();

\var_dump(
$d->render((string) Language::HACK),
$d->renderEnum(Language::HACK)
);

実はEnumsで記述した型は、通常のクラスで利用する型と互換性がないようです。

このためそれぞれキャストして値を扱う様になっています。

Enumsの型に暗黙の型変換を適用したい場合は、下記の様に記述する必要があります。

enum Language: string as string {

PHP = 'PHP';
HACK = 'Hack';
}

これで文字列型として扱うことができる様になりました。

利用するクラスも次の様に変更できます。

<?hh // strict

namespace Acme;

use type Acme\Enums\Language;

final class Display {

public function render(string $l): string {
return $l;
}

public function renderEnum(Language $l): string {
return $l;
}
}

暗黙の型変換が作用しますので、クラスに記述していたキャストがなくなりました。


Enums Functions

Enumsは当然これだけではありません。

いくつかのメソッドが用意されていますので、少しだけ紹介します。


assert()

文字列やintを渡してEnumsで定義されたものかどうかを確認したい場合に利用します。

Enumsで記述していない値を渡す場合は次の通りです。

  public function put(): void {

$o = new \Acme\Display();
\var_dump(
Language::assert('Scala')
);
}

エラーにもしっかりと表示されて、例外が発生します。

存在する値の場合は、そのまま値を返却します。

Fatal error: Uncaught exception 'UnexpectedValueException' with message 'Scala is not a valid value for Acme\Enums\Language' in Path/To/*.php


coerce()

assertと基本的には同じですが、こちらは例外ではなくnullを返却します。

  public function put(): void {

$o = new \Acme\Display();
\var_dump(
Language::coerce('Scala') // null返却
);
}


getNames()、getValues()

Enumsで記述した名前、もしくは値を取得します。

それぞれarrayで返却されますので、必要に応じてdictやMapに変換をするなどしましょう。

  public function put(): void {

$o = new \Acme\Display();
\var_dump(
dict(Language::getNames()),
dict(Language::getValues())
);
}


isValid()

与えられた値がEnumsで利用できるかどうかを調べます。

  public function put(): void {

$o = new \Acme\Display();
\var_dump(
Language::isValid('PHP'),
Language::isValid('Scala')
);
}

それぞれbooleanで返却されます。

今回はEnumsについて簡単に紹介しました。

どれも利用方法は非常に簡単ですので、利用したい場面で使ってみましょう。