PHP对象相关知识总结


Posted in PHP onApril 09, 2017

对象传递:一种说法是“PHP对象是通过引用传递的”,更准确的说法是别名(标识符)传递,即它们都保存着同一个标识符(ID)的拷贝,这个标识符指向同一个对象的真正内容。

<?php
 class A {
   public $foo = 1;
 } 
 
 $a = new A;
 $b = $a;   // $a ,$b都是同一个标识符的拷贝
       // ($a) = ($b) = <id>
 $b->foo = 2;
 echo $a->foo."\n";//2

 $c = new A;
 $d = &$c;  // $c ,$d是引用
       // ($c,$d) = <id>
 
 $d->foo = 2;
 echo $c->foo."\n";//2
 
 $e = new A;
 
 function foo($obj) {
   // ($obj) = ($e) = <id>
   $obj->foo = 2;
 }
 
 foo($e);
 echo $e->foo."\n";//2

•对象复制:对象复制可以通过 clone 关键字来完成,如果原对象定义了 __clone() 方法,则新对象中的 __clone() 方法将在复制完后被调用,__clone() 方法可用于修改复制对象属性的值。当对象被复制后,会对对象的所有属性执行一个浅复制(shallow copy),但所有的引用属性仍然会是一个指向原来的变量的引用。

<?php
 class SubObject
 {
   static $instances = 0;
   public $instance;
 
   public function __construct()
   {
     $this->instance = ++self::$instances;
   }
 
   public function __clone()
   {
     $this->instance = ++self::$instances;
   }
 }
 
 class MyCloneable
 {
   public $object1;
   public $object2;
 
   function __clone()
   {
     // 强制复制一份this->object, 否则仍然指向同一个对象
     $this->object1 = clone $this->object1;
   }
   
   function cloneTest()
   {
     echo 'cloneTest';
   }
 }
 
 $obj = new MyCloneable();
 
 $obj->object1 = new SubObject();
 $obj->object2 = new SubObject();
 
 $obj2 = clone $obj;
 
 print("Original Object:\n");
 print_r($obj);
 
 print("Cloned Object:\n");
 print_r($obj2);
 echo $obj2->cloneTest().":\n";
 echo (new Reflectionclass($obj2));

上例输出结果:

Original Object:
MyCloneable Object
(
  [object1] => SubObject Object
    (
      [instance] => 1
    )

  [object2] => SubObject Object
    (
      [instance] => 2
    )

)
Cloned Object:
MyCloneable Object
(
  [object1] => SubObject Object
    (
      [instance] => 3
    )

  [object2] => SubObject Object
    (
      [instance] => 2
    )

)
cloneTest:
Class [ <user> class MyCloneable ] {
 @@ /public/t.php 18-33

 - Constants [0] {
 }

 - Static properties [0] {
 }

 - Static methods [0] {
 }

 - Properties [2] {
  Property [ <default> public $object1 ]
  Property [ <default> public $object2 ]
 }

 - Methods [2] {
  Method [ <user> public method __clone ] {
   @@ /public/t.php 23 - 27
  }

  Method [ <user> public method cloneTest ] {
   @@ /public/t.php 29 - 32
  }
 }
}

•对象遍历: foreach只能遍历对象的可见属性,无法遍历其方法,实现起来比较容易;另外,也可通过实现Iterator接口或IteratorAggregate接口的方法遍历对象属性。

•类型约束: PHP作为一种弱类型语言,类型约束可以让编程更加规范,也少出些差错;类型约束不只能用在对象定义中,也能用在函数定义中。类型约束可指定对象、接口、array、callable(闭包callback),类型约束用来保证实际数据类型与原型定义一致,不一致则抛出一个可捕获的致命错误;不过如果定义了默认值为NULL,那么实参可以是NULL;类型约束不能用于标量类型如 int 或 string,Traits 也不允许。

•对象序列化与还原:函数serialize()可将打成包含字节流的字符串便于存储对象,函数unserialize()能够还原字符串为对象。但有一个前提是,无论序列化还是反序列化,对象的类定义已经完成,即需要先导入类(文件)。

•重载:PHP的重载包括属性和方法,更像一个套用说法,不支持常见的重载语法规范,具有不可预见性,影响范围更宽泛,就是利用魔术方法(magic methods)来调用当前环境下未定义或不可见的类属性或方法。所有重载方法都必须被声明为 public(这一条应该比较好理解,别人可能因不可见才需要你,那你自己必须可见才行),参数也不能通过引用传递(重载方法具有不可预见性,估计出于安全方面的考虑吧,防止变量被随意引用)。在除 isset() 外的其它语言结构中无法使用重载的属性,这意味着当对一个重载的属性使用 empty() 时,重载魔术方法将不会被调用; 为避开此限制,必须将重载属性赋值到本地变量再使用 empty(),可见重载属性是介于合法属性与非法属性之间的存在。

[属性重载]:这些方法不能被声明为 static,在静态方法中,这些魔术方法将不会被调用
public void __set ( string $name , mixed $value )
在给不可访问属性赋值时,__set() 会被调用

public mixed __get ( string $name )
读取不可访问属性的值时,__get() 会被调用

public bool __isset ( string $name )
当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用

public void __unset ( string $name )
当对不可访问属性调用 unset() 时,__unset() 会被调用

Note:
因为 PHP 处理赋值运算的方式,__set() 的返回值将被忽略。类似的, 在下面这样的链式赋值中,__get() 不会被调用:
 $a = $obj->b = 8;

[方法重载]:
public mixed __call ( string $name , array $arguments )
在对象中调用一个不可访问方法时,__call() 会被调用

public static mixed __callStatic ( string $name , array $arguments )
在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用

•静态属性和方法:static 关键字用来定义静态属性、静态方法,静态属性不能通过实例化的对象-> 来访问(但静态方法可以)。静态属性只能被初始化为常量表达式,所以可以把静态属性初始化为整数或数组,但不能初始化为另一个变量或函数返回值,也不能指向一个对象。可以用一个变量表示类来动态调用静态属性,但该变量的值不能为关键字 self,parent 或 static。

class Foo
 {
   public static $my_static = 'foo';
 
   public function staticValue() {
     return self::$my_static;
   }
 }
 
 class Bar extends Foo
 {
   public function fooStatic() {
     return parent::$my_static;
   }
 }
 
 
 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";

•后期静态绑定:static:: 定义后期静态绑定工作原理是存储了上一个“非转发调用”(non-forwarding call)的类名。当进行静态方法调用时,该类名即为明确指定的那个(通常在 :: 运算符左侧部分);当进行非静态方法调用时,即为该对象所属的类。使用 self:: 或者 __CLASS__ 对当前类的静态引用,取决于定义当前方法所在的类;static:: 不再被解析为定义当前方法所在的类,而是在实际运行时计算的,可以用于静态属性和所有方法的调用。

<?php
 class A
 {
   
   private $proPrivate = "private of A";
   protected $proProtected = "protected of A";
   public $proPublic = "public of A";
   
   private function foo()
   {
     echo $this->proPrivate."\n";
     echo $this->proProtected."\n";
     echo $this->proPublic."\n";
   }
   
   public function test()
   {
     $this->foo();
     static::foo();
   }
 }
 
 class B extends A
 {
  /* foo() will be copied to B, hence its scope will still be A and
   * the call be successful */
 }
 
 class C extends A
 {
   private $proPrivate = "private of C";
   protected $proProtected = "protected of C";
   public $proPublic = "public of C";
   
   private function foo()
   {
     /* original method is replaced; the scope of the new one is C */
     echo "I am C\n";
   }
   
   public function myFoo()
   {
     //parent::foo();
     $this->foo();
   }
 }
 
 echo "Class B:\n";
 $b = new B();
 $b->test();
 echo "\nClass C:\n";
 $c = new C();
 $c->myFoo();
 $c->test();  //fails

上例输出结果:

Class B:
private of A
protected of A
public of A
private of A
protected of A
public of A

Class C:
I am C
private of A
protected of C
public of C 
Fatal error: Uncaught Error: Call to private method C::foo() from context 'A' in /public/t.php:19 Stack trace: #0 /public/t.php(54): A->test() #1 {main} thrown in /public/t.php on line 19

•继承:官方文档对继承有这样一段描述“当扩展一个类,子类就会继承父类所有公有的和受保护的方法。除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能”,言下之意似乎私有属性和方法不会被继承;然而上例又告诉我们子类拥有与父类一致的属性和方法,继承就是全盘复制,这才能满足我们对继承编程的需求,如果私有的不能继承,子类就必须自行重新定义,在大多数时候没有必要。另外就是可见性问题,父类的私有属性和方法在子类是不可见的。上例还告诉我们对象实际执行的域要考虑可见性、继承、后期静态绑定机制。

PHP 相关文章推荐
让你的PHP同时支持GIF、png、JPEG
Oct 09 PHP
ob_start(),ob_start('ob_gzhandler')使用
Dec 25 PHP
PHP整数取余返回负数的相关解决方法
May 15 PHP
PHP列出MySQL中所有数据库的方法
Mar 12 PHP
PHP异常处理浅析
May 12 PHP
PHP实现在线阅读PDF文件的方法
Jun 17 PHP
四个常见html网页乱码问题及解决办法
Sep 08 PHP
PHP QRCODE生成彩色二维码的方法
May 19 PHP
php连接oracle数据库的方法(测试成功)
May 26 PHP
php实现生成带二维码图片并强制下载功能
Feb 24 PHP
PHP实现链表的定义与反转功能示例
Jun 09 PHP
php输出反斜杠的实例方法
Sep 19 PHP
YII2框架中使用yii.js实现的post请求
Apr 09 #PHP
PHP使用SWOOLE扩展实现定时同步 MySQL 数据
Apr 09 #PHP
CentOS系统中PHP安装扩展的方式汇总
Apr 09 #PHP
PHP将身份证正反面两张照片合成一张图片的代码
Apr 08 #PHP
ThinkPHP中调用PHPExcel的实现代码
Apr 08 #PHP
yii框架无限极分类的实现方法
Apr 08 #PHP
PHP下载远程图片的几种方法总结
Apr 07 #PHP
You might like
jquery 输入框数字限制插件
2009/11/10 Javascript
Extjs学习过程中新手容易碰到的低级错误积累
2010/02/11 Javascript
JS input文本框禁用右键和复制粘贴功能的代码
2010/04/15 Javascript
{}与function(){}选用空对象{}来存放keyValue
2012/05/23 Javascript
js jquery数组介绍
2012/07/15 Javascript
正负小数点后两位浮点数实现原理及代码
2013/09/06 Javascript
js阻止冒泡及jquery阻止事件冒泡示例介绍
2013/11/19 Javascript
微信小程序 用户数据解密详细介绍
2017/01/09 Javascript
深入剖析Express cookie-parser中间件实现示例
2018/02/01 Javascript
React Hooks的深入理解与使用
2018/11/12 Javascript
node.js使用yargs处理命令行参数操作示例
2020/02/11 Javascript
javascript将16进制的字符串转换为10进制整数hex
2020/03/05 Javascript
[03:12]TI9战队档案 - Virtus Pro
2019/08/20 DOTA
Python实现telnet服务器的方法
2015/07/10 Python
Python实现登录接口的示例代码
2017/07/21 Python
Python3多进程 multiprocessing 模块实例详解
2018/06/11 Python
python实现梯度下降算法
2020/03/24 Python
如何安装多版本python python2和python3共存以及pip共存
2018/09/18 Python
在pycharm下设置自己的个性模版方法
2019/07/15 Python
Python 将 QQ 好友头像生成祝福语的实现代码
2020/05/03 Python
如何通过python计算圆周率PI
2020/11/11 Python
python 动态渲染 mysql 配置文件的示例
2020/11/20 Python
viagogo法国票务平台:演唱会、体育比赛、戏剧门票
2017/03/27 全球购物
StubHub中国:购买和出售全球活动门票
2020/01/01 全球购物
俄罗斯GamePark游戏商店网站:购买游戏、游戏机和配件
2020/03/13 全球购物
外企C语言笔试题
2013/11/10 面试题
药剂专业学生求职信范文
2013/12/28 职场文书
环保倡议书怎么写
2014/05/16 职场文书
汽车服务工程专业自荐信
2014/09/02 职场文书
党的群众路线教育实践活动剖析材料
2014/09/30 职场文书
2014年化工厂工作总结
2014/11/25 职场文书
2015年学校心理健康教育工作总结
2015/05/11 职场文书
党组织结对共建协议书
2016/03/23 职场文书
浅谈Redis的keys命令到底有多慢
2021/10/05 Redis
Win11 S Mode版本泄露 正式上线后叫做Windows 11 SE
2021/11/21 数码科技
微软官方消息,在 2023 年 4 月 11 日之后微软将不再为 Office 2013 和 Skype for Business 2015 提供安全更新
2022/04/21 数码科技