オーバーロード
プログラミング言語において関数や演算子やメソッドの同一名や同一の演算子記号について複数定義し、利用時にプログラムの文脈に応じて選択することで複数の動作を行わせる仕組みである。
PHPには「オーバーロード」という機能が存在するが、これはプロパティやメソッドを動的に作成するための機能であり、他の多くのオブジェクト指向言語とは異なる意味で用いられている。
PHPマニュアル(PHP: オーバーロード - Manual )によると
オーバーロード
PHP におけるオーバーロード機能は、 プロパティやメソッドを動的に "作成する" ための手法です。
これらの動的エンティティは、マジックメソッドを用いて処理されます。
マジックメソッドは、クラス内でさまざまなアクションに対して用意することができます。
オーバーロードメソッドが起動するのは、 宣言されていないプロパティやメソッドを操作しようとしたときです。
また、現在のスコープからは アクセス不能な プロパティやメソッドを操作しようとしたときにも起動します。
このセクションでは、これらの (宣言されていない、 あるいは現在のスコープからはアクセス不能な) プロパティやメソッドのことを "アクセス不能プロパティ" および "アクセス不能メソッド" と表記することにします。
オーバーロードメソッドは、すべて public で定義しなければなりません。
変更履歴
バージョン | 説明 |
---|---|
5.3.0 | __callStatic() が追加されました。 public で、かつ static でない宣言を強制するような警告が追加されました。 |
5.1.0 | __isset() と __unset() が追加されました。 __get() が private プロパティのオーバーロードに対応しました。 |
5.0.0 | __get() が追加されました。 |
プロパティのオーバーロード
public bool __isset ( string $name
)
引数 $name は、 操作しようとしたプロパティの名前です。
__set( ) メソッドの引数 $value は、 $name に設定しようとした値となります。
プロパティのオーバーロードはオブジェクトのコンテキストでのみ動作します。
これらのマジックメソッドは、静的コンテキストでは起動しません。
したがって、これらのメソッドを static 宣言してはいけません。
PHP 5.3.0 以降、マジックオーバーロードメソッドを static 宣言すると警告が発生します。
例1 __get( )、 __set( )、__isset( ) および __unset( ) メソッドを使ったプロパティのオーバーロードの例
<?php
class PropertyTest
{
/** オーバーロードされるデータの場所 */
private $data = array();
/** 宣言されているプロパティにはオーバーロードは起動しません */
public $declared = 1;
/** クラスの外部からアクセスした場合にのみこれがオーバーロードされます */
private $hidden = 2;
public function __set($name, $value)
{
echo "Setting '$name' to '$value'\n";
$this->data[$name] = $value;
}
public function __get($name)
{
echo "Getting '$name'\n";
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
$trace = debug_backtrace();
trigger_error(
'Undefined property via __get(): ' . $name .
' in ' . $trace[ 0]['file'] .
' on line ' . $trace[0]['line'],
E_USER_NOTICE);
return null;
}
/** PHP 5.1.0 以降 */
public function __isset($name)
{
echo "Is '$name' set?\n";
return isset($this->data[$name]);
}
/** PHP 5.1.0 以降 */
public function __unset($name)
{
echo "Unsetting '$name'\n";
unset($this->data[$name]);
}
/** マジックメソッドではありません。単なる例として示しています */
public function getHidden()
{
return $this->hidden;
}
}
echo "<pre>\n";
$obj = new PropertyTest;
$obj->a = 1;
echo $obj->a . "\n\n";
var_dump (isset($obj->a));
unset($obj->a);
var_dump(isset($obj->a));
echo "\n";
echo $obj->declared . "\n\n";
echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";
?>
上の例の出力は以下となります。
Setting 'a' to '1' Getting 'a' 1 Is 'a' set? bool(true) Unsetting 'a' Is 'a' set? bool(false) 1 Let's experiment with the private property named 'hidden': Privates are visible inside the class, so __get() not used... 2 Privates not visible outside of class, so __get() is used... Getting 'hidden' Notice: Undefined property via __get(): hidden in <file> on line 70 in <file> on line 29
メソッドのオーバーロード
public mixed __call ( string $name
, array $arguments
)
public static mixed __callStatic ( string $name
,array $arguments
)
・ __callStatic( ) は、 アクセス不能メソッドを静的コンテキストで実行したときに起動します。
引数 $name は、 コールしようとしたメソッドの名前です。
引数$arguments は配列で、メソッド $name に渡そうとしたパラメータが格納されます。
例2 __call( ) および __callStatic( ) メソッドによる、メソッドのオーバーロードの例
<?php
class MethodTest
{
public function __call($name, $arguments)
{
// 注意: $name は大文字小文字を区別します
echo "Calling object method '$name' "
. implode(', ', $arguments). "\n";
}
/** PHP 5.3.0 以降 */
public static function __callStatic($name, $arguments)
{
// 注意: $name は大文字小文字を区別します
echo "Calling static method '$name' "
. implode(', ', $arguments). "\n";
}
}
$obj = new MethodTest;
$obj->runTest('in object context');
MethodTest::runTest('in static context'); // PHP 5.3.0 以降
?>
上の例の出力は以下となります。
Calling object method 'runTest' in object context Calling static method 'runTest' in static context
マジックメソッドはPHPマニュアル(PHP: マジックメソッド - Manual )によると
マジックメソッド ¶
以下の関数名
__construct( ), __destruct( ), __call( ), __callStatic( ), __get( ), __set( ), __isset( ), __unset( ), __sleep( ), __wakeup( ), __toString( ), __invoke( ), __set_state( ), __clone( ) および __debugInfo( ) は、PHP クラスにおける特殊関数の名前です。
これらの関数に関連する特別な機能を使用する場合を除き、 クラス内にこれらの名前を有する関数を作成してはいけません。
__sleep( ) と __wakeup( )
serialize( ) は、クラスに特殊な名前 __sleep( ) の関数があるかどうかを調べます。
もしあれば、シリアル化の前にその関数を実行します。
この関数で、オブジェクトをクリアすることができます。
またこの関数は、シリアル化するオブジェクトについて、 すべての変数の名前を配列で返すことが前提となっています。
このメソッドが何も返さなかった場合は、NULL
がシリアル化され、E_NOTICE
が発生します。
典型的な __sleep( ) の使用法は、 途中のデータをコミットしたり、 似たようなタスクのクリアを行うといったものです。
また、オブジェクトが非常に大きく、かつ、完全に保存する必要がない場合、 この関数が有用です。
逆に、unserialize( ) は、 特殊な名前 __wakeup( ) を有する 関数の存在を調べます。
もし存在する場合、この関数は、オブジェクトが有する可能性が あるあらゆるリソースを再構築することができます。
意図される __wakeup( ) の使用法は、 シリアル化の際に失われたデータベース接続を再度確立したり、 その他の再初期化を行うことです。
例1 sleep および wakeup
<?php
class Connection
{
protected $link;
private $dsn, $username, $password;
public function __construct($dsn, $username, $password)
{
$this->dsn = $dsn;
$this->username = $username;
$this->password = $password;
$this->connect();
}
private function connect()
{
$this->link = new PDO($this->dsn, $this->username, $this->password);
}
public function __sleep()
{
return array('dsn', 'username', 'password');
}
public function __wakeup()
{
$this->connect();
}
}?>
__toString( )
__toString( ) メソッドにより、 クラスが文字列に変換される際の動作を決めることができます。
たとえば echo $obj; としたときに何を表示させるかといったことです。
このメソッドは文字列を返さなければなりません。それ以外の場合は E_RECOVERABLE_ERROR
レベルの致命的なエラーが発生します。
例2 簡単な例
<?php
// 簡単なクラスを宣言
class TestClass
{
public $foo;
public function __construct($foo)
{
$this->foo = $foo;
}
public function __toString()
{
return $this->foo;
}
}
$class = new TestClass('Hello');
echo $class;
?>
上の例の出力は以下となります。
Hello
注意が必要なのは、PHP 5.2.0 より前では、 __toString( ) メソッドは echo または print と直接結合された場合のみコールされていたということです。
PHP 5.2.0 以降では、これはすべての文字列コンテキスト (たとえば printf( ) における %s 修飾子) でコールされます。
しかし、その他の型のコンテキスト (たとえば %d 修飾子) ではコールされません。
PHP 5.2.0 以降では、__toString( ) メソッドを持っていないオブジェクトを文字列に変換しようとするとE_RECOVERABLE_ERROR
が発生します。
__invoke( )
__invoke( ) メソッドは、 スクリプトがオブジェクトを関数としてコールしようとした際にコールされます。
例3 __invoke( ) の使用
<?php
class CallableClass
{
public function __invoke($x)
{
var_dump($x);
}
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));
?>
上の例の出力は以下となります。
int(5) bool(true)
__set_state( )
$properties
) この static メソッドは、 PHP 5.1.0 以降で var_export( ) によって エクスポートされたクラスのためにコールされます。
このメソッドの唯一のパラメータは、エクスポートされたプロパティを array('property' => value, ...) の形式で保持する配列です。
例4 __set_state( ) の使用法 (PHP 5.1.0 以降)
<?php
class A
{
public $var1;
public $var2;
public static function __set_state($an_array) // PHP 5.1.0 以降
{
$obj = new A;
$obj->var1 = $an_array['var1'];
$obj->var2 = $an_array['var2'];
return $obj;
}
}
$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';
eval('$b = ' . var_export($a, true) . ';'); // $b = A::__set_state(array(
// 'var1' => 5,
// 'var2' => 'foo',
// ));
var_dump($b);
?>
上の例の出力は以下となります。
object(A)#2 (2) { ["var1"]=> int(5) ["var2"]=> string(3) "foo" }
__debugInfo( )
このメソッドは、var_dump( ) がオブジェクトをダンプするときに、 プロパティの情報を取得するために呼ばれます。
もしオブジェクトにこのメソッドが定義されていなければ、 すべての public, protected, private プロパティを表示します。
この機能は PHP 5.6.0 で追加されました。
例5 __debugInfo( ) の使用法
<?php
class C {
private $prop;
public function __construct($val) {
$this->prop = $val;
}
public function __debugInfo() {
return [
'propSquared' => $this->prop ** 2,
];
}
}
var_dump(new C(42));
?>
上の例の出力は以下となります。
object(C)#1 (1) { ["propSquared"]=> int(1764) }
Wikipediaによるとオーバーロードとは、英語で『過負荷』 という意味らしいです。
『パーフェクトPHP』によると、PHPでのオーバーロードは、
「特定の処理がクラスまたはオブジェクトに施された時のデフォルトの挙動を上書きする機能です。」 とのことです。
例:オーバーロードのプログラム
オーバーロードのプログラムを実行していきます。
$obj = new SomeClass( );
$obj->foo = 10;
fooは宣言されていないプロパティなので、アクセス不能プロパティへの代入として__set( )メソッドが呼び出されます。
このときの引数には、代入しようとしたプロパティ名 " foo " と代入しようとした値10が渡されます。
この値はprivateなプロパティ$valueという配列に保存されます。
set: foo setted to 10
続いて、" foo " を取得しようとします。
var_dump($obj->foo);
$fooプロパティは宣言されてないので、アクセス不能なプロパティの取得を行ったことになり、__get( )メソッドが呼ばれます。
get: foo
int(10)
続いて、isset( )、empty( )を$fooプロパティに対して実行します。
var_dump(isset($obj->foo));
var_dump(empty($obj->foo));
// " foo " という文字列を引数に__isset( )メソッドが呼ばれます。issetメソッドでは、" foo " が$valueにセットされているかを理論値で返します。
isset: foo
bool(true)
isset: foo
get: foo
bool(false)
続いて、unset( )を呼び出します。
unset($obj->foo);
var_dump(isset($obj->foo));
unset( )を呼び出すことにより、__unset( )が呼び出され、$valueプロパティから " foo " のキーがunsetされ、続くisset( )では結果がfalseとなります。
unset: foo
isset: foo
bool(false)
メソッドのオーバーロードの例。
アクセス不能なメソッドへのアクセスで、__call( )、__callStatic( )が呼び出されます。
$obj->bar('baz');
SomeClass::staticBar('baz');
上の出力の結果は以下となります。
" bar " という文字列を引数に、privateな_barメソッドや_staticBarメソッドが呼び出されています。
call: bar
bar called with arg 'baz'
callStatic: staticBar
staticBar called with arg 'baz'
メソッドのオーバーロードは、呼び出されるメソッド名によって処理を切り替えるなど、複雑な実装も可能だそうです。
詳しくは『パーフェクトPHP (PERFECT SERIES 3) 大型本 – 2010/11/12 小川 雄大 (著), 柄沢 聡太郎 (著), 橋口 誠 (著)』という書籍で。
今回はこのへんで。