實驗-用Google App Engine跑CodeIgniter
Tue, 12 May 2009 20:19:02 +0800怕標題太聳動,所以先聲明一下,我只是想讓CodeIgniter根目錄下的index.php會動。(怎麼在Google App Engine跑php,請參考前篇用Google App Engine跑php ,至於為何挑CodeIgniter?因為他架構比較簡單,似乎比較容易改...)
Google App Engine有一個限制,就是無法寫入檔案系統,這對於許多framework及php有很大殺傷力。尤其是cache機制,需要可以動態產生、更新cache,這樣在Google App Engine跑php就有很多問題。例如想要跑CodeIgniter,就會出現一堆錯誤訊息,主要來自無法寫入檔案。
Quercus有一個很方便的地方,就是可以自己寫QuercusModule,QuercusModule的公有方法就會成為php函數。使用自訂的函數,配合Google App Engine的datastore服務,就有可能把datastore當作檔案系統來使用。
先trace一下CodeIgniter,看看哪裡需要改動。看了一下原始碼,有幾個程式跟IO有關,其中會影響到index.php的我判斷有:
- 'system/codeigniter/Common.php'裡面的is_really_writable函數
- 'system/libraries/Output.php',這個是cache控制的主要程式。
- 'system/libraries/Log.php',log機制也有可能會寫入檔案系統
這裡面用到的filesystem functions有幾個:
- fopen
- fread
- fwrite
- fclose
- chmod
- file_exists
- flock
- is_dir
- is_writable
- unlink
package tw.idv.fillano;
import com.caucho.quercus.module.AbstractQuercusModule;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Blob;
public class GAEFileModule extends AbstractQuercusModule {
public Entity GAEfopen(String filePath, String mode) {
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
try {
Entity en = ds.get(KeyFactory.createKey("file", filePath));
return en;
}
catch(EntityNotFoundException e) {
Entity en = new Entity("file", filePath);
return en;
}
}
public boolean GAEfclose(Entity fd) {
return true;
}
public String GAEfread(Entity fd, int length) {
Blob b = (Blob)fd.getProperty("fileData");
byte[] sb = b.getBytes();
byte[] rb = new byte[length];
for(int i=0; i<length; i++) {
rb[i] = sb[i];
}
return new String(rb);
}
public int GAEfwrite(Entity fd, byte[] data, int length) {
fd.setProperty("fileData", new Blob(data));
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
ds.put(fd);
return data.length;
}
public boolean GAEflock(GAEFileData fd, int operation) {
return true;
}
public boolean GAEunlink(String filePath) {
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
ds.delete(KeyFactory.createKey("file", filePath));
return false;
}
public boolean GAEis_dir(String pathName) {
return false;
}
public boolean GAEfile_exists(String filePath) {
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
try {
@SuppressWarnings("unused")
Entity en = ds.get(KeyFactory.createKey("file", filePath));
return true;
}
catch(EntityNotFoundException e) {
return false;
}
}
public int GAEfilesize(String filePath) {
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
try {
Entity en = ds.get(KeyFactory.createKey("file", filePath));
Blob b = (Blob)en.getProperty("fileData");
return b.getBytes().length;
}
catch(EntityNotFoundException e) {
return 0;
}
}
public boolean GAEis_writable(String fileName) {
return true;
}
public boolean GAEchmod(String fileName, int mode) {
return true;
}
}
接下來把上述三個php程式裡面用到的這些函數,改成自己寫的GAE字首的函數,例如fopen->GAEfopen,然後跑一跑看看:
不過即使是這樣,距離真正可以在Google App Engine上跑CodeIgniter還差的遠,而且自己寫的這些替代方法也很簡陋。有空的話,再來試試讓CodeIgniter的ActiveRecord相關方法可以存取DataStore(不過不知道是否可行),但是還是要用JDO來定義資料就是了。
另外,其實使用datastore當作filesystem代價似乎有點昂貴,也許直接用一個in memory的cache比較好,有機會再試試。
(PS. java的程式是我一面看javadoc一面寫的,所以考慮不完整,別真的拿來用喔...)
