header-bg.jpg
PHP面向对象之魔术常量(Magic constants)与魔术方法(Magic Methods)
发表于 2018-01-06 23:18
|
分类于 PHP
|
评论次数 0
|
阅读次数 1490

attachment/2018/01/06/51961515250375.jpg

一 魔术常量(Magic constants)

PHP 向它运行的任何脚本提供了大量的预定义常量。不过很多常量都是由不同的扩展库定义的,只有在加载了这些扩展库时才会出现,或者动态加载后,或者在编译时已经包括进去了。

有八个魔术常量它们的值随着它们在代码中的位置改变而改变。例如 __LINE__ 的值就依赖于它在脚本中所处的行来决定。这些特殊的常量不区分大小写

__LINE__

?
1
2
//输出当前代码所在行号
echo __LINE__;    //output: 2
__FILE__
?
1
2
//输出文件的完整路径和文件名。如果用在被包含文件中,则返回被包含的文件名。
echo __FILE__;    //output: F:\Office Software\phpStudy\WWW\2018\1.6Magic\MagicConstants.php
__DIR__  (PHP 5.3.0中新增)

?
1
2
3
//文件所在的目录。如果用在被包括文件中,则返回被包括的文件所在的目录。它等价于 dirname(__FILE__)。
//除非是根目录,否则目录中名不包括末尾的斜杠。
echo __DIR__;    //output: F:\Office Software\phpStudy\WWW\2018\1.6Magic

__FUNCTION__  (PHP 4.3.0新增)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//输出当前函数的名称 在 PHP 4 中该值总是小写字母。
//自 PHP 5 起本常量返回该函数被定义时的名字(区分大小写)。
//并且当前有命名空间的话会返回包括命名空间的前缀
//但是如果方法在类中 不管有无命名空间都只返回方法名(不含命名空间的前缀)
 
//有命名空间
namespace Magic\MagicConstants;
function foo(){
    echo __FUNCTION__;
}
foo();    //output: Magic\MagicConstants\foo
 
//无命名空间
function foo(){
    echo __FUNCTION__;
}
foo();    //output: foo

__CLASS__  (PHP 4.3.0中新增)    &    __METHOD__  (PHP 5.0.0新增)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
//__CLASS__输出当前类的名称 在 PHP 4 中该值总是小写字母。
//自 PHP 5 起本常量返回该函数被定义时的名字(区分大小写)。
//并且当前有有命名空间的话会返回包括命名空间的前缀
//自 PHP 5.4 起 __CLASS__ 对 trait 也起作用。当用在 trait 方法中时,__CLASS__ 是调用 trait 方法的类的名字。
namespace Magic\MagicConstants;
class Foo{
    public function bar(){
        echo __CLASS__;    //output: Magic\MagicConstants\Foo
        echo __FUNCTION__;    //output: bar
        echo __METHOD__;    //output: Magic\MagicConstants\Foo::bar
    }
}
(new Foo)->bar();

__TRAIT__  (PHP 5.4.0中新增)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//输出Trait 的名称 自 PHP 5.4 起此常量返回 trait 被定义时的名字(区分大小写)。
//并且当前有有命名空间的话会返回包括命名空间的前缀
namespace Magic\MagicConstants;
trait Leo {
    function traitName() {
        echo __TRAIT__;    //output: Magic\MagicConstants\Leo
        echo '<br>';
        echo __CLASS__;    //output: Magic\MagicConstants\Test
    }
}
 
trait Coco {
    use Leo;
}
 
class Test {
    use Coco;
}
 
(new Test)->traitName();

__NAMESPACE__  (PHP 5.3.0 新增)

?
1
2
3
4
//输出当前命名空间的名称(区分大小写)。此常量是在编译时定义的
<?php
namespace Magic\MagicConstants;
echo __NAMESPACE__;    //output: Magic\MagicConstants
二 魔术方法(Magic Methods)

__construct( )    构造函数

构造函数 __construct( ) 的一些特性

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
/**
 * PHP 5 允行开发者在一个类中定义一个方法作为构造函数。
 * 具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。
 */
class Foo{
    public function __construct(){
        echo 'In Foo constructor';
    }
}
/**
 * 如果子类中定义了构造函数则不会隐式调用其父类的构造函数。
 * 要执行父类的构造函数,需要在子类的构造函数中调用 parent::__construct()。
 * 如果子类没有定义构造函数则会如同一个普通的类方法一样从父类继承(假如没有被定义为 private 的话)。
 */
class Bar extends Foo{
    public function __construct(){
        parent::__construct();    //output: 'In Foo constructor'
        echo 'In Bar constructor';    //output: 'In Bar constructor'
    }
}
new Bar;
?
1
2
3
4
5
6
7
8
9
10
11
12
<?php
/**
 * 为了实现向后兼容性,如果 PHP 5 在类中找不到 __construct() 函数并且也没有从父类继承一个的话,
 * 它就会尝试寻找旧式的构造函数,也就是和类同名的函数。
 * 如果在PHP 7 中这样使用会提示Deprecated信息 提醒用法已经过时 但是仍会输出结果
 */
class Foo{
    public function foo(){
        echo 'it\'s constructor';
    }
}
new Foo;    //output: it's constructor
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
namespace Foo;
/**
 * 自 PHP 5.3.3 起,在命名空间中,与类名同名的方法不再作为构造函数。这一改变不影响不在命名空间中的类。
 */
class Bar{
    public function bar(){
        echo 'not a constructor';
    }
 
    public function foo(){
        echo 'test';
    }
}
(new Bar)->foo();   //output: 'test'

构造方法 __construct( ) 妙用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
/**
 * 利用method_exists创造出多个构造方法
 * 实例化时传入不同数量的参数时 调用与参数相对应的构造方法
 */
class Foo {
    public function __construct() {
        $a = func_get_args();
        $n = func_num_args();
        $f = '__construct' . $n;
        if(method_exists($this, $f)) {
            call_user_func_array([$this,$f], $a);
        }
    }
 
    public function __construct1($a) {
        echo '__construct with 1 param called : ' . $a;
    }
 
    public function __construct2($a, $b) {
        echo '__construct with 2 param called : ' . $a . ', ' . $b;
    }
 
    public function __construct3($a, $b, $c){
        echo '__construct with 3 param called : ' . $a . ', ' . $b . ', ' . $c;
    }
}
new Foo('sheep');   //output: '__construct with 1 param called : sheep'
new Foo('sheep', 'cat');    //output: '__construct with 2 param called : sheep, cat'
new Foo('sheep', 'cat', 'dog');    //output: '__construct with 3 param called : sheep, cat, dog'
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
/**
 * 利用method_exists调用子类自定义方法__init 达到在子类构造方法中使用parent::__construct的效果
 */
class Foo {
    public function __construct() {
        echo 'In foo constructor';
        if(method_exists($this, '__init')) {
            $this->__init();
        }
    }
}
 
class Bar extends Foo {
    public function __init() {
        echo 'In bar constructor';
    }
}
 
new Bar();
//output:
//'In foo constructor'
//'In Bar constructor'

__destruct( )    析构函数

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
/**
 * 和构造函数一样,父类的析构函数不会被引擎暗中调用。
 * 要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct()。
 * 此外也和构造函数一样,子类如果自己没有定义析构函数则会继承父类的。
 */
class Foo {
    public function __construct() {
        echo 'In constructor';
        echo '<br>';
        $this->name = 'My DestructableClass';
    }
 
    public function bar(){
        echo 'In bar';
        echo '<br>';
    }
 
    public function __destruct() {
        echo 'Destorying ' . $this->name;
    }
}
(new Foo)->bar();
/**
 * output:
 * In constructor
 * In bar
 * Destorying My DestructableClass
 */

__call( ) 与 __callStatic( )    方法重载

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class Foo {
    //在对象中调用一个不可访问方法时,__call() 会被调用。
    //$name参数是要调用的方法名称 $arguments参数是一个枚举数组,包含着要传递给方法$name的参数。
    public function __call($name , $arguments) {
        echo "Calling object method '$name' " . implode(', ', $arguments);
    }
 
    //在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。(PHP 5.3.0中新增)
    public static function __callStatic($name, $arguments) {
        echo "Calling static method '$name'" . implode(', ', $arguments);
    }
}
(new Foo)->bar('In object context', 'success!');
//output: Calling object method 'bar' In object context, success!
 
Foo::bar('In static context', 'success!');
//output: Calling static method 'bar' In static context, success!

__get( )  __set( )   __isset( )  __unset( )    属性重载

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
/**
 * 属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。
 * 所以这些方法都不能被 声明为 static。
 * 从 PHP 5.3.0 起, 将这些魔术方法定义为 static 会产生一个警告。
 */
class propertyTest {
    /**  重载不能被用在已经定义的属性  */
    public $declared = 1;
    /**  只有从类外部访问这个属性时,重载才会发生 */
    private $hidden = 2;
    /**  被重载的数据保存在此  */
    private $data;
 
    /**
     * 在给不可访问属性赋值时,__set() 会被调用。
     * 参数 $name 是指要操作的变量名称。$value 参数指定了 $name 变量的值。
     */
    public function __set($name, $value) {
        echo 'Setting ' . $name . ' to ' . $value;
        $this->data[$name] = $value;
    }
 
    /*  读取不可访问属性的值时,__get() 会被调用。 */
    public function __get($name) {
        echo 'Getting ' . $name;
        if(array_key_exists($name, $this->data)){
            return $this->data[$name];
        }
        echo 'getting private property';
        return null;
    }
 
    /**  当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。(PHP 5.1.0之后版本) */
    public function __isset($name) {
        echo 'Testing ' . $name;
        return isset($this->data[$name]);
    }
 
    /**  当对不可访问属性调用 unset() 时,__unset() 会被调用。(PHP 5.1.0之后版本) */
    public function __unset($name) {
        echo 'Unsetting ' . $name;
        unset($this->data[$name]);
    }
 
    /**  非魔术方法  */
    public function getHidden() {
        return $this->hidden;
    }
}
$obj = new propertyTest;
$obj->a = 1;    //output: 'Setting a to 1'
echo $obj->a;    //output: 'Getting a'
                 //output: 1
//因为 PHP 处理赋值运算的方式,__set() 的返回值将被忽略。类似的, 在下面这样的链式赋值中,__get() 不会被调用:
$a = $obj->b = 8;    //output: 'Setting b to 8'
var_dump(empty($obj->a));    //output: 'Testing a'
                             //output: bool(false)
var_dump(isset($obj->a));    //output: 'Testing a'
                             //output: bool(true)
 
unset($obj->a);    //output: 'Unsetting a'
var_dump(isset($obj->a));    //output: bool(false)
echo $obj->getHidden();    //output: 2
$obj->hidden;    //output: 'Getting hidden'
                 //output: 'Getting private property'
__sleep() 和 __wakeup()


发布评论
还没有评论,快来抢沙发吧!