目次
コンストラクタ、デストラクタ、final
修飾子、オブジェクト引数のクラス指定、abstruct
修飾子(抽象クラス・抽象メソッド)
その1の続き。。
コンストラクタ
その1ではPHP4式のコンストラクタを使っていた。 なぜならPHP5式のコンストラクタがあるって知らなかったからだ!
PHP5式では __construct
というメンバ関数を定義するとこれがコンストラクタになる。
<?php
class BASE
{
function BASE(){
echo "BASE::BASE \n";
}
function __construct(){
echo "BASE::__construct() \n";
}
}
class EXTEND extends BASE
{
function EXTEND(){
echo "EXTEND::EXTEND \n";
}
function FUNC(){
echo "EXTEND::extend2";
}
function __construct(){
echo "EXTEND::__construct() \n";
}
function parent_constructor(){
parent::__construct();
}
}
$base = new BASE();
$extend = new EXTEND();
$extend->BASE();
$extend->parent_constructor();
$extend->__construct();
EXTEND::FUNC();
EXTEND::EXTEND();
?>
結果
BASE::__construct()
EXTEND::__construct()
BASE::BASE
BASE::__construct()
EXTEND::__construct()
EXTEND::extend2
Fatal error: Non-static method
EXTEND::EXTEND() cannot be called statically in *** on line 52
あれ~?コンストラクタとして扱われないと思って EXTEND::EXTEND()
をコールしたら Fatal error…ということはコンストラクタとして呼ばれはしないけれど一応コンストラクタ扱いなのか。
デストラクタ
名前からしてコンストラクタの反対っぽい感じですがそのとおりでした。 オブジェクト(インスタンス)へのアクセス方法がなくなる(参照がすべて消える)ときに自動的にコールされるメソッド。
<?php
class CLS1{
public $var = 1;
function __destruct(){
echo $this->var . "\n";
}
}
class CLS2 extends CLS1{
public $var = 2;
};
class CLS3 extends CLS1{
public $var = 3;
};
$class1 = new CLS1;
$class1 = new CLS2;
$class2 = new CLS1;
new CLS3;
unset($class2);
?>
結果
1
3
1
2
どのタイミングでデストラクタがどうして実行されるのかを把握するべし。
<?php
class BASE{
public $var;
function __destruct(){
echo $this->var . "\n";
}
}
class EXTEND extends BASE{
function __construct($num){
$this->var = $num;
}
}
new EXTEND(100);
$class1 = new EXTEND(1);
$class2 = $class1;
$class3 =&$class1;
$class4 = clone($class1);
$class5 = new EXTEND(5);
echo "-0-\n"; unset($class1);
echo "-1-\n"; unset($class2);
echo "-2-\n"; unset($class3);
echo "-3-\n"; unset($class4);
echo "-4-\n";
EXTEND::$var; // Fatal error
?>
結果
100
-0-
-1-
-2-
1
-3-
1
-4-
Fatal error: Access to undeclared static property:
EXTEND::$var in *** on line 24
デストラクタをオーバーロードすると基底クラス側のデストラクタは動作しない。
E_ERROR
による強制終了時にはデストラクタは実行されない…?
ほでなすPHPに書いてある説明と違うな…バージョンや環境で動作が違う?
…っぽいな。違うとこでやったら E_ERROR
後に 5
が出力された。
final
修飾子
継承・オーバーライドを禁止する修飾子
<?php
final class FINAL_CLS{}
class CLS extends FINAL_CLS{}
?>
結果
Fatal error: Class CLS may not inherit
from final class (FINAL_CLS) in *** on line 3
final
修飾子で宣言されたクラスを継承しようとすると E_ERROR
<?php
class CLS1{
public function func1(){}
}
class CLS2 extends CLS1{
final public function func2(){}
}
class CLS3 extends CLS2{
}
class CLS4 extends CLS3{
function func2(){}
}
?>
結果
Fatal error: Cannot override final method
CLS2::func2() in *** on line 12
final
修飾子で宣言されたメソッドをオーバーライドしようとすると E_ERROR
<?php
class TEST{
final public $test;
}
?>
結果
Fatal error: Cannot declare property TEST::$test final,
the final modifier is allowed only for methods and classes
in *** on line 3
final
修飾子はクラスとメソッドだけに使用可能ですよ
オブジェクト同士の比較
<?php
function test($text){
echo "$text | ";
$text = '
$a = new stdClass;
$b = new stdClass;
$r =& $a;
' . "return" . $text . ";" ;
eval($text) ? print("TRUE\n") : print("FALSE\n") ;
}
test('$a');
test('(array) $a');
test('$a == $b');
test('$a ===$b');
test('$a == clone($a)');
test('$a ===clone($a)');
test('$a ===$r');
?>
結果
$a | TRUE
(array) $a | FALSE
$a == $b | TRUE
$a ===$b | FALSE
$a == clone($a) | TRUE
$a ===clone($a) | FALSE
$a ===$r | TRUE
オブジェクト引数のクラス指定
関数の引数を特定のクラスに限定する。
<?php
class classA{}
class classBA extends classA{}
class classCBA extends classBA{}
$class_a = new classA;
$class_ba = new classBA;
$class_cba = new classCBA;
function p( $obj ){
echo get_class( $obj ) . "\n";
}
function a ( classA $obj ){ p($obj); }
function ba ( classBA $obj ){ p($obj); }
function cba( classCBA $obj ){ p($obj); }
a ($class_a);
a ($class_ba);
a ($class_cba);
ba ($class_ba);
ba ($class_cba);
cba($class_cba);
echo"//以下E_ERROR組みの皆さん\n";
ba ($class_a);
cba($class_a);
cba($class_ba);
?>
結果
classA
classBA
classCBA
classBA
classCBA
classCBA
//以下E_ERROR組みの皆さん
Catchable fatal error: Argument 1 passed to ba() must be
an instance of classBA, instance of classA given,
called in *** on line 24 and defined in *** on line 13
Catchable fatal error: Argument 1 passed to cba() must be
an instance of classCBA, instance of classA given,
called in *** on line 25 and defined in *** on line 14
Catchable fatal error: Argument 1 passed to cba() must be
an instance of classCBA, instance of classBA given,
called in *** on line 26 and defined in *** on line 14
引数に classA
を指定したものは classA
, BA
, CBA
のすべてを通した。
引数に classCBA
を指定したものは classCBA
のみを受け付けた(classA
は B
と C
を持たないから通らない。 classBA
は C
を持たないから通らない)。
つまり指定したクラスのインスタンス および 指定したクラスを継承したクラスのインスタンス が引数として使用可能ということですな。
abstract
修飾子
abstract
修飾子を使うと抽象クラス・中傷メソッドを定義できる。抽象なんたらは何かということは、とりあえず置いておいて定義してみる。
ちなみに abstract
を abstruct
だと思っていてパースエラーがでまくり少々嵌った。。
<?php
class NormalClass{
public function normal_method(){}
//後ろに {処理} があるのは普通の関数
//セミコロンつけるとパースエラーだよ
}
abstract class AbstractClass{
//abstract修飾子をつけてクラスを定義
//すると抽象クラスの出来上がり。
abstract public function abstract_method($arg1, $arg2);
//abstract修飾子をつけて関数を定義
//関数名の後ろは (); で終了。
//これは抽象メソッド
private function normal_method(){}
//普通のメソッドも定義できる。
public function abstract_method2();
//abstract を省略するとE_ERROR
}
?>
結果
Fatal error: Non-abstract method AbstractClass::abstract_method2()
must contain body in *** on line 19
abstract
修飾子をつけずに定義するメソッドは 普通のメソッド(Non-abstract method)なのです。
abstract
を消すと「抽象メソッドじゃないんだから入れ物 (contein body : つまり {}
) 付けてね」と怒られました。
<?php
class NormalClass{
abstract public function abstract_method();
}
?>
結果
Fatal error: Class NormalClass contains 1 abstract method
and must therefore be declared abstract or implement the remaining methods (NormalClass::abstract_method) in *** on line 4
非抽象クラスに抽象メソッドが含まれると E_ERROR
<?php
abstract class ABS{
abstract function abs_func($arg1,$arg2);
public function normal_func(){}
}
class CLS extends ABS{
function __construct(){
echo "CLSのインスタンスを生成しました";
}
}
new CLS;
?>
結果
Fatal error: Class CLS contains 1 abstract method
and must therefore be declared abstract
or implement the remaining methods (ABS::abs_func) in *** on line 10
抽象クラス ABS
を継承したクラス CLS
のインスタンスを生成しようとしたら Fatal error が出ましたとさ。
抽象クラスはインスタンスを生成することはできないからね。
また、この場合では抽象クラス ABS
を継承したクラス CLS
も抽象クラスであることがわかる…が次の例はどうだろう?
<?php
abstract class ABS{
abstract function abs_func($arg1,$arg2);
public function normal_func(){}
}
//さてさて抽象クラスの派生クラスを作ってみましょう
class CLS extends ABS{
function __construct(){
echo "CSLのインスタンスを生成しました\n";
}
function abs_func($arg1,$arg2){}
function normal_func($hoge){}
}
new CLS;
new ABS;
?>
結果
CSLのインスタンスを生成しました
Fatal error: Cannot instantiate abstract class ABS in *** on line 15
前の例と違って 抽象クラスを継承したクラスのインスタンスを生成できている。 継承により抽象メソッドをオーバーライドすると非抽象メソッドになる。 その結果として定義したクラスに抽象メソッドが含まれなくなればそのクラスは非抽象メソッドとなり、 含まれれば抽象メソッドとなる。このオーバーライドを「実装」と言うらしい。
<?php
abstract class AbsCls{}
class CLS extends AbsCls{}
new CLS;
new AbsCls;
?>
結果
Fatal error: Cannot instantiate
abstract class AbsCls in *** on line 6
抽象メソッドを持たない抽象クラスを継承しても抽象クラスにはなれないようで
CLS
のインスタンスは無事に生成できた。
とはいえ抽象メソッドを持たない抽象クラスは、
抽象クラスだからインスタンスを生成しようとすると E_ERROR
。日本語でおk
<?php
echo "HOGEHOGE";
abstract class Abs{
abstract function func($arg1, $arg2, $arg3);
}
class CLS1 extends Abs{
function func($test, $test, $test){}
}
class CLS2 extends Abs{
function func($arg1){}
}
?>
結果
Fatal error: Declaration of CLS2::func() must be
compatible with that of Abs::func() in *** on line 11
CLS1
は無事に定義できたのに、CLS2
は死んだ。
抽象メソッドの引数の数と同じ数の引数で実装しないとダメなのだよ。
あと HOGEHOGE
が出力されてないな。。クラスの定義から先に行うのかな?
<?php
abstract class AbsCls{
abstract function func();
}
class NormalCls extends AbsCls{
function func(){
parent::func();
}
}
$class = new NormalCls;
$class->func();
?>
結果
Fatal error: Cannot call abstract method AbsCls::func() in *** on line 7
実装により抽象メソッドは消えたのかなって思ってアクセスしてみたら一応残ってはいるみたい。
とはいえ E_ERROR
になるので実質的に消えているようなもの…なのかな?と思ったけどいやいや違いました↓
<?php
abstract class Abs{
abstract function func();
}
class CLS1 extends Abs{
function func(){}
}
class CLS2 extends CLS1{
function func($arg1){}
}
?>
結果
Fatal error: Declaration of CLS2::func()
must be compatible with that of Abs::func() in *** on line 10
CLS1
は抽象クラスではなくなったけれど、
CLS1
のオーバーライドには抽象メソッドの制限が残ってるんだね。。
ちなみに抽象クラスに定義したスタティックメンバへはアクセス可能だよ。