搜索
首页web前端js教程PHP静态绑定使用详解
PHP静态绑定使用详解May 09, 2018 am 11:24 AM
php使用详解

这次给大家带来PHP静态绑定使用详解,PHP静态绑定使用的注意事项有哪些,下面就是实战案例,一起来看一下。

基础知识

1. 范围解析操作符 (::)

  • 可以用于访问静态成员,类常量,还可以用于覆盖类中的属性和方法

  • 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

结论:

self::和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)如果没有指定访问控制,属性和方法默认为公有。

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 = 'Foo';
print $classname::$my_static . "\n"; // As of PHP 5.3.0
print Bar::$my_static . "\n";
$bar = new Bar();
print $bar->fooStatic() . "\n";
?>

c.用于后期静态绑定(出现位置: 类的方法中,用于修饰变量或方法)

下面详细分析

后期静态绑定(late static binding)

自 PHP 5.3.0 起,PHP 增加了一个叫做后期静态绑定的功能,用于在继承范围内引用静态调用的类。

1.转发调用与非转发调用

转发调用 :

指的是通过以下几种方式进行的静态调用:self::,parent::,static:: 以及 forward_static_call()。

非转发调用 :

明确指定类名的静态调用(例如Foo::foo())

非静态调用(例如$foo->foo())

2.后期静态绑定工作原理

原理:存储了在上一个“非转发调用”(non-forwarding call)中的类名。意思是当我们调用一个转发调用的静态调用时,实际调用的类是上一个非转发调用的类。

例子分析:

<?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'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

应用

前面已经提到过了,引入后期静态绑定的目的是:用于在继承范围内引用静态调用的类。
所以, 可以用后期静态绑定的办法解决单例继承问题。

先看一下使用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的同一个对象

再来看看使用static会得到什么样的结果

<?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) {
}

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

Vue如何使用动态刷新Echarts组件

vuex使用步骤详解

Vue.js里computed使用案例详解

以上是PHP静态绑定使用详解的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
php怎么把负数转为正整数php怎么把负数转为正整数Apr 19, 2022 pm 08:59 PM

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

php怎么实现几秒后执行一个函数php怎么实现几秒后执行一个函数Apr 24, 2022 pm 01:12 PM

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php字符串有没有下标php字符串有没有下标Apr 24, 2022 am 11:49 AM

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php怎么除以100保留两位小数php怎么除以100保留两位小数Apr 22, 2022 pm 06:23 PM

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

php怎么读取字符串后几个字符php怎么读取字符串后几个字符Apr 22, 2022 pm 08:31 PM

在php中,可以使用substr()函数来读取字符串后几个字符,只需要将该函数的第二个参数设置为负值,第三个参数省略即可;语法为“substr(字符串,-n)”,表示读取从字符串结尾处向前数第n个字符开始,直到字符串结尾的全部字符。

php怎么根据年月日判断是一年的第几天php怎么根据年月日判断是一年的第几天Apr 22, 2022 pm 05:02 PM

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

php怎么替换nbsp空格符php怎么替换nbsp空格符Apr 24, 2022 pm 02:55 PM

方法:1、用“str_replace("&nbsp;","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\&nbsp\;||\xc2\xa0)/","其他字符",$str)”语句。

php怎么查找字符串是第几位php怎么查找字符串是第几位Apr 22, 2022 pm 06:48 PM

查找方法:1、用strpos(),语法“strpos("字符串值","查找子串")+1”;2、用stripos(),语法“strpos("字符串值","查找子串")+1”。因为字符串是从0开始计数的,因此两个函数获取的位置需要进行加1处理。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具