slack(一款著名国外团队协作saas软件) 把 php 用在了其大多数服务端应用程序逻辑上,这个在如今算是一个不寻常的选择了。为什么我们要选择用这门语言来构建一个新的项目呢?你会这样做吗?
大多数只是偶尔使用过php的程序都知道有关于它的两件事情: 这是一门糟糕的语言, 如果提供了其它选择,他们永远都不会使用它;还有就是历史上的一些特别成功的项目用到了它。这两种认知并不矛盾,不过这应该会让我们感到好奇。尽管使用了 php,facebook, wikipedia, wordpress, etsy, 百度, box, 还有最近的 slack 不都成功了吗? 如果用 ruby、erlang、 haskell这些语言来表达他们的应用程序,会不会更好呢?
也许不会。php这门语言有许多的缺陷,这无疑会拖我们的后腿,但 php 这个环境的好处足以补偿这些缺陷。还有针对 php 语言级别缺陷的提升选项都相当令人印象深刻。总而言之,相较于其它竞争对手而言,php针对构建,变更以及操作一个成功的项目提供了更好的支持。如今我会毫无保留意见的支持使用 php 来开始一个项目,无怨无悔。
背景
按照现代语言的说法,php诞生于一个web服务。 它的优势与面向请求的服务器端执行的上下文紧密耦合。
php代表“个人主页”。它是由rasmus lerdorf于1995年首次发布的,旨在支持小型,简单的动态web应用程序,例如在网络早期流行的访客簿和热门计数器。
php已经开始应用于比它的创造者预期得更复杂的项目中。 它通过了几次重大修订,每次都有新的机制用于适用一些更复杂的应用程序。 到2016年,它已经成为了功能丰富的综合开发语言(mpdpl)系列[1]的一员,该系列还包括javascript,python,ruby和lua。 如果你最后一次接触php还是早期的aughts,那么现在的php代码库的特性,闭包,生成器可能会让你大吃一惊。
php的优点
php对有些事情的处理非常深刻,独到,准确。
第一,状态。每一个网页请求都是从一个完完全全的白板开始。除了提供原始功能和生命支持的标准的全局变量,函数和类以外,它的命名空间和全局变量都是未初始化的。通过从已知状态开始每一个请求,我们可以得到一种本质上的故障隔离;如果请求t 遇到了软件的缺陷和失败,这个缺陷不会直接干扰后续的请求t+1。状态驻留在程序堆以为的其他地方,当然它有可能有状态地弄糟数据库,或者缓存,或者文件信息系统。但是php和所有允许存在的可能环境分担了它的弱点。隔离请求堆从另一个方面降低了大多数程序缺陷的成本。
第二,并发。一个独立的网络请求运行在一个单独的php线程上。乍看,这似乎是一个愚蠢的限制。但是一旦你的程序执行在了一个网络服务器的上下文中以后,我们就有了一个可用的自然并发:网络请求。异步地curl到本地服务(甚至是网络服务)提供了一个开发并行性的无共享,拷入/拷出的方式。在实践中,这对错误来说比大多数其他通用语言提供的锁共享状态方法要更安全,更具有弹性。
最后一个,事实上php程序在一个请求级别操作意味着程序员的工作流程是快速而有效的,并保持随着应用的变化而快速变化。许多开发者使用的语言声称是这样,但是如果它们没有为每一个请求重置状态,主事件循环将和请求共享程序级状态,它们几乎总是需要一些启动时间。例如,对一个典型的python应用服务,调试周期看起来像这样“想;编辑;重启服务;发送一些测试请求”。即使“重启服务”只花了几秒,但这也会让我们人类有限的大脑为了保持到微妙状态浪费15到30秒的时间。
我敢说,php简单的“想,编辑,重新加载页面”周期让开发者更有效率。在一个漫长而复杂的软件项目的生命周期过程中,这提高了生产力。
php的缺点
如果上面所说的全都是对的,那为什么还有那么多人不喜欢php呢?当你抛开各种各样夸张的说法,对于php的抱怨,最多聚集在几个最基本的原因:
1.未知类型转换。程序员这些天几乎在比较所有的语言,例如整数和浮点数与> =运算符; heck,甚至c都是允许。其意图是完全清楚的。不太清楚的是使用==比较字符串和整数意味着什么,不同的语言做出了不同的选择。 php在这个部分的选择是特别不恰当的,这导致了异常和未检测到的错误。例如,123 ==“123foo”求值为true(看看它在这做什么?),但0123 ==“0123foo”是false(hmm)。
2.值语义,引用的不一致。php 3有一个明确的语义--赋值,参数传递和返回都是通过值来传递,创建一个问题的数据的浅拷贝。程序员可以选择参考语义与注释[2]。这将与php 4和5中引入了面向对象的编程程序冲突。 php的面向对象符号大部分是从java借鉴的,java具有通过引用处理对象的语义,而基本类型由值来处理。所以php的语义的当前状态是对象通过引用传递(选择java over,比如说c ++),基本类型通过值传递(其中java,c ++和php允许),但是旧的引用语义和&表示法依然保留,有时候与新的方式对接会出现未知的现象。
failure-oblivious哲学。 php进行了非常多的尝试来满足运行需求,它甚至做了一些很奇特的事情。例如,除以0不会抛出异常,或返回inf值,或终止请求。默认情况下。它警告并赋值为false。由于false在数值上默认值0,所以许多应用程序在部署和运行中没有检测到除以0的错误。这个特殊的问题在php 7中改变了,但是设计的动力是不断的发展,在过去它可能有意义的,也遍及很多的库中。
标准库中不一致。在php发展初期,其受众者大都熟悉c语言,许多api使用c标准库的设计语言:六个字符的小写名称,成功和失败返回一个整数返回值,返回值为“实数“,返回值由调用者out参数返回等等。随着php成熟,通过前缀_命名空间的c风格变得更加普遍:mysql_ ...,json_ ...等等。最近,java风格的camelcase类的camelcase方法的已经成为最多的常见的引入新函数的方式。因此,有时我们看到的代码片断交错表达式像新的directoryiterator($路径)与if(!($ f = fopen($ p,'w +'))...一个全新的方式。
免得我看起来像一个草率的php辩护者:这些所有的严重问题使得缺陷更加明显。而且他们都是自然出现的错误。 这里没有很好的权衡php的优点和这些缺点。应该构建一个这样的php,限制这些缺点的同时保留好的部分。
hhvm 与hack
php的后继系统称为hack[3]。
hack是人们称之为php的“渐进式输入系统”的编程语言。 '输入系统'意味着它允许程序员通过代码编写自动验证不变的数据类型的传递:这个函数接受一个字符串和一个整数,并返回一个fribbles列表,就像在java或c ++或haskell或任何你喜欢的静态类型语言。 '渐进'部分意味着你的代码库的一些部分可以静态类型化,而其他部分仍然是多样的动态的php。它们的混合能力使得大代码库的逐步迁移成为可能。
不是在这里描述hack的类型系统和它的工作原理,just go play with it.。当你回来的时候我会在这里。
这是一个整洁的系统,并且你可以认为它相当雄心勃勃。可以选择逐步将项目迁移到hack,以防它比您预期的更好,这是php生态系统的独特优势。 hack的类型检查保留了'think;编辑;重新加载页面“工作流,因为类型检查器在后台运行,在监测到到对文件系统的修改时持续更新其代码库的模型。 hack项目提供了与所有流行的编辑器和ide的集成,以便在输入完成后就能立即获得有关类型错误的反馈,就像在web演示中一样。
让我们评估php在light of hack中提出的真正的风险:
强制类型转换在hack文件中会报错。整个类的问题将会得到解决。
2.引用和值语义通过简单地禁止hack中的旧式引用来清除,因为它们在新的代码库中是不必要的。这留下了相同的对象引用和所有的else-by-value语义如同java或c#。
3.php的failure-obliviousness更多的是运行时和库的一种属性,并且很难像hack这样的语义检查器可以直接到达这些系统里。然而,在实践中,许多failure-obliviousness 形式需要未知的类型转换传递到很远。例如,由除以零返回的'false'的问题产生的错误,最后要通过类型检查边界[4],但它将布尔值像数值处理是失败的。这些边界在hack代码库中更频繁。使编写这些类型变得更为容易,hack在实践中减少了许多错误执行的“滑动距离”。
4.最后,标准库中的不一致仍然存在。hack最希望做的是使它不那么复杂,把它们包装在更安全的抽象化中。
hack提供了一个其他流行的mpdpl家族成员没有的选项:在初始开发后引入类型系统的能力,并且只有在那些的价值超过成本的部分系统里。
hhvm
hack 一开始是作为hiphop 虚拟机(hiphop virtual machine)的一部分而开发的,这个也被称作 hhvm, 它是一个开放源代码的用于php的jit环境。hhvm 为成功的项目提供了另外一个重要的选择,那就是能让你的网站更快且更加经济的运行。facebook的报告提到php解释器的cpu执行效率有11.6倍的提升,而wikipedia则报告说有6倍的提升。
据说最近 slack将它的环境迁移到了,所有端点的延迟程度都有显著的下降, 但在写作本文时我们对此还缺乏对cpu效率一对一的衡量数据。目前我们也正将代码库的一部分挪到 hack 中去,这一过程中的相关经验将会在这里向大家实时报告。
展望未来
一开始我们提到了php是一门被许多成功项目应用到的非常糟糕的语言,这个悖论,而我们发现其糟糕的名声的确实至名归。使用了php的这些项目的成功,比起php这门语言而言,更多要归功于 php 环境的特性,以及其带来的高节奏的工作流。而环境的优势(通过故障隔离降低了bug所带来的成本,安全的并发以及较高的开发人员工作效率)所带来的价值要比由语言缺陷所造成的问题更加重要。
还有一点在 mpdpl 当中比较独特的就是, 以 hack 和 hhvm 的形式向更高的性能,更安全和更高的可维护性这些方面的迁移,途径是明确的。slack 目前正处在向 hhvm 过渡的后期阶段,以及向 hack 过渡的早期阶段,而我们也乐观的认为它们将会让我们的更加快速的产出更好的软件。
注意
我构造了“mpdpl”这个次。虽然这些语言之间并没有直接的遗传关系,但彼此之间的影响还是很大的。浏览其语法会发现它们大同小异。在包括mips 汇编, haskell, c++, forth, 以及 erlang 这些语言的领域中,很难否认 mpdpl 在语言设计空间中是一个紧密的集合。不幸的是 & 被标记在了被调用的一方,而不是发起调用的那一方。因此程序员们都表示希望能通过引用接收参数,但实际上通过引用来传递参数并没有得到重视。这使得在阅读代码时很难去理解什么可能会发生改变,并显得 php 的高效实现更加复杂化。见http://dl.acm.org/citation.cfm?id=2660199中的图2。是的, hack 是一个几乎不那么 google 的编程语言命名。在几乎会让人迷惑的场合,“hacklang”有时也会被用到。如果google 自己就能够用一个仍旧更加不那么 google 的名字go来命名一种流行的编程语言,为什么不呢?hack 程序中的类型检查在运行时也是默认开启的, 因为它们捎带提供了 php 的“类型提示”工具。这个在hack和经典的php代码混合在一起的混合代码库中能提高安全性。