<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	>

<channel>
	<title>WestKit TechBlog</title>
	<atom:link href="http://techblog.westkit.net/index.php/feed/" rel="self" type="application/rss+xml" />
	<link>http://techblog.westkit.net</link>
	<description>西杰技術分享</description>
	<pubDate>Sun, 16 Aug 2009 16:02:21 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>IE set innerHTML的CSS問題</title>
		<link>http://techblog.westkit.net/index.php/javascript/ie-innerhtml-stupid-problem-on-css/</link>
		<comments>http://techblog.westkit.net/index.php/javascript/ie-innerhtml-stupid-problem-on-css/#comments</comments>
		<pubDate>Sun, 16 Aug 2009 16:02:21 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[browser]]></category>

		<category><![CDATA[javascript]]></category>

		<category><![CDATA[css]]></category>

		<category><![CDATA[html]]></category>

		<category><![CDATA[ie]]></category>

		<category><![CDATA[innerhtml]]></category>

		<category><![CDATA[render]]></category>

		<guid isPermaLink="false">http://techblog.westkit.net/?p=177</guid>
		<description><![CDATA[做mix2ool的template元素時發現的一個問題。在IE中，當我set innerHTML時，如果我要加入的element不是標準的element(e.g. &#60;a&#62; &#60;div&#62; )，IE就不會把style 應用到那些element了。
解決方法是這樣的，雖然使用innerHTML來加入element有問題，但如果加入document.createElement產生的element卻是可以成功的。因此，只要用這些DOM方法把要設定的html轉為DOM element就可以了。可幸的是，這個工作已經有人做了。
innerDOM
這個innerDOM可以把string轉為DOM，把DOM轉為String。
]]></description>
		<wfw:commentRss>http://techblog.westkit.net/index.php/javascript/ie-innerhtml-stupid-problem-on-css/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Too much recursion</title>
		<link>http://techblog.westkit.net/index.php/uncategorized/too-much-recursion/</link>
		<comments>http://techblog.westkit.net/index.php/uncategorized/too-much-recursion/#comments</comments>
		<pubDate>Sat, 15 Aug 2009 04:18:58 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://techblog.westkit.net/?p=172</guid>
		<description><![CDATA[近期在搞Mix2ool，是mix framework的進化版，這次view engine由B/S 轉為C/S架構，大量使用了javascript。
其中寫了這麼的一段

1
2
3
4
5
6
7
8
9
10
11
mix.Bindable.bindingFunc = function &#40;n, old, vari&#41;&#123;
	var b = $&#40;this&#41;.attr&#40;&#34;binding&#34;&#41;;
	if &#40;b == null&#41; b = &#34;&#34;;
	var context = this;
	b.replace&#40;&#34; &#34;, &#34;&#34;&#41;.split&#40;&#34;;&#34;&#41;.removeNothing&#40;&#41;.each&#40;function &#40;v&#41;&#123;
		var t = v.split&#40;&#34;:&#34;&#41;;
		if &#40;t&#91;1&#93; == vari&#41;&#123;
			$&#40;this&#41;.attr&#40;t&#91;0&#93;, n&#41;;
		&#125;
	&#125;, this&#41;;
&#125;;

就是做binding的工作，然而卻產生了too much recursion的問題，因為當我setAttribute的時候，下一個element也會notify一個attrChange event，那個event同時又做了binding的工作，結果bind得沒完沒了。
解決方法是把$(this).attr(t[0], n)放在一個新的closure中進行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mix.Bindable.bindingFunc = function &#40;n, old, vari&#41;&#123;
	var b = $&#40;this&#41;.attr&#40;&#34;binding&#34;&#41;;
	if &#40;b == null&#41; b = &#34;&#34;;
	var context = this;
	b.replace&#40;&#34; &#34;, &#34;&#34;&#41;.split&#40;&#34;;&#34;&#41;.removeNothing&#40;&#41;.each&#40;function [...]]]></description>
		<wfw:commentRss>http://techblog.westkit.net/index.php/uncategorized/too-much-recursion/feed/</wfw:commentRss>
		</item>
		<item>
		<title>PHP MVC分工指導</title>
		<link>http://techblog.westkit.net/index.php/php/php-mvc-collaboration/</link>
		<comments>http://techblog.westkit.net/index.php/php/php-mvc-collaboration/#comments</comments>
		<pubDate>Sun, 05 Jul 2009 13:07:52 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[MVC]]></category>

		<category><![CDATA[PHP]]></category>

		<category><![CDATA[collaboration]]></category>

		<category><![CDATA[programming]]></category>

		<category><![CDATA[分工]]></category>

		<category><![CDATA[編程]]></category>

		<guid isPermaLink="false">http://techblog.westkit.net/?p=166</guid>
		<description><![CDATA[這兩天開始新project了，這次不是唱獨腳戲了，而是與數人合作，亦因此有分工問題。我們三個programmer將會使用PHP JS HTML等等技術來開發一個網站，而程式架構將會是MVC的，問題就是我們的工作時間不一，有人可以早上做，有人晚上做，如果一人負責一部份(M、V、C)，那很可能會做成C要等V完成才可以繼續寫，而且每次開始之前亦要先回顧V做過的東西，做不到同步開發，那麼便失去了分工的意義。更恐怖的是C要等V做完才可以測試，或者要另外寫tester來測試，浪費十分多的時間。
因此，我們研究了另一個開發模式，是橫分MVC，以模組為單位，如登入是一個功能模組，搜尋是一個功能模組，每個模組的MVC都由同一人開發，而每個模組之間亦保持高度分離。如果模組間要溝通(例如搜尋模組可分為搜尋框和結果列表)，就會使用observer pattern來連接，即建立一個全局的事件處理機制，當搜尋完結時便fire一個event，讓結果列表(observer)更新自己的資料。
另外，為減少回顧別人寫的code 的時間，我們亦設計了一個新的程式碼架構，架構是把Controller的action分開成不同file，並且把JS、PHTML(template檔)分開成多個file，要使用時才載入，以避免測試時會受到別人的syntax error影響。唯一美中不足的是，PHP的物件機制不夠動態，Model很難做到分開數個檔案並動態載入，所以只能夠把Model的method寫在同一檔案。
Skeleton - 這是我們的開發模式骨架。
]]></description>
		<wfw:commentRss>http://techblog.westkit.net/index.php/php/php-mvc-collaboration/feed/</wfw:commentRss>
		</item>
		<item>
		<title>高登+API釋出</title>
		<link>http://techblog.westkit.net/index.php/%e9%ab%98%e7%99%bb/release-of-hkgoldenplus-api/</link>
		<comments>http://techblog.westkit.net/index.php/%e9%ab%98%e7%99%bb/release-of-hkgoldenplus-api/#comments</comments>
		<pubDate>Sat, 16 May 2009 14:31:11 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[高登]]></category>

		<guid isPermaLink="false">http://techblog.westkit.net/?p=162</guid>
		<description><![CDATA[做好了一套高登+的API了，開發者可以在此看到高登+API文檔
]]></description>
		<wfw:commentRss>http://techblog.westkit.net/index.php/%e9%ab%98%e7%99%bb/release-of-hkgoldenplus-api/feed/</wfw:commentRss>
		</item>
		<item>
		<title>做了兩晚的一句SQL</title>
		<link>http://techblog.westkit.net/index.php/php/sql-missing-hour-from-data/</link>
		<comments>http://techblog.westkit.net/index.php/php/sql-missing-hour-from-data/#comments</comments>
		<pubDate>Wed, 14 Jan 2009 15:15:39 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[MySQL]]></category>

		<category><![CDATA[PHP]]></category>

		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false">http://techblog.westkit.net/?p=152</guid>
		<description><![CDATA[這數天都在做一個網站流量統計的程式，其中一步是要找出每小時的人流。很直觀的，我會使用GROUP BY來處理，然而，這就出現了一個問題。如果有其中一小時沒有人來過網站的話，資料庫內就沒有那一小時的資料，那麼就會出現在每小時的人流統計中缺少了一小時的資料(那小時的人流該為0，現在卻是少了這筆資料)。要解決的方法有很多，但西杰程式的標準是高效和代碼優雅，這確然令我少了很多選擇。第一個選擇，亦是最佳的選擇，是在SELECT的時候產生一個時間列表，然後再把該小時的資料合併在內，十分直觀的選擇。當然，如果可以做到的話，就沒有這篇文章。問題就是，MySQL本身不具備generate的功能(如PgSQL的generate_series)，沒有此功能這個方法就完全不可行了。
第二個選擇是按照原本的SQL來SELECT，拿到PHP後就當作是例外來處理，這個方法是高效的，然而要另外寫一個例外處理就弄污我的程式了，這只能作為後備方案。
最後的一個選擇是新增一個時間列表，包含未來十年的每一小時(總數為87600筆資料)，那就避免了在SELECT時才產生一個時間列表。這個選擇的壞處是十年後要再重新產生一個時間列表，運作上是有缺憾的。然而，為了不破壞PHP程式的優雅，我還是選擇了這個方法。
使用這個方法，有兩個思路。我的第一個思路是，從時間列表找出每一個小時，然後再LEFT JOIN流量資料。很快地，我寫了一句這樣的SQL，可是效率差得很驚人，只是從一萬多筆資料中找出二百多筆資料快花費了十多秒，那怎能接受？這個效率問題相信是LEFT JOIN的效率太低了，還記得以前我也試過用LEFT JOIN，速度也是十分低。那好了，第一個思路不行，就用第二個思路。
第二個思路是，從時間列表找出每一小時的資料列，再用那條&#8221;錯&#8221;的SQL，即沒有了不存在流量的小時那條，UNION兩個SET，再GROUP在一起。寫完才發現原來UNION後是不能直接GROUP的，那就麻煩了。上官網找了一下資料，原來可以利用subquery來解決。思路就是把這條SELECT當成subquery，再在外層SELECT 一次這個暫時性列表，那就可以使用GROUP BY了。
最後的SQL是這樣的︰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SELECT `date`,`hour`,SUM&#40;`count`&#41; AS `count` FROM &#40;&#40;
	SELECT DATE_FORMAT&#40;FROM_UNIXTIME&#40;`webStatistics_stat`.`time`&#41;, '%b %d, %Y'&#41; AS `date`,
	HOUR&#40;FROM_UNIXTIME&#40;`webStatistics_stat`.`time`&#41;&#41; AS `hour`,
	COUNT&#40;*&#41; AS `count` FROM `webStatistics_stat`
	WHERE `webStatistics_stat`.`requestURL` LIKE 'http://hkg.westkit.net%' &#38;&#38;
	&#40;'1231689600' &#60; `time` &#38;&#38; `time` &#60; '1231948800'&#41;
	GROUP BY `date`,`hour`
&#41; UNION &#40;
	SELECT DATE_FORMAT&#40;`webStatistics_calendar`.`dateHour`, '%b %d, %Y'&#41; AS `date`,
	HOUR&#40;`webStatistics_calendar`.`dateHour`&#41; AS `hour`,
	0 AS `count` FROM `webStatistics_calendar`
	WHERE FROM_UNIXTIME&#40;'1231689600'&#41; &#60; `dateHour` &#38;&#38; `dateHour` [...]]]></description>
		<wfw:commentRss>http://techblog.westkit.net/index.php/php/sql-missing-hour-from-data/feed/</wfw:commentRss>
		</item>
		<item>
		<title>PHP程式優化實錄</title>
		<link>http://techblog.westkit.net/index.php/php/php-optimize-programming-in-real-scene/</link>
		<comments>http://techblog.westkit.net/index.php/php/php-optimize-programming-in-real-scene/#comments</comments>
		<pubDate>Wed, 07 Jan 2009 14:14:45 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[csv]]></category>

		<category><![CDATA[fgetcsv]]></category>

		<category><![CDATA[fgets]]></category>

		<category><![CDATA[ip to country]]></category>

		<category><![CDATA[ip2country]]></category>

		<category><![CDATA[優化]]></category>

		<guid isPermaLink="false">http://techblog.westkit.net/?p=128</guid>
		<description><![CDATA[剛才在寫一個將IP轉換為國家名稱的程式，使用了市面上提供的IP文字資料庫。資料庫官網建議使用mysql資料庫來記載資料，然而，如果mysql資料庫和PHP伺服器的主機不同，要使用網絡來拿取資料的話，相信會比本機查找慢。於是，最後我決定封裝一個本機查詢IP資料的程式。
初版是這樣的︰

//...
public function getCountry&#40;$ip&#41;&#123;
	$fp = fopen&#40;$this-&#62;csvPath, &#34;r&#34;&#41;;
	while &#40;list&#40;$ipF, $ipT, $temp, $temp, $countryName&#41; =fgetcsv&#40;$fp&#41;&#41;&#123;
		if &#40;$ipF &#60;= $this-&#62;ipToNum&#40;$ip&#41; &#38;&#38; $ipT &#62;=$this-&#62;ipToNum&#40;$ip&#41;&#41;&#123;
			fclose&#40;$fp&#41;;
			return trim&#40;$countryName&#41;;
		&#125;
	&#125;
	fclose&#40;$fp&#41;;
	return FALSE;
&#125;
//...

測試程式是查找一個IP的國家名稱，第一次運行結果使用了1.3s，當然不能接受，如果有一百個人同時觀看已經慢了不少，優化是必須的。IP資料庫有近八萬九千行，即while-loop要被執行八萬九千次，要做優化就當然要從while-loop著手。優化第一步，將重複使用的變數抽出來，即$this-&#62;ipToNum($ip)一句。
第一次改良後︰

//...
public function getCountry&#40;$ip&#41;&#123;
	$fp = fopen&#40;$this-&#62;csvPath, &#34;r&#34;&#41;;
	$ipNum = $this-&#62;ipToNum&#40;$ip&#41;;
	while &#40;list&#40;$ipF, $ipT, $temp, $temp, $countryName&#41; =fgetcsv&#40;$fp&#41;&#41;&#123;
		if &#40;$ipF &#60;= $ipNum &#38;&#38; $ipT &#62;= $ipNum&#41;&#123;
			fclose&#40;$fp&#41;;
			return trim&#40;$countryName&#41;;
		&#125;
	&#125;
	fclose&#40;$fp&#41;;
	return FALSE;
&#125;
//...

有著明顯的改善，足足快了0.4s，現在使用0.9s了，然而，還不是很令人滿意，再改良。奇怪的事情發生了，我本來以為用PHP內置的函數來解析CSV檔一定比自己寫的快，事實證明我錯了，原來自己寫explode比fgetcsv還要快，最後版是這樣的︰

//...
public function getCountry&#40;$ip&#41;&#123;
	$fp = fopen&#40;$this-&#62;csvPath, &#34;r&#34;&#41;;
	$ipNum = $this-&#62;ipToNum&#40;$ip&#41;;
	while &#40;list&#40;$ipF, $ipT, $temp, $temp, $countryName&#41; = [...]]]></description>
		<wfw:commentRss>http://techblog.westkit.net/index.php/php/php-optimize-programming-in-real-scene/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Wordpress 主題 - 西杰.NET</title>
		<link>http://techblog.westkit.net/index.php/wordpress/wordpress-theme-westkitnet/</link>
		<comments>http://techblog.westkit.net/index.php/wordpress/wordpress-theme-westkitnet/#comments</comments>
		<pubDate>Tue, 06 Jan 2009 09:44:46 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[Wordpress]]></category>

		<category><![CDATA[theme]]></category>

		<category><![CDATA[主題]]></category>

		<category><![CDATA[佈景主題]]></category>

		<guid isPermaLink="false">http://techblog.westkit.net/?p=122</guid>
		<description><![CDATA[花了兩天時間做了一個Wordpress的主題，就是大家現在看到的黑紅主題了，素材主要還是用西杰.NET的圖片。不得不提，麻煩的I.E.真的十分麻煩，用png圖片又不能直接做到透明效果，轉了gif質素又會變差，最後還是轉gif後自己一pixel一pixel的改動:(。
下載連結在這︰ westkit
]]></description>
		<wfw:commentRss>http://techblog.westkit.net/index.php/wordpress/wordpress-theme-westkitnet/feed/</wfw:commentRss>
		</item>
		<item>
		<title>js 動態載入 script，同步或異步？</title>
		<link>http://techblog.westkit.net/index.php/javascript/js-load-script-asynchronously/</link>
		<comments>http://techblog.westkit.net/index.php/javascript/js-load-script-asynchronously/#comments</comments>
		<pubDate>Sun, 04 Jan 2009 13:31:10 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[javascript]]></category>

		<category><![CDATA[AJAX]]></category>

		<category><![CDATA[callback]]></category>

		<category><![CDATA[dynamic load script]]></category>

		<category><![CDATA[XHR]]></category>

		<category><![CDATA[動態載入]]></category>

		<category><![CDATA[同步]]></category>

		<category><![CDATA[異步]]></category>

		<guid isPermaLink="false">http://techblog.westkit.net/?p=115</guid>
		<description><![CDATA[今天寫程式時遇到一個問題，我希望動態載入一個語言檔(因為不同使用者可能會使用不同的語言)，然而如果我異步地載入的話，可能語言檔未載入好就需要用了，那當然會出現runtime error。當然，我可以加入callback function，當語言檔載入好才執行callback function，那就可以解決runtime error，然而這樣寫的話程式碼會很不整潔，違反西杰寫程式的原則:)。
動態載入script檔，一般會使用的方法是利用DOM創造一個script node，然後把script node加入至head node之中，瀏覽器就會載入src的script檔。
var load = function (scriptName){
var script = document.createElement(&#8221;script&#8221;);
script.type = &#8220;text/javascript&#8221;;
script.src = scriptName;
document.getElementsByTagName(&#8221;head&#8221;)[0].appendChild(script);
}
在Google的第一個結果中就建議了使用這種方法來動態載入script檔，要加入callback function很簡單
var load = function (scriptName, callback){
var script = document.createElement(&#8221;script&#8221;);
script.type = &#8220;text/javascript&#8221;;
script.src = scriptName;
script.onload = callback;
document.getElementsByTagName(&#8221;head&#8221;)[0].appendChild(script);
}
那麼當載入script檔完成後就會執行callback的function了。然而，這種寫法會令我的程式加多很多不必要的程式碼，而且亦因為使用callback function，這會令到callback function中的程式碼改變了scope，也不是一個好的OOP編程風格，於是我就想到了要用同步載入script檔來解決此問題。
要同步載入script檔，我的方法是利用AJAX(XHR)來實現，正確來說是SJAX，也就是利用XHR來發出要求，再eval載入了的script檔。
var load = function (scriptName){
var req = new XMLHttpRequest;
req.open(&#8217;GET&#8217;,  scriptName, false);
req.send(null);
if (req.status == 200)
window.eval(req.responseText);
}
這個版本不支援I.E.，但是要改成I.E. compatible並不困難，主要就是差在XHR的Object而已。理念是利用XHR同步要求的特性，阻止javascript的執行，就可以解決需要的script檔比現行script檔遲載入以致的runtime error了。
]]></description>
		<wfw:commentRss>http://techblog.westkit.net/index.php/javascript/js-load-script-asynchronously/feed/</wfw:commentRss>
		</item>
		<item>
		<title>高登Core 更新 version: 08.12.30.16.1</title>
		<link>http://techblog.westkit.net/index.php/%e9%ab%98%e7%99%bb/goldencore-081230161/</link>
		<comments>http://techblog.westkit.net/index.php/%e9%ab%98%e7%99%bb/goldencore-081230161/#comments</comments>
		<pubDate>Tue, 30 Dec 2008 08:52:23 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[高登]]></category>

		<category><![CDATA[golden]]></category>

		<category><![CDATA[golden API]]></category>

		<category><![CDATA[golden core]]></category>

		<category><![CDATA[高登core]]></category>

		<guid isPermaLink="false">http://techblog.westkit.net/?p=113</guid>
		<description><![CDATA[高登Core API是連接高登討論區的核心引擎，高登+和高登RSS都是使用此引撉開發的。
新增特色︰

加入skip list，可以避免使用某部份伺服器
加入替換連結功能，可以將貼文內的高登討論區連結轉為自己伺服器的連結

下載連結︰http://hkg.westkit.net/goldenCore-latest.tar.gz
]]></description>
		<wfw:commentRss>http://techblog.westkit.net/index.php/%e9%ab%98%e7%99%bb/goldencore-081230161/feed/</wfw:commentRss>
		</item>
		<item>
		<title>圖片閃爍問題</title>
		<link>http://techblog.westkit.net/index.php/uncategorized/picture-flash-problem/</link>
		<comments>http://techblog.westkit.net/index.php/uncategorized/picture-flash-problem/#comments</comments>
		<pubDate>Sun, 28 Dec 2008 03:54:14 +0000</pubDate>
		<dc:creator>admin</dc:creator>
		
		<category><![CDATA[圖片]]></category>

		<category><![CDATA[圖片閃爍]]></category>

		<category><![CDATA[按鈕]]></category>

		<category><![CDATA[發光效果]]></category>

		<category><![CDATA[閃爍]]></category>

		<guid isPermaLink="false">http://techblog.westkit.net/?p=102</guid>
		<description><![CDATA[有些時候，我們不想用瀏覽器預設的按鈕，我們會自己繪畫自己的按鈕。如果我們希望當鼠標經過按鈕時按鈕會有發光的效果，就需要換一張圖片了。然而，引申的問題就是，瀏覽器不會預先載入該圖片，而是當瀏覽器需要該圖片時才會再向伺服器要求該圖片，當中會有一段時間差，這就是閃爍現象。這是一個不好的用戶體驗，突如其來的一閃很可能會嚇怕你的觀眾啊！
示範例子
例子中，上面是有閃爍問題的圖片，下面是解決了問題的圖片。
既然知道問題所在，要解決就變得十分簡單。如何讓瀏覽器預先載入該圖片呢？就是用img tag來載入，因為即使那張圖片是被設定為display:none，瀏覽器仍然會載入該圖片。可以參考西杰.NET的做法，用一個div層來包含所有的img tag，而div層本身就是設定為display:none，那麼就可以不影響界面之餘又能預載圖片了。由於預載了圖片，當鼠標經過按鈕時，瀏覽器就能夠從快取裏取出該圖片，時間差會縮短得肉眼看不出來，這就解決了閃爍問題了。
]]></description>
		<wfw:commentRss>http://techblog.westkit.net/index.php/uncategorized/picture-flash-problem/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
