原文译文操作

Slack uses PHP for most of its server-side application logic, which is an unusual choice these days. Why did we choose to build a new project in this language? Should you?

Most programmers who have only casually used PHP know two things about it: that it is a bad language, which they would never use if given the choice; and that some of the most extraordinarily successful projects in history use it. This is not quite a contradiction, but it should make us curious. Did Facebook, Wikipedia, Wordpress, Etsy, Baidu, Box, and more recently Slack all succeed in spite of using PHP? Would they all have been better off expressing their application in Ruby? Erlang? Haskell?

Slack在多数服务器端应用程序逻辑中使用PHP,这在当时是一个不寻常的选择。为什么我们选择这种语言来创建新项目呢?你应该吗? 

多数程序员都只是随意的使用PHP,只知道关于它的两件事:这是一种糟糕的语言,如果给他们选择的话,他们将永远不会使用它;历史上一些非常卓越的成功项目中使用它。这不是一个矛盾,但是却让我们好奇。Facebook、维基百科、Wordpress、Etsy、百度、盒子和最近的Slack这些成功的网站难道不是因为使用PHP吗?它们会更好的表达它们的应用程序在Ruby中吗?Erlang?Haskell?

纠正翻译

Perhaps not. PHP-the-language has many flaws, which undoubtedly have slowed these efforts down, but PHP-the-environment has virtues which more than compensate for those flaws. And the options for improving on PHP’s language-level flaws are pretty impressive. On the balance, PHP provides better support for building, changing, and operating a successful project than competing environments. I would start a new project in PHP today, with a reservation or two, but zero apologies.

Background

Uniquely among modern languages, PHP was born in a web server. Its strengths are tightly coupled to the context of request-oriented, server-side execution.

也许情况不是这样。因为PHP这门编程语言本身有着非常多的缺陷,这毫无疑问会把它这些成就降低许多,但PHP的编程环境有着可以抵消这些缺陷的有点。所以,现今人们对于如何提高PHP的编程语言水平保有非常大的热情。与PHP本身的缺点相反的是,PHP这门语言比其他竞争的变成语言为开发者提供了更多关于新建,改变以及创建一个成功的项目的支持。综合看来,我会毫不犹豫保留一两个项目用PHP语言来进行开发。

背景

与当前流行的编程语言不同,PHP产生于web服务器。PHP的优势大部分在于结果导向和服务端执行的编程环境中。

纠正翻译

PHP stands for “Personal Home Page.” It was first released in 1995 by Rasmus Lerdorf, with an aim of supporting small, simple dynamic web applications, like the guestbooks and hit counters that were popular in the web’s early days.

From PHP’s inception, it has been used for far more complicated projects than its creators anticipated. It has been through several major revisions, each of which brought new mechanisms for wrangling these more complex applications. Today, in 2016, it is a feature-rich member of the Mixed-Paradigm Developer Productivity Language (MPDPL) family[1], which includes JavaScript, Python, Ruby, and Lua. If you last touched PHP in the early ‘aughts, a contemporary PHP codebase might surprise you with traits,closures, and generators.

       PHP的含义是“个人主页”。PHP在1995年由Rasms Lerdorf发布初始版本,着眼于支持小型,简单的动态网络应用,例如在网络早期受欢迎的留言板和计数器。

      从PHP发布以来,它一直被用在许许多多复杂的项目之中,这大大超出了PHP创建者们的预期。在此过程中,PHP完成了几个重大的版本更替,每一次的版本更替都给这些更复杂的应用程序带来新的机制。在2016年的今天,PHP凭借着自身丰富的功能,已经是混合范式开发生产语言(MPDPL)这个大家庭的一员,JavaScript,Python,Ruby和Lua也在这个大家庭其中。如果你仅仅只接触过早期版本的PHP,那么最新版本的PHP代码库将会用它保有的特性,包容性以及可造性给你一个大大的惊喜。

 

纠正翻译

Virtues of PHP

PHP gets several things very deeply, and uniquely, right.

First, state. Every web request starts from a completely blank slate. Its namespace and globals are uninitialized, except for the standard globals, functions and classes that provide primitive functionality and life support. By starting each request from a known state, we get a kind of organic fault isolation; if request t encounters a software defect and fails, this bug does not directly interfere with the execution of subsequent request t+1. State does reside in places other than the program heap, of course, and it is possible to statefully mess up a database, or memcache, or the filesystem. But PHP shares that weakness with all conceivable environments that allow persistence. Isolating request heaps from one another reduces the cost of most program defects.

PHP的优点

PHP对有些事情的处理非常深刻,独到,准确。

第一,状态。每一个网页请求都是从一个完完全全的白板开始。除了提供原始功能和生命支持的标准的全局变量,函数和类以外,它的命名空间和全局变量都是未初始化的。通过从已知状态开始每一个请求,我们可以得到一种本质上的故障隔离;如果请求t 遇到了软件的缺陷和失败,这个缺陷不会直接干扰后续的请求t+1。状态驻留在程序堆以为的其他地方,当然它有可能有状态地弄糟数据库,或者缓存,或者文件信息系统。但是PHP和所有允许存在的可能环境分担了它的弱点。隔离请求堆从另一个方面降低了大多数程序缺陷的成本。

纠正翻译

Second, concurrency. An individual web request runs in a single PHP thread. This seems at first like a silly limitation. But since your program executes in the context of a web server, we have a natural source of concurrency available: web requests. Asynchronously curl’ing to localhost (or even another web server) provides a shared-nothing, copy-in/copy-out way of exploiting parallelism. In practice, this is safer and more resilient to error than the locks-and-shared-state approach that most other general-purpose languages provide.

Finally, the fact that PHP programs operate at a request level means thatprogrammer workflow is fast and efficient, and stays fast as the application changes. Many developer productivity languages claim this, but if they do not reset state for each request, and the main event loop shares program-level state with requests, they almost invariably have some startup time. For a typical Python application server, e.g., the debugging cycle will look something like “think; edit; restart the server; send some test requests.” Even if “restart the server” only takes a few seconds of wall-clock time, that takes a big cut of the 15–30 seconds our finite human brains have to hold the most delicate state in place.

第二,并发。一个独立的网络请求运行在一个单独的PHP线程上。乍看,这似乎是一个愚蠢的限制。但是一旦你的程序执行在了一个网络服务器的上下文中以后,我们就有了一个可用的自然并发:网络请求。异步地CURL到本地服务(甚至是网络服务)提供了一个开发并行性的无共享,拷入/拷出的方式。在实践中,这对错误来说比大多数其他通用语言提供的锁共享状态方法要更安全,更具有弹性。

最后一个,事实上PHP程序在一个请求级别操作意味着程序员的工作流程是快速而有效的,并保持随着应用的变化而快速变化。许多开发者使用的语言声称是这样,但是如果它们没有为每一个请求重置状态,主事件循环将和请求共享程序级状态,它们几乎总是需要一些启动时间。例如,对一个典型的Python 应用服务,调试周期看起来像这样“想;编辑;重启服务;发送一些测试请求”。即使“重启服务”只花了几秒,但这也会让我们人类有限的大脑为了保持到微妙状态浪费15到30秒的时间。

纠正翻译

I claim that PHP’s simpler “think; edit; reload the page” cycle makes developers more productive. Over the course of a long and complex software project’s life cycle, these productivity gains compound.

The Case Against PHP

If all of the above is true, why all the hate? When you boil the colorful hyperbole away, the most common complaints about PHP cluster around these root causes:

  1. Surprise type conversions. Almost all languages these days let programmers compare, e.g., integers and floats with the >= operator; heck, even C allows this. It’s perfectly clear what is intended. It’s less clear what comparing a string and an integer with == is supposed to mean, and different languages have made different choices. PHP’s choices in this department are especially perverse, leading to surprises and undetected errors. For instance, 123 == “123foo” evaluates to true (see what it’s doing there?), but 0123 == “0123foo” is false (hmm).
  2. Inconsistency around reference, value semantics. PHP 3 had a clear semantic that assignment, argument passing, and return are all by value, creating a logical copy of the data in question. The programmer can opt into reference semantics with a & annotation[2]. This clashed with the introduction of object-oriented programming facilities in PHP 4 and 5, though. Much of PHP’s OO notation is borrowed from Java, and Java has the semantic that objects are treated by reference, while primitive types are treated by value. So the current state of PHP’s semantics is that objects are passed by reference (choosing Java over, say, C++), primitive types are passed by value (where Java, C++, and PHP agree), but the older reference semantics and & notation persist, sometimes interacting with the new world in weird ways.
  3. Failure-oblivious philosophy. PHP tries very, very hard to keep the request running, even if it has done something deeply strange. For instance, division by zero does not throw an exception, or return INF, or fatally terminate the request. By default, it warns and evaluates to the value false. Since false is silently treated as 0 in numeric contexts, many applications are deployed and run with undiagnosed divisions by zero. This particular issue is changed in PHP 7, but the design impulse to keep plowing ahead, past when it could possibly make sense, pervades libraries too.
  4. Inconsistencies in the standard library. When PHP was young, its audience was most familiar with C, and many APIs used the C standard library’s design language: six-character lower case names, success and failure returned in an integer return value with “real” values returned in a callee-supplied “out” param, etc. As PHP matured, the C style of namespacing by prefixing with _ became more pervasive: mysql_…, json_…, etc. And more recently, the Java style of camelCase methods on CamelCase classes has become the most common way of introducing new functionality. So sometimes we see code snippets that interleave expressions like new DirectoryIterator($path) with if (!($f = fopen($p, ‘w+’)) … in a jarring way.

我敢说,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允许),但是旧的引用语义和&表示法依然保留,有时候与新的方式对接会出现未知的现象。

3.Failure-oblivious哲学。 PHP进行了非常多的尝试来满足运行需求,它甚至做了一些很奇特的事情。例如,除以0不会抛出异常,或返回INF值,或终止请求。默认情况下。它警告并赋值为false。由于false在数值上默认值0,所以许多应用程序在部署和运行中没有检测到除以0的错误。这个特殊的问题在PHP 7中改变了,但是设计的动力是不断的发展,在过去它可能有意义的,也遍及很多的库中。

4.标准库中不一致。在PHP发展初期,其受众者大都熟悉C语言,许多API使用C标准库的设计语言:六个字符的小写名称,成功和失败返回一个整数返回值,返回值为“实数“,返回值由调用者out参数返回等等。随着PHP成熟,通过前缀_命名空间的C风格变得更加普遍:mysql_ ...,json_ ...等等。最近,Java风格的CamelCase类的camelCase方法的已经成为最多的常见的引入新函数的方式。因此,有时我们看到的代码片断交错表达式像新的DirectoryIterator($路径)与if(!($ f = fopen($ p,'w +'))...一个全新的方式。

纠正翻译

Lest I seem like an unreflective PHP apologist: these are all serious problems that make defects more likely. And they’re unforced errors. There’s no inherent trade-off between the Good Parts of PHP and these problems. It should be possible to build a PHP that limits these downsides while preserving the good parts.

HHVM and Hack

That successor system to PHP is called Hack[3].

Hack is what programming language people call a ‘gradual typing system’ for PHP. The ‘typing system’ means that it allows the programmer to express automatically verifiable invariants about the data that flows through code: this function takes a string and an integer and returns a list of Fribbles, just like in Java or C++ or Haskell or whatever statically typed language you favor. The ‘gradual’ part means that some parts of your codebase can be statically typed, while other parts are still in rough-and-tumble, dynamic PHP. The ability to mix them enables gradual migration of big codebases.

免得我看起来像一个草率的PHP辩护者:这些所有的严重问题使得缺陷更加明显。而且他们都是自然出现的错误。 这里没有很好的权衡PHP的优点和这些缺点。应该构建一个这样的PHP,限制这些缺点的同时保留好的部分。

HHVM 与 Hack

PHP的后继系统称为Hack[3]

Hack是人们称之为PHP的“渐增类型系统”的编程语言。 '类型系统'意味着它允许程序员通过代码编写自动验证不变的数据类型的传递:这个函数接受一个字符串和一个整数,并返回一个Fribbles列表,就像在Java或C ++或Haskell或任何你喜欢的静态类型语言。 '渐进'部分意味着你的代码库的一些部分可以静态类型化,而其他部分仍然是多样的动态的PHP。它们的混合能力使得大代码库的逐步迁移成为可能。
 

纠正翻译

Rather than spill a ton of ink here describing Hack’s type system and how it works, just go play with it. I’ll be here when you get back.

It’s a neat system, and quite ambitious in what it allows you to express. Having the option of gradually migrating a project to Hack, in case it grows larger than you first expected, is a unique advantage of the PHP ecosystem. Hack’s type checking preserves the ‘think; edit; reload the page’ workflow, because the type checker runs in the background, incrementally updating its model of the codebase when it sees modifications to the filesystem. The Hack project provides integrations with all the popular editors and IDEs so that the feedback about type errors comes as soon as you’re done typing, just like in the web demo.

不是在这里描述Hack的类型系统和它的工作原理, just go play with it.。当你回来的时候我会在这里。

这是一个整洁的系统,并且你可以认为它相当雄心勃勃。可以选择逐步将项目迁移到Hack,以防它比您预期的更好,这是PHP生态系统的独特优势。 Hack的类型检查保留了'think;编辑;重新加载页面“工作流,因为类型检查器在后台运行,在监测到到对文件系统的修改时持续更新其代码库的模型。 Hack项目提供了与所有流行的编辑器和IDE的集成,以便在输入完成后就能立即获得有关类型错误的反馈,就像在Web演示中一样。

纠正翻译

Let’s evaluate the set of real risks that PHP poses in light of Hack:

  1. Surprise type conversions become errors in Hack files. The entire class of problems boils away.
  2. Reference and value semantics are cleaned up by simply banning old-style references in Hack, since they’re unnecessary in new codebases. This leaves behind the same objects-by-reference-and-everything-else-by-value semantics as Java or C#..
  3. PHP’s failure-obliviousness is more a property of the runtime and libraries, and it is harder for a semantic checker like Hack to reach directly into these systems. However, in practice most forms of failure-obliviousness require surprise type conversions to get very far. For instance, problems that arise from propagating the ‘false’ returned from division by zero eventually cross a type-checked boundary[4], which fails on treating a boolean numerically. These boundaries are more frequent in Hack codebases. By making it easier to write these types, Hack decreases the ‘skid distance’ of many buggy executions in practice.
  4. Finally, inconsistencies in the standard library persist. The most Hack hopes to do is to make it less painful to wrap them in safer abstractions.

让我们评估PHP在light of Hack中提出的真正的风险:

  1. 强制类型转换在Hack文件中会报错。整个类的问题将会得到解决。

  2. 引用和值语义通过简单地禁止Hack中的旧式引用来清除,因为它们在新的代码库中是不必要的。这留下了相同的对象引用和所有的else-by-value语义如同Java或C#。

  3. .PHP的failure-obliviousness 更多的是运行时和库的一种属性,并且很难像Hack这样的语义检查器可以直接到达这些系统里。然而,在实践中,许多failure-obliviousness 形式需要未知的类型转换传递到很远。例如,由除以零返回的'false'的问题产生的错误,最后要通过类型检查边界[4],但它将布尔值像数值处理是失败的。这些边界在Hack代码库中更频繁。使编写这些类型变得更为容易,Hack在实践中减少了许多错误执行的“滑动距离”。

  4. 最后,标准库中的不一致仍然存在。Hack最希望做的是使它不那么复杂,把它们包装在更安全的抽象化中。

 

纠正翻译

Hack provides an option that no other popular member of the MPDPL family has: the ability to introduce a type system after initial development, and only in the parts of the system where the value exceeds the cost.

HHVM

Hack was originally developed as part of the HipHop Virtual Machine, or HHVM, an open source JIT environment for PHP. HHVM provides another important option for the successful project: the ability to run your site faster and more economically. Facebook reports an 11.6x improvement in CPU efficiency over the PHP interpreter, and Wikipedia reports a 6x improvement.

Hack提供了一个其他流行的MPDPL成员没有的选项:即在初始开发后引入类型系统的能力,并且它仅限于价值超过成本的系统。

HHVM

Hack 一开始是作为 HipHop 虚拟机(HipHop Virtual Machine) 的一部分而开发的,这个也被称作 HHVM, 它是一个开放源代码的用于PHP的JIT环境。HHVM 为成功的项目提供了另外一个重要的选择,那就是能让你的网站更快且更加经济的运行。Facebook的报告提到PHP解释器的CPU执行效率有11.6倍的提升,而Wikipedia则报告说有6倍的提升。

纠正翻译

Slack recently migrated its web environments into HHVM, and experienced significant drops in latency for all endpoints, but we lack an apples-to-apples measurement of CPU efficiency at this writing. We’re also in the process of moving portions of our codebase into Hack, and will report our experience here.

Looking Ahead

We started with the apparent paradox that PHP is a really bad language that is used in a lot of successful projects. We find that its reputation as a poor language is, in isolation, pretty well deserved. The success of projects using it has more to do with properties of the PHP environment, and the high-cadence workflow it enables, than with PHP the language. And the advantages of that environment (reduced cost of bugs through fault isolation; safe concurrency; and high developer throughput) are more valuable than the problems that the language’s flaws create.

据说最近 Slack 将它的环境迁移到了HHVM ,所有端点的延迟程度都有显著的下降, 但在写这篇文章的时候,我们对此还缺乏对CPU效率一对一的衡量数据。目前我们也正将代码库的一部分挪到 Hack 中去,这一过程中的相关经验将会在这里向大家实时报告。

展望未来

一开始我们提到了PHP是一门被许多成功项目应用到的非常糟糕的语言,这个悖论,而我们发现其糟糕的名声的确实至名归。使用了PHP的这些项目的成功,比起PHP这门语言而言,更多要归功于 PHP 环境的特性,以及其带来的高节奏的工作流。而环境的优势(通过故障隔离降低了BUG所带来的成本,安全的并发以及较高的开发人员工作效率)所带来的价值要比由语言缺陷所造成的问题更加重要。

纠正翻译

Also, uniquely among the MPDPLs, there is a clear migration path to a higher performance, safer and more maintainable medium in the form of Hack and HHVM. Slack is in the later stages of a transition to HHVM, and the early stages of a transition to Hack, and we are optimistic that they will let us produce better software, faster.

Notes

  1. I made up the term ‘MPDPL.’ While there is little direct genetic relationship among them, these languages have influenced one another heavily. Looking past syntax, they are much more similar than different. In a universe of programming languages that includes MIPS assembly, Haskell, C++, Forth, and Erlang it is hard to deny that the MPDPLs form a tight cluster in language design space. 
  2. Unfortunately the & was marked in the callee, not the caller. So the programmer declares a desire to receive params by reference, but actually passing them by reference is unmarked. This makes it hard to understand what might change when reading code, and complicates an efficient implementation of PHP significantly. See Figure 2 inhttp://dl.acm.org/citation.cfm?id=2660199 
  3. Yes, Hack is a nearly unGoogleable programming language name. ‘Hacklang’ is sometimes used when ambiguity is possible. If Google themselves can name a popular language the still-more-unGoogleableGo, why not? 
  4. The typechecks in a Hack program are also enforced at runtime by default, because they piggy-back on PHP’s “type hint” facility. This increases safety in mixed codebases where Hack and classic PHP are co-mingled. 

还有一点在 MPDPL 当中比较独特的就是, 以 Hack 和 HHVM 的形式向更高的性能,更安全和更高的可维护性这些方面的迁移,途径是明确的。Slack 目前正处在向 HHVM 过渡的后期阶段,以及向 Hack 过渡的早期阶段,而我们也乐观地认为它们将会让我们的更加快速的产出更好的软件。

注意

  1. 我构造了“MPDPL”这个次。虽然这些语言之间并没有直接的遗传关系,但彼此之间的影响还是很大的。浏览其语法会发现它们大同小异。在包括 MIPS 汇编, Haskell, C++, Forth, 以及 Erlang 这些语言的领域中,很难否认 MPDPL 在语言设计空间中是一个紧密的集合。

  2. 不幸的是 & 被标记在了被调用的一方,而不是发起调用的那一方。因此程序员们都表示希望能通过引用接收参数,但实际上通过引用来传递参数并没有得到重视。这使得在阅读代码时很难去理解什么可能会发生改变,并显得 PHP 的高效实现更加复杂化。见 http://dl.acm.org/citation.cfm?id=2660199 中的图2。 

  3. 是的, Hack 是一个几乎不那么 Google 的编程语言命名。在几乎会让人迷惑的场合,“Hacklang”有时也会被用到。如果 Google 自己就能够用一个仍旧更加不那么 Google 的名字 Go 来命名一种流行的编程语言,为什么不呢? 

  4. Hack 程序中的类型检查在运行时也是默认开启的, 因为它们捎带提供了 PHP 的“类型提示”工具。这个在Hack和经典的PHP代码混合在一起的混合代码库中能提高安全性。

纠正翻译