Sunday, November 8, 2009

Solution for "Can't update table in stored function/trigger because it is already used by statement which invoked this stored function/trigger"

I try to create a mysql trigger for testing:

DELIMITER $$

DROP TRIGGER IF EXISTS change_password $$

CREATE TRIGGER change_password
AFTER INSERT ON user
FOR EACH ROW
BEGIN
UPDATE user SET password = 'newpassword' WHERE id = NEW.id;
END
$$

DELIMITER ;

And then i do an insertion: "INSERT INTO user SET username='alice', password='changeme'"; I got this error:
"Can't update table user in stored function/trigger because it is already used by statement which invoked this stored function/trigger".

I search online and found a solution here: http://crazytoon.com/2008/03/03/mysql-error-1442-hy000-cant-update-table-t1-in-stored-functiontrigger-because-it-is-already-used-by-statement-which-invoked-this-stored-functiontrigger/

The point is, if you want to create a trigger on the table which will update itself, you must use the NEW.column_name to refer to the row. So i modify my trigger as:

DELIMITER $$

DROP TRIGGER IF EXISTS change_password $$

CREATE TRIGGER change_password
AFTER INSERT ON user
FOR EACH ROW
BEGIN
SET NEW.password = 'newpassword';
END
$$

DELIMITER ;

This time i got another error when i create this trigger: "Updating of NEW row is not allowed in after trigger". So i change the trigger to:
DELIMITER $$

DROP TRIGGER IF EXISTS change_password $$

CREATE TRIGGER change_password
BEFORE INSERT ON user
FOR EACH ROW
BEGIN
SET NEW.password = 'newpassword';
END
$$

DELIMITER ;

The trigger is created successfully and being executed when i run the insert sql statement again.

Wednesday, August 5, 2009

外汇学习

最近开始对炒外汇感兴趣, 先来说说为什么要炒外汇, 也就是炒外汇的好处吧. (一开始就说风险不把别人吓跑才怪) 以下都是从网上收集来的:

1. 交易成本低,无需佣金
外汇投资者无需通过代理机构来进行交易,因此不会被收取交易佣金。外汇交易商的利润仅从报价点差中来。如 EUR/USD 1.4225/1.4227,其中仅有的 2 个点的点差就是交易者的成本。股票交易通常会收取 $7 至 $20 不等的交易佣金(也有根据交易量大小计算费用),买进卖出一个来回,佣金将被收取两次。相对而言,外汇的交易成本要小得多。

2. 无论熊市牛市, 获利机会均等
在股市中,投资者如果想卖空(Short)利用熊市来获利,将面对诸多限制。如较高的保证金要求 (通常是卖空仓位市值的 150% 左右)、提高报价规则(uptick rule)以及借贷成本等。而外汇的做空机制非常灵活,没有任何限制,外汇交易者能自由地借助市场的涨势和跌势来投资获利。

3. 24小时全球交易
外汇市场是一个 24 小时永不停滞的全球市场,交易者可以根据自己的生活习惯安排交易时间。这也是为什么众多上班族选择炒汇的原因之一。同时,越来越多的人开始利用股市休市的时间交易外汇,把其当作一项分散投资风险的有效渠道。

4. 高至200:1的杠杆(据个人了解外汇杠杆可以高达400:1甚至500:1)
外汇交易中提供的杠杆比率通常是股票交易的 100 倍。比如,在股市中投资者能用 $1,000 来买价值 $2,000 的股票,而通过高达 200:1 的杠杆,外汇交易者可以用同样的 $1,000 来获得 $200,000 的购买力。显然,外汇交易以小博大的效力,是远胜于股票的。

当然,我们必须认识到,杠杆比率是一把双刃剑,它能使投资者迅速获利,同时也很容易增加风险。

5. 瞬间交割无限制
外汇市场是短线投资者的乐园,因为交易者能在一日内自由进出场,瞬间即可成交,随时兑现盈利。而股票交易者若想进行短线交易(Day Trading),将面对诸多限制,如交易者必须满足 $25,000 的最低保证金要求等。

6. 8000支股票 VS 7种主要货币
纽约证交所和那斯达克两所股市中有 8000 支以上的股票,面对繁多的市场杂讯,选股成为一件令投资者头痛的事。而在外汇市场中,7 种主要货币集中了 85% 的交易量,这使交易者能更快选择投资对象,节约时间和资金。

refactoring and performance

Many programmers always put performance at the first place in development. However, in my opinion, performance should be the last and least thing we need to worry about. Let's have a look at what Martin Fowler says in his book - Refactoring.

"To make the software easier to understand, you often make changes that will cause the program to run more slowly...but it also makes the software more amenable to performance tuning. The secret to fast software...is to write tunable software first and then to tune it for sufficient speed."

"Changes that improve performance usually make the program harder to work with"

"Build your programm in a well-factored manner without paying attention to performance until you begin a performance optimization stage, usually fairly late in development."

"A well-factored program ... gives you time to spend on performance tunning... Because the code is clearer, you have better understanding of your options and of what kind of tuning will work."

I always believe the first task for developer is to make the software easy to maintain, extend, modify. It also means we should focus more on architecture, structure, design, and coding style.

Unfortunately most of performance-first developers haven't realized that their performance achieved coding creates buggy, hard to understand and maintain system and finally achieves nothing but lost.

Why do we not just use assembly language for programming? We can get better performance than using C++, Java, PHP.

Why do we prefer OOP instead of coding in native language way? Native PHP language code runs faster than OOP code with those classes, objects.

Why do we break code into smaller pieces and functions? Putting everything in one function can save a lot of time than calling other functions and then return to the caller.

Why do we use template engine? Why not just embed PHP into HTML? It is faster.

Why do we use framework? It absolutely runs slower than a index.php.

Why do we use java? Every performance oriented programmer is crying Java is slow.

Thursday, July 2, 2009

中国试行社会养老

中国终于要实行社会养老了:http://news.sohu.com/20090702/n264931053.shtml

先在部分地方试行,然后覆盖全国.实行这项制度的意义是不言而喻的.养儿防老在中国农民家庭中是根深蒂固的.这也是为什么很多农民家庭一定要生儿子,生男孩,生很多男孩的原因之一(另一个原因是要增加劳动力).这不能怪他们,因为中国农村生产力低下,而社会又不能为农民的老年提供保障.他们就只能指靠儿子了.

实行社会养老是一个重大改变.是完善社会保障制度的一个重要举措.虽然实施起来肯定会遇上不少困难,但一定要坚持下去,真正做到老有所养.

Monday, June 29, 2009

Convert Zend Framework project to UTF-8

Here is an article about converting Zend Framework project to UTF-8:

http://www.iezzi.ch/archives/371

another post of PHP performance

http://php100.wordpress.com/2009/06/26/php-performance-google/

PHP performance tips from Google

Posted by Stas on June 26, 2009

I saw a link on twitter referring to PHP optimization advice from Google. There are a bunch of advices there, some of them are quite sound, if not new – like use latest versions if possible, profile your code, cache whatever can be cached, etc. Some are of doubtful value – like the output buffering one, which could be useful in some situations but do nothing or be worse in others, and if you’re a beginner generally it’s better for you to leave it alone until you’ve solved the real performance problems.

However some of the advices make no sense at best and are potentially harmful at worst. Let’s get to it:

First one: Don’t copy variables for no reason. I don’t know what the author intended to describe there, but PHP engine is refcounting copy-on-write, and there’s absolutely no copying going on when assigning variables as they described it:
$description = strip_tags($_POST['description']);
echo $description;

I don’t know where it comes from but it’s just not so, unless maybe in some prehistoric version of PHP. Which means unless you’re going back to 1997 in a time machine this advice is no good for you.

Next one: Avoid doing SQL queries within a loop. This actually might make sense in some situations, however the code examples they give there is missing one important detail that makes it potentially harmful for beginners (see if you can spot it):
$userData = [];
foreach ($userList as $user) {
$userData[] = '("' . $user['first_name'] . '", "' . $user['last_name'] . '")';
}
$query = 'INSERT INTO users (first_name,last_name) VALUES' . implode(',', $userData);
mysql_query($query);

Please repeat after me – DO NOT INSERT USER DATA INTO SQL WITHOUT SANITIZING IT!
Of course, I can not know that $user was not sanitized. Maybe the intent was that it was. But if you give such example and target beginners, you should say so explicitly, every time! People tend to copy/paste examples, and then you get SQL injection in a government site.

Another thing: most of real-life PHP applications usually do not insert data in bulk, except for some very special scenarios (bulk data imports, etc.) – so actually in most cases one would be better off using PDO and prepared statements. Or some higher-level frameworks which will do it for you. But if you roll your own SQL – sanitize the data! This is much more important than any performance tricks.

Next one: Use single-quotes for long strings. PHP code is parsed and compiled, and any possible difference in speed between parsing “” and ” is really negligible unless you operate with hundreds of megabyte-size strings embedded in your code. If you do so, your quotes probably aren’t where you should start optimizing. And of course, using caching (see below) eliminates this difference altogether.

Next one: Use switch/case instead of if/else. This makes no sense since switch does essentially the same things as if’s do. See for yourself, here is the “if” code:
0 2 A(0) = FETCH_R(C("_POST")) [global]
1 2 A(1) = FETCH_DIM_R(A(0), C("action")) [Standard]
2 2 T(2) = IS_EQUAL(A(1), C("add"))
3 2 JMPZ(T(2), 7)
4 3 INIT_FCALL_BY_NAME(function_table, C("addUser"))
5 3 Au(3) = DO_FCALL_BY_NAME() [0 arguments]
6 4 JMP(16)
7 4 A(4) = FETCH_R(C("_POST")) [global]
8 4 A(5) = FETCH_DIM_R(A(4), C("action")) [Standard]
9 4 T(6) = IS_EQUAL(A(5), C("delete"))
10 4 JMPZ(T(6), 14)
11 5 INIT_FCALL_BY_NAME(function_table, C("deleteUser"))
12 5 Au(7) = DO_FCALL_BY_NAME() [0 arguments]
13 6 JMP(16)
14 7 INIT_FCALL_BY_NAME(function_table, C("defaultAction"))
15 7 Au(8) = DO_FCALL_BY_NAME() [0 arguments]
16 9 RETURN(C(1))
17 9 HANDLE_EXCEPTION()

Here is the “switch” code:
0 2 A(0) = FETCH_R(C("_POST")) [global]
1 2 A(1) = FETCH_DIM_R(A(0), C("action")) [Standard]
2 3 T(2) = CASE(A(1), C("add"))
3 3 JMPZ(T(2), 8 )
4 4 INIT_FCALL_BY_NAME(function_table, C("addUser"))
5 4 Au(3) = DO_FCALL_BY_NAME() [0 arguments]
6 5 BRK(0, C(1))
7 6 JMP(10)
8 6 T(2) = CASE(A(1), C("delete"))
9 6 JMPZ(T(2), 14)
10 7 INIT_FCALL_BY_NAME(function_table, C("deleteUser"))
11 7 Au(4) = DO_FCALL_BY_NAME() [0 arguments]
12 8 BRK(0, C(1))
13 9 JMP(15)
14 9 JMP(19)
15 10 INIT_FCALL_BY_NAME(function_table, C("defaultAction"))
16 10 Au(5) = DO_FCALL_BY_NAME() [0 arguments]
17 11 BRK(0, C(1))
18 12 JMP(20)
19 12 JMP(15)
20 12 SWITCH_FREE(A(1))
21 13 RETURN(C(1))
22 13 HANDLE_EXCEPTION()
No. CONT BRK Parent
0 20 20 -1

You can see there’s a little difference – the latter has CASE/BRK opcodes, which act more or less like IS_EQUAL and JMP, but their plumbing is a bit different, but in general, code is the same (you could even argue “switch” code is a bit less optimal, but that is really the area you shouldn’t be concerned with before you can read and understand the code in zend_vm_def.h – which is not exactly a beginner stuff.

Another thing that the author absolutely failed to mention and which should be one of the very first things anybody who cares about performance should do – is to use a bytecode cache. There are plenty of free ones (shameless plug: Zend Server CE includes one of them – all the performance improvements for $0 and you don’t have to change a bit of code to run it.

Now, I understand Google is not a PHP shop like Yahoo or Facebook or many others. But this article is signed “Eric Higgins, Google Webmaster” and one would expect something much more sound from such source. And in fact there are a lot of blogs and conference talks on the topic and lots of community folks around that I am sure would be ready to help with such article – I wonder why wasn’t it done? Why apparently the best advice we can find from Google is either trivial or useless or wrong?

I think they can do much better, and they should if they take “making the web faster” seriously.

Monday, June 15, 2009

facebook username

I got my facebook username: hengrui.li, so now u can reach me at http://www.facebook.com/hengrui.li

Friday, June 12, 2009

vim中大小写转化

vim中大小写转化的命令是
gu或者gU
形象一点的解释就是小u意味着转为小写;大U意味着转为大写.

剩下的就是对这两个命令的限定(限定操作的行,字母,单词)等等

1、整篇文章大写转化为小写
打开文件后,无须进入命令行模式。键入:ggguG

解释一下:ggguG分作三段gg gu G
gg=光标到文件第一个字符
gu=把选定范围全部小写
G=到文件结束
2、整篇文章小写转化为大写
打开文件后,无须进入命令行模式。键入:gggUG

解释一下:gggUG分作三段gg gU G
gg=光标到文件第一个字符
gU=把选定范围全部大写
G=到文件结束
3、只转化某个单词
guw 、gue
gUw、gUe
这样,光标后面的单词便会进行大小写转换
想转换5个单词的命令如下:
gu5w、gu5e
gU5w、gU5e
4、转换几行的大小写
将光标定位到想转换的行上,键入:1gU 从光标所在行 往下一行都进行小写到大写的转换
10gU,则进行11行小写到大写的转换
以此类推,就出现其他的大小写转换命令
gU0 :从光标所在位置到行首,都变为大写
gU$ :从光标所在位置到行尾,都变为大写
gUG :从光标所在位置到文章最后一个字符,都变为大写
gU1G :从光标所在位置到文章第一个字符,都变为大写

Wednesday, June 10, 2009

naming submit button "submit" break things

The form element has a method named submit, but also has the form elements in the form as members.

If you have a button in the form named submit, you could access it using document.form1.submit. However, as that is the same name as the submit method, there is no longer any way of accessing that method. If you use the method to submit the form, that will no longer work.

For example, if you have a button that submits the form using Javascript, that doesn't work:

< input type="button" name="submit" onclick="this.form.submit();" value="try" />

When the button tries to use the submit method, it will instead get a reference to itself (and an error message when trying to call it, as the button is not a function).

Monday, June 8, 2009

install svn on windows

Setting up a Subversion Server under Windows

I talked a little bit about what Subversion is in my previous post. Now, let's get it set it up in Windows.

Luckily for us, Joe White has done most of the work already; he has a tremendously helpful post that documents how to set up Subversion. I'll see if I can streamline his excellent post a bit further, and illustrate it with screenshots.

A) Download Subversion

You'll need the latest version of..

* the Windows binaries
* the "run Subversion as a windows service" wrapper
* the TortoiseSVN shell integration utility

B) Install Subversion

1. Unzip the Windows binaries to a folder of your choice. I chose c:\program files\subversion\ as my path.
2. Now, add the subversion binaries to the path environment variable for the machine. I used %programfiles%\subversion\bin\

3. You'll also need another environment variable, SVN_EDITOR, set to the text editor of your choice. I used c:\windows\notepad.exe

C) Create a Repository

1. Open a command prompt and type

svnadmin create "c:\Documents and Settings\Subversion Repository"

2. Navigate to the folder we just created. Within that folder, uncomment the following lines in the /conf/svnserve.conf file:

[general]
anon-access = read
auth-access = write
password-db = passwd

Next, uncomment these lines in the /conf/passwd file:

[users]
harry = harryssecret
sally = sallyssecret

D) Verify that everything is working

1. Start the subversion server by issuing this command in the command window:

svnserve --daemon --root "C:\Documents and Settings\Subversion Repository"

2.

Create a project by opening a second command window and entering this command:

svn mkdir svn://localhost/myproject

It's a standard Subversion convention to have three folders at the root of a project:

/trunk
/branches
/tags

3.

At this point, Notepad should launch:

Enter any comment you want at the top of the file, then save and exit.

4.

You'll now be prompted for credentials. In my case I was prompted for the administrator credentials as well:

Authentication realm: 0f1a8b11-d50b-344d-9dc7-0d9ba12e22df
Password for 'Administrator': *********
Authentication realm: 0f1a8b11-d50b-344d-9dc7-0d9ba12e22df
Username: sally
Password for 'sally': ************

Committed revision 1.

Congratulations! You just checked a change into Subversion!

E) Start the server as a service

1. Stop the existing command window that's running svnserve by pressing CTRL+C.
2. Copy the file SVNService.exe from the zip file of the same name to the subversion\bin folder.
3. Install the service by issuing the following commands:

svnservice -install --daemon --root "C:\Documents and Settings\Subversion Repository"
sc config svnservice start= auto
net start svnservice

4. Test the new service by listing all the files in the repository:

svn ls svn://localhost/

You should see the single project we created earlier, myproject/

F) Set up the shell extension

1. Run the TortoiseSVN installer. It will tell you to restart, but you don't need to.
2. Create a project folder somewhere on your hard drive. Right click in that folder and select "SVN Checkout..."



type svn://localhost/myproject/ for the repository URL and click OK.


3. Create a new file in that directory. Right click the file and select "TortoiseSVN, Add"


4. The file hasn't actually been checked in yet. Subversion batches any changes and commits them as one atomic operation. To send all your changes to the server, right click and select "SVN Commit":



And we're done! You now have a networked Subversion server and client set up on your machine. Note that the default port for svnserve is 3690.


CHINESE VERSION:

SubVersion服务器Windows安装指南
SubVersion及TortoiseSVN下载请到其官方网站:http://subversion.tigris.org
注:如果要使用TortoiseSVN中文界面,还要下载其语言安装包. 若只在本地计算机上使用,不使用SubVersion服务,则只需安装TortoiseSVN相关组件即可.

一.下载文件列表:

1.SubVersion 点击这里下载,版本是1.3.0,SubVersion服务程序.

2.TortoiseSVN 点击这里下载,版本是1.3.2,SubVersion客户端程序.

3.TortoiseSVN 中文语言包 点击这里下载,版本与上面的匹配.

4.SVNService 点击这里下载,可以将SubVersion服务包装成Windows NT服务.

二.安装步骤

1.安装SubVersion

2.安装TortoiseSVN及语言包

3.配置SubVersion服务

SubVersion本身是由命令行启动的,在SubVersion安装目录中找到,它不能像Windows NT服务一样随Windows启动,需要借SVNService来包装成Windows NT服务.具体设置如下:

将下载的SVNService解包后,将其中的拷贝到SubVersion的安装目录中的\bin目录, 保证SVNService.exe和svnserve.exe在同一目录.

安装服务:
运行命令:SVNService -install -d -r
在实际操作中,代表SubVersion服务的电子仓库目录.

改变服务设置:
运行命令:SVNService -setup -d -r
在实际操作中,代表与SubVersion服务启动时不同的电子仓库目录.

删除服务:
运行命令:SVNService -remove

4.用TortoiseSVN创建电子仓库

TortoiseSVN客户端不是独立的程序,而是与Windows Explorer进行了外壳集成,其操作通过环境菜单来完成.



如上图所示:
1.创建名为SVNroot的空目录
2在目录上,或在在目录内空白处点右键,从TortoiseSVN菜单中选择Create repository here...(在此处创建电子仓库),在选择电子仓库类型后,将会自动生成一些文件和目录.这样就完成了电子仓库的创建,你可以用同样的方法创建多个电子仓库.

注:电子仓库类型有两种可供选择,选择任意一种即可:
1.Native filesystem(FSFS) 本地文件系统
2.Berkely database(BDB) Berkely数据库
不同的选择只会影响电子仓库的数据存取格式,默认选择为:Native filesystem

在本例中,可以用:SVNService -install -d -r e:\svnroot来安装SubVersion服务

要问本机安装的服务请在右键菜单中选择Reop-browser(电子仓库浏览),弹出如下对话框:

Congratulations, Roger Federer

从你身上, 我看到了执着,坚韧和顽强. 更明白了什么叫天道酬勤. 祝贺你成为历史上最伟大的网球选手, 更欣喜你坚持不懈的付出终于得到了回报.

Monday, June 1, 2009

MYSQL主从数据库同步备份

首先两台机器:
a: 192.168.0.1
b: 192.168.0.2


打开a机器的my.cnf/my.ini设置

首先要保证要同步的数据库内容完全一致

在[mysqld]下添加以下内容
server-id=1

log-bin=/var/db/mysql/master.log

binlog-do-db=要同步的数据库名称 //不写就同步全部。
#binlog-ignore-db = mysql //忽略的数据库

增加一个backup的用户

GRANT FILE,REPLICATION SLAVE,REPLICATION CLIENT,SUPER ON *.* TO backup@'192.168.0.2' IDENTIFIED by 'backuppassword';
这个权限表示,这个backup账号只能由从备份机192.168.0.2访问只能用来进行备份操作


因为要实现双向备份,所以a上还要加以下内容。 如果是单向,则不需要加。
master-host=192.168.0.2
master-user=backup
master-password=backuppassword
master-port=3306
master-connect-retry=60
replicate-do-db=要同步的数据库
#replicate-ignore-db=不同步的数据库

打开b机器的my.cnf/my.ini文件
添加或修改以下内容
server-id=2

master-host=192.168.0.1
master-user=backup
master-password=backuppassword
master-port=3306
master-connect-retry=60
replicate-do-db=要同步的数据库
#replicate-ignore-db=不同步的数据库

如果要双向同步则再加上以下内容

log-bin=/var/db/mysql/master.log
binlog-do-db=要同步的数据库名称
#binlog-ignore-db = mysql //忽略的数据库

再增加一个帐号给a
GRANT FILE,REPLICATION SLAVE,REPLICATION CLIENT,SUPER ON *.* TO backup2@'192.168.0.1' IDENTIFIED by 'backuppassword';


至此基于mysql的同步功能就做好了。
重起两边的服务器。 用show master status查看主服务器状态。 用 show slave status 查看从服务器状态。

slave stop; 停止从服务器
slave start; 启动从服务器
master stop; 停止主服务器
master start; 启动主服务器

用show processlist可以查看同步状态。
如果有什么错误打开mysql的 hostname.err 查看原因,再调用下面的修改命令:

CHANGE MASTER TO
MASTER_HOST='master_host_name',
MASTER_USER='master_user_name',
MASTER_PASSWORD='master_pass',
MASTER_LOG_FILE='recorded_log_file_name',
MASTER_LOG_POS=recorded_log_position;

然后再重起slave;

install multiple mysql instances on windows

1.正常安装Windows版的Mysql,例如安装在d:\mysql文件夹里;

2.按照常规配置好Mysql;

3.复制备份安装好的文件夹,比如备份到另外一个文件夹,或者命名为“复件mysql";

4.运行卸载程序删除安装的MYSQL和文件夹;

5.把备份的mysql文件夹,重新恢复原来的名字,或还原到原来的位置上;

6.再次运行安装程序,安装在另外一个目录,例如:“d:\mysql2"。配置端口为3307;

现在,后面安装的第二个Mysql——mysql2肯定是可以正常运行的,下面让起初安装的第一个Mysql

服务正常运行:

在cmd命令行模式下,进入第一个Mysql的\bin文件夹。

执行mysql-nt install mysql2命令。作用是为Mysql安装一个服务,服务名是mysql2,你也可以自己改名字,比如mysql5,那么命令就是:

mysql-nt install mysql5

完毕后,在运行里运行REGEDIT,打开WINDOWS注册表编辑器,在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\项目找到你刚才手动安装的服务,

我这里是mysql2。打开,修改下面的ImagePath数值。

你一般将会看到"D:\mysql\bin\mysqld-nt" mysql2类似的数值。

在mysql-nt"和mysql2之间加入my.ini的路径信息,格式为:

--defaults-file="d:\mysql\my.ini"

其中粗体部分替换你的正确的路径,

修改完毕后数据数值一般是类似这个样子:

"D:\mysql\bin\mysqld-nt"
--defaults-file="d:\mysql\my.ini" mysql2

确定,退出注册表编辑器。

然后就可以正常启动mysql2服务了,在CMD窗口下输入命令:

net start mysql2

一般应能正常启动了。

我的经历, 按以上配置好后, 我无法同时启动两个MYSQL服务, 经研究, 原因是

MYSQL5.1把DATA文件夹分离出来, 放在datadir="C:/ProgramData/MySQL/MySQL Server 5.1/Data/", 要修改其中一个MY.INI配置, 把DATA文件夹放在另一个不同的地方, 就可以了.

Saturday, May 30, 2009

a javascript desktop web application

this is amazing:

http://www.jsdesk.com/

Tuesday, May 19, 2009

Medicare levy surcharge

I asked some of my friends and it seems few of them know this 'Medicare levy surcharge' thing. Below is the explanation from ato website:

What is the Medicare levy surcharge (MLS)?

Individuals and families on incomes above the MLS thresholds, who do not have private patient hospital cover pay MLS for any period during 2007-08 that they did not have this cover.

We calculate MLS at the rate of 1% of your taxable income. The MLS is in addition to the 1.5% Medicare levy.

http://www.ato.gov.au/individuals/content.asp?doc=/content/7128.htm

Friday, April 3, 2009

Web database design

I believe most of general datbase design principles apply to web database design as well. However, i also believe web database design should have some specific. No matter all, database design is an old subject in computer science while web application, compared with database, is a pretty new area.

I think the biggest headache to web developers is finding a balance between redundancy and normalization, etc, speed and space. Redundancy can minimize the database query times and the need to join tables. However, this obviously violates the 3rd normalization principle, the school theory of database design.

Personally, i don't mind to have some redundancy in my database. But there is one rule: only the redundent information can let you uniquely identify a row, which means, with that redundent field, you can have construct a unique key for the table.

Let's explain with an example. Suppose we have mobile subscription services. Each service have a shortcode. To subscribe these services, a user can send a signup keyword to a shortcode. Obviously, shortcode and signup keyword must be unique. However, one service can have variety of signup keywords.

The shortcode table may be like this
id, shortcode, country_id

The campaign table is
id, name, shortcode_id

The signup keyword table is
id, keyword, campaign_id

Now if a user send a keywork to a shortcode, i need to find out which campaign he is going to subscribe. Based on the above tables, i cannot uniquely identify a keyword, and then find out the campaign. In this situation, i don't mind adding a shortcode_id to the keyword table, so that with keyword and shortcode, we can uniquely identify the keyword, and then identify the campaign.

Thursday, March 26, 2009

PHP PDO bug

Today i just tried to do a simple query 'SELECT * FROM content LIMIT 0, 15'. Since i have been extremely on PDO, i wrote the query in this way, 'SELECT * FROM content LIMIT :offset, :limit', $bind = array('offset'=>0, 'limit'=>15). I got this error:

SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''0', '15'' at line 1

And then i found this is a PHP bug: http://bugs.php.net/bug.php?id=40740&edit=2

Bug #40740 PDO::execute() errors when parameters are used in LIMIT clause

That is ok. For someone like me who uses PHP to an extreme extent, getting some PHP bugs is normal. I've seen another PDO bug before, which had been fixed. That bug only exists in one PHP release.

What really surprises me is submitted at 6 Mar 2007, while at the end of this bug report, i found this 'Still not fixed in the 5.2.8 release'. I am using 5.2.8!

Friday, March 20, 2009

JQuery & Html Entities

htmlentities() is well known for PHP developers. JQuery can do the same thing at client side. Here is the interesting blog : http://debuggable.com/posts/encode-html-entities-with-jquery:480f4dd6-13cc-4ce9-8071-4710cbdd56cb

Actually, it is quite simple: $('#content').text('This is fun & stuff').html(); // evaluates to "This is fun & a m p ; stuff"

However, i have another issue. I send a json data { myValue: 'AT& a m p ;T' } to jquery. And what jquery is supposed to do is $("#myInput").val(myValue). This, however, will display AT & a m p ;T in the input field, instead of AT&T, the one i really want to show.

Instead of writting your own html entities parser, here is a hacky solution:
var realValue =$("#js_temp").html(myValue).text();
$("#myInput").val(realValue);

Tuesday, March 17, 2009

JQuery event functions do not work for dynamic added elements

$("a").click(function () {
alert("I was clicked");
});

<a href="http://www.blogger.com/post-edit.do">click me</a>

If you click the link, a window will popup. However, if you use JQuery dynamicly add some new <a> elements, they won't work with the click function. For example,

$("p").append("<a href='#'>I am added dynamically</a>"), and if you click the link, you will find that the function doesn't work at all.

Monday, March 16, 2009

Zend framework source code learning - Zend_Loader

include 'Zend/Loader.php';
Zend_Loader::registerAutoload();

The above two lines can save us from those long reqire_once list on top of php files. Let's have a look at the registerAutoload().

The method definition is public static function registerAutoload($class = 'Zend_Loader', $enabled = true). If we don't pass any parameters, the default class is Zend_Loader, and $enable is true, which allows the function to call spl_autoload_register.

if (!function_exists('spl_autoload_register')) {
require_once 'Zend/Exception.php';
throw new Zend_Exception('spl_autoload does not exist in this PHP installation');
}
It checks if spl_autoload_register function exists, because the magic autoload relies on this function.

self::loadClass($class);
loadClass() is the sole method of Zend_Loader. It looks through the path and directory, based on the $class name, and tries to load the class file. For example, if the $class is 'Zend_Log_Writer_Stream', it looks through 'Zend/Log/Writer/' and tries to load Stream.php file.

$methods = get_class_methods($class);
if (!in_array('autoload', (array) $methods)) {
require_once 'Zend/Exception.php';
throw new Zend_Exception("The class \"$class\" does not have an autoload() method");
}
spl_autoload_register(array($class, 'autoload'));
The codes check if the class possesses autoload method and tries to register the method.

Zend_Load defines and autoload method, which actually calls the loadClass().

So that is how Zend_Load works. If we call a class not defined, it automatically searchs the class file by its class name in include_path, and load the class file if it is found.

I havn't done the test by myself. But some articles on internet point out that Zend_Loader::registerAutoload() is bad in performance. It is quite possible, based on its code. So the developer must find a balance between speed and convenience.

Zend framework source code learning - Zend_Registry

Zend_Registry extends ArrayObject, which is a class in Standard PHP Library(SPL).
Let's take a look at its set method:
public static function set($index, $value)
{
$instance = self::getInstance();
$instance->offsetSet($index, $value);
}
We can see, although it is a static method, it implements a singleton inside: $instance = self::getInstance().

offsetSet($index, $value) is a method of ArrayObject. It will set the value at the specified $index as $value.

Now let's check through get method:
public static function get($index)
{
$instance = self::getInstance();

if (!$instance->offsetExists($index)) {
require_once 'Zend/Exception.php';
throw new Zend_Exception("No entry is registered for key '$index'");
}

return $instance->offsetGet($index);
}
It gets a singleton instance and tries to call offsetGet($index), which is another ArrayObject method to return the value at $index. If the specified $index doesn't exist, it throws an exception.

We use Zend_Registry to store the resources that must be accessed through the whole web site, such as database resource.

Sunday, March 15, 2009

Zend framework source code learning - Zend_Config

Zend_Config class

To understand this class, we only need to look through its 3 methods.
The first is, naturally, its constructor.

public function __construct(array $array, $allowModifications = false)
{
$this->_allowModifications = (boolean) $allowModifications;
$this->_loadedSection = null;
$this->_index = 0;
$this->_data = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
$this->_data[$key] = new self($value, $this->_allowModifications);
} else {
$this->_data[$key] = $value;
}
}
$this->_count = count($this->_data);
}
Many developers prefer using arrays in php file to save config data, such as $option['path'] = ''. From this constructor, we know that to load the config data, we can instantiate a Zend_Config class in this way: $config = new Zend_Config(require 'config.php');

Zend_Config use getter to retrieve config data. It provide magic method __get as well. However, __get also call the getter.

public function get($name, $default = null)
{
$result = $default;
if (array_key_exists($name, $this->_data)) {
$result = $this->_data[$name];
}
return $result;
}

public function __get($name)
{
return $this->get($name);
}

So, to get the value of $option['path'], we can use either $config->get('path') or $config->path. I think most of PHP developers(including myself) cannot resist the temptation of using the latter one to get data.

That is pretty enough for Zend_Config class.

Thursday, March 12, 2009

Test javascript in IE

Firefox is a perfect companion to web developers due to its web development tools like firebug. However, in some cases it might be too good especially when we are working on javascript. Or, maybe more precisely, i should say IE sucks! However, we have to accept such a reality that most internet users still use IE as their default and only browser. So, just keep in mind that you should test your javascript in IE ALWAYS. Don't assume that JQuery or other javascript frameworks or libraries can take care of this issue for you. The truth is, none of them can. In the world of javascript, we don't have the magic like java and java VM that let you write once, run everywhere.

Tuesday, March 10, 2009

Another article about PHP DAO

Decoupling models from the database: Data Access Object pattern in PHP

http://codeutopia.net/blog/2009/01/05/decoupling-models-from-the-database-data-access-object-pattern-in-php/

Monday, March 9, 2009

Zend_Db component

Zend framework provides some database access components. A common feature of these components is, it can access/query database in OO way. So, it is quite possible that you won't see a whole, integrated sql query in code. Instead, you probably will see a lot of these queries: $where = 'where id = ?'; $db->select('*', $where);

When regarding database query, i am not a supporter of OO. I prefer writing full,whole, or integrated sql, even some times this means duplication. For example, i probably need to use two queries: 1. SELECT * FROM user WHERE id=?; 2.SELECT * FROM user WHERE username = ?. I would like to write them twice, instead of write them in this way: $select = "SELECT * FROM user"; $whereId = " WHERE id=?"; $whereName = " WHERE username=?". Finally, I would just run the whole query by $db->query($sql);

So my model will not extend from any Zend_Db components. And there is no data access object carried with model. I have DAO layer. Instead of calling $user->save(), i would call $daoUser->save($user). And in my dao object, it is always like this: $sql = "SELECT * FROM user"; $db->query($sql);

Zend_Form

I give Zend_Form a couple of tries. I recognize it is a really cool component. However, i think it kind of violate the layer separation law. What if a designer wants to decorate the form? He must open up the PHP file and learn how Zend_Form work?

In spite of this, I cannot resist the temptation of using this component. The benefit is it can save a lot of time. With Zend_validator and Zend_Filter, Zend_Form can finish most of form processing job for you. You, as a developer, probably just need to add some extra own validators and that is it!

Performance, easy development, code solid

Performance is an issue often mentioned by developers. Regarding zend framework, one consideration is using Zend autoload or not. Autoload can make our life much easier. As long as we specify the file name and class name in accordance with name space rule, we don't need to put include/require on top of files. Some articles on internet, however, point out that zend autoload slow down the web site's performance. They prefer puting lot of include/require statement on top of files.

For me, i prefer using autoload, because not only it really saves a lot of time, and makes the code look more clean, but also most problems of a web application, based on my opinions, are caused by coding errors, bugs instead of performance.

some thoughts of using zend framework

1. Where is business logic located? Controller + Model? Fat controller + Thin model or Thin controller + fat model, or controller + model + another business logic layer?

2. If the web application is organized in modules, and we don't want to use zend autoload due to performance consideration, how to access one module's data/model from another module?

3. Active Record or DAO? I personally prefer DAO. And the reason is obvious: data access get separated from business model, and your model won't become heavy. Just think of a situation: send email to millions of subscribers, if each subscriber has to carry db accesss object, that is gonna take a lot of resurces.
Here is an article about active record and DAO: http://blog.astrumfutura.com/archives/128-DAO-vs-ActiveRecord-DAO-Wins.html

Thursday, March 5, 2009

Integrate Smarty with Zend_Layout

If you can endure adding {include file="header.tpl"} and {include file="footer.tpl"} in all of your template files, then you don't have to worry about this issue. And you are lucky! (You are even more lucky if you don't use smarty at all!)

Unfortunately, I cannot accept this kind of coding, so I have to face the pain and find out how to integrate smarty with zend_layout.

I can only find one article from internet regarding this issue. The article is here: http://anders.tyckr.com/2008/03/12/implementing-zend-layout-and-smarty-using-zend-framework-mvc/

It gives us a detailed solution to implement smary with zend layout, and it DOES WORK! Thank God...

CLI/Console program with Zend Framework

You can google this tutorial "PHP frameworks, Part 5: Integrating external tasks" for dealing with working outside the frameworks.

Even for a web application, we may need to run scripts in command line in some cases. For example, a cron job. And we want to use our models and Zend framework components. The most important thing is to setup include path to include your Zend library and your models. For example, if Zend is placed in 'library' folder, we can
set_include_path('.' . PATH_SEPARATOR . ROOT_DIR . '/library/'
. PATH_SEPARATOR . ROOT_DIR . '/application/models/'
. PATH_SEPARATOR . get_include_path());

require_once('Zend/Loader.php');
Zend_Loader::registerAutoload();

That is it. Now you can use your models and Zend components as you use them in your web application. For example,

$config = new Zend_Config_Ini(ROOT_DIR . '/config/config.ini', 'cli');
Zend_Registry::set('config', $config);
//set up default time zone
date_default_timezone_set($config->date_default_timezone);

// configure database and store to the registery
$db = Zend_Db::factory($config->database);
Zend_Db_Table_Abstract::setDefaultAdapter($db);
Zend_Registry::set('db', $db);

//do sth
$user = new User();
$user->save();

$logFile = Zend_Registry::get('config')->logFile;
$log = new Zend_Log(new Zend_Log_Writer_Stream($logFile));
$log->info('a new user is saved');

Zend_Log limitation

I researched Zend_Log for a while today. My case is I want to user one logger and log messages to different files based on the message type. The message type is not Zend_Log's message priority. It is more event/action based. For example, i want to log login information to login.log and pay information to pay.log.

Although we can add multiple writters to a logger, we can't specify which writter we want to use. Well, i should say, I didn't find out how to specify a writter for Zend_Log.

Sure i know where there is an issue, there is a solution. But I wish next Zend framework release could bring us a better Zend_Log

smarty $compile_id for different $template_dirs

We might have different template dirs storing template files with an identified name. For example, we could have /Auth/index.tpl, /Buy/index.tpl. However, Smarty uses only one compile dir, and Smarty is not that smart. To display the two index.tpl correctly, we must specify $compile_id. If you set a distinct $compile_id for each template_dir, then Smarty can tell the compiled templates apart by their $compile_id.

Wednesday, March 4, 2009

2011年全球手机成人内容市场价值33亿美元

2011年全球手机成人内容市场价值33亿美元

发表日期:2006年11月29日 出处:CNET New
-----------------------------------------------------------------------------------------------

  就如同当年的互联网媒体产业,如今的移动内容市场也走上了迅速繁荣的老路:色情内容服务。

  据CNET News.com报道,据市场调查机构Juniper分析家预测,到2011年的时候,全球移动成人内容服务市场总价值将达到33亿美元,实际上,该数字在今年已经达到了14亿美元。

  据悉,欧洲是最大的移动色情内容服务市场,在色情内容的提供和需求方面,均走在全球的第一位。与此同时,亚太地区则在色情服务产业内排名第二。

  据Jupiter分析家透露,移动成人内容服务的崛起,与手机技术的迅速发展不无关系,可以说是技术的飞跃带动了一次又一次服务的创新。据统计,目前绝大多数手机色情服务还属于文字形式,而随着未来3G手机网络的逐渐普及,更多的多媒体形式服务将登陆移动市场,例如图片、音频和视频等,届时更为丰富的色情内容将出现在世面,而色情服务基础,也将从呆板的文字过渡向更为刺激的图像世界。据一份手机产业报告,到2011年的时候,移动市场70%的营收将来自于媒体服务,而不再是简单的硬件销售。

  “手机色情服务是一块巨大的市场,实际上,成人内容服务总是可以拉拢某一媒体平台的迅速发展。”Jupiter分析家表示。据悉,目前的手机成人内容服务还处于起步阶段,大多数用户发送含有成人内容的文字信息,只是为了取悦自己的伴侣而已。

Mobile content revenues to grow exponentially to 2009

From: http://www.wiliam.com.au/wiliam-blog/mobile-phone-and-mobile-content-to-grow-exponentially-to-2009

According to an article (Mobile revenue to top $1bn by 2009, AAP) in The Sydney Morning Herald, revenues from mobile content (mobile phones) in Australia are set to explode, exceeding a fantastic $1 billion on 2009.

Revenues from Australian mobile content were $129 million in 2004, with personalisation services such as mobile phone wallpapers and ring tones accounting for 69%.

Based on study by Frost & Sullivan, the key areas of mobile content growth will be entertainment, enterprise applications and productivity services; adult services are considered entertainment in the study, and are predicted to be one of the biggest movers (122% growth).

Also mentioned is the growth of high-bandwidth applications and mobile content (3G (and 4G?) such as mobile video, predicted to account for 13% of US revenues by 2009.

There are a variety of obvious factors behind the growth including bandwidth (3G) and increasing demand from ever-savvier consumers.

The article concludes by predicted the challenge for the industry being establishing sustainable and successful business models; 1999, here we come again!

At Wiliam, we have followed the emergence and growth of the mobile content industry, and have developed a number of mobile applications with Vodafone Australia in the past year; only a few weeks back, we completed a project with client Mogenic, where we integrated our Content Management System sitedock with the Vodafone MMS gateway to allow participants to submit their MMS photos (mobile phone photos) directly to the Mogenic website.

In fact, several of our current websites in production are being built in xHTML, allowing for easier portability of content to mobile phones.

I suspect that within six months, all of our content will be mobile-ready, and that within twelve months, clients will place emphasis on mobile optimisation of their content.

Start coding on project officially

I officially start coding on my new project now. Since i decided to use Zend Framework, which is pretty new to me, in the first several days i just try to set up the framework and the file structure. I also implement Authentication and ACL, integrated Smarty with Zend.

I personally don't like smarty. But many other php developers like it, so at this point i compromise a little bit.

Thursday, February 19, 2009

5th Day

We went to an indian restaurant for lunch today. Indian food might be a good option if you want to taste something different occasionally. But it definitely could not stay on your long term diet list. Indian food features with curry and spicy. A survey shows XX% of indians suffer diabetes, because they like to put coco milk in their food.

My project's functional spec is done. Now i need to set up the framework, design the database and classes.

4th day

infrastructure upgrade

since we have more applications coming up, we need to upgrade infrastructure. However, we don't want to jump a big step once. We need to upgrade, get better performance, while still save cost.

The current status is we have one server, all applications and databases are running on this server. We think about having 4 servers, 1 of them works as database server. The rest work as application server and implement load balance. But this plan seem too big for us at the moment.

At the end, we reach an agreement that we just get 1 more server. So we have two servers, 1 for databases, the other 1 for applications.

We will see how this works at the moment. If our business keeps growing, we definitely need to upgrade our hardware platform later.

Wednesday, February 18, 2009

3rd Day

First meeting on Functional Spec

2nd Day

Keep working on functional specification

Some issues to figure out: WAP Page, Architecture, Framework, Report

First day

1. Set up my development environment

2. Suggest using bug track system

3. Suggest and start to work on functional specification

Monday, January 12, 2009

Mysql Optimization - schema optimization

1. Choose optimal data type
a. smaller is better - less space on disk, in memory, in CPU cache
b. simple is good (use integer instead of ip address, for example);
c. avoid null if possible - null requires special processing, makes index, comparisons more complicated.

2.Index strategies
a. isolate the column. mysql can't use index unless the columns are isolated in query
e.g., suppose id is primary key.
select * from test where id+1 = 5; should be changed to select * from test where id = 4

b. Prefix index
e.g., if u need to index long character columns, index the first few characters
alter table test add key (long_name(5));
downside is prefix index cannot be used for order by or group by

c. covering indexex
e.g. an index containing all the data needed for a query is covering index
-index entries are smaller and faster than row size
-indexes are sorted by their values

Normalization and Denormalization
A mixture of Normalized and Denormalized

Thursday, January 8, 2009

Zend framework Zend_Acl

Acl stands for action control list.

The typical flow for using Zend_Acl in a web application is as follows:
1. Instantiate the Zend_Acl class (let’s call this object $acl).
2. Add one or more roles to $acl using the addRole() method.
3. Add resources to $acl using the add() method.
4. Add the full list of privileges for each role (that is, use allow() or deny() to indicate which resources roles have access to).
5. Use the isAllowed() method on $acl to determine whether a particular role has access
to a particular resource/privilege combination.
6. Repeat step 5 as often as necessary while the script executes.

Example:
$this->auth = $auth;
$this->acl = new Zend_Acl();
//add the different user role
$this->acl->addRole(new Zend_Acl_Role($this->_defaultRole));
$this->acl->addRole(new Zend_Acl_Role('admin'));
$this->acl->addRole(new Zend_Acl_Role('member'));
$this->acl->addRole(new Zend_Acl_Role('administrator'), 'member');
// add the resources we want to have control over
$this->acl->add(new Zend_Acl_Resource('account'));
$this->acl->add(new Zend_Acl_Resource('admin'));

// allow access to everything for all users by default
// except for the account management and administration areas
$this->acl->allow();
$this->acl->deny(null, 'account');
$this->acl->deny(null, 'admin');
// add an exception so guests can log in or register
// in order to gain privilege
$this->acl->allow('guest', 'account', array('login', 'fetchpassword' , 'register' , 'registercomplete'));
// allow members access to the account management area
$this->acl->allow('member', 'account');
$this->acl->allow('admin', 'admin');

public function preDispatch (Zend_Controller_Request_Abstract $request)
{
// check if a user is logged in and has a valid role,
// otherwise, assign them the default role (guest)
if ($this->auth->hasIdentity()) {
$role = $this->auth->getIdentity()->subscription_type;
} else {
$role = $this->_defaultRole;
}
if (! $this->acl->hasRole($role)) {
$role = $this->_defaultRole;
}
// the ACL resource is the requested controller name
$resource = $request->controller;
// the ACL privilege is the requested action name
$privilege = $request->action;
// if we haven't explicitly added the resource, check
// the default global permissions
if (! $this->acl->has($resource))
$resource = null;
// access denied - reroute the request to the default action handler
if (! $this->acl->isAllowed($role, $resource, $privilege)) {
$request->setControllerName($this->_authController['controller']);
$request->setActionName($this->_authController['action']);
}
}

in bootstrap file, we add
$frontController->registerPlugin(new ControllerAclManager($auth));

Wednesday, January 7, 2009

Zend framework Zend_Auth

We can use Zend_Auth to process user authentication.

$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) { //user already login
$this->_redirect(/*redirect url*/);
}

$request = $this->getRequest();
if ($request->isPost()) {
$username = trim($request->getPost('username'));
$password = trim($request->getPost('password'));
//below is the key part of authentication
//$this->db must be a db adapter
$adapter = new Zend_Auth_Adapter_DbTable($this->db, 'username', 'password', 'md5(?)');
$adapter->setIdentity($username);
$adapter->setCredential($password);
$result = $auth->authenticate($adapter);
if ($result->isValid()) { //login successful
$auth->getStorage()->write($identity); //save itentity
//redirect
} else {//login failed

}
}

logout is simple as well: Zend_Auth::getInstance()->clearIdentity(); That is it.

Monday, January 5, 2009

Superficial impression of Zend Framework

It is huge, compared with other frameworks such as CakePHP or CodeIgnitor.

You can use it as a library without following its MVC architecture design. For example, Zend_Validate class family can save you a lot of work on input validation.

Naturally, huge framework comes with huge document - Programmer's reference guide.

Sometimes you need to head into the source code to figure out what really happened.

Despite its huge size, I think it is still easy to learn, although it may not be easier than any other frameworks.

Sunday, January 4, 2009

Integrate Smarty with Zend Framework

I collected some information regarding this topic.

The first one is probably the easiest approach to use Smarty in Zend framework.
require('Zend.php');

include 'smarty/Smarty.class.php';
$smarty = new Smarty();
$smarty->template_dir = 'resources/templates';
$smarty->compile_dir = 'resources/templates_c';
$smarty->plugins_dir = array(SMARTY_DIR . 'plugins', 'resources/plugins');
//may be some more smarty setup

//register this smarty
Zend::register('smarty', $smarty);

//use it in action controller
class IndexController extends Zend_Controller_Action
{
function index()
{
$smarty = Zend::registry('smarty');
$smarty->assign('title', 'Test');
$smarty->display('index.tpl');
}
}

However, you have to handle view logic in your action controllers.

The second option is to extend Zend_View_Abstract
class Templater extends Zend_View_Abstract
{
protected $_path;
protected $_engine;

public function __construct()
{
$config = Zend_Registry::get('config');

require_once('Smarty/Smarty.class.php');

$this->_engine = new Smarty();
$this->_engine->template_dir = $config->paths->templates;
$this->_engine->compile_dir = sprintf('%s/tmp/templates_c',
$config->paths->data);

$this->_engine->plugins_dir = array($config->paths->base .
'/include/Templater/plugins',
'plugins');
}

public function getEngine()
{
return $this->_engine;
}

public function __set($key, $val)
{
$this->_engine->assign($key, $val);
}

public function __get($key)
{
return $this->_engine->get_template_vars($key);
}

public function __isset($key)
{
return $this->_engine->get_template_vars($key) !== null;
}

public function __unset($key)
{
$this->_engine->clear_assign($key);
}

public function assign($spec, $value = null)
{
if (is_array($spec)) {
$this->_engine->assign($spec);
return;
}

$this->_engine->assign($spec, $value);
}

public function clearVars()
{
$this->_engine->clear_all_assign();
}

public function render($name)
{
return $this->_engine->fetch(strtolower($name));
}

public function _run()
{ }
}
And we need to set up view renderer in bootstrap file
// setup the view renderer
$vr = new Zend_Controller_Action_Helper_ViewRenderer();
$vr->setView(new Templater());
$vr->setViewSuffix('tpl');
Zend_Controller_Action_HelperBroker::addHelper($vr);

An alternative is to implement Zend_View_Interface
class App_View implements Zend_View_Interface
{
/**
* Smarty object
* @var Smarty
*/
protected $_smarty;

/**
* Constructor
*
* @param string $tmplPath
* @param array $extraParams
* @return void
*/
public function __construct($tmplPath = null, $extraParams = array())
{
require_once ('Smarty/Smarty.class.php');
$this->_smarty = new Smarty;

if (null !== $tmplPath) {
$this->setScriptPath($tmplPath);
}

foreach ($extraParams as $key => $value) {
$this->_smarty->$key = $value;
}
$config = Zend_Registry::get('config');
$this->_smarty->compile_dir = sprintf('%s/templates_c', $config->paths->data);
$this->_smarty->plugins_dir = '';
$this->base_url = $config->paths->baseurl;
}

/**
* Return the template engine object
*
* @return Smarty
*/
public function getEngine()
{
return $this->_smarty;
}

/**
* Set the path to the templates
*
* @param string $path The directory to set as the path.
* @return void
*/
public function setScriptPath($path)
{
if (is_readable($path)) {
$this->_smarty->template_dir = $path;
return;
}

throw new Exception('Invalid path provided');
}

/**
* Retrieve the current template directory
*
* @return string
*/
public function getScriptPaths()
{
return array($this->_smarty->template_dir);
}

/**
* Alias for setScriptPath
*
* @param string $path
* @param string $prefix Unused
* @return void
*/
public function setBasePath($path, $prefix = 'Zend_View')
{
return $this->setScriptPath($path);
}

/**
* Alias for setScriptPath
*
* @param string $path
* @param string $prefix Unused
* @return void
*/
public function addBasePath($path, $prefix = 'Zend_View')
{
return $this->setScriptPath($path);
}

/**
* Assign a variable to the template
*
* @param string $key The variable name.
* @param mixed $val The variable value.
* @return void
*/
public function __set($key, $val)
{
$this->_smarty->assign($key, $val);
}

/**
* Retrieve an assigned variable
*
* @param string $key The variable name.
* @return mixed The variable value.
*/
public function __get($key)
{
return $this->_smarty->get_template_vars($key);
}

/**
* Allows testing with empty() and isset() to work
*
* @param string $key
* @return boolean
*/
public function __isset($key)
{
return (null !== $this->_smarty->get_template_vars($key));
}

/**
* Allows unset() on object properties to work
*
* @param string $key
* @return void
*/
public function __unset($key)
{
$this->_smarty->clear_assign($key);
}

/**
* Assign variables to the template
*
* Allows setting a specific key to the specified value, OR passing
* an array of key => value pairs to set en masse.
*
* @see __set()
* @param string|array $spec The assignment strategy to use (key or
* array of key => value pairs)
* @param mixed $value (Optional) If assigning a named variable,
* use this as the value.
* @return void
*/
public function assign($spec, $value = null)
{
if (is_array($spec)) {
$this->_smarty->assign($spec);
return;
}

$this->_smarty->assign($spec, $value);
}

/**
* Clear all assigned variables
*
* Clears all variables assigned to Zend_View either via
* {@link assign()} or property overloading
* ({@link __get()}/{@link __set()}).
*
* @return void
*/
public function clearVars()
{
$this->_smarty->clear_all_assign();
}

/**
* Processes a template and returns the output.
*
* @param string $name The template to process.
* @return string The output.
*/
public function render($name)
{
return $this->_smarty->fetch($name);
}

/**
* get smarty template dir
*
* @return string template dir
*/
public function getScriptPath()
{
return $this->_smarty->template_dir;
}//end getScriptPath()
}


There is one problem with this method: all template files have to be placed in one directory. What if we want to put template files in different folders? Say, i have two modules, and their templates are placed in two folders named with the module names?

The key is the value of template_dir. To work around, we can specify template_dir at action controller.

class App_ControllerAction extends Zend_Controller_Action
{
public $db;
public function init()
{
//set smarty templete dir
$module = $this->getRequest()->getModuleName();
$path = Zend_Registry::get('config')->paths->templates;
if (empty($module) === false) {
$this->view->setScriptPath($path . '/' . $module);
}
}
}

Share two FREE books download websites

http://www.ebookee.com/

http://www.pdfchm.net/

The second website specializes in IT books. I am not sure if they are legal but, hey, they are there and you do can download books from them. And these books may cost you hundreds of bucks if you buy the print version from amazon or any book stores.