运营人员、产品人员必知的30个硬道理!你承认不承认,事实就是这样!

1、无情之人,做不好社区

2、工作想要做的最好,有三种境界;第一种,叫把工作当成工作;第二种,叫把工作当成事业;第三,叫把工作当成“儿子”。

3、最好的运营,是综合能力更强、视野面够宽、内心更强大的运营。运营要耐得住寂寞。
最好的产品,是一个能衔接好各个环节的纽带。

4、运营就是一个挺苦逼的工作,谁坚持下来谁就最强。没受过委屈、吃过苦吃过亏、没背过黑锅、挨过用户的骂、被同事领导公司质疑、没为工作哭过、写过长邮件、发过牢骚抱怨还得坚忍着把工作做好、遇到瓶颈绞尽脑汁、请教别人没人搭理……根本不算做过运营。

5、能成为你的leader的人,一定有他的过人之处。如果遇到一个业务能力很强、人品不错、又肯教你本事的领导,那就知足吧!其实,什么都懂的领导,真没那么多。

6、从没接触过用户的产品经理,根本不配做产品!你做的其实是一个只满足你个人需求的产品吧?

7、牛逼的公司的确能给你一个光环,但不代表你就牛逼。

8、工作前10年是积累经验最好的时期,这10年决定你的未来。这十年里,一定至少要在一个公司好好至少沉淀3-4年。因为经验是需要连续的。

9、用人单位最看重的是什么?他们更看重你在以前公司积累的内在的东西,不是你平时做什么,而是你做出了什么成绩、你有什么资源、你在这个公司的成长轨迹、因为有你给这个公司带来了什么。不信你去问问猎头。

10、有很多人,都是在不断跳槽中迷失了自己。

11、抱怨是徒劳的,反正再抱怨,活儿也得继续干。关于这个问题,我给6个字:要么忍,要么滚。

12、在团队里到处传播负面情绪的人,一定要开掉!哪怕他能力再强也要开,因为他抱怨的越多,小宇宙就会变的越弱小。

13、自学能力,对于所有互联网从业人员,更加重要。尤其是运营人员,因为运营到涉及的东西实在太多了,而且没有正式的教程。

14、运营,其实没有什么捷径。实战才是王道,看再多的书,听再多的课,不如自己多练一次,基本功很重要。

15、我不相信任何方法论,因为运营更需要灵活性,循规蹈矩、按部就班必死。

16、以前做运营的时候看那些产品,觉得他们有一堆问题、缺点,当我自己做产品时,发现做产品真的挺难的。这个时候,我再回头看运营,又是另一个视角。
人生就是这样,别总说别人的不是,只要换个角度,你会发现,谁都不容易。

17、一个好的运营,会更加懂感恩。你会在不知不觉中被用户感染、感动,会发现他们其实都挺可爱的。

18、不要把自己的责任归咎给用户,工作做不好,就是你自己的问题。

19、做运营,一定要经常逼自己做做总结,别只顾闷头做事。有太多人,肚子里的墨水就是倒不出来,你说难受不?

20、谁都可以成为一个产品的“灵魂”,灵魂不一定是CEO。

21、工作最可贵的是什么?工作过程中磨练出来的责任心、态度、对细节的关注、坚韧和坚持、逆境商、做事的魄力、价值观,才是一个人成功与否的制胜法典,这些才决定你的未来。

22、有时候,选择比能力更加重要。会选择是一门学问,事实就是这样。

23、用户运营的核心出发点,只有4个字:尊重用户。
不要让用户感知到你在压迫TA,你高高在上,你没资格!

24、工作的时候也要居安思危,想想你离开这个公司还能剩下多少东西?如果啥也没有,那就继续干活吧。

25、你永远遇不到最完美的公司。在任何公司,你都可以学到很多东西。要做个有心人。

26、你有生之年,不可能做完运营工作所涉及到的所有环节,但你可以尽量涉猎。涉猎的前提,是把当前你正在做的东西,先夯实好。

27、我说我愿意用10年时间来磨练自己,可以受任何委屈、任何苦楚、任何不理解,摈弃钱的诱惑,积累冰山以下的东西,换我未来10年、20年的生活。有人说类,你太傻了,爱咋说咋说去吧,我相信傻人早晚会有傻福。

28、有一点我很庆幸,从小员工升到总监,我一级都没落过。每一级,我都收获很大。人的成长是需要过程的,没恋爱就结婚,一定有问题。拔苗助长不是对每个人都有好处,得看人。

29、把兴趣和工作结合在一起,会让自己变的更强大。
当年我误打误撞进了互联网,并慢慢喜欢上了它。这说明兴趣是可以被改变和慢慢培养的。

30、工作越久,职位越高,懂的越多,越觉得自己很“无知”、啥都不懂。一个人的能力总是有限的。学无止境,我还在学习!

sublime text 3解放鼠标的快捷键总结

Sublime text 3是我最喜欢的代码编辑器,每天和代码打交道,必先利其器,掌握基本的代码编辑器的快捷键,能让你打码更有效率。刚开始可能有些生疏,只要花一两个星期坚持使用并熟悉这些常用的快捷键,今后就能解放鼠标了,省心省力又省时,何乐而不为呢。

以下是个人总结不完全的快捷键总汇,祝愿各位顺利解放自己的鼠标。

选择类

Ctrl+D 选中光标所占的文本,继续操作则会选中下一个相同的文本。

Alt+F3 选中文本按下快捷键,即可一次性选择全部的相同文本进行同时编辑。举个栗子:快速选中并更改所有相同的变量名、函数名等。

Ctrl+L 选中整行,继续操作则继续选择下一行,效果和Shift+↓ 效果一样。

Ctrl+Shift+L 先选中多行,再按下快捷键,会在每行行尾插入光标,即可同时编辑这些行。

Ctrl+Shift+M 选择括号内的内容(继续选择父括号)。举个栗子:快速选中删除函数中的代码,重写函数体代码或重写括号内里的内容。

Ctrl+M 光标移动至括号内结束或开始的位置。

Ctrl+Enter 在下一行插入新行。举个栗子:即使光标不在行尾,也能快速向下插入一行。

Ctrl+Shift+Enter 在上一行插入新行。举个栗子:即使光标不在行首,也能快速向上插入一行。

Ctrl+Shift+[ 选中代码,按下快捷键,折叠代码。

Ctrl+Shift+] 选中代码,按下快捷键,展开代码。

Ctrl+K+0 展开所有折叠代码。

Ctrl+← 向左单位性地移动光标,快速移动光标。

Ctrl+→ 向右单位性地移动光标,快速移动光标。

shift+↑ 向上选中多行。

shift+↓ 向下选中多行。

Shift+← 向左选中文本。

Shift+→ 向右选中文本。

Ctrl+Shift+← 向左单位性地选中文本。

Ctrl+Shift+→ 向右单位性地选中文本。

Ctrl+Shift+↑ 将光标所在行和上一行代码互换(将光标所在行插入到上一行之前)。

Ctrl+Shift+↓ 将光标所在行和下一行代码互换(将光标所在行插入到下一行之后)。

Ctrl+Alt+↑ 向上添加多行光标,可同时编辑多行。

Ctrl+Alt+↓ 向下添加多行光标,可同时编辑多行。

编辑类

Ctrl+J 合并选中的多行代码为一行。举个栗子:将多行格式的CSS属性合并为一行。

Ctrl+Shift+D 复制光标所在整行,插入到下一行。

Tab 向右缩进。

Shift+Tab 向左缩进。

Ctrl+K+K 从光标处开始删除代码至行尾。

Ctrl+Shift+K 删除整行。

Ctrl+/ 注释单行。

Ctrl+Shift+/ 注释多行。

Ctrl+K+U 转换大写。

Ctrl+K+L 转换小写。

Ctrl+Z 撤销。

Ctrl+Y 恢复撤销。

Ctrl+U 软撤销,感觉和Gtrl+Z一样。

Ctrl+F2 设置书签

Ctrl+T 左右字母互换。

F6 单词检测拼写

搜索类

Ctrl+F 打开底部搜索框,查找关键字。

Ctrl+shift+F 在文件夹内查找,与普通编辑器不同的地方是sublime允许添加多个文件夹进行查找,略高端,未研究。

Ctrl+P 打开搜索框。举个栗子:1、输入当前项目中的文件名,快速搜索文件,2、输入@和关键字,查找文件中函数名,3、输入:和数字,跳转到文件中该行代码,4、输入#和关键字,查找变量名。

Ctrl+G 打开搜索框,自动带:,输入数字跳转到该行代码。举个栗子:在页面代码比较长的文件中快速定位。

Ctrl+R 打开搜索框,自动带@,输入关键字,查找文件中的函数名。举个栗子:在函数较多的页面快速查找某个函数。

Ctrl+:打开搜索框,自动带#,输入关键字,查找文件中的变量名、属性名等。

Ctrl+Shift+P 打开命令框。场景栗子:打开命名框,输入关键字,调用sublime text或插件的功能,例如使用package安装插件。

Esc 退出光标多行选择,退出搜索框,命令框等。

显示类

Ctrl+Tab 按文件浏览过的顺序,切换当前窗口的标签页。

Ctrl+PageDown 向左切换当前窗口的标签页。

Ctrl+PageUp 向右切换当前窗口的标签页。

Alt+Shift+1窗口分屏,恢复默认1屏(非小键盘的数字)

Alt+Shift+2 左右分屏-2列

Alt+Shift+3 左右分屏-3列

Alt+Shift+4 左右分屏-4列

Alt+Shift+5 等分4屏

Alt+Shift+8 垂直分屏-2屏

Alt+Shift+9 垂直分屏-3屏

Ctrl+K+B 开启/关闭侧边栏。

F11 全屏模式

Shift+F11 免打扰模式

其实sulime text菜单栏各个选项中都会提示相关的快捷键,各位可以自己去看看,本文主要是整理一些隐藏或不被发掘的快捷键,个人整理难免会有实用的快捷键遗漏,如果你还发现有实用的sublime text的快捷键和使用栗子,欢迎补充~最后说一句,死记硬背是记不住的,请结合自己的需求,有选择的使用、练习、熟悉相关快捷键,一两个星期后定能提高效率!

http://dengo.org/archives/970

每个程序员都必读的10篇文章

  1. 程序员必知之内存篇

这是篇非常经典的文章,它将会带领你走入内存的各个方面,有老的,也有新的,有知道的,也有不知道的。尽管内存十分常见,无处不在,但并不是每个程序员都足够了解它。如果你正在编写高性能的应用程序的话,了解现代系统中的内存尤为重要。硬件设计师们带来了更复杂的内存处理及加速的技术,比如说CPU缓存,但如果离开了程序员它们也无法发挥出最大的价值。我仍在阅读这篇文章,很难说清楚我到底从中学到了多少关于随机访问内存(RAM),CPU缓存,包括1级,2级缓存,不同类型的内存,直接内存访问,内存控制器设计及普通内存的知识。简而言之,它是各个层次的程序员都必读的文章。

  1. 每个计算机科学家都必知之浮点数运算

浮点数运算是一个非常有技术含量的话题,不太容易掌握。许多Java程序员都不清楚使用==操作符比较float/double类型的话到底出现什么问题。许多人在使用float/double进行货币计算的时候经常会犯错。这篇文章是这一系列中的精华,所有的软件开发人员都应该读一下。随着你经验的增长,你肯定想去深入了解一些常见的东西的细节,浮点数运算就是其中之一。作为一名Java高级开发人员,你必须了解如何进行货币运算,何时使用float,double或者BigDecimal,如何对浮点数进行舍入运算等等。就算你知道了浮点数运算的一些基础知识,读下这篇文章,你肯定还会学到一些新的东西。

  1. 每个程序员必知之Unicode篇

字符编码是另一个许多程序员痛苦挣扎的领域,”每一个程序员都绝对绝对应该了解Unicode以及字符集(不许找任何借口!)“填补了这一空白。标注一下,是的,这就是这篇文章的标题。它是由stackoverflow.com的创始人之一,Joel Spolsky所写的。Joel10年前就在他的博客上发表了这篇文章,不过对于现在而言仍然适用。这篇文章会告诉你什么是Unicode,什么是字符编码,字符是如何通过字节来表示的等等。这篇文章最赞的一点就是它的语言及行文,尽管你根本不知道什么是Unicode,你也能够很容易地看懂。一句话,这又是一篇程序员,码农,软件工程师必读的文章。

  1. 每个程序员必知之时间篇

除了字符编码外,时间和日期又是另一个程序员经常栽倒的领域,包括我自己在内。甚至是高级开发人员也会被格林尼治标准时间(GMT),世界标准时间(UTC),夏令时,闰秒这些东西搞的死去活来。坦白地说,在处理时区这个问题上,很难不踩一些坑,更别说再加上夏令时什么的。如果你想要试错法的话则更糟糕,因为这么做永远也解决不了你的问题。这里有许多可能出错或者产生误解的地方。比方说,日期是否包括时区可能就会困扰到你,将UNIX时间转化成其它的时区可能会让你崩溃,你还是忘了时钟同步和延迟这些事吧。我希望读完这篇经典的文章后,你的许多关于时间的误解都能够消除,自己能够巩固一些关于日期的基础常识。

  1. 每个WEB开发人员必知之URL编码篇

这篇文章介绍了Uniform Resource Locator(URL)编码中的一些常见的误解,然后试图阐明什么是HTTP的URL编码,最后列举了一些常见的错误及解决方案。尽管这篇文章的内容并不特定于某种编程语言,但它是用Java来进行问题的描述,并修复URL编码中存在的问题。你会学习到URL的基础语法,HTTP及其它协议中通用的URL格式。这篇文章还介绍了URL中的一些常见错误,比如字符编码,URL不同部分的保留字符,以及URL编解码问题。如果你是一名Java开发人员,你会学习到如何在Java中正确地处理URL,如何构造URL,以及如何使用Apache Common Http client库。最后它还提供了一些最佳实践的建议,比如你应该在构建URL的时候进行编码,确保你的重写URL的过滤器正确地处理了URL,等等。这是任何WEB开发人员必读的文章 。

  1. 每个程序员必知之WEB开发

这是programmers.stackexchange.com上的一篇非常有趣的文章,它讲的是程序员在将网站对外发布之前需要实现的技术细节。这包括接口设计及用户体验,安全性,WEB标准,性能,搜索引擎优化(SEO),以及一些重要的资源。当今世界严重地依赖于互联网,很多程序员都有自己的网站,通常是一个博客。这篇文章中学到的东西可能对你的专业没有帮助,但它对你个人的事情肯定会有所帮助。你会了解到一些关键的技术,比如HTML,HTTP,XML,CSS,JavaScript,浏览器兼容性,减少网站加载时间的技巧,XML站点地图,W3C规范,以及许多其它的关键的细节。

  1. 每个程序员必知之SEO

这对WEB开发人员,程序员,博主而言又是一篇很重要的文章。由于很多程序员同时也是博主,你无法对SEO视而不见,学一个搜索引擎优化的基础知识还是非常重要的,这能帮助Google检索到你的内容并推荐给其它程序员。在今天这个网络互联的时代,没有任何公司可以脱离WEB而存在,SEO变得尤为重要。如果你有一家初创企业在卖一些产品,那么SEO就是你要关注的事情。所有程序员,尤其是WEB开发人员,都可以从这篇文章中受益良多。记住,SEO是一个很宽泛变化的题目,不同的搜索引擎,比如Google,Yahoo等,它们的SEO都各不相同。因此,要想掌握这项技能你得经常更新你的知识库。

  1. C程序必知的未定义行为

C语言有一个未定义行为的概念。未定义行为是一个很宽泛的主题,它有许多细微的差别,这就是为什么我喜欢Java的一个原因,没有这么多的未定义行为,没有这么多的困惑,更稳定也更消停。很多人看起来很自然的事情在C里其实是未定义的行为,这也是程序BUG的一个常见的来源。除此之外,C中的任何未定义行为都由具体实现来决定(编译器和运行时),它们生成的代码会格式化你硬盘,做一些意想不到的事情,甚至更糟。读下这篇文章,到未定义行为的海洋里遨游吧。

  1. 程序员必知之网络

这段话摘自这篇文章,”你是一个程序员。你有想过多玩家的游戏是如何实现的吗?从外面看起来它是:两个或多个玩家通过网络共享相同的经验,就像他们在同一个虚拟世界中一样。不过作为程序员,我们知道底下的真相和你看到的东西是有所区别的。事实上这都只是一个错觉“。这是篇关于网络的非常有趣的文章,它是为游戏开发人员而写的,不过我认为每个程序开发人员都能从中受益。

  1. Java程序员必知之String

这是我自己关于java.lang.String的一篇文章,我个人认为每个Java开发人员都应该了解一下。String在日常的Java开发中非常重要,这就是为什么每个Java程序员都应该对它非常了解。这篇文章涉及到了String的许多重要的领域,包括String池,字符串常量,使用==和equals比较字符串,将字节数组转化成String,为什么字符串是不可变的,如何正确拼接字符串,等等。高级程序员应该都知道这些东西了,不过回顾一下也是不错的。

  1. 程序员必知之安全

有一个计算机系的学生在StackOverFlow上问了这个问题。就像我们了解到的一些常见编程概念比如操作系统,算法,数据结构,计算机体系结构,以及其它东西一样,了解安全也同样重要。安全是一个很大的话题,包括加密解密,SSL,WEB安全,混淆,认证,授权,等等,它是每个程序员都应该了解的基础知识。刚进这行的时候,我个人对安全这块还不是很了解,这时候我就开始基于Servlet/JSP写一些的Java WEB程序,后来我才了解到EWB安全性以及一些安全威胁比如SQL注入,拒绝服务,XML注入,跨站脚本,等等。作为一名Java开发人员,我现在遵循fortify, PMP,以及其它一些静态代码分析器提供的Java安全编码规范进行编码。这篇文章是关于安全这个主题的一个很好的合集,不管你现在还写不写代码,这些资源对你来说都非常有用。

  1. 程序员必知之延迟时间

这篇文章是一份额外的福利,但也是每个程序员都应该读的。不管用的是什么语言,Java还是C++都好,要写出高性能的应用程序,你都得了解延迟时间的基础常识,比如从内存中读取一个变量需要多久,从一级缓存中读取又需要多久,二级缓存呢,SSD硬盘中随机读又需要多久,还有要是从磁盘中读呢?互斥锁的加锁解锁需要多长时间,从一个城市发送一个数据包到另一个城市需要多长时间,在同一个数据中心又需要多久?这些延迟的数字是独立于任何编程语言的,开发人员必须要想写高频低延迟的应用程序,它们是核心知识库中的一部分。这篇文章还有一个好消息就是,它会告诉你这些年以来这些延迟时间的变化。你可以看到2006年的延迟时间是多少,而现在又是多少。

这就是所有程序员必读文章的列表。看到这些文章的标题,每个程序员都应该知道的XXX,你能了解到关于某个主题的一些比较有深度的知识。坦白说程序员有太多东西要学了,像学习Java语言这种只是冰山的一角,但并不是所有人都有热情去学习。写程序是一份很有挑战的工作,在你的职业生涯中,能帮忙到你的就是这些基础知识,比如,内存,Unicode,浮点数,时间,安全,这些对任何程序员来说都非常重要。有些文章是和语言相关的,比如我自己那篇关于String的文章,还有每个C程序员都应该了解的未定义行为,不过对于许多初学者来说学习一下也有好处。

译注:我觉得还有一个必读的,就是数数,这里一共是12篇了。。有几篇访问不了的,我也搬运到自己的博客上了。

英文原文链接
原文地址:
 http://www.php100.com/html/it/focus/2014/0515/6881.html

WordPress让PHP更加流行

TIOBE 7月编程语言排行榜的榜单明星为PHP,文中曾提及PHP流行背后似乎得益于最新发布的PHP Zend Framework,但并未做具体的缘由阐述。然而,促使PHP份额上升背后真正的推动者却是WordPress而并非PHP Frameworks。作者Manuel Lemos执笔详细阐述了WordPress推动PHP更加流行的七大理由。译文如下:
1.  WordPress是目前最流行的PHP应用
大多数人会选择使用几大流行的搜索网站来对比衡量目前相对流行的技术、语言、应用或者产品。然而这种方式似乎并不靠谱。因为当某个搜索网站改变其算法后,搜索结果也会随之而改变。
因此,最为靠谱的方法就是采用Google Trends,Google会根据关键字进行检索显示相关结果。如下图所示,与其他的PHP应用相比,包括Joomla、 Drupal、Magento WordPress,WordPress无疑最流行的一款。
2.  WordPress比其他PHP框架更流行
利用Google Trends,可以将不同的PHP框架进行对比。如下图所示,WordPress 与Zend Framework、Symfony PHP及CakePHP的对比情况,WordPress流行趋势则遥遥领先。
3.  WordPress不使用第三方PHP框架
此前,WordPress也使用第三方组件,但并非全栈框架都在使用。这就意味着WordPress从不需要创建全栈框架,而像这样的框架也很难流行起来。
4.  应用让技术变得更加流行,而非组件
在上面的图表中,你可能会注意到Magento要比Zend Framework更受欢迎。Magento是基于Zend Framework创建而来。这似乎暗示着,促使该技术流行的背后是能否解决用户问题,而不是基于此搭建而来的组件。
同样地,WordPress也很受欢迎,这是因为对于很多用户来说它的用处很大。当用户需要托管安装WordPress,需要寻求PHP主机服务。因此,在此基础上用户至少要知道什么是PHP以及所需要的PHP主机。
也就是说,是WordPress促使PHP更加流行。越多人安装WordPress,PHP就越流行。
5.  可扩展的WordPress插件系统
促使WordPress流行的最重要的原因之一就是各种插件支持额外功能。有了WordPress插件,你几乎能创建任何你想要的东西,包括嵌入到其他应用中,比如,论坛、电子商务网站、故障通知单系统等,除了博客系统。
倘若WordPress能够在博客应用中普及,那么其势必将成为更受欢迎的一个平台。
6.  非程序员开发PHP,只因WordPress
如果你需要创建某个网站,而这些特性却是WordPress所没有的,那么你可以通过开发一款新的插件来扩展WordPress从而满足你的需求。
许多WordPress用户并非是程序员,他们开始接触编程只是因为他们需要的某些功能在WordPress 中没有合适的插件。
也就是说,许多非程序员必须去学习PHP,才能够开发出新的WordPress 插件。这也是促使PHP更为流行的一大原因。
7.  实用优于虚构
用一句话来说,WordPress和PHP流行背后主要是因为它解决了众多用户的实际需求。无论是WordPress还是PHP的软件项目案例都是值得称赞的,它们始终遵循良好的项目计划采取超赞的设计模式。
原文出自:PHPClasses

如何学习php框架

关于PHP框架的问题,是我们很多的一个方面。推荐一篇文章,详细介绍使用PHP框架的原因,供大家参考。

如今的PHP框架层出不穷,其中有些比其他的更加惹人耳目。我不是这方面的专家,甚至不能熟练地使用其中的一种,所以我不做推荐,也不想讨论哪些算是框架哪些不算框架(我本文的前提是所有自称为PHP框架的皆是框架)。这里我要讨论的是如何才能更快地开始使用某个新的框架。

首先你当然必须选择一个框架,比如ZF、Cake、Symfony、atk、Yii、CodeIgniter、Solar、PRADO等。选择哪个呢?有些框架提供了非常好“step-by-step”入门教程,另一些则比较难入门了。

我很喜欢这篇文章:《学习一种新的编程语言所需做的练习》,但我因为已经很熟悉PHP了,所以这个方法不能用在学习框架上。所以我想列举一些类似的练习用以学习PHP框架。

如果你还不知道怎么使用框架,何不按照本文的练习来学呢?

1、你好世界(hello world)

这里练习的必要性我就不多作说明了。在一个新的框架上构建一个应用,在页面中显示“‘hello world”。当然,做起来不会那么简单。

2、计算器(calculator)  

写一个简单的计算器程序,结合表单,对两个操作数进行加减乘除等运算,并把结果输出到页面上。你需要为每一种运算设置单独的动作,或者说方法(每个框架的叫法不一样),而且还要用到框架中的表单验证功能(如果框架提供了这一功能的话)。

3、留言板(guestbook)

让我们回到1998年——建立一个留言本程序,在页面上显示一个表单让用户填写,然后提交到这个应用程序里,继而存储到数据库或文本文件中(视你自己的情况而定),并把用户留言的内容显示到页面上。如果框架提供了创建HTML表单、对象模型或层等功能,就用上它们吧。

4、解析和分页(parse and paginate)  

选一个RSS源或其他XML源,解析其中的条目并显示出来。同时添加一个分页的功能,让用户可以五条五条地浏览,并在页面的顶部或底部添加页码。如果框架里提供了这样的功能,就使用它吧。

完成以上的练习后,你应该就可以清楚地知道如何使用这个框架了,并能用它完成更复杂的工作,或是继续探索这个框架的其他功能。如果你还是不会用,或者花了很多时间来完成以上练习,那么我觉得这个框架不适合你,还是找些其他的来用吧。

记住,如果在使用框架的过程中遇到困难,记得去该框架的社区里寻求帮助。一个有这强大社区支持的框架会为你未来的工作带来很大帮助,同时也表明这个框架是个不错的框架。没有人希望在使用某框架后的六个月,框架的开发者就宣布停止开发,这就太气人了。

希望通过本文的介绍,能给你带来帮助。

Eclipse运行时提示failed to create the java virtual machine 如何解决

突然就遇到了这个问题,打开Eclipse就提示“failed to create the java virtual machine”,分析了一下,感觉应该是由于分配的内存过大引起的,果断更改,成功了!

更改方法:

找到Eclipse的安装目录,打开ZendStudio.ini文件

然后将-Xmx1024M这里面的1024改小点,我改为了512,不知道会不会出现其他的问题,但是打开就提示“failed to create the java virtual machine”的问题解决了。

PHP开发:从程序化到面向对象编程

首先要明白此文较长,阅读时间基本在60分钟以上,请先做好准备,这是一篇不错的文章

这份教程的诞生源自一年多之前Robert C.Martin在演讲中带给我的启发。当时他的演讲主题在于讨论创造“终极编程语言”的可能性。在过程中,他提出了这样几个问题:为什么会存在“终极编程语言”?这样的语言应具备哪些特性?但随着他的讲解,我从中发现另一种有趣的思路:每种编程范式都在无形中给程序员带来诸多无法避免的局限性。为了正本溯源,我打算在正式进入PHP由程序化向面向对象转变这一话题之前,先与大家分享一些理论知识。

范式局限

每种编程范式都限制了我们将想象转化为现实的能力。这些范式去掉了一部分可行方案,却纳入另一些方案作为替代,但这一切都是为了实现同样的表示效果。模块化编程令程序规模受到制约,强迫程序员只能在对应模块范畴之内施展拳脚,且每个模块结尾都要以“go-to”来指向其它模块。这种设定直接影响了程序成品的规模。另外,结构化编程与程序化编程方式去掉了“go-to”声明,从而限制了程序员对序列、选择以及迭代语句的调整能力。序列属于变量赋值,选择属于if-else判断,而迭代则属于do-while循环。这些已经成为当下编程语言与范式的构建基石。

面向对象编程方式去掉了函数指针,同时引入多态特性。PHP使用指针的方式与C语言有所不同,但我们仍能从变量函数库中找到这些函数指针的变体形式。这使得程序员能够将某个变量的值当成函数名称,从而实现以下内容:


  1. 		function foo() {
  2.     echo “This is foo”;
  3. }
  4. function bar($param) {
  5.     echo “This is bar saying: $param”;
  6. }
  7. $function = ‘foo’;
  8. $function();        // Goes into foo()
  9. $function = ‘bar’;
  10. $function(‘test’);  // Goes into bar()

初看起来,这种特性似乎无关紧要。但仔细想想,大家一定会发现其中蕴含着极为强大的潜力。我们可以将一条变量作为参数发往某函数,然后让该函数根据参数数值调用其它函数。这绝对非同小可。它使我们能够在不了解函数功能的前提下对其进行调用,而且函数自身根本不会体现出任何差异。

这项技术也正是我们实现多态性调用的关键所在。

现在,我们姑且不谈函数指针的作用,先来看看其工作机制。函数指针中其实已经隐藏着“go-to”声明,或者至少以间接方式实现了与“go-to”相近的执行效果。这可不是什么好消息。事实上,PHP通过一种非常巧妙的方式在不直接使用的前提下实现“go-to”声明。如前例所示,我需要首先在PHP中做出声明。虽然这看起来不难理解,但在大型项目以及函数种类繁多且彼此关联的情况下,我们还是很难准确做出判断。而在C语言这边,这种关系就变得更加晦涩且极难理解。

然而仅仅消除函数指针还远远不够。面向对象的编程机制必然带来替代方案,事实也确实如此,它包含着多态特性与一套简单语法。重点来了,多态性正是面向对象编程的核心价值,即:控制流与源代码在依赖关系上正好相反。

\

在上面的图片中,我们描绘了一个简单的例子:多态性如何在两个不同范式之间发挥作用。在程序化或者结构化编程领域,控制流与源代码在依赖关系上非常相似——二者都指向更具体的输出行为。

而在面向对象编程方面,我们可以逆转源代码的依赖关系,使其指向抽象执行结果,并保持控制流仍旧指向具体执行结果。这一点至关重要,因为我们希望控制机制能尽可能触及具体层面与代码中的不稳定部分,这样我们才能真正让执行结果与预期相符。但在源代码这边,我们的要求却恰好相反。对于源代码,我们希望将具体结果与不稳定因素排除在外,从而简化修改流程、让改动尽量不影响其它代码。这样不稳定部分可以经常修正,但抽象部分则仍然有效。大家可以点击此处阅读由Robert C.Martin所撰写的依赖倒置原则研究论文。

试手任务

在本章中,我们将创建一款简单应用,旨在列出谷歌日程表及其事件提醒内容。首先,我们尝试利用程序化方式进行开发,只涉及简单功能、避免以任何形式使用类或对象。开发工作结束之后,我们更进一步、在不改动程序化代码的前提下通过行为进行代码整理。最后,尝试将其转化为面向对象版本。

谷歌PHP API客户端

谷歌专门针对PHP提供一套API客户端,我们将利用它与自己的谷歌账户进行对接,从而对日程表服务加以操作。要想让代码正确起效,大家需要通过设定让自己的谷歌账户接受来自日程表的查询。

虽然这是本篇指南文章的重要前提,但并不能算主要内容。为了避免在这方面浪费太多篇幅,请大家直接参考官方说明文档。各位不必担心,整个设置过程非常简单,而且只要五分钟左右即可搞定。

本教程附带的示例代码中包含谷歌PHP API客户端代码,建议大家就使用这一套以确保整个学习过程与文章说明保持一致。另外,如果大家想尝试自行安装,请点击此处查看官方说明文档。

接下来按照指示向apiAccess.php文件中填写信息。该文件在程序化与面向对象两套实例中都会用到,因此大家不必在新版本中重复填写。我在文件中留下了自己填写的内容,这样大家就能更轻松地找到对应位置并将其按自己的资料进行修改。

如果大家碰巧用的是NetBeans,我把各个项目文件保存在了包含有不同范例的文件夹当中。这样大家可以轻松打开该项目,并点选Run——>Run Project在本地PHP服务器(要求使用PHP 5.4)上直接加以运行。

与谷歌API对接的客户端库为面向对象型。为了示例的正常运行,我编写了一套小小的函数合集,其中囊括了本教程所需要的所有函数。通过这种方式,我们可以利用程序化层在面向对象客户端库之上进行软件编写,且代码不会涉及任何对象。

如果大家打算快速测试自己的代码与指向谷歌API的连接是否正常起效,则可以直接使用位于index.php文件中的代码。它会列出账户中所有日程表信息,且应该至少有一套具备summary字段的日程表中包含您的姓名。如果日程表中存在联系人生日信息,那么谷歌API将无法与之正常协作。不过大家不用惊慌,另选一套即可。


  1. 		require_once './google-api-php-client/src/Google_Client.php';
  2. require_once ‘./google-api-php-client/src/contrib/Google_CalendarService.php’;
  3. require_once __DIR__ . ‘/../apiAccess.php’;
  4. require_once ‘./functins_google_api.php’;
  5. require_once ‘./functions.php’;
  6. session_start();
  7. $client = createClient();
  8. if(!authenticate($client)) return;
  9. listAllCalendars($client);

这个index.php文件将成为我们应用程序的入口点。我们不会使用任何Web框架或者其它复杂的机制。我们要做的只是简单输出一些HTML代码而已。

程序化开发方案

现在我们已经了解了所需创建的目标以及所能使用的资源,接下来就是下载附件中的源代码。我会提供代码中的有用片段,但为了进一步了解全局情况,大家可能希望访问其初始来源。

在这套方案中,我们只求成果能按预期生效。我们的代码可能会显得有些粗糙,而且其中只涉及以下几个文件:

• index.php – 这是惟一一个我们需要通过浏览器直接访问并转向其GET参数的文件。

• functions_google_api.php – 囊括所有前面提到的谷歌API。

• functions.php – 一切奇迹在此发生。

functions.php将容纳应用程序的所有执行过程。包括路由逻辑、表现以及一切值与行为全部发生于此。这款应用非常简单,其主逻辑如下图所示:

\

这里有一项名为doUserAction()的函数,它的生效与否取决于一条很长的if-else声明;其它方法则根据GET变量中的参数决定调用情况。这些方法随后利用API与谷歌日程表对接,并在屏幕上显示出我们需要的任何结果。


  1. 		function printCalendarContents($client) {
  2.    putTitle(‘These are you events for ‘ . getCalendar($client, $_GET[‘showThisCalendar’])[‘summary’] . ‘ calendar:’);
  3.    foreach (retrieveEvents($client, $_GET[‘showThisCalendar’]) as $event) {
  4.       print(‘<div style=”font-size:10px;color:grey;”>’ . date(‘Y-m-d H:m’, strtotime($event[‘created’])));
  5.       putLink(‘?showThisEvent=’ . htmlentities($event[‘id’]) .
  6.             ‘&calendarId=’ . htmlentities($_GET[‘showThisCalendar’]), $event[‘summary’]);
  7.       print(‘</div>’);
  8.       print(‘<br>’);
  9.    }
  10. }

这个例子恐怕要算我们此次编写的代码中最为复杂的函数。它所调用的是名为putTitle()的辅助函数,其作用是将某些经过格式调整的HTML输出以充当标题。标题中将包含我们日程表的实际名称,这是通过调用来自functions_google_api.php文件中的getCalendar()函数来实现的。返回的日历信息是一个数组,其中包含一个summary字段,而这正是我们要找的内容。

$client变量被传递到我们的所有函数当中。它需要与谷歌API相连,不过这方面内容我们稍后再谈。

接下来,我们整理一下日程表中的全部现有事件。这份数组列表由封装在retrieveEvents()函数中的API请求运行得来。对于每个事件,我们都会显示出其创建日期及标题。

\

其余部分代码与我们之前讨论过的内容相近,甚至更容易理解。大家可以抱着轻松的心情随便看看,然后抖擞精神进军下一章。

组织程序化代码

我们当前的代码完全没问题,但我想我们可以通过调整使其以更合适的方式组织起来。大家可能已经从附带的源代码中发现,该项目所有已经组织完成的代码都被命名为“GoogleCalProceduralOrganized”。

使用全局客户端变量

在代码组织工作中,第一件让人心烦的事在于,我们把$client变量作为参数推广到全局以及嵌套函数的深层当中。程序化编程方案对这类情况提供了一种巧妙的解决办法,即全局变量。由于$client是由index.php所定义,而从全局观点来看,我们需要改变的只是函数对该变量的具体使用方式。因此我们不必改变$client参数,而只需进行如下处理:


  1. 		function printCalendars() {
  2.    global $client;
  3.    putTitle(‘These are your calendars:’);
  4.    foreach (getCalendarList($client)[‘items’] as $calendar) {
  5.       putLink(‘?showThisCalendar=’ . htmlentities($calendar[‘id’]), $calendar[‘summary’]);
  6.       print(‘<br>’);
  7.    }
  8. }

大家不妨将现有代码与附件中的代码成品进行比较,看看二者有何不同之处。没错,我们并没有将$client作为参数传递,而是在所有函数中使用global $client并将其作为只传递向谷歌API函数的参数。从技术角度看,即使是谷歌API函数也能够使用来自全局的$client变量,但我认为最好还是尽量保持API的独立性。

从逻辑中分离表示

某些函数的作用非常明确——只用于在屏幕上输出信息,但有些函数则用于判断触发条件,更有些函数身兼两种作用。面对这种情况,我们往往最好把这些存在特殊用途的函数放在属于自己的文件当中。我们首先整理只用于屏幕信息输出的函数,并将其转移到functions_display.php文件当中。具体做法如下所示:


  1. 		function printHome() {
  2.    print(‘Welcome to Google Calendar over NetTuts Example’);
  3. }
  4. function printMenu() {
  5.    putLink(‘?home’, ‘Home’);
  6.    putLink(‘?showCalendars’, ‘Show Calendars’);
  7.    putLink(‘?logout’, ‘Log Out’);
  8.    print(‘<br><br>’);
  9. }
  10. function putLink($href, $text) {
  11.    print(sprintf(‘<a href=”%s” style=”font-size:12px;margin-left:10px;”>%s</a> | ‘, $href, $text));
  12. }
  13. function putTitle($text) {
  14.    print(sprintf(‘<h3 style=”font-size:16px;color:green;”>%s</h3>’, $text));
  15. }
  16. function putBlock($text) {
  17.    print(‘<div display=”block”>’.$text.'</div>’);
  18. }

要完成剩余的表示分离工作,我们需要从方法中提取出表示部分。下面我们就以单一方法为例演示这一过程:


  1. 		function printEventDetails() {
  2.    global $client;
  3.    foreach (retrieveEvents($_GET[‘calendarId’]) as $event)
  4.       if ($event[‘id’] == $_GET[‘showThisEvent’]) {
  5.          putTitle(‘Details for event: ‘. $event[‘summary’]);
  6.          putBlock(‘This event has status ‘ . $event[‘status’]);
  7.          putBlock(‘It was created at ‘ .
  8.                date(‘Y-m-d H:m’, strtotime($event[‘created’])) .
  9.                ‘ and last updated at ‘ .
  10.                date(‘Y-m-d H:m’, strtotime($event[‘updated’])) . ‘.’);
  11.          putBlock(‘For this event you have to <strong>’ . $event[‘summary’] . ‘</strong>.’);
  12.       }
  13. }

我们可以明显看到,无论if声明中的内容如何、其代码都属于表示代码,而余下的部分则属于业务逻辑。与其利用一个庞大的函数处理所有事务,我们更倾向于将其拆分为多个不同函数:


  1. 		function printEventDetails() {
  2.    global $client;
  3.    foreach (retrieveEvents($_GET[‘calendarId’]) as $event)
  4.       if (isCurrentEvent($event))
  5.          putEvent($event);
  6. }
  7. function isCurrentEvent($event) {
  8.    return $event[‘id’] == $_GET[‘showThisEvent’];
  9. }

分离工作完成后,业务逻辑就变得简单易懂了。我们甚至提取了一个小型方法来检测该事件是否就是当前事件。所有表示代码现在都由名为putEvent($event)函数负责,且被保存在functions_display.php文件当中:


  1. 		function putEvent($event) {
  2.    putTitle(‘Details for event: ‘ . $event[‘summary’]);
  3.    putBlock(‘This event has status ‘ . $event[‘status’]);
  4.    putBlock(‘It was created at ‘ .
  5.          date(‘Y-m-d H:m’, strtotime($event[‘created’])) .
  6.          ‘ and last updated at ‘ .
  7.          date(‘Y-m-d H:m’, strtotime($event[‘updated’])) . ‘.’);
  8.    putBlock(‘For this event you have to <strong>’ . $event[‘summary’] . ‘</strong>.’);
  9. }

尽管该方法只负责显示信息,但其功能仍需在对$event结构非常了解的前提下方能实现。不过对于我们的简单实例来说,这已经足够了。对于其余方法,大家可以通过类似的方式进行分离。

清除过长的if-else声明

目前代码整理工作还剩下最后一步,也就是存在于doUserAction()函数中的过长if-else声明,其作用是决定每项行为的实际处理方式。在元编程方面(通过引用来调用函数),PHP具备相当出色的灵活性。这种特性使我们能够将$_GET变量的值与函数名称关联起来。如此一来,我们可以在$_GET变量中引入单独的action参数,并将该值作为函数名称。


  1. 		function doUserAction() {
  2.    putMenu();
  3.    if (!isset($_GET[‘action’])) return;
  4.       $_GET[‘action’]();
  5. }

基于这种方式,我们生成的菜单将如下所示:


  1. 		function putMenu() {
  2.    putLink(‘?action=putHome’, ‘Home’);
  3.    putLink(‘?action=printCalendars’, ‘Show Calendars’);
  4.    putLink(‘?logout’, ‘Log Out’);
  5.    print(‘<br><br>’);
  6. }

如大家所见,经过重新整理之后,代码已经呈现出面向对象式设计的特性。虽然目前我们还不清楚其面向的是何种对象、会执行哪些确切行为,但其特征已经初露端倪。

我们已经让来自业务逻辑的数据类型成为表示的决定性因素,其效果与我们在文首介绍环节中谈到的依赖倒置机制比较类似。控制流的方向仍然是从业务逻辑指向表示,但源代码依赖性则与之相反。从这一点上看,我认为整套机制更像是一种双向依赖体系。

设计倾向上的面向对象化还体现在另一个方面,即我们几乎没有涉及到元编程。我们可以调用一个方法,但却对其一无所知。该方法可以拥有任何内容,且过程与处理低级多态性非常相近。

依赖性分析

对于当前代码我们可以绘制出一份关系图,内容如下所示。通过这幅关系图,我们可以看到应用程序运行流程的前几个步骤。当然,把整套流程都画下来就太过复杂了。

\

蓝色线条代表程序调用。如大家所见,这些线条与始终指向同一个方向。图中的绿色线条则表示间接调用,可以看到所有间接调用都要经过doUserAction()函数。这两种线条代表控制流,显然控制流的走向基本不变。

红色线条则引入了完全不同的概念,它们代表着最初的源代码依赖关系。之所以说“最初”,是因为随着应用的运行其指向将变得愈发复杂、难以把握。putMenu()方法中包含着被特定关系所调用的函数的名称。这是一种依赖关系,同时也是适用于所有其它关系创建方法的基本规则。它们的具体关系取决于其它函数的行为。

上图中我们还能看到另一种依赖关系,即对数据的依赖。我前面曾经提到过$calendar与$event,输出函数需要清楚了解这些数组的内部结构才能实现既定功能。

完成了以上内容之后,我们已经做好充分准备、可以迎来本篇教程中的最后一项挑战。

面向对象解决方案

无论采用哪种范式,我们都不可能为问题找到完美的解决方案。因此以下代码组织方式仅仅属于我的个人建议。

从直觉出发

我们已经完成了业务逻辑与表现的分离工作,甚至将doUserAction()方法作为一个独立单元。那么我的直觉是先创建三个类,Presenter、Logic与Router。三者以后可能都需要进行调整,但我们不妨先从这里着手,对吧?

Router中将只包含一个方法,且实现方式与之前提到的方法非常相似。


  1. 		class Router {
  2.    function doUserAction() {
  3.       (new Presenter())->putMenu();
  4.       if (!isset($_GET[‘action’]))
  5.          return;
  6.       (new Logic())->$_GET[‘action’]();
  7.    }
  8. }

现在我们要做的是利用刚刚创建的Presenter对象调用putMenu()方法,其它行为则利用Logic对象加以调用。不过这样会马上产生问题——我们的一项行为并不包含在Logic类当中。putHome()存在于Presenter类中,我们需要在Logic中引入一项行为,借以在Presenter中作为putHome()方法的委托。请记住,目前我们要做的只是将现有代码整理到三个类当中,并将三者作为面向对象设计的备选对象。现在所做的一切只是为了让设计方案能够正常运作,待代码编写完成后、我们将进一步加以调试。

在将putHome()方法引入Logic类后,我们又遇上新的难题。怎样才能从Presenter中调用方法?我们可以创建一个Presenter对象,并将其传递至Logic当中。下面我们从Router类入手。


  1. 		class Router {
  2.    function doUserAction() {
  3.       (new Presenter())->putMenu();
  4.       if (!isset($_GET[‘action’]))
  5.          return;
  6.       (new Logic(new Presenter))->$_GET[‘action’]();
  7.    }
  8. }

现在我们可以向Logic添加一个构造函数,并将其添加到Presenter内指向putHome()的委托当中。


  1. 		class Logic {
  2.    private $presenter;
  3.    function __construct(Presenter $presenter) {
  4.       $this->presenter = $presenter;
  5.    }
  6.    function putHome() {
  7.       $this->presenter->putHome();
  8.    }
  9. […]
  10. }

通过对index.php的一些小小调整、让Presenter包含原有display方法、Logic包含原有业务逻辑函数、Router包含原有行为选择符,我们已经可以让自己的代码正常运行并具备“Home”菜单元素。


  1. 		require_once './google-api-php-client/src/Google_Client.php';
  2. require_once ‘./google-api-php-client/src/contrib/Google_CalendarService.php’;
  3. require_once __DIR__ . ‘/../apiAccess.php’;
  4. require_once ‘./functins_google_api.php’;
  5. require_once ‘./Presenter.php’;
  6. require_once ‘./Logic.php’;
  7. require_once ‘./Router.php’;
  8. session_start();
  9. $client = createClient();
  10. if(!authenticate($client)) return;
  11. (new Router())->doUserAction();

下面就是其执行效果。

\

接下来,我们需要在Logic类中适当变更指向display逻辑的调用指令,从而与$this->presenter相符。现在我们有两个方法——isCurrentEvent()与retrieveEvents()——二者只被用于Logic类内部。我们将其作为专用方法,并据此变更调用关系。

下面我们对Presenter类进行同样处理,并将所有指向方法的调用都变更为指向$this->something。由于putTitle()、putLink()与putBlock()都只由Presenter使用,因此需要将其变为专用。如果感到上述变更过程难于理解及操作,请大家查看附件源代码内GoogleCalObjectOrientedInitial文件夹中的已完成代码。

现在我们的应用程序已经能够正常运行,这些按面向对象语法整理过的程序化代码仍然使用$client全局变量,且拥有大量其它非面向对象式特性——但仍然能够正常运行。

如果要为目前的代码绘制依赖关系类图,则应如下所示:

\

控制流与源代码的依赖关系都通过Router、然后是Logic、最后通过表示层。最后一步变更削弱了我们在之前步骤中所观察到的依赖倒置特性,但大家千万不要因此受到迷惑——原理依然如故,我们要做的是使其更加清晰。

恢复源代码依赖关系

很难界定基础性原则之间哪一条更重要,但我认为依赖倒置原则对我们的应用设计影响最大也最直接。该原则规定:

A:高层模块不应依赖于低级模块,二者都应依赖于抽象。

B:抽象不应依赖于细节,细节应依赖于抽象。

简单来说,这意味着具体实施应依赖于抽象类。类越趋近抽象,它们就越不容易发生改变。因此我们可以这样理解:变更频繁的类应依赖于其它更为稳定的类。所以任何应用中最不稳定的部分很可能是用户界面,这在我们的应用示例中通过Presenter类来实现。让我们再来明确一下依赖倒置流程。

首先,我们让Router仅使用Presenter,并打破其对Logic的依赖关系。


  1. 		class Router {
  2.    function doUserAction() {
  3.       (new Presenter())->putMenu();
  4.       if (!isset($_GET[‘action’]))
  5.          return;
  6.       (new Presenter())->$_GET[‘action’]();
  7.    }
  8. }

然后我们变更Presenter,使其使用Logic实例并由此获取需要的信息。在我们的例子中,我认为由Presenter来建立该Logic实例也可以接受,但在生产系统当中、大家可能通常会利用Factories来创建与对象相关的业务逻辑,并将其注入表示层当中。

现在,原本同时存在于Logic与Presenter两个类中的putHome()函数将从Logic中消失。这一现象说明我们已经开始进行重复数据清除工作。指向Presenter的构造函数与引用也从Logic中消失了。另一方面,由构造函数所创建的Logic对象则必须被写入Presenter。


  1. 		class Presenter {
  2.    private $businessLogic;
  3.    function __construct() {
  4.       $this->businessLogic = new Logic();
  5.    }
  6.    function putHome() {
  7.       print(‘Welcome to Google Calendar over NetTuts Example’);
  8.    }
  9. […]
  10. }

以上变更完成之后,点击Show Calendars,屏幕上会出现错误提示。由于我们链接内部的所有行为都指向Logic类中的函数名称,因此必须通过更多一致性调整来恢复二者之间的依赖关系。下面我们对方法进行一一修改,先来看第一条错误信息:


  1. 		Fatal error: Call to undefined method Presenter::printCalendars()
  2. in /[…]/GoogleCalObjectOrientedFinal/Router.php on line 9

我们的Router希望调用Presenter中某个并不存在的方法,也就是printCalendars()。我们在Presenter中创建这样一个方法,并检查它会对Logic造成哪些影响。在结果中大家可以看到,它输出了一条标题,并在重复循环之后再次调用putCalendars()。在Presenter类中,printCalendars()方法如下所示:


  1. 		function printCalendars() {
  2.    $this->putCalendarListTitle();
  3.    foreach ($this->businessLogic->getCalendars() as $calendar) {
  4.       $this->putCalendarListElement($calendar);
  5.    }
  6. }

在Logic方面,该方法则非常单纯——直接调用谷歌API库。


  1. 		function getCalendars() {
  2.    global $client;
  3.    return getCalendarList($client)[‘items’];
  4. }

这可能让大家心中出现两个问题,“我们真的需要Logic类吗?”以及“我们的应用程序是否存在任何逻辑?”好吧,目前我们还不知道答案,现在能做的只是继续上述过程,直到所有代码都能正常工作且Logic不再依赖于Presenter。

接下来,我们将使用Presenter中的printCalendarContents()方法,如下所示:


  1. 		function printCalendarContents() {
  2.    $this->putCalendarTitle();
  3.    foreach ($this->businessLogic->getEventsForCalendar() as $event) {
  4.       $this->putEventListElement($event);
  5.    }
  6. }

这将反过来允许我们简化Logic中的getEventsForCalendar(),并将其转化为如下形式:


  1. 		function getEventsForCalendar() {
  2.    global $client;
  3.    return getEventList($client, htmlspecialchars($_GET[‘showThisCalendar’]))[‘items’];
  4. }

现在应用已经不再报错,但我却又发现了新的问题。$_GET变量同时被Logic与Presenter类所使用——$_GET应该只被Presenter类使用才对。我的意思是,由于需要创建用于填充$_GET变量的链接,Presenter是肯定需要感知$_GET的。这就意味着$_GET与HTTP密切相关。现在,我们希望自己的代码能与命令行或者桌面图形用户界面协同运作。

因此我们需要保证只有Presenter感知到这一情况,即将以上两个方法变换为下列内容:


  1. 		function getEventsForCalendar($calendarId) {
  2.    global $client;
  3.    return getEventList($client, $calendarId)[‘items’];
  4. }

  1. 		function printCalendarContents() {
  2.    $this->putCalendarTitle();
  3.    $eventsForCalendar = $this->businessLogic->getEventsForCalendar(htmlspecialchars($_GET[‘showThisCalendar’]));
  4.    foreach ($eventsForCalendar as $event) {
  5.       $this->putEventListElement($event);
  6.    }
  7. }

现在我们需要实现特定事件的输出功能。对于本文中的范例,我们假设自己无法直接检索任何事件,即必须亲自进行事件查找。Logic类这时候就要派上用场了,我们可以在其中操作事件列表并搜索特定ID:


  1. 		function getEventById($eventId, $calendarId) {
  2.    foreach ($this->getEventsForCalendar($calendarId) as $event)
  3.       if ($event[‘id’] == $eventId)
  4.          return $event;
  5. }

然后Presenter的对应调用会完成输出工作:


  1. 		function printEventDetails() {
  2.    $this->putEvent(
  3.       $this->businessLogic->getEventById(
  4.          $_GET[‘showThisEvent’],
  5.          $_GET[‘calendarId’]
  6.       )
  7.    );
  8. }

就是这样,我们已经成功完成了依赖倒置。

\

控制流仍然由Logic指向Presenter,所有输出内容也完全由Logic进行定义。这样如果我们打算接入其它日程表服务,则只需创建另一个Logic类并将其注入Presenter即可,Presenter本身不会感知到任何差异。再有,源代码依赖关系也被成功倒置。Presenter是惟一创建且直接依赖于Logic的类。这种依赖关系对于保证Presenter可随意变更数据显示方式而又不影响Logic内容而言至关重要。此外,这种依赖关系允许我们利用CLI Presenter或者其它任何向用户显示信息的方法来替代HTML Presenter。

摆脱全局变量

现在惟一漏网的潜在设计缺陷就只剩下$client全局变量了。应用程序中的所有代码都会对其进行访问,但与之形成鲜明对比的是,真正有必要访问$client的只有Logic类一个。最直观的解决办法肯定是使其变更为专用类变量,但这样一来我们就需要将$client经由Router传递至Presenter处,从而使presenter能够利用$client变更创建出Logic对象——这对于解决问题显然无甚作用。我们的设计初衷是在独立环境下建立类,并准确为其分配依赖关系。

对于任何大型类结构,我们都倾向于使用Factories;但在本文的小小范例中,index.php文件已经足以容纳逻辑创建了。作为应用程序的入口点,这个类似于高层体系结构中“main”的文件仍然处于业务逻辑的范畴之外。

因此我们将index.php中的代码变更为以下内容,同时保留所有内容以及session_start()指令:


  1. 		$client = createClient();
  2. if(!authenticate($client)) return;
  3. $logic = new Logic($client);
  4. $presenter = new Presenter($logic);
  5. (new Router($presenter))->doUserAction();

结语

现在工作彻底完成了。当然,我们的设计肯定还有很多改进的空间。我们可以为Logic类中的方法编写一些测试流程,也许Logic类本身也可以换个更有代表性的名称,例如GoogleCalendarGateway。我们还可以创建Event与Calendar类,从而更好地控制相关数据及行为,同时将Presenter的依赖关系根据数据类型拆分为数组。另一项改进与扩展方针则是创建多态性行为类,用于取代直接通过$_GET调用函数。总而言之,对于这一范例的改进可谓无穷无尽,有兴趣的朋友可以尝试将自己的想法转化为现实。我在附件的GoogleCalObjectOrientedFinal文件夹中保存有代码的最终版本,大家能够以此为起点进行探索。

如果大家的好奇心比较强,也可以试着将这款小应用与其它日程表服务对接,看看如何在不同平台上以不同方式实现信息输出。对于使用NetBeans的朋友,每个源代码文件夹中都包含有NetBeans项目,大家只要直接打开即可。在最终版本中,PHPUnit也已经准备就绪。不过我在其它项目中将其移除了——因为还没有经过测试。

感谢您的阅读。

附件下载地址:https://github.com/tutsplus/From-Procedural-to-Object-Oriented-PHP

原文链接:https://net.tutsplus.com/tutorials/php/from-procedural-to-object-oriented-php/

OpenERP API 接口

API 接口?

使用Web服务(Working with Web Services)?

鉴于OpenERP的架构,它不适合直接通过PostgreSQL客户端或者 ODBC 这样的连接方法访问数据库, 幸运的是,OpenERP提供了一个非常全面的web服务集,允许你通过标准协议做任何事情。

Note

虽然直接访问数据库,在技术上是可行的,你必须意识到这可能对您的数据造成灾难性的后果,除非你知道你是在做 什么。当你直接访问数据库的时候,建议您关闭OpenERP服务器,以避免缓存和并发问题。

支持的网络服务协议(Supported Web Services Protocols)?

目前支持的协议是XML-RPC和Net-RPC。XML-RPC是用于Web服务的第一批标准之一, 几乎可以再任何语言中使用。这是一个非常详细的协议,而且在需要的时候可以引入潜在位。 另一方面,Net-RPC是一个优化的协议,特别用在Python编写的应用程序之间。.

对于REST风格的web服务的支持在将来的OpenErp发布包中支持。

对于SOAP协议,目前的OPenErp已经不再支持,但是如果在社区有足够的爱好者的话将来可能恢复。

可用的Web服务(Available Web Services)?

OpenERP 为你提供了以下的Web服务.

Note

你能在服务的源码(/bin/service/web_services.py)的对应类里面找到每种服务的细节 .

db:
提供函数创建、删除、备份、恢复数据库. 请谨慎使用!

Technorati 标签: ,,,

common:

让你登录和退出 OpenERP, 并且提供各种实用功能。你只有登录后才能使用其他的网络服务.

object:
这是最有用的网络服务,因为通过它可以访问 OpenERP 对象. 值得注意的是, 函数 “execute” 让你调用对象的方法, 比如可以搜索的大部分的ORM方法,读写记录。它也可以用来调用价格计算等对象的其他方法.

Note

主要的 ORM 方法一览:

create({‘field’:’value’})
  • 创建一个具有指定值的新纪录
  • Returns: 新纪录的ID
search([(‘arg1’,’=’,’value1’)…], offset=0, limit=1000)
  • arg1, arg2, .. ,argN: 指定列表的搜索条件
  • offset: 跳过的可选的记录
  • limit: 返回的最大数量的记录
  • Returns: 匹配给定条件的记录
read([IDS], [‘field1’,’field2’,…])
  • fields: 返回的字段名字(默认全部返回) (default: all fields)
  • Returns: 每条记录的ID和请求字段的值
write([IDS], {‘field1’:’value1’,’field2’:3})
  • values: 更新的字段的值
  • Updates 对给定的记录按照给定的值进行更新
  • Returns: True
unlink([IDS])
  • 按照给定的IDS删除记录
  • Returns: True

通过 Web 服务不能使用 Browse() 函数.

另一个有用的功能是 “exec_workflow”, 它可以让你通过工作流制定记录的进展.

向导:

提供对旧式的向导。新风格的向导是基于ORM的,因此他们可以通过 “object” web 服务来进行访问.

报告:

让你生成和检索报告.

例子:通过Web服务写入数据(Example:writing data through the Web Service)?

下面是一个写数据的例子程序。在下一章你会发现关于多种编程语言 XML-RPC的更详尽的例子.

  1. login: 在Web服务 “common” 中调用 “login” 函数,使用下面的参数:
    • database
    • user name
    • password
  2. 创建一个新的合作者: 在Web服务 “object” 中调用 “execute” 函数,使用下面的参数:
    • database
    • user id provided by “login” in step 1.
    • the object name : ‘res.partner’
    • the name of the ORM method : “create”
    • some data to be recorded

上面提到的数据都是键值对, 比如:

  • name: Fabien Pinckaers
  • lang: fr_FR

但是更复杂的数据结构也可以发送。比如你可以在一个单一的Web服务调用中创建一个合作者 和他的地址。在那种情况下,所有的数据在服务的相同的数据库事务中来处理。这意味. 着你一定要保存好你的数据一致性的状态。这是对所有ERP应用的关键要求.

XML-RPC Web服务(XML-RPC Web Services)?

XML-RPC 是一个著名的Web服务. Web 服务是一个工具,它可以再现有的网络基础设施上面设置分布式的应用程序。这些应用程序使用一种传输层的网络但是并不提供直接通过浏览器的人机界面。可扩展标记语言(XML)提供了描述远程过程调用(RPC)的词汇表,RPC是使用超文本传输协议(HTTP) 在计算机之间传输。实际上,RPC让各开发者自行定义网络调用中的接口。这些接口可以是很简单的一个函数调用也可以像大型API那样复杂.

XML-RPC 允许在两台或者更多运行不同操作系统和不同语言程序的计算机之间协同处理。比如,一个JAVA应用可以和一个Perl应用会谈,一个Perl应用可以同一个同ASP会谈的PYTHON应用会谈,等等。系统集成商往往在不同系统之间建立自己的连接,创建它们自己定义的格式的协议来进行通信,但是这造成了大量的不常使用的协议。RPC方法的程序员无需了解底层的协议、网络以及各种实施细则.

XML-RPC 可以同 Python, Java, Perl, PHP, C, C++, Ruby, Microsoft’s .NET 以及许多其他的编程语言来一起使用。它的实现被广泛用于 Unix, Linux, Windows 和 Macintosh 的平台.

一个 XML-RPC 调用实在双方之间进行的,客户端(调用程序)和服务器(被调用过程)。服务时提供在一个特定的URL上的,比如 (such as http://example.org:8080/rpcserv/).

上面我们只是接触了 XML-RPC 的表面. 我推荐 O’Reilly’s “Programming Web Service with XML-RPC” 进行进一步的学习。还可以阅读以下几个环节:

接口(InterFaces)?
XML-RPC?
XML-RPC 架构?

OpenERP 基于C/S体系结构。服务器和客户端之间的通信使用XML-RPC协议。XML-RPC是一个非常简单的协议,它允许客户端进行远程过程调用。被调用的函数,它的参数,调用结果通过XML编码并且使用HTTP进行传输。欲了解更多的关于XML-RPC的详尽信息,请参阅: http://www.xml-rpc.com.

架构(Architecture)?

下面的图标综合了OpenERP的客户端和服务器结构。OpenERP的服务器和客户端通信使用 XML-RPC.

../_images/tech_arch.png

客户端

OpenERP 的逻辑是在服务器端配置的。客户端是很简单的,它是仅用于POST的数据(forms, lists, trees)并且把结果发回服务器。新功能的更新和加入并不需要客户端的升级,这使得OpenERP更容易维护.

客户端并不明白POST的内容。即使像点击打印图标的行动时发送到服务器并且询问如何作出反应.

客户端的操作时很简单的,当客户发出一个动作(保存一个表格、打开一个目录、打印…)它发送动作到服务器。然后服务器执行客户端的请求并将结果发送回来.

下面是三种行为;

  • Open a window (form or tree)
  • Print a document
  • Execute a wizard
Python?
通过 xml-rpc 获取数据?
代码示例?
  • 创建一个合作伙伴和他的地址
import xmlrpclib

username = 'admin' # OpenERP 登陆用户
pwd = 'admin'      # 登陆密码
dbname = 'terp'    # OpenERP 帐套

# Get the uid
sock_common = xmlrpclib.ServerProxy ('http://localhost:8069/xmlrpc/common')
uid = sock_common.login(dbname, username, pwd)

#replace localhost with the address of the server
sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/object')

partner = {
   'name': 'Fabien Pinckaers',
   'lang': 'fr_FR',
}

partner_id = sock.execute(dbname, uid, pwd, 'res.partner', 'create', partner)

address = {
   'partner_id': partner_id,
   'type' : 'default',
   'street': 'Chaussée de Namur 40',
   'zip': '1367',
   'city': 'Grand-Rosière',
   'phone': '+3281813700',
   'fax': '+3281733501',
}

address_id = sock.execute(dbname, uid, pwd, 'res.partner.address', 'create', address)
  • 查询业务伙伴
    args = [('vat', '=', 'ZZZZZZ')] # 查询过滤条件
    ids = sock.execute(dbname, uid, pwd, 'res.partner', 'search', args)
  • 读取业务伙伴数据
    fields = ['name', 'active', 'vat', 'ref'] # 需要读取的数据字段
    data = sock.execute(dbname, uid, pwd, 'res.partner', 'read', ids, fields) #ids is a list of id
  • 更新业务伙伴数据
    values = {'vat': 'ZZ1ZZZ'} # 待更新数据Dictionary
    result = sock.execute(dbname, uid, pwd, 'res.partner', 'write', ids, values)
  • 删除业务伙伴
    # ids : 待删除业务伙伴id列表
    result = sock.execute(dbname, uid, pwd, 'res.partner', 'unlink', ids)
PHP?
通过 xml-rpc 获取数据?

下载 XML-RPC PHP Library

windows / linux: 从 http://phpxmlrpc.sourceforge.net/ 上下载xmlrpc框架,目前最新的正式版本是2007年2月25日发行的2.2版本

配置 PHP XML-RPC Library

从xmlrpc-2.2.tar.gz 解压出xmlrpc.inc 文件,把这个文件放到php函数库文件夹中,重启apache/iis 服务器

代码示例

  • 登陆OpenERP
function connect() {
   var $user = 'admin';
   var $password = 'admin';
   var $dbname = 'db_name';
   var $server_url = 'http://localhost:8069/xmlrpc/';

   if(isset($_COOKIE["user_id"]) == true)  {
       if($_COOKIE["user_id"]>0) {
       return $_COOKIE["user_id"];
       }
   }

   $sock = new xmlrpc_client($server_url.'common');
   $msg = new xmlrpcmsg('login');
   $msg->addParam(new xmlrpcval($dbname, "string"));
   $msg->addParam(new xmlrpcval($user, "string"));
   $msg->addParam(new xmlrpcval($password, "string"));
   $resp =  $sock->send($msg);
   $val = $resp->value();
   $id = $val->scalarval();
   setcookie("user_id",$id,time()+3600);
   if($id > 0) {
       return $id;
   }else{
       return -1;
   }
 }
  • 查询业务伙伴
/**
 * $client = xml-rpc handler
 * $relation = name of the relation ex: res.partner
 * $attribute = name of the attribute ex:code
 * $operator = search term operator ex: ilike, =, !=
 * $key=search for
 */

function search($client,$relation,$attribute,$operator,$keys) {
     var $user = 'admin';
     var $password = 'admin';
     var $userId = -1;
     var $dbname = 'db_name';
     var $server_url = 'http://localhost:8069/xmlrpc/';

     $key = array(new xmlrpcval(array(new xmlrpcval($attribute , "string"),
              new xmlrpcval($operator,"string"),
              new xmlrpcval($keys,"string")),"array"),
        );

     if($userId<=0) {
     connect();
     }

     $msg = new xmlrpcmsg('execute');
     $msg->addParam(new xmlrpcval($dbname, "string"));
     $msg->addParam(new xmlrpcval($userId, "int"));
     $msg->addParam(new xmlrpcval($password, "string"));
     $msg->addParam(new xmlrpcval($relation, "string"));
     $msg->addParam(new xmlrpcval("search", "string"));
     $msg->addParam(new xmlrpcval($key, "array"));

     $resp = $client->send($msg);
     $val = $resp->value();
     $ids = $val->scalarval();

     return $ids;
}
  • 创建业务伙伴
<?

include('xmlrpc.inc');

$arrayVal = array(
'name'=>new xmlrpcval('Fabien Pinckaers', "string") ,
'vat'=>new xmlrpcval('BE477472701' , "string")
);

$client = new xmlrpc_client("http://localhost:8069/xmlrpc/object");

$msg = new xmlrpcmsg('execute');
$msg->addParam(new xmlrpcval("dbname", "string"));
$msg->addParam(new xmlrpcval("3", "int"));
$msg->addParam(new xmlrpcval("demo", "string"));
$msg->addParam(new xmlrpcval("res.partner", "string"));
$msg->addParam(new xmlrpcval("create", "string"));
$msg->addParam(new xmlrpcval($arrayVal, "struct"));

$resp = $client->send($msg);

if ($resp->faultCode())

    echo 'Error: '.$resp->faultString();

else

    echo 'Partner '.$resp->value()->scalarval().' created !';

?>
  • 更新业务伙伴数据
/**
 * $client = xml-rpc handler
 * $relation = name of the relation ex: res.partner
 * $attribute = name of the attribute ex:code
 * $operator = search term operator ex: ilike, =, !=
 * $id = id of the record to be updated
 * $data = data to be updated
 */

function write($client,$relation,$attribute,$operator,$data,$id) {
     var $user = 'admin';
     var $password = 'admin';
     var $userId = -1;
     var $dbname = 'db_name';
     var $server_url = 'http://localhost:8069/xmlrpc/';

     $id_val = array();
     $id_val[0] = new xmlrpcval($id, "int");

     if($userId<=0) {
     connect();
     }

     $msg = new xmlrpcmsg('execute');
     $msg->addParam(new xmlrpcval($dbname, "string"));
     $msg->addParam(new xmlrpcval($userId, "int"));
     $msg->addParam(new xmlrpcval($password, "string"));
     $msg->addParam(new xmlrpcval($relation, "string"));
     $msg->addParam(new xmlrpcval("write", "string"));
     $msg->addParam(new xmlrpcval($id, "array"));
     $msg->addParam(new xmlrpcval($data, "struct"));

     $resp = $client->send($msg);
     $val = $resp->value();
     $record = $val->scalarval();

     return $record;

}
JAVA?
通过 xml-rpc 获取数据?

下载 JAVA XML-RPC Library

从 http://ws.apache.org/xmlrpc/ 上下载java xmlrpc框架,目前最新版本是2007年8月发布的3.1版本. All OpenERP errors throw exceptions because the framework allows only an int as the error code where OpenERP returns a string.

代码示例

  • 获取OpenERP帐套列表
import java.net.URL;
import java.util.Vector;

import org.apache.commons.lang.StringUtils;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;

public Vector<String> getDatabaseList(String host, int port)
{
  XmlRpcClient xmlrpcDb = new XmlRpcClient();

  XmlRpcClientConfigImpl xmlrpcConfigDb = new XmlRpcClientConfigImpl();
  xmlrpcConfigDb.setEnabledForExtensions(true);
  xmlrpcConfigDb.setServerURL(new URL("http",host,port,"/xmlrpc/db"));

  xmlrpcDb.setConfig(xmlrpcConfigDb);

  try {
    //Retrieve databases
    Vector<Object> params = new Vector<Object>();
    Object result = xmlrpcDb.execute("list", params);
    Object[] a = (Object[]) result;

    Vector<String> res = new Vector<String>();
    for (int i = 0; i < a.length; i++) {
    if (a[i] instanceof String)
    {
      res.addElement((String)a[i]);
    }
  }
  catch (XmlRpcException e) {
    logger.warn("XmlException Error while retrieving OpenERP Databases: ",e);
    return -2;
  }
  catch (Exception e)
  {
    logger.warn("Error while retrieving OpenERP Databases: ",e);
    return -3;
  }
}
  • 登陆
import java.net.URL;

import org.apache.commons.lang.StringUtils;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;

public int Connect(String host, int port, String tinydb, String login, String password)
{
  XmlRpcClient xmlrpcLogin = new XmlRpcClient();

  XmlRpcClientConfigImpl xmlrpcConfigLogin = new XmlRpcClientConfigImpl();
  xmlrpcConfigLogin.setEnabledForExtensions(true);
  xmlrpcConfigLogin.setServerURL(new URL("http",host,port,"/xmlrpc/common"));

  xmlrpcLogin.setConfig(xmlrpcConfigLogin);

  try {
    //Connect
    params = new Object[] {tinydb,login,password};
    Object id = xmlrpcLogin.execute("login", params);
    if (id instanceof Integer)
      return (Integer)id;
    return -1;
  }
  catch (XmlRpcException e) {
    logger.warn("XmlException Error while logging to OpenERP: ",e);
    return -2;
  }
  catch (Exception e)
  {
    logger.warn("Error while logging to OpenERP: ",e);
    return -3;
  }
}
  • 查询业务伙伴
    TODO
  • 创建业务伙伴
    TODO
  • 更新业务伙伴
    TODO
Python 代码示例?

创建合作伙伴和他们的地址的例子.

import xmlrpclib

sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/object')
uid = 1
pwd = 'demo'

partner = {
    'title': 'Monsieur',
    'name': 'Fabien Pinckaers',
    'lang': 'fr',
    'active': True,
}

partner_id = sock.execute(dbname, uid, pwd, 'res.partner', 'create', partner)

address = {
    'partner_id': partner_id,
    'type': 'default',
    'street': 'Rue du vieux chateau, 21',
    'zip': '1457',
    'city': 'Walhain',
    'phone': '(+32)10.68.94.39',
    'fax': '(+32)10.68.94.39',
}

sock.execute(dbname, uid, pwd, 'res.partner.address', 'create', address)

用下面的脚本来获得用户的 UID :

sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/common')
 UID = sock.login('terp3', 'admin', 'admin')

CRUD(创建/读取/更新/删除)代码示例:

"""
:The login function is under
::    http://localhost:8069/xmlrpc/common
:For object retrieval use:
::    http://localhost:8069/xmlrpc/object
"""
import xmlrpclib

user = 'admin'
pwd = 'admin'
dbname = 'terp3'
model = 'res.partner'

sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/common')
uid = sock.login(dbname ,user ,pwd)

sock = xmlrpclib.ServerProxy('http://localhost:8069/xmlrpc/object')

# CREATE A PARTNER
partner_data = {'name'.. code-block:: php:'Tiny', 'active':True, 'vat':'ZZZZZ'}
partner_id = sock.execute(dbname, uid, pwd, model, 'create', partner_data)

# The relation between res.partner and res.partner.category is of type many2many
# To add  categories to a partner use the following format:
partner_data = {'name':'Provider2', 'category_id': [(6,0,[3, 2, 1])]}
# Where [3, 2, 1] are id fields of lines in res.partner.category

# SEARCH PARTNERS
args = [('vat', '=', 'ZZZZZ'),]
ids = sock.execute(dbname, uid, pwd, model, 'search', args)

# READ PARTNER DATA
fields = ['name', 'active', 'vat', 'ref']
results = sock.execute(dbname, uid, pwd, model, 'read', ids, fields)
print results

# EDIT PARTNER DATA
values = {'vat':'ZZ1ZZ'}
results = sock.execute(dbname, uid, pwd, model, 'write', ids, values)

# DELETE PARTNER DATA
results = sock.execute(dbname, uid, pwd, model, 'unlink', ids)

PRINT(打印) 示例代码:

  1. 打印发票
  2. IDS is the invoice ID, as returned by:
  3. ids = sock.execute(dbname, uid, pwd, ‘account.invoice’, ‘search’, [(‘number’, ‘ilike’, invoicenumber), (‘type’, ‘=’, ‘out_invoice’)])
import time
import base64
printsock = xmlrpclib.ServerProxy('http://server:8069/xmlrpc/report')
model = 'account.invoice'
id_report = printsock.report(dbname, uid, pwd, model, ids, {'model': model, 'id': ids[0], 'report_type':'pdf'})
time.sleep(5)
state = False
attempt = 0
while not state:
    report = printsock.report_get(dbname, uid, pwd, id_report)
    state = report['state']
    if not state:
    time.sleep(1)
    attempt += 1
    if attempt>200:
    print 'Printing aborted, too long delay !'

    string_pdf = base64.decodestring(report['result'])
    file_pdf = open('/tmp/file.pdf','w')
    file_pdf.write(string_pdf)
    file_pdf.close()
PHP 代码示例?

下面的例子是如何使用 PHP 创建一个合作伙伴. 这里使用 phpxmlrpc 库, 在 sourceforge 上有效.

<?

    include('xmlrpc.inc');

    $arrayVal = array(
    'name'=>new xmlrpcval('Fabien Pinckaers', "string") ,
    'vat'=>new xmlrpcval('BE477472701' , "string")
    );

    $client = new xmlrpc_client("http://localhost:8069/xmlrpc/object");

    $msg = new xmlrpcmsg('execute');
    $msg->addParam(new xmlrpcval("dbname", "string"));
    $msg->addParam(new xmlrpcval("3", "int"));
    $msg->addParam(new xmlrpcval("demo", "string"));
    $msg->addParam(new xmlrpcval("res.partner", "string"));
    $msg->addParam(new xmlrpcval("create", "string"));
    $msg->addParam(new xmlrpcval($arrayVal, "struct"));

    $resp = $client->send($msg);

    if ($resp->faultCode())

        echo 'Error: '.$resp->faultString();

    else

        echo 'Partner '.$resp->value()->scalarval().' created !';

    ?>
Perl 代码示例?

下面的例子是使用 Perl 创建、查找、删除一个合作伙伴.

#!c:/perl/bin/perl
# 17-02-2010
# OpenERP XML RPC communication example
# Todor Todorov <todorov@hp.com> <tttodorov@yahoo.com>

use strict;
use Frontier::Client;
use Data::Dumper;

my($user) = 'admin';
my($pw) = 'admin';
my($db) = 'put_your_dbname_here';
my($model) = 'res.partner';

#登录
my $server_url = 'http://localhost:8069/xmlrpc/common';
my $server = Frontier::Client->new('url' => $server_url);
my $uid = $server->call('login',$db,$user,$pw);

print Dumper($uid);

my $server_url = 'http://localhost:8069/xmlrpc/object';
my $server = Frontier::Client->new('url' => $server_url);

print Dumper($server);

#
# 创建合作伙伴
#
my $partner_data = {'name'=>'MyNewPartnerName',
                  'active'=> 'True',
          'vat'=>'ZZZZZ'};
my $partner_id = $server->call('execute',$db, $uid, $pw, $model, 'create', $partner_data);

print Dumper($partner_id);

#
# 搜索合作伙伴
#
my $query = [['vat', '=', 'ZZZZZ']];

print Dumper($query);

my $ids = $server->call('execute',$db, $uid, $pw, $model, 'search', $query);

print Dumper($ids);

#这里等待用户输入
#OpenERP interface my be checked if partner is shown there

print $/."Check OpenERP if partner is inserted. Press ENTER".$/;
<STDIN>;

#
# 删除合作伙伴
#
my $results = $server->call('execute',$db, $uid, $pw, $model, 'unlink', $ids);

print Dumper($results);

在 OpenERP 的 GTK 或 web 客户端的一切活动都是通过 XML/RPC webservices. 启动 openERP GTK 客户端 使用 ./openerp-client.py -l debug_rpc (or debug_rpc_answer) 然后你可以在 GTK 客户端操作,查看客户端操作日志, 你将看到webservice的标签。通过在日志中创建缩进将帮助您 找出它的web服务.

http://tomora.blog.51cto.com/2624569/1305859 原文

每个Web开发者必备的9个软技能

 

对于一份工作,你可能专注于修炼自己的内功,会在不自觉中忽视软技能。硬技能决定你是否能得到工作,而软技能能够表明你是否适合这份工作和适应工作环境等。所有的公司都有属于自己的文化,并努力将这些文化传承下去,所以他们必须确保每一位新员工能够适应这个环境。

许多硬技能很容易获得并可很快的提高,而软技能由于和你性格等联系紧密,可想要改变他们是一件很困难和缓慢的事情。这里有七个核心的软技能,适用于大多数的企业,它们不分行业,但作为Web开发者不应该忽视其中的任何一个。但是,有两个软技能不仅有利于Web开发者,还适用于所有的开发者,那就是往往被企业和员工忽视的:批判性思维和自负管理。

1. 交流

现代科技发展得很快,移动通信的发达,你必须确保自己能够熟练运用这些。

  • 电话或手机
  • 电子邮件
  • 在线聊天
  • 社交
  • 各种即时消息应用

而这些也只是简单的工作,你真正需要做到的是必须提高沟通技巧。作为一名Web开发者更需要自信和有效地进行相关的远程交流,对象包括:上司、同事、用户和其它开发者。

与用户沟通是一件非常困难的事情,因为你不得不向非技术人员解释技术性很强的概念。你在平时与同事交流中可以习惯性的使用一些行业术语,但不要指望用户能够理解这些术语。

另外,沟通的关键是要把握好细节,俗话说:“伟大的产品微末。”那么,你与人沟通时会使用正式的还是轻松的言语呢?不同人之间都有很大的差异。也许最关键的是,你得知道如何接受他们的批评并在批评中学习。

2. 倾听

倾听和交流是紧密相连的,但对于Web开发者需要单独拿出来讨论。

通常你会被很多杂事包围,谁也逃避不了。若是你有电话过来,请打开手机的扬声器,然后继续回到屏幕做你该做的事情,你只需要听对方说的什么,并甄选出极其重要的信息即可。如果有不明白的地方,直接说出来。例如,对于一些代码的修改或者功能的讨论,用扬声器是一个非常好的方式。

3. 适应能力

适应能力是Web开发者的一个重要技能,作为一名Web开发者,你应该学会适应各种不同的环境,看看互联网在近八年的惊人变化你就应该知道是为什么了。你至少需要适应以下几种情况:

  • 不得不学习新的技能
  • 不得不承担额外的责任
  • 你负责的项目会被突然取消
  • 工作时间会有很大的变化
  • 办公室的变化或公司搬迁到新的城市
  • 公司被合并或被收购

随着社会的发展,行业的竞争,你可能会同时面临好几个上述的问题,如果你出生在1980年~2000年,更需要以一个开放、积极去的心态来处理各种艰难的困境。

4. 合作

你永远不可能一个人完成繁杂的事情,你必须要有一个团队,而谁也不愿意不被团队成员所接纳。你需要关心每一位同事,了解他们的优缺点,有则改之无则加勉。同时,你若是遇到一个很难找到和解决的Bug,你至少可以明确地知道求助谁。如果你突然成为一个新项目组的Leader,需要按照每一个团队成员的具体情况进行有效地分配任务。

5. 态度

前面讨论了团队合作的重要性,如果没人愿意花时间和你一起愉快的玩耍,说明你真的不擅长团队合作。可以通过以下几个方法来提升这个技能:

  • 主动问候对方
  • 少抱怨
  • 提供建议,而不仅是批评
  • 不要偏激

有一个积极的态度不仅可以让你更轻松的面对挑战,也会让你的同事更乐于帮助你。

6. 职业道德

你是一个非常有原则的人吗?你是一个诚实和真诚的人吗?你是一个有道德的人吗?具备良好的职业道德,关乎着你的诚信。

你需要保持自己不被卷入企业之间的间谍活动或内幕交易,以及不要经常抱怨领导和同事、表现出负面情绪,这只能凸显出你的不诚信。如果你能够做到这些,那么你一定会被公司永远地评为“本月最佳员工”。

7. 判断/辨别

这是一个概念性的总称,包含了技术到常识的决策能力。对于上级和同事采取什么样的态度呢?在和外界沟通时,你得知道可以谈的内容尺度,什么是机密、什么是个人隐私等。具备一个良好的判断力,能够为你的职业生涯保驾护航。例如,你在发布Facebook或Twitter时需要有分寸。

8. 批判性思维

批判性思维有很多学术定义,这里只来探讨应用到工作中方法。

问题:我经常上班迟到怎么办?最简单的解决方案就是:早点去公司。

如果针对这个问题进行批判性思维,涉及到评估和分析。你需要寻找所有上班迟到可能的原因,然后评估和分析出每个原因的解决方案,每个解决方案可能会导致新的问题,需要接着进行综合的分析,最后再总结出能得到一个积极结果的方案。

作为一名Web开发者,你有时候会面临一个问题的多个解决途径,都能实现相同效果,批判性思维可以让你快速、有效地分析出最优的方法。

9. 自负管理

自负会对你的职业发展带来不可预知的积极或消极的影响。作为一名Web开发者,不管你写的代码多么出色,你也不是神、明星,充其量只能算是一个D-list 上的一员。如果你不知道如何管理自负,不妨看下以下几个方法:

  • 向他人学习。总有值得你学习人,并积极接受别人的建议。
  • 愿意分享和教别人。不要以居高临下的姿态,学会谦和地分享自己的心得和体会。
  • 提供具有建设性的批评意见而非单纯的批评。最好别说:你的代码很吓人、有点烂,而应该说:如果你这样做,你的代码会更好。建设性的批评目的是帮助别人,而不是体现出你多么的伟大。

你在团队里是为了和他人合作并一同进步,而不鼓励孤立自己或试图掌控团队。
原文地址: http://www.php100.com/html/it/focus/2014/0402/6740.html