PHP核心技術與最佳實踐 讀書筆記 第一章面向對象思想的核心概念

第一章 面向對象思想的核心概念

面向對象程序設計是一種程序設計範型,同時也是一種程序開發方法。它將對象做爲程序的基本單元,將程序和數據封裝其中,以提升軟件的重用性,靈活性和可擴展性。  php

{函數式編程  如LISP , mysql

命令式編程 { 面向過程, 面向對象} sql

} 數據庫

1.1面向對象的「形」與「本」

類是對象的抽象組織,對象是類的具體存在。 編程


1.1.1對象的「形」

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

1.1.2 對象的「本」

#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;

對象是一種很普通的變量,不一樣的是其攜帶了對象的屬性和類的入口。 函數

1.1.3 對象與數組

$student_arr=array('name'=>'Tom','gender'=>'male');
echo "\n";
echo serialize($student_arr);

對象和數組的區別在於:對象還有個指針,指向了它所屬的類。 this

1.1.4 對象與類

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);

當一個對象的實例變量應用其餘對象時,序列化該對象時也會對引用對象進行序列化。

1.2 魔術方法的應用

語法糖:指那些沒有給計算機語言添加新功能,而只是對人類來講更「甜蜜」的語法。 如魔術方法的寫法。

1.2.1 __set和__get方法

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;



PHP一個類中只容許有一個構造函數。

PHP中的「重載」是指動態地「建立」類屬性和方法。__set和__get方法被歸到重載裏。

定義了__get和__set方法,直接調用私有屬性 和在對外的public方法中操做private屬性 的原理同樣,只不過操做起來更簡單。


1.2.2 __call 和__callStatic方法

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;


1.2.3 __toString 方法

只有實現了__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);


1.3 繼承與多態

繼承是類級別的複用,多態是方法級別的複用。

1.3.1 類的組合與繼承

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();


1.3.2 各類語言中的多態

重載不是面向對象裏的東西,它數據域多態的一種表現形式。

多態性是一種經過多種狀態或者階段描述相同對象的編程方式。實際開發中,只要關心一個接口或者基類的編程,而沒必要關心一個對象所屬於的具體類。

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);


1.4 面向接口編程

1.4.1 接口的做用

接口中全部的方法都是抽象的,沒有程序體。

接口的方法必須被所有實現,不然將報錯。

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());

1.4.2 對PHP接口的思考

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中的接口沒有契約限制,缺乏足夠多的內部接口。

1.5 反射

反射是指在PHP運行狀態中,擴展分析PHP程序,導出或提取出關於類,方法,屬性等詳細信息,包括註釋。

這種動態獲取信息以及動態調用對象方法的功能成爲反射API。

1.5.1 如何使用反射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";

1.5.2 反射有什麼做用

反射能夠用於文檔生成,作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');

1.6 異常和錯誤處理

PHP把許多異常看做錯誤。

1.6.1 如何使用異常處理機制


在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 語言級別的健壯性要求
   能夠把異常形成的邏輯中斷破壞下降到最小範圍內,並通過補救處理後不影響業務邏輯的完整性;亂拋異常和只拋不捕獲,或捕獲而不補救,會致使數據混亂。 

1.6.2 怎樣看PHP的異常

1.6.3 PHP中的錯誤級別

depreccated
notice
warning
fetal error
prase error
$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 '致命錯誤後呢,還會執行嗎?'

1.6.4 PHP中的錯誤處理機制

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';
相關文章
相關標籤/搜索