1. 程式人生 > >PHP面向物件的一些坑爹技巧

PHP面向物件的一些坑爹技巧

最近在肝PHP和Mysql

以我學了四天PHP的感覺來看

PHP的類如果不是考慮前後端徹底分離的話,用class來封裝整個網頁的Page用來重複利用還是不錯的

然後在學習面向物件的時候有些坑爹(高深)的操作讓我有些苦惱

1.迭代屬性

<?php
	class myClass
	{
		public $a="xiao";
		public $b="yu";
		public $c="yu";
	}
	$x=new myClass;
	foreach ($x as $key )
	{
		echo $key."<br />";
	}
	echo "<br />";
?>

上面這段程式碼還是很簡單的,就是用foreach輸出類中的所有屬性,然後開始玄學,迭代器……

2.迭代器

為什麼要用迭代器呢,如果我們的迭代行為很複雜,用迭代器是不錯的操作

然後重點來了迭代器(iterator)需要IteratorAggregate介面,getIterator(返回該迭代器例項)

生命一個類有迭代器介面的方法:

class ObjectIterator implements Iterator //生命ObjectIterator是一個迭代器,可以呼叫迭代器介面所要求的一系列函式

有哪些函式是需要的呢,如下(不需要解構函式)

	class ObjectIterator implements Iterator //生命ObjectIterator是一個迭代器,可以呼叫迭代器介面所要求的一系列函式
	{
		private $obj;
		private $count;
		private $currentIndex;
		//建構函式
		function __construct($obj)
		{
			$this->obj=$obj;
			$this->count=count($this->obj->data);
		}
		//重置內部資料指標
		function rewind()
		{
			$this->currentIndex=0;
		}
		//判斷資料指標當前位置是否還存在更多資料
		function vaild()
		{
			return $this->currentIndex<$this->count; //true即為vaild
		}
		//返回資料指標值
		function key()
		{
			return $this->currentIndex;
		}
		//返回當前資料指標指向的資料
		function current()
		{
			return $this->obj->data[$this->currentIndex];
		}
		//向後移動資料指標1step
		function next()
		{
			$this->currentIndex++;
		}
	}

其實就是要把foreach的操作封裝在類裡面來應付各種各樣的情況,乍一看比較多此一舉,但是實際應用還是有價值的,不同的迭代方式我們用這些操作基本都可以搞定

這只是迭代器,我們還需要迭代器的介面IteratorAggregate,我們上面用的data就是接口裡的變數,通過這個介面把需要迭代的變數傳給迭代器運作(通過getIterator)

class Object implements IteratorAggregate
	{
		public $data=array();
		function __construct($in)
		{
			$this->data=$in;
		}
		function getIterator()
		{
			return new ObjectIterator($this);
		}
	}

分解開來慢慢分析其實還可以,就是把變數傳進data,在把變數的指標傳給迭代器,從而進行運作

給個例子

<?php
	class ObjectIterator implements Iterator //生命ObjectIterator是一個迭代器,可以呼叫迭代器介面所要求的一系列函式
	{
		private $obj;
		private $count;
		private $currentIndex;
		//建構函式
		function __construct($obj)
		{
			$this->obj=$obj;
			$this->count=count($this->obj->data);
		}
		//重置內部資料指標
		function rewind()
		{
			$this->currentIndex=0;
		}
		//判斷資料指標當前位置是否還存在更多資料
		function valid()
		{
			return $this->currentIndex<$this->count; //true即為vaild
		}
		//返回資料指標值
		function key()
		{
			return $this->currentIndex;
		}
		//返回當前資料指標指向的資料
		function current()
		{
			return $this->obj->data[$this->currentIndex];
		}
		//向後移動資料指標1step
		function next()
		{
			$this->currentIndex++;
		}
	}
	
	class Object implements IteratorAggregate
	{
		public $data=array();
		function __construct($in)
		{
			$this->data=$in;
		}
		function getIterator()
		{
			return new ObjectIterator($this);
		}
	}

	$myObject=new Object(array(2,4,6,8,10));   //data改為一個array
	$myIterator= $myObject->getIterator();     //data的地址傳入迭代器
	for($myIterator->rewind();$myIterator->valid();$myIterator->next())
	{
		$key=$myIterator->key();
		$value=$myIterator->current();
		echo $key."=>".$value."<br />";
	}
	echo "<br />";
?>

結果如下

這個其實還比較容易,但是如果複雜了呢,或者是用了別的迭代方式麼,不論內部實現怎麼變化,資料介面都是固定的,不用我們多費心,這就是迭代器的作用

3.生成器

生成器怎麼說了也算是在迭代中使用的,而且比迭代器簡單且容易理解,如果能用生成器的話,我個人傾向於直接while、for之類的,這裡就不做介紹了

4.反射API

這個操作有點像爬去類的結構的爬蟲?

如果我們手裡有一些類的文件,但是不熟悉,變數很複雜,很容易看的一頭霧水,這個時候這個操作就很有用了,來個例子比較直觀

<?php
	class Page
	{
		public $content;
		public $title="TLA Consulting Pty Ltd";
		public $keywords="TLA Consulting, Three Letter Abbreviation, some of my best friends are search engines";
		public $buttons=["Home"=>"12_home.php",
						 "Contact"=>"12_contact.php",
						 "Services"=>"12_services.php",
						 "Site Map"=>"12_map.php"];
		//class Page's operations
		public function __set($name,$value)
		{
			$this->$name=$value;
		}
		public function Display()
		{
			echo "<html>\n<head>\n";
			$this->DisplayTitle();
			$this->DisplayKeywords();
			echo "</head>\n<body>\n";
			$this->DisplayHeader();
			$this->DisplayMenu($this->buttons);
			echo $this->content;
			$this->DisplayFooter();
			echo "</body>\n</html>\n";
		}
		public function DisplayTitle()
		{
			echo "<title>".$this->title."</title>";
		}
		public function DisplayKeywords()
		{
			//<meta> 標籤永遠位於 head 元素內部
			//元資料總是以名稱/值的形式被成對傳遞的
			echo "<meta name='keywords' content='".$this->keywords."'/>";
			echo "<title>".$this->title."</title>";
		}
		public function DisplayHeader()
		{
			?>
			<!-- page header -->
			<header>
				<img src="./11.jpg" alt="TLA logo" height="200" width="600"/>
				<h1>TLA Consulting</h1>
			</header>
			<?php
		}
		public function DisplayMenu($buttons)
		{
			echo "<!--menu-->
			<nav>";
			while(list($name,$url)=each($buttons))
			{
				$this->DisplayButton($name,$url,!$this->IsURLCurrentPage($url));
			}
			echo "</nav>\n";
		}
		public function IsURLCurrentPage($url)
		{
			//例如,在地址為 http://example.com/foo/bar.php 的指令碼中使用 $_SERVER['PHP_SELF'] 將得到 /foo/bar.php
			if(strpos($_SERVER['PHP_SELF'],$url)===false)
			{
				return false;
			}
			else
			{
				return true;
			}
		}
		public function DisplayButton($name,$url,$active=true)
		{
			if($active)
			{
				?>
				<div class="menuitem">
					<a href="<?=$url?>">
						<img src="./1.jpg" alt="" height="50" width="50"/>
						<!-- ?=?可以在html中表示php的屬性值 -->
						<span class="menutext"><?=$name?></span>
					</a>
				</div>
				<?php
			}
			else
			{
				?>
				<div class="menuitem">
					<img src="./1.jpg" alt="" height="50" width="50"/>
					<span class="menutext"><?=$name?></span>
				</div>
				<?php
			}
		}
		public function DisplayFooter()
		{
			?>
			<!-- page footer -->
			<footer style="background: #ccccff;">
				<p>TLA Consulting Pty Ltd.<br />
				Please see our legal information page.</p>
			</footer>
			<?php
		}
	}
?>

上面這是我們的一個類,東西還是蠻多的對於我這個菜雞來說,我們接下來用反射API來爬取

<?php
	//反射API
	require_once("12_page.php");
	$class =new ReflectionClass("Page");
	echo "<pre>".$class."</pre>";	
?>

結果如下

對我這種菜鳥幫助還蠻大的,能一眼看出一個類大概的操作和結構

5.延遲靜態繫結

這是繼承裡的操作,下面這個例子的輸出可以很好地解釋

<?php
	class A
	{
		public static function whichclass()
		{
			echo __CLASS__; //使用 self:: 或者 __CLASS__ 對當前類的靜態引用,取決於定義當前方法所在的類
		}
		public static function test()
		{
			self::whichclass();
		}
	}
	class B extends A
	{
		public static function whichclass()
		{
			echo __CLASS__;
		}
	}
	A::test();
	echo "<br />";
	B::test();
?>

輸出結果如下

兩個A,而不是A B

我們要解決這個問題很簡單,把self改成static就好啦,可以試一下哦

6.多重繼承?(Trait)

對不起,PHP沒有多重繼承,但是可以用別的方法實現,比如Trait,又比如介面

Trait對我來說感覺好的地方就是他強調的程式碼重用是包含了實現的,因人而異吧,介面的話比較快捷,Triat組合起來我覺得還蠻爽的

<?php
	trait fileLogger
	{
		public function logmessage($message,$level="DEBUG")
		{
			//code
		}
	}
	trait sysLogger
	{
		public function logmessage($message,$level="ERROR")
		{
			//code
		}
	}
	class fileStorage
	{
		use fileLogger,sysLogger
		{
			fileLogger::logmessage insteadof sysLogger;
			sysLogger::logmessage as logsymessage;
		}
		function store($data)
		{
			$this->logmessage($message);
			$this->logsymessage($message);
		}
	}
?>

上述程式碼就體現Trait的組合特點,而且我們可以看到如果遇到重名的函式,可以用instead和as來輕鬆解決,這樣新的類就可以使用你所需要的Trait了,繼承不同Trait,但不是直接繼承不同的類的部分,算是一種模擬的多重繼承吧,用起來有時候有不錯的效果,而且很靈活,可是隨時給類“打補丁”