php中引用&的真正理解-变量引用、函数引用、对象引用

5.3以后已经不赞成使用引用来传递参数了貌似

只是再分析CI3的源码的时候还是能看到函数引用

总之再复习一下吧

总没有坏处

====================================================================

    php的引用(就是在变量或者函数、对象等前面加上&符号) //最重要就是 删除引用的变量 ,只是引用的变量访问不了,但是内容并没有销毁 在PHP 中引用的意思是:不同的名字访问同一个变量内容.

变量的引用

        PHP 的引用允许你用两个变量来指向同一个内容 

1
2
3
4
5
6
7
8
<?php
$a="ABC"
$b =&$a
echo $a;//这里输出:ABC 
echo $b;//这里输出:ABC 
$b="EFG"
echo $a;//这里$a的值变为EFG 所以输出EFG echo $b;//这里输出EFG 
?>

  函数的传址调用 传址调用我就不多说了 下面直接给出代码

1
2
3
4
5
6
7
<?php
function test(&$a){ 
    $a=$a+100; 
$b=1; 
echo $b;//输出1 test($b);   //这里$b传递给函数的其实是$b的变量内容所处的内存地址,通过在函数里改变$a的值 就可以改变$b的值了 echo "<br>"; echo $b;//输出101
?>

 要注意的是,在这里test(1);的话就会出错,原因是:PHP规定传递的引用不能为常量(可以看错误提示)。

1
2
3
4
5
6
7
8
9
10
11
12
<?php
function &test(){ 
    static $b=0;//申明一个静态变量 
    $b=$b+1; 
    echo $b
    return $b; }
}
$a=test();//这条语句会输出 $b的值 为1 
$a=5; $a=test();//这条语句会输出 $b的值 为2
$a=&test();//这条语句会输出 $b的值 为3 
$a=5; $a=test();//这条语句会输出 $b的值 为6
?>

下面解释下:  通过这种方式$a=test();得到的其实不是函数的引用返回,这跟普通的函数调用没有区别 至于原因: 这是PHP的规定 PHP规定通过$a=&test(); 方式得到的才是函数的引用返回 至于什么是引用返回呢(PHP手册上说:引用返回用在当想用函数找到引用应该被绑定在哪一个变量上面时。) 这句狗屁话 害我半天没看懂

       用上面的例子来解释就是 $a=test()方式调用函数,只是将函数的值赋给$a而已, 而$a做任何改变,都不会影响到函数中的$b,而通过$a=&test()方式调用函数呢, 他的作用是 将return $b中的 $b变量的内存地址与$a变量的内存地址 指向了同一个地方 即产生了相当于这样的效果($a=&b;) 所以改变$a的值 也同时改变了$b的值 所以在执行了 $a=&test(); $a=5; 以后,$b的值变为了5

这里是为了让大家理解函数的引用返回才使用静态变量的,其实函数的引用返回多用在对象中

对象的引用 

1
2
3
4
5
6
7
8
9
10
<?php
class a{
    var $abc="ABC";
$b=new a; 
$c=$b
echo $b->abc;//这里输出ABC 
echo $c->abc;//这里输出ABC $b->abc="DEF"; 
echo $c->abc;//这里输出DEF
?>

       以上代码是在PHP5中的运行效果 在PHP5中 对象的复制是通过引用来实现的。上列中$b=new a; $c=$b; 其实等效于$b=new a; $c=&$b; PHP5中默认就是通过引用来调用对象, 但有时你可能想建立一个对象的副本,并希望原来的对象的改变不影响到副本 . 为了这样的目的,PHP定义了一个特殊的方法,称为__clone.

引用的作用 

       如果程序比较大,引用同一个对象的变量比较多,并且希望用完该对象后手工清除它,个人建议用 "&" 方式,然后用$var=null的方式清除. 其它时候还是用php5的默认方式吧. 另外, php5中对于大数组的传递,建议用 "&" 方式, 毕竟节省内存空间使用。

取消引用 当你 unset 一个引用,只是断开了变量名和变量内容之间的绑定。这并不意味着变量内容被销毁了。例如:

<?php $a = 1; $b =& $a; unset ($a); ?>

不会 unset $b,只是 $a。 function quoteTest(){ global $var ; //相当于 $var = &$GLOBALS['var']; unset($var); //删除只是删除引用,而引用的内容还存在,同上这并不意味着变量内容被销毁了}$var=1;quoteTest();echo $var; //  结果 1

———————————————————————————————-

不会 unset $b,只是 $a。

function quoteTest(){ global $var ; //相当于 $var = &$GLOBALS['var']; $var = 5; //因为他们都指向 同一内存内容}$var=1;quoteTest();echo $var; //结果 5———————————————————————————————-

'&' 这就是引用

23111813-8cf28c728bcd4002b1bbee0ef6b99d4a.jpg

global 引用 当用 global $var 声明一个变量时实际上建立了一个到全局变量的引用。也就是说和这样做是相同的:

<?php $var =& $GLOBALS["var"]; ?>

这意味着,例如,unset $var 不会 unset 全局变量。

$this 在一个对象的方法中,$this 永远是调用它的对象的引用。

//下面再来个小插曲 php中对于地址的指向(类似指针)功能不是由用户自己来实现的,是由Zend核心实现的,php中引用采用的是“写时拷贝”的原理,就是除非发生写操作,指向同一个地址的变量或者对象是不会被拷贝的。

通俗的讲 1:如果有下面的代码 [php] $a="ABC"; $b=$a; [/php] 其实此时 $a与$b都是指向同一内存地址 而并不是$a与$b占用不同的内存

2:如果在上面的代码基础上再加上如下代码 [php] $a="EFG"; [/php] 由于$a与$b所指向的内存的数据要重新写一次了,此时Zend核心会自动判断 自动为$b生产一个$a的数据拷贝,重新申请一块内存进行存储

23111848-f447888b753a40748f27c64c222c8ea9.jpg

PHP __call拦截器 实现委托

__call方法可能是最有用的拦截器方法。

当客户端代码要调用类中未定义的方法时,__call会被调用。

__call()接受2个参数,一个是方法的名称,另一个是传递给要调用方法的所有参数(数组)。

__call()方法返回的任何值都会返回给客户,就好像调用一个真实存在的方法一样。

__call()方法对于实现委托也很有用。委托是指一个对象转发或者委托一个请求给另一个对象,被委托的一方替原先对象处理请求。

这类似于继承,和在子类中调用父类的方法有点相似。

但再继承时,父类与子类的关系是固定的,而使用委托则可以再代码运行时改变使用的对象,这意味着委托比继承具有更大的灵活性。

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
//将Person类信息格式化并输出
class personWriter{
    function writeName(Person $p){
        print $p->getName();
    }
 
    function writeAge(Person $p){
            print $p->getAge();
    }
 
}
 
 
//当然我们可以通过集成PersonWrite类以不同的方式输出Person类的信息。
//下面的的代码结合使用__call()方法和PersonWriter对象来实现Person类:
class Person{
    private $writer;
 
    function __construct(PersonWriter $write){
        $this->write = $write;
    }
 
    function __call($methodname$args){
        if(method_exists($this->writer, $methodname)){
            return $this->writer->$methodname($this);
        }
    }
 
    function getName(){ return "LeoKim"; };
    function getAge(){ return 31; }
}

代码中Person类接受一个PersonWriter对象作为构造方法的参数,并将它存储再属性变量$writer中。

在__call()方法中,我们使用参数$methodname,检查PersonWriter对象中是否存在同名的方法。

如果相应的方法存在,我们就委托PersonWriter对象来处理对方法的调用,把当前类(Person)的实例作为参数传递给PersonWriter对象(使用$this伪变量)。

因此,可以这样调用Person类:

1
2
$person new Person(new PersonWriter());
$person->writeName();

PHP 闭包

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<?php
/** 
 * 下面提到的代码在PHP5.3以上版本运行通过. 
 */  
function callback($callback) {  
    $callback();  
}  
 
//输出: This is a anonymous function.<br />/n  
//这里是直接定义一个匿名函数进行传递, 在以往的版本中, 这是不可用的.  
//现在, 这种语法非常舒服, 和JavaScript语法基本一致, 之所以说基本呢, 需要继续向下看  
//结论: 一个舒服的语法必然会受欢迎的.  
callback(function() {  
    print "This is a anonymous function.<br />/n";  
});  
 
 
 
//输出: This is a closure use string value, msg is: Hello, everyone.<br />/n  
//这里首先定义了一个闭包, 这次户口本上有名字了...  
//use, 一个新鲜的家伙...  
//众所周知, 闭包: 内部函数使用了外部函数中定义的变量.  
//在PHP新开放的闭包语法中, 我们就是用use来使用闭包外部定义的变量的.  
//这里我们使用了外部变量$msg, 定义完之后, 又对其值进行了改变, 闭包被执行后输出的是原始值  
//结论: 以传值方式传递的基础类型参数, 闭包use的值在闭包创建是就确定了.  
$msg "Hello, everyone";  
$callback function () use ($msg) {  
    print "This is a closure use string value, msg is: $msg. <br />/n";  
};  
$msg "Hello, everybody";  
callback($callback);  
 
 
//输出: This is a closure use string value lazy bind, msg is: Hello, everybody.<br />/n  
//换一种引用方式, 我们使用引用的方式来use  
//可以发现这次输出是闭包定义后的值...  
//这个其实不难理解, 我们以引用方式use, 那闭包use的是$msg这个变量的地址  
//当后面对$msg这个地址上的值进行了改变之后, 闭包内再输出这个地址的值时, 自然改变了.  
$msg "Hello, everyone";  
$callback function () use (&$msg) {  
    print "This is a closure use string value lazy bind, msg is: $msg. <br />/n";  
};  
$msg "Hello, everybody";  
callback($callback);  
 
 
//输出: This is a closure use object, msg is: Hello, everyone.<br />/n  
//闭包中输出的是之前被拷贝的值为Hello, everyone的对象, 后面是对$obj这个名字的一个重新赋值.  
//可以这样考虑  
//1. obj是对象Hello, everyone的名字  
//2. 对象Hello, everyone被闭包use, 闭包产生了一个对Hello, everyone对象的引用  
//3. obj被修改为Hello, everybody这个对象的名字  
//4. 注意, 是名字obj代表的实体变了, 而不是Hello, everyone对象, 那自然闭包的输出还是前面的Hello, everyone  
$obj = (object) "Hello, everyone";  
$callback function () use ($obj) {  
    print "This is a closure use object, msg is: {$obj->scalar}. <br />/n";  
};  
$obj = (object) "Hello, everybody";  
callback($callback);  
 
 
//输出: This is a closure use object, msg is: Hello, everybody.<br />/n  
//还是按照上面的步骤, 按部就班的来吧:  
//1. obj名字指向Hello, everyone对象  
//2. 闭包产生一个引用指向Hello, everyone对象  
//3. 修改obj名字指向的对象(即Hello, everyone对象)的scalar值  
//4. 执行闭包, 输出的自然是Hello, everybody, 因为其实只有一个真正的对象  
$obj = (object) "Hello, everyone";  
$callback function () use ($obj) {  
    print "This is a closure use object, msg is: {$obj->scalar}. <br />/n";  
};  
$obj->scalar = "Hello, everybody";  
callback($callback);  
 
 
//输出: This is a closure use object lazy bind, msg is: Hello, everybody.<br />/n  
//闭包引用的是什么呢? &$obj, 闭包产生的引用指向$obj这个名字所指向的地址.  
//因此, 无论obj怎么变化, 都是逃不脱的....  
//所以, 输出的就是改变后的值  
$obj = (object) "Hello, everyone";  
$callback function () use (&$obj) {  
    print "This is a closure use object lazy bind, msg is: {$obj->scalar}. <br />/n";  
};  
$obj = (object) "Hello, everybody";  
callback($callback);  
 
/** 
 * 一个利用闭包的计数器产生器 
 * 这里其实借鉴的是Python中介绍闭包时的例子... 
 * 我们可以这样考虑: 
 *      1. counter函数每次调用, 创建一个局部变量$counter, 初始化为1. 
 *      2. 然后创建一个闭包, 闭包产生了对局部变量$counter的引用. 
 *      3. 函数counter返回创建的闭包, 并销毁局部变量, 但此时有闭包对$counter的引用,  
 *          它并不会被回收, 因此, 我们可以这样理解, 被函数counter返回的闭包, 携带了一个游离态的 
 *          变量. 
 *      4. 由于每次调用counter都会创建独立的$counter和闭包, 因此返回的闭包相互之间是独立的. 
 *      5. 执行被返回的闭包, 对其携带的游离态变量自增并返回, 得到的就是一个计数器. 
 * 结论: 此函数可以用来生成相互独立的计数器. 
 */  
function counter() {  
    $counter = 1;  
    return function() use(&$counter) {return $counter ++;};  
}  
$counter1 = counter();  
$counter2 = counter();  
echo "counter1: " $counter1() . "<br />/n";  
echo "counter1: " $counter1() . "<br />/n";  
echo "counter1: " $counter1() . "<br />/n";  
echo "counter1: " $counter1() . "<br />/n";  
echo "counter2: " $counter2() . "<br />/n";  
echo "counter2: " $counter2() . "<br />/n";  
echo "counter2: " $counter2() . "<br />/n";  
echo "counter2: " $counter2() . "<br />/n";  
?>

php匿名函数和闭包

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
67
68
69
70
71
72
73
74
75
76
77
<?php
 
class Product{
    public $name;
    public $price;
 
    function __construct($name$price){
        $this->name = $name;
        $this->price = $price;
    }
}
 
class ProcessSale{
    private $callbacks;
 
    function registerCallback($callback){
        if(!is_callable($callback)){
            throw new Exception("allback not callable");
        }
 
        $this->callbacks[] = $callback;
    }  
 
    function sale($product){
        print "{$product->name}:processing \n";
        foreach ($this->callbacks as $callback) {
            call_user_func($callback$product);
        }
    }
}
 
class Mailer{
    function doMail($product){
        print "mailing({$product->name})<br/>";
    }
}
 
class Totalizer{
    static function warnAmount($amt){
        $count = 0;
        // return function ($product){
        //     if($product->price > 5){
        //         print "reached high price: {$product->price}<br />";
        //     }
        // };
        return function ($productuse ($amt, &$count){
            $count += $product->price;
            print "count: $count <br />";
            if($count $amt){
                print "high price reached:{$count} <br>";
            }
        };
    }
}
 
 
 
 
 
// $logger = create_function('$product',
//                           'print "logging({$product->name})\n";' );
 
// $logger2 = function($product){
//     print "logging ({$product->name})<br/>";
// };
 
$processor new ProcessSale();
// $processor->registerCallback($logger2);
// $processor->registerCallback(array( new Mailer(), "doMail"));
$processor->registerCallback(Totalizer::warnAmount(8));
 
$processor->sale( new Product("shose", 6));
print "<br>";
$processor->sale( new Product("coffee", 6));
 
 
?>

PHP Static延迟静态绑定,后期静态绑定

这个文章看着表述也是不太明确的

我看到现在只是觉得如果不用延迟静态绑定 那么子类没有重写的function 会调用父类内的“资源”

如果用了延迟静态绑定 那么这个执行结果也就是子类应该有的正确的结果了

可能我表述的也不是那么通俗 233333333

==============================================================

本文实例讲述了PHP Static延迟静态绑定用法。分享给大家供大家参考,具体如下:

PHP5.3以后引入了延迟静态绑定static,它是为了解决什么问题呢?php的继承模型中有一个存在已久的问题,那就是在父类中引用扩展类的最终状态比较困难。来看一个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class 
  public static function echoClass(){ 
    echo __CLASS__
  }
  public static function test(){ 
    self::echoClass();    
  }
}
class extends 
{    
  public static function echoClass() 
  
     echo __CLASS__
  
B::test(); //输出A

在PHP5.3中加入了一个新特性:延迟静态绑定,就是把本来在定义阶段固定下来的表达式或变量,改在执行阶段才决定,比如当一个子类继承了父类的静态表达式的时候,它的值并不能被改变,有时不希望看到这种情况。

下面的例子解决了上面提出的问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class 
  public static function echoClass(){ 
    echo __CLASS__
  
  public static function test() 
  
    static::echoClass();    
  
class extends 
{    
  public static function echoClass(){ 
     echo __CLASS__
  
B::test(); //输出B

第8行的static::echoClass();定义了一个静态延迟绑定方法,直到B调用test的时候才执行原本定义的时候执行的方法。

后期静态绑定的用法

http://php.net/manual/zh/language.oop5.late-static-bindings.php

PHP 设计模式 – 静态方法不用实例化调用

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
<?php
class ShopProduct {
    private $title;
    private $producerMainName;
    private $producerFirstName;
    protected $price;
    private $discount = 0; 
    private $id = 0;
     
    public function __construct(   $title$firstName
                            $mainName$price ) { 
        $this->title             = $title;
        $this->producerFirstName = $firstName;
        $this->producerMainName  = $mainName;
        $this->price             = $price;
    }
 
    public function setID( $id ) {
        $this->id = $id;
    }
 
    public function getProducerFirstName() {
        return $this->producerFirstName;
    }
 
    public function getProducerMainName() {
        return $this->producerMainName;
    }
 
    public function setDiscount( $num ) {
        $this->discount=$num;
    }
 
    public function getDiscount() {
        return $this->discount;
    }
     
    public function getTitle() {
        return $this->title;
    }
 
    public function getPrice() {
        return ($this->price - $this->discount);
    }
 
    public function getProducer() {
        return "{$this->producerFirstName}".
               " {$this->producerMainName}";
    }
 
    function getSummaryLine() {
        $base  "$this->title ( $this->producerMainName, ";
        $base .= "$this->producerFirstName )"
        return $base;
    }
 
    public static function getInstance( $id, PDO $pdo ) {
        $query "select * from products where id='$id'";
        $stmt $pdo->prepare("select * from products where id=?");
        $result $stmt->execute( array$id ) );
        $row $stmt->fetch( );
        if empty$row ) ) { return null; }
 
        if $row['type'] == "book" ) {
            $product new BookProduct( 
                                    $row['title'], 
                                    $row['firstname'], $row['mainname'], 
                                    $row['price'], $row['numpages'] ); 
        else if $row['type'] == "cd" ) {
            $product new CdProduct(
                                    $row['title'], 
                                    $row['firstname'], $row['mainname'], 
                                    $row['price'], $row['playlength'] ); 
        else {
            $product new ShopProduct(     
                                    $row['title'], 
                                    $row['firstname'], $row['mainname'], 
                                    $row['price'] ); 
        }
        $product->setId(            $row['id'] );
        $product->setDiscount(      $row['discount'] );
        return $product;
    }
}
 
class CdProduct extends ShopProduct {
    private $playLength = 0;
 
    public function __construct(   $title$firstName
                            $mainName$price$playLength ) { 
        parent::__construct(    $title$firstName
                                $mainName$price );
        $this->playLength = $playLength;
    }
 
    public function getPlayLength() {
        return $this->playLength;
    }
 
    function getSummaryLine() {
        $base = parent::getSummaryLine();
        $base .= ": playing time - $this->playLength";
        return $base;
    }
  
}
 
class BookProduct extends ShopProduct {
    private $numPages = 0;
 
    public function __construct(   $title$firstName
                            $mainName$price$numPages ) { 
        parent::__construct(    $title$firstName
                                $mainName$price );
        $this->numPages = $numPages;
    }
 
    public function getNumberOfPages() {
        return $this->numPages;
    }
    
    function getSummaryLine() {
        $base = parent::getSummaryLine();
        $base .= ": page count - $this->numPages";
        return $base;
    }
 
    public function getPrice() {
        return $this->price;
    }
}
 
require_once("generate_product_pdo.php");
$pdo = getPDO();
$obj = ShopProduct::getInstance( 1, $pdo );
print_r( $obj );
$obj = ShopProduct::getInstance( 2, $pdo );
print_r( $obj );
$obj = ShopProduct::getInstance( 3, $pdo );
print_r( $obj );
?>

这个方法再类中会比在对象中更有用。

我们可以轻松地将原始数据转换为一个对象,而不需要一开始就使用ShopProduct对象。

这个方法并没有使用任何实例属性或方法吗所以没有理由不把他定义为static。

只要有一个有效的PDO对象,我们就可以再程序的任何地方(应该要先把class include进来吧)调用这个方法:

1
2
3
4
$dsn "xxxxxxxxxxxxx";
$pdo new PDO($dns,null,null);
$pdo->getAttribute(xxxxxxxxxxxxxxxxx, xxxxxxxxxxxxx);
$bjg = ShopProduct::getInstance(1, $pdo);

如果这个类的父类已经有了数据库连接的实例应该不用传这个pdo实例就可以直接返回数据了。