① 請詳細說明一下php5中的魔術方法
PHP處理對象部分的內核完全重新開發過,提供更多功能的同時也提高了性能。在以前版本的php中,處理對象和處理基本類型(數字,字元串)的方式是一樣的。這種方式的缺陷是:當將對象賦值給一個變數時,或者通過參數傳遞對象時,對象將被完全拷貝一份。在新的版本里,上述操作將傳遞引用(可以把引用理解成對象的標識符),而非值。
很多PHP程序員可能甚至沒有察覺到老的對象處理方式。事實上,大多數的php應用都可以很好地運行。或者僅僅需要很少的改動。
私有和受保護成員
PHP5引入了私有和受保護成員變數的概念。我們可以用它來定義類成員的可見性。
例子
受保護成員可以被子類訪問, 而私有成員只能被類本身訪問。
代碼:--------------------------------------------------------------------------------
<?php
class MyClass {
private $Hello = "Hello, World!\n";
protected $Bar = "Hello, Foo!\n";
protected $Foo = "Hello, Bar!\n";
function printHello() {
print "MyClass::printHello() " . $this->Hello;
print "MyClass::printHello() " . $this->Bar;
print "MyClass::printHello() " . $this->Foo;
}
}
class MyClass2 extends MyClass {
protected $Foo;
function printHello() {
MyClass::printHello(); /* Should print */
print "MyClass2::printHello() " . $this->Hello; /* Shouldn't print out anything */
print "MyClass2::printHello() " . $this->Bar; /* Shouldn't print (not declared)*/
print "MyClass2::printHello() " . $this->Foo; /* Should print */
}
}
$obj = new MyClass();
print $obj->Hello; /* Shouldn't print out anything */
print $obj->Bar; /* Shouldn't print out anything */
print $obj->Foo; /* Shouldn't print out anything */
$obj->printHello(); /* Should print */
$obj = new MyClass2();
print $obj->Hello; /* Shouldn't print out anything */
print $obj->Bar; /* Shouldn't print out anything */
print $obj->Foo; /* Shouldn't print out anything */
$obj->printHello();
?>
--------------------------------------------------------------------------------
私有方法和受保護方法
PHP5也引入了私有方法和受保護方法的概念。
例子:
代碼:--------------------------------------------------------------------------------
<?php
class Foo {
private function aPrivateMethod() {
echo "Foo::aPrivateMethod() called.\n";
}
protected function aProtectedMethod() {
echo "Foo::aProtectedMethod() called.\n";
$this->aPrivateMethod();
}
}
class Bar extends Foo {
public function aPublicMethod() {
echo "Bar::aPublicMethod() called.\n";
$this->aProtectedMethod();
}
}
$o = new Bar;
$o->aPublicMethod();
?>
--------------------------------------------------------------------------------
以前的不使用類的老代碼,沒有訪問修飾符(public, protected, private)的代碼可以不經改動運行。
抽象類和抽象方法
Php5也引入了抽象類和抽象方法的概念。抽象方法只是聲明了方法的簽名並不提供它的實現。包含抽象方法的類必須被聲明成抽象類。
例子:
代碼:--------------------------------------------------------------------------------
<?php
abstract class AbstractClass {
abstract public function test();
}
class ImplementedClass extends AbstractClass {
public function test() {
echo "ImplementedClass::test() called.\n";
}
}
$o = new ImplementedClass;
$o->test();
?>
--------------------------------------------------------------------------------
抽象類不能被實例化。以前的不使用抽象類的老代碼可以不經改動運行。
介面
Php5引入了介面。一個類可以實現多個介面。
例子:
代碼:--------------------------------------------------------------------------------
<?php
interface Throwable {
public function getMessage();
}
class MyException implements Throwable {
public function getMessage() {
// ...
}
}
?>
--------------------------------------------------------------------------------
以前的不使用介面的老代碼可以不經改動運行
類的型別提示
PHP5依然是弱類型的,不過在定義函數參數時,可以使用類的型別提示來聲明期望傳入的對象類型
Example
代碼:--------------------------------------------------------------------------------
<?php
interface Foo {
function a(Foo $foo);
}
interface Bar {
function b(Bar $bar);
}
class FooBar implements Foo, Bar {
function a(Foo $foo) {
// ...
}
function b(Bar $bar) {
// ...
}
}
$a = new FooBar;
$b = new FooBar;
$a->a($b);
$a->b($b);
?>
--------------------------------------------------------------------------------
和其他強類型語言一樣,php5類的型別提示在運行期間檢查而非編譯期間檢查。即:
代碼:--------------------------------------------------------------------------------
<?php
function foo(ClassName $object) {
// ...
}
?>
和下面的代碼是一樣的:
<?php
function foo($object) {
if (!($object instanceof ClassName)) {
die("Argument 1 must be an instance of ClassName");
}
}
?>
--------------------------------------------------------------------------------
這個語法只適用於類,不適用於內建類型。
Final
PHP 5 引入了final關鍵字來聲明final成員和final方法。final成員和final方法不能被子類覆蓋。
Example
代碼:--------------------------------------------------------------------------------
<?php
class Foo {
final function bar() {
// ...
}
}
?>
--------------------------------------------------------------------------------
更進一步,可以把類聲明成final。將類聲明成final可以阻止這個類被繼承。final類裡面的方法預設地都是final的,無需再聲明一次。
Example
代碼:--------------------------------------------------------------------------------
<?php
final class Foo {
// class definition
}
// the next line is impossible
// class Bork extends Foo {}
?>
--------------------------------------------------------------------------------
屬性不能定義成為final.
以前的不使用final的老代碼可以不經改動運行.
對象克隆
Php4沒有提供一種機制來讓用戶自己定義復制構造子( constructor)控制對象的復制過程。Php4做二進制的拷貝,因而很精確地復制了對象的所有屬性。
精確地復制對象的所有屬性可能並不是我們一直想要的。有個例子可以很好地說明我們確實需要復制構造子:比如一個GTK Window的對象 a。 a持有它所需要的全部資源。當復制的這個GTK Window到對象b時候,我們更希望b持有新的資源對象。再舉個例子:對象a包含了一個對象c, 當你把對象a 復制到對象c的時候。我們可能更希望對象b包含一個新的對象c的, 而不是一個對象c的引用。(譯者註:這里所說的就是淺克隆和深克隆。)
對象的復制是通過clone這個關鍵字達到的(Clone調用被克隆對象的__clone()方法)。對象的__clone方法不能夠直接被調用。
代碼:--------------------------------------------------------------------------------
<?php
$_of_object = clone $object;
?>
--------------------------------------------------------------------------------
當developer創建對象的一份拷貝的時候,php5將會檢查 __clone()方法是否存在。如果不存在,那麼它就會呼叫預設的__clone()方法,復制對象的所有屬性。如果__clone()方法已經定義過,那麼_clone()方法就會負責設置新對象的屬性。為了方便起見,Engine會預設地復制所有的屬性。所以在__clone()方法中,只需要覆蓋那些需要更改的屬性就可以了。如下:
Example
代碼:--------------------------------------------------------------------------------
<?php
class MyCloneable {
static $id = 0;
function MyCloneable() {
$this->id = self::$id++;
}
function __clone() {
$this->address = "New York";
$this->id = self::$id++;
}
}
$obj = new MyCloneable();
$obj->name = "Hello";
$obj->address = "Tel-Aviv";
print $obj->id . "\n";
$obj_cloned = clone $obj;
print $obj_cloned->id . "\n";
print $obj_cloned->name . "\n";
print $obj_cloned->address . "\n";
?>
--------------------------------------------------------------------------------
統一構造函數
Php5允許開發者聲明一個類的構造方法。擁有構造方法的類在每次創建新的對象的時候都會呼叫這個方法,因此構造方法適合對象在被使用之前的初始化工作
Php4中,構造方法的名稱和類的名稱一樣。考慮到從子類構造方法呼叫父類構造方法的情況是非常普遍的,而將類從一個繼承體系中搬遷引起的父類變更就常常導致需要更改類的構造方法,php4的做法顯然是不太合理的。
Php5引入了一個聲明構建函數的標准方法: __construct().如下:
Example
代碼:--------------------------------------------------------------------------------
<?php
class BaseClass {
function __construct() {
print "In BaseClass constructor\n";
}
}
class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "In SubClass constructor\n";
}
}
$obj = new BaseClass();
$obj = new SubClass();
?>
--------------------------------------------------------------------------------
為保持向後的兼容性,如果php5不能夠找到 __construct(),它會尋找老式的構造方法,即與類同名的方法。簡單的說,只有當老代碼里包含了一個__construct()方法的時候,才存在一個兼容性的問題。
析構方法
對於面向對象的編程來說,可以定義析構方法是非常有用的一個功能。析構方法可以用來記錄調試信息,關閉資料庫連接等等一些清除收尾的工作。Php4中沒有析構方法,盡管php4已經支持可以注冊一個函數以便請求結束的時候被調用。
Php5引進的析構方法的概念和其他面向對象的語言(比如java)是一致的。當指向這個對象的最後一個引用被銷毀的時候,析構方法被調用,調用完成後釋放內存。注意:析構方法不接受任何參數。
Example
代碼:--------------------------------------------------------------------------------
<?php
class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = "MyDestructableClass";
}
function __destruct() {
print "Destroying " . $this->name . "\n";
}
}
$obj = new MyDestructableClass();
?>
--------------------------------------------------------------------------------
和構建方法一樣,父類的析構方法也不會被隱含調用。子類可以在自己的析構方法通過調用parent::__destruct()來顯式地調用它。
Constants
Php5引入了class級別的常量。
代碼:--------------------------------------------------------------------------------
<?php
class Foo {
const constant = "constant";
}
echo "Foo::constant = " . Foo::constant . "\n";
?>
--------------------------------------------------------------------------------
老的沒有使用const的代碼仍然正常運行。
Exceptions
Php4沒有異常控制。Php5引入了和其它語言(java)相似的異常控制模式。應該注意的是php5裡面支持捕捉全部異常,但是不支持finally子句。
在catch語句塊裡面,可以重新拋出異常。也可以有多個catch語句,在這種情況下,被捕捉到的異常從上往下依次比較和catch語句比較異常,第一個類型匹配的catch語句將會被執行。如果一直搜索到底還沒有發現匹配的catch子句,則尋找下一個try/catch語句。最後不能捕捉的異常將被顯示出來。如果異常被捕捉,那麼程序會接著catch語句塊的下面開始執行。
Example
代碼:--------------------------------------------------------------------------------
<?php
class MyException {
function __construct($exception) {
$this->exception = $exception;
}
function Display() {
print "MyException: $this->exception\n";
}
}
class MyExceptionFoo extends MyException {
function __construct($exception) {
$this->exception = $exception;
}
function Display() {
print "MyException: $this->exception\n";
}
}
try {
throw new MyExceptionFoo('Hello');
}
catch (MyException $exception) {
$exception->Display();
}
catch (Exception $exception) {
echo $exception;
}
?>
--------------------------------------------------------------------------------
上面的例子表明可以定義一個並不繼承自 Exception的異常類,但是,最好還是從Exception繼承並定義自己的異常。這是因為系統內建的Exception類能夠收集到很多有用的信息, 而不繼承它的異常類是得不到這些信息的。下面的php代碼模仿了系統內建Exception類。每個屬性後面都加了注釋。每個屬性都有一個getter,由於這些getter方法經常被系統內部處理調用,所以這些方法被標明了final。
Example
代碼:--------------------------------------------------------------------------------
<?php
class Exception {
function __construct(string $message=NULL, int code=0) {
if (func_num_args()) {
$this->message = $message;
}
$this->code = $code;
$this->file = __FILE__; // of throw clause
$this->line = __LINE__; // of throw clause
$this->trace = debug_backtrace();
$this->string = StringFormat($this);
}
protected $message = 'Unknown exception'; // exception message
protected $code = 0; // user defined exception code
protected $file; // source filename of exception
protected $line; // source line of exception
private $trace; // backtrace of exception
private $string; // internal only!!
final function getMessage() {
return $this->message;
}
final function getCode() {
return $this->code;
}
final function getFile() {
return $this->file;
}
final function getTrace() {
return $this->trace;
}
final function getTraceAsString() {
return self::TraceFormat($this);
}
function _toString() {
return $this->string;
}
static private function StringFormat(Exception $exception) {
// ... a function not available in PHP scripts
// that returns all relevant information as a string
}
static private function TraceFormat(Exception $exception) {
// ... a function not available in PHP scripts
// that returns the backtrace as a string
}
}
?>
--------------------------------------------------------------------------------
如果我們定義的一異常類都是繼承自Exception基類
無兼容性問題。老的代碼不會受到這一特性的影響。
Dereferencing objects returned from functions
Php4中不能再次引用函數返回的對象以進一步呼叫返回對象的方法,而php5是可以的。
代碼:--------------------------------------------------------------------------------
<?php
class Circle {
function draw() {
print "Circle\n";
}
}
class Square {
function draw() {
print "Square\n";
}
}
function ShapeFactoryMethod($shape) {
switch ($shape) {
case "Circle":
return new Circle();
case "Square":
return new Square();
}
}
ShapeFactoryMethod("Circle")->draw();
ShapeFactoryMethod("Square")->draw();
?>
--------------------------------------------------------------------------------
靜態成員變數能夠被初始化。
Example
代碼:--------------------------------------------------------------------------------
<?php
class foo {
static $my_static = 5;
public $my_prop = 'bla';
}
print foo::$my_static;
$obj = new foo;
print $obj->my_prop;
?>
--------------------------------------------------------------------------------
靜態方法
PHP 5 引入了靜態方法,可以在不實例化類的情況下呼叫靜態方法。
Example
代碼:--------------------------------------------------------------------------------
<?php
class Foo {
public static function aStaticMethod() {
// ...
}
}
Foo::aStaticMethod();
?>
--------------------------------------------------------------------------------
偽變數$this不能夠在靜態方法方法中使用。
instanceof
Php5引入了instanceof關鍵字,允許用它來測試一個對象是一個類的實例,或者是一個派生類的實例,或者實現了某個介面
Example
代碼:--------------------------------------------------------------------------------
<?php
class baseClass { }
$a = new baseClass;
if ($a instanceof baseClass) {
echo "Hello World";
}
?>
--------------------------------------------------------------------------------
Static function variables
現在,靜態變數在編譯階段處理。因此程序員可以通過引用為靜態變數賦值。這可以改善性能,不過,不能夠使用對靜態變數的間接引用了。
按引用傳遞的函數參數現在也可以設置預設值了。
Example
代碼:--------------------------------------------------------------------------------
<?php
function my_function(&$var = null) {
if ($var === null) {
die("$var needs to have a value");
}
}
?>
--------------------------------------------------------------------------------
__autoload()
__autoload() 攔截函數在一個未聲明的類被初始化的時候自動調用。該類的名字會被自動傳遞給__autoload()函數。而__autoload()也只有這么唯一的一個參數。
Example
代碼:--------------------------------------------------------------------------------
<?php
function __autoload($className) {
include_once $className . ".php";
}
$object = new ClassName;
?>
--------------------------------------------------------------------------------
可重載的方法呼叫和屬性訪問
方法呼叫和屬性訪問都能夠通過__call, __get() and __set()方法重載。
Example: __get() and __set()
代碼:--------------------------------------------------------------------------------
<?php
class Setter {
public $n;
public $x = array("a" => 1, "b" => 2, "c" => 3);
function __get($nm) {
print "Getting [$nm]\n";
if (isset($this->x[$nm])) {
$r = $this->x[$nm];
print "Returning: $r\n";
return $r;
} else {
print "Nothing!\n";
}
}
function __set($nm, $val) {
print "Setting [$nm] to $val\n";
if (isset($this->x[$nm])) {
$this->x[$nm] = $val;
print "OK!\n";
} else {
print "Not OK!\n";
}
}
}
$foo = new Setter();
$foo->n = 1;
$foo->a = 100;
$foo->a++;
$foo->z++;
var_mp($foo);
?>
--------------------------------------------------------------------------------
Example: __call()
代碼:--------------------------------------------------------------------------------
<?php
class Caller {
private $x = array(1, 2, 3);
function __call($m, $a) {
print "Method $m called:\n";
var_mp($a);
return $this->x;
}
}
$foo = new Caller();
$a = $foo->test(1, "2", 3.4, true);
var_mp($a);
?>
--------------------------------------------------------------------------------
迭代
當和foreach一起使用對象的時候,迭代的方式被重載過了。預設的行為是迭代類的所有屬性。
Example
代碼:--------------------------------------------------------------------------------
<?php
class Foo {
public $x = 1;
public $y = 2;
}
$obj = new Foo;
foreach ($obj as $prp_name => $prop_value) {
// using the property
}
?>
--------------------------------------------------------------------------------
一個類的所有對象都能夠被迭代瀏覽到, 如果這個類實現了一個空的介面:Traversable. 換句話說,實現了Traversable介面的類可以和foreach一起使用。
介面 IteratorAggregate 和Iterator允許指定類的對象在代碼中如何迭代。IteratorAggregate介面有一個方法:getIterator() 必須返回一個數組
Example
代碼:--------------------------------------------------------------------------------
<?php
class ObjectIterator implements Iterator {
private $obj;
private $num;
function __construct($obj) {
$this->obj = $obj;
}
function rewind() {
$this->num = 0;
}
function valid() {
return $this->num < $this->obj->max;
}
function key() {
return $this->num;
}
function current() {
switch($this->num) {
case 0: return "1st";
case 1: return "2nd";
case 2: return "3rd";
default: return $this->num."th";
}
}
function next() {
$this->num++;
}
}
class Object implements IteratorAggregate {
public $max = 3;
function getIterator() {
return new ObjectIterator($this);
}
}
$obj = new Object;
// this foreach ...
foreach($obj as $key => $val) {
echo "$key = $val\n";
}
// matches the following 7 lines with the for directive.
$it = $obj->getIterator();
for($it->rewind(); $it->hasMore(); $it->next) {
$key = $it->current();
$val = $it->key();
echo "$key = $val\n";
}
unset($it);
?>
--------------------------------------------------------------------------------
新的__toString方法
可以通過覆蓋__toString方法來控制對象到字元串的轉換。
Example
代碼:---------------------------------------
② PHP模式、映射
1.這三種是面向對象的軟體設計模式,好像和具體的語言沒有關系吧
2.正確應該稱為「單例模式」,在整個應用系統范圍內只有一個實例對象,例子:
class Temple{
private static $ins;
private function __construct(){
}
public static function instance(){
if(Temple::$ins == null){
Temple::$ins = new Temple();
}
return Temple::$ins;
}
public function add($a,$b){
return $a + $b;
}
}
$tp = Temple::instance();
//$tp就是通過單例模式得到的對象,任何地方用到這個對象都是同一個
//解釋一下代碼吧
//構造函數使用的是private修飾,即私有,所以外部無法新建新對象,只能通過該類的類方法接觸
//$ins使用private+static修飾,private使得外部無法改變該屬性,static則保證在應用程序在用時
不會銷毀對象
//綜合instance()類方法,獲取到了Temp的實例對象,而該對象一經創建,在應用范圍內不會銷毀,即所有外部文件使用的都是同一個對象,這就是所謂的 單例模式
3.至於映射,沒用過,也講不清楚,具體用在哪裡不清楚 php的實現函數是
get_class_methods($classname) //獲取一個類的方法,返回方法名數組
get_class_vars($classname)//獲取一個類的屬性,返回屬性名數組
//就這些了,單例模式還是很清楚的,具體分數怎麼給,看你自己了
③ PHP中幾種常見的開發模式
單例模式
$_instance必須聲明為靜態的私有變數
構造函數和析構函數必須聲明為私有,防止外部程序new 類從而失去單例模式的意義
getInstance()方法必須設置為公有的,必須調用此方法 以返回實例的一個引用
::操作符只能訪問靜態變數和靜態函數
new對象都會消耗內存
使用場景:最常用的地方是資料庫連接。
使用單例模式生成一個對象後, 該對象可以被其它眾多對象所使用。
私有的__clone()方法防止克隆對象
單例模式,使某個類的對象僅允許創建一個。構造函數private修飾,
申明一個static getInstance方法,在該方法里創建該對象的實例。如果該實例已經存在,則不創建。比如只需要創建一個資料庫連接。
工廠模式
工廠模式,工廠方法或者類生成對象,而不是在代碼中直接new。
使用工廠模式,可以避免當改變某個類的名字或者方法之後,在調用這個類的所有的代碼中都修改它的名字或者參數。
注冊模式
注冊模式,解決全局共享和交換對象。已經創建好的對象,掛在到某個全局可以使用的數組上,在需要使用的時候,直接從該數組上獲取即可。將對象注冊到全局的樹上。任何地方直接去訪問。
策略模式
策略模式,將一組特定的行為和演算法封裝成類,以適應某些特定的上下文環境。
eg:假如有一個電商網站系統,針對男性女性用戶要各自跳轉到不同的商品類目,並且所有的廣告位展示不同的廣告。在傳統的代碼中,都是在系統中加入各種if else的判斷,硬編碼的方式。如果有一天增加了一種用戶,就需要改寫代碼。使用策略模式,如果新增加一種用戶類型,只需要增加一種策略就可以。其他所有的地方只需要使用不同的策略就可以。
首先聲明策略的介面文件,約定了策略的包含的行為。然後,定義各個具體的策略實現類。
執行結果圖:
總結:
通過以上方式,可以發現,在不同用戶登錄時顯示不同的內容,但是解決了在顯示時的硬編碼的問題。如果要增加一種策略,只需要增加一種策略實現類,然後在入口文件中執行判斷,傳入這個類即可。實現了解耦。
實現依賴倒置和控制反轉(有待理解)
通過介面的方式,使得類和類之間不直接依賴。在使用該類的時候,才動態的傳入該介面的一個實現類。如果要替換某個類,只需要提供一個實現了該介面的實現類,通過修改一行代碼即可完成替換。
觀察者模式
1:觀察者模式(Observer),當一個對象狀態發生變化時,依賴它的對象全部會收到通知,並自動更新。
2:場景:一個事件發生後,要執行一連串更新操作。傳統的編程方式,就是在事件的代碼之後直接加入處理的邏輯。當更新的邏輯增多之後,代碼會變得難以維護。這種方式是耦合的,侵入式的,增加新的邏輯需要修改事件的主體代碼。
3:觀察者模式實現了低耦合,非侵入式的通知與更新機制。
定義一個事件觸發抽象類。
當某個事件發生後,需要執行的邏輯增多時,可以以松耦合的方式去增刪邏輯。也就是代碼中的紅色部分,只需要定義一個實現了觀察者介面的類,實現復雜的邏輯,然後在紅色的部分加上一行代碼即可。這樣實現了低耦合。
裝飾器模式
1:裝飾器模式,可以動態的添加修改類的功能
2:一個類提供了一項功能,如果要在修改並添加額外的功能,傳統的編程模式,需要寫一個子類繼承它,並重寫實現類的方法
3:使用裝飾器模式,僅需要在運行時添加一個裝飾器對象即可實現,可以實現最大額靈活性。
④ 怎麼用php實現單例模式
類內使用方法,類似
public static function getInstance($class_name){
if(! isset(static::$classList[$class_name])){
static::$classList[$class_name] = new $class_name;
}
return static::$classList[$class_name];
}
實例化
(className)::getInstance($class_name);