澳门新浦京娱乐游戏php之基础深入—类与对象篇

有些东西如果不是经常使用,很容易忘记,比如魔术方法和魔术常量。

1、类的自动加载: spl_autoload_register()函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载,这样可以避免include一大堆文件。

魔术方法(Magic methods)

PHP中把以两个下划线__开头的方法称为魔术方法,这些方法在PHP中充当了举足轻重的作用。
魔术方法包括:

  • __construct(),类的构造函数
  • __destruct(),类的析构函数
  • __call(),在对象中调用一个不可访问方法时调用
  • __callStatic(),用静态方式中调用一个不可访问方法时调用
  • __get(),获得一个类的成员变量时调用
  • __set(),设置一个类的成员变量时调用
  • __isset(),当对不可访问属性调用isset()empty()时调用
  • __unset(),当对不可访问属性调用unset()时被调用。
  • __sleep(),执行serialize()时,先会调用这个函数
  • __wakeup(),执行unserialize()时,先会调用这个函数
  • __toString(),类被当成字符串时的回应方法
  • __invoke(),调用函数的方式调用一个对象时的回应方法
  • __set_state(),调用var_export()导出类时,此静态方法会被调用。
  • __clone(),当对象复制完成时调用

比如你有一个(甚至很多个)需要引用的类文件Example.php,它是这样子的

__construct()__destruct()

构造函数和析构函数应该不陌生,他们在对象创建和消亡时被调用。例如我们需要打开一个文件,在对象创建时打开,对象消亡时关闭

<?php 
class FileRead
{
    protected $handle = NULL;

    function __construct(){
        $this->handle = fopen(...);
    }

    function __destruct(){
        fclose($this->handle);
    }
}
?>

这两个方法在继承时可以扩展,例如:

<?php 
class TmpFileRead extends FileRead
{
    function __construct(){
        parent::__construct();
    }

    function __destruct(){
        parent::__destruct();
    }
}
?>
<?php
class Example{
    public function __construct() {
        echo "I am zhylioooo";
    }
}

__call()__callStatic()

在对象中调用一个不可访问方法时会调用这两个方法,后者为静态方法。这两个方法我们在可变方法(Variable
functions)调用中可能会用到。

<?php
class MethodTest 
{
    public function __call ($name, $arguments) {
        echo "Calling object method '$name' ". implode(', ', $arguments). "n";
    }

    public static function __callStatic ($name, $arguments) {
        echo "Calling static method '$name' ". implode(', ', $arguments). "n";
    }
}

$obj = new MethodTest;
$obj->runTest('in object context');
MethodTest::runTest('in static context');
?>

你还有一个父文件,或者父类,或者说是公共文件,总之是你必须要用到的文件,它是这样子的

__get()__set()__isset()__unset()

当get/set一个类的成员变量时调用这两个函数。例如我们将对象变量保存在另外一个数组中,而不是对象本身的成员变量

<?php 
class MethodTest
{
    private $data = array();

    public function __set($name, $value){
        $this->data[$name] = $value;
    }

    public function __get($name){
        if(array_key_exists($name, $this->data))
            return $this->data[$name];
        return NULL;
    }

    public function __isset($name){
        return isset($this->data[$name])
    }

    public function unset($name){
        unset($this->data[$name]);
    }
}
?>
<?php
function getClass($class) {
    $file = $class . '.php';
    if (is_file($file)) {
        require_once($file);
    }
}
spl_autoload_register('getClass');
//实例化一个本文件没有的类
new Example();    //当你在这个公共文件上面或者包含它的文件下面实例化不在同一个文件上的类时,就会自动查找并把那个类文件加载进来,就可以实例化了

__sleep()__wakeup()

当我们在执行serialize()unserialize()时,会先调用这两个函数。例如我们在序列化一个对象时,这个对象有一个数据库链接,想要在反序列化中恢复链接状态,则可以通过重构这两个函数来实现链接的恢复。例子如下:

<?php
class Connection 
{
    protected $link;
    private $server, $username, $password, $db;

    public function __construct($server, $username, $password, $db)
    {
        $this->server = $server;
        $this->username = $username;
        $this->password = $password;
        $this->db = $db;
        $this->connect();
    }

    private function connect()
    {
        $this->link = mysql_connect($this->server, $this->username, $this->password);
        mysql_select_db($this->db, $this->link);
    }

    public function __sleep()
    {
        return array('server', 'username', 'password', 'db');
    }

    public function __wakeup()
    {
        $this->connect();
    }
}
?>

当然,这只是最简单的例子,但是当需要引用的类文件比较多的时候,就很能体现出spl_autoload_register()的价值与灵活性了。

__toString()

对象当成字符串时的回应方法。例如使用echo $obj;来输出一个对象

<?php
// Declare a simple class
class TestClass
{
    public function __toString() {
        return 'this is a object';
    }
}

$class = new TestClass();
echo $class;
?>

这个方法只能返回字符串,而且不可以在这个方法中抛出异常,否则会出现致命错误。

2、代码的复用:trait定义和class相似,但是无法实例化自身,通常用use来实现代码复用,相当于把trait的内容复制到use的地方,use多个trait类时用逗号隔开

__invoke()

调用函数的方式调用一个对象时的回应方法。如下

<?php
class CallableClass 
{
    function __invoke() {
        echo 'this is a object';
    }
}
$obj = new CallableClass;
var_dump(is_callable($obj));
?>
<?php
trait A{
    function say(){
        echo "hello";
    }
}
class B{
    use A;
}
$obj=new B();
$obj->say();

__set_state()

调用var_export()导出类时,此静态方法会被调用。

<?php
class A
{
    public $var1;
    public $var2;

    public static function __set_state ($an_array) {
        $obj = new A;
        $obj->var1 = $an_array['var1'];
        $obj->var2 = $an_array['var2'];
        return $obj;
    }
}

$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';
var_dump(var_export($a));
?>

从基类继承的成员会被 trait
插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而
trait 则覆盖了被继承的方法。

__clone()

当对象复制完成时调用。例如在设计模式详解及PHP实现:单例模式一文中提到的单例模式实现方式,利用这个函数来防止对象被克隆。

<?php 
public class Singleton {
    private static $_instance = NULL;

    // 私有构造方法 
    private function __construct() {}

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

    // 防止克隆实例
    public function __clone(){
        die('Clone is not allowed.' . E_USER_ERROR);
    }
}
?>
<?php
trait A{
    function say(){
        echo "hello";
    }
    function listen(){
        echo "Wow!";
    }
}
class B{
    function say(){
        echo "zhylioooo";
    }
}
class C extends B{
    use A;
    function listen(){
        echo "boom!";
    }
}
$obj=new C();
$obj->say();    //输出"hello",trait类A覆盖了父类B的相同方法
$obj->listen(); //输出"boom",当前类C覆盖了trait类A的相同方法

魔术常量(Magic constants)

PHP中的常量大部分都是不变的,但是有8个常量会随着他们所在代码位置的变化而变化,这8个常量被称为魔术常量。

  • __LINE__,文件中的当前行号
  • __FILE__,文件的完整路径和文件名
  • __DIR__,文件所在的目录
  • __FUNCTION__,函数名称
  • __CLASS__,类的名称
  • __TRAIT__,Trait的名字
  • __METHOD__,类的方法名
  • __NAMESPACE__,当前命名空间的名称

这些魔术常量常常被用于获得当前环境信息或者记录日志。

3、匿名类的创建和使用:PHP 7 开始支持匿名类。
匿名类可以创建一次性的简单对象,可以传递参数到匿名类的构造器,也可以扩展(extend)其他类、实现接口(implement
interface),以及像其他普通的类一样使用 trait,

<?php
class SomeClass {}
interface SomeInterface {}
trait SomeTrait {}
var_dump(new class(10) extends SomeClass implements SomeInterface {
    private $num;
    public function __construct($num)
    {
        $this->num = $num;
    }
    use SomeTrait;
});
/*
 *以上代码会输出:
 *object(class@anonymous)#1 (1) {
 *["Command line code0x104c5b612":"class@anonymous":private]=>
 *int(10)
 *  }
 */

4、重载overloading:当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用,所有的重载方法都必须被声明为 public,这些魔术方法的参数都不能通过引用传递

属性重载

<?php
class A{
    private $name="zhylioooo";
    private $data=array();       //保存被重载的数据
    //在给不可访问属性赋值时,__set() 会被调用
    public function __set($name, $value) {
        $this->data[$name]=$value;
    }
    //读取不可访问属性的值时,__get() 会被调用
    public function __get($name) {
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }else{
            return "Undefined property ".$name;
        }
    }
    //当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用
    public function __isset($name) {
        return isset($this->data[$name]);
    }
    //当对不可访问属性调用 unset() 时,__unset() 会被调用
    public function __unset($name){
        echo $name."has unset.";
        unset($this->data[$name]);
    }
    //调用存在的属性
    public function getName(){
        return $this->name;
    }
}

$obj=new A();
echo $obj->getName();   //输出"zhylioooo"
echo $obj->age;         //输出"Undefined property age"
var_dump(isset($obj->age)); //输出 ...03.php:34:boolean false
unset($obj->age);       //输出"age has unset"
$obj->age=24;
echo $obj->age;         //输出24

方法重载

<?php
class A{
    private function say1(){
        echo "hello";
    }
    private static function say2(){
        echo "world";
    }
    //在对象中调用一个不可访问方法时,__call() 会被调用
    //$name 参数是要调用的方法名称。$arguments 参数是一个枚举数组,包含着要传递给方法 $name 的参数
    public function __call($name, $arguments) {
        echo "call the ".$name.":";
        $this->say1();
    }
    public static function __callStatic($name, $arguments){
        echo "call the static method ".$name;
        self::say2();
    }
}
$obj=new A();
$obj->say1();   //输出call the say1:hello
A::say2();      //输出call the static method say2world

 5、魔术方法:PHP 将所有以
__(两个下划线)开头的类方法保留为魔术方法,包括

  • __construct(),类的构造函数(类实例化时自动调用)
  • __destruct(),类的析构函数(销毁一个类之前调用)
  • __call(),在对象中调用一个不可访问方法时调用
  • __callStatic(),用静态方式中调用一个不可访问方法时调用
  • __get(),获得一个类的成员变量时调用
  • __set(),设置一个类的成员变量时调用
  • __isset(),当对不可访问属性调用isset()或empty()时调用
  • __unset(),当对不可访问属性调用unset()时被调用。
  • __sleep(),执行serialize()时,先会调用这个函数
  • __wakeup(),执行unserialize()时,先会调用这个函数
  • __toString(),类被当成字符串时的回应方法
  • __invoke(),调用函数的方式调用一个对象时的回应方法
  • __set_state(),调用var_export()导出类时,此静态方法会被调用(var_export和var_dump类似)
  • __clone(),当对象复制完成时调用
  • __autoload(),尝试加载未定义的类(建议使用spl_autoload_register())
  • __debugInfo(),打印所需调试信息 

其中__sleep和__wakeup

<?php
//serialize()函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,则该方法会优先被调用,然后才执行序列化操作。
//unserialize()会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,再进行反序列化操作。
//序列化函数serialize()的作用是把不方便存储和传递的php变量序列化为可存储的字符串,而反序列化函数unserialize()则是把相应字符串反序列化为php变量。
//serialize()可处理除了 resource 之外的任何类型
$arr=array(1,2,3);
$data= serialize($arr);
var_dump($data);   //输出..3.php:6:string 'a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}' (length=30)
$new_arr= unserialize($data);
var_dump($new_arr); 
/*输出C:wamp64wwwtest3.php:8:
array (size=3)
  0 => int 1
  1 => int 2
  2 => int 3
 * */

<?php
class A{
    private $name,$pswd;
    public function __construct($name,$pswd) {
        $this->name=$name;
        $this->pswd=$pswd;
        $this->login();
    }
    public function login(){
        echo "hello ".$this->name."!your pswd is ".$this->pswd;
    }

    public function __sleep() {
        return array("name","pswd");
    }
    public function __wakeup() {
        $this->login();
    }
}
$obj=new A("zhylioooo","123456");   //输出hello zhylioooo!your pswd is 123456
$str=serialize($obj);
unserialize($str);      //再次输出hello zhylioooo!your pswd is 123456
/*
*序列化操作对象时,__sleep()用于提交未提交的数据,即保持对象现场状态
*__wakeup()则是恢复现场 
*/

6、  类与对象的几个重要函数和关键字

class_exists() 判断某个类是否存在
interface_exists() 判断接口是否存在
class_implements() 返回指定类实现的所有接口
class_parents() 返回指定类的父类
get_class() __CLASS__ 获取某个对象所处的类名
get_parent_class() 获取某个对象所属父类的类名
get_class_methods() 获取一个类所有方法,返回索引数组
get_class_vars() 获取一个类所有属性,下标为属性名
get_declared_classes() 获取所有声明过的类(包括系统类)
is_object() 判断是否对象
get_object_vars() 获得对象所有属性,返回数组,下标为属性名
property_exists() 判断对象中是否存在该属性
get_called_class() 获取当前主调类的类名(最初调用的类)

this 是指向当前对象的指针(姑且用C里面的指针来看吧)
self 是指向当前类的指针
parent 是指向父类的指针
static 是指向最初调用的类

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注