Skip to content

Psalmでの型付け

PsalmはすべてのPHPDoc型アノテーションを解釈し、それらを使用してコードベースをさらに理解することができます。

型は、プロパティ、変数、関数パラメータ、およびreturn $xに対して許容される値を記述するために使用されます。

Docblock型構文

Psalmでは、docblockで複雑な型情報を表現することができます。

すべてのdocblock型は、アトミック型union型、またはintersection型のいずれかです。

さらに、PsalmはPHPDocの型構文、および提案されているPHPDoc PSR型構文をサポートしています。

詳細な説明はPsalmでの型付けにあります。

プロパティ宣言型 vs 代入型ヒント

/** @var Type */docblockを使用して、プロパティ宣言と変数の代入の両方にアノテーションを付けることができます。

プロパティ宣言型

Psalmでクラスプロパティ宣言に特定の型を指定するには、@var宣言を使用できます:

<?php
/** @var string|null */
public $foo;

$this->foo = $some_variable;をチェックする際、Psalmは$some_variablestringまたはnullのいずれかであるかどうかを確認し、そうでない場合は問題を発生させます。

プロパティ型のdocblockを省略すると、PsalmはMissingPropertyTypeの問題を発生させます。

代入型ヒント

以下のコードを考えてみましょう:

<?php
namespace YourCode {
  function bar() : int {
    $a = \ThirdPartyFoo();
    return $a;
  }
}

namespace ThirdParty {
  function foo() {
    return mt_rand(0, 100);
  }
}

Psalmは、サードパーティの関数ThirdPartyFooが何を返すかわかりません。なぜなら、作者が戻り値の型を追加していないからです。関数が特定の値を返すことがわかっている場合は、次のように代入型ヒントを使用できます:

<?php
namespace YourCode {
  function bar() : int {
    /** @var int */
    $a = \ThirdPartyFoo();
    return $a;
  }
}

namespace ThirdParty {
  function foo() {
    return mt_rand(0, 100);
  }
}

これにより、Psalmは$aの可能な型がintであることを理解し、return $a;が整数を生成することを推論できるようになります。

ただし、プロパティ型とは異なり、代入型ヒントは拘束力がありません。Psalmが問題を発生させることなく、新しい代入によって上書きすることができます。例:

<?php
/** @var string|null */
$a = foo();
$a = 6; // $aは今intとして型付けされています

特定の変数に対しても型ヒントを使用できます。例:

<?php
/** @var string $a */
echo strpos($a, 'hello');

これはPsalmに$aが文字列であると仮定するように指示します(ただし、$aが未定義の場合はエラーをスローします)。

文字列/整数オプションの指定(別名 enums)

Psalmでは、特定の関数やメソッドに対して許可される文字列/整数値の特定のセットを指定できます。

以下のコードはすべてのパスが値を返さないとPsalmが警告するでしょう:

<?php
function foo(string $s) : string {
  switch ($s) {
    case 'a':
      return 'hello';
    case 'b':
      return 'goodbye';
  }
}

$sのパラム型を'a'|'b'と指定すると、Psalmはすべてのパスが値を返すことを知ります:

<?php
/** 
 * @param 'a'|'b' $s
 */
function foo(string $s) : string {
  switch ($s) {
    case 'a':
      return 'hello';
    case 'b':
      return 'goodbye';
  }
}

値がクラス定数にある場合でも、それらを使用できます:

<?php
class A {
  const FOO = 'foo';
  const BAR = 'bar';
}

/** 
 * @param A::FOO | A::BAR $s
 */
function foo(string $s) : string {
  switch ($s) {
    case A::FOO:
      return 'hello';
    case A::BAR:
      return 'goodbye';
  }
}

クラス定数が共通のプレフィックスを共有している場合、ワイルドカードを使用してそれらをすべて指定できます:

<?php
class A {
  const STATUS_FOO = 'foo';
  const STATUS_BAR = 'bar';
}

/** 
 * @param A::STATUS_* $s
 */
function foo(string $s) : string {
  switch ($s) {
    case A::STATUS_FOO:
      return 'hello';
    default:
      // その他のステータス
      return 'goodbye';
  }
}

ユーザーノート