ホームページ  >  記事  >  ウェブフロントエンド  >  PHP後期静的バインディング解析と応用の詳細説明

PHP後期静的バインディング解析と応用の詳細説明

亚连
亚连オリジナル
2018-05-26 17:45:361120ブラウズ

この記事は、PHP の後期静的バインディング分析と応用に関する関連知識ポイントをまとめたもので、興味のある方は学習してください。

基礎知識

1. 範囲解決演算子 (::)

  • は、静的メンバーやクラス定数にアクセスするために使用でき、クラス内のプロパティやメソッドをオーバーライドするためにも使用できます。

  • 3 つの特別なキーワード self、parent、static は、クラス定義内のプロパティまたはメソッドにアクセスするために使用されます。

  • parent は、親クラスでオーバーライドされたプロパティまたはメソッドを呼び出すために使用されます (それが表示される場合、対応するクラスの親クラスに解決されます)。

  • self は、このクラスのメソッドまたはプロパティを呼び出すために使用されます (これが表示される場合、対応するクラスに解析されます。$this との違いに注意してください。$this は、現在インスタンス化されているオブジェクトを指します)。

  • サブクラスが親クラスのメソッドをオーバーライドする場合、PHP は親クラスでオーバーライドされたメソッドを呼び出しません。親クラスのメソッドを呼び出すかどうかは子クラスに依存します。

2. PHP カーネルはクラスの継承実装を「コンパイルフェーズ」に置きます

<?php
class A{
 const H = &#39;A&#39;;

 const J = &#39;A&#39;;

 static function testSelf(){
  echo self::H; //在编译阶段就确定了 self解析为 A
 }
}

class B extends A{
 const H = "B";

 const J = &#39;B&#39;;

 static function testParent(){
  echo parent::J; //在编译阶段就确定了 parent解析为A
 }

 /* 若重写testSelf则能输出“B”, 且C::testSelf()也是输出“B”
 static function testSelf(){
  echo self::H;
 }
 */

}

class C extends B{
 const H = "C";

 const J = &#39;C&#39;;
}

B::testParent();
B::testSelf();

echo "\n";

C::testParent();
C::testSelf();

実行結果:

AA
AA


結論:


自分: : およびparent:: がクラス X の定義に出現すると、親クラスのメソッドがサブクラスでオーバーライドされない限り、対応するクラス X に解析されます。

3.Static (静的) キーワード

機能:

- 関数本体内の変数を変更する static キーワードは、静的ローカル変数を定義するために使用されます。

- クラスのメンバー関数とメンバー変数を変更するときに静的メンバーを宣言するために使用されます。
- (PHP5.3 以降) スコープ リゾルバー (::) の前の静的な遅延バインディングを表す特別なクラス。

例:

静的ローカル変数を定義します (出現: ローカル関数内)


特徴: 静的変数はローカル関数スコープ内にのみ存在しますが、プログラムの実行がこのスコープを離れても、その値は失われません。

<?php
function test()
{
 static $count = 0;

 $count++;
 echo $count;
 if ($count < 10) {
  test();
 }
 $count--;
}

静的メソッド、静的属性を定義する


a) クラスの属性またはメソッドを静的として宣言し、クラスをインスタンス化せずに直接アクセスできるようにします。


b) クラスのインスタンス化されたオブジェクトを通じて静的プロパティにアクセスすることはできません (ただし、静的メソッドはアクセスできます)


c) アクセス制御が指定されていない場合、プロパティとメソッドはデフォルトで public になります。


d) 静的メソッドではオブジェクトを呼び出す必要がないため、疑似変数 $this は静的メソッドでは使用できません。


e) -> 演算子を使用してオブジェクトから静的プロパティにアクセスすることはできません。


f) 非静的メソッドを静的に呼び出すと、E_STRICT レベルのエラーが発生します。


g) 他のすべての PHP 静的変数と同様、静的プロパティは式ではなくリテラルまたは定数にのみ初期化できます。したがって、静的プロパティは整数または配列に初期化できますが、別の変数や関数の戻り値に初期化したり、オブジェクトを指すことはできません。

a. 静的メソッドの例 (クラスメソッド定義に表示)

<?php
class Foo {
 public static function aStaticMethod() {
  // ...
 }
}

Foo::aStaticMethod();
$classname = &#39;Foo&#39;;
$classname::aStaticMethod(); // 自PHP 5.3.0后,可以通过变量引用类
?>

b. 静的属性の例 (クラス属性定義に表示)

<?php
class Foo
{
 public static $my_static = &#39;foo&#39;;

 public function staticValue() {
  return self::$my_static; //self 即 FOO类
 }
}

class Bar extends Foo
{
 public function fooStatic() {
  return parent::$my_static; //parent 即 FOO类
 }
}

print Foo::$my_static . "\n";

$foo = new Foo();
print $foo->staticValue() . "\n";
print $foo->my_static . "\n";  // Undefined "Property" my_static 

print $foo::$my_static . "\n";
$classname = &#39;Foo&#39;;
print $classname::$my_static . "\n"; // As of PHP 5.3.0

print Bar::$my_static . "\n";
$bar = new Bar();
print $bar->fooStatic() . "\n";
?>

c.位置: クラスメソッド内で、変数またはメソッドを変更するために使用されます)


以下の詳細な分析

遅延静的バインディング (遅延静的バインディング)


PHP 5.3.0 以降、PHP には遅延静的バインディングと呼ばれる機能が追加されました継承スコープ内で静的に呼び出されるクラスを参照するために使用されます。

1. 転送された通話と転送されていない通話

転送された通話:

は、self::、parent::、static::、および forward_static_call() の方法で行われる静的呼び出しを指します。


非転送呼び出し:

クラス名を明示的に指定する静的呼び出し (例: Foo::foo())


非静的呼び出し (例: $foo->foo())


2 . Post-static バインディングの仕組み

原則: 前回の「非転送呼び出し」のクラス名が保存されます。これは、転送呼び出しである静的呼び出しを呼び出す場合、実際に呼び出されるクラスは、前の非転送呼び出しのクラスであることを意味します。

分析例:

<?php
class A {
 public static function foo() {
  echo __CLASS__."\n";
  static::who();
 }

 public static function who() {
  echo __CLASS__."\n";
 }
}

class B extends A {
 public static function test() {
  echo "A::foo()\n";
  A::foo();
  echo "parent::foo()\n";
  parent::foo();
  echo "self::foo()\n";
  self::foo();
 }

 public static function who() {
  echo __CLASS__."\n";
 }
}

class C extends B {
 public static function who() {
  echo __CLASS__."\n";
 }
}

C::test();

/*
 * C::test(); //非转发调用 ,进入test()调用后,“上一次非转发调用”存储的类名为C
 *
 * //当前的“上一次非转发调用”存储的类名为C
 * public static function test() {
 *  A::foo(); //非转发调用, 进入foo()调用后,“上一次非转发调用”存储的类名为A,然后实际执行代码A::foo(), 转 0-0
 *  parent::foo(); //转发调用, 进入foo()调用后,“上一次非转发调用”存储的类名为C, 此处的parent解析为A ,转1-0
 *  self::foo(); //转发调用, 进入foo()调用后,“上一次非转发调用”存储的类名为C, 此处self解析为B, 转2-0
 * }
 *
 *
 * 0-0
 * //当前的“上一次非转发调用”存储的类名为A
 * public static function foo() {
 *  static::who(); //转发调用, 因为当前的“上一次非转发调用”存储的类名为A, 故实际执行代码A::who(),即static代表A,进入who()调用后,“上一次非转发调用”存储的类名依然为A,因此打印 “A”
 * }
 *
 * 1-0
 * //当前的“上一次非转发调用”存储的类名为C
 * public static function foo() {
 *  static::who(); //转发调用, 因为当前的“上一次非转发调用”存储的类名为C, 故实际执行代码C::who(),即static代表C,进入who()调用后,“上一次非转发调用”存储的类名依然为C,因此打印 “C”

 * }
 *
 * 2-0
 * //当前的“上一次非转发调用”存储的类名为C
 * public static function foo() {
 *  static::who(); //转发调用, 因为当前的“上一次非转发调用”存储的类名为C, 故实际执行代码C::who(),即static代表C,进入who()调用后,“上一次非转发调用”存储的类名依然为C,因此打印 “C”
 * }
 */


故最终结果为:
A::foo()
A
A
parent::foo()
A
C
self::foo()
A
C

3. 静的遅延静的バインディングのその他の例

a) Self、Parent、Static の比較

<?php
class Mango {
 function classname(){
  return __CLASS__;
 }

 function selfname(){
  return self::classname();
 }

 function staticname(){
  return static::classname();
 }
}

class Orange extends Mango {
 function parentname(){
  return parent::classname();
 }

 function classname(){
  return __CLASS__;
 }
}

class Apple extends Orange {
 function parentname(){
  return parent::classname();
 }

 function classname(){
  return __CLASS__;
 }
}

$apple = new Apple();
echo $apple->selfname() . "\n";
echo $apple->parentname() . "\n";
echo $apple->staticname();

?>

运行结果:
Mango
Orange
Apple

b) forward_static_call() の使用

<?php
class Mango
{
 const NAME = &#39;Mango is&#39;;
 public static function fruit() {
  $args = func_get_args();
  echo static::NAME, " " . join(&#39; &#39;, $args) . "\n";
 }
}

class Orange extends Mango
{
 const NAME = &#39;Orange is&#39;;

 public static function fruit() {
  echo self::NAME, "\n";

  forward_static_call(array(&#39;Mango&#39;, &#39;fruit&#39;), &#39;my&#39;, &#39;favorite&#39;, &#39;fruit&#39;);
  forward_static_call(&#39;fruit&#39;, &#39;my&#39;, &#39;father\&#39;s&#39;, &#39;favorite&#39;, &#39;fruit&#39;);
 }
}

Orange::fruit(&#39;NO&#39;);

function fruit() {
 $args = func_get_args();
 echo "Apple is " . join(&#39; &#39;, $args). "\n";
}

?>


运行结果:
Orange is
Orange is my favorite fruit
Apple is my father&#39;s favorite fruit

c) get_called_class() を使用します

<?php


class Mango {
 static public function fruit() {
  echo get_called_class() . "\n";
 }
}

class Orange extends Mango {
 //
}

Mango::fruit();
Orange::fruit();

?>



运行结果:
Mango
Orange

Application

前述したように、遅延静的バインディングを導入する目的は次のとおりです: 継承スコープ内の静的参照の呼び出しクラス。

したがって、遅延静的バインディングを使用してシングルトン継承の問題を解決できます。

まず、self を使用することがどのようなものかを見てみましょう:

<?php
// new self 得到的单例都为A。
class A
{
 protected static $_instance = null;

 protected function __construct()
 {
  //disallow new instance
 }

 protected function __clone(){
  //disallow clone
 }

 static public function getInstance()
 {
  if (self::$_instance === null) {
   self::$_instance = new self();
  }
  return self::$_instance;
 }
}

class B extends A
{
 protected static $_instance = null;
}

class C extends A{
 protected static $_instance = null;
}

$a = A::getInstance();
$b = B::getInstance();
$c = C::getInstance();

var_dump($a);
var_dump($b);
var_dump($c);




运行结果:
E:\code\php_test\apply\self.php:37:
class A#1 (0) {
}
E:\code\php_test\apply\self.php:38:
class A#1 (0) {
}
E:\code\php_test\apply\self.php:39:
class A#1 (0) {
}

上記の例から、self を使用すると、インスタンス化されたオブジェクトはすべてクラス A の同じオブジェクトであることがわかります

静的はどのような結果を取得しますか?

<?php
// new static 得到的单例分别为D,E和F。
class D
{
 protected static $_instance = null;

 protected function __construct(){}
 protected function __clone()
 {
  //disallow clone
 }

 static public function getInstance()
 {
  if (static::$_instance === null) {
   static::$_instance = new static();
  }
  return static::$_instance;
 }
}

class E extends D
{
 protected static $_instance = null;
}

class F extends D{
 protected static $_instance = null;
}

$d = D::getInstance();
$e = E::getInstance();
$f = F::getInstance();

var_dump($d);
var_dump($e);
var_dump($f);




运行结果:
E:\code\php_test\apply\static.php:35:
class D#1 (0) {
}
E:\code\php_test\apply\static.php:36:
class E#2 (0) {
}
E:\code\php_test\apply\static.php:37:
class F#3 (0) {
}

上記は私がまとめたもので、将来的には皆さんのお役に立てば幸いです。

関連記事:

AjaxSubmit() はファイルを送信します

Ajax で冗長な更新を解決する 2 つの方法

Ajax の同期と非同期の問題と解決策

以上がPHP後期静的バインディング解析と応用の詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。