基本概念
PHP はオブジェクトを参照やハンドルと同じように扱います。つまり、各変数はオブジェクト全体のコピーではなく、オブジェクトへの参照を保持します。
新しいオブジェクトが作成されると、オブジェクトがコンストラクターを定義し、エラーで例外がスローされない限り、オブジェクトには常に値が割り当てられます。クラスはインスタンス化する前に定義する必要があります。
オブジェクトを作成するとき、クラスが名前空間に属している場合は、その完全名を使用する必要があります。
クラス定義内では、new self
和new parent
を使用してオブジェクトを作成できます。
このコードの出力は次のとおりです。これはなぜですか?
リーリーPHP 5.3 では、オブジェクトのインスタンスを作成するための 2 つの新しいメソッドが導入されており、次のメソッドを使用してインスタンスを作成できます。
リーリーPHP は多重継承をサポートしていません。継承されたメソッドとプロパティは、同じ名前で再宣言することでオーバーライドできます。もちろん、コンストラクターを除いて、パラメーターは一貫性を保つ必要があります。ただし、親クラスがメソッドを定義するときに final
,则该方法不可被覆盖。可以通过parent::
来访问被覆盖的方法和属性,parent::
只能访问父类中的常量const
を使用すると、変数にアクセスできなくなります。
PHP 5.5 以降、キーワードの完全修飾名 class
也可用于类名的解析。使用ClassName::class
你可以获取一个字符串,包含了类ClassName
。
プロパティ
属性。クラスの変数メンバーです。プロパティ内の変数は初期化できますが、初期化された値は定数である必要があります。ここでの定数は、PHP スクリプトがコンパイル段階で値を取得でき、実行時情報に依存せずに評価できることを意味します。
クラスのメンバー メソッドで、$this->property
,访问静态属性使用self::$property
。静态属性声明时使用static
キーワードを使用して非静的プロパティにアクセスします。
クラス定数
定数を定義する場合、$
シンボルとアクセス制御キーワードは必要ありません。
定数はインターフェースでも定義できます。
クラスの自動ロード
オブジェクト指向アプリケーションを作成する場合、クラス定義ごとに 1 つの PHP ソース ファイルを使用するのが一般的です。ファイルがこれらのクラスを呼び出す必要がある場合、含まれるファイルの長いリストをファイルの先頭に書き込む必要があります。実際、これは必要ありません。まだ定義されていないクラスを使用しようとすると自動的に呼び出される __autoload()
関数を定義できます。
マニュアルのヒントには、spl_autoload_register()
クラスの自動ロードを実装するためのより柔軟な方法が提供されていると記載されています。これについては後で説明します。
自動ロードは、PHP の CLI インタラクティブ モード (コマンド ライン モード) では使用できません。
ユーザー入力に危険な文字が含まれている可能性があります。__autoload()
の場合、少なくとも入力を検証する必要があります。
クラスは次の方法で自動的にロードできます。
リーリー例外処理については後ほど見ていきます。
コンストラクターとデストラクター
PHP 5 では、開発者はクラス内のコンストラクターとしてメソッドを定義できますが、コンストラクターはオーバーロードをサポートしていません。
サブクラスにコンストラクターが定義されている場合、親クラスのコンストラクターは暗黙的に呼び出されることはありません。それ以外の場合は、通常のクラスメソッドと同様に親クラスから継承されます(private
)。要执行父类的构造函数,需要在子类构造函数中调用parent::__construct()
として定義されていない場合)。
に異なるパラメータがある場合はオーバーライドできます。 __construct()
与父类__construct()
デストラクターは、オブジェクトへのすべての参照が削除されるか、オブジェクトが明示的に破棄されるときに実行されます。デストラクターは、
を使用してスクリプトが終了された場合でも呼び出されます。 exit()
アクセス制御
クラス属性は、public、protected、private のいずれかとして定義する必要があり、キーワードは省略できません。クラス内のメソッドにアクセス制御キーワードが設定されていない場合、メソッドはデフォルトで public になります。
同じクラスのオブジェクトは、同じインスタンスでなくても、互いのプライベート メンバーや保護されたメンバーにアクセスできます。サンプルプログラムは以下の通りです。
リーリー
オブジェクトの継承あるクラスが別のクラスを拡張する場合、親クラスは子クラスの前に宣言する必要があります。
スコープ解析演算子
スコープ解決演算子は、簡単に言えば、コロンのペアであり、静的メンバーやクラス定数にアクセスするために使用でき、親クラスのプロパティやメソッドを呼び出すためにも使用できます。
クラス定義の外でこれらの項目を参照する場合は、クラス名を使用します。
静的
キーワードを使用して、静的メソッドと静的プロパティを定義し、静的変数と遅延静的バインディングを定義します。クラスのプロパティまたはメソッドを static として宣言すると、クラスをインスタンス化せずに直接アクセスできるようになります。 static
アクセス制御が指定されていない場合、プロパティとメソッドはデフォルトで public になります。
静的メソッドから非静的メソッドを呼び出すと、
レベルのエラーが発生します。 E_STRICT
抽象类
PHP 5支持抽象类和抽象方法。类中如果有一个抽象方法,那这个类必须被声明为抽象的。
抽象类不能被实例化。抽象方法只是声明了其调用方式(参数),不能定义其具体的功能实现。继承抽象类时,子类必须定义父类中的所有抽象方法,且这些方法的访问控制必须和父类一样活更宽松。
方法的调用方式必须匹配。但是,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。这也试用与PHP 5.4起的构造函数。可以在子类中定义父类签名中不存在的可选参数。
<code><?php abstract class AbstractClass { abstract protected function prefixName($name); } class ConcreteClass extends AbstractClass { public function prefixName($name, $separator = ', ') { if($name === "Pacman") { $prefix = 'Mr'; } elseif($name === 'Pacwoman') { $prefix = "Mrs"; } else { $prefix = ''; } return "$prefix $separator $name "; } } $class = new ConcreteClass; echo $class->prefixName('Pacman'); echo $class->prefixName('Pacwoman');</code>
对象接口
听说过接口,一直没用过。使用接口,可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容,也就是说接口中定义的所有方法都是空的。接口中定义的所有方法都必须是公有的,这是接口的特性。
接口也可以继承多个接口,用逗号分隔,使用extends
操作符。类中必须实现接口中定义的所有方法,否则会报错。要实现一个接口,使用implements
操作符。类可以实现多个接口,用逗号分隔。实现多个接口时,接口中的方法不能有重名。类要实现接口,必须使用和接口中所定义的方法完全一致的方式。
接口中也可定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口覆盖。
traits
从PHP 5.4.0开始,可以使用traits
实现代码复用。Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制。Trait 不能通过它自身来实例化。它为传统继承增加了水平特性的组合。
优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。
通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误,为解决冲突,需使用insteadof
操作符来指明使用冲突方法中的哪一个,这种方法仅允许排除掉其它方法。as
操作符可以将其中一个冲突的方法以另一个名称(别名)来引入。
<code><?php trait A { public function smallTalk() { echo 'a'; } public function bigTalk() { echo 'A'; } } trait B { public function smallTalk() { echo 'b'; } public function bigTalk() { echo 'B'; } } class Talker { use A, B { B::smallTalk insteadof A; A::bigTalk insteadof B; B::bigTalk as talk; } } $t = new Talker(); $t->smallTalk(); // b $t->bigTalk(); // A $t->talk(); // B</code>
使用as
操作符还可以用来调整方法的访问控制,或者给方法一个改变了访问控制的别名,原版方法的访问控制规则没有改变。
<code><?php trait HelloWorld { public function sayHello() { echo 'Hello World.'; } } class MyClass1 { use HelloWorld { sayHello as protected; } } class MyClass2 { use HelloWorld { sayHello as private myPrivateHello; } }</code>
就像类能够使用trait
那样,多个trait
能够组合为一个trait
。
为了对使用的类施加强制要求,trait 支持抽象方法的使用。
<code><?php trait Hello { public function sayHelloWorld() { echo 'Hello ' . $this->getWorld(); } abstract public function getWorld(); } class MyHelloWorld { private $world; use Hello; public function getWorld() { return $this->world; } public function setWorld($val) { $this->world = $val; } } $c = new MyHelloWorld; $c->setWorld('world'); $c->sayHelloWorld();</code>
如果trait
定义了一个属性,那类将不能定义同样名称的属性,否则会产生错误。
重载
PHP提供的重载是指动态地创建类属性和方法,与其它绝大多数面向对象语言不同。通过魔术方法来实现。当使用不可访问的属性或方法时,重载方法会被调用。所有的重载方法都必须被声明为public
。
使用__get()
,__set()
,__isset()
,__unset()
进行属性重载,示例如下。
<code><?php class PropertyTest { private $data = array(); public $declared = 1; private $hidden = 2; public function __set($name, $value) { echo "Setting $name to $value. " . '<br>'; $this->data[$name] = $value; } public function __get($name) { echo "Getting $name. <br>"; if(array_key_exists($name, $this->data)) { return $this->data[$name]; } return null; } public function __isset($name) { echo "Is $name set? <br>"; return isset($this->data[$name]); } public function __unset($name) { echo "Unsetting $name. <br>"; unset($this->data[$name]); } } $obj = new PropertyTest; $obj->a = 1; var_dump($obj->a); var_dump(isset($obj->a)); unset($obj->a); var_dump(isset($obj->a)); var_dump($obj->declared); var_dump($obj->hidden);</code>
输出结果如下:
<code>Setting a to 1. Getting a. int 1 Is a set? boolean true Unsetting a. Is a set? boolean false int 1 Getting hidden. null</code>
在对象中调用一个不可访问方法时,__call()
会被调用。用静态方式中调用一个不可访问方法时,__callStatic()
会被调用。参数为调用方法的名称和一个枚举数组,注意区分大小写。
使用__call()
和__callStatic()
对方法重载,示例如下。
<code><?php class MethodTest { public function __call($name, $arguments) { echo "Calling object method $name " . implode(', ', $arguments) . '<br>'; } public static function __callStatic($name, $arguments) { echo "Calling static method $name " . implode(', ', $arguments) . '<br>'; } } $obj = new MethodTest; $obj->runTest('in object context'); MethodTest::runTest('in static context');</code>
遍历对象
对象可以用过单元列表来遍历,例如用foreach
语句。默认所有可见属性都将被用于遍历。
<code><?php class MyClass { public $var1 = 'value 1'; public $var2 = 'value 2'; public $var3 = 'value 3'; private $var4 = 'value 4'; protected $var5 = 'value 5'; } $obj = new MyClass; foreach($obj as $key => $value) { echo "$key => $value <br>"; }</code>
示例程序2实现了Iterator接口的对象遍历,示例程序3通过实现IteratorAggregate来遍历对象。
魔术方法
PHP 将所有以__
(两个下划线)开头的类方法保留为魔术方法。定义类方法时,除魔术方法外,建议不要以__
为前缀。
前面遇到过的魔术方法有:__construct()
,__destruct()
,__call()
,__callStatic()
,__get()
,__set()
,__isset()
,__unset()
。后面将会介绍:__sleep()
,__wakeup()
,__toString()
,__invoke()
,__set_state()
,__clone()
和__debugInfo()
。
__sleep
和__wakeup
不清楚具体做什么用的,示例程序中给出了个数据库连接的例子。
__toString
方法用于一个类被当成字符串时应怎样回应。此方法必须返回一个字符串,且不能再方法中抛出异常。如果将一个未定义__toString()
方法的对象转换为字符串,将产生错误。
当尝试以调用函数的方式调用一个对象时,__invoke()
方法会被调用。
当调用var_export()
导出类时,__set_state()
会被调用。
当调用var_dump()
时,__debugInfo
会被调用。PHP 5.6新加入,没合适的环境无法测试。
final
果父类中的方法被声明为final
,则子类无法覆盖该方法。如果一个类被声明为final
,则不能被继承。属性不能被定义为final
,只有类和方法才能被定义为final
。
对象复制
多数情况,我们不需要完全复制一个对象,但有时确实需要。对象复制可以通过clone
关键字来完成。这种复制是通过调用对象的__clone()
方法实现的,但是对象中的__clone()
方法不能被直接调用。
对象比较
比较运算符==
为真的条件是:两个对象的属性和属性值都相等,而且两个对象是同一个类的实例。
继承与统一个基类的两个子类的对象不会相等==
。
<code><?php class Base {} class A extends Base {} class B extends Base {} $a = new A; $b = new B; var_dump($a == $b); // false</code>
全等运算符===
为真的条件是:两个对象变量一定要指向某个类的同一个实例(即同一个对象)。
类型约束
类型约束是指函数的参数可以指定必须为对象、接口、数组或者callable
类型。但是类型约束不能用于标量类型如int
或string
,traits
也不允许。类型约束允许NULL
值。
后期静态绑定
后期静态绑定,用于在继承范围内引用静态调用的类。
转发调用,指的是通过以下几种方式进行的静态调用:self::
,parent::
,static::
以及forward_static_call()
。
后期静态绑定的工作原理是,存储了上一个非转发调用的类名。
当进行静态方法调用时,该类名即为明确指定的那个;当进行非静态方法调用时,即为该对象所属的类。
使用self::
或者__CLASS__
对当前类的静态引用,取决于定义当前方法所在的类。
<code><?php class A { public static function who() { echo __CLASS__; } public static function test() { self::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test(); // A B::who(); // B</code>
用static::
关键字表示运行时最初调用的类,后期静态绑定就是这样使用。如下面程序所示,也就是说调用test()
时引用的类是B
而不是A
。
<code><?php class A { public static function who() { echo __CLASS__; } public static function test() { static::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test(); // B B::who(); // B</code>
示例2给出的是非静态环境下使用static::
。
后期静态绑定的解析,会一直到取得一个完全解析了的静态调用为止。另外,如果静态调用使用parent::
或self::
将转发调用信息。
<code><?php class A { public static function foo() { static::who(); } public static function who() { echo __CLASS__; } } class B extends A { public static function test() { A::foo(); parent::foo(); self::foo(); } public static function who() { echo __CLASS__; } } class C extends B { public static function who() { echo __CLASS__; } } C::test(); // ACC</code>
那么问题来了,结果为什么是这样的呢?
对象和引用
默认情况下,对象时通过引用传递的。但这种说法不完全正确,其实两个对象变量不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。
对象序列化
所有PHP里面的值,都可以使用函数serialize()
来返回一个包含字节流的字符串来表示。unserialize()
函数能够重新把字符串变为原来的值。
序列化一个对象,将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。为了能够unserialize()
一个对象,这个对象的类必须已经定义过。在应用程序中序列化对象以便在之后使用,强烈推荐在整个应用程序都包含对象的类的定义。
(全文完)
以上就介绍了类与对象 - PHP手册笔记,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。