强伦轩人妻一区二区三区四区,www久久久久久久久久久久久久久久久,《诱人的奶头》电影,日本色妇色视频

分享到:

魔術(shù)方法的應(yīng)用

日期:2016-10-25 20:45:00     閱讀:403     文章來源:源美網(wǎng)絡(luò)     標(biāo)簽:深圳網(wǎng)頁設(shè)計(jì),魔術(shù)方法

魔術(shù)方法是以兩個(gè)下畫線“_”開頭、具有特殊作用的一些方法,可以看做PHP的“語法糖”。

語法糖指那些沒有給計(jì)算機(jī)語言添加新功能,而只是對(duì)人類來說更“甜蜜”的語法。語法糖往往給程序員提供了更實(shí)用的編碼方式或者一些技巧性的用法,有益于更好的編碼風(fēng)格,使代碼更易讀。不過其并沒有給語言添加什么新東西。PHP里的引用、SPL等都屬于語法糖。

實(shí)際上,在1.1節(jié)代碼中就涉及魔術(shù)方法的使用。family類中的_construct方法就是一個(gè)標(biāo)準(zhǔn)魔術(shù)方法。這個(gè)魔術(shù)方法又稱構(gòu)造方法。具有構(gòu)造方法的類會(huì)在每次創(chuàng)建對(duì)象時(shí)先調(diào)用此方法,所以非常適合在使用對(duì)象之前做一些初始化工作。因此,這個(gè)方法往往用于類進(jìn)行初始化時(shí)執(zhí)行一些初始化操作,如給屬性賦值、連接數(shù)據(jù)庫等。

以代碼清單1-1所示代碼為例,family中的_construct方法主要做的事情就是在創(chuàng)建對(duì)象的同時(shí)對(duì)屬性賦值。也可以這么使用:

$tom=new family($student,'peking');

$tom->people->say(); 

這樣做就不需要在創(chuàng)建對(duì)象后再去賦值了。有構(gòu)造方法就有對(duì)應(yīng)的析構(gòu)方法,即_destruct方法,析構(gòu)方法會(huì)在某個(gè)對(duì)象的所有引用都被刪除,或者當(dāng)對(duì)象被顯式銷毀時(shí)執(zhí)行。這兩個(gè)方法是常見也是最有用的魔術(shù)方法。


_set和_get方法

_set和_get是兩個(gè)比較重要的魔術(shù)方法,如代碼清單所示。

代碼清單 magic.php

<?php

class Account{

private$user=1;

private$pwd=2;

}

$a=new Account();

echo$a->user;

$a->name=5;

echo$a->name;

echo$a->big;

運(yùn)行這段代碼會(huì)怎樣呢?結(jié)果報(bào)錯(cuò)如下:

Fatal error:Cannot access private property Account::$user in G:\bak\temp\tempcode\_sg.php on line 7 

所報(bào)錯(cuò)誤大致是說,不能訪問Account對(duì)象的私有屬性u(píng)ser。在代碼清單類定義里增加以下代碼,其中使用了_set魔術(shù)方法。

public function _set($name,$value){

echo "Setting$name to$value\r\n";

$this->$name=$value;

}

public function _get($name){

if(!isset($this->$name)){

echo'未設(shè)置';

$this->$name="正在為你設(shè)置默認(rèn)值";

}

return$this->$name;

再次運(yùn)行,看到正常輸出,沒有報(bào)錯(cuò)。在類里以兩個(gè)下畫線開頭的方法都屬于魔術(shù)方法(除非是你自定義的),它們是PHP中的內(nèi)置方法,有特殊含義。手冊(cè)里把這兩個(gè)方法歸到重載。

PHP的重載和Java等語言的重載不同。Java里,重載指一個(gè)類中可以定義參數(shù)列表不同但名字相同的多個(gè)方法。比如,Java也有構(gòu)造函數(shù),Java允許有多個(gè)構(gòu)造函數(shù),只要保證方法簽名不一樣就行;而PHP則在一個(gè)類中只允許有一個(gè)構(gòu)造函數(shù)。

PHP提供的“重載”指動(dòng)態(tài)地“創(chuàng)建”類屬性和方法。因此,_set和_get方法被歸到重載里。

這里可以直觀看到,若類中定義了_set和_get這一對(duì)魔術(shù)方法,那么當(dāng)給對(duì)象屬性賦值或者取值時(shí),即使這個(gè)屬性不存在,也不會(huì)報(bào)錯(cuò),一定程度上增強(qiáng)了程序的健壯性。

我們注意到,在account類里,user屬性的訪問權(quán)限是私有的,私有屬性意味著這個(gè)屬性是類的“私有財(cái)產(chǎn)”,只能在類內(nèi)部對(duì)其進(jìn)行操作。如果沒有_set這個(gè)魔術(shù)方法,直接在類的外部對(duì)屬性進(jìn)行賦值操作是會(huì)報(bào)錯(cuò)的,只能通過在類中定義一個(gè)public的方法,然后在類外調(diào)用這個(gè)公開的方法進(jìn)行屬性讀寫操作。

現(xiàn)在有了這兩個(gè)魔術(shù)方法,是不是對(duì)私有屬性的操作變得更方便了呢?實(shí)際上,并沒有什么奇怪的,因?yàn)檫@兩個(gè)方法本身就是public的。它們和在對(duì)外的public方法中操作private屬性的原理一樣。只不過這對(duì)魔術(shù)方法使其操作更簡(jiǎn)單,不需要顯式地調(diào)用一個(gè)public的方法,因?yàn)檫@對(duì)魔術(shù)方法在操作類變量時(shí)是自動(dòng)調(diào)用的。當(dāng)然,也可以把類屬性定義成public的,這樣就可以隨意在類的外部進(jìn)行讀寫。不過,如果只是為了方便,類屬性在任意時(shí)候都定義成public權(quán)限顯然是不合適的,也不符合面向?qū)ο蟮脑O(shè)計(jì)思想。


_call和_callStatic方法

如何防止調(diào)用不存在的方法而出錯(cuò)?一樣的道理,使用_call魔術(shù)重載方法。

_call方法原型如下:

mixed _call(string$name,array$arguments) 

當(dāng)調(diào)用一個(gè)不可訪問的方法(如未定義,或者不可見)時(shí),_call()會(huì)被調(diào)用。其中$name參數(shù)是要調(diào)用的方法名稱。$arguments參數(shù)是一個(gè)數(shù)組,包含著要傳遞給方法的參數(shù),如下所示:

public function _call($name,$arguments){

switch(count($arguments)){

case 2:

echo $arguments[0]*$arguments[1],PHP_EOL;

break;

case 3:

echo array_sum($arguments),PHP_EOL;

break;

default:

echo '參數(shù)不對(duì)',PHP_EOL;

break;

}

}

$a->make(5);

$a->make(5,6); 

以上代碼模擬了類似其他語言中的根據(jù)參數(shù)類型進(jìn)行重載。跟_call方法配套的魔術(shù)方法是_callStatic。當(dāng)然,使用魔術(shù)方法“防止調(diào)用不存在的方法而報(bào)錯(cuò)”,并不是魔術(shù)方法的本意。實(shí)際上,魔術(shù)方法使方法的動(dòng)態(tài)創(chuàng)建變?yōu)榭赡?,這在MVC等框架設(shè)計(jì)中是很有用的語法。假設(shè)一個(gè)控制器調(diào)用了不存在的方法,那么只要定義了_call魔術(shù)方法,就能友好地處理這種情況。

試著理解代碼清單1-3所示代碼。這段代碼通過使用_callStatic這一魔術(shù)方法進(jìn)行方法的動(dòng)態(tài)創(chuàng)建和延遲綁定,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的ORM模型。

代碼清單1-3 simpleOrm.php

<?php

abstract class ActiveRecord{

protected static $table;

protected $fieldvalues;

public $select;

static function findById($id){

$query = "select*from"

.static::$table

."where id=$id";

return self::createDomain($query);

}

function _get($fieldname){

return $this->fieldvalues[$fieldname];

}

static function _callStatic($method,$args){

$field = preg_replace('/^findBy(\w*)$/','${1}',$method);

$query = "select*from"

.static::$table

."where$field = '$args[0]'";

return self::createDomain($query);

}

private static function createDomain($query){

$klass = get_called_class();

$domain = new$klass();

$domain->fieldvalues = array();

$domain->select = $query;

foreach($klass::$fields as$field=>$type){

$domain->fieldvalues[$field]='TODO:set from sql result';

}

return $domain;

}

}

class Customer extends ActiveRecord{

protected static $table='custdb';

protected static $fields=array(

'id'=>'int',

'email'=>'varchar',

'lastname'=>'varchar'

);

}

class Sales extends ActiveRecord{

protected static $table='salesdb';

protected static $fields=array(

'id'=>'int',

'item'=>'varchar',

'qty'=>'int'

);

}

assert("select*from custdb where id=123"==

Customer::findById(123)->select);

assert("TODO:set from sql result"==

Customer::findById(123)->email);

assert("select*from salesdb where id=321"==

Sales::findById(321)->select);

assert("select*from custdb where Lastname='Denoncourt'"==

Customer::findByLastname('Denoncourt')->select); 

再舉個(gè)類似的例子。PHP里有很多字符串函數(shù),假如要先過濾字符串首尾的空格,再求出字符串的長(zhǎng)度,一般會(huì)這么寫:

strlen(trim($str)); 

如果要實(shí)現(xiàn)JS里的鏈?zhǔn)讲僮鳎热缦裣旅孢@樣,應(yīng)該怎么實(shí)現(xiàn)?

很簡(jiǎn)單,先實(shí)現(xiàn)一個(gè)String類,對(duì)這個(gè)類的對(duì)象調(diào)用方法進(jìn)行處理時(shí),觸發(fā)_call魔術(shù)方法,接著執(zhí)行call_user_func即可。


_toString方法

再看另外一個(gè)魔術(shù)方法_TOstring(在這里故意這么寫,是要說明PHP中方法不區(qū)分大小寫,但實(shí)際開發(fā)中還需要注意規(guī)范)。

當(dāng)進(jìn)行測(cè)試時(shí),需要知道是否得出正確的數(shù)據(jù)。比如打印一個(gè)對(duì)象時(shí),看看這個(gè)對(duì)象都有哪些屬性,其值是什么,如果類定義了_toString方法,就能在測(cè)試時(shí),echo打印對(duì)象體,對(duì)象就會(huì)自動(dòng)調(diào)用它所屬類定義的_toString方法,格式化輸出這個(gè)對(duì)象所包含的數(shù)據(jù)。如果沒有這個(gè)方法,那么echo一個(gè)對(duì)象將報(bào)錯(cuò),例如"Catchable fatal error:Object of class Account could not be converted to string"語法錯(cuò)誤,實(shí)際上這是一個(gè)類型匹配失敗錯(cuò)誤。不過仍然可以用print_r()和var_dump()函數(shù)輸出一個(gè)對(duì)象。當(dāng)然,_toString是可以定制的,所提供的信息和樣式更豐富,如代碼清單1-4所示。

代碼清單1-4 magic_2.php

<?php

class Account{

public $user = 1;

private $pwd = 2;

//自定義的格式化輸出方法

public function_toString(){

return"當(dāng)前對(duì)象的用戶名是{$this->user},密碼是{$this->pwd}";

}

}

$a = new Account();

echo$a;

echo PHP_EOL;

print_r($a); 

運(yùn)行這段代碼發(fā)現(xiàn),使用_toString方法后,輸出的結(jié)果是可定制的,更易于理解。實(shí)際上,PHP的_toString魔術(shù)方法的設(shè)計(jì)原型來源于Java。Java中也有這么一個(gè)方法,而且在Java中,這個(gè)方法被大量使用,對(duì)于調(diào)試程序比較方便。實(shí)際上,_toString方法也是一種序列化,我們知道PHP自帶serialize/unserialize也是進(jìn)行序列化的,但是這組函數(shù)序列化時(shí)會(huì)產(chǎn)生一些無用信息,如屬性字符串長(zhǎng)度,造成存儲(chǔ)空間的無謂浪費(fèi)。因此,可以實(shí)現(xiàn)自己的序列化和反序列化方法,或者json_encode/json_decode也是一個(gè)不錯(cuò)的選擇。

為什么直接echo一個(gè)對(duì)象就會(huì)報(bào)語法錯(cuò)誤,而如果這個(gè)對(duì)象實(shí)現(xiàn)_toString方法后就可以直接輸出呢?原因很簡(jiǎn)單,echo本來可以打印一個(gè)對(duì)象,而且也實(shí)現(xiàn)了這個(gè)接口,但是PHP對(duì)其做了個(gè)限制,只有實(shí)現(xiàn)_toString后才允許使用。從下面的PHP源代碼里可以得到驗(yàn)證:

ZEND_VM_HANDLER(40,ZEND_ECHO,CONST|TMP|VAR|CV,ANY)

{

zend_op*opline = EX(opline);

zend_free_op free_op1;

zval z_copy;

zval *z =GET_OP1_ZVAL_PTR(BP_VAR_R);

//此處的代碼預(yù)留了把對(duì)象轉(zhuǎn)換為字符串的接口

if(OP1_TYPE ! = IS_CONST&&

Z_TYPE_P(z) == IS_OBJECT&&Z_OBJ_HT_P(z)->get_method ! = NULL&&

zend_std_cast_object_tostring(z,&z_copy,IS_STRING TSRMLS_CC) == SUCCESS){

zend_print_variable(&z_copy);

zval_dtor(&z_copy);

}else{

zend_print_variable(z);

}

FREE_OP1();

ZEND_VM_NEXT_OPCODE();

由此可見,魔術(shù)方法并不神奇。

有比較才有認(rèn)知。最后,針對(duì)本節(jié)代碼給出一個(gè)Java版本的代碼,供各位讀者用來對(duì)比兩種語言中重載和魔術(shù)方法的異同,如代碼清單1-5所示。

代碼清單1-5 Account.java

import org.apache.commons.lang3.builder.ToStringBuilder;

/**

*類的重載演示Java版本

*@author wfox

*@date@verson

*/

public class Account{

private String user;//用戶名

private String pwd;//密碼

public Account(){

System.out.println("構(gòu)造函數(shù)");

}

public Account(String user,String pwd){

System.out.println("重載構(gòu)造函數(shù)");

System.out.println(user+"---"+pwd);

}

public void say(String user){

System.out.println("用戶是:"+user);

}

public void say(String user,String pwd){

System.out.println("用戶:"+user);

System.out.println("密碼"+pwd);

}

public String getUser(){

return user;

}

public void setUser(String user){

this.user = user;

}

public String getPwd(){

return pwd;

}

public void setPwd(String pwd){

}

@Override

public String toString(){

return ToStringBuilder.reflectionToString(this);

}

public static void main(String……){

Account account = new Account();

account.setUser("張三");

account.setPwd("123456");

account.say("李四");

account.say("王五","123");

System.out.println(account);

}

可以看出,Java的構(gòu)造方法比PHP好用,PHP由于有了_set/_get這一對(duì)魔術(shù)方法,使得動(dòng)態(tài)增加對(duì)象的屬性字段變得很方便,而對(duì)Java來說,要實(shí)現(xiàn)類似的效果,就不得不借助反射API或直接修改編譯后字節(jié)碼的方式來實(shí)現(xiàn)。這體現(xiàn)了動(dòng)態(tài)語言的優(yōu)勢(shì),簡(jiǎn)單、靈活。





文章引用:http://www.lt-ad.com/new/140.html

本站文章為深圳網(wǎng)站建設(shè)·源美網(wǎng)絡(luò)原創(chuàng)策劃,如有版權(quán)糾紛或者違規(guī)問題,請(qǐng)聯(lián)系我們刪除,謝謝!

上一篇: 在網(wǎng)站制作中各種語言中的多態(tài)

下一篇: 網(wǎng)站設(shè)計(jì)中的說服力

返回列表
最新案例
OUR ADVANTAGE WORKS

售后保障

承諾任何問題1小時(shí)內(nèi)解決

數(shù)據(jù)備份

更安全、更高效、更穩(wěn)定

價(jià)格公道精準(zhǔn)

項(xiàng)目經(jīng)理精準(zhǔn)報(bào)價(jià)不弄虛作假

合作無風(fēng)險(xiǎn)

重合同講信譽(yù),無效全額退款