面向對象程序設計是一種程序設計範型,同時也是一種程序開發方法。它將對象做爲程序的基本單元,將程序和數據封裝其中,以提升軟件的重用性,靈活性和可擴展性。 php
{函數式編程 如LISP , mysql
命令式編程 { 面向過程, 面向對象} sql
} 數據庫
類是對象的抽象組織,對象是類的具體存在。 編程
class person{ public $name; public $gender; public function say(){ echo $this->name," is ",$this->gender; } } $student=new person(); $student->name="Tom"; $student->gender='male'; $student->say(); $teacher=new person(); $teacher->name='Kate'; $teacher->gender='female'; $teacher->say(); print_r((array)$student); var_dump($student); $str=serialize($student); echo $str; file_put_contents('store.txt',$str); $str=file_get_contents('store.txt'); $student=unserialize($str); $student->say();
所謂序列化,就是把保存在內存中的各類對象狀態(屬性)保存起來,而且在須要時能夠還原出來。 數組
對象序列化後,存儲的只是只是對象的屬性。
對象就是數據,對象自己不包含方法。可是對象有一個「指針」指向一個類,這個類裏能夠有方法。 dom
序列化和反序列化時都須要包含類的對象的定義,不然可能返回不正確的結果。 ide
#zend/zend.h typedef union_zvalue_value{ long lval; double dval; struct{ char *val; int len; } str; HashTable *ht; zend_object_value obj; } zvalue_value; #zend/zend.h typedef struct_zend_object{ zend_class_entry *ce; HashTable *properties; HashTable *guards; } zend_object;
對象是一種很普通的變量,不一樣的是其攜帶了對象的屬性和類的入口。 函數
$student_arr=array('name'=>'Tom','gender'=>'male'); echo "\n"; echo serialize($student_arr);
對象和數組的區別在於:對象還有個指針,指向了它所屬的類。 this
class person{ public $name; public $gender; public function say(){ echo $this->name,"\tis ",$this->gender,"\r\n"; } } class family{ public $people; public $location; public function __construct($p,$loc){ $this->people=$p; $this->location=$loc; } } $student=new person(); $student->name='Tom'; $student->gender='male'; $student->say(); $tom=new family($student,'peking'); echo serialize($student); $student_arr=array('name'=>'Tom','gender'=>'male'); echo "\n"; echo serialize($student_arr); print_r($tom); echo "\n"; echo serialize($tom);
當一個對象的實例變量應用其餘對象時,序列化該對象時也會對引用對象進行序列化。
語法糖:指那些沒有給計算機語言添加新功能,而只是對人類來講更「甜蜜」的語法。 如魔術方法的寫法。
class Account{ private $user=1; private $pwd=2; public function __set($name,$value){ echo "Setting $name to $value \r\n"; $this->$name=$value; } public function __get($name){ if(!isset($this->$name)){ echo '未設置'; $this->$name='正在爲你設置默認值'; } return $this->$name; } } $a=new Account(); echo $a->user; $a->name=5; echo $a->name; echo $a->big;
class Account{ 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 '參數不對',PHP_EOL; break; } } } $a=new Account(); echo $a->make(5); echo $a->make(5,6); echo $a->make(5,6,7);
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]='ATODO: 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 Sale extends ActiveRecord{ protected static $table='salesdb'; protected static $fields=array( 'id'=>'int', 'item'=>'varchar', 'qty'=>'int' ); } echo Customer::findById(111)->select; echo Customer::findById(111)->email; echo Customer::findByLastname('liu')->select;
只有實現了__toString方法才能直接echo 對象。
class Account{ public $user=1; public $pwd=2; public function __toString(){ return "當前對象的用戶是{$this->user},密碼是{$this->pwd}"; } } $a=new Account(); echo $a; echo PHP_EOL; print_r($a);
繼承是類級別的複用,多態是方法級別的複用。
class person{ public $name='Tom'; public $gender; static $money=10000; public function __construct(){ echo '這裏是父類',PHP_EOL; } public function say(){ echo $this->name,"\t is ",$this->gender,"\r\n"; } } class family extends person{ public $name; public $gender; public $age; static $money=100000; public function __construct(){ parent::__construct(); echo '這裏是子類',PHP_EOL; } public function say(){ parent::say(); echo $this->name,"\t is \t",$this->gender,", and is \t",$this->age,PHP_EOL; } public function cry(){ echo parent::$money,PHP_EOL; echo '%>_<%',PHP_EOL; echo self::$money,PHP_EOL; echo '(*^-^*)'; } } $poor=new family(); $poor->name='Lee'; $poor->gender='female'; $poor->age=25; $poor->say(); $poor->cry();
從這個例子能夠看出 父類直接共享了子類的成員值。
在開發時,設置最嚴格的報錯等級,在部署時可適當下降。
低耦合指模塊與模塊之間,儘量地使模塊間獨立存在;模塊與模塊之間的接口儘可能少而簡單。
解耦是要解除模塊與模塊之間的依賴。
繼承和組合都可的狀況下,傾向用組合。
繼承破壞封裝性。繼承是緊耦合的。繼承擴展複雜。不恰當地使用繼承可能違反現實世界中的邏輯。
底層代碼多用組合,頂層、業務層代碼多用繼承。底層用組合能夠提升效率,避免對象臃腫。頂層代碼用繼承能夠提升靈活性,讓業務更方便。
多重繼承:一個類能夠同時繼承多個父類,組合兩個父類的功能。
PHP中的Traits既可使單繼承模式的語言得到剁成繼承的靈活,有能夠避免多重繼承帶來的種種問題。
class car{ public function addoil(){ echo "Sdd oil \r\n" ; } } class bmw extends car{ } class benz{ public $car; public function __construct(){ $this->car=new car(); } public function addoil(){ $this->car->addoil(); } } $bmw=new bmw(); $bmw->addoil(); $benz=new benz(); $benz->addoil();
重載不是面向對象裏的東西,它數據域多態的一種表現形式。
多態性是一種經過多種狀態或者階段描述相同對象的編程方式。實際開發中,只要關心一個接口或者基類的編程,而沒必要關心一個對象所屬於的具體類。
class employee{ protected function working(){ echo '本方法須要重載才能運行'; } } class teacher extends employee{ public function working(){ echo '教書'; } } class coder extends employee{ public function working(){ echo '敲代碼'; } } function doprint($obj){ if(get_class($obj)=='employee'){ echo 'Error'; }else{ $obj->working(); } } doprint(new teacher()); doprint(new coder()); doprint(new employee());
interface employee{ public function working(); } class teacher implements employee{ public function working(){ echo '教書'; } } class coder implements employee{ public function working(){ echo '敲代碼'; } } function doprint(employee $i){ $i->working(); } $a=new teacher(); $b=new coder(); doprint($a); doprint($b);
接口中全部的方法都是抽象的,沒有程序體。
接口的方法必須被所有實現,不然將報錯。
interface mobile{ public function run(); } class plain implements mobile { public function run(){ echo "我是飛機"; } public function fly(){ echo "飛行"; } } class car implements mobile{ public function run(){ echo "我是汽車"; } } class machine{ function demo(mobile $a){ $a->fly(); } } $obj=new machine(); $obj->demo(new plain()); $obj->demo(new car());
trait Hello{ public function sayHello(){ echo 'Hello '; } } trait World{ public function sayWorld(){ echo 'World'; } } class MyHelloWorld{ use Hello,World; public function sayExcalamationMark(){ echo '!'; } } $o=new MyHelloWorld(); $o->sayHello(); $o->sayWorld(); $o->sayExcalamationMark();
PHP中的接口沒有契約限制,缺乏足夠多的內部接口。
反射是指在PHP運行狀態中,擴展分析PHP程序,導出或提取出關於類,方法,屬性等詳細信息,包括註釋。
這種動態獲取信息以及動態調用對象方法的功能成爲反射API。
class person{ public $name; public $gender; public function say(){ echo $this->name,"t is ",$this>gender,"\r\n"; } public function __set($name,$value){ echo "Setting $name to $value \r\n"; $this->$name=$value; } public function __get($name){ if(!isset($this->$name)){ echo '未設置'; $this->$name="正在爲你設置默認值"; } return $this->$name; } } $student=new person(); $student->name='Tom'; $student->gender='male'; $student->age=24;
$reflect=new ReflectionObject($student); $props=$reflect->getProperties(); foreach($props as $prop){ print $prop->getName()."\n"; } $m=$reflect->getMethods(); foreach($m as $prop){ print $prop->getName()."\n"; }
使用class函數,返回對象屬性的關聯數組以及更多的信息
//返回對象屬性的關聯數組 var_dump(get_object_vars($student)); //類屬性 var_dump(get_class_vars(get_class($student))); //返回由類的方法名組成的數組 var_dump(get_class_methods(get_class($student))); //獲取對象屬性列表所屬的類 echo get_class($student);
//反射獲取類的原型 $obj=new ReflectionClass('person'); $className=$obj->getName(); $Methods=$Properties=array(); foreach($obj->getProperties() as $v){ $Properties[$v->getName()]=$v; } foreach($obj->getMethods() as $v){ $Methods[$v->getName()]=$v; } echo "class {$className} \n {\n"; is_array($Properties)&&ksort($Properties); foreach($Properties as $k=>$v){ echo "\t"; echo $v->isPublic()?'Public':'', $v->isPrivate()?'private':'', $v->isProtected()?'protected':'', $v->isStatic()?' static':''; echo "\t{$k}\n"; } echo "\n"; if(is_array($Methods)) ksort($Methods); foreach($Methods as $k=>$v){ echo "\tfunction{$k}(){}\n"; } echo "}\n";
反射能夠用於文檔生成,作hook實現插件功能,作動態代理。
class mysql{ function connect($db){ echo "鏈接到數據庫${db[0]} \r\r"; } } class sqlproxy{ private $target; function __construct($tar){ $this->target[]=new $tar(); } function __call($name,$args){ foreach($this->target as $obj){ $r=new ReflectionClass($obj); if($method=$r->getMethod($name)){ if($method->isPublic()&& !$method->isAbstract()){ echo "方法前攔截記錄Log\r\n"; $method->invoke($obj,$args); echo "方法後攔截\r\n"; } } } } } $obj=new sqlproxy('mysql'); $obj->connect('member');
PHP把許多異常看做錯誤。
$a=null; try{ $a=5/0; echo $a,PHP_EOL; }catch(exception $e){ $e->getMessage(); $a=-1; } echo $a;
PHP一般是沒法自動捕獲有意義的異常,它把全部不正確的狀況都視做錯誤,你要想捕獲這個異常,就得使用if else 結構,保證代碼是正常的,而後判斷若是xxx,則手工拋出異常,再捕獲。
class emailException extends exception{ } class pwdException extends exception{ function __toString(){ return "Exception{$this->getCode()}:{$this->getMessage()} in File:{$this->getFile()} on line:{$this->getLine()}"; } } function reg($reginfo=null){ if(empty($reginfo) || !isset($reginfo)){ throw new Exception("參數非法"); } if(empty($reginfo['email'])){ throw new emailException("郵件爲空"); } if($reginfo['pwd']!=$reginfo['repwd']){ throw new pwdException("兩次密碼不一致"); } echo '註冊成功'; }
try{ reg(array('email'=>'bcc@126.com','pwd'=>'abc','repwd'=>'12342')); }catch(emailException $ee){ echo $ee->getMessage(); }catch(pwdException $ep){ echo $ep; echo PHP_EOL,'特殊處理'; }catch(Exception $e){ echo $e->getTraceAsString(); echo PHP_EOL,'其餘狀況,統一處理'; }
注意:exception做爲超類應該放在最後捕獲,否則捕獲這個異常超類後,後面的捕獲就終止了。
如下三種情景下會用到異常處理機制。
1 對程序的悲觀預測
2 程序的須要和對業務的關注
異常偏重於保護業務數據一致性,並強調對異常業務的處理。
3 語言級別的健壯性要求
能夠把異常形成的邏輯中斷破壞下降到最小範圍內,並通過補救處理後不影響業務邏輯的完整性;亂拋異常和只拋不捕獲,或捕獲而不補救,會致使數據混亂。
$date='2012-12-20'; if(ereg("([0-9]{4}-([0-9]{1,2})-([0-9]{1,2})",$date,$regs)){ echo "$regs[3].$regs[2].$regs[1]"; }else{ echo "Invalid date format:$date"; } if($i>5){ echo '$i 沒有初始化啊',PHP_EOL; } $a=array('o'=>2,3,6,8); echo $a[o]; $result=array_sum($a,3); echo fun(); echo '致命錯誤後呢,還會執行嗎?'
function customError($errno,$errstr,$errfile,$errline){ echo "<b>錯誤代碼:</b>[${erron}] ${errstr}\r\n"; echo "錯誤所在的代碼行:{$errline} 文件{$errfile}\r\n"; echo " PHP 版本 ",PHP_VERSION,"(",PHP_OS,")\r\n"; } set_error_handler("customError",E_ALL|E_STRICT); $a=array('o'=>2,3,45,6); echo $a[o];
若是使用自定義的set_error_handler接管PHP的錯誤處理,@將失效,這種錯誤也會被顯示。
能夠把「異常」像錯誤同樣使用set_error_handler接管,進而主動拋出異常,來捕獲異常和非致命的錯誤。
function customError($erron,$errstr,$errfile,$errline){ //自定義錯誤處理時,手動拋出異常 throw new Exception($erron.'|'.$errstr); } set_error_handler("customError",E_ALL|E_STRICT); try{ $a=5/0;} catch(Exception $e){ echo '錯誤信息:',$e->getMessage(); }
錯誤拋出
$divisor=0; if($divisor==0){ trigger_error("Can not divide by zero",E_USER_ERROR); } echo 'break';