はじめに
Callable
という擬似型は PHP 5.4
系で導入されていたようなのですが、ある値が Callable
かどうかがどのように決まるのかさっぱり理解できず、業務で使う機会もそんなになかったことから放置していました。
業務で使う機会が出てきたというわけでも全くもって全くないのですが、ふと考えてみたら完全に理解できた気がしてしまったので、ツッコミ覚悟で記事にまとめることにしました。
この記事ではどんな値を is_callable()
に渡したら true
を返されるのかを探っていきます。 is_callable()
が true
を返す値はそのまま call_user_func()
, call_user_func_array()
などに渡して呼び出すことが可能です。
var_dump()
を含んだコードの動作確認は paiza.IOで行っています。執筆時のPHPバージョンは PHP 7.4.1
でした。
この記事のスコープ外
「関数やクラスがどの時点で定義されるのか」というのは、これはこれで複雑な話題です。
この記事では、「関数やクラスが定義されているかどうか」を明確にした上で Callable
かどうかの判定方法を理解したいため、関数やクラスの定義方法は最もシンプルなもののみを用います。
<?php// シンプルな関数定義functionsomeFunction(){}// シンプルなクラス定義classSomeClass(){publicstaticfuncionSomeClassMethod(){}publicfunctionSomeObjectMethod(){}}// シンプルでない関数定義たちif(true){functionanotherFunction(){}}functiondefineYetAnotherFunction(){functionyetAnotherFunction(){}}// シンプルでないクラス定義たちif(true){classAnotherClass(){publicstaticfuncionAnotherClassMethod(){}publicfunctionAnotherObjectMethod(){}}}functiondefineYetAnotherClass(){classYetAnotherClass(){publicstaticfuncionYetAnotherClassMethod(){}publicfunctionYetAnotherObjectMethod(){}}}
::
を含まない文字列
文字列と同じ名前の関数が定義されている場合は Callable
であり、そうでない場合は Callable
でない、と判定されます。
<?phpfunctionis_odd(int$int):bool{return$int%2===1;}var_dump(is_callable('is_odd'));// => bool(true)var_dump(is_callable('is_even'));// => bool(false)
::
の両側になんかある文字列
::
の左側の文字列と同じ名前のクラスが定義されていて、かつ ::
の右側の文字列と同じ名前のメソッドが定義されていて public
な場合は Callable
であり、そうでない場合は Callable
でないと判定されます。
<?phpclassSomeClass{publicstaticfunctionsomePublicClassMethod(){}protectedstaticfunctionsomeProtectedClassMethod(){}privatestaticfunctionsomePrivateClassMethod(){}publicfunctionsomePublicObjectMethod(){}protectedfunctionsomeProtectedObjectMethod(){}privatefunctionsomePrivateObjectMethod(){}}var_dump(is_callable('SomeClass::somePublicClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::someProtectedClassMethod'));// => bool(false)var_dump(is_callable('SomeClass::somePrivateClassMethod'));// => bool(false)var_dump(is_callable('SomeClass::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('SomeClass::someProtectedObjectMethod'));// => bool(false)var_dump(is_callable('SomeClass::somePrivateObjectMethod'));// => bool(false)var_dump(is_callable('AnotherClass::somePublicClassMethod'));// => bool(false)var_dump(is_callable('AnotherClass::somePublicObjectMethod'));// => bool(false)var_dump(is_callable('SomeClass::anotherPublicClassMethod'));// => bool(false)var_dump(is_callable('SomeClass::anotherPublicObjectMethod'));// => bool(false)
クラスのメソッド内で Callable
かどうかを判定する場合、 ::
の左側の文字列には追加で self
, parent
が許可され、 ::
の右側の文字列には追加で protected
, private
なメソッドの名前が許可されます。
<?phpclassParentClass{publicstaticfunctionsomePublicClassMethod(){}publicfunctionsomePublicObjectMethod(){}}classSomeClassextendsParentClass{publicstaticfunctionclassMethod(){var_dump(is_callable('SomeClass::somePublicClassMethod'));// => bool(true)var_dump(is_callable('self::somePublicClassMethod'));// => bool(true)var_dump(is_callable('parent::somePublicClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::someProtectedClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::somePrivateClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('self::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('parent::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('SomeClass::someProtectedObjectMethod'));// => bool(true)var_dump(is_callable('SomeClass::somePrivateObjectMethod'));// => bool(true)}publicstaticfunctionsomePublicClassMethod(){}protectedstaticfunctionsomeProtectedClassMethod(){}privatestaticfunctionsomePrivateClassMethod(){}publicfunctionobjectMethod(){var_dump(is_callable('SomeClass::somePublicClassMethod'));// => bool(true)var_dump(is_callable('self::somePublicClassMethod'));// => bool(true)var_dump(is_callable('parent::somePublicClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::someProtectedClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::somePrivateClassMethod'));// => bool(true)var_dump(is_callable('SomeClass::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('self::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('parent::somePublicObjectMethod'));// => bool(true)var_dump(is_callable('SomeClass::someProtectedObjectMethod'));// => bool(true)var_dump(is_callable('SomeClass::somePrivateObjectMethod'));// => bool(true)}publicfunctionsomePublicObjectMethod(){}protectedfunctionsomeProtectedObjectMethod(){}privatefunctionsomePrivateObjectMethod(){}}SomeClass::classMethod();(newSomeClass())->objectMethod();
Closure
オブジェクト
無名関数式を使って作成した Closure
オブジェクトは Callable
と判定されます。
<?phpvar_dump(is_callable(function():bool{returntrue;}));// => bool(true)
__invoke()
を実装したオブジェクト
public function __invoke()
を実装したオブジェクトは Callable
と判定されます。
<?phpvar_dump(is_callable(newclass(){publicfunction__invoke():bool{returntrue;}}));// => bool(true)
[クラス名, ::を含まない文字列]
※力尽きたので一旦記事は公開する
[クラス名, ::の両側になんかある文字列]
※力尽きたので一旦記事は公開する
[オブジェクト, ::を含まない文字列]
※力尽きたので一旦記事は公開する
[オブジェクト, ::の両側になんかある文字列]
※力尽きたので一旦記事は公開する