<?xml version="1.0" encoding="UTF-8"?><!-- generator="WordPress/ 5.0.1 Final" -->
<rss version="0.92">
<channel>
	<title>PHP源码阅读，PHP设计模式，PHP学习笔记，项目管理-胖胖的空间</title>
	<link>http://www.phppan.com</link>
	<description>PHP　内核　扩展　程序　项目管理</description>
	<lastBuildDate>Wed, 18 Jan 2012 02:40:23 +0000</lastBuildDate>
	<docs>http://backend.userland.com/rss092</docs>
	<language>en</language>
	
	<item>
		<title>胖子的乱看乱想笔记二</title>
		<description>1、最困难的时候，你需要做的就是坚持突围，不要想别的，因为没有其他的路可以走。－－ 乐淘网CEO毕胜

2、一个好的Leader做好两个字就够了：“勇”和“容”。前者是勇于承担责任；后者是从内心包容下属。 －－ 网络：双鱼座

3 、过程的形参名必须局部于有关的过程体--SICP

.entry p {margin:13px 5px 0 5px;}

4、你得让计算机告诉你它在干什么，它干了什么！

5、项目越大，所需要的人越多，从而沟通的成本也就会越高，如果解决了沟通问题呢？

6、并发，时间是一个本质问题 ﹣SCIP

7、将语法分析和执行分离，分离后，可以专注优化各部分，各自成长。

8、第一，要相信没有什么真正难的程序；第二，要意识到程序是写给人看的。--Bernie Cosell

9、不要只阅读那些编程习惯跟自己一样的家伙的代码--Dinald Knuth

10、都是解渴，有的白开水，有的要泡杯茶　－　Damon Peng

11、遇到问题，一定要到现场亲自观察确认问题的根本原因，不要用经验轻易下结论。现场有神灵。

12、有其他涉众的问题，先沟通，再做事。

13、做技术也需要了解你的产品所处的环境，可能出问题的不是你的代码，而是你的代码所在服务器国家的某些政策和法规。

14、预警机制的方式不能仅仅是邮件，在这里也应该有高可用性的考量，确保一种方式失效后有其它的方式可以马上替换或自动切换。

15、1.写文章，多发表个人见解，增加个人思考机会；2.大量看书，自学，但一定要选好书；3.多和圈里高手交流，听君一席话，胜读十年书；4.建立个人文件管理系统，不断整理自己的原创；5.参加系统学习，找到短板，快速学习；6.实践，大量实践！ -- 李开复: 如何提升个人专业能力

16、经验这东西，往往并不能告诉我们什么一定对，但是可以告诉我们什么一定不对。﹣霍炬

17、书是一个很好弥补没有高手在场的方法，因为书是最好的老师。﹣徐宥

18、分析每天时间怎么花的

19、系统发布时要考虑预热时间。 

19、 自古不谋万世者,不足谋一时; 不谋全局者,不足谋一域。 ---[清] 陈澹然《寤言二迁都建藩议》

20、不登上山顶，你就无法体会登顶的感觉，也无法看到山顶的风景。

21、成功的几率很小，因为必须同时应对的因素太多　－　《形式综合论》

22、敬人者人恒敬之 爱人者人恒爱之　－　《孟子》

23、 我的天性是挺胸直立，骄傲而无所畏惧，勇敢地面对这个世界。－《管理精英宣言》

24、制定计划从大到小，实施计划从小到大，先有想法，然后再想办法让想法落地。

以上的内容纯粹属于胖子各种时间记录在evernote的汇集，此版权不属于胖子 </description>
		<link>http://www.phppan.com/2012/01/evernote-02/</link>
			</item>
	<item>
		<title>一次查询优化过程</title>
		<description>问题描述
在正在维护的系统中有一个数据分析的模块，用于来分析一些用户访问的数据。其中有一个操作是查找某URL地址对应的ID。在这里ID与URL是存储MySQL数据的一个表中，此表就两个字段，id,url.表存储引擎类型MyISAM，当然URL字段是加了索引的。现在每天按小时定时分析数据，开始程序没有问题，当几年的数据积累下来，特别是最近业务量增加时，明显感觉到程序的执行过程变得很漫长，通过xdebug分析一次执行过程的所有执行时间，发现瓶颈在于前面所提到的通过URL地址对应的ID。下面就开始了漫长的优化之路。

.entry p {margin:13px 5px 0 5px;}



第一次优化：优化细节，针对特殊地址的优化
由于应急，先考虑一些细节的调整：
首先，我们针对一些特殊的地址进行了处理，直接返回ID，比如没有URL的情况，比如Google的首页地址；
然后，我们针对一些常见的地址作为Hash存储，以及一些热数据进行内存缓存。

结果，提高一些性能，能满足当时的业务需求，特别是当特殊的地址较多的时候，其情况还是非常乐观的。

过了一段时间，到了另一个高峰点，发现此分析模块仍然会时不时的出些问题，导致数据不准确，各种情况，各种应付...不得已，开始思考另一个方案。

 
第一个方案： key-value数据库
 依据前面的对于热点数据，将其存储到内存缓存的思路，我们考虑是否将所有数据都作为热点数据，于是先做前期预研，将部分数据导到redis中，发现速度有了极大的提升，大概在500倍的一个量级，那叫一个激动，于是决定将所有数据都迁移到redis中，想法是美好的，结果是不言而喻的。失败了。为什么呢？很简单，没资源，我们没有那么大的内存存储N个上千万的表。

 
第二个方案：基于单词查找树的文件结构
从前面的key-value数据库方案看，存储url到id的映射，在实现思路上还是可行的，于是开动了小心思，既然内存不够用，那么用硬盘呢？

这个既便宜又实惠，是居家旅行，杀人越货的必备良品。于是计划以URL的主域名为目录，以每个地址的md5值为文件名，每个文件存储这个地址对应的ID。考虑到域名可能很多，于是计划使用变形的单词查找树来设置多级目录，但是当域名地址太长时，文件系统在生成目录的时候会出错，只得去掉单词查找树，使用某种简单的按主域名转化后的字符串转建立目录。在预研时，先生成了5万数据的文件结构，发现生成的速率很慢，并且由于大量小文件的生成，对于整个目录的查询会很慢，但对于单个文件的读取还是很快的，然而将随着数据生成越来越多，当达到40万时，整个目录占用了大概4G+的硬盘空间。

假想一下，当1千万的数据以这种方式存储到硬盘上，会是多少？如果数据持续增加呢？到达一个亿呢？感觉到不靠谱了。于是继续想下一种方案，这个方案继续执行。

 
第三个方案：信赖于数据库，增加md5值存储的字段
在第二种方案中，我们是使用md5值作为文件名，在想在数据库对于md5后的文件名的查找会更快一些呢？在ID和URL映射表中，增加一个字段url_md5，存储url使用md5后的值，在这里我们直接使用MySQL的md5函数更新表数据，测试后，发现其速度有了显著提升，单个查找操作大概能提升50倍+。整体性能提升5倍的样子。于是，果断采用此方案，结果是该分析模块一直没啥事发生。(^_^)

 
总结

不要过早优化
优化要先找瓶颈
数据结构和对工具的使用很重要
优化时需要理清问题的根本原因是什么，最好能到理论层面或实现原理层面。
 </description>
		<link>http://www.phppan.com/2012/01/query-optimization/</link>
			</item>
	<item>
		<title>无分支开发</title>
		<description>在一个代码库上创建分支的能力是版本控制系统最重要的特性。它的作用是创建一个副本，并将在这个副本上进行操作，从此它就有了它自己的发展方向，也许有一天会回来，回来时就会合并。分支的作用就是为了更好的帮助并行开发，即在同一时刻能够在两个或更多的工作流上面开发，而不会相互影响。分支开会可以用于哪些应用场景呢？

.entry p {margin:13px 5px 0 5px;}

假设我们现在使用的是SVN，并且都是在主干上开发，此时A项目组下在开发，而B项目组准备发布，并且两个项目组中间有一些公共的代码，这个时候就产生了B项目组发布对于A项目组的时间依赖。此时分支开发是一种解决方案。

常见的分支方法有两种：

在开发时分支。针对不同的项目或开发团队做分支操作，每个项目或开发团队有一个副本，其所有的操作都在副本上进行，在发布时将分支合并到主干，做整体的合并集成，并做整体发布。这是先分后合的招式。

在发布时分支。主干上开发完成发布后，创建分支，在分支上修改BUG，当修改的BUG发布时将修改的内容合并到主干。这是先合后分再合的招式。



分支是基于并行开发的一种理想状态，即各个分支是完全独立存在的，并且互不干扰，各成系统。但是现实是各个分支基本上都是有一些关联或依赖的。在开发时可能没有感觉，但是当需要将分支的内容合并时，就会产生较多的冲突和问题，虽然现在的版本控制系统已经在这个方面做得相当好了，但是实际操作中还是会存在一些问题，还是会花费较多的时间。因此在做分支开发时，通常会建议以尽可能高的频率将分支的代码合并到主干净，并且确保主干的正常运行，这样会在一定程序上减缓最终实施合并时的冲突。

什么是无分支开发呢？所谓无分支开发就是指所有的开发工作都在主干上进行，当然这是一种理想情况，但是我们可以尽可能向它靠拢。如果实施了无分支开发，此时就不会存在分支、合并、解决合并冲突等问题了，解决合并冲突的问题最好的方法就是永远不要合并，这和赌博不输的最好的办法就是永远不赌一样。为什么说这是理想情况呢？因为在我们的项目中存在大量的依赖，一个文件或一个类，甚至一个函数可能会被多个人或多个团队修改，如果是多个团队的多个项目并行的话，这咱依赖关系将会影响项目的发布。那么怎么减少这些依赖，嗯，是减少，因为我们基本不可能完全去掉这种依赖，这和我们写代码一样，一些有必要的耦合是完全存在的，并且有其存在的必要性。


迪米特法则。迪米特法则（Law of Demeter）又叫作最少知识原则（Least Knowledge Principle 简写LKP），”不要和陌生人说话“，也就是说一个对象应当对其他对象有尽可能少的了解。这是到类级，对象级别的讲究。

减小需求的粒度。以一种更加敏捷的方式实现需求，将大的需求分割成小的需求，以增量的方式实现并发布。

使用通过抽象来模拟分支的方式实现代码库中大范围的变更。

对于公共部分做到文件级的版本发布。即一次发布多个文件的多个版本。

使用模块和组件，从功能、团队或变更频率等角度对代码进行切割，以解耦代码间的依赖.

将新功能隐藏起来，直到它完成。


以上的几点更适合于小规模团队。

关于第一点，这是面向对象设计的重要原则之一，除此之外，我们经常还会念叨开闭原则，KISS等等。这些都是实现无分支开发时代码级的一些优化措施。

关于第二点，将大的需求变为小的需求本身就是一个比较困难的工作，拆分的原则是什么？粒度以多大 为优，这些都需要依据实现的情况进行判断，以周为单位？以页面为单位？这种方式会比较轻，可以随时停下，即使需求方向有问题，我们也是可以在较少损失的情况下调头。如果我们坚持这样做，那么就意味着我们在解决一个问题：保持应用的持续可工作。

关于第三点，通过抽象来模拟分支的实现步骤如下：

当需求分解后还是无法增量开发或增量开发无法解决问题时，考虑引入中间层或抽象层。使要修改的部分与调用者分离，使其直接低耦合。

在需要修改的那部分系统代码上创建一个抽象层。

重构系统的其他部分，让他使用这个抽象层。

创建一种新的实现代码，在它完成之前不要将它作为产品代码的一部分。

更新抽象层，让他使用这个新的实现代码。(如果没有完善的回滚机制，建议在此处做切换开关，即以一个变量控制抽象层的实现，当然，这并不是一个优雅的解决方案)。

移除原来的实现代码

如果不再需要抽象层，就移除它。

关于第四点，对于公共部分的代码，其修改频率较之其它功能模块更频繁，并且可能存在需要发布旧版本的需求。

关于第五点，模块化，组件化在架构设计中现在已经是非常普遍的行为，但是要想完全模块化或组件化，其难度较大，尽可能的向这方面靠拢，以一定的规则划分模块，针对模块进行设计实现，并在最后将所有模块的结果有机的结合起来。这是在设计层面的分支和合并操作的替代品。

关于第六点，将新功能隐藏起来，直到它完成。这是在没有增量式发布，小步快跑的前提下，或需求确实无法拆分，一组特性一定要一起实现，此时通常会开一个分支做这些新功能的发布，而无分支开发中，可以将这部分代码放在主干，但这些功能对用户不可见，以某种配置项（文件或数据库都行）的方式来操作其对用户的可见性。

读《持续交付》总结一 </description>
		<link>http://www.phppan.com/2012/01/no-branch/</link>
			</item>
	<item>
		<title>2011年度总结</title>
		<description>时间在不知不觉中从我们的指间悄悄的溜走，有些忙乱的2011已经离我们而去，是我们度过了2011，还是2011度过了我们？

回首一年的历程，有过出错，有过迷茫，有过灰心，也有过激情迸发，随着时间的流逝，一切都过去了。现在的我们不需要为那些过去而惋惜什么，珍惜当下，错过的不要再错，做得好的我们继续……

2011就这样过去了，以前没有写总结的习惯，那么从2011开始吧。

今年工作重心发生了一些变化，从纯粹的简单的写程序变成了业余写程序。从开始的各种忙乱，各种不适应，各种出错，各种请求指点，到能应付常规工作，能够满足项目进程推进，能够关注项目技术选型和决策，能够挤出一些时间陪女朋友。虽然过程之中还有错误出现，但是已经有了一些初步的想法和解决方案，这些待到下一次写总结前解决吧。当然，对于写程序，还是非常有热情，也非常希望能够参与项目的开发工作，只是没能实现，也希望能挤出时间回到那个纯粹的编码时间。这些在来年会有一些改变。

今年一个读书比较杂的一年，从douban读书列表中看到总记是60+本书，包括技术、管理、心理学、时间管理、经济学、小说等等。还是和之前一样，不求甚解。不过也有重读之前读过的一些书。对于经常翻书的我来说，重读经典书籍是非常有必要的，这个问题需要在2012有更深刻的体现。

TIPI项目在2011年发布了6章，第7章也写得差不多了，但是由于团队内部认为本章及前面章节需要优化，则没有在元旦节前发布，这也算一个遗憾。这一年中对于TIPI项目的时间投入较多，当然这不仅仅是我一个人的投入，更多的是reeze，er在这个项目上的付出，感谢上苍让我遇到TIPI的兄弟，从兄弟们的身上我也学到了许多，包括对于项目的参与，关于文档的书写，对技术的热情等等。谢谢！这个项目还将继续，在来年会有更多，质量更好的章节发布。

今年的blog没有之前写得多了，也差不多在一周一篇的样子，这个速度在2012也是要保持的。在写blog的过程中能够梳理自己的知识体现，记录自己学到看到的东西，记录自己曾经出现问题的地方，沉淀自己的过去。在evernote中积累了N多的未完成的文章列表，可能是当时有一个点，想写一下，记录下来后却没有时间去清理，这在2012是需要不断清理的。坚持、沉淀、厚积。

需要感谢人：Damon、reeze、er、晓民、江江、家里的领导、国恒、念洋、Poleon、Hilda、Justin、Ben、fffox、blank yao .....　

以上没有先后，只是想表示自己的谢意，当然肯定还有遗漏的，感谢你们在2011年对我好，对我不好，陪我扯淡、让我陪我高兴，让我伤心…… 我这条线在2011是与你们的直线想交，因为有了你们，所以才精彩，谢谢！

2012，给个量化的值吧。

读书：10+技术书，30+非技术且不含小说

工作：把没有实现的那2个想法实现，参与开发，每周至少一个工作日在编码。

生活：把1件大事给办了。

TIPI；发布3章+


 </description>
		<link>http://www.phppan.com/2012/01/2011-summary/</link>
			</item>
	<item>
		<title>wordpress升级到3.3引起的血案</title>
		<description>某位哥哥因为看着wordpress3.3的升级提示激动了，一不小心就将它升级了，升级后一切正常，只是之前可用的附件上传功能现在不能用了，显示报错如下:



Warning: touch() [function.touch]: SAFE MODE Restriction in effect.
 The script whose uid is 10041 is not allowed to access /tmp owned by uid 0 in ...... 



各种纠结......


依据提示我们知道是由于PHP开启了安全模式的原因，最简单的办法：修改php.ini文件，将safe_mode设置为off，将安全模式关闭。


只是这是生产环境，关闭安全模式不太靠谱。只能换方案。
依据刚才的错误提示，我们知道是安全模式在检查执行程序的文件的UID与需要写入的文件夹的UID不一样，这是因为在PHP的安全模式中，对于部分函数是需要检查被操作的文件或目录是否与被执行的脚本有相同的 UID（所有者），很不幸，touch就是这样的函数之一。当然，有这个规则就应该有跳过这个规则的办法，在php.ini文件中，我们可以设置safe_mode_include_dir参数，多个路径以冒号隔开(Windows是分号)，在此参数内设置的目录及其子目录都将会越过 UID/GID 检查。


除此之外，安全模式相关的还有一个open_basedir设置项，PHP 所能打开的文件限制在open_basedir指定的目录树，包括文件本身。本指令不受安全模式打开或者关闭的影响。


safe_mode_include_dir和open_basedir指定的限制实际上是一个前缀，而非一个目录名。这也就是说“safe_mode_include_dir = /tmp”将允许访问“/tmp和“/template”(如果它们存在的话)，。如果希望将访问控制在一个指定的目录，那么请在结尾加上一个斜线，例如：“safe_mode_include_dir = /tmp”。 


这两个与本次安全问题相关的内容都添加了/tmp和当前操作的目录，发现依旧报错。

追根溯源，从源码中开始吧，如果对于PHP源码不太熟悉，那么我们可以从本次事件的关键点safe_mode_include_dir开始。


在PHP源码中搜索safe_mode_include_dir，除了与此配置项相关的初始化操作，我们可以找到验证此项的函数php_check_safe_mode_include_dir。现在，我们可以用GDB　debug的方法查看，给php_check_safe_mode_include_dir函数增加断点，运行一个你认为一定会经过此参数检查的PHP函数调用，如fopen等。运行，发现会在断点处停下，如果我们运行一个只包含了touch函数的代码，会发现程序会执行完，即touch函数根本就没有调用php_check_safe_mode_include_dir函数，即在安全模式下，根本无法跳过UID的检测。


或者我们直接查看touch函数的PHP源码实现，其代码在/ext/standard/filestat.c文件649行。在其代码中我们可以清楚的看到其实现过程在处理了相关参数后，就直接判断完全模式和检测UID了，如下代码:



PHP_FUNCTION(touch)
{

...

/* Safe-mode */
if (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
RETURN_FALSE;
}

/* Check the basedir */
if (php_check_open_basedir(filename TSRMLS_CC)) ...</description>
		<link>http://www.phppan.com/2011/12/upgrade-to-wordpress3-3-safe-mode/</link>
			</item>
	<item>
		<title>Scheme学习笔记2: 输入和输出</title>
		<description>本文包括以下内容：

	read和write
	字符操作
	文件操作



作为一门语言，它需要提供与外设交互的方式，在各种语言中都提供了输入输出，Scheme语言也不例外。Scheme语言的输入输出功能，是在C基础上的一种封装。

read和write

输入输出中我们常用是读和写，对应读与写，在Scheme中有read和write。
read是读操作，它将 Scheme 对象的外部表示转换为对象本身。
read的标准格式为：(read port)
其中port参数可以省略，此时它使用的默认是current-input-port的返回值。



write是写操作，它的标准格式为：(read obj port)
write过程的作用是向给定端口port输出obj的内容。其中输出内容的反斜线、双引号等会被反斜线转义。write的参数中port参数可以省略，默认情况下使用current-output-port的返回值。


这里的默认值我们可以对应到标准输入和输出。从ZOJ的第一题我们看下read的使用。
如下代码：

ZOJ第一题(zoj1001)：


(define (main)

(let ((a (read)) (b (read)))

(if (not(eof-object? a))
(begin
(display (+ a b))
(newline)
(main)
)
)
)

)

(main)




题目很简单，就是输入两个数，输出和。这里的难点是对于输入结束的判断，在C语言中scanf函数有一个EOF的判断，而在Scheme语言中，我们通过判断输入的输入的值是否为eof来判断，其对应的判断过程为eof-object?（嗯，问号也是调用过程的组成部分，个人非常喜欢这种表达方式），在判断不为结束时继续递归调用，实现C语言中的while循环。


字符操作

对于字符的读写操作，我们常用的三个过程如下：


read-char
标准格式：　(read-char port)
读取一个字符，并将port指针指向下一个字符。
read-char的参数中port可以省略，默认情况下使用current-input-port的返回值。


write-char
标准格式：　(write-char port)
write-char过程的作用是向给定端口port输出的内容。
write-char的参数中port可以省略，默认情况下使用current-output-port的返回值。


peek-char
标准格式：　(peek-char port)
获取一个字符，但并不将port指针指向下一个字符
peek-char的参数中port可以省略，默认情况下使用current-input-port的返回值。 

以一个示例说明这三个过程的调用，示例实现从指定文件中按字符读取内容，并放到一个列表中。


(define input_port (open-input-file "temp"))

(define read-char-with-file (lambda (port)
(if (eof-object? (peek-char port))
'()
(cons (read-char port) (read-char-with-file port))
)
)
)

(if (input-port? input_port)
(begin
(display (read-char-with-file input_port))
(close-input-port input_port)
)

(begin
(display "read file form temp failed")
(newline)
)
)



这段代码写得有些复杂，我们可以通过let关键字和call-with-input-file过程来简化这段代码，如下：



(define output-chars
(call-with-input-file "temp"
(lambda (p)
(let f ((x (read-char p)))
(if ...</description>
		<link>http://www.phppan.com/2011/12/scheme-study-02-input-and-output/</link>
			</item>
	<item>
		<title>Scheme学习笔记一</title>
		<description>

Scheme学习笔记

本文包括如下内容：

	开发环境配置
	hello world
	变量、过程
	标准输入
	if语句
	文件写入

环境
“工欲善其事 必先利其器”。

学习之前我们需要先将Scheme的运行环境搭建起来，环境分Win和Linux，在Win下，直接到 http://www.gnu.org/software/mit-scheme/ 下载，安装。

在Ubuntu下，使用apt-get安装（强制建议使用*inux环境），Scheme的解释器有多种选择，这里我们选择mzscheme和guile，二者都可。
; mzscheme安装
sudo apt-get install  mzscheme

;guile 解释器安装
sudo apt-get install guile-1.8
Hello World
创建hello.scm文件，在编辑器(vi/emacs)中输入如下内容：

;hello.scm the first program
(begin
(display "hello world!")
(newline))


在命令行下，guile hello.scm 或　mzscheme -r  hello.scm

如果你直接使用guile或mzscheme命令进入其命令行模式。则可以通过(load "hello.scm")加载执行，注意，这里的括号是必须的。

解释下这段代码：

在分号（;）之后一直到行末之间就是注释。如hello.scm的开头部分。标准的Scheme语言定义中没有多行注释，不过在它部分实现中还是存在的。比如在Guile中就有多行注释，以符号组合“#!”开始，以相反的另一符号组合“!#”结束，其中内容为注释。如果加了这样的注释，这个程序就只能用guile来运行，如果使用mzscheme命令来运行则会报错。如果考虑代码的通用性，不建议使用此类注释。

display是输出过程，将内容输出到屏幕， newline的作用是换行显示。
变量，过程
在scheme中变量是通过define关键字定义，通过set!改变，并且scheme的变量是固定的类型，这有些类似于现在的python,  php等动态语言，其在运行过程中是可变的，或者说这些动态语言是从函数式编程语言中学习了这些特性。  scheme的过程定义也是通过define关键字来实现的，只是其格式与变量有一些不同。

结合上面的hello world示例，我们看一个示例：


;var.scm

    ; 定义变量x
    (define x 10)

    ;定义过程cout
    ...</description>
		<link>http://www.phppan.com/2011/11/scheme-study-01/</link>
			</item>
	<item>
		<title>胖子的乱看乱想笔记一</title>
		<description>
1、在设计框架或给应用增加一个模块或一个功能时，是否我们也需要思考如何让这些功能下线。
不仅仅是显示层的下线，也包括代码层或数据层的移除
2、一门语言很多自带的东西都是可以解决问题的，并且它也是为解决这些问题而存在的，特别是类似于字符串这种常规的操作，用一门语言的时候需要用这门语言的思想去思考问题
3、一门编程语言都有其适用场景，都有其存在的意义，语言本身没错，可能错在你错了语言。
4、工具它只能是工具，它也只是工具。
5、也许没有也许。
6、人的选育用留，长板凳计划
7、文化！文化！！
8、说到，做到，诚信，开会准点到，树立自己的品牌。
9、喧闹的一天在同事的88声中归于平静，我今天做了些什么？学到了些什么？有较昨天进步否？
10, 数学真的很重要，很有用。
11，MySQL的存储引擎，最好不要在一台服务器上混用，他们的运行方式和内存消耗方式不同，混用很难充分的利用内存和CPU
12、对于在后台运行的程序发布后，需要在一天、一周、一个月后Check程序的健康情况。
13、高春辉的gtalk签名：　看五年想三年做好一两年。
14、项目主管以各种方式告诉团队成员：“我们过去在那里，我们现在在这里，我们将来在哪里？“
15、危机也是机遇
16、未雨绸缪
17、笨鸟先飞
18、做权威，而非掌权者
19、能够按时解决问题，才有存在的价值
20、用户害怕新技术，开发人员也是用户的一种。
21、最关键的问题是此人无法胜任工作,至于如何去找可以接替他的人,则是下一个问题。 －－ 马歇尔
22、当对外提供的接口需要返回状态等信息时，如果要做到通用，尽量以数字等通用的值返回，并约定返回值的含义，提供详细的说明文档。切记不要包含特定语言的信息，如中文信息等。
23、进度落后就是一场梦，发现落后就是觉醒。
24、设计达到完美的时候,不是增加得不能再增加了、而是减少得不能再减少了

以上的内容纯粹属于胖子各种时间记录在evernote的汇集，此版权不属于胖子





 </description>
		<link>http://www.phppan.com/2011/11/evernote-01/</link>
			</item>
	<item>
		<title>与代码的相处之道</title>
		<description>与代码的相处之道  -- 读《编程人生》一二章有感

最近在阅读《编程人生》，看了作者对Jamie Zawinski(Lisp黑客、XEmacs开发者、Netscape浏览器和Mozilla核心开发者)和Brad Fitzpatrick(80后程序员、LiveJournal和memcached开发者，Google员工)的采访，除了感慨作者采访的准备充分外，对于牛人的一些观点有一些共鸣，也有一些观点不太认同，尽信书不如无书，对于牛人也是如此吧。
一个程序员，大多数时间都在写代码，调试代码，阅读代码、评论他人的代码……林林总总，都是与代码相处。那如何与代码相处呢？首先我们需要认知到代码是什么，代码是一种语言，一种我们与计算机沟通的语言。我们需要通过代码告诉那个有点傻傻的计算机需要做点什么。


对于一些新增加的内容，我们可以从如下的方面与代码相处。

第一，了解写这些代码是为了什么，或者说你的需求是什么，列出所有的功能点，估算你实现这些功能需要多长的时间，在估算的过程中，你不能将自己的工作时间完全算在开发中，大概50%差不多，毕竟我们不会一整天的写，还会有思考，发呆，梦游，开各种网页，看各种新闻，被各种im打断……

第二，为你的新的功能搭出基本的架子，比如写好空类或空的函数，后面的工作就是填充这些空的地方了。当然在写这些空类或空函数的过程中，把注释写清楚，这是一个理顺自己代码结构和业务逻辑的过程。

第三，这一步当然就是填充之前留空的内容了，在整个过程中你可能会写入一些调试的代码，没关系，先放着，因为在这个过程中你可能会再次需要这些调试的代码。

第四，验证需求，重构代码。每个设计方案在开始的时候都是完美的，包括就在刚才你的代码结构设计。只是在细化需求时，可能部分细节没有考虑到，此时你就需要重构刚才的设计和代码，以适应这些变化。

第五，打完收工，这是一个收尾的工作。此时，我们需要再次阅读你刚才写好的代码，清除这个过程中出现的调试代码，确认整个过程是否已经全部完成了所需要的功能点。

第六，提交

 

如果你不是重新开始写一个功能，而是在别人（或你自己之前）的代码上修改并增加新的功能，此时当如何相处呢？

第一，了解过去，分析影响范围，列出checklist。一段旧的代码，你最好先了解他的过去，看他与哪些其它模块有耦合，或者有哪些内容依赖于他，如果修改了这些内容，对其他模块是否有影响，如果要修改的是对外的接口，是否需要适配这些接口？


第二，修改代码。此时你可能会重构之前的代码，那么在重构的过程中需要把握重构的度，不要轻易的将重构变成重写。毕竟之前的一些细节可能是你没有考虑到的。如果修改的代码中有对外的接口，此时可能需要保留这些接口，或者修改所有的调用这个接口的地方，这个就要权衡两者的机会成本了 。


第三，根据checklist验证所修改的内容是否正确，验证是否影响了其它相关的内容


第四，提交


 

如果是不是重新开始写一个功能，而是由于bug或其它原因需要调试代码呢?此时当如何相处？

第一，了解它。我们需要先通读代码，审阅整个代码的结构，确认在整体的方向上没有问题。

第二，在关键点打印信息，当然，你也可以使用调试工具与之交互，但是打印语句是一种绿化无污染的调试方式，不依赖于外部的环境，不过你需要通过第一步，先对代码有一定的了解才行。

第三，确认问题，修改。此时可能需重复上面的修改流程

第四，提交

 

前面三个都是通过代码告诉计算机怎么做，在某些时候我们也需要计算机告诉我们他做了什么。因此，在这里我们也需要通过代码告诉计算机如何将信息反馈给我们。一般来说，需要告诉他如何将整个代码执行过程中发生了什么告诉我们，比如日志，比如执行过程中的信息打印。


如果你是想优化一些代码，此时当如何相处呢？ 

第一，确认需要优化的内容。可能你需要优化时间，也可能是需要优化空间，确认优化的内容。嗯，Knuth说过：过早优化是万恶之源。

第二，找出瓶颈。优化并不是优化所有的地方，而是要找到优化的关键点，比如循环调用许多次的函数，或者花费时间或空间较多的地方等等。

第三，寻找优化方案。可能你需要的仅仅是调换一下代码的执行顺序，或者释放执行过程中的某些变量所占的内存，当然也有可能需要优化整个数据结构，或者换台机器也是一个不错的主意。

第四，验证优化结果。

第五，提交


优化是一个持续的过程，在写代码的过程中能够随手优化的就优化吧，比如局部变量的使用等。

 

代码是我们与计算机沟通的语言，也是我们与其它程序员沟通的语言，因此除了让计算机了解代码外，我们也需要为其它程序员（或一段时间后的自己）了解这些代码做点什么，除了团队内部构建良好的代码规范，统一代码风格这些老生常谈（老生常谈其实挺好）外，书中的牛人提出关于注释的观点也非常认同：关于注释我们得写点不一目了然的东西。至少不要出现类似于循环结束，某某值加1的注释，一般来说注释应该是写点与业务相关的东西，至少在第一眼看代码无法看出来的东西。虽然现在敏捷提倡“代码即文档”，不要注释，提高代码的可读性，但是一些注释还是必要的。

  </description>
		<link>http://www.phppan.com/2011/11/with-code/</link>
			</item>
	<item>
		<title>锁机制概述</title>
		<description>本文主要回答如下问题：

	什么是锁机制？
	锁机制的作用是什么？
	锁机制的类型有哪几种？

关键字：并发、并发控制、乐观锁、悲观锁
在我们常见的程序设计、操作系统和数据库等领域，并发是非常棘手的问题之一。而并发在操作系统、数据库等领域，最终还是会体现在软件开发（代码实现）上。并发的主要纠结点在于对共享资源的处理，虽然现在有事务来处理并发，但是这只是在一定程序上缓解了并发的问题，并没有彻底解决，比如跨事务的并发。
在软件开发过程中，并发控制是确保及时纠正由并发操作导致的错误的一种机制。并发控制主要采用时间戳、乐观并发控制和悲观并发控制等技术手段来实现。而今天我们要说的锁机制主要是指后面的两种技术手段：乐观并发控制和悲观并发控制，他们分别对应乐观锁和悲观锁。锁机制是管理对共享资源的并发访问机制。

乐观锁并不是纯粹意义上的锁，它可以理解为冲突检测，属于事后的操作，其中一种实现是依赖数据版本记录机制。在数据源增加一个版本标记，当请求方读取出数据时，将此版本号一同读出，之后更新时，对此版本号加一。此时，将提交数据的版本数据与数据源中对应记录的当前版本信息进行比对，如果提交的数据版本号大于数据源当前版本号，则予以更新，否则认为是过期数据。比如某个共享数据被并发访问修改，在各个请求方获取了数据后，请求方都会被分配一个相同版本，如果有一个请求方提交了修改，则将原始数据版本增加1，则其它请求方再次提交修改时，由于提交的版本小于当前版本则显示冲突。除了基于数据版本的控制外，还有包含对更新时间的控制，对不同字段的对比控制等。

悲观锁可以理解为冲突避免，属于事前的操作，即不让并发修改的操作发生，减少并发出现。当一个用户访问一个共享对象时锁住它，即先获得锁，此时其它用户无法访问此对象，当对共享对象的操作完成以后要为被它封锁的对象解锁，此时其它对象才能访问此共享对象。如果此时一个用户一直占着一个资源不放，其它所有用户都只能永远等待，此时可能需要引入其它机制来防止这种情况的发生。

悲观锁减少了并发的程序，而乐观锁在一定程度上会更加自由一些，其在获取资源时是不受限制的，仅在提交的时候才会有限制。当需要在悲观锁与乐观锁之间抉择时，可以考虑如下两个点：

	冲突的频率 如果冲突少，通常可以选择乐观锁，这样可以获得更多的并发性，但是此时也需要考虑冲突的严重性，如果系统不能容忍冲突的出现，则需要考虑牺牲并发性，使用悲观锁
	冲突的严重性 如果冲突所产生的后果比较严重或者为用户不能容忍，需要使用悲观锁

无论是乐观锁还是悲观锁都存在其优点和缺点，只使用某一种机制都会产生其它问题，可以考虑将这两种锁放一起使用，或者提供两种机制，供用户选择，默认使用乐观锁。 </description>
		<link>http://www.phppan.com/2011/11/lock-overview/</link>
			</item>
	<item>
		<title>Yii框架的组件行为管理机制和Mix-in</title>
		<description>Yii框架的组件行为管理机制和Mix-in
本文包括以下内容：


	Yii框架的组件行为管理机制介绍
	Ruby、PHP5.4和Mix-in

在Yii框架的官网，我们可以看到关于Behaviors &#38; events的介绍: Behaviors are simply a way of adding methods to an object.
我们看官网上的使用示例：

class SomeClass extends CBehavior{
    public function add($x, $y) { return $x + $y; }
}

class TestComponent extends CComponent {
}

$test_comp = new TestComponent();
$test_comp-&#62;attachbehavior('blah', new SomeClass);
$test_comp-&#62;add(2, 5);
在TestComponent类的对象创建的后，我们可以通过调用attachbehavior给对象添加新的方法。
通过其源码（在base/CComponent.php）可以知道它是通过在组件类内部以私有变量的方式存储这些添加的方法所在的对象， 通过魔术方法__call，当调用一个未定义的方法时需要调用__call方法的特性，遍历所有通过attachbehavior方法添加进来的对象， 并判断此对象是否禁用并且此对象是否存在需要调用的方法，如果存在则调用。
此种实现方式存在如下一些问题：


	如果多个对象存在相同的方法，则程序调用时永远会调用第一次添加进去的方法
	如果我们只是需要某个对象中的某个方法，但是在存储上需要将整个对象添加到列表中

也许你会觉得这些都是一些如果，都是一些假设，可能不会出现，这有些像众所周知的goto语句问题，如果用得好，这是一个利器，如果用得不好，可能会给你带来痛苦。 Yii框架中的这种机制实现运行时的方法绑定，虽然类的属性和实例参数仍然归属于其它类和对象。
在官方说明中也提到了这是一种类似于ruby语言的实现方式，如果我们用Ruby实现上面的方法该如何写呢？如下：

module SomeClass
    def add(x, y)
 ...</description>
		<link>http://www.phppan.com/2011/11/yii-framework-component-behavior-and-mix-in/</link>
			</item>
	<item>
		<title>代理模式(Proxy)和PHP的反射功能</title>
		<description>代理模式(Proxy)和PHP的反射功能
本文包括以下内容：


	代理模式概述
	代理模式常规示例
	使用PHP的反射功能实现多代理

正文
模式意图 ：为其他对象提供一种代理以控制对这个对象的访问［GOF95］
代理模式是对象的结构模式，代理模式给某一个对象提供一个代理对象，并由此代理对象控制对原代理对象的引用。代理模式不应该让用户感觉到代理的存在，所以代理对象和原对象的对外的调用接口是一致的。
代理模式一般包括三个角色：


	抽象主题角色(Subject)：它的作用是统一接口。此角色定义了真实主题角色和代理主题角色共用的接口，这样就可以在使用真实主题角色的地方使用代理主题角色。
	真实主题角色(RealSubject)：隐藏在代理角色后面的真实对象。
	代理主题角色(ProxySubject)：它的作用是代理真实主题，在其内部保留了对真实主题角色的引用。它与真实主题角色都继承自抽象主题角色，保持接口的统一。它可以控制对真实主题的存取，并可能负责创建和删除真实对象。代理角色并不是简单的转发，通常在将调用传递给真实对象之前或之后执行某些操作，当然你也可以只是简单的转发。 与适配器模式相比：适配器模式是为了改变对象的接口，而代理模式并不能改变所代理对象的接口。

从以上三个角色我们可以得出一个简单的示例：

/**
 * 代理模式简单示例 2011-10-30 sz
 * @author phppan.p#gmail.com  http://www.phppan.com
 * @package design pattern
 */

/**
 * 抽象主题角色
 */
abstract class Subject {
    abstract public function action();
}

/**
 * 真实主题角色
 */
class RealSubject extends Subject {

    public function __construct() {
    }

   ...</description>
		<link>http://www.phppan.com/2011/10/php-design-pattern-proxy-and-reflection/</link>
			</item>
	<item>
		<title>使用Bison和re2c构建词法分析和语法分析器</title>
		<description>使用说明： 本文需要读者对C语言有一定的基础，对于re2c和bison有一些了解，最好也熟悉linux命令
我们在前面介绍了PHP的语法分析器-Bison入门 和PHP的词法解析器：re2c，那么如何将re2c与bison集成在一起的呢？ 我们以一个从PHP源码中剥离出来的示例来说明整个过程。这个示例的功能与词法分析器的示例类似，作用都是识别输入参数中的字符串类型。 本示例是在其基础上添加了语法解析过程。 首先我们看这个示例的语法文件：demo.y

%{
#include &#60;stdio.h&#62;
#include "demo_scanner.h"
extern int yylex(znode *zendlval);
void yyerror(char const *);

#define YYSTYPE znode   //关键点一，znode定义在demo_scanner.h   
%}

%pure_parser    //  关键点二

%token T_BEGIN
%token T_NUMBER
%token T_LOWER_CHAR
%token T_UPPER_CHAR
%token T_EXIT
%token T_UNKNOWN
%token T_INPUT_ERROR
%token T_END
%token T_WHITESPACE

%%

begin: T_BEGIN {printf("begin:\ntoken=%d\n", $1.op_type);}
     &#124; begin variable {
   ...</description>
		<link>http://www.phppan.com/2011/10/bison-and-re2c/</link>
			</item>
</channel>
</rss>

