见世界

身不饥寒,天未曾负我

0%

年富力强,能有点事为之拼搏忙碌殚精竭虑,才算幸福。

发财的事,不得其门而入。没有存款,现有工作丢不下。
工作之余,我在浪费自己的时间。

这样不好。
看一辈子电视活成一个胖子也不是不能接受,但更多的事等着人去做,我愿意试一试。出路都是自己找的。

结个婚,步入26。对待生活、事业和一辈子将做的事,该有严肃认真的态度。
我逐渐发觉,人事有差异,看似谁都能做的事,并非谁都能做到。

结合objective-c把《算法精解》这本书上的数据和算法形成代码,修炼内功。
这山望着那山高不理智。做了程序员就做到top 20%,这里的机会才是我的机会。

“最近在做一个“评诗网”,guwen.wobu2.com。 去年年中测试爬虫时抓的数据,近期代码写得少,拿来练手保持节奏。不觉得这玩意儿能赚到钱,能做出点别处没有的功能,方便有需要的人就够了。运营一个微信号“问世间是否此山最高”,每日一首情诗情话。蛋疼的朋友可以加 most_favourite”

搜索引擎看不到微博(@Dlad)的推介,在这里做个入口。
虽然不对盈利有期待,但是搜都搜不到就太过分了。

一个诗文检索网站
http://guwen.wobu2.com

微信公众号:
问世间是否此山最高 (most_favourite)
每日一首情诗情话
psb

……

“可是还有一个人,列文,我认为他才是《安娜卡列琳娜》里真正的主角,他爱上了ketty, 遭到拒绝,但是他没有成为无病呻吟的人,又是忧伤,又是惆怅,又是悲悯,好像这世界上只有他一个失恋似的,甚至还会有人逢人就讲自己失恋的故事,像钱钟书写的”把他们的伤心立刻像叫花子的烂腿,血淋淋的地公开展览,博人怜悯,或者时过境迁,像战士的金疮旧癍,脱衣指示,使人惊佩 “(人民文学出版社《围城》106页)。列文回到他的农场,跟庄家人一起干活,他思考,写农业改革计划,他在爱情失败后,仍旧让他的人生丰富多彩,充满意 义和快乐。没有爱情的生活似乎不是很精彩,但也不至于黯淡无光。Ketty 对沃伦斯基的爱情遭到了拒绝,病倒了,到了乡下,时隔了1年左右列文和Ketty 再次相遇,相谈非常和谐。关键是列文依旧爱着Ketty. 然后在一个非常合适的场合,列文在桌上写”你的拒绝仅限于当时,还是永远”… Ketty 默默得看着列文… 这样的一对眼神直接把他们送进了教堂。这是我最最喜欢的一段关于爱情的描写。没有出轨,没有婚外恋,没有谴责,没有嫉妒,只有祝福,来自天上和地上的。”

 

看起来似乎很坚硬,其实一碰就碎了。
出冷汗,坐立不安。

无法违心的说热爱挫折。
但是挫折的确使人成熟,变的冰冷,增长器量。
生存压力之后,这是我唯一知道的有效动力。
踏实,专注,我又可以做到了。

 

 

知道是collation的问题,不想逐表修改,又没有好办法。
寻求redmine区域配置而不得,最终找到方法将所有表设定为 CHARSET=utf8。

参考这位同胞的方法:凍仁的筆記
导出表结构:
mysqldump –user=root -p redmine_default > redmine_default.sql
vim批量替换:
:%s/latin1/utf8/g
导入表结构:
mysql -uroot -p redmine_default < redmine_default.sql

PS:
前两天写接口collation默认latin,手动一个一个改成了utf8_general_ci.
心想应该写个脚本做这件事,只想了导出表名、批量生成sql一条路。
上面这个方法既直观也很方便,记一笔。

=============续:默认中文数据乱码=============
症状:过滤器下,第二列可选项乱码。角色中文名乱码。
涉及表trackers, roles, enumerations等。

redmine官方安装文档 中
第六步建立表结构,无法设定编码。(设定成功的兄弟请纠正)
第七步导入默认数据,可以通过REDMINE_LANG参数设定编码

RAILS_ENV=production REDMINE_LANG=zh rake redmine:load_default_data

若第六步数据库charset与collation未设定为utf8与utf8_general_ci,第七部插入中文数据会显示为“???”。
先按上文叙述设置charset,再导入中文数据能修复此问题。

接到任务为公司股东做个企业站。

日方站做得很好,心说争口气,让这些只接触过外包公司的传统行业见识下现代互联网。
商量好了流程,搭了框架Git,绷紧神经准备干一票呢。今天突然被叫停,非叫用个现成的改一下。

就这么几个页面,功能交叉不多,改cms未必有重头写来的快。达到高标准反而要做更多工作。

_被闪了一下。但也松了口气。
工作不忙的时候标准自动定得很高,工作忙过线就行了。
越是想把一件事情做好,越可能过于执着,看不清事实,不尊重规律。
这种一不是主要工作内容,二没有内行产品推动,三不是自己项目。何必呢?图什么啊!
同事说得很好,要不找个500块的外包吧

=================内心独白的分割线====================
记得上学时候,学习之外老师家长安排的事,都是过线就行。一直是个很懒散的家伙,何时变成一个负责的人了。
最恨不飘逸了!但是觉得自己很无趣的说……怎么回事
现在这样也不错,结果中庸有得有失,总体感觉靠谱。全按自己意思,疯起来受众太小……
容易路人转黑!

_

最近和不同风格的人一起工作,慢慢把握到自己的节奏。
接触了许多强人,受到很大震动。心情就像上膛的子弹,找到方向随时冲出去的感觉。

跟群里coder学习讨论了两周,还是觉得zf2坑略深,不顺手。
本周自己搭了个单入口mvc的框架。
实现越快越好,技术问题不是主要问题。
ios app节奏太慢,决定做之前基于map api的社交游戏,先让别人知道自己。

https://github.com/qdladoooo/bmw27
框架地址。基本机制已完成,边边角角用到再完善。
求羞辱,求指点,求探讨。

Linode是我见过的最专业省心的VPS服务商。
使用一年后,昨天把付费周期调整为两年自动续费。

放出我的referral code:2d3ccfa5fcad20b84d950a91e1b4600a2e8449b2
使用此code购买linode服务,服务运行满90天,Linode将支付我$20的推广费用。
我会分你一半 ^ ^

PS:
当年买VPS,一个叫 留点后路 的伙计号称可以返利,就用了他的referal code.
四个月后找他要钱,说账户没钱等下月,后来就不理我了。
这还是个打开门做生意的人,居然为了六十几块食言而肥……— —#

待整理待验证

制作全新ERP取代南湖现有ERP行不通。

正确方法应当选取10-100个涵盖80%日常操作的功能,建立快速原型搭建可用替代系统。在一段时间内新旧系统并行,逐步把功能从老系统向新系统迁移。直至新系统能够完全胜任工作。
唯有这样才能保证不脱离实际,吸纳现有领域经验的同时建立一套成熟、务实的旅游管理系统。

技术实施的过程中:

1.注重建立稳固底层,2.持续重构,3。有意识的把南湖当做一个普通商家来进行产品设计和数据结构设计。
最坏情况,在构建新系统过程中没人提纲挈领的 意识到/总结出 具有指导意义的模式。此时我们将得到一个使用互联网思想改造过的,同粤2旅游集团契合的不错的ERP系统。
较好的情况是,在构建过程中,面对各种逐渐浮现的问题,大家集思广益,找到各种场景下的捷径或最优解。得到一个大幅提高生产力的产品。

 

关于B2B平台。

考虑阿里巴巴,我认为通用平台比垂直平台逻辑上要简单。
阿里巴巴没能解决上下游信息屏蔽的问题,我们也不要去解决。
这不是智力高低或威权大小的问题。
纵观信息革命以来的趋势,靠阻碍信息流动牟利的公司/行业已日渐式微。

看到一句话大意说:看一个人的智力,要看他能否持有两种相反观点的同时,还具备行动的能力。

对我而言,这是一个鼓舞。
我意识到所谓的“单线程生物”“不能同时做多件事”“没有方向”,都是懒惰的借口。
把自己没在做正事的时间分出一半来做构架、设计、编码,就算要同时实践多个想法也够了。

被动的压力再也不能帮助我了。
我要。
要宝马的SUV,
要精装修的大房子,
要各式各样的开发板,
要系统训练所需的时间和金钱,
要经济自由。

做个有价值的产品,打动1000个人。
以此为目标开始行动。

今天做一个拖动排序的效果(demo
功能很明确,拖动部分居然出现问题。
调试近一个小时,最终发现是浏览器默认行为导致的。
对img对象进行拖动,ff和chrome会创造一个跟随的半透明图片副本,光标变为“阻止”。

 

 

同时鼠标移动不再触发mousemove事件。
解决方案:使用div设置背景替代img标签
<div class=”pic”></div>
.pic {
width: 13px;
height: 16px;
background-image: url(“ic_launcher.png”);
}

这个问题点略偏。
调试蛮久,记录一下。
贴下09年“想当然”实现的拖动类。
虽然后来看到了许多完备实现,然而面对问题时发觉还是自己东西用着最顺手。
预先解决小问题,碰到大问题就能降低复杂度并大大减轻思想负担。

被工作推着走了这么久,从技术狂热、跃跃欲试到克制热情、应用为王,
竟然已经有很久没有想着去解决一个复杂点的技术问题了。
有些项目觉得已经想的很清楚了,却迟迟不能下手。
今天顺着zuo.com看到许多执行力强人,颇受触动。
生活渐稳定,要多写代码让别人认识自己。

e.g.
$("#move_handle").bind("mousedown", {dragedDom: MoveableDom}, Drag.start);
var Drag = {
            dragedDom : null,
            difX : null,
            difY : null,
            start : function(event) {          
                Drag.difX = parseFloat(Drag.dragedDom.css("left")) - event.pageX;
                Drag.difY = parseFloat(Drag.dragedDom.css("top")) - event.pageY;

                $(document).bind("mousemove", Drag.dragging);
                $(document).bind("mouseup", Drag.end);
            },

            dragging : function(event) {
                Drag.dragedDom.css("left", event.pageX + Drag.difX);
                Drag.dragedDom.css("top", event.pageY + Drag.difY);
            },

            end : function(event) {
                    $(document).unbind("mousemove", Drag.dragging);
                    $(document).unbind("mouseup", Drag.end);
                    Drag.dragedDom = null;
                    Drag.difX = null;
                    Drag.difY = null;
            }
    };

_我认为负责PHP实现的相关程序员今天可以多做一个魂器_。

自年初爆出PHP易被攻击的hash设计漏洞,改用了5.4版本。
告别了各种包管理,有时要按需编译。

今天下午安装freetype扩展,试了多种靠谱方案(包括来自chinaunix和stackoverflow的方案)。
始终得到两种结果:
1.无效果,不报错
2.make错误,刷出一屏幕各种函数未定义
抓狂了。

静下心,换个思路。
删除多余包,编译安装PHP5.4.6,打开phpinfo一看,好了。
编译5.6.3不到2分钟,5.4.6超过五分钟;想起gd手册页有人说过

我觉得可能是,某些中间文件编译一次就被缓存起来的缘故。

近两天情绪状态不好,今天本来兴致盎然搞个cms,结果搞到很想死。
写点字记录,今后记得make clean这回事儿。

 

发微博上了,直接搬过来。

在最近的工作中我无意中找到一个简便易行的文本对象分组方法。设有3w个商品title,分词,记录不重复出现的词及其词频。取出现频率最高的双字词 100个,为每个title建立一个二进制的索引(每一位表示一个词是否出现过),这个索引已经为这批title画好了格子,每格中的数据具有一定相关 度。

用于索引的词汇数量,若一个词也不取,相当于所有数据在一个格子中。若取所有词汇,则格子数量等同于title数量,为3w。词汇的选取,能够控制分组的粒度。优化时一定可以通过调整“索引词汇”,取得较优解。最优解也在词汇组合之中。

这个算法的时间复杂度,全量为o(n),增量为o(1),可保证即时性。其中二进制索引字段支持任两条目间相关度的计算(比如:在两个条目中都出现的词除 以每个条目词汇数目)。后悔当年怎么没有想到这个算法。这是在一个边缘需求中,压力下两个小时实现的。3w数据分为2800组,肉眼看了下组内相关度,效 果明显。

这个东西满足了我的需求。绕过“两两比较,海量计算”的常规思路而且有一定效果。
当时如果用这个方法做比价项目,操作上就简单多了。

 

一直有点轻微强迫症。昨天丢了100块钱。
回忆,自责,臆断,诛心,懊恼。循环往复。
不过已经能够像以前那样开导自己了。感到重新开始,渐渐的务实。

进公司月余,想找机会申请转正。
a.以薪酬区分coder,在当前层次我得算是弓马娴熟了吧。
我都不争取“一月转正”,那这政策不是形同虚设了么?
b.如果此时不申请,而将来有谁申请并通过了,那自己颜面何存?
程序员本来都已经没有什么别的追求了。
c.何况六个月试用实在太长了,不能忍。

今日无事,抽空抓了些古诗文回来。
有事没事读一读查一查做做交叉索引,比图书馆借书便利多了。
一向说多做少,这次学乖拒绝思考直接抓。
考虑索引一下,类别理出来。
觉得用处还蛮多的。

创意才重要。
而我是好产品。
好到只肯做自己的产品经理。
没有程序员的时候,自学了程序。
现在回到开始的地方,在产品这条战线继续推进。
调动……组织……
目前宾语还是只有我自己。
设计!前端!
要么花钱买要么结成利益共同体,想要自己干的想法太二了。屌丝本位。

1.对现状很满意。
女朋友终于来到身边。
工作流程完善,现有架构有很多东西可以学。
和同样有志于程序的人共事。

2.但无数事实证明,我是那种凡事往最坏处想的杞人忧天强迫症患者。
因为对现状满意, 非常积极的担心会失去这份工作。
回来的路上,一边打kof97,脑子里都在想这件事。

3.我有自己的观点和底线。
会照顾他人的情绪。但如果问心无愧,判断不为外界倾斜。
这次是因为,我知道自己并没有尽全力工作。

4.早上到cnblogs.com看编辑推荐。
午餐时间,Google reader读几篇关心的博客。
下午四点,喷嚏图卦。
以前空闲时才会做这些事,现在有任务还这么干原因有三点:

a. 鳟鱼效应。
新鲜血液易使技术/自信不够强的老员工有压力。而我讨厌被人嫉恨。
同事之间,和和睦睦开开心心做事最重要。
做个纯粹的程序员我就很满意,若顺便做了有益于社会/影响世界的产品就更加好。
有套页面给我做,没走脑子随口说自己肯定比一般程序员写得快,当时就觉得说错话了。
_同事看着不太开心。我让他判断一下时间,他说他做要三天。后来找过来跟我说我不了解框架,写慢点没关系。在我看来,也许他怕我压力太大,也许怕我太积极影响不好吧。
_
我决定收着点编码,定了五天,所以才有时间读喷嚏图卦。
代码写得太快,若让boss认为团队之前偷懒,重估生产力,那以后大家日子都不好过。

b.接触到boss一个以前的下属,给描述,一字记之曰狠。
看以前代码,知道一个水平不错的写了很多低层机制的牛人刚刚离职。
全员持股,A轮之前进来,职位不低,为啥要走呢?
看到他凌晨3-4点写的代码。不是修复bug。
新一轮招聘进来,发觉公司人员零落。
听到一些传言,说公司无情东西做好人就逼走。
……
语言和代码立场鲜明,每天一起工作的感觉却不是这样,我还没有自己的判断。
但这些先入的观念的确令我心生防备。我还记得那个每每按时完成,却总得到更多工作和更短工期的coder。

c.我小声说,同工同酬吗?
钱是很好,但我不喜欢加班。
写得更快完成更多的工作维护更多的代码得到更多的bug。(设水平相同则与代码量正相关)
我把生命交给了别人,别人能为我负责吗?

写出来感觉好多了。
工程师不会没有项目做,但我现在不想面对未知。
我是一个病人,每天要和自己战斗。 这么想着反而涌起一种睥睨万物的气魄。

ps:
古人说:远则怒,近则狎,唯女子与小人难养也。
古人标准跟职场规则正好相反,不巧我却是个老派的人。

女人创业,也许的确是态度大于能力,关系重于业务。
我想要经济自由,但几万十几万这种程度不解决问题。
换份工作,做个外包也能赚到。
为此妥协在今天的我看来还不值得。

我会示好,不会讨好。
好好工作,因为我期望自己早日成为酷酷的专业人士。
而非为了谁的施舍。

没看到什么“sphinx最佳实践”,我这种懒人往往是读读文档,实现功能后就不再深究。
最近改了个增量索引引起的bug,借机思考总结出一些问题。写在这里。

现有tag, skill, skill_tag表,一个skill可以对应多个tag。
以id, tag_id, skill_id … …的形式导入sphinx。
通过php sphinxapi:setGroupBy的同时setSelect以此赋予tag_id不同的权重。
结果发现得到的权重与预期不同。
进过排查调试发现原因:从主索引、增量索引中检索数据,出现重复的skill_id。

这里涉及我们实现增量索引的方式。
我们不做合并,每天更新一次主索引,据skill_tag表最后更新时间设定界限,根据界限值每三分钟做一次增量索引。
问题就在于,这个限定条件,并不能限定skill_id在主、增索引中不存在交集。

解决思路只用一个索引,方案有两个:
1. 只做主索引,每三分钟更新一次,数据量不大时可以接受
2. 每三分钟做增量索引,合并进主索引。通用性比较好

=====================    主观的分割线    ===========================
解决这个问题的过程中我犯了几个错误,我惊恐的发现有些错误以前也犯过。

=============我的第一个问题=================
思路:

新入职公司,问题放在眼前。我看到

  1. 项目的sphinx取数据时做了大量联表操作,冗余了大量信息。查询语句也是把全文索引当做关系型数据库在用。
  2. 技术总监亲自调试了很长时间,没有结论。
    我一贯的观点是专用:索引的归索引,DB的归DB。我倾向于只使用sphinx加速文本检索,因为mysql作为关系型数据库,处理各个数量级的数据有一整套成熟方案。sphinx在关系型数据库方面的表现还是未知。

强力同事调试没有头绪,我判断是设计问题,猜测问题出在sphinx遍历数据时忽略了最大值之后的数据。请示后改用我的思路:小而专用的sphinx索引,id运算交给mysql。
方案的问题:
一元分词查询的准确度,显然不如id的关系运算。
换用coreseek进行分词,没有db配合,也不能完美重现程序功能。
上头又不许用“sql+缓存”的策略,因为推倒重来毕竟不现实。
总结:
做事风风火火,缺少深思熟虑和严谨验证。
具体到这个问题,本来很多细节都能够再敲定一下。我也有意识的去克制大动干戈的操作,却仍不足以驾驭犯二的热情。“技术狂热”的状态下,对判断依据的理解有太强的选择性。

我不是所谓的完美主义者,但一直觉得如果同样的错误只犯一次,那今天永远比昨天强。代码写下去,是在向前走。这是我学习方法的基础之一,这点必须保证。所以像这种意识到了却不能阻止其发生的错误,我认为需要写篇文章记录一下。理科生讨厌这种无力感。

=============之后的问题=================
定位是索引的问题,纠正的过程中险些又犯了错。
既然错误的症状是skill_id在主索引和增量索引上有重复,那么重写增量索引的建立逻辑,保证skill_id的严格分界是不是就解决了问题?于是着手改配置文件。
可是一旦限定了skill_id唯一,sphinx就不能及时更新skill_tag的关系变化情况,以tag为主题进行的搜索功能就缺失了。不能这么改。
是不是多建一个索引呢?或者变更业务逻辑使用另外的索引实现?隐约觉得这并不是好的解决方案。
我开始思考,能不能两全?进而思考,这种关系型的数据能否建增量索引?
关系的更新,不外乎add, delele和update,update可由add+delete实现,只有两种操作。
add操作,增量可以实现。delete呢?我们建增量的方式不能改动主索引,必须换方法。这个才是问题的根源。
思路走到这里,问题已经明朗,不由得为自己浅尝辄止的思维习惯感到后怕。
但是还没完。
我在crontab里看到这样一句被注释掉的命令:
#/root/ss/bin/indexer -c /root/ss/conf/xxxxx.conf –merge skill_tag_index skill_tag_delta_index –merge-dst-range deleted 0 0 –rotate
不知道谁因为什么加了注释,它为主索引同步了delete操作,亦即我们方法的缺陷。
这条命令是增量索引的常规做法必备的命令。

因为缺乏这个经验,因为没有切实的去部署那么一次,付出了很大的调试代价。
我从之前磕磕绊绊形而上思索带来的满足感中,一下子落回地面,觉得很空虚。
“十分力做八分事,掌握成熟的解决方案,以期完美的解决问题”,坚持这么做的自己反而尝到了苦果。这一点和上面的问题何其相似啊!

ps:
始终觉得,自高中懒做习题以来,对数学的远离影响了逻辑思维的严谨性。
想不到现在甚至远离了逻辑思维本身。
上面的推理进行的很艰苦,感到变化太多,无法控制。用笔在纸上画图的时候,居然觉得这一切十分陌生。有很久不打球咋上场肌肉颤抖的感觉。
用进废退,上一次深入思考什么东西是在啥时候?

人生能有几个欧洲杯决赛!
不过真的很困,有些脑力不济,做不了复杂的事情。

boss们在外面开会,我在读一本中文版的《集体智慧编程》。
先给需求场景,再讲技术。读起来容易理解也有动力。
sphinx让我能够“阅读”海量数据,这本书将教给我如何“理解”它们。

 

上个项目用了一个模块化的php爬虫。
这次有机会一展所长,一周时间写了这个。抓取部分用了threads模块,效率还可以。

b2c_crawler.gif

 

 

 

 

搭配db服务,实现抓取/更新和统计。作为一个解决方案能够满足要求。
尽管我已经把不同网站间的变化尽力封装进data_picker,但还是觉得不够通用。
1.仍需要编写针对性的代码;2.得到数据在db层面
至多算是个程序员工具,甚至可能只是我的工具。
不知道那些发布自用爬虫出去的人是怎么构架的。

关于perl语言,至今没有通读骆驼书,缺一步查漏补缺的过程。基础不扎实,有时碰到问题,不免怀疑走了弯路。加了群,感觉氛围不是太好。
又有人推荐学python。我觉得py不如pl这么有人文气息,更重要的是pyer缺少pler这种二感啊。

新生成的model : Friends.php

<?php
class Friends extends CI_Model
{
    private $ID;
    private $rid;
    private $frid;
    private $gid;
    private $source;
    private $status;
    private $crdt;
    private $updt;

    public $table_name = 'fs_friends';

    private $debug = TRUE;

    //
    private $mod_params = array();
    private $results = array();
    private $results_count = 0;

    //
    function __construct()
    {
        parent::__construct();
        $this->load->database();
        $this->load->helpers('memcache');
    }

    function create($para=array())
    {
        //sql
        $ksql = '';
        $vsql = '';
        foreach ($para as $key => $val)
        {
            $ksql .= $key. ',';
            $vsql .= '\'' . $val . '\',';
        }
        $ksql = rtrim($ksql , ',');
        $vsql = rtrim($vsql , ',');
        //sql
        $sql =  'INSERT INTO '. $this->table_name .'(' . $ksql .') ';
        $sql .=    'VALUES('.$vsql.')';

        if($this->debug)
        {
            echo  '<hr>';
            echo $sql;
            echo  '<hr>';
        }

        $ret = $this->db->query($sql);
        if(! $ret)
        {
            return FALSE;
        }
        else
        {
            return $this->db->insert_id();
        }
    }

    function Modify()
    {
        $sql = 'UPDATE '. $this->table_name .' SET ';
        $count = 0;
        foreach ($this->mod_params as $key => $value)
        {
            if ($count > 0)
                $sql = $sql . ', ';

            if (is_string($value) == TRUE)
                $sql = $sql . $key . '= \'' . $value . '\' ';
            else
                $sql = $sql . $key . '=' . $value;

            $count = $count + 1;
        }
        $sql = $sql . ', updt = ' . time() ;
        $sql = $sql . ' WHERE ID = ' . $this->ID;
        if($this->db->query($sql))
            return $this->db->affected_rows();
        else
            return FALSE;
    }

    // retrieve
    function Retrieve($ID)
    {
        $sql = 'SELECT * FROM '. $this->table_name .' WHERE ID = ' . $ID ;
        $query = $this->db->query($sql);
        if($query)
            return $query->row_array();
        else
            return FALSE;
    }

    function GetResultArray()   { return($this->results); }
    function GetResultCount()   { return($this->results_count); }
    function GetModParams()     { return($this->mod_params); } 

    /* get prop */
    function GetID()            { return $this->ID; }
    function GetRid()            { return $this->rid; }
    function GetFrid()            { return $this->frid; }
    function GetGid()            { return $this->gid; }
    function GetSource()            { return $this->source; }
    function GetStatus()            { return $this->status; }
    function GetCrdt()            { return $this->crdt; }
    function GetUpdt()            { return $this->updt; }

    /*set props */
    function SetID($val, $ignore_mod=false)  { if ($ignore_mod != TRUE) $this->mod_params['ID'] = $val; $this->ID = $val;  }
    function SetRid($val, $ignore_mod=false)  { if ($ignore_mod != TRUE) $this->mod_params['rid'] = $val; $this->rid = $val;  }
    function SetFrid($val, $ignore_mod=false)  { if ($ignore_mod != TRUE) $this->mod_params['frid'] = $val; $this->frid = $val;  }
    function SetGid($val, $ignore_mod=false)  { if ($ignore_mod != TRUE) $this->mod_params['gid'] = $val; $this->gid = $val;  }
    function SetSource($val, $ignore_mod=false)  { if ($ignore_mod != TRUE) $this->mod_params['source'] = $val; $this->source = $val;  }
    function SetStatus($val, $ignore_mod=false)  { if ($ignore_mod != TRUE) $this->mod_params['status'] = $val; $this->status = $val;  }
    function SetCrdt($val, $ignore_mod=false)  { if ($ignore_mod != TRUE) $this->mod_params['crdt'] = $val; $this->crdt = $val;  }
    function SetUpdt($val, $ignore_mod=false)  { if ($ignore_mod != TRUE) $this->mod_params['updt'] = $val; $this->updt = $val;  }

}

生成脚本,以及配套使用的pdo中间层: init.php

<?php
/*
 * @author: dlad@wobu2.com
 * @param: $tName, $cName
 *
 * @desc:生成通用的model代码,tableName,className以get形式传入
 *
 * @e.g.: 访问页面
 *        http://domain/init.php?t=ms_msg_unread&c=MsgUnread
 */
require_once('db.php');
if(isset($_SERVER['CLIENTNAME'])) {
    $tName = $argv[1];
    $cName = $argv[2];
} else {
    $tName = $_GET['t'];
    $cName = $_GET['c'];
}

$db = new db();
$db->Enq('use wz_sns;');
$sql = "desc $tName";
$colInfo = $db->Eq($sql);

$rows = array();
$rows[] = '<?php';
$rows[] = "class {$cName} extends CI_Model";
$rows[] = '{';

foreach($colInfo as $col) {
    $rows[] = "    private \${$col['Field']};";
}

$rows[] = '    ';
$rows[] = '    private $table_name = \'' . $tName .  '\';';
$rows[] = '    ';
$rows[] = '    private $debug = TRUE;';
$rows[] = '    ';
$rows[] = '    //';
$rows[] = '    private $mod_params = array();';
$rows[] = '    private $results = array();';
$rows[] = '    private $results_count = 0;';
$rows[] = '';
$rows[] = '    //';
$rows[] = '    function __construct()';
$rows[] = '    {';
$rows[] = '        parent::__construct();';
$rows[] = '        $this->load->database();';
$rows[] = '        $this->load->helpers(\'memcache\');';
$rows[] = '    }';
$rows[] = '    ';
$rows[] = '    function create($para=array())';
$rows[] = '    {';
$rows[] = '        //sql';
$rows[] = '        $ksql = \'\';';
$rows[] = '        $vsql = \'\';';
$rows[] = '        foreach ($para as $key => $val)';
$rows[] = '        {';
$rows[] = '            $ksql .= $key. \',\';';
$rows[] = '            $vsql .= \'\\\'\' . $val . \'\\\',\'; ';
$rows[] = '        }';
$rows[] = '        $ksql = rtrim($ksql , \',\');';
$rows[] = '        $vsql = rtrim($vsql , \',\');';
$rows[] = '        //sql';
$rows[] = '        $sql =  \'INSERT INTO \'. $this->table_name .\'(\' . $ksql .\') \'; ';
$rows[] = '        $sql .=    \'VALUES(\'.$vsql.\')\';';
$rows[] = '        ';
$rows[] = '        if($this->debug)';
$rows[] = '        {';
$rows[] = '            echo  \'<hr>\';';
$rows[] = '            echo $sql;    ';
$rows[] = '            echo  \'<hr>\';';
$rows[] = '        }';
$rows[] = '        ';
$rows[] = '        $ret = $this->db->query($sql);';
$rows[] = '        if(! $ret)';
$rows[] = '        {';
$rows[] = '            return FALSE;';
$rows[] = '        }';
$rows[] = '        else ';
$rows[] = '        {';
$rows[] = '            return $this->db->insert_id();';
$rows[] = '        }';
$rows[] = '    }';
$rows[] = '    ';
$rows[] = '    function Modify()';
$rows[] = '    {';
$rows[] = '        $sql = \'UPDATE \'. $this->table_name .\' SET \';';
$rows[] = '        $count = 0;';
$rows[] = '        foreach ($this->mod_params as $key => $value)';
$rows[] = '        {';
$rows[] = '            if ($count > 0)';
$rows[] = '                $sql = $sql . \', \';';
$rows[] = '';
$rows[] = '            if (is_string($value) == TRUE)';
$rows[] = '                $sql = $sql . $key . \'= \\\'\' . $value . \'\\\' \';';
$rows[] = '            else';
$rows[] = '                $sql = $sql . $key . \'=\' . $value;';
$rows[] = '';
$rows[] = '            $count = $count + 1;';
$rows[] = '        }';
$rows[] = '        $sql = $sql . \', updt = \' . time() ;';
$rows[] = '        $sql = $sql . \' WHERE ID = \' . $this->ID;';
$rows[] = '        if($this->db->query($sql))';
$rows[] = '            return $this->db->affected_rows();';
$rows[] = '        else ';
$rows[] = '            return FALSE;';
$rows[] = '    }';
$rows[] = '    ';
$rows[] = '    // retrieve';
$rows[] = '    function Retrieve($ID)';
$rows[] = '    {';
$rows[] = '        $sql = \'SELECT * FROM \'. $this->table_name .\' WHERE ID = \' . $ID ;';
$rows[] = '        $query = $this->db->query($sql);';
$rows[] = '        if($query)';
$rows[] = '            return $query->row_array();';
$rows[] = '        else ';
$rows[] = '            return FALSE;    ';
$rows[] = '    }';
$rows[] = '    ';
$rows[] = '    function GetResultArray()   { return($this->results); }';
$rows[] = '    function GetResultCount()   { return($this->results_count); }';
$rows[] = '    function GetModParams()     { return($this->mod_params); } ';
$rows[] = '    ';

$rows[] = '    /* get prop */';
foreach($colInfo as $col) {
    $forFunName = ucfirst($col['Field']);
    $rows[] = "    function Get{$forFunName}()            { return \$this->{$col['Field']}; }";
}
$rows[] = '';
$rows[] = '    /*set props */';
foreach($colInfo as $col) {
    $forFunName = ucfirst($col['Field']);
    $rows[] = "    function Set{$forFunName}(\$val, \$ignore_mod=false)  { if (\$ignore_mod != TRUE) \$this->mod_params['{$col['Field']}'] = \$val; \$this->{$col['Field']} = \$val;  }";
}
$rows[] = '    ';
$rows[] = '}';

//写入文件
$fh = fopen("{$cName}.php", 'w');
fwrite($fh, implode("\n", $rows));
fclose($fh);

db.php

<?php
/*
 *   @author dlad@wobu2.com
 *
**/

class db {
    static protected $pdo;

    function __construct() {
        $this->GetInstance();
    }

    //获取唯一的数据库连接对象
    function GetInstance() {
        if (self::$pdo == null) {
            self::$pdo = new PDO(__DATABASE_DSN, __DATABASE_USER, __DATABASE_PW);
            self::$pdo->exec('SET NAMES UTF8');
//            var_dump("once");
        }

        return self::$pdo;
    }

    //执行sql语句,返回结果矩阵
    function Eq($sql) {
        $res = self::$pdo->query($sql);
        //如果存在,打印错误信息
        $this->IsError();
        $res->setFetchMode(PDO::FETCH_ASSOC);
        return $res->fetchAll();
    }

    //执行sql语句,返回单行结果
    function Eor ($sql) {
        $res = self::$pdo->query($sql);
        //如果存在,打印错误信息
        $this->IsError();
        $res->setFetchMode(PDO::FETCH_ASSOC);
        $data = $res->fetch();
        return $data;
    }

    //执行sql语句,返回单列结果
    function Ec($sql) {
        $res = self::$pdo->query($sql);
        //如果存在,打印错误信息
        $this->IsError();
        $res->setFetchMode(PDO::FETCH_ASSOC);
        $data = $res->fetchAll(PDO::FETCH_COLUMN, 0);
        return $data;
    }

    //执行sql语句,返回数量(单个结果)
    function Es($sql) {
        $res = self::$pdo->query($sql);
        //如果存在,打印错误信息
        $this->IsError();
        $res->setFetchMode(PDO::FETCH_NUM);
        $data = $res->fetch(PDO::FETCH_COLUMN);
        return $data;
    }

    //执行sql语句,返回影响行数
    function Enq($sql) {
        $res = self::$pdo->exec($sql);
        //如果存在,打印错误信息
        $this->IsError();

        return $res;
    }

    //todo:貌似用不到啊,直接用pdo对象吧
    //执行sql语句,返回影响行数
    function ExecuteNoneQueryWithPrepare($sql) {

    }

    //调试信息,发布后可关闭
    function IsError() {
        $errorCode = self::$pdo->errorCode();
        if($errorCode != '00000') {
            var_dump(self::$pdo->errorInfo());
            exit();
        }
    }

    //添加引号
    function quote($var) {
        return self::$pdo->quote($var);
    }
}

还有一个处理模版的perl脚本,作用是做掉init.php中“$rows[] = ‘ xxxxxxxxx’;”这种机械性的工作。
刚发现被自己的sync覆盖掉了(sync很靠谱,主要是刚入职没有公司域账号,后来切换时程序没有保存)。逻辑很简单,就是逐行修改,注意替换单引号和美元符号,生成后最好再使用语法高亮的编辑器肉眼检查一下。
===================失而复得的分割线==========================
2012年6月14日15:19:26
新电脑启用sync,居然找到了。果然靠谱,不枉信你这么多年。有个问题就是没有考虑数组$ar[‘name’]中的单引号,多的话加条规则吧。

#!/usr/bin/perl -w
use strict;

open SFH, '<s.php';
open DFH, '>d.php';

while(<SFH>) {
    chomp $_;
    $_ =~ s/\\'/\\\\'/g;
    $_ =~ s/'/\\'/g;
    my $line = '$rows[] = \'' . $_ . '\'' . ";\n";

    print DFH $line;
}

close DFH;
close SF;

===================失而复得的分割线==========================
其实这个需求很适合使用heredoc语法。
我在使用过程中发现一个无法回避的问题是:heredoc内部转义变量或heredoc同其他字符串变量连接,会消灭掉除heredoc中“可见”换行效果外的所有换行(测试中涉及文件换行主要使用双引号内的\n,chr(10),chr(13)和chr(10)chr(13))。
换句话说就是,不使用heredoc语法,输出文件能正常换行。
heredoc包含变量,则转义的变量中换行符失效;将heredoc和一个包含换行的字符串变量连接,变量中的换行符失效。
初步的结论是存储heredoc的变量不能简单当做字符串变量使用。
这算不算php的一个bug呢?还有待严谨设计的实验去证明。

======================忍不住要说的分割线==========================
我一直对oo持保留态度(博问)。认为复杂度低的情况引入oo弊大于利。
不止一次在博客微博甚至面试中吐槽。“这种工程技术,是为了让5w 年薪coder胜任20w年薪工作而设计的。”上文中“年薪”替换成“智商”也没问题。
这次项目使用CI框架,又见如此Model。
你说这一坨坨的set,get函数有神马用啊?
你当你是C艹啊?

团队协作嘛,统一标准的好处很明显。我不是也写了生成函数做这事儿了么。
不过还是希望今后php的程序之路中,能早日发现ORM不可替代的便利。

迁移博客到linode vps。
sae(新浪app引擎)没有常规文件io,不能自动更新,不能上传图片。不太方便。
而且最近在公司发觉隔段时间就不能访问(可能由于出口ip单一,访问量过大被限制)。
总之先迁回来了。

分三步
1.代码checkout到vps
2.数据库使用DeferredJob导出(phpmyadmin导出会触及单位时间限制)
3.进入wp后台“设置=>常规”修改域名

额外做一些事情
博客文章中的图片链接还指向sae,在数据库中批量替换

UPDATE
  wp_posts
SET
  post_content = REPLACE(
    post_content,
    'http://dlad.sinaapp.com',
    'https://wobu2.com'
  )
WHERE post_content LIKE "%sinaapp%"
  AND post_status = 'publish'

最后修改原wp入口文件,加入跳转语句

$url = $_SERVER['REQUEST_URI'];
header("Location:https://wobu2.com" . $url);

 

作为一个伪军,我的门卡信息不能进入考勤系统。
每天早晚要发一封签到邮件,悲催。TAT
但是我发现可以这么干

sendemail -f liyaohui@snda.com -t dlad@wobu2.com -u "上班" -m 1 -s smtp.snda.com -xu xxx-xp xxxxx

明天是不是不用来了^^

======================贱贱的分割线===========================
2012年5月25日12:55:05
早睡早起,吾所欲也。
上面的想法多半是个玩笑,没有真的决定使用。
不过……下雨天打孩子,闲着也是闲着。我还是把它完成了。
限制
公司电脑是绑定ip的,公司邮件也需要内网ip,邮件密码包含密保随机码。
随身设备只有台BB8310,可浏览网页。

我在外网服务器上启用了一个页面。
设置接口,维护一个状态位和一组随机码。
之后在本地虚拟机做了个守护进程

#!/usr/bin/perl -w
use strict;
use LWP;

$| = 1;

my $url = '此页面常年提供1.是否发送邮件;2.随机码;3.参数设置接口';
my $cmd = '邮件发送命令';

my $ua = LWP::UserAgent->new;

while(1) {
    my $res = $ua->get($url);
    my $content = $res->content;

    #解析数据
    my @match = split '######',$content;
    my $switch = $match[0];
    my $rkey = $match[1];

    #关闭状态位,并发送邮件
    if($switch eq 'on') {
        #设置状态位
        $res = $ua->get($url . "?参数列表");
        $content = $res->content;
        #若状态位的确关闭,发送邮件
        if($content =~ m/^off/) {
            system($cmd . $rkey);
        }
    }

    sleep(60);
}

如此,若不关公司电脑,我能够手机访问指定网址设置随机码,一分钟之内即会发送签到邮件。