Symfony 限制

发现Symfony的2个限制,其实这个是Propel的限制,Propel是PHP的一个ORM的数据库代码生成器,通过XML或YML文件配置,自动创建针对每个数据库和表的CRUD模型,并有良好的继承关系,真正地以面向对象的方式操纵数据库的每一行每一列。

目前发现2点限制:

  1. 不能适应分表(动态选择表名)
  2. 整型字段不能设置unsigned类型

在设计数据库的时候,采用分表存储,是一个优化性能的好方法, 比如,保存用户的文章,按用户ID,散列到8个表上。这8个表的结构都完全一样,只是表名不一样:tbl_post_0, tbl_post_1, …, tbl_post7。

当前版本的Propel,生成的代码,是和表名紧紧耦合在一起的,也就是说这8个表,会生成8套几乎一摸一样的代码,同样需要写8份schema.xml。

这样很不合理。

应该再抽象出一个机制,考虑结构相同表的统一处理。

Propel的另一个问题就是整数字段不能设置unsigned类型,查了官方的说明,说是PHP的整数类型就是-2的31次方到2的31次方减一。我还没来得及证实,不过这也不是很大的问题。因为,Symfony就是为了从零开始快速开发。一个新项目,数据量从零增加到20多亿条记录,还是需要一些时间的,应该避免项目初期的过度设计。

等你的数据规模达到10亿的时候,考虑找风投给你1000万美元,招人帮你重构吧。

新书上架 2007年出版

今天下了几本电子书,主要是网站开发方面的,看书名就知道是好书了。

给大家分享一下,都是英文版的,既能学知识,又能练英语阅读!

只要抽出时间认真看看,一定会有很多收获。

其实很多牛人,并没有多聪明,只是多看了几本书而已……

  1. OReilly High Performance Web Sites Sep 2007
  2. Pragmatic Design Accessible Web Sites Nov 2007
  3. pragmatic bookshelf interface oriented design jun 2006
  4. OReilly Understanding MySQL Internals Apr 2007
  5. Pragmatic Prototype and script aculous Dec 2007
  6. OReilly An Introduction to Testing Web Applications with twill and Selenium 2007
  7. ZK Ajax without the Javascript Framework

卡巴斯基6 授权文件 激活 证书

前一阵我的卡巴6过期了,到网上到处寻找可用的激活码,后来找到几个授权文件,导入到卡巴就可以了,现在我的卡巴到期时间是2010年3月。

我把收集到的几个文件一起打包了,大家不用再到处寻找了,记住这个地址就可以啦。

http://down.leakon.com/software/2007/12/Kaba_Key.zip

编码检查 UTF-8 浏览器编码

编码问题一直是做网站开发的工程师们很头疼的事,如果你希望自己的网站能够被更多不同语言环境的人浏览和使用,那就一定要解决好编码的问题。

我的经验是,从HTML页面的编码,到PHP程序文件的编码,到数据库的设计以及与PHP之间连接的编码,全部使用UTF-8,这样就能保证你的页面不会出现乱码。

不要总是觉得,你的网站是给中国人用的,不给其他国家的人使用。如果你的中国用户希望保存一些日文、韩文等亚洲文字, 你也没有理由拒绝吧?

还是在一开始就解决好编码的问题为好,做网站,免不了这一步,今天不做,以后迟早要还的。

如果你认为我说的毫无道理,那我给大家引用一篇W3C组织的官方说明吧:

http://www.w3.org/International/questions/qa-forms-utf-8.en.php

注意回答部分的第一句:The best way to deal with encoding issues in (X)HTML forms is to serve all your pages in UTF-8.

用UTF-8吧,肯定没错的,你可以避免很多很多问题。

不过,就算页面使用了UTF-8,也还是会遇到一个问题:

浏览器接收编码的转换问题。

就这个问题的说明,我做了一个测试页面,可以在我的Google SVN下载:

http://leakon.googlecode.com/svn/trunk/leakon/php/detect_utf8/

在浏览器里打开test.php页面即可。

这个页面是UTF-8编码的,如果你不是用SVN软件CheckOut而是直接复制的源码,请记得要把test.php这个文件保存成UTF-8格式。

这个页面有一个Form表单,你可以在输入框内输入中文,然后看地址栏里word字段的值,一个汉字对应3个%,因为UTF-8是变长编码的,针对不同的文字,%的数量是1-4个。

如果你熟悉GBK编码的页面,应该注意到每个汉字对应2个%,因为汉字都是双字节编码的。

这个时候,如果你足够细心,应该可以发现一个冲突的问题。

如果,我在浏览器的地址栏的word字段后面直接输入汉字,得到的是什么结果呢?给大家一个提示,这里最好使用Firefox浏览器,如果你在地址栏输入中文,Firefox会按照GBK的编码方式,按双字节编码,也就是一个汉字对应2个%。

可是你的页面默认是接收UTF-8编码的字符的,给你一个GBK编码,你会解析成乱码。

很多网站存在这个问题,包括Google和Yahoo。 他们只对各自的中文网站就解决了这个问题,英文的和日文的都没有处理。下面我们逐一测试一下:

我们给一个测试用例,中文:百度;GBK:%B0%D9%B6%C8;UTF-8:%E7%99%BE%E5%BA%A6。

看到了吧,GBK是4个%,UTF-8是6个%。

  1. http://www.google.cn/search?hl=zh-CN&q=%E7%99%BE%E5%BA%A6&btnG=Google+%E6%90%9C%E7%B4%A2&meta=
  2. http://www.google.cn/search?hl=zh-CN&q=%B0%D9%B6%C8&btnG=Google+%E6%90%9C%E7%B4%A2&meta=
  3. http://yahoo.cn/s?p=%E7%99%BE%E5%BA%A6&v=web
  4. http://yahoo.cn/s?p=%B0%D9%B6%C8&v=web
  5. http://www.google.com/search?hl=en&q=%E7%99%BE%E5%BA%A6&btnG=Google+Search
  6. http://www.google.com/search?hl=en&q=%B0%D9%B6%C8&btnG=Google+Search
  7. http://search.yahoo.com/search?p=%E7%99%BE%E5%BA%A6&fr=yfp-t-501&toggle=1&cop=mss&ei=UTF-8&vc=&fp_ip=CN
  8. http://search.yahoo.com/search?p=%B0%D9%B6%C8&fr=yfp-t-501&toggle=1&cop=mss&ei=UTF-8&vc=&fp_ip=CN
  9. http://www.google.co.jp/search?hl=ja&newwindow=1&q=%E7%99%BE%E5%BA%A6&btnG=%E6%A4%9C%E7%B4%A2&lr=
  10. http://www.google.co.jp/search?hl=ja&newwindow=1&q=%B0%D9%B6%C8&btnG=%E6%A4%9C%E7%B4%A2&lr=
  11. http://search.yahoo.co.jp/search?p=%E7%99%BE%E5%BA%A6&ei=UTF-8&fr=sfp_as&x=wrt
  12. http://search.yahoo.co.jp/search?ei=UTF-8&fr=sfp_as&p=%B0%D9%B6%C8&meta=vc%3D

以上是对Google和Yahoo两大搜索引擎的中文、英文、日文网站进行UTF-8、GBK编码的访问。

可以看到,2家公司只对中文网站做了编码检查,发现不是UTF-8就对关键词进行编码转换,得到了正确的结果,英文和日文都没有处理,给GBK编码时得到的是乱码。

他们就忽略了英文和日文用户搜索中文的需求。

其实,这个问题不是不可以解决的,中文网站都做到了,其他语言的怎么就不行呢?

这个不讨论了,还是说说怎么检查UTF-8编码吧。

其实就是一段正则表达式:

$regex    = '/^('
.
'[\x09\x0A\x0D\x20-\x7E]|'        # ASCII
.
'[\xC2-\xDF][\x80-\xBF]|'        # non-overlong 2-byte
.
'\xE0[\xA0-\xBF][\x80-\xBF]|'        # excluding overlongs
.
'[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|'    # straight 3-byte
.
'\xED[\x80-\x9F][\x80-\xBF]|'        # excluding surrogates
.
'\xF0[\x90-\xBF][\x80-\xBF]{2}|'    # planes 1-3
.
'[\xF1-\xF3][\x80-\xBF]{3}|'        # planes 4-15
.
'\xF4[\x80-\x8F][\x80-\xBF]{2}'    # plane 16
.
')*\z/x';

大家从SVN里可以下载源码。

我封装了一个类,DetectUT8,用于检测和转换编码。

过程是这样的:如果检查到不是UTF-8编码,就进行GBK -> UTF-8的转换。因为在地址栏输入的非ASCII字符都会按照GBK编码。

转换函数会检查系统是否启用了 mbstring 函数库,如果没有,则改用 iconv 转换。

这里要注意一点,不要把UTF-8写成utf8,也不要写成其他格式,iconv 的检查比较严格。

如果内置了 mbstring 函数,还是用 mb_detect_encoding 比较保险,但经测试表明,用上面这段正则,编码检测的成功率和 mb_detect_encoding 没有差别。

还要重申一个问题,编码检测的可靠性不是 100%,但应用这种方法已经极大地改善了用户体验,建议大家都采用这种方式。

PHP 文件锁 flock 负载均衡

最近有个项目,采用单台前端服务器提供Web服务,程序需要实时访问后端服务器。后端一共有几十台服务器,但有压力限制,单台负载不能过高,必须做负载均衡。

最简单的方式是用随机数,前端来请求的时候,随机挑选一台后端服务器,但这并不能保证压力平均分布,很有可能在某一段时间内请求都落到同一台服务器上,很容易导致这台服务器停止服务。

后来想到用文件锁的方式,来标记访问计数,顺序访问后端的每一台服务器,让每一台服务器一个周期只被访问一次。

在进行了多次功能测试和压力测试后,验证了这种想法的可行性,然后写了一个IDService类,封装了整个过程。

我在Google提供的SVN服务器上保存了源码,大家可以在

http://leakon.googlecode.com/svn/trunk/leakon/php/flock/flock.php

这个地址看到源码,或者用SVN工具CheckOut到本地。

核心过程,就是初始化的时候给一个ID范围,默认是从0开始,如果你的server_count是32,那么调用getId()方法的时候,我会顺序给你31至0这32个ID,采用文件锁就是考虑到并发请求之间彼此独立,一个进程读数据文件的时候要加独占锁,解锁前,其他进程无法读取数据文件。

ID分配给你了,每个ID对应哪个服务器,就是你自己做映射的事了,保证了这个模块的无关性和独立性,和其他所有模块保持无耦合。

这是在PHP5的环境下写的,const 定义了3个类常量:

LINE_FEED 是换行符,Windows 下是 \r\n,Linux 下是 \n,只是为了方便测试的时候实时查看数据,可以是任意字符,只要不是数字就OK;

MAX_LOAD 是计数器的最大值,计数器都是从0开始,如果有任何一个ID达到了最大值,则所有ID计数器全部归零,开始新的一轮计数,其实这个设置只要大于0就可以,最好不要太大,因为存储数字也是要占用存储空间的,越小,id_data_file的尺寸就越小,硬盘读取就越快;

DATA_BLOCK 是设置一次读文件的数据大小,硬盘的一个文件至少要占一个簇,一般文件系统一个簇是4K,这个取值要跟ID的总量有关系,如果你的LINE_FEED是\n,MAX_LOAD是99(采用文本方式存储,2字节),那么一个ID占用3个字节,如果你有100个ID,那么数据文件占用空间就是300Byue,因此DATA_BLOCK设置为300是最佳值,需要注意,如果ID范围变大,需要同步更改此值,因此我默认设置了2048字节,小于硬盘的一个簇,相对于300字节来说没有性能损失,因为都在一个簇内,数据存储是连续的,硬盘只需一次寻道和一次读写。

源码里有使用说明,很简单,在实例化对象的时候指定ID范围和数据文件位置即可。

已经经过测试,给一些压力测试数据吧:

在AMD3000+和7200转80G硬盘的台式机环境,可以提供到 1300+次/秒 的速度,此时磁盘IO是瓶颈;
换上Linux服务器,具体配置不太清楚,反正是SCSI硬盘,100多G,只是开发用机,性能并不高,但可以提供 5000+次/秒 的速度。

综合2中环境的测试数据,以目前前端服务器的最高负载(最高也就 200+次/秒),以及项目的实际负载,此代码性能足够满足需要。

终于解决favicon.ico无法显示的问题

很久以前就做好了favicon.ico,在浏览器的地址栏前面也显示正常.但是收藏在Firefox的书签工具栏里的时候,这个图标无法显示,所以显示的是空白页: .

我以为是Firefox2.0的问题,因为我用Firefox3.0beta的时候图标显示的非常好,只要在书签工具栏里收藏了这个网站,马上就会显示图标.所以我认为是Firefox2.0的bug.

今天sofish做了一个博客圈的RSS聚合.我再次发现favicon的问题,我的图标没有显示.我开始想,也许不是Firefox的问题.因为Firefox2.0显示别人网站的favicon都没问题,但是我的独独出错.可能是我的某些地方没有写标准.

我先把注意力放在了代码的书写上.

<link rel=”Shortcut Icon” href=”地址” type=”image/x-icon” />

<link rel=”shortcut” href=”/favicon.ico” />

<LINK href=”ico地址” rel=”SHORTCUT ICON”>

有以上这几种写法,都差不多.而且,如果favicon.ico放在根目录之下的话,可以省略这些代码.如果不在根目录下,就要给出地址了.我的代码没问题,ico的名字也是favicon.ico,放在根目录下, 很规范,所以问题不是出在这上面.

然后我就想到了难道是ico文件的问题.这个是我自己用photoshop画的,然后转成了ico,可能对于网络图标的有些规范或者约定俗成的东西没有注意.然后我打开了几个显示良好的blog的ico文件,还有我的ico文件,查看属性.

我首先以为是尺寸的问题,但是发现没有区别,都是16*16.再对比一下,发现问题可能出在ico文件的大小上面.一般的favicon.ico只有1-2K,我的有20+K.所以我换了漫步的图标,试试,硬刷新以后,书签栏上的书签显示良好了

我的结论是,favicon的大小虽然没有被规定应该是多大,但是还是小一点比较好,Firefox3.0还是beta版的浏览器,以后的网络可能会对大的favicon.ico更加宽容,但是现在,还是约定用小一点的图标吧.加载也快一点.

你的favicon显示有问题吗?看看它的大小是否合适吧.还有问题?留言告诉我吧

安装php5出现 mysql configure failed

安装PHP5时出现了错误。
configure: error: mysql configure failed. Please check config.log for more information.
./configure –prefix=/usr/local/php –with-apxs2=/usr/local/apache2/bin/apxs –with-gd –with-jpeg-dir –with-zlib-dir –with-png-dir –with-ttf –with-mysql enable-track-vars 出现的问题。

如果是自己选择GZ包安装的MYSQL。这里要加路径。
加的方法为–with-mysql-dir=/user/local/mysql
切记不可写为:–with-mysql=/user/local/mysql

Web在线任务管理

今天终于找到一个好用的在线任务管理网站:

Remember The Milk

http://www.rememberthemilk.com/

中文名怎么翻译呢?别忘了喝奶?

挺有意思的,小奶牛的图标很可爱,一下就吸引了我,让我决心好好研究一番。

是我自己要寻找这样的网站,最近好忙,每天都有好多工作要做,还有好多事想做。事情总是这样,有时候突然想做点什么,但工作很忙,没有时间;等周末休息的时候,有时间了,却突然不知道该做点什么。

工作的时候,会有很多很多任务,如何安排这些事,每件事应该在什么时候完成,都应该有预先的安排。新的工作环境,对我有很大影响。这里做事都必须有安排。

每周的开始,都要列出本周要完成的任务,以及每个任务的时间点。而且要明确任务结果,必须设置预期结果。

比如,今天是周一,制定本周计划:

  1. 某项目的升级详细设计,周三完成文档,周五完成评审;
  2. 某项目的升级模块,周二完成开发,周三完成测试,周四完成上线;

注意“完成”两个字,每件事,都要早明确的时间点,取得明确的结果。

这里不能使用应该、差不多、也许、大概和基本等等模糊的词汇。

现在我的工作状态越来越好,我觉得这背后的原因,就是做事有安排,合理分配,按时完成。

不过,我还缺少一个工具,来帮我规划个人任务。

以前我是写便签纸,就是黄色的小方块,上面有一条不干胶,记录我今天要做什么。

便签容易写,但不容易改,小小的空间,记不下什么东西,而且不容易保存,过后也想不起来都做了什么。

以前有人教我用Outlook的任务管理来提醒我什么时候该做什么。

我用着不习惯,因为这些都是提醒工具,而互联网工作的性质就是要应对突如其来的各种事件。

就算我订好了下午3点开始写文档,但可能2点半的时候发现线上服务的Bug,必须立即修复,等改完的时候已经是4点半了。当然,3点的时候在屏幕上出现提示框提醒我写文档,但那个时候我可能去理会可能去处理吗?事情过去了,很可能我就会忘了,也许直到第二天早上才会想起来昨天的事情没做完。

这类例子不用再举了,大家肯定都有各自的经历。

今天上午,搜了一下在线任务管理,找到了一个网站列表:

19个在线个人任务管理系统

我一眼就看见了这个RememberTheMilk,小奶牛还挺显眼的。

进去试用了一下,都是Ajax开发的,用户体验很好,可以按标签区分不同类型的任务,每个任务可以指定不同的优先级,还有详细的设置,感觉很好用。

我把一天的工作都写了进去,然后一项一项做,做完一样改一下状态,再做另一个,感觉一天都很有条理,也不会遗漏细小的任务。

具体的设计,很人性化,真的是个不错的工具。

我喜欢这样的网站。

Remember The Milk

简单快捷地管理日常任务。
设计优良的界面让您管理待办事务更加愉快。您甚至可以使用类似下周五两周内来定义事务到期时间。当然您也可以使用键盘快捷方式加速您的日程管理。

Remember The Milk

提醒,无处不在。
通过电邮、短信和即时通讯软件(支持AIM, Gadu-Gadu, Google Talk, ICQ, Jabber, MSN, Skype 和 Yahoo! )接收提醒。

Remember The Milk

按您的方式管理信息。
您可以根据需要创建任意多个列表。所有事件都可以定义标签,并且您可以通过“标签云”看到需要待办的事务。另外,您也可以针对事件创建便签记事。

Remember The Milk

定位您的任务。
使用地图来查看您的任务在现实世界所处的地点,查看他们是否就在附近,并且有计划的以最佳方式完成任务。

Remember The Milk

合作搞掂。
通过你的联络对象或整个世界分享、邮寄和发布任务与列表。提醒亲友做家务。

Remember The Milk

无论何时何地添加任务。
添加任务像发射一封邮件(甚至是打电话)一样简单。在网上查看一个重要的约会?用快速添加项将它添加到你的列表中。

Remember The Milk

随时随地访问你的待办事务。
用你支持web的手机来查看任务。打印表现未来工作的整个列表或者一份星期计划手册。用Apple iCal或者Google Calendar在你的日历中查看工作。订阅使用Atom/RSS的feeds。

Remember The Milk

分配您的时间。
看看今明两天该做的事与你错过未做的事情。设定其为优先,估计你的时间并作适当的推迟。设定每周重复的事情或者两个月后还要做的事情。

Remember The Milk

智能搜索。
您可以使用高级搜索功能查找事件。同时还可以将搜索保存成为智能列表,方便以后查看。

Remember The Milk

享受有条理的生活。
“撤消”功能意味着您再也不必担心操作失误。因此您可以放心地注册账户,并开始使用Remember The Milk。

澄清Java 接口与继承

计算机学院研二的兄弟与我讨论Java,一见面,几个问题全是关于接口,接口有什么用?为什么要用接口?什么时候该使用接口?很庆幸他们不是问我Java如何连接SQL Server,或者是如何开发J2EE应用,这类问题有杀伤力,避之则吉。今年计算机学院本科有个毕业设计课题是做J2ME,选这个题目的学生在5月末都还在苦着脸研究java.util.*这个包,这个这个……唉。

大多数人认为,接口的意义在于顶替多重继承。众所周知Java没有c 那样多重继承的机制,但是却能够实作多个接口。其实这样做是很牵强的,接口和继承是完全不同的东西,接口没有能力代替多重继承,也没有这个义务。接口的作用,一言以蔽之,就是标志类的类别(type of class)。把不同类型的类归于不同的接口,可以更好的管理他们。OO的精髓,我以为,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c 、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。(cowboy的名言是“抽象就是抽去像的部分”,看似调侃,实乃至理)。

设计模式中最基础的是工厂模式(Factory),在我最近的一个很简单的应用中,我想尽量的让我的程序能够在多个数据库间移植,当然,这涉及很多问题,单是如何兼容不同DBMS的SQL就让人头痛。我们不妨先把问题简单化,只考虑如何连接不同的数据库。

假设我有很多个类,分别是Mysql.java、SQLServer.java、Oracle.java、DB2.java,他们分别连接不同的数据库,统一返回一个Connection对象,并且都有一个close方法,用于关闭连接。只需要针对你的DBMS,选择不同的类,就可以用了,但是我的用户他会使用什么数据库?我不知道,我希望的是尽量少的修改代码,就能满足他的需要。我可以抽象如下接口:
package org.bromon.test;
public interface DB
{
java.sql.Connection openDB(String url,String user,String password);
void close();
}

这个接口只定义两个方法,没有任何有实际意义的代码,具体的代码由实作这个接口的类来给出,比如Mysql.java:

Package org.bromon.test;
import java.sql.*;
public class Mysql implements DB
{
private String url=”jdbc:mysql:localhost:3306/test”;
private String user=”root”;
private String password=””;
private Connection conn;
public Connection openDB(url,user,password)
{
//连接数据库的代码
}

public void close()
{
//关闭数据库
}
}

类似的当然还有Oracle.java等等,接口DB给这些类归了个类,在应用程序中我们这样定义对象:

org.bromon.test.DB myDB;

使用myDB来操作数据库,就可以不用管实际上我所使用的是哪个类,这就是所谓的“开-闭”原则。但是问题在于接口是不能实例化的,myDB=new DB(),这样的代码是绝对错误的,我们只能myDB=new Mysql()或者myDB=new oracle()。麻烦了,我还是需要指定具体实例化的是哪个类,用了接口跟没用一样。所以我们需要一个工厂:

package org.bromon.test;
public class DBFactory
{
public static DB Connection getConn()
{
Return(new Mysql());
}
}

所以实例化的代码变成:myDB=DBFactory.getConn();
这就是23种模式中最基础的普通工厂(Factory),工厂类负责具体实例化哪个类,而其他的程序逻辑都是针对DB这个接口进行操作,这就是“针对接口编程”。责任都被推卸给工厂类了,当然你也可以继续定义工厂接口,继续把责任上抛,这就演变成抽象工厂(Abstract Factory)。

整个过程中接口不负责任何具体操作,其他的程序要连接数据库的话,只需要构造一个DB对象就OK,而不管工厂类如何变化。这就是接口的意义—-抽象。

继承的概念不用多说,很好理解。为什么要继承呢?因为你想重用代码?这绝对不是理由,继承的意义也在于抽象,而不是代码重用。如果对象A有一个run()方法,对象B也想有这个方法,所以有人就Class B extends A。这是不经大脑的做法。如果在B中实例化一个A,调用A的Run()方法,是不是可以达到同样的目的?如下:
Class B
{
A a=new A();
a.run();
}

这就是利用类的聚合来重用代码,是委派模式的雏形,是GoF一贯倡导的做法。

那么继承的意义何在?其实这是历史原因造成的,最开始的OO语言只有继承,没有接口,所以只能以继承来实现抽象,请一定注意,继承的本意在于抽象,而非代码重用(虽然继承也有这个作用),这是很多Java烂书最严重的错误之一,它们所造成的阴影,我至今还没有完全摆脱,坏书害人啊,尤其是入门类的,流毒太大。什么时候应该使用继承?只在抽象类中使用,其他情况下尽量不使用。抽象类也是不能实例化的,它仅仅提供一个模版而已,这就很能说明问题。

软件开发的万恶之源,一是重复代码而不是重用代码,二是烂用继承,尤以c 程序员为甚。Java中取缔多重继承,目的就是制止烂用继承,实是非常明智的做法,不过很多人都不理解。Java能够更好的体现设计,这是让我入迷的原因之一。

Symfony 如何在项目中使用多个数据库

all:
database1:
class: sfPropelDatabase
param:
dsn: pgsql://foo:bar@hostname/database1

database2:
class: sfPropelDatabase
param:
dsn: mysql://foo:bar@hostname/database2

先建立一个
PROJECT_DIR/config/database1.schema.xml
再建立一个
PROJECT_DIR/config/database2.schema.xml

最后build一下:

$ symfony propel-build-model

$database1_connection_handler = Propel::getConnection(FooPeer::DATABASE_NAME);
$database2_connection_handler = Propel::getConnection(BarPeer::DATABASE_NAME);
Google

Google
LAMP-Linux-redhat LAMP-Apache LAMP-MySQL LAMP-Php Leakon-Wiki Leakon-BBS XueBaoBao Xyoyou