2009年10月27日 星期二

多重Singleton時的問題,與嘗試解決

一樣是寫遊戲時遇到的狀況。因為越來越複雜了,不寫下來搞不好過一段時間我就忘了。(關於Singleton是什麼我就不在這邊介紹了,請參閱「Design Pattern」,有中文版)

首先是一開始。我的遊戲需要以PHP提供的標準函式庫連接SQL資料庫,其中需要保管該次連接唯一的connection。最早是在最上層開一個connection,然後逐層傳下去。想也知道這樣缺乏效率(主要是每個function都需要有那麼一個傳入欄位),所以後來改的作法是採用Singleton。要的function自己去拿一份reference出來。

我的Singleton的實作方式是將reference以static member的形式存在class中,然後每次需要時就去拿該object的reference出來。PHP的object是在沒有人reference到他時才釋放。因此在這裡,該object的release是在全部程式碼執行完要釋放static member的Shutdown階段。



##ReadMore##
好了,SQL connection的問題解決,再來是SQL的table(跟row)。我把SQL各table取名為XxxxDB(xxxx是table名),例如:CharDB ,BattleDB。每個object代表一個row。construct的時候讀出資料存入row,destruct的時候檢查有哪些值被更新了,寫入資料庫後才結束。

這樣的作法運作正常,但有個問題:在多層的function call裡面,容易造成資料不同步的情形。比如說現在的狀況是,function a呼叫b然後b呼叫c。在a跟b跟c裡面都各自造出了CharDB的同一個row的object。c裡面那個object改動了數值,並在離開c的scope後寫入了資料庫。但a跟b裡面持有的還是舊的資料,並沒有跟資料庫同步到。

在這邊一開始要寫Singleton時遭遇到困難(後述),因此採用的是別的方法。那時最後的作法是當c的值更新時,他會去通知a跟b之後要記得取得新值。

這個做法彆扭了些,而且某些狀況下需要手動處理。(想一下上面的a,b,c的狀況,但是這次是a在呼叫b之前造出該object並改動了值。在這裡a還沒有到scope結束所以不會寫入資料庫,但不寫入資料庫b就得用錯誤的資料。因此,a得手動進行寫入)因此我又開始想Singleton的作法了。



在這邊講一下XxxxDB也用Singleton時的問題。如上所述,Singleton的object,釋放時機是Shutdown階段。可是我們現在用到了兩類用途不同的class。其中一個會用到另一個。XxxxDB在釋放時有可能會更新資料到資料庫,因此這時SQL connection不能被釋放。但是這兩個都是Singleton...

所以看來我們得控制Shutdown階段,release的順序了。有人可能想到,在XxxxDB的object裡面存放一個SQL connection的reference如何?嗯,我試過了,不過顯然Shutdown階段release時不會去管還有沒有reference。所以SQL connection還是有可能在XxxxDB之前釋放,失敗。另外,PHP沒有辦法讓你調整Shutdown階段釋放資源的順序。(我懷疑有哪個程式語言有)



後來我找到的作法是,標準函式庫的register_shutdown_function()。這個函式會讓你註冊一到數個function,並在Shutdown開始前依序呼叫它們。所以我利用這個函式,在Shutdown開始前把各XxxxDB來自static member的reference清掉。這樣,object的reference就斷光並開始自動release。XxxxDB在Shutdown開始前release完畢,SQL connection則在Shutdown時release。順序正確。



不過有那麼簡單我就不會想發這篇了。新的問題是出在一個名為BattleDB的class。因為Web Game的某些特性,單場戰鬥的資料不能只放在記憶體還得寫入資料庫,而這就是對應的class。

BattleDB的重點在於,為了操作方便,他存有兩個CharDB的object的reference。然後還有一個名為BattleAct的class。這是為了處理行動回數等資料方便而造出的class。每個BattleDB的object持有一個BattleAct的object。為了方便,每個BattleAct也同樣持有對應的BattleDB的object(的reference)。而在我將XxxxDB處理的部份改為Singleton後,BattleDB這邊動作出了問題。

我原本以為是CharDB的object的reference釋放上的問題,不過後來發現不是。重點在於BattleAct持有的BattleDB object reference。想像一下。原本BattleDB object是在Shutdown階段前,因為register_shutdown_function()的呼叫而切斷static member的reference。釋放。但是現在BattleAct還有它的reference。而BattleAct object本身的reference被BattleDB object持有,也不會自動釋放。所以變成得到Shutdown,釋放所有資源時才release。若這時SQL connection已經release,自然的問題就出現了。



問題的解法呢。目前的作法是每次BattleDB用完時就呼叫自訂函式Close(),在裡面把BattleAct釋放掉。幸好BattleDB使用到的時機挺單純的。不過當然了,這樣做彆扭又可能對未來的擴充造成影響,之後可能還是得改。一個做法是把BattleAct併回BattleDB內部,取消相互reference問題。

這次的教訓是:注意相互reference的dead lock...尤其在記憶體自動回收的程式語言中。

2009年5月31日 星期日

[書評]Web安全測試錦囊妙技

電腦書,題材如其名,出版社歐萊禮。

這本的主題是教你如何用各種免費軟體搞掛....噢不,測試Web應用程式。要注意的是,這本主要採用的是測試員的角度,也就是說「你沒有完整的Server端程式碼也沒有相關知識,也許你可以跟工程師要,但是不太可能太頻繁或詳細」的立場。如果你自己就是Web應用程式的撰寫者,會免去不少麻煩,但其中幾個也會不那麼需要做。當然,相關知識先有總是好的。

內容上,書中詳細的提到了各種基礎動作跟比較具體的攻擊法。更詳細的深入的話,書中有介紹網站跟書籍。

如果對Web安全議題沒有多少了解的話,這本相信是很不錯的入門書籍。

2009年5月21日 星期四

Web Security測試用工具

以下是從「Web安全測試錦囊妙計」第二章擷取出來的(歐萊禮出版,原書名Web Security Testing Cookbook)。目的是為了我不用每次都打網址...XD

下面列表我會寫上必要的註解,不過還是建議需要的話自己去看原書

工具列表:
  1. Firefox

  2. Fx Extension:
    1. View Source Chart (不適用Fx3,但有適用的實驗版本)
    2. Firebug
    3. Tamper Data
    4. Edit Cookies (適用於Fx3的是新版:Add N Edit Cookies)
    5. User Agent Switcher (不知為何裝起來沒作用...)
    6. SwitchProxy

  3. WebScarab (需要Java Runtime)

  4. Perl (Windows: ActivePerl)

  5. CAL9000
    注意:原作者有警告,不要把這東西放在網路Server上,請在本機端環境操作他

  6. ViewState Decoder

  7. cURL

  8. Pronzilla (這是一組而非一個工具)
    注意:這些是為了情色目的開發的工具,但只要你不針對該方向使用,工具本身事「純真無邪」的(書上就這麼寫XD)。
    1. RefSpoof: 修改HTTP Referer
    2. Digger: 目錄橫跨(Directory traversal)工具
    3. Spiderzilla: Website Spider
    4. Increment & Decrement: 竄改URL參數

  9. Cygwin (建議加裝Perl)

  10. Nikto 2 (需要Perl & Cygwin, 需要LibWhisker)

  11. Burp Suite (非完全免費,但免費的部份就有用)

  12. Apache HTTP Server

2009年5月12日 星期二

以索引的方式取用Smarty的config變數

我遇到的狀況是這樣。假設我在Smarty的config檔裡面寫了這樣的內容:
level1 = 等級一
level2 = 等級二
level3 = 等級三
一般來說在template裡面,我要呼叫上述內容的寫法是:
{#level1#}

問題來了,如果我現在要依照PHP檔輸入的內容動態的取用上述內容的話呢?
比如說,透過PHP檔內的動作,現在template裡有個變數$para,內容是"level1"。
我想用它來取得config檔內同名的變數的數值。

很直覺的想法會是
{#$para#}
之類的對吧?不過很可惜,Smarty不支援這種語法。
Smarty的config檔的變數(已讀入的)也會以$smarty->config->level1之類的方式儲存,
不過當然的,我也不能寫成$smarty->config->$para。

另一種想法是config裡面能不能直接寫陣列?
我自己嘗試不行,我也沒看到哪個範例可以的。

最後達成的作法是藉由template內嵌php程式碼。
將config的內容重組成template variables的陣列,傳入template中。
$ar = array('level1', 'level2', 'level3');
foreach($ar as $v)
{
$ar2[$v] = $this->get_config_vars($v);
}
$this->assign('ar', $ar2);
然後在template裡面使用。
$ar[$para]


雖然是相當拐彎抹角的作法,不過會比在template裡面寫一堆重複的程式碼好多了。

2009年5月5日 星期二

如何建立單機性質的SVN版本控管

單機性質SVN


版本控管以及備份的重要性應該不用多講了,而SVN有多麼優秀也不用多提。
只是一般聽到的SVN都是需要一個網路上的SVN Server,
那自己寫點小東西,沒有SVN Server又想要版本控管的話該怎麼辦呢?
這就是這篇文章的主題。建立一個個人的SVN版本控管。
基本上是參考這篇的,不過我會用中文口語的講出來。

步驟:
  1. 下載TortoiseSVN最新版。我是用Windows版
  2. 選擇你要拿來存放版本控管的內容的目錄(最好是空的),
    檔案管理員右鍵->TortoiseSVN->Create Repository here...
  3. 接下來就可以使用Repository Browser連到該目錄了,
    位址類似這樣:file:///Q:/THBRsvntest
  4. 在把該目錄作最初的整理後(比如trunk/branches/tags三分法),將初始的程式檔案import進來。
    在你要載入的資料夾上,檔案管理員右鍵->TortoiseSVN->Import...。
    注意該資料夾不會一併import,只有資料夾的內容會。
    還有,被你指定import的資料夾並不會因此就有版本控管。要再進行checkout才行
  5. 進入Repository Browser,對你要checkout的資料夾右鍵->Checkout...。
    注意一樣,只有資料夾內的內容會被checkout。
  6. checkout出來的資料已經有了版本控管了。剩下的跟一般TortoiseSVN用法相同。

以隨身碟做為存放Repository的裝置


上述作法我遇到的主要問題之一是,如果你在兩個以上的地方都有電腦而且都會寫程式,使用頻率還差不多怎麼辦?分別建立單機SVN是個辦法,但是版本控管不能統一。

另一種作法是,同樣分別建立單機SVN,但是建立在同一個隨身碟上。兩台(或以上)的電腦對同一個隨身碟做checkout跟commit,所以版本控管的來源只有一個。比較有問題的是隨身碟的代號可能每次放入時不一樣(已經插了其他裝置導致代號改變...之類的),每次都來一次Relocate又太麻煩了。我的作法是將隨身碟的代號改為一個較少用的代號,這樣就比較不容易被其他會插拔的裝置影響到。(更改代號可以從我的電腦->管理->磁碟管理裡面著手)

2009年4月24日 星期五

(要錢的)軟體開發工具小調查

UltraEdit:
49.95美金。目前v.15版。還沒仔細試過與129.95美金的UEStudio的差異....
PChome商店街Yahoo奇摩購物中心有代理版。

Visual Studio系列:
目前2008。最便宜的Standard版本似乎是10590台幣。(原價記得是300美金)
有免費的Express版,但不支援ATL跟MFC。想用到這些東西的話請買Standard版本。
不知道該說他黑心還是慷慨。

Visual Assist系列:
Whole Tomato Software出的,跟Visual Studio搭配提昇功能用的輔助套件。
正常可大量授權可續約的版本一套249美金,個人版本則是99美金。
在價格不怎麼便宜外,這套軟體不支援Express版。
(不過用習慣了就會覺得想要繼續使用...)

主要的功能....
  • 提示功能的加強。Visual Studio原本只會對class的method跟variable名作提示而已,裝了Visual Assist後打任何關鍵字都會自動提示。
  • Refatoring。諸如一次大量改名之類的....
  • 上色。把巨集/變數/常數等加以不同的上色。

2009年4月23日 星期四

Doxygen補充

原本主要是看Documenting the code頁面來寫相關註解,
不過發現有一些指令他沒有詳加解釋...就去找了command的完整頁面

個人來說比較有用的是下面的Section indicators的部份就是了...
諸如\param, \sa, \return, \retval之類的。

2009年4月1日 星期三

Doxygen 細項設定memo

把幾個重要的細項列出來....
這邊指的是Windows版的GUI的Wizard/Expert tab下面的項目。
不過其他版本應該很容易依樣畫葫蘆。

Wizard
  • Project
    • Scan recursively:讓他搜尋到子目錄

  • Mode
    • "Select the desired extraction mode:":除非已經做了完整的註解,否則All Entities才能列出足夠的資料
    • "Include cross-referenced source code in the output":勾起來則會把原始碼一起文件化並加上link。建議勾選

  • Output
    • HTML→"with frames and a navigation tree":會在左邊以frame建立索引連結

##ReadMore##
Expert
  • Project
    • CREATE_SUBDIRS:建立複數個目錄。怕同一目錄裡檔案太多的選項
    • INLINE_INHERITED_MEMB:將子類別從父類別那邊繼承來的method也列出。不包括constructor、destrcutor跟assignment operators
    • STRIP_FROM_PATH:將原本完整列出的檔案位置消去指定的path

  • Build
    • EXTRACT_PRIVATE:列出private成員
    • EXTRACT_STATIC:列出(C/C++)檔案的static內容
    • SHOW_DIRECTORIES:列出目錄結構。適合依照目錄分層的project

  • Source Browser
    • SOURCE_BROWSER:產生頁面詳列Source code。強烈建議。
    • INLINE_SOURCES:是否將原本只包含declaration的文件改成顯示完整的definition。注意資料太多反而會模糊焦點。建議不要。
    • REFERENCED_BY_RELATION:將用到此成員的method等列出。強烈建議
    • REFERENCES_RELATION:將此成員用到的method/variables等列出。強烈建議。
    • REFERENCES_LINK_SOURCE:若勾選,則上述兩個項目列出的連結連往Source code頁面,否則連往文件。看個人需求。

  • HTML
    • GENERATE_TREEVIEW:跟Wizard的"with frames and a navigation tree"同作用
    • TREEVIEW_WIDTH:上述TreeView的寬度

2009年3月26日 星期四

從程式碼自動生成說明文件 - Doxygen

官方網站:Doxygen

可以生成HTML、LaTeX、Man Pages、RTF跟XML,不過我只有用HTML。
支援C/C++/Java/C#/PHP/Fortran/VHDL,
會將你的程式碼讀入,生成簡明易懂的說明文件,
並將交互參照的部份自動建立連結。
當然,沒有註解的話它並不會自己幫你生成註解,註解還是得你自己寫 XD
註解該怎麼寫才會被讀到,請參考官網文件

不確定其他版本狀況,不過Windows版有一個有GUI的Wizard,
可以經過簡單的設定之後就生成文件出來。
如果想要自己詳細設定的話,Expert Tab裡也可以調。

以下註記一些比較需要注意或是容易忘記的memo...

  • Source code directory可以有複數個。要在Expert→Input→INPUT調。
  • 對於某些source code不能寫成UTF-8的project,可以用它的轉碼功能。
    • 像部分C/C++ compiler不吃UTF-8原始檔,要轉碼成UTF-8再生成文件又大費周章
    • 但要注意有些狀況轉碼會失敗....比如說,我從big5轉UTF-8,內含「/」好像就會失敗。不確定為什麼。
  • 如果要顯示Static function或private method/member,要到Expert → Build → EXTRACT_PRIVATE / EXTRACT_STATIC調。
  • 要顯示目錄(比如說,如果文件包括好幾個小project,彼此是用目錄來區分的),要到Expert → Build → EXTRACT_PRIVATE/SHOW_DIRECTORIES調。
  • 原本是設計給英文語系用的,所以一部份地方要注意一下。比如說,他會自動將class name/file name等辨識並加連結,但是如果這些跟中文連在一起的話就不會識別出來(英文的字是以空格分開的嘛),所以最好這類名稱前後都各加空格格開。

2009年3月22日 星期日

協助HTML/JavaScript/CSS開發的Firefox擴充套件

之前搞Firefox擴充套件開發的survey時找到幾個不錯的,
連同之前在用的一併列出。

DOM Inspector
觀察DOM tree用的套件。原本是Fx內建,Firefox 3還哪時候開始變成額外擴充。
(變額外擴充也好,因為原本內建但是要在安裝時特地選起來,很容易漏過)
雖然說底下要介紹的Firebug比他強大些....

Firebug
同樣是觀察DOM tree用的,不同的是它有簡單明顯的UI,
當游標移到tree的某條敘述上,對應的顯示的部份會以遮罩突顯出來,
所以能很快的找到自己要的部份的code的位置。
必備套件。

JavaScript Debugger
這套強大的地方在於,他是個可以逐步執行並下中斷點的JavaScript Debugger。
就這樣,不過也夠強悍了。

Web Developer
可以進行很多網頁細部設定。比如說關掉Javascript、對Cookie的操縱、取得JavaScript內容等等。
很多項目不會很常用,不過需要時就會覺得蠻好用的。

Console²
將錯誤主控台的UI稍稍改裝。
老實講改的不多(而且稍微有點醜....),不過有比沒有好。

Extension Developer
很多輔助Extension開發用的功能,不過也有助於網頁開發。
比如說Extension的包裝、即時的Javascript command line、
即時的HTML Editor和Regular Expression Evaluator。

2009年3月19日 星期四

Firefox Add-on撰寫memo (2)

逐步建立Extension的教學
很好用所以重貼一遍。注意裡面只有講到XUL。整合JavaScript和CSS的部份請自己另外找。

另一個教學

XUL

XUL Explorer
輔助開發XUL的工具

XUL Overlays
講解XUL Overlays(整理大大小小XUL整合成UI的概念)

JavaScript的window.dump

Extension常用的程式碼片段

XPCOM

Plugins

2009年3月17日 星期二

Firefox Add-on撰寫memo (1)

因為工作有點相關所以順便survey。
東西太多了,不記點memo再看頭會痛....

官方相關主頁面

逐步建立Extension的教學

建立一個適合撰寫Extension的環境

Install.rdf的細節

XUL教學

iGoogle與Gadget (3)

因為工作關係大致試寫了一兩個純測試用的Gadget。
測試出來的重點大致如下:

  • Google提供給Gadget的API多半是介面相關的,也就是說如何控制Gadget呈現給使用者的樣貌。
  • Remote取得資訊的方式基本上是HTTP中的GET。POST跟https基本上不行(也許有辦法技術性躲開)。不可能另開socket。
  • 顯示「需要登入」的資訊會有問題。比如說,我想要用Gadget顯示pixiv的內容,所以事先在pixiv登入,然後把pixiv內嵌到Gadget中希望能看到我的pixiv頁面,但是失敗。失敗的原因其實也很簡單:因為不能跨domain存取cookie。這會造成安全性問題。

    目前想到的解法都不怎麼有效,因為使用者得讓Gadget「轉交」帳號密碼,會引發盜帳號的疑慮....。這應該是Web widget共通的限制了。無解。

    唯一能避開這個問題的只有平台提供者本身的widget,因為沒有domain問題-在iGoogle平台上就是Google自己。所以Google Reader跟GMail的內嵌能運作正常。

2009年3月10日 星期二

iGoogle與Gadget (2)

因為這樣跟那樣所以又研究了一下。算是暫時memo。

提交Google Gadget
提交gadget的網頁,有提醒最好有哪些東西,還有相關條款。

將 Google 小工具新增到您的網頁上
用JavaScript將gadget內嵌到網頁上。
可以在網頁上使用原本放在iGoogle的gadget。

Gadgets API Reference
往下拉是Google提供的JavaScript API

##ReadMore##

Google Gadget的相關條款:
  1. Google Gadgets and Themes API Terms of Service
  2. Google Gadget內嵌到網頁上時的服務條款
  3. Google官方Gadget內嵌到網頁上時的使用限制
第二點的重點歸結(個人memo用,要正式使用請去上述網址自己確認):
  • Gadget可商業使用,但需符合Gadget設計者的使用條款以及法律限制(Google官方寫的Gadget的使用條款即上面的第三點)
  • 不可移除「gadgets powered by Google」字樣以及「+Google」按鈕。如果你條列出眾多Gadget你必須註明來源。Google有權將「+Google」按鈕置換成廣告或其他東西。
  • Google不為Gadget做背書。製作Gadget的第三方有權隨時停止Gadget
  • 如果該小工具違反著作權法或其他法律,你同意將它移除。不管是你自己發現或Google如此告知你。
  • Google可隨時修改此條款。Google有權因為你違反使用條款或Google自行做的判斷來隨時停止此服務。
第三點的重點歸結:
  • Google News, Google Video跟Youtube的官方Gadget不得使用在商業網站
  • Google Maps的官方Gadget不能使用在需要付費才能閱讀網站內容的網站

2009年3月4日 星期三

PHP的小小抽象滲漏 2

最近有點累所以之前想寫的東西一直沒上來。先來寫個小的。

延續上次的抽象滲漏。一樣是在__get跟__set上的問題。
通常來說,你可以透過傳參考來讓function回傳內容,
比如說,function a(&$b, &$c)。
在a裡面把$b跟$c賦值,值就會存到外面(傳入)的變數裡面。

不過問題來了。使用__set跟__get「造」出來的member這樣做會有問題。
而且有些時候連警告訊息都不會出現。

聽起來很理所當然嗎?知道原理的話當然是啊。
不過光看表面的code有時不易分辨耶。

只是小小紀錄。

2009年2月23日 星期一

用C/C++加入/退出Multicast Group的方法

在VLC裡面找這部份,看了wikipedia就往IGMP的方向找,結果證實是浪費時間... orz

總而言之,參考這裡。簡單的講,加入Multicast Group是用setsockopt()搭配IP_ADD_MEMBERSHIP這個參數。範例請參考上述網頁。離開則是IP_DROP_MEMBERSHIP

其他還有IP_MULTICAST_IF, IP_MULTICAST_TTL, IP_MULTICAST_LOOP等。不過因為我只是要找出VLC哪部份在弄Multicast,所以就沒去詳細嘗試這些的功用是什麼了。

Blog顯示程式碼

貼了程式碼才發現上次改網誌主題,所以之前編的CSS跑掉了....
用"blog 程式碼"之類的關鍵字去找結果找到一堆沒處理好縮排的,
千辛萬苦才找回以前參考用的這個

現在的做法是,在CSS裡面加下面這段:
pre.code  {
display: block;
font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace;
color: #000000;
background-color: #eee;
font-size: 12px;
border: 1px dashed #999999;
line-height: 14px;
padding: 5px;
overflow: auto;
width: 100%
}

然後程式碼就前面加上<pre class=code><code>
後面加上
</pre></code>這樣。

C語言: 讀出";"結尾的字串

while( sscanf(pStr, "%[^;]", scan)==1 )
{
pStr += strlen(scan);
while(*pStr == ';')
pStr++;
printf("%s\n", scan);
}

sscanf(..., "%[^;]", ....)這個用法其實很多地方有寫是有寫,
可是寫的很隱密....。

從一件小事看iPod nano的歌曲星等實作機制

我平常在外聽音樂用的是iPod nano。用了四年以上的古董了。
(不過小小一台很方便,雖然偶爾有點秀抖,電池也開始衰退了)

平常聽歌都是編輯一個「某某歌手多少星等以上」的Smart playlist,
不過這樣就得歌曲要給星等。最近新增還沒給星等的歌曲又有點多,
就想說先來排序(用自己寫的簡單javascript),然後看哪個區間要給星等。

所以排序完。把原本有給星等的歌通通取消星等。
依照排序的結果一個一個給分。嗯,這邊五分....這裡開始四分....以下略。

今天早上要聽歌的時候打開同樣的Smart playlist發現,
咦,歌曲的列表怎麼是照我給分的順序?
##ReadMore##

理論上如果歌曲是以資料庫的方式,每首歌給一個row來存的話是不太可能會這樣的。
給五星就是五星,五星的歌曲間不會有任何區分,先給後給都一樣。
資料庫本身不會記憶被給星等的時間。

我最後的結論是,他們星等是另外維持一個列表,
表上列出哪些歌是五星,哪些歌是四星,依此類推。
由於這樣你把歌曲給星等時,不是像資料庫一樣分數被「改」動而是被「加」上去的,
所以會有加上去的先後順序。會依照加上去的順序列出歌曲。

至於為什麼會這樣我就不知道了。
也許因為給星等的歌通常不多,避免佔據空間?(可是一首挺多1Byte耶)
也許這樣比較快?或者只是因為這是四年多前的iPod了,現在已經改掉了。

不過這樣的排序方式對我來說也方便就是了 :p

2009年2月22日 星期日

Mysql的Truncate與Lock

只是小小附註。lock tables跟truncate table不能一起用...。
當你lock某個table,你就不能truncate他。

某些狀況下這有點麻煩....

2009年2月19日 星期四

PHP的小小抽象滲漏

關於什麼是抽象滲漏請看這裡

1.Firefox的自動reload


在未指定字碼而且實際字碼與Firefox的預設不同時,
Firefox會先讀入一次,判斷目前所使用的字碼不對,
然後再跟Server要一次資料。所以PHP之類的CGI會被執行兩次。
所以,網頁請寫好Content-Type的header。
(什麼,你說這不是理所當然的嗎?
嗯,的確是,不過我現在程式還在寫內部部分還沒要處理UI,
這部份隨便寫寫結果就中招了。)

順帶一提IE不會這樣。他會直接用亂碼印出來。


2.empty的抽象滲漏


##ReadMore##
PHP的empty()只能拿來判斷變數。
如果你拿它來判斷函數回傳,他會給你一個錯誤訊息。
到這邊都很正常。

不過PHP 5.x開始多了個類別的method,叫__set跟__get。
__get的功用是他會在找不到member時以這個method去找資料。
比如說如果變數實際上沒有$t->num,他就會去呼叫$t->__get('num')。

實際上來說,這兩個就只是語法上的甜頭就是了,
因為一般使用上可以自己寫兩個函式,比如說Set()跟Get()來做同樣事,效果一樣。
好處只是,你可以寫$t->num++而不是$t->Set('num', $t->Get('num')+1)。

問題出在於,當你empty($t->num)時。
假設$t->str會回傳字串,而你想要判斷這是不是個空字串,
他不會照你預期的回傳,也不會有錯誤訊息。
就算字串有東西,他會照樣回傳true。
而你就字面上看,empty($t->str)怎麼看都沒有問題。

順帶一提strcmp($t->str, "")===0也一樣會出錯。
我最後成功時是使用strlen($t->str)===0。

供參考。

2009年2月17日 星期二

iGoogle與Gadget

又是一個暫時不會用到的東西....

總而言之研究了一下Gadget。就iGoogle上那堆小工具。
大致上的結論是:
  1. 是XML、HTML跟JavaScript混成。另外有gadget特有的API。
  2. 起頭教學
  3. 原始檔要自己找Server存。沒有的話,上面頁面提供的Google Gadgets Editor可以讓你存在Google的空間。
  4. 可以加點developer tool來協助開發。雖然實際上裡面有用的只有一個,可以拿來看別人的Gadget的程式碼
  5. 完整API列表在
看起來這現在跟OpenSocial API整合了...。暫時不關我事就是了。
(除非哪天mixi、plurk或pixiv提供足夠的支援)

網頁的檔案上傳(以及php的處理與驗證)

因為這樣跟那樣所以去查了。暫時用不著,先記起來。

這裡的範例一有HTML的示範,範例二則是PHP的部份。
檢查$_FILES['userfile']['type']可以確認副檔名,
不過僅只是副檔名,沒有分析檔案內容,所以參考就好。
順帶一提,照範例程式的話,重複上傳同名檔案後面會蓋到前面的。

分析檔案內容的話,好像有個mime_content_type
不過官網不建議。建議使用的Fileinfo則是要PHP5.3.0版。
在更早的版本是算PECL的一部分的樣子。

2009年2月16日 星期一

Web Game: 該lock全部或是部份的table

*以下是個人寫Web Game中途產生的心得。
很有可能不適用於其他類型的應用程式,請注意。

話說從頭


包括Web Game在內的多人網路遊戲都需要對資料庫或檔案進行鎖定(lock)。
鎖定的目的我原本寫了個範例出來,不過這樣這篇文章會太長所以算了吧。
抽象一點的舉例,就像是在白板上留言,要一個人寫完才能輪別人,否則留言就會看不懂。
多人遊戲時大家也得排隊輪著讀寫資料庫(雖然這動作時間很短所以看起來像大家同時)。

今天要講的是,一次該鎖定多少?

##ReadMore##

資料庫通常會起碼提供你一次最低可鎖定到一個table。
假設現在有三個table分別代表角色(char),地區(area)跟組隊(team)。
你可以鎖定它們其中之一,其中兩個或全部。

直覺的想法是,需要幾個就鎖定幾個。
如果自己暫時不會用到,就給別人用啊。
假設A鎖定了char跟area,B鎖定了team。
A跟B就可以同時動作而不會互斥。
效率比雙方都一次全部鎖定,B要等A做完要來的好。

理論上如此,而且其他應用程式很可能是對的,但遊戲不是。

因為沒有漸進式鎖定


首先原因是,我不確定其他資料庫,不過Mysql的lock是一次性的。
你不能先lock char,然後跟資料庫講我要多lock area。
這樣做會把char解鎖才去鎖定area,而不是你所想的鎖定了兩個table。

事實上這是很合理的,想一想下面這個情況:
A鎖定了char跟area而B鎖定了team。
A想要多鎖定team,B想要多鎖定char。
要不就雙方都拿不到(因為在對方那邊,而且對方不會放手),
要不就資料庫提供更複雜的機制解決這問題。

所以你不能漸進式鎖定。你在發出lock指令時就得指定好所有的table。

可是寫遊戲,通常資料庫存取動作並沒有那麼單純。
要在開頭就完全確定這裡只會用到某些table是很困難的。
有可能今天你要加某個功能,所以這個角色動作就得多使用到一個table。

更糟一點的狀況是,那個功能隱藏在好幾層函式呼叫的下面。
你得一層一層爬上去,找到到底誰呼叫了那個函式。
如果你每次做這個動作就得往上爬個四層尋找會呼叫它的一百個地方,
改個幾次你大概就投降了。

效率問題?


更重要的一點是,全部一次鎖定其實也沒什麼效率問題。

這邊的重點是,這是遊戲,而且還是Web Game。
遊戲的重點是玩家的角色。Web Game的動作則都是由玩家觸發的。
也就是說,90%以上的鎖定都一定會包括角色(char)的table
既然大家都要用到同一個table,那部份鎖定跟全部鎖定是一樣的
-不管怎樣都要排隊。

而且一次全部鎖定,也可以讓你不用去思索什麼時候該開始鎖定-
每個指令執行期間全部鎖起來就對了。
不用去看哪些動作有用到資料庫哪些沒有,省掉coding時考慮這些的麻煩,
可以更直接而輕鬆的使用資料庫。

所以我的建議是,如果你是要寫遊戲的話,就全部鎖定吧。
coding會方便很多,又不會真的犧牲多少效率。