PHP 和 MySQL 专家级编程(一)

原文:Expert PHP and MySQL

协议:CC BY-NC-SA 4.0

零、介绍

这是从精通 PHP 编程到能够开发商业应用的一大步。正如弗雷德·布鲁克斯(Fred Brooks)在他的经典著作《神话中的人月》(Addison-Wesley,1995 年)中估计的那样,“一个编程产品的成本至少是具有相同功能的调试程序的三倍。”

我写这本书的目的是帮助你迈出这一大步。

除了 PHP 编程,你还必须知道什么?嗯,有项目组织(包括人员配备和时间安排),让客户满意,确定需求(敏捷但不马虎),选择开发和生产平台,设计数据库,构建应用以处理表单和按钮,处理安全性和错误处理,以及将数据从旧系统转换到新系统。这也是本书的顶级主题列表。

在任何一家大型书店里随便找一本 PHP/MySQL 书籍,你都会找到关于安装 PHP、变量、语句、函数、字符串处理、数组、对象、文件处理、调试的章节,可能还有一个玩具电子商务网站。不是这本书!我假设你已经知道所有这些东西,或者如果你不知道的话,你可以在别的地方找到。相反,我试图涵盖我从未在任何书籍中见过的基本主题,例如在共享主机和云中虚拟机之间进行选择,更新一个动态应用,将 MySQL 约束错误转换为用户可以理解的内容,以正确的方式保护密码(哈希、加盐和拉伸),实现双因素身份验证,使您的网站免受攻击(通过 SQL 注入、跨站点脚本、跨站点请求伪造或点击劫持),实现数据库触发器验证, 开发 CSV 文件或 pdf 格式的报告,转换不同名称拼写的数据,避免法律纠纷,以及 PHP/MySQL 程序员在开发工业级应用时每天都要处理的许多事情。

除了技术细节,我还试图传递我在开发商业软件的四十年中所学到的东西。我最喜欢的一句语录(出处不详)是,“好的判断来自经验,经验来自坏的判断。”我肯定我比你表现出更多的错误判断。我的软件比任何人都有更多的错误,更多错误的平台选择,更多的架构死胡同,更多的用户界面灾难,以及更多的客户支持失败,但我愿意认为这是因为我比任何人都做得更久。(毕竟,贝比·鲁斯三振出局 1330 次,这是任何普通棒球运动员都无法企及的数字。)所以我的判断现在相当不错,你得到了好处。你可以期待犯你自己原创的、创造性的错误——没有必要重复我的错误。我希望你也能像我一样打出很多本垒打。(比喻性的。)

当我知道答案时,我也试图给出简单的答案,以及为什么是这个答案的原因,而不是列出利弊,告诉你做出适合你情况的最佳选择。那会节省你的时间。使用 PDO 作为你的 PHP-MySQL 接口,FPDF 作为你的 PDF 库,MySQL Workbench 作为你的数据库设计工具,jQuery 作为你的 JavaScript 库,Phpass 作为密码散列,以及我的 17 节需求大纲。当然,你不必按照我的方式去做,但是在开发一个应用的过程中,你必须做出数百个设计选择,而且你也没有能力把每一个都变成一个研究项目。被告知最佳路线难道不是一种解脱吗?

整本书都有代码示例,都可以从www.apress.com下载。主要技术体现在 PHP 类中——Access、DbAccess、Form、Page、Report 和 Security——这些类足够健壮,可以直接集成到您自己的应用中。我以小的、有些不连贯的块来呈现代码,但是您可以通过下载源代码并跟随它阅读关于我为什么以及如何以我所做的方式做事的技术解释来避免迷失。

总共有八章,分为三组。前两章,项目组织和需求,应该一起阅读,但是如果你急于开始 PHP/MySQL 编程,可以在第一次阅读时跳过,尽管我喜欢认为我最有价值的见解都在那里。(你会喜欢我的战争故事。)中间的四章,平台和工具,数据库,应用结构,以及安全,表单和错误处理,构成了本书编程部分的核心,需要按顺序阅读。最后两章报告和其他输出数据转换建立在中间章节的基础上。

在这一点上,作者通常会感谢审稿人的工作,但承认任何剩余的错误都是他一个人的责任。是啊,但是出版社的员工太棒了,如果有什么东西通过了,肯定是他们的错,对吗?好吧,我是在开玩笑,只是想搞笑,很可能失败了。我希望在阅读这本书时,你会发现我其他的幽默尝试更成功。回到正题,如果你确实发现了这些错误,请给book@basepath.com发电子邮件。它们真的都是我的。还在努力提高我的判断力。

-马克罗什金

科罗拉多州博尔德

2013 年 7 月

一、项目组织

好的开始是成功的一半。

—亚里士多德

亚里士多德有些夸张,但是我相信他的观点是,没有一个正确的开端,你的软件开发努力可能会化为乌有。如果你一开始就组织好这个项目,坚持下去就会成功。在这一章中,我解释了成功的基本决定因素,如何确保它们到位,以及如何让你的项目专注于它们。然后,我略微谈到了两个实际问题:如何远离法律纠纷,以及如何获得报酬。

人决定成功

这本书的副标题里有“应用”这个词是有原因的。它是关于写程序给人们使用,这就是应用。这意味着你的应用开发的成功完全取决于人们是否满意。

没错。即使你的数据库是第三范式,你的 PHP 是面向对象的,你的 HTML 使用 CSS(层叠样式表)来分离形式和功能,你使用最新的敏捷流程,你已经找出了所有的错误,如果你为之构建系统的人不满意它也没关系。反之亦然:如果他们满意,他们会称你的工作成功,即使你知道它在技术上有所欠缺。

所以,既然人决定成功,很明显有两件事你绝对需要知道:这些重要的人是谁和 ?? 如何满足他们。

这些人是谁?

人是你的应用的利益相关者:雇佣你的人、直接用户、报告的接收者、期望节约成本的首席财务官、期望提高效率的首席执行官、运行与你联系的系统的 IT(信息技术)人员,以及任何与项目成功有利害关系的人。对用户的看法过于狭隘是错误的,这可能是受到今天强调“可用性”或“用户友好性”的鼓励这些都很重要,但是您需要满足的许多成分永远不会直接使用应用,甚至可能永远不会看到它运行。

例如,当我开始为 Richardson(德克萨斯州)学区开发一个学生信息系统时,我第一次见到了 IT 主管,他为我描绘了这个项目的草图。第二天,我见到了他的三名直属员工,他们从新学年开始,也就是他们第一次开始使用新系统的时候,就一直在和一家外部供应商打交道。几个月后,在一次包括小学助理督学在内的会议上,结果是他们想从我这里得到的是一个简单的系统,只用于小学成绩单,当然,我说我可以为他们制作。我们称之为 Rgrade (R 代表理查森)。当我开始设计这个新应用时,我遇到了更多的 IT 人员、一些教师、两个在城市另一边的另一栋大楼里运行服务器的人、另一个负责评估的助理主管(这在德克萨斯州是一件大事),以及其他几个我一直不太清楚姓名和头衔的人。

为了使我的项目成功,必须满足列表中的哪些人,按优先顺序排列?你需要分清主次,因为,当然,你不可能让每个人都满意,至少不可能完全满意。谁必须首先满足,其次,第三,等等?

嗯,第一条规则是,你最需要的是让雇佣你并支付你薪水的人满意。(几年前在拉丁语课上,我了解到罗马士兵总是由他们的将军直接支付军饷,而不是由政客控制的政府支付,所以将军可以确信他们会忠于他。)

但是,什么能让 IT 主管满意呢?他对该系统的任何功能、使用起来有多简单、运行成本(在合理范围内)或其他任何技术问题都不屑一顾。我不知道他是否在乎孩子们是否被评分。我从与小学助理督学的会面中了解到,老师们对现有的供应商体系感到不满,她想要和平。IT 主管希望与她和平相处,因为是他将他们如此不喜欢的现有系统付诸实施。

因此,下一个需要满足的群体将是使用该系统的教师。我的清单到此为止;没有第三或第四优先。只要我不打扰他们太多,服务器人员并不重要。IT 人员无关紧要——如果他们再也没有听说过初级成绩单,他们会很高兴。只要成绩单生成了,评估人员就很高兴。

这个故事的重点不是提出一个如何优先考虑满意度的公式,或者解释德克萨斯州的学区是如何工作的(无论如何可能都是不可能的)。关键是,对于任何项目,你必须拿出所有人的完整名单,并了解他们每个人的需求,他们的需求如何联系(如果助理主管高兴,IT 主管就高兴,如果教师高兴,她也高兴),以及如何最大限度地提高满意度。

另一个例子:我是一家公司的工程副总裁,该公司开发了一个名为 SuperSked 的系统,用于优化超市收银员的时间表,该系统被出售给大型食品连锁店,如 Safeway 和 Kroger。这是一个基本上现成的产品,而不是像 Rgrade 那样的定制工作。我们所有的客户互动都是与连锁店总部的运营部门进行的。该系统由商店经理的员工使用,但我们从未见过任何直接用户。众所周知,杂货店的利润率很低,因此 SuperSked 节省的劳动力非常可观。这是所有行动关心的。当然,系统必须是可用的,但是易用性并不重要。如果它能省钱,商店就会使用它,即使这意味着用鼻子尖输入数据。我们必须满足谁?运营部门。

每个案例都会不一样,所以你要深挖。不要猜测。

怎么满足?

然而,仅仅知道谁让 ?? 满意还不够。你需要知道如何满足他们。计算机人士称之为“如何”满足要求。很简单:如果需求是正确的,并且你满足了它们,你需要满足的人就会得到满足,这个系统就成功了。如果需求是错误的,你将满足错误的人或者没有人,无论哪种方式你都失败了。

我将在第二章中更多地讨论需求,所以我在这里只从高层次上讨论它们。对于 Rgrade,它们很容易表达:老师们希望有一个单一的、易于理解的表格,他们可以将分数放入其中,从列表中选择老师的评论(该地区不允许自由格式的评论),并有英语和西班牙语的成绩单弹出。报告卡的格式已经确定,西班牙语的翻译意见也是如此。产量要求是显而易见的:与去年和前年一样的成绩单。只要它起作用,对老师来说最重要的是他们多快能拿到分数。他们通常工作到很晚,甚至在家里,花在这上面的每一分钟都是从他们更喜欢做的事情中抽出来的。可能是备课或帮助学生,也可能是看球赛和喝啤酒。但从不升级。

那么输入表单呢?应该是什么样子?我把它弄得和成绩单一样。它看起来非常像那张成绩单,老师们几乎不需要任何培训。他们认为他们是在卡片上打字,就像他们使用纸质报告时一样。所有的用户界面都实现了模型,在这种情况下,完美的模型很容易被发现。

我确实做了另外两件事,我知道这对让老师们高兴是必要的,尽管他们从来没有提到过。首先,我安排该系统由运行该学区服务器的人托管,这样它就可以一天 24 小时可用。第二,我要求大量的处理能力,以确保系统不会过载。有三个高端服务器,一个用于数据库,两个用于应用,前端有一个漂亮的负载平衡器,将 web 请求发送到负载最小的应用服务器。这被证明是矫枉过正,但是 IT 部门没有人在意。他们对幸福的定义是一堆毫无意义的麻烦报告。

这听起来像一个大获成功的情况,对不对?它?满足几百个整天和小怪物打交道的被欺骗的德州老师?与其说是扣篮,不如说是三分球!只有看起来简单,因为我涵盖了两个基本步骤:识别那些必须满足的人,然后找出如何满足他们。

的确,有些人表达了他们对我正在构建的系统的想法,但我知道其中许多想法,尤其是来自 IT 部门的想法,不会吸引老师。我是一名外部顾问,所以我听取了这些人的意见。但后来我忽略了他们。我只是为了让雇佣我的 IT 主管和老师们满意而工作。其他人都不重要。如果你试图取悦错误的人,这很容易做到,如果他们是你每天都见到的人,你就输了。

(如果你正在使用敏捷方法,你的团队中有一个客户告诉你实现了什么用户故事,如果这个人不能准确地代表需要满足的客户,你就会失败。实际的团队成员往往是 IT 部门的人或产品经理,因为你真正想要的人不在。)

对于 SuperSked 来说,怎样才能让食品公司的 it 部门满意呢?是的,超级间谍,但只是间接的。我们真正的产品是有记录的成本节约,我们有一个博士,公司创始人之一,他全职研究劳动力模型和优化成本带来的节约。我们可以证明年复一年的节约,所以客户很高兴。

所以,就像你必须识别你真正的顾客一样,你也必须识别你真正的产品。然后你的工作就是构建和交付产品,这也是本书的其余部分。

项目有三个维度

软件项目有三个维度。

  1. 需求:系统会做什么。
  2. :将构建它的开发团队
  3. 进度:需要多长时间建成。

固定其中两个维度,第三个维度必须相应地调整。如果需求和时间表已经确定,你将需要足够的程序员来完成这一切。如果需求和人都定了,也只能建这么快。如果时间表和人员都已确定,那么只能开发这么多功能。

我试图想出一个有趣的类比来说明这三个维度的不变性。我能找到的最好的是希腊神话中的三个复仇女神。(这只怪物腹背受敌的数量只有两只。)这不是一个完美的类比,但我搜索过的网站threes.com上的一句话是正确的:“没有祈祷、没有牺牲、没有眼泪能够感动他们,或者保护他们迫害的不幸对象。。."如果有人告诉你他们有办法绕过需求、人员和时间表,那他们就错了。他们无路可走。

我也见过这三个维度被称为铁三角,类似百慕大三角。重点,我猜是你的项目可以沉入铁三角。

显然,调整尺寸有其局限性。团队只能发展到可管理的规模;过了这一点,沟通和协调的困难开始降低生产力。该系统必须有一些最低限度的功能才是有用的;如果检查者的时间表不能被张贴或报告卡不能被打印,它是没有好处的。日程只能这么长;过了一个点,系统就变得无关紧要,或者成本失控。或者,在我们这个超小型公司的例子中,我们已经耗尽了风险投资。

在这些限制范围内,这三个方面必须共同构成一个成功的公式。试图建立一个过度约束的系统——要做的事情太多,没有足够的人去做,或者没有足够的时间去做——是行不通的。你不能从岩石中提取水。

要求

需求只能通过增加或减少功能来调整,而不能通过调整质量来调整。始终只有尽可能高的质量才是可接受的。缺失一个重要的功能远比让它不可靠地工作要好得多。(几年前,贝尔实验室的某个人评论说,他安装的一个新磁盘驱动器速度非常快,但容易出现数据错误。他的同事回应道:“见鬼,如果它不需要传递正确的数据,我可以做得更快!”)

将更多的人加入团队不会提高生产力,除非他们是顶尖的执行者;如果不是这样,就会降低生产率。让团队保持小规模并支付他们每个人应得的报酬要好得多。你还是会省钱的。因此,人员维度远不如其他两个维度灵活。

因为需求和人员都在那里,任何人都可以看到,所以时间表通常是伪造的。我很快会有更多的话要说。

需求是最难的部分:在项目开始时很难知道它们是什么,它们通常过于雄心勃勃,它们经常没有清晰地表达出来,并且在开发过程中会发生变化。这就是为什么他们有自己的一章。我将在本章的下两节讨论开发团队和时间表。

开发团队

20 世纪 70 年代中期的某个时候,我在贝尔实验室最喜欢的经理 Evan Ivie 参加了一个会议,会上他们讨论了如何衡量程序的质量。人们提出了各种各样的方案,例如计算缺陷的数量,分析代码中的“go-to”语句,以及检查设计文档。最后,Evan 宣布他知道一种即时、简单的方法来确定程序的质量。每个人都屏住呼吸,期待着艾维的启示。“看看是谁写的,”埃文说。

正如选择房子时要考虑的三个最重要的标准是位置、地点和地点一样,预测软件项目成功与否时要考虑的三个最重要的标准是人、人和人。

(人又。为了成功,你也必须满足他们。)

程序员的生产力——他们能以多快的速度写出多少高质量的代码——可能会因人而异,比其他任何领域都要多。一名职业网球运动员的发球速度大约是每小时 130 英里,只比我快 50%。一个专业的木匠可以在一天内搭建一套书架,而我可以在一周内搭建一个同样好的书架。但是,一个顶尖的程序员可以比一个平庸的程序员多出 50 倍,很容易,在两个小时内写出几乎完美的代码,而隔壁小隔间的小丑在两个星期内仍然在工作,即使那样也是错误的。

但不仅仅是生产力。顶级程序员可以很快产生一个简单、优雅的解决方案,但是来自一个弱程序员的解决方案无论酝酿了多长时间,都永远不会是好的。你可以通过观察代码来判断它是出自艺术家之手,还是经过几天的锤炼而形成的。在电影《阿马德乌斯》中,萨列里谈到莫扎特时说:“他只是简单地写下了脑海中已经完成的音乐。一页又一页,好像他只是在做听写。”伟大的程序员就是这样工作的。他们似乎在键盘上编程,但他们只是在打字。这个程序已经在他们的头脑中形成了。(这就是为什么我对结对编程持怀疑态度,结对编程是大多数敏捷过程的组成部分。)

我所说的这种顶级程序员非常罕见,而且你也不会找到很多愿意为你的项目工作的人。在你可以选择的人中,生产率的比率可能只有 10:1。即便如此,这在实践中意味着两个优秀程序员的团队可以胜过十个普通程序员的团队。(实际上,即使十个中包括两个好的,也是如此,因为八个落后者会破坏整个团队。)不幸的是,人们通常被分配到项目中的方式以及他们如何获得报酬的假设比例可能是 1.5:1。差了一个数量级。

排在人、人和人之后的第四重要的东西——良好的设备、开明的管理、敏捷的技术、愉快的工作条件——都不那么重要,以至于在组织一个项目时,你应该把几乎所有的时间都花在人身上。做对了,你就有可能成功;即使你不知道如何运行项目,他们也会。和错误的人在一起,你就完了。

雇佣最优秀的员工

当我第一次成为经理时,我不擅长招聘。我真的不知道该怎么做,所以我只想尽快结束这件事。这导致人们只能勉强接受还可以的人,但不是最好的。想起上世纪 80 年代末和 90 年代初我为自己的公司 XVT 软件雇佣的一些程序员,我就不寒而栗。但是几年后,我读了一本与这部分同名的书,马丁·亚特的《??:雇佣最好的 ??》,我在这方面做得更好了。

在你开始招聘之前,你需要确保最优秀的人愿意为你的团队工作。如果你的公司陷入困境,你的技术和产品陈旧,你的薪水没有竞争力,你的位置糟糕,或者项目听起来沉闷,你就不会有好的人为你工作。(也许你自己都不应该在那里。)

(如果你不是招聘经理,而是潜在雇员,这对你也很重要。看看现有员工的素质。)

该公司的麻烦和它的位置很难解决,至少不会马上解决,所以你可能不得不忍受它们。你能改变的是技术,工资,以及你如何运作这个项目。

如果出于某种原因,您的组织没有使用最新的技术——过时的编程语言或操作系统,或者糟糕的硬件——是时候升级了。你不能用老方法招募最优秀的人来工作。如果系统运行在 Windows XP 上,就把它转移到 Windows 8 上。用 HTML5、CSS3、JavaScript、Ajax 和 jQuery 编写新的网页。你可能会发现,一些高级经理对新技术望而却步,或者对那些想使用 Explorer 6 和不能运行最新操作系统的旧电脑的客户听得太多,所以你可能不得不抗争。最优秀的人不会全力以赴地研究他们认为落后于时代的技术。如果你最终和那些自己的技术技能已经过时的平庸的人在一起,你就完了。

当我在 2008 年面试一份工作时,在该公司的网页上注意到其产品需要 Windows 95、奔腾 3 和至少 500MB 的内存,我希望这只是更新网页的失败。无论如何,这是一个真正的倒胃口。

薪资也可能需要调整。如果一个程序员的工资上限是 10 万美元,你就雇不到最好的。五个价值 15 万美元的程序员比十个挣 10 万美元的程序员收入高得多。你会有一个更好的团队,也更便宜。一些公司有严格的薪酬结构,不允许程序员比高级会计或营销人员挣得更多。尽你所能改变这一切。也许你会失败,但你必须尝试。如果你不能支付最好的,也许你也不应该在那里。你也是最棒的,对吧?

BMC 软件公司的创始人约翰·穆雷斯提出了一个他称之为“产品作者”的薪酬计划该产品的主要开发者从销售中获得提成。约翰告诉我关于“双逗号俱乐部”的事情,该俱乐部的会员需要一个带两个逗号的年薪数字。他把一辆法拉利借给了他的一个双逗号程序员,让他开着,直到他自己的车来了。正如你可能猜到的,BMC 软件公司取得了巨大的成功,有一段时间,它的员工人均收入是全国同规模公司中最高的。也许你的公司不会采取像 BMC 那样的补偿方案。另一方面,也许你自己已经走得那么远了:你是一名顾问,或者像我一样,在你的网站和苹果的应用商店里销售你自己的软件。(目前为止我只差一个逗号。)

一旦你知道这份工作有吸引力,薪水范围合适,你就可以开始招聘了。关于如何做到这一点的深入讨论超出了本书的范围,无论如何,Martin Yate 的书对此做了很好的阐述。我想在他的技巧中加入一点,那就是查看候选人的投资组合,在这种情况下,这意味着他可以带进来给你看一个不平凡的项目。你可能会被告知,候选人之前的所有工作都是专有的,不能公开。也许是这样,但我对任何值得被称为“最好”的程序员持怀疑态度,他们从来没有在家里自己做过任何编程,即使只是一个带有 JavaScript 的网页。

查看候选人投资组合的一种方式是在小组会议上,但你也可以一对一地进行。你想听候选人解释这个项目,或者至少是其中的几个部分,特别要注意为什么事情是这样做的,以及可能会考虑哪些替代方案。如果你了解程序员,你就能很容易地判断出候选人是否真的在他或她的游戏中处于领先地位,或者是一个埋头苦干的人,能够把一个程序组装起来,但没有天生的艺术天赋。这就是你要找的。

我也提出类似这样的问题:要初始化应用,需要从一个参数文件中读入一个颜色名称和 RGB 值的列表,总共大约一百个,并对它们进行排序以便以后显示。应该使用冒泡排序、插入排序还是快速排序?

两种类型的答案是错误的:表明候选人对排序算法一无所知,或者坚定地说快速排序是最快的。最好的答案是这样的:*我会使用编程语言库中的任何函数,或者,如果没有函数,就使用冒泡排序,因为这是我第一次尝试最有可能成功的函数。对于如此小的列表,初始化期间的效率是无关紧要的。*套用老查理金枪鱼广告的话,你不想要排序最快的程序员,你想要排序最快的那个。

或者,这个问题:我们有一个报告生成功能,可以进行数据库查询并编写一个包含结果的多页 PDF,但是客户抱怨运行时间太长。你会怎么解决?

在这里,您希望听到包含以下内容的答案:确定多长时间是太长,重现问题以便进行实验,检测系统以便可以识别瓶颈,运行实验以查看即使在理论上是否有可能加速,然后估计修复问题的成本,以便可以决定是否继续。潜在的解决方案,比如改进查询、添加索引或者提供排队功能,这样用户就不必等待结果,也是很好的选择。你主要是想了解候选人是如何思考的。

你不想听到的是数据库需要索引,MySQL 是错误的选择,或者任何表明候选人已经得出结论的简单陈述。当然,你也不想听到候选人不知道如何继续下去。

我的观点是:少花点时间在教育成就、以前的职位和过去的项目上,多花点时间在应聘者如何对待工作上。这需要真正深入的采访,而不是午餐时的闲聊。

时间表

调度是所有管理任务中最令人害怕和不被尊重的。也是被误解了。时间表的目的不是预测未来的事件,也不是提供一种鞭策装病者的手段。计划的目的是

  • 强制仔细查看所有要求,以及
  • 迫使团队想出至少一个可能成功的场景,并且
  • 保持开发以稳定的速度运行,以及
  • 为组件开发的精细程度设定一个界限。

我可以用一个例子来解释我最后一点的意思:假设你的任务是在三天内实现一个数据导出工具。您可能希望提供一个基本的过滤功能来选择数据,一个简单的预览输出的方法,并且可能选择逗号分隔值(CSV)或制表符分隔的输出格式。现在假设你有两个月的时间去做导出模块。您希望开发一个更复杂的查询工具,一个更广泛的输出选择,可能包括 XML(可扩展标记语言)、SQL(结构化查询语言),甚至 RTF(富文本格式),以及一种将导出规范存储为预置以供以后调用的方法。

它们都是出口设施,没有本质上的对错。客户可能想要更好的,但是,如果你知道这只是三天的工作,你会告诉客户这只是一个最小的设施,他或她也会很高兴。由于这是一个外围功能,除了数据库之外,与系统的任何其他部分都没有真正的联系,所以您可以在将来的版本中用更有雄心的东西来替换它。

另一方面,如果时间表要求在三天内完成转换,就需要有人站出来说话。对于大多数现实世界的系统来说,这是远远不够的。如果你只有三天时间,那么成功的可能性就不存在。

如果不遵守时间表,那么制定时间表又有什么意义呢?因为时间安排的练习将迫使团队仔细检查每一个需求,以确保他们理解范围。当试图安排时间时,各种模糊性都会得到解决。

在实践中,严格遵守时间表,并在现实表明它是错误的时候改变它,保持开发的速度,就像一个准备比赛的跑步者为他或她的训练跑计时一样。没有一个时间表,当程序员因为系统的其他部分没有被开发而不能集成他们的工作时,他们会分心或沮丧。时间表是交响乐指挥的指挥棒,帮助每个人同时演奏同一首曲子。

安排不可知的事情

日程安排有两个主要困难。

  • 你不知道这些要求,而且它们随时都有可能改变。
  • 即使你知道一个需求,也很难预测开发它需要多长时间。

和两个较小的困难。

  • 程序员的生产率差异很大(根据我的经验,是 10:1)。
  • 程序员的大部分时间花在非项目任务上,比如客户和销售支持、HR(人力资源)会议、培训、休假、生病和探索新技术。

但是你不能让这些问题阻止你安排时间——这太重要了。你还是要做。

的确,你不知道需求,而且它们会在开发过程中发生变化。但是不知道细节和完全不知道要做什么是有区别的,不知道细节可能不会对时间表有太大影响。

这里有一个例子:假设您知道您将需要大约 10-15 个报告,这些报告都采用数据库查询的形式,将结果格式化为网页上的表格,并将数据下载为 CSV 文件。您可以用一周的时间来构建整个模板,用一天的时间来完成每个报告,总共需要四周的时间。这可能很高,但很安全。如果你知道做这项工作的程序员是一个杰出的人,那就把它减少到每份报告四分之一天,总共两周。所以,不管怎样,都要两到四周。不是一周,也不是两个月。您完全不知道这些报告中到底需要包含什么内容,您将在工作即将开始时与客户一起解决这些问题。

另一个例子:你正在为一个库做一个项目,一个重要的功能是根据借书人读过和喜欢的其他书来推荐书籍。(就像网飞对电影做的那样。)你应该安排什么?一周?两个月?三年?你不知道。然而,日程安排的不确定性暴露了这是一个主要的技术风险,也可能是一个主要的开发工作。所以,你马上找一两个最聪明的人来研究什么是可用的,建立一个原型,并向客户展示。这可能需要一个月的时间,但是任何有风险的事情都必须马上去做。如果你需要增加三个月的时间来完成这个项目,在项目开始时告诉客户比在大型库国际贸易展前一周告诉客户要好得多,这样会给你的客户留下一个华丽的展位,里面没有任何东西可以展示。

因此,您总是基于您确实拥有的需求进行计划,但是您将该计划限定为基于松散定义的需求。随着需求变得更加具体,您可以细化时间表。

第二个问题,不知道开发需要多长时间,比较好处理。挑选一两个对手头项目最有经验的程序员,让他们估计他们完成需求中的每一项需要多长时间。对于非编程任务,选择有这些技能的人。估计可以用半天,假设半天是四个小时。确保每项任务都包括在内:文档、培训开发、测试、本地化,以及一个完整产品所需的一切。

任务应该被标记为三种类型中的一种,这取决于它们被估计的准确程度。

  • 类型 1 任务类似于以前做过的事情,所以它们的实现时间可以相当准确地预测。
  • 第二类任务是新的,但是很容易理解如何去做。
  • 第三类任务是那些需求不明确或者实现方法未知的任务,对它们的估计都是瞎猜。

接下来,将生产力因素应用到评估中,因为你为评估挑选的顶尖人员很可能高于你的团队的平均水平。2 到 4 倍的倍数可能是有意义的。

然后算出一天有多少实际工作时间。如果有技术支持和其他非项目职责,可能只有四个。如果是新项目,图六。七个或更多可能太乐观了。假设每周只有 40 小时。

所有类型 2 的任务得到 1.5 倍的额外系数,因为估计是粗略的,所有类型 3 的任务得到 3 倍的系数。当然,你并不真的知道这个系数是 3 倍,但这是个不错的开始。

把所有这些加起来,你就得到了需要的天数。假设一年大约有 46 个工作周(52 个减去假期、假日和疾病),你已经得到了交付日期,假设工作被分配,每个人都一直很忙,没有人在等其他人。(以数据库为中心的设计在这里很有帮助,我将在本章后面解释。)

不喜欢我的算术?好吧,用你自己的。关键是你应该使用某种方法来得出实数。这项工作的好处是巨大的。许多问题将会被提出,其中许多将不得不留到以后回答。知道自己不知道的事情,比一头栽进沙子里跌跌撞撞要好得多。

时间表中的任何类型 3(瞎猜)任务都会使它变得非常不准确,所以最好的办法是将项目分成两个阶段:原型阶段,其中类型 3 任务被简化为类型 2(但除了作为原型之外,没有实际实现),然后是实现阶段。宣布实施时间表将仅在原型阶段之后创建。这样,你就不会在胡乱猜测的基础上引用一个交付日期,然后又不得不修改它。

原型阶段的目的不仅仅是帮助计划。在项目开始时解决高风险的设计问题对于开发也是有意义的。你不知道探索这个未知领域需要什么资源,也不知道他们的设计会对系统的其余部分产生什么影响。这样大的扰动应该尽早到来。

(在电影《教父》中,汤姆·哈根听到制片人杰克·沃尔兹拒绝让约翰尼·方丹出演角色后,他起身离开,并说:“柯里昂先生是一个坚持马上听到坏消息的人。”作为一名经理,我也坚持这一点。别担心,我从没杀过赛马。)

当您到达实现阶段时,您只有类型 1 和 2 的任务。第二类任务安排得不太准确,所以把它们移到前面。然后,随着项目的进展和时间表的修改,它会变得越来越准确,因为你面前的工作比计划时已经完成的工作更为人所知。此外,随着项目的深入,您对团队的生产力有了更多的了解,因此时间估计也更加现实。

总结一下:您从一个不准确的时间表开始,但是一个基于反映完成项目所需的所有任务的详细结构的时间表。在原型阶段之后,你宣布时间表。随着项目的进展,您会修改它,并且随着每次修改,它会变得越来越精确。

如果你这样做,团队看起来会很好,特别是如果他们已经公开了最初几个时间表背后的不确定性。即使最后的日期不得不推迟——考虑到我所有的捏造因素,它可能不会——当你还有六个月的时候,推迟一两个星期也是可以的。当一个交付日期在没有通知的情况下被吹了,团队看起来很无能。当团队试图通过延长工作时间、减少测试和在编码上偷工减料来弥补失误时,就会发生这种情况。以正确的方式安排时间,那就没有必要了。

如果你发现自己在一个期望时间表是不可动摇的承诺并责备开发人员的组织中工作,确保你记录了在整个项目中将发生的所有需求变更。然后,您可以将每个进度变更与导致它的需求变更联系起来。如果这是你工作的地方的政治,你就必须玩这个游戏。

调度示例

世界事务会议(CWA)聚集了来自世界各地的大约 100 名演讲者,参加关于政治、艺术、商业、科学、人类事务等各种话题的小组讨论。在过去的 65 年里,它每年春天都在博尔德的科罗拉多大学举行。直到今年我为 CWA 建立了一个 PHP/MySQL 系统,该组织一直用 Excel 电子表格、文本文件和一个只在助理协调员的电脑上运行的 FileMaker 数据库来跟踪与会议有关的一切。

图 1-1 显示了一个大大简化的进度表,用 Microsoft Excel 为 CWA 系统的一部分构建。类型 1 项(101 到 106)是表单,每个数据库实体一个表单。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-1 。时间表示例

报告(107 到 110)是类型 2,因为它们都需要从 PHP 生成一个 PDF,为了这个例子的目的,我将假装我以前没有做过。第三类项目是关于两个我完全不确定的部分:一个是自动分配来访小组成员的方法,另一个是确定每个小组成员的发言时间是否分配合理的方法。(我真的没有做这两件事,但让我们假装我做了。)

这些列基于上一节中的调度方法。假设我知道自己在做什么,那么半天栏反映了我认为自己能多快完成每项任务。 Avg。产品。假设我的速度是普通程序员的两倍,那么专栏将对它应用一个 2 倍的因子。风险调整栏对第 2 类项目采用 1.5 倍的系数,对第 3 类项目采用 3 倍的系数(因为我不知道自己在做什么)。

请注意 3 次输入扩展了多少,一直扩展到 36 个半天来完成住房分配项目。也许时间太少了。也可能是三倍太长。这是 3 型,所以我们真的不知道。

这个模型告诉我们的是,在类型 3 的项目原型化到成为类型 2,甚至类型 1 之前,我们不能真正地提出一个时间表。因此,在计划工作时,这两个项目将是首先被攻击的。直到有一个让客户满意的工作原型,项目才处于原型阶段,并且没有正式的时间表。当这些项目有了详细的设计,或者更好的是,有了可以实际试用的工作原型,实现阶段就可以开始了,同时还有第一个工作时间表。

项目失败的原因

为什么很多项目都会失败?

有两种主要的失败:当项目在完成之前就被扼杀了,当项目完成了但是没有完成它需要做的事情。第二类是未能激发市场现成销售产品,但这更可能是营销失败,而不是开发失败,虽然我很想谈论它,但它远远超出了本书的范围。我们是技术人员,所以我只处理技术问题。

一个项目在它的时间之前就被扼杀了,因为它已经耗尽了它的资金,有权终止项目的管理层已经对它失去了信心,或者这个项目本来要做的任何事情都不再需要了。最后一类是 20 世纪 70 年代贝尔实验室的一个项目,建造一个机器人自动化主配线架(AMDF ),可以管理电话中心局的布线。该项目实际上进行得相当顺利,但与此同时,贝尔实验室的另一个小组已经开发出了电子开关。电线变得过时了,所以不再需要机器人来连接它们,AMDF 项目被终止了。

但是让我们面对它,在大多数情况下,一个仍在开发中的项目的死亡是一种安乐死。进展是如此糟糕,演示是如此令人不安,以至于它的预期客户不再相信它会在任何合理的时间框架内以任何合理的金额完成。

第二种失败是,一个完成的项目没有让客户满意,这可能更常见,但不容易识别,因为,只要它工作,客户的抱怨可以通过一系列的维护版本来解决。结果可能并不美好,但随着时间的推移,这个体系可以发挥作用。大概 90%的内部系统都是这样的。他们的发展是创伤性的,在第一年左右的时间里有一些问题,但是现在每个人都已经习惯了,他们只是机构景观的一部分。

然而,如果它是现成销售的商业产品,如果它是蹩脚的,它就不会销售。如果公司要生存下去,技术人员必须做得更好。

嗯,这些可能是有趣的分类,但是真正的原因是什么呢?史蒂夫·麦康奈尔是优秀书籍 Code CompleteRapid Development:Taming Wild Software Schedules的作者,他在stevemcconnell.com/rdenum.htm列出了更完整的 36 个经典错误。

我将在这里列出我自己的更短的列表,大概是按照它们出现频率的顺序。

较差的要求

随着开发的进行,演进需求是可以的,甚至是更好的,但是它们必须汇聚到导致成功的东西上。如果对需求的追求永无止境地循环而没有越过终点线,或者没有瞄准市场想要购买的东西,那么需求过程就是有缺陷的,对于不知道他或她真正想要什么的客户,再多的抱怨也不能扭转局面。这个项目失败了。

下一章会有更多关于需求的内容。

弱队

没有人想说开发团队的人不够好。他们最接近的说法是这些人没有得到足够的训练,这可能是真的。但是,让我们面对现实吧,人们有时就是没有才华或者经验不足,或者两者都有。正如我前面说过的,和最优秀的人在一起,无论如何你都可能会成功,因为最优秀的人会尽一切努力去成功。和错误的人在一起,很难看到你能取得成功。

未能建立高风险特征的原型

你不能就这么一头扎进去,假装那些高风险的第三类特征是正常的。当团队不顾一切地试图让系统按照承诺的方式工作时,项目会因为过于接近交付日期而陷入困境,这是一个真正的危险。正如我所说的,高风险的任务必须首先被攻击,而不是被推迟,以便团队可以通过采摘低挂的果实来显示早期的,但误导性的进展。(我想起了 1993 年,当运行自动行李处理系统的软件被调试时,完全完工的丹佛新机场推迟了一年半才开放。这一部分早就应该完成了。)

糟糕的设计

设计是获取需求并提出实现这些需求的蓝图的过程,有点像建筑在建造建筑物中的作用。如果设计不好,即使需求是正确的,设计的实现是完美的,项目也会失败。

在我看来,许多系统根本就不是设计出来的。程序员获取他们拥有的需求,如果他们使用敏捷方法,可能只需要一周的时间,然后开始编码。即使有,也很少项目中有人拥有应用设计者的头衔。的确,有用户界面设计师,但用户界面只是设计的一小部分,正如门把手、电梯按钮和 HVAC 控制只是建筑的一小部分。

糟糕的开发过程

即使有一套好的需求,如果开发过程很糟糕,项目也会陷入大麻烦。其中的罪恶有

  • 等到开发后期才集成组件。
  • 单元测试不充分,导致集成失败和大量时间浪费。
  • 模块分配给人的方式很差,没有清晰和最小的界面。
  • 笨拙和/或频繁变化的数据库模式。
  • 草率的编程习惯,在代码遍历期间没有得到纠正。
  • 覆盖有缺口的系统测试。
  • 没有通过频繁的演示和 alpha 版本吸引客户。

改变了优先级

20 世纪 70 年代我在贝尔实验室时,这是项目的杀手。一个项目开始了,人员配备水平只有贝尔实验室的所有者能够负担得起,然后几年后因为技术进步(我已经提到过 AMDF 项目),更重要的项目需要人员,或者经济变化而终止。也许另一家公司在开始一个前景如此可疑的项目之前会考虑得更仔细一些,但在那个年代,在& T 是一个受监管的垄断企业,不必监督其开支。

如今,优先级的改变很可能意味着风险投资的枯竭。

破坏

破坏是经理故意扼杀项目的企图,通常是秘密进行的。一种常见的情况是,从未支持过项目的人替换了经理。这种以及其他形式的公司内斗超出了本书的范围;我在这里列出这种情况只是为了承认这是很常见的。

管理项目

根据定义,管理是利用资源来完成目标。在一个软件项目中,目标是满足客户,在日常工作中,它的代理就是需求。资源主要是人,尽管设备和外部服务也可能参与进来。

管理有很多,但是,有五个必要的任务,如果做得正确,将导致成功。前三个反映了我前面讨论的三个维度。

  1. 防止需求不必要的扩展。
  2. 让合适的人加入团队。
  3. 密切监视时间表,并在必要时进行更改。
  4. 确保所有的工作都分配好了。
  5. 让人们愉快地专注于他们的任务。

就这样!做到这五点,如果需求正确,项目就成功了。任何一项失败,项目都会失败。

我已经解释了为什么前三个任务如此重要,我将在第二章中对需求做更多的说明。

令人惊讶的是,经常被忽视的任务 4 是确保你开发所有可交付物的*。人们很容易被管理编程的复杂性所困扰,而忘记了更普通的需求,如文档、培训、安装和支持。任务 5 是确保你确实执行了你通过做前四项任务而制定的计划。程序员因偏离他们的主要任务而臭名昭著。更重要的是,你必须让他们开心,否则他们的生产力会一路下降到零,甚至可能是负数。*

我一直小心翼翼地使用“管理”这个术语,而不是“经理”所有的项目都必须有管理,不管多小;没有它,资源就不能用于实现目标。对于一两个人的团队,没有必要指定其中一个人作为经理。但是三人以上的团队需要知道谁是老大。通过一致意见来决定事情的效率太低,而且有失去通往目标之路的风险。半打以上的人,还需要有一个专职经理。

但是,不管你怎么安排,这五项基本任务必须完成。你可能会说,对于一个协作的、无我的、面向团队的方法来说,时间表和工作分配是不必要的,但我不同意。我认为这导致了必然的失败:一个晚期的项目,带有不受欢迎的工作,例如转换、文档或培训,没有完成或随意完成。

下面是我如何为超级超市的收银员调度软件 SuperSked 做事。当我被请来管理工程时,他们已经花了一年的时间烧完了 50 万美元左右的风险投资,这笔投资本该让他们获得一个 Windows 版本的系统,而这个系统原本是为面向字符的 UNIX 终端开发的。一年后,他们完成了一个花哨的面向对象的用户界面框架,除此之外别无其他。我同意再拿 25 万美元左右的风险投资,并在大约 6 个月内完成这个系统。如果我失败了,插头就会被拔掉,公司就会倒闭。

因此,在需求、资源和进度这三个维度中,有两个已经确定了。实际上,需求也是如此,因为新系统必须完全取代旧系统,这意味着它必须提供与旧系统相同的需求预测、遵守工会规则、成本优化和打印时间表,但在 Windows 上。

我认为这是可行的,就签约了。大约有八个人在开发,包括一个从事优化算法的数学家和一个测试专家。新 CEO 和我解雇了三名技术能力足够强的人,但他们花了太多时间争论现有系统是否在正确的轨道上。经过一周左右的学习,我扔掉了他们花了一年时间编写的 100%的代码,但保留了数据库设计,这非常棒。我雇了两个程序员,一个是以前的同事推荐给我的,一个是报纸广告上的。

列表上的任务 1 和 2 到此结束:人员和需求。

我知道时间表的终点,但不知道中间点。因为我们还有六个月的寿命,我计划了四个月的开发,给我们一个月的时间进行系统测试,一个月的时间处理不可预见的问题,根据定义,这是无法计划的。

所以,我尽我所能绘制出这个系统,并把工作分成 16 个每周部分。当然,我们并没有严格按照计划行事——从来没有人会这样做——但是我必须说服自己,这个问题有一个解决方案。任务 3 完成。

然后我单方面地分配工作。在其他项目中,这将通过协作来完成,但没有时间这样做。我只是告诉人们我想让他们做什么。这是任务 4。

有了这些,我唯一的管理工作就是任务 5,让每个人都集中注意力。我在每周例会上做到了这一点,我们在房间里走来走去,这样每个人都可以很快说出自己在哪里。我们还要维护现有的系统,所以客户的问题也摆在了桌面上。

每周例会通常会产生一些需要我进一步关注的问题,我会私下拜访相关人员来解决。一条严格的规则是,我们在每周例会上从不讨论任何事情。他们只是为了地位。他们从未超过半小时。

团队中的许多人已经习惯了拖拖拉拉的会议,在这些会议上,人们试图就某个问题做出决定。我在第一次会议上解释说,我只想要地位,但这并没有马上流行起来。该公司的创始人之一 Seth(化名)开始喋喋不休地谈论他试图找到他正在做的事情的解决方案。以下是随后的对话:

我:“塞斯,别说了。”

赛斯:“我在解释我一直在做的事情。”

我:“我知道,但这是一个状态会议。只需告诉我们它是已完成、正在进行还是尚未开始。”

赛斯:“正在进行中,但我需要解释一下,它变得复杂了。”

我:“Seth,你把这和人们谈论的会议搞混了。不是的。在这个会议上,几乎所有的发言都由我来做,轮到你的时候你也可以发言,但你只能说三件事情中的一件。”

大家:嘲笑我的讽刺。幸运的是,塞斯也在笑。

赛斯:“进行中。”

我:“很棒的报告。下一项?”

许多年后,我遇到了测试专家,她告诉我她多么喜欢这个项目。我告诉她,我可能表现得像一个讽刺的混蛋。她不同意,告诉我我是他们有过的第一个真正管理的经理;他们厌倦了失败,而且,他们都认为我的讽刺很有趣。(是;我很擅长这个。)

顺便说一下,我们按时交付了新系统,客户很喜欢它,该公司被卖给了同一行业的一家更大的公司,十年后该公司的产品仍然很好。对投资者来说,更重要的是,在吻别之后,他们拿回了自己的钱,甚至更多。我继续前进,但我认为创始人塞思仍在那里,他所有的股票都完好无损。

这是我方法的另一个例子,这次是在我去的下一家公司,也有麻烦。一个叫 Brian 的程序员开始让数据库管理员恼火,因为他卷入了性能和备份之类的数据库问题,甚至与他的工作毫不相干。我请他到我的办公室来。

我:“我知道你已经开始帮忙处理数据库了。”

布莱恩:“是啊。真的没做对。我很乐意帮忙。”

我:“你还记得你的作业吗?”

布莱恩:“当然。web 更新页面。”

我:“所以,这就是我要你做的。就那样。”

布莱恩:“我正在做。但我也认为我应该在力所能及的地方为其他事情做出贡献。”

我:“绝对的。你可以做任何你认为最好的事。完全由你决定。我无权干涉。”

布莱恩:开始紧张地微笑。

我:“这里只有你不行。在这里,你只能做你的作业。”

布莱恩明白了:“所以。。。我来更新网页。而不是数据库。”

我:“那就好。谢谢你。顺便说一句,它看起来真的很好。”(不知道这是不是真的,但我希望他开心地离开。)

你可能会对我的独裁风格感到震惊,但是考虑到我们董事会强加的限制,我打算最多花两分钟让这个优秀但心不在焉的程序员回到他的任务中。

另一个重要的监控任务是确保由不同的人开发的各种组件能够组合在一起,并使用最新的数据库模式。如果系统需要构建,它应该每晚自动构建。(PHP 程序通常不需要构建,只需要将文件放在服务器上适当的目录中。)已经开发的任何自检系统测试也应该运行。团队中的每个人每天早上都应该收到一封电子邮件,告知结果。如果有任何问题,修复它是当务之急——在系统集成并通过自检测试之前,不应进行任何进一步的开发。

分工

他是松树,我是苹果园。

我的苹果树永远也过不去了

我告诉他,吃他松树下的球果。

他只说,“好篱笆造就好邻居。”

——罗伯特·弗罗斯特,修补墙壁

一个有很多交流的项目是一个令人愉快的项目,但是很少的交流应该是关于组件之间的接口。界面需要简单、最小和稳定。一旦建立,如果有更多的关于他们的讨论,事情是非常错误的。但是,如果接口是正确的,系统将顺利地集成,并适应变化,而不会失去其结构完整性。

组件之间的接口也应该是开发团队成员之间的接口。这使得开发人员可以按照自己的节奏和最有意义的顺序工作。糟糕的界面——泄露太多信息的界面——导致开发人员不得不等待对方完成工作,并导致已经完成的工作被重做。

这并不是说团队不应该每天都有重要的事情要谈。谈论每夜构建和测试的结果,关于客户不断发展的需求的新见解,以及可以从团队工作中受益的技术问题是很好的。但是经常说接口就不行了。他们需要稳定到无话可说。

利用数据库中心性

我几段前提到的那个 SuperSked 应用是我管理的第一个以数据库为中心的项目,有两三个人以上参与,开发进行得非常顺利,尽管应用本身相当复杂。几乎没有集成困难。通过测试发现的错误很快被识别和修复。

我们做了很多正确的事情:一个了不起的开发团队,充分理解的需求(它是现有系统的替代品),以及一个紧凑但合理的时间表。但是项目组织的另一个方面真正起了作用:所有的组件只与数据库对话。他们完全从用户(通过表单)或数据库获取数据,并将结果存储回数据库。组件之间从不直接对话。

测试基于从现有系统的客户那里捕获的数据库快照,然后使用相同的转换程序转换到新的数据库,这些转换程序后来成为已部署系统的一部分。一个组件可以独立运行,因为它需要的一切都在数据库中。(一些组件需要一些数据输入。)那么数据库中的结果可以容易地被检查。如果有什么问题,那一定是组件本身的问题,因为这不可能是接口问题。

好消息:PHP/MySQL 应用,这本书的重点,提供了完全相同的优势。然而,为了利用这一优势,您必须将每个组件——每个表单、报表和业务逻辑模块——设计为只与数据库接口。

组件开发人员和数据库设计人员之间有很多交流,尤其是在项目开始的时候,因为模式很复杂,有很多微妙之处。但是一旦这个模型被很好的理解了,即使是这些对话也几乎停止了。

下面是一个组件如何只与数据库接口而不是直接与数据库接口的示例:安排检查程序的第一步是根据一年中的时间、一周中的日期、是否有假期、预测的天气以及其他一些因素来预测需求。第二,必须确定劳动力的可用性,这取决于工作时间表、工会规则、培训(不是每个人都能处理快速注册)等等。这两个初步步骤的结果然后被输入到一个非常先进的优化算法中,该算法计算一段时间,可能是 10 或 15 分钟,然后咳出一个时间表,然后被安排到各种报告中,包括一个张贴在员工公告栏上的报告。

显而易见,要做的事情是找到一种方法,例如使用 XML,将预测和可用性模块连接到优化器。我们没有那么做。我们让他们将数据插入数据库,并设计优化器来查询数据库的输入。这样做的一个巨大好处是,从事优化器工作的程序员可以继续在相同的输入上运行它,而根本不用处理前面的模块。而且,一旦从事这些工作的程序员验证了数据库中的数据是正确的,他们就完成了。不需要实际运行优化器。

对于从事遗留系统工作的程序员来说,数据存在于内部巨大的 Fortran 风格的数组中,将所有这些数字写入数据库的想法看起来非常奇怪。但是,一旦完成这项工作的函数被编码,工作就完成了,他们再也不用处理接口问题了。

将组件分配给人员

许多 PHP/MySQL 项目非常小,可能是因为大型企业开发倾向于使用更高级的技术,如 Java EE 或. NET。我没有具体的数字,但我猜一半的 PHP/MySQL 项目是由一两个人完成的,四分之一是由四个人或更少的人完成的。所以,工作通常只分成几种方式。

对于任何 PHP/MySQL 项目,以及大多数其他项目,开发工作的类别都是

  1. 数据库设计和实现,
  2. CRUD(创建、检索、更新、删除)网页,
  3. 业务逻辑(例如,安排超市收银员、递送路线、计算发票),
  4. 报告和其他产出,
  5. 转换,
  6. 系统测试,
  7. 文档,以及
  8. 训练。

如果有两个人,一个人负责数据库、转换和报告,因为这几乎是一个独立的应用。一旦数据库加载了转换后的数据,就应该有足够的数据来测试报告。另一个人可以做 CRUD 和业务逻辑。系统测试、文档和培训可以按照你认为合适的任何方式分开。

对于三个人来说,如果业务逻辑很复杂,就像 SuperSked 的情况一样,那就是一份全职工作,其他工作可以像两个人一样分开做。

如果超过三个,将数据库和转换结合起来仍然是有意义的,因为转换暴露了数据库需要能够建模的太多内容。此外,一旦设计并实现了数据库,它就不再是一项全职工作。其他组件可以根据它们的复杂性和团队成员的技能来划分。

请记住,无论您如何分配组件,它们只与数据库接口,而不会相互接口。

职场

组织一个项目的一部分是组织一个工作场所。

就技术而言,对于 PHP/MySQL 项目,物理位置并不重要。在您自己的计算机上建立一个开发系统并把文件发送到开发服务器进行集成是很容易的。你不需要任何特殊的硬件;任何运行 MacOS、Windows 或 Linux 的笔记本电脑或台式机都可以。

如果每个人每天都来办公室,项目的运作就会容易得多。电话、Skype、短信和电子邮件都不能代替面对面的交流。也许最大的区别是,只有当你知道你有事情要谈的时候,你才会开始电子交流。相比之下,面对面的交谈会带来意外的交流。例如,一个程序员可能会对另一个程序员说,“我忽略了你关于连接人员和状态表的讨论。我很确定已经有了一个观点。”或者,经过某人的桌子,瞥了一眼他的显示器,“你怎么能让菜单这样工作呢?jQuery UI,还是自己卷的?”这些互动不太可能发生在每个人都在家工作的情况下。

是的,有关于超级碗,钓鱼旅行,以及谁将取代老板的谈话,他们只是浪费时间。大多数远程工作者会说他们在家更有效率,他们可能是对的。

我两种方式都做过,每个人每天都参与工作的项目更容易管理,士气更高,并按计划进行。让程序员保持专注是五个必要的管理任务之一,正如我之前所说的,如果程序员自己离开了,这就更难做到了。不是不可能,只是更难。

因此,如果这将是一场势均力敌的比赛,每场比赛都至关重要,就像超级任务项目一样,每个人都需要在场上。

另一个工作场所的问题是,如果有办公室,是有私人办公室、小隔间还是一个大的工作区。如果每个人都在同一个房间里,就很难集中注意力,尽管这可以创造一个非常有创意和刺激的氛围。如今,对于大多数公司来说,私人办公室几乎总是过于昂贵。所以,它将会是小隔间,有严格的关于谈话和其他吵闹的追求的规则。

程序员可以花些时间在休息室是个好主意。它为盯着屏幕的人提供了喘息的机会,也为那些偶然的对话提供了肥沃的土壤。无论如何,安装一个足球桌;这至少会给你的新兵留下深刻印象。

问题跟踪

随着项目的进展,团队必须跟踪需求的变化、测试中发现的错误、要解决的设计问题、各种要做的事情等等。我把它们都叫做问题,并把它们输入数据库,这样它们就不会被遗忘。

找到您喜欢的具有以下属性的系统:

  • 足够灵活,可以处理各种问题,不仅仅是 bug,还可以充当客户支持系统。
  • 允许你定义你自己的项目、子系统、人员、状态、严重性、优先级和所有你想要的属性。我见过的大多数系统都可以做到这一点。
  • 有一个网络界面。不管你使用什么平台,如果有一个本地应用也没问题,但是网络界面允许通用访问。
  • 使用一个可以用 PHP 访问的数据库,这样你就可以根据需要编写自己的实用程序。

在过去一年左右的时间里,我一直在使用 FogBugz ( fogcreek.com/fogbugz)来支持我自己的软件产品,它可以做前面列表中的所有事情。它对一两个用户是免费的,对于更大的群体,每个用户每月 25 美元。(免费版没有数据库访问。)你可以在自己的服务器上安装其他完全免费的替代软件,比如 Bugzilla ( bugzilla.org)或 HESK ( hesk.com),但你必须花时间安装并运行它们。我发现 HESK 的安装是由我的虚拟主机公司(A2 Hosting)自动完成的,所以我后来改用了它。这是一个 PHP/MySQL 应用,这意味着你可以用自己的 PHP 程序轻松访问数据库,我将在第二章的中展示。

你用什么系统并不重要,重要的是你用它来解决所有问题,所以只有一个地方可以看。电子邮件、纸片、短信和口头评论都应该输入到问题跟踪器中。

如果您直接与客户合作,请考虑让您的客户访问问题跟踪器。这样做的缺点是它提供了太多的细节,可能会让客户不必要地担心对开发人员来说是无关紧要的事情。“建筑因为缺少头问题而被炸毁”或者“严重崩溃,需要重新生成”并不是什么大问题,但是对于外行人来说,它们听起来可能是不祥之兆。也许提供及时的报告更好,可能在一个网站上,每天晚上可以自动更新。

被确定为真实且需要关注的问题应由整个团队至少每周审查一次,以确保没有任何东西被忽略,并且每个团队成员都专注于他或她的任务(本章前面列出的管理任务 5)。仅仅通过电子邮件分发名单是不够的。一个问题需要单独讨论,即使只有一两秒钟。如果你能旁听我的一次会议,你会听到类似下面这样的话:

领导:“1478。”

鲍勃:“成交。”

领导:“1492。”

简:“成交。”

领导:“1501。”

玛丽:“正在进行中。需要讨论。”

领导:“1504。”

汤姆:“成交。”

然后在点名结束时,任何标记为需要讨论的问题都可以讨论。当整个团队都在房间里时,你不希望花超过一两分钟的时间来谈论一个可能只涉及几个人的问题,所以在这么短的时间后,应该离线讨论。如果情况紧急,经理可以去见相关人员。如果没有,它将在下周的会议上再次被提出。

既然已经完成了为什么还要调出 1478 期?这不是已经记录在问题跟踪器中了吗?是的,但是鲍勃应该有机会大声说出来。他不能发表演讲——毕竟只是小小的 1478 年——但他有两秒钟的发言权。

法律事务

就像我在本章开头所说的,你的目标是让你的客户满意,如果你这样做了,你就可以避免法律上的麻烦。尽管如此,还是有一些具体的事情需要你关注。

有书面合同

在不同的时候,我的三个朋友请我帮助他们解决与咨询客户的纠纷。一个是关于准备培训课程的复杂付款公式,一个是关于专利所有权,一个是关于软件权利。这三家公司都有一个共同点:没有书面合同。我不是说没有律师起草的文件;我的意思是根本没有写下来。

我不知道为什么会这样。也许他们认为书面合同需要律师,他们不想要这笔费用。也许他们仓促上马了这个项目。也许有一方希望日后能够灵活地退出交易。无论如何,你不应该落入那个陷阱。把它写下来。

合同中需要包含哪些内容?嗯,他们说记者应该写什么哪里什么时候为什么怎么(除了婚礼,他们跳过了为什么)。这也是一份不错的书面合同清单。谁将为谁做这项工作,这项工作是什么,系统将在哪里开发和安装,何时交付,以及如何完成。如果你愿意,可以跳过为什么。但是,包括婚礼之后的事情:离婚。说明如何终止合同以及终止后会发生什么。还包括谁拥有什么,这我将在下一节讨论。当然,还有你会得到多少报酬,多久一次。

真的,如果我的任何一个朋友有这份清单,甚至作为一套非律师的要点,他们会避免他们的困难。

你可能会发现自己处于这样一种情况,你认为建议你和你的客户签订书面合同是令人不快的。在这种情况下,不要使用“合同”这个词只需发一封电子邮件,说类似这样的话:“我想我应该回顾一下我们的工作协议,以避免以后给我们的回忆增加负担。”然后继续列出要点,并通过回复电子邮件要求客户确认他或她的同意。电子邮件可能会澄清任何分歧,如果没有澄清,任何调解人/仲裁人都会从中开展工作——med/ARB 是一个非常灵活的流程。法院也可能,但你不太可能走到那一步。

知道谁拥有什么

你将系统交付给你的客户,但是他或她拥有它吗?是吗?以后你能为另一个客户使用这些代码吗?如果它包含了您在项目开始前编写的代码呢?你失去所有权了吗?专利呢?版权?商标?

这一切都被称为知识产权 (IP)。在我工作过的每个项目中,客户拥有我在向该客户收费期间开发的所有东西。因为工作的性质,我从来没有想过要拥有它。但是,如果你被雇佣去发明一些真正新颖的东西,比如推荐电影的算法,或者安排送货车辆的路线,或者一个社交网站,你最好就所有权进行谈判并写下来。

有几次,我决定开发一些通用软件,用于当前的项目,并保留下来供将来使用。为了发展这一点,我关掉了时钟,当一般化的部分完成时,又把它打开了。这是合同中规定的。

有一个法律术语叫做“雇佣作品”,在版权的背景下,意思是雇佣作品的人就是作者。这通常是你工作的方式,不管你是雇员还是承包商,你都可以把这个条款写进你的合同里。如果你想要任何其他安排,你必须小心,在这种情况下,你可能需要一名律师参与。

无论如何,正如我所说的,在大多数情况下,你应该抛开任何你会从这个项目中得到一些东西的想法,除了你的工资,一个满意的客户,和更多工作的可能性。试图对知识产权提出要求变得非常棘手,而且无论如何,你的项目不太可能产生任何有价值的东西。

如果你认为你写的一些通用代码可能是有用的,你可能会得到一个条款,说你可以在其他项目中使用任何非特定应用的通用代码,而不需要付费。许多客户会同意这一点。如果他们没有,就像我提到的,在下班后做。

在我的一个朋友卷入的专利纠纷中,问题是他希望自己的名字出现在专利上,因为他是发明人,只要专利被转让给他们,他们就没问题。他不同意这一点。如我所说,什么都没写下来。最终,他们以不署名的方式提交了专利*,声称自己是发明者。从法律上来说,我不知道这是否正确,因为这与雇佣工作有些关系。它从未被提起诉讼。他只是生气地离开了。令人惊讶的是,这是第二次这个家伙陷入知识产权纠纷,两次都没有书面合同。显然,他不听我的劝告。不过,你应该。*

当心许可证纠纷

应用中包含的一些软件既不属于您也不属于您的客户:第三方包,如 PDF 生成库、您调用的可执行文件以及产品附带的 JavaScript 代码,如 jQuery。其中大部分将被某种开源许可所覆盖,比如 MIT、Apache、BSD、GPL2、Lesser GPL (LGPL)或 GPL3 许可,这是我最常遇到的五种许可。还有商业牌照,每个都不一样。

每个许可证都对软件用户提出了要求,从包括版权声明(MIT)到提供你自己的应用的源代码(GPL3)到为每一个使用的拷贝支付版税。你最好了解你使用的每个组件的来龙去脉。

如果可能的话,请仅使用麻省理工学院、阿帕奇、BSD 或 LGPL 许可涵盖的第三方软件。商业软件应该被避免,除非它提供的功能无法用其他方式提供。如果你打算将 GPL2,尤其是 GPL3 软件集成到你自己的系统中,这是很危险的,因为它可能会迫使你做一些你和你的客户都不想做的事情:向公众发布你的代码。然而,如果你只是打算连接到它(MySQL)或者在它上面运行(Linux),你不需要担心。安装必须为此获得许可,而不是软件开发人员。大多数时候,我使用提供 MySQL 和操作系统(通常是 Linux)的商业主机服务,所以对他们来说不存在许可问题。我只是担心我并入 PHP 程序的源代码。

涉及律师

有些情况下你需要律师,比如你想拥有你开发的 IP,但这种情况很少。你通常不需要律师,即使你用了,他或她也不能帮你避免法律麻烦。

原因如下:如果对书面合同有争议,几乎肯定会是关于要开发什么,是否通过验收测试,以及何时交付。所有这些都是你指定的,而不是你的律师。以下是律师起草的合同中的典型段落:

开发商应作为买方的承包商,被 IRS 定义为 1099 承包商,并应根据功能规范和相关信息(如有)设计、开发和实施应用软件(下称“软件”),该功能规范和相关信息作为附件 A 附于本协议,并通过引用纳入本协议(下称“规范”),在本协议中有更全面的规定。

猜猜谁提供证据 A,你还是你的律师?你的律师根本不知道这意味着什么。作为一名专家证人,我在涉及美国电话电报公司、IBM、微软和其他一些小得多的公司的合同中看到过这种情况。这些大公司使用了世界上最昂贵、最有声望的律师事务所,毫无疑问,他们让非常合格的律师负责这些账户。但是没有一个律师知道什么是操作系统内核、非统一内存架构、虚拟设备驱动程序或危险废物清单。然而,这就是战斗的意义所在。

所以你的律师会收取你每小时 300 美元(或更多)的费用,但不会让你远离麻烦。如果你需要任何特殊的法律性质的东西,比如涉及知识产权的东西,请找律师,但是技术问题你得自己解决。

获得报酬

如果你是一名受薪雇员,你会得到报酬,除非你的公司濒临破产,在这种情况下,你可能已经在找另一份工作了。但是许多 PHP/MySQL 开发人员是顾问或承包商,所以及时付款不会自动发生。获得报酬有两个部分:开发票和收款。

货品计价

每个项目都需要一份书面合同,即使只是一封简单的概述关键协议的电子邮件。除非你是一名志愿者,就像我在世界事务系统会议上一样,这些协议必须包括以下内容:

  • 你会得到多少报酬。
  • 你多久给客户开一次发票。
  • 客户何时支付发票。
  • 谁能拿到发票。
  • 您需要参考什么特殊代码,例如项目编号或供应商编号。

如果雇佣你的人不知道最后两个,不要惊讶。如果你找不到答案,你可能会在发现需要支付项目费用之前,向应付帐款部门发送几张发票。即使这样,你可能会发现,如果你留下你的供应商编号,你的发票可能会被忽略。接到一个友好的人打来的电话,主动提出写下一些关键信息,这是你不能指望的奢侈。许多组织欢迎延期付款的借口。尽量不要帮他们。

我总是按小时工作,从来没有固定价格的合同。当你加入客户团队的时候。以固定的价格工作会让你成为一个对手,每一个需求变更或 bug 报告都是产生分歧的机会。别这么做。

每小时多少钱?要看客户是谁,在哪里。阿尔伯克基或博伊西的费用可能是纽约或旧金山的一半。财富 500 强公司支付的比家庭经营多得多。事实上,每小时收费低于 125 美元左右,财富 500 强公司甚至不会雇用你,因为你显然没有资格做他们的工作。如果一定量的工作有保证,我有时会给折扣,因为我知道在寻找下一份工作时,我不会有那么多的停工时间。

如果可能的话,费用——旅行、设备、软件等等——应该是额外的。理查森学区无法证明支付外州承包商的差旅费是合理的,所以我把他们的差旅费计入了我的时薪。这很容易计算,因为我们已经同意每月两次为期两天的旅行。

找出你需要哪些费用文件,有哪些限制,如果是设备,是否需要归还,何时归还,归还给谁。

尽你所能找到发票的确切寄送地点和方式(电子邮件、PDF、公司供应商网站等)。).确保你有所有你需要的参考代码,这样他们就知道发票是谁开的,而且是经过授权的。(下一节将详细介绍这一点。)

及时收到你的发票。我曾经有一个客户破产了,因为我推迟了几个星期才寄出我的最后一张发票,所以一直没有收到付款。

收集

你会认为如果你提交了一份正确的发票,客户就会给你一张支票,对吗?事情并不总是这样,在下面的警示故事中就不是这样。

我当时是芝加哥一家大型律师事务所的专家证人,这家律师事务所的客户微软被另一家软件公司以违约为由起诉,该公司与微软有一份联合开发合同。我被指示把我的发票送到律师事务所,我照做了,前两笔都按时支付了。但是,从第三个开始,什么都没有。

律师事务所告诉我,他们甚至不应该支付前两笔费用,因为他们应该通过微软的法律服务系统,这就是我此后必须处理的事情。几天后,我收到一封电子邮件,里面有一个叫 MS Invoice 的东西的注册信息。

我上了网站,开始填表格,但是因为不知道订单号而卡住了。几封邮件来来回回试图得到它,期间我被告知使用 DataCert,而不是 MS Invoice。登录 DataCert 需要更多的电子邮件,其中一封邮件要求我提供我的微软联系人的姓名,而我没有。(我在案件期间没有和微软的任何人说过话;专家证人通常不直接与他们作证的一方说话。)我在说明中了解到,使用 DataCert 需要一笔可观的费用来支付安装成本。投诉的时候被告知不会收费。还有一份厚厚的多页法律协议要签署。最后,我签了协议,接受了他们的承诺,没有任何费用。

一旦我进入 DataCert,我需要一个问题编号,但我也没有。更多的邮件来得到它。最终,经过几周的反复,我能够让系统识别我的发票,此时我发现它们将在 90 天内支付。我最终得到了他们欠我的所有东西,但是,除了前两张发票,等待第三张发票的总时间大约是六个月。

所以,如果你在为微软这样的官僚机构工作,就要小心了。我不知道它荒谬的计划是一种改善现金流的故意方式,还是仅仅因为复杂性变得疯狂。不管怎样,获得报酬都是一个挑战。

雪上加霜的是,微软明确禁止我向微软开账单,因为我花了很多时间向微软开账单。因此,我有几个小时的时间是没有报酬的。

(如果你好奇的话,我从来没有发现这场诉讼发生了什么,因为它是在庭外解决的,大多数都是这样,结果是保密的。我问我的律师联系发生了什么事,但他只是笑着说他不能告诉我。)

教训:同意每小时付给你这么多钱是好的,但这本身不会让你得到报酬。弄清楚你要做什么才能真正得到报酬,否则你会等很久。

章节总结

  • 你为其开发应用的客户决定了它的成功,所以你需要知道那些客户是谁,怎样才能满足他们。
  • 项目的三个维度是需求、人员和进度。设置任意两个决定第三个。
  • 开发团队的质量是应用能否成功的第二重要的决定因素。(首先是有正确的要求。)
  • 即使有些因素,如需求和实现它们需要多长时间,是不可知的,你也必须安排时间。
  • 对于 PHP/MySQL 项目,有简单明了的分工方式。一个指导方针是将转换和数据库设计放在一起,也许还有报告。
  • 永远要有一份书面合同,但是你可能不需要律师来写。
  • 仅仅因为你给顾客开了发票,并不意味着你很快就能拿到钱。你还需要知道如何收集。

二、需求

哦,你不可能总是得到你想要的东西

但是如果你有时尝试,你可能会发现

你得到你需要的

——滚石乐队

第一章 讲述的是项目的总体情况,无论是 iPhone 应用、喷气式飞机的航空电子设备、医疗记录系统,还是美国国税局的电子申报系统。但是现在是时候关注这本书的主题,并坚持 PHP/MySQL 应用的简单得多的世界。虽然一般来说收集需求的工作可能很难,或者在某些情况下,甚至是不可能的,但是对于 PHP/MySQL 应用来说,这是非常简单的。这在很大程度上是因为对性能要求很高的应用,或者非常复杂以至于需要几十甚至几百名开发人员的应用很少使用 PHP 编程,或者使用 MySQL 而不是 Oracle、SQL Server 或 DB2 之类的数据库。

因此,我们将把那些大型的、复杂的、多年的项目留给其他人,而只担心那些更小的、更简单的项目,它们是 PHP/MySQL 世界的特征。

通常,大多数写需求的作者会讨论产生需求的过程,但他们不会给你需求本身。显然,我也做不到这一点,因为我不知道你想要建立什么。但是,我可以比大多数人更接近。我会告诉你一个需求文档必须有哪些部分,并解释你需要在每个部分放什么。在某些情况下,我会给你你可以使用的确切的词。(记住,我只处理 PHP/MySQL 应用。)而不是讨论所有可能的方法,我只是告诉你该怎么做。当然,这只是一个友好的建议,而不是法律要求,但是我认为既然开发人员如此讨厌写需求,他们会喜欢一个按数字绘制的工具包,而不是一个四年制的美术学士学位。十年前,我会被指责过分简化了软件工程过程的一个关键部分,但是现在,考虑到敏捷方法的流行,它完全免除了预先的需求,我听起来像一个老式的、墨守成规的传统主义者。我感谢敏捷运动让我曾经激进的想法看起来像保守的想法。

需求文档的大纲

是的,它真的需要一份文件。写下来。不要潦草地写在白板上,写着清洁人员不可擦除的记号,也不要写在一堆便利贴上,或者电子邮件的档案里。如果你不想写文字,用图表或漫画。只要它以独立文档、电子表格或数据库的形式写下来。

虽然你当然可以用 Microsoft Word、Apple Pages 或任何其他文字处理器来编写需求,但是将它们保存为文本文件确实有好处,正如我将在“当需求改变时”一节中解释的那样

在我看来,PHP/MySQL 应用的需求分为 17 个部分,所有部分都必须存在:

  1. 数据库:主要实体(在第四章中解释)。不需要属性,因为它们应该放在数据库设计文档中。
  2. CRUD :用于创建、检索、更新和删除数据的 PHP 页面。
  3. 处理:任何比 CRUD 或报告更复杂的事情,比如安排超市员工,给房间分配会议,推荐书籍。
  4. 报告:数据库输出(屏幕、PDF、CSV、XML、RTF 等。).
  5. 外部接口:与其他计算机系统的连接。
  6. 国际化:使应用本地化——以及本地化本身——使其适应特定的语言和文化,比如西班牙语或德语。(国际化通常缩写为 I18N,代表 I、N 和中间的 18 个字母。本地化为 L10N)。
  7. 无障碍:针对残疾用户。
  8. 用户管理:管理用户登录和访问限制。
  9. 计费:向用户收费。
  10. 浏览器和平台:支持的浏览器及其运行的操作系统(客户端)。还有应用运行的平台(服务器)。
  11. 安装:支持安装应用。
  12. 容量:并发用户数量、数据库中的数据量、报告大小、响应时间等。
  13. 文档:提供给用户、管理员和开发人员的内部和外部文档。
  14. 培训:针对用户、管理员、开发者。
  15. 支持和维护:持续支持(错误、功能请求、使用问题)和更新。
  16. 转换:从以前的系统或其他记录(电子或纸质)转换到新系统。
  17. 用例:参与者(人或其他系统)和应用之间交互的详细描述,产生对参与者有价值的结果。

始终包括所有 17 个部分,即使没有什么要做的(例如,计费或转换),在这种情况下,该要求将被表述为负面的(“将不支持计费。”).这可以防止客户错误地认为会包含某些内容。(“我知道我们没有明确指定培训,你这个笨蛋,但所有系统都有培训!”)此外,如果事实证明你不是唯一一个竞争这份工作的人,这将有助于确保你在一个公平的环境中竞争。

粗略的初稿:没有细节的范围

客户一开始并不知道他们所有的需求是什么。他们必须先看到系统。他们会看到一些他们绝对不喜欢的东西,这将有助于他们清楚地表达他们想要什么。这一直是正确的,也是敏捷方法的基石。

但是,在某种程度上,客户确实知道他们想要什么,这就是你需要在需求的初稿中捕捉到的。不知道报告应该是什么样的?那么报告部分可以说:“将会有报告。”不准备任何文档吗?然后说:“不会有文档。”这可能会导致客户说:“你说没有文档是什么意思?我们需要文档!”看,顾客确实知道他们想要什么,只是不知道细节。第一稿对应的是他们知道自己想要的东西,对于他们含糊不清的东西也是含糊其辞。你在项目开始的时候,在任何开发开始之前写下第一份草稿。这些不仅仅是及时的需求;它们是预先的要求。

对于许多部分,我只是不相信等到开发进行到一半,并能够试用半打的临时版本会帮助客户知道答案。我可以想出一个奇怪的场景,在这个场景中,直到有机会试用这个系统,客户才知道是否需要德语本地化,但是,实际上,在这个过程中什么也不会发生。跟营销有关,跟动手使用系统无关。对于外部接口、计费、安装、文档、转换和一些其他部分也是如此。这些决定可以在一开始就做出,也应该这样做,因为它们对设计和开发有着巨大的影响。

另一方面,任何与用户界面设计、报告和处理相关的东西都应该在开始时以最普通的方式指定。对于这些问题,需求应该随着开发人员和客户将一个活的(如果不完整的)系统作为实验室一起工作而发展。

在项目的开始和整个过程中,最重要的是范围被描述出来,所以很清楚应用应该解决多少问题,同样重要的是,它不会解决什么问题。它如何解决它应该解决的问题——细节—应该稍后指定,要么是因为直到后来对它了解得还不够,要么是因为开发人员直到后来才需要细节,或者两者都有。如果开发人员稍后才需要细节,比如到底需要什么样的报告,它们看起来像什么,那么最好等等。对细节的任何猜测都可能改变,当客户和开发人员都非常积极地在系统的这一部分工作时,最好在他们之间合作解决问题。当有这么多其他事情要考虑的时候,任何一方都不倾向于一开始就考虑这些细节。

以下是我对世界事务会议(CWA)系统的需求文档的初稿:

  1. 数据库:输入城市是人、小组、话题、场地、捐赠、房子、旅行。
  2. CRUD :每个实体的一个 web 页面,包含一个表单,每个表单中有一个字段。
  3. 处理:无。(应用几乎只是将数据从一个地方推到另一个地方,实际上并不做任何事情)。
  4. 报告:小组成员、Alpha 列表、稳定列表、Betty 表、Trips、住房。稍后将详细说明更多内容。样品已经提供。(阿尔法列表、稳定列表和贝蒂工作表是 CWA 术语;它们是什么意思并不重要)。
  5. 外部接口:无。完全独立。可能会有一个报告来获取数据,以填充 CWA 网站上的在线时间表,但这只是一个 CSV 文件。
  6. I18N 和 L10N :无;仅限英语。(虽然数据本身可以是 Unicode)。
  7. 可访问性:除了操作系统提供的功能(例如,更大的光标)。
  8. 用户管理:管理员和用户登录。没有更精细的限制。
  9. 计费:无。
  10. 浏览器和平台:MAC OS 上的 Safari 和 Chrome,Windows 上的 Internet Explorer 和 Chrome。仅最新的浏览器;不会花力气去支持非常老的浏览器。手机没什么特别的。如果它能在 iPhone 上运行,那很好;如果没有,运气不好。
  11. 安装:没有,除了科罗拉多大学管理服务部运行的单一生产系统。
  12. 容量:五到十个同时使用的用户。数据库必须保存 80 年的数据(CWA 始于 1948 年),每年有 100 名专门小组成员和 200 个专门小组。多达一万名捐赠者。
  13. 文件:无。
  14. 培训:没有正式的培训,但是开发者偶尔会和用户见面。
  15. 支持和维护:通过电子邮件提供支持。偶尔打个电话也可以。该系统将根据需要在未来几年进行更新和增强。
  16. 转换:从现有的 Excel 电子表格和 FileMaker 数据库。CWA 办事处将提取数据,并通过电子邮件发送给开发商。
  17. 用例 : TBD(待定)。(在第一稿中遗漏了,尽管我对该系统应该如何使用有一个非常清晰的想法,并有许多来自我要替换的系统的截图、报告和注释)。

答对了。的要求!只花了大约半个小时就写完了,它们的范围是完整的,没有遗漏任何东西。嗯,除了细节:CRUD 页面看起来像什么,报告是什么。

实际上,我在开始时对 CWA 人说的甚至没有 17 部分的需求文档详细:“系统将处理专家组、专门小组成员和其他人,以及捐款,它将生成所有你习惯拥有的报告。”这正是他们想听到的。

有了需求,我就投入到开发中。我从数据库设计开始,这也是您应该开始的地方。(提醒:本章和书的其余部分只讲 PHP/MySQL 应用。)然后,为了检查数据库,我进行了转换。有了数据库中的一些真实数据,我开始研究报告,使用以前会议的样本作为指南。那时,我非常确定数据库设计基本上没问题。随着开发的进行,必须对它进行修改,这是常有的事,但它基本上是正确的。

我没有向 CWA 办公室的工作人员出示任何报告,因为它们与工作人员提供给我的样本完全相同。我还没有准备好了解员工可能需要的新报告。通过以数据库为中心的设计,可以在不影响系统其他部分的情况下添加报告,所以我并不担心。

此时,我已经为 CRUD 页面做好了准备。我猜测了一下什么可能有用。每次我向用户展示我所拥有的东西时,他们都会提出改进的建议。这是开发中最耗时的部分,也是用户投入最多的部分。最终,我们拿出了一套他们满意的 CRUD 页面,我们采用了这些页面,并在他们使用系统时做了一些额外的调整。

报告也是如此。正如我所说的,我精确地复制了过去几年的报告(大部分来自 Excel ),当 CWA 的员工提出想法时,我们添加了半打额外的报告。我还放入了一个通用的基于 SQL 的查询工具,最初是供我自己使用的,但是助理 CWA 协调员非常喜欢它,她自学了 SQL 并开始使用它。她开发了一套大约十几个固定的查询,这些查询本可以由我按照她的要求开发的报告来处理,但是我不需要做任何事情。也许这种程度的主动性是大学助理协调员独有的,但你应该检查一下。你的用户可能比你想象的更有能力。

在这一章的结尾,我将把我的方法称为计划敏捷,我从 20 世纪 60 年代末就开始使用这种方法。它足够敏捷(小写的“a”),但是在任何开发开始之前有一个规划/设计阶段。

仔细查看需求部分

下面是对 17 个需求部分的一些附加注释。

数据库

这是第四章的主题。

污垢

对于我开发的每一个 PHP 应用,所有的 CRUD 页面都遵循一个模式:有一个简短的查询表单,有时只有一个字段(例如,姓氏),和一个搜索(或查找)按钮。单击该按钮将查询数据库并显示一个行(记录)列表,每一行都由最少量的数据汇总,如姓氏和名字,并有一个关联的详细信息按钮,每行一个。单击 Detail 显示该行的所有数据,此时用户可以读取数据(CRUD 中的 R )或更新数据( U )。细节按钮旁边还有一个删除行的按钮( D )。顶部的按钮显示一个空表单,用于创建( C )新行。

在开始时向客户展示一个 CRUD 页面的模型是一个好主意,可能在 PHP 文件中有一些样本数据,因为数据库可能还没有准备好。每个页面都有一个共同的布局,像一个标志,一个帮助按钮,和应用的关键部分的链接,你也可以展示一个模型。例如,图 2-1 显示了 CWA 主题页面的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-1 。主题 CRUD 页面

主题(小组成员谈论的内容)可以通过代码或年份进行检索。图 2-2 显示了您点击搜索时看到的部分内容:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-2 。检索到的主题

如果客户能看到 CRUD 交互的例子,他们就能想象出开发的方向。他们中的一些人习惯于桌面应用,可能从来没有使用过 web 数据库应用,所以他们适应 PHP/MySQL 应用是什么样子是很重要的。事实上,如果他们曾经在亚马逊上买过东西或者访问过脸书,他们就会使用这样的应用,但是也许他们从来没有意识到这一点。

我的 CWA 应用将会有一个比亚马逊或脸书少得多的用户界面,我希望我的客户也知道这一点。对于您将要构建的大多数简单的 PHP/MySQL 应用来说,并不需要大量复杂的 JavaScript 来使页面具有高度的交互性。你也不太可能想雇佣(或者没钱雇佣)世界级的图形设计师。我做自己的设计;它们很笨重,但很实用,而且我的客户也买得起。这是关于设定期望值。

处理

如果在这一部分中有任何事情,您将希望首先安排它的开发,因为复杂的处理需要未知数量的开发时间,并且需要未知数量的验收测试。它甚至可能是一个研究项目。

或者,也许不是。当我们开始为 Windows 构建 SuperSked 超市调度应用时,我们已经有了来自基于字符的 UNIX 系统的调度模块,我们所要做的就是将它从 Fortran 翻译成 c。这需要时间,但不涉及任何研究或实验,而且单元测试已经构建好了。

数量惊人的应用不做任何处理。它们只是垃圾和报告,CWA 系统就是这样。有些报告涉及到复杂的 SQL 和 PHP 计算来整理数据,但是我不会把它们归类为复杂到足以保证在处理部分。

报告

我在第七章讨论这个话题。

外部接口

这里我们讨论的是向应用提供数据的任何其他系统,或者应用必须提供数据的任何其他系统。在线提要往往难以实现。导入或导出数据文件更容易,因为您只需处理数据格式,而不必处理复杂的数据传输。

客户可能在项目开始时就知道这些是什么。在接下来的三个月或六个月的开发中,不会发生任何事情来揭示外部接口是什么。

也许您在开始时所能做的就是枚举接口。收集技术文档和组装第三方组件(例如,开放数据库连接(ODBC)驱动程序和数据格式库)可能需要时间,但至少您知道需要什么。

将与外部接口相关的任何事情都视为高风险,因为您永远不知道那些第三方组件会工作得多好,以及其他系统有多可靠。高风险建议您在计划的早期进行开发工作,这样您可以尽早得到所有的坏消息。

有时,唯一的外部接口是将数据以 CSV 或 XML 等易于处理的格式提供给另一个系统。在这种情况下,工作并不比报告复杂多少,但我仍然会将它列在这一部分,并尽早完成,因为在您尝试之前,您永远不知道下游系统将如何处理您的数据。此外,虽然 XML 定义良好,但 CSV 却不是。系统以各种方式处理逗号和引号,或者有时根本不处理。

I18N 和 L10N

国际化,或 I18N,意味着设计应用,使其可以本地化为一种语言和文化。通常,字符串是最大的问题,但是日期、时间、数字和货币单位也可能涉及到。

提供 I18N 机制使系统适应特定语言和文化所需的任何东西称为本地化(L10N)。您可以本地化任何应用,即使它不是为 I18N 设计的,方法是复制源代码并对其进行更改。但这是一种可怕的方式。如果本地化是可能的,你需要 I18N。

如果你在一开始就为它设计,I18N 是相当容易的,但是在应用完成之后再添加就太麻烦了。通常,处理字符串的方法是从表中取出用户界面上出现的任何内容,每个本地化版本都有自己的表。然而,有两个复杂因素。

  1. 几乎每种语言都比英语冗长,所以本地化的字符串会弄乱你的页面布局。
  2. 从右向左的语言可能需要特殊处理。

PHP 有本地化日期和时间的库函数,数字和货币单位也很容易处理。

一旦为 I18N 设计了应用,就必须为每个必需的地区提供本地化。这项工作通常由外部承包商完成,他们有一批能胜任这项工作的员工。试图通过使用谷歌翻译或依靠你的高中语言课程来廉价地自己完成它可能是一个坏主意。

可访问性

此部分应包含使应用可供残障人士使用的任何要求。构建这样的 web 应用并不难,因为真正的工作是由浏览器和运行它的操作系统(OS)来完成的。真正的问题是你是否有预算和时间对残疾用户进行测试,这是判断你的设计是否成功的唯一方法。

更多信息,谷歌“网页内容可访问性指南。”

用户管理员

你肯定想实现一个登录机制,我会在第六章中给你所有你需要的代码。复杂的部分是如果你需要不同类别的用户。例如,CWA 的一些数据是由学生输入的,尤其是捐款,这些数据是定期输入的。但我们不希望这些学生接触到小组成员的数据,其中大部分是保密的。

我们认为不同类别的用户对于第一版的 CWA 系统来说太复杂了,需求也说明了这一点。(在第七章中,我会解释如果你必须这么做,你会怎么做。)

演员表

使用可能按月或年、按会话、按访问的信息(例如,每个信用报告这么多)或以其他方式计费。如果你有这样做的需求,你将不得不实现必要的簿记。也许你还得开账单。这可能会变得非常复杂,所以请确保您明确地陈述了任何需求。

浏览器和平台

你通常不关心用户的电脑或操作系统,只关心他或她的浏览器。HTML、CSS 和 JavaScript 标准在过去的几年里有了很大的发展,所以支持任何比最新版本旧的浏览器都意味着额外的工作,包括实现和测试。即使进行了测试,如果用户的浏览器与开发团队的不同,问题还是会出现。

如果可能的话,只允许少量的浏览器,并且只允许最新版本的浏览器。如果客户在一个组织内,并且可以自由升级他们的计算机,这是可行的,但是如果网站对全世界开放,这是不切实际的。在这种情况下,您将面临许多实现问题和测试。所以你最好确保你陈述了任何支持老浏览器的需求。

在应用主要运行的服务器端,您关心 PHP 和 MySQL 版本。几乎可以肯定的是,web 服务器将会是 Apache 或 IIS,这一点无关紧要,操作系统也是如此,它将是某种形式的 UNIX(可能是 Linux 或 BSD)或 Windows。OS X 服务器或其他任何东西是非常罕见的。

为了简单起见,看看你是否能写出在服务器端指定 LAMP 的需求:Linux、Apache、MySQL 和 PHP。使用 BSD 而不是 Linux 没有关系,但是任何其他不同的东西都会导致通常可以避免并且应该避免的复杂性。

装置

让系统可安装是很难的,但是 web 应用的美妙之处在于它们通常不需要安装超过一次。如果可以,请详细说明。在第三章中,我将讨论如何搭建你需要的平台。这就是你想要的所有安装。如果系统必须是一个可安装的产品,确保它在需求中,这样你就可以安排额外的开发和测试。

容量

容量可能无关紧要,如 CWA 应用;或者重要但合理,如 Rgrade(成绩单应用);或者极具挑战性,如脸书(实际上是用 PHP 和 MySQL 实现的)。在任何情况下,你必须知道,这样你才能相应地计划。添加应用服务器没有问题,因为每次登录都是独立的。但是一旦数据库对于单个实例来说太大,事情就会变得非常复杂,大大增加了开发成本。

文件

文档—包含应用交付给用户的任何信息材料,包括帮助文件、在线手册、印刷书籍和快速参考卡,以及为将来维护提供的任何内部文档(代码注释除外)。

如今,内部文档非常罕见,我已经好几年没见过了。有时像 Doygen 这样的系统会自动生成文档,但这不算,因为它是自动生成的。有时特殊格式的注释被添加在每个函数的正上方;如果你这样做,确保它们随着代码的变化而更新。

写一本合适的用户手册,我已经做了很多,这是一个巨大的工作,所以,如果你已经承诺写一本,确保它的人员配备、时间安排和费用。帮助文件更简单,因为它们更短、更简洁,但是它们仍然需要时间来做好。

培养

对于一个内部的应用,用户希望得到培训,但这可能不是由开发人员进行的。大多数情况下,你会被要求培训用户组织中的一些关键人员,他们会进行实际的培训。你所做的培训可以是非正式的——不需要数百张幻灯片。

除非您的应用非常昂贵,否则商业用户不会期望任何培训。大多数情况下,有些人会希望看到一些用截屏工具和一些旁白拍摄的培训视频。

无论你计划做什么,确保它在需求中。

支持和维护

总会有支持和维护,除非你是员工,否则你通常会按开发费用的小时费率收费。在需求中可以这样表述。您需要特别注意的是,是否有任何 24/7 或周末可用性的预期、任何电话支持或任何其他不应留给客户想象的东西。

转换

关于转换有很多要说的,都在第八章中。

用例

这很重要;这将在本章后面的单独一节中讲述。

当要求改变时

注意是“当”,不是“如果”需求总是在开发期间甚至开发之后发生变化,因为世界在变化,随着系统的实现,对它需要做什么有了更好的理解。由于需求开始时缺少很多细节,它们最好改变,否则开发人员不知道该做什么。你可以推迟确定需求,但不能永远推迟。

在时间安排和人员配备之前或同时编写初始需求,会产生一个基线需求文档。这可以称为需求开发。随后改变基线的是需求管理。当需求变更时,您必须执行两个基本活动:记录变更和修改需求文档。

测井要求变更

在我在第一章中描述的问题跟踪系统中记录每一个需求变更的请求,这个系统用于记录 bug 和其他支持问题。这个过程至少有三个目的。

  1. 确保提议的变更不会放错地方或被忽略。
  2. 将其列入状态或计划会议的议程。(我通过问题跟踪组织所有此类会议)。
  3. 记录这一变化,以防将来有人想知道为什么进度落后。

我喜欢让事情变得非常简单,你已经知道了。只需记录收到的文本(例如,电子邮件的文本或从备忘录中复制的文本)、简短的标题(五到十个单词)、唯一的 ID(例如,1234、REQ-0123 或让跟踪者生成一个 ID)、日期、发件人、类别(“需求”)和状态(“建议”)。然后,如果获得批准,将其状态更改为“approved ”,并根据需要编写新的文本,以准确记录批准的内容。保持原文不变。我使用的问题跟踪器 HESK 不允许我定制状态,所以我给新的需求一个“进行中”的状态,然后当它们被批准时,将它们更改为“已解决”。

当需求被安排到一个特定的版本时,填写一个版本号字段。然后,问题日志成为发布内容的最终列表。修复的 bug 得到同样的待遇。

一个很大的错误是在问题跟踪器中定义了如此多的字段,以至于记录所有内容成为一种负担。不要忘记你的工作是实现应用,而不是因为记录你是如何做的而获奖。

修改需求文件

这里也一样,把记账保持在一个容易做的水平,以增加你实际做的机会。(反正我就是这样。)我喜欢让需求文档保持最新变得非常容易。

正如我在第四章中解释的,数据库设计中的一个重要原则是,相同的数据不应该出现两次,因为副本很容易失去同步。这也适用于这里:当问题已经在问题跟踪器中时,您不希望在需求文档中有问题的副本。因此,在您准备修改需求文档之前,只需通过 ID 引用一个新的需求。(这就像在数据库中使用外键一样。)当您修订文档时,问题跟踪者的副本不再是主要参考,并且应该以这种方式进行标记,这样您就知道了。在本节的剩余部分,我将一步一步地介绍所有这些内容。

为了使通过 ID 引用问题的需求变得更加容易阅读,从问题跟踪系统中生成一个批准的需求报告来伴随需求文档是非常方便的。对于读者来说,这仍然意味着要来回奔波。更好的方法是使用一个脚本,自动将跟踪程序中的问题文本插入到需求文档中。用运行在 Microsoft Word 中的 Visual Basic 脚本来实现这一点是可能的,尽管我还没有尝试过。如果您以纯文本的形式编写需求文档,事情会简单得多,正如我将要演示的那样。

请注意,这并不违反一份拷贝的规则,因为源文档只有一个对问题的引用。自动插入问题文本的组合文档仅供查看,不可重新编辑。只是一份报告。

举个例子,考虑清单 2-1 中的基线需求,它可能会被输入到文本编辑器中。

清单 2-1 。住房报告基线要求

Housing Report

One row for each participant or other person to be housed.

Columns: Name, Companion, Housing Committee Contact, Housers Names, Houser Street/ZIP, Houser Phone, Arrival Trip Details, Departure Trip Details, Days Here, Smoking OK, Pets OK, Participant Notes

See sample from last year for format and other details.

假设一个需求变更被批准。在 HESK issue tracker 中为 Issue 1553,如图图 2-3 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-3 。更改住房报告要求

当这个变更被批准时,需求被编辑以通过其 ID 引用问题,如清单 2-2 中的所示。

清单 2-2 。参考第 1553 期的房屋报告基线要求

Housing Report

One row for each participant or other person to be housed.

Columns: Name, Companion, Housing Committee Contact, Housers Names, Houser Street/ZIP, Houser Phone, Arrival Trip Details, Departure Trip Details, Days Here, Smoking OK, Pets OK, Participant Notes

See sample from last year for format and other details.

{Issue 1553}

现在来看最精彩的部分:由于所有的问题都在 MySQL 数据库中(这是我选择 HESK 的一个原因),而需求文档是一个文本文件,所以很容易编写一个结合两者的 PHP 程序,如清单 2-3 所示。

清单 2-3 。将问题插入需求文档

define(DB_USER, "rochkind_hesk");
define(DB_PASSWORD, "...");

$pdo = new PDO('mysql:host=localhost;dbname=rochkind_hesk',
  DB_USER, DB_PASSWORD);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$s = file_get_contents("CWA-requirements.txt");
$s = str_replace("\n", "<br>", $s);
while (preg_match('/^(.*)\{(\w+) (\d+)}(.*)$/s', $s, $m))
    $s = $m[1] . issue($m[2], $m[3]) . $m[4];
echo $s;

function issue($cmd, $n) {
    global $pdo;

    $stmt = $pdo->prepare("select id, subject, message from
      hesk_tickets where id = :id");
    $stmt->execute(array('id' => $n));
    if ($row = $stmt->fetch()) {
        if ($cmd == "Issue")
            return "
                <table border=1 cellspacing=0 cellpadding=10>
                <tr><td>
                <p><b>Issue {$row['id']}: {$row['subject']}</b>
                <p>{$row['message']}
                </table>
              ";
        else
            return "<br>Issue {$row['id']}: {$row['subject']}";
    }
    else
        return "<b>[Can't locate Issue $n]</b>";
}

PHP 程序用数据库中的数据替换了“{Issue 1553}”,输出如图图 2-4 所示,更容易阅读。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-4 。综合要求

作为一名高级 PHP 程序员,您应该能够看到这个程序在做什么,但这里有一个非常简短的演示:分配或引用变量$pdo的代码行打开一个到 HESK 数据库的 PDO 连接,并在函数issue内获取该问题的 ID、主题和消息。(我在第五章的中更多地谈到了 PDO。)

file_get_contents的调用从需求文档本身读取整个文本,下一行放入 HTML brk标签来维护段落。接下来是一个preg_match循环,用包含问题细节的 HTML 替换问题引用(例如,“{Issue 1553}”),这些细节由对函数issue的调用提供。然后输出处理后的文本。

该函数在数据库中查询问题数据,如果需要完整的问题,则将数据格式化为表格,否则只需要 ID 和主题。(后者我很快会解释。)

如果纯文本对您来说太简单了,对我来说也是如此,您可以使用 Markdown 来添加一些格式,为文本添加标题标记、加粗和一些其他修饰。(详见daringfireball.net/projects/markdown。)清单 2-4 显示了添加了一些降价的需求(###**)。请注意,即使有分散的减价注释,文档仍然完全可读。

清单 2-4 。添加了降价的房屋报告要求

### Housing Report

One row for each participant or other person to be housed.

**Columns:** Name, Companion, Housing Committee Contact, Housers Names, Houser Street/ZIP, Houser Phone, Arrival Trip Details, Departure Trip Details, Days Here, Smoking OK, Pets OK, Participant Notes

See sample from last year for format and other details.

{Full 1553}

你可以在michelf.ca/projects/php-markdown使用免费的 PHP 实现 Markdown。(它也包含在本书在www.apress.com的可下载资源中。)对 PHP 程序进行处理 Markdown 的更改是微不足道的。该文件必须包含在顶部

require_once 'markdown.php';

这两条线

$s = file_get_contents("CWA-requirements.txt");
$s = str_replace("\n", "<br>", $s);

都变成了单行

$s = Markdown(file_get_contents("CWA-requirements.txt"));

因为当出现空行时,Markdown 会自动开始一个新段落。图 2-5 显示了新的输出,现在已经格式化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-5 。输出格式 ted with Markdown

即使问题在需求中内联扩展,如果有很多变化,文档仍然可能变成不可读的拼凑物,这是肯定会有的。您可能希望在某个时候抽出时间,制作另一个版本,将所有的更改直接合并到文本中。然而,参考这些问题仍然是一个好主意。一个好的方法是在问题引用中使用“Title”而不是“issue”(加粗),如清单 2-5 所示,输出如图图 2-6 所示。

清单 2-5 。更新住房报告要求。

### Housing Report

One row for each participant or other person to be housed.

**Columns:** Name, Companion, Housing Committee Contact, Housers Names, Houser Street/ZIP, Houser Phone, Arrival Trip Details, Departure Trip Details, Days Here, Smoking OK, Pets OK, Participant Notes

Where a companion has a different arrival and/or departure flight, show that info separately in the trip columns.

See sample from last year for format and other details.

*This requirement incorporates the following issues:*
{Title 1553}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-6 。更新住房报告要求

正如我所说的,一旦一个问题被合并到需求文档的修订中,它在问题跟踪器中的文本仅仅是为了历史的目的;这份文件是权威。如果必须修改需求,必须创建新的问题。我在 HESK 中添加了一个名为“InDoc”的自定义字段,以记录已经合并到文档中的问题,因此不再是主要参考。

这里显示的小 PHP 实用程序是一个很好的例子,说明了一点点代码就可以产生巨大的生产力差异。想象一下,对于每一个问题引用,都必须引用问题跟踪程序,甚至是从中生成的报告。需求文档中只引用了几个问题,这还不错,但是在现实世界中,会有上千个问题。那将是无法忍受的。

让我写这个小程序可行的是关于我如何表示需求的两个决定。

  • 需求文档是一个纯文本文件,用 Markdown 扩充。
  • 需求变更存储在 MySQL 数据库中,可以从 PHP 轻松访问。

全部都是用完全免费的软件完成的,几乎没有什么机制。正如建筑师密斯·凡·德罗所说,“少即是多。”扔掉微软的 Word,用一个专有的数据库把自己从昂贵的问题跟踪器中解放出来,你就真的有所收获了。

这里有另一种说法:所有的工程文档,包括需求文档,也应该能够被视为数据,任何数据库都应该允许从 PHP(或其他脚本语言)访问。不允许专有格式。

用例

PHP/MySQL 应用不会只是坐在那里。它通常被人类使用,但有时也会被其他系统使用。一个人或系统(?? 角色)和应用之间的交互的详细描述被称为用例。收集足够多的这些,你就有了一个完整的系统应该如何使用的图片。

例如,这里有一个老师记录成绩的 Rgrade(成绩单系统)的用例。

  1. 前提条件:安装了 Rgrade,教师设置使用它,学生在系统中,教师已经决定给什么分数。
  2. 登录 Rgrade。
  3. 导航到学生列表,按姓氏和名字(如有必要)查找学生。
  4. 导航到学生的成绩单。
  5. 找到类别和评级期间。
  6. 输入或更改等级。
  7. 保存表单,除非这是自动的。
  8. 验证是否输入了正确的等级。
  9. 后置条件:记录学生的成绩。

请注意,教师执行的实际步骤前面是前提条件,在交互发生之前假设为真,后面是后置条件,在交互之后为真。事实上,实现后置条件是交互的全部目的。

我并不知道这些步骤如何出现在帮助页面或培训手册中,因为术语不太正确(“类别”、“评分周期”、“表格”等)。).但它确实抓住了教师和升级应用之间最重要的互动。

其他 Rgrade 用例涵盖了如下交互:

  • 添加不在列表中的学生。
  • 删除学生。
  • 更改学生姓名。
  • 输入教师的评论。
  • 生成草稿报告卡。
  • 打印最终报告卡。

通用建模语言(UML),一个你可以在uml.org阅读的国际标准,提供了一个用例的符号,如果你了解 UML,你可以使用它。但是写下用例比使用特定的形式来写它们更重要,而且我自己从来没有使用过 UML。编号的步骤将很适合您的用例。(不要让一种你必须以绝对正确的方式做每件事的感觉威胁到你根本不做事!)

除了有足够的用例来涵盖重要的交互之外,同样重要的是确保所有的步骤都在那里,不留任何假设。请注意,在前面的示例中,我有登录、定位类别和评分周期以及验证结果的步骤,所有这些都是假设的。现在不是言简意赅的时候。

用例本身进入需求文档的第十七部分,作为它们自己的需求。此外,它们还用于检查其他需求。你应该做的,无论是单独还是与团队一起,是慢慢地检查每个用例的每个步骤,确保一个或多个需求覆盖了执行每个步骤所必需的所有系统功能。然后根据需要添加需求。例如,步骤 6,“输入或更改成绩”,意味着必须有一个要求,提供一些编辑数据的方法。像“成绩单上的所有分数都应该是可编辑的”这样的要求就达到了目的。这个例子可能太明显了,但是实践中出现的其他例子更加微妙,如果您忽略了需求,会在以后引起问题。

一个涉及用例的完全独立的活动是确保数据库设计中有实体和属性(表和列)来支持每个用例的每一步。即使这个简单的例子也需要像教师、学生和成绩单这样的实体。类别和分级周期可能是实体,也可能是属性。当然,成绩也是需要的。因为用户登录发生在用例中,所以数据库中需要为此准备一些东西。

使用用例来检查其他需求不会完全捕获用例中的所有含义,这就是为什么它们被单独放入需求中的原因。

用例的另一个伟大之处,在其他种类的需求中是独一无二的,那就是它们是有人物和情节的小故事。因此它们更容易被应用所面向的人所理解。你可以花两个小时谈论 CRUD 需求,几乎得不到任何评论,但是当你开始浏览一个用例时,你会在最初的五分钟内被类似下面的评论所阻止:“如果两个学生有相同的名字怎么办?”或者“我们有时不得不在办公室将新生完全注册并录入系统之前给他们打分。”当你还在明确需求的时候,揭露这些混乱的现实,要比在老师们开始尝试这个系统,并决定这个系统一定是由来自德克萨斯之外的某个甚至没有在小学教过书的家伙实现的时候,要好得多。(哦,等等,原来是!)

同样有益的是像“系统不需要这样做”这样的评论。我们自己就是这样做的。”如果这是官方的说法,那么您已经节省了大量的开发时间。

总之:用例可能是需求中最重要的部分。

需求战争故事

这里有两个关于需求的真实故事,一个悲伤,一个快乐。具有讽刺意味的是,在 sad 中,需求是完整的、清晰的,并且被很好地表达出来。在快乐的一个,要求是摇摇欲坠的。悲伤的先走。我已经改了相关人员的名字。

逃跑的开发商

回到我在 1987 年创办的用户界面工具公司 XVT 软件公司,我们需要一个小的子系统,各种模块可以调用它来获取参数值。这种事情现在可以用 XML 来处理,但是,正如当时常见的那样,我们发明了自己简单的属性值语言来表示参数(例如,像 Windows INI 文件或 Mac OS plist 文件)。在几位受过高等教育的计算机科学家的参与下,我们正式指定了这种语言,没有留下任何回旋的余地。这些是完整的、清晰的、清晰的需求。

负责编写读入和访问参数的代码的开发人员开始着手这项工作,但是几个星期后他仍在从事这项工作。我认为以他的天赋,一个程序员应该能够在最多两天内编写一个小的解析器,将参数放入哈希表或类似的东西中,然后就完成了。我们是一家小公司,这只是整个系统中很小的一部分。我自己也在不到一天的时间里编写了类似任务的代码。

所以我叫他来聊天。他对自己的工作非常自豪,但还没有完全完成。我请他告诉我他是如何着手这件事的。原来他所做的根本不是编译我们简化的参数语言。他已经定义了一种描述输入规范的元语言,并且正在为那个编写一个编译器,以及一个他发明的中间语言的解释器。然后,完成了这些,他所要做的就是用他的元语言写下我们语言的规范,然后,很快!,他早就玩完了。

我不好意思说我发脾气了。我不应该,因为这是我的错。他只是做他自己。

这个故事的寓意是什么?落实要求就挺够了。一个聪明的程序员可能会超越一点,做一些明显的概括,比如如果需求是两个,就允许五个电话号码。但是,在没有这种需求的情况下发明和实现一种新的编程语言,这种做法太过分了。足够多的这种失控的开发会扼杀一个项目。

(在敏捷世界中,这被称为 YAGNI,意思是“你不需要它”,但是,当真正实践时,这就走了另一条路。有时几个需求可以被吸收到同一个通用设备中。)

阿尔扎诺牧场

我的一个熟人迈克找到我和我们共同的朋友艾伦,问我们是否愿意和他一起为一位名叫埃德·阿尔扎诺的航空公司顾问做一个小小的编程项目。基于迈克的哥哥,一位应用数学家所做的一些理论工作,埃德开发了一种新的飞机座位定价方法,以实现收入最大化。非常舒适。

我同意了,并告诉迈克我的时薪。他告诉我要多收很多钱,所以我就多收了。我们都是。

与艾德的第一次会面进行得很顺利。他概述了他需要的东西,我们都马上着手去做。迈克研究算法,接受数学训练,艾伦研究简单的用户界面,我研究后端数据管理。几个星期后,我们就完成了。我们给艾德看了,他很喜欢。他把它拿下来给一家航空公司的运营专家看,我想那家公司是位于丹佛的 Frontier。他们也很喜欢。

埃德非常激动,当我们再次与他见面时,他提出了更多的想法。我们开始修改系统。同样的事情:我们按时完成了,ed 很喜欢,然后他去演示他的发明。

下一次和艾德见面,重播。更多的变化,更多的发展,更多的示范。

几个月以来,一直如此。艾德总是很快乐,但从不满足。不是因为我们让他失望,而是因为我们的系统激励他提出新的想法。

您可能会认为几个月都没有完成,而每两周就有需求变更是一件坏事,但事实并非如此。我们喜欢这份工作,艾德正在取得真正的进步。他也是个好人。看到项目结束,我们很难过。(几年后,我听说他创办了自己的航空公司)。

我们在这场演出中赚了很多钱,以至于我们在科罗拉多州的布雷肯里奇买了一套滑雪公寓。整件事——不是分时度假。当然,我们把它命名为阿尔扎诺牧场。

寓意:这一切都发生在 20 世纪 80 年代中期;如果我想到称之为敏捷开发,我可能已经是某个人了。

好吧,一个更严肃的道德:如果需求的改变是因为现实在改变,而不是因为你没有记录它们,那么就随波逐流吧。回想一下第一章中的内容,让客户满意是项目成功的关键。

敏捷需求

在现实世界的项目中,比如建造一座桥,变更的成本在后期会变得更高,一旦开始施工就会变得令人望而却步。因此,关键阶段——需求、设计、建造、验证——必须严格按照这个顺序进行,并且在开始下一阶段之前,每个阶段都必须 100%完成,尽可能完美。

在软件开发的早期,遵循了相同的方法,在构建和验证之间增加了一个集成阶段,因为软件通常是以模块的形式开发的(有时桥也是如此)。这个就是所谓的瀑布模型,之所以这样命名是因为进步就像水从岩石上落下一样从一个阶段流向另一个阶段。

就我个人而言,在我 45 年的软件开发生涯中,我从未参与过完全遵循瀑布模型的项目,尽管我参与过许多将集成和验证(测试)保存到最后的项目,正如你所料,这总是一场灾难。

瀑布方法或者其他类似方法的另一个问题是,处理需求很困难。正如我在本章中所展示的,一开始就知道所有的需求是不可能的,而且,对于像用户界面这样的领域,假装知道是有害的。然而,你确实知道很多,而且你所知道的肯定应该在一开始就被记录下来。但是在开发过程中,必须有一种方法来结合需求的演化,或者可能是彻底的改变。

大约十几年前,一群开发人员将敏捷软件开发正式化,直接反对瀑布方法,主要是为了处理我提到的两个问题:进化需求和最终的集成/测试。虽然有很多不同的敏捷方法,但最重要的是

  1. 将项目分成非常短的(比如一周)固定长度的增量,在每个增量的末尾有一个可交付的系统。
  2. 只为每个增量建立需求,因此允许在整个项目中任意的改变。
  3. 与客户代表持续沟通,最好是在开发团队中。
  4. 开发团队成员之间的日常交流。
  5. 连续单元测试和集成。

多年来,我一直在实践#1、#3、#4 和#5 的变体,却对敏捷方法一无所知。在 20 世纪 70 年代中期,我们贝尔实验室的一群人开发了程序员工作台,它使用当时新的 UNIX 系统作为大型机程序员使用的开发工具的平台。在开始的时候,几个刚完成一个大型军事项目并希望形式化需求的开发人员和包括我在内的几个人之间有一场激烈的争论,他们对如何处理软件项目的态度要宽松得多。我们赢了。

所以,当我了解敏捷方法时,我就像莫里哀的中的角色,那个资产阶级绅士发现自己“一生都在说散文,却不自知!”

敏捷中让我感到不舒服的部分是,你一次只能开发一周的需求。真的吗?是的,是真的。敏捷知识分子对需求只有蔑视。

例如,在他的书敏捷武士中,Jonathan Rasmusson 说,“无论你收集什么需求,都保证会改变。”他的意思是,如果 CWA 办公室告诉我,他们需要参与者提交的主题列表,格式与他们多年来使用的格式完全相同,那么主题列表的格式肯定会改变?学校董事会批准的理查森学区成绩单规范肯定会改变?数百家 AP、Kroger 和 Safeway 商店张贴的超市收银员轮班时间表肯定会改变?不,这些不会改变,它们应该在项目开始时与其他已知的东西一起记录下来。在任何编码开始之前,它们应该是初始规划、分析和设计的一部分。

这里还有另一个例子:在关于敏捷方法的开创性著作中,极限编程解释道:拥抱变化,Kent Beck 说,“在用于描述需求的数千页中,如果你交付了正确的 5%、10%或 20 %,你将有可能实现为整个系统设想的所有商业利益。那么剩下的 80%是什么呢?不是要求——它们不是强制性的或必须的。”我想不出有哪一个项目能够满足 20%的需求,甚至 75%的需求。我可以跳过年级的哪一部分?列出所有的学生?教师登录?输入成绩?允许老师评论?打印成绩单?不,在学区部署系统之前,我必须实现 100%的要求。作为商业软件,SuperSked 可能已经满足了 90%的需求。CWA 数据库项目,可能也是 90%,因为我们已经在第一年将其精简到最低限度,明年我们会做得更多。

因此,当谈到需求时,我认为敏捷作者和顾问要么夸大了戏剧性的效果,要么他们真的相信这就是软件项目应该运行的方式。如果是后者,他们就大错特错了。

总之,足够的敏捷抨击。如果你正在使用敏捷方法,我将只提出我对你应该如何处理需求的看法,而把整理争论的工作留给下一次。(我希望我已经说得很清楚了,当涉及到客户交流、团队交流、单元测试、持续集成和频繁交付时,敏捷人员完全在正确的轨道上。)

用图来做吧。首先,图 2-7 显示了一个严格的瀑布序列,图 2-8 显示了敏捷迭代。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-7 。瀑布项目

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-8 。敏捷项目

有一些严格的瀑布方法可能适用的项目,以及非常小的、非正式的项目,比如我前面描述的 Arzano Ranch,适合严格的敏捷方法。但是,一般来说,没有一个是合适的:瀑布式太死板和理想化了,敏捷缺乏整体计划。没有整体计划,就没有办法估计完成日期或预算,没有办法提出一致的数据库设计,也没有办法通过用通用编码处理类似的功能来利用开发,除非这些功能出现在同一次迭代中。

实际上,我不相信任何项目会使用严格的敏捷方法,尽管大师们宣扬什么。图 2-9 展示了项目真正做的事情,以及我控制项目时一直做的事情。由于每个人都喜欢想出自命不凡的名字,我将把我的方法称为计划敏捷:你从计划/分析/设计阶段开始,在这个阶段你处理高层次的需求,但是你迭代地做低层次的计划/分析/设计工作。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-9 。计划中的敏捷项目

重复我自己,我并不声称计划的敏捷方法是原创的。恰恰相反:几乎所有的敏捷项目都是这样做的,但是出于某种原因,他们不愿意承认这一点。

一周的迭代不一定是一周;对于许多项目和团队来说,太短是没有效率的,就像十字路口的交通灯太短是没有效率的,因为它需要时间来进行下一次迭代。

在最初的计划/分析/设计阶段发生了什么?尽可能地记录需求,列举用例,根据需求运行用例,设计数据库,构建实现,决定平台和工具(第三章)。在整个项目中,当团队成员专注于他们的迭代时,有人——不一定是整个团队——必须管理整个计划。在敏捷术语中,每个迭代的计划来自于从 backlog 中选择故事。在有计划的敏捷中,有一些是这样的,但是也意识到整体计划应该决定攻击的顺序。

如果你不相信我,有计划的敏捷是敏捷项目实际做的,或者应该做的,那就去看看两本权威的书,虽然读起来冗长乏味,但它们强调敏捷需要一个全面的计划。

  • Dean Leffingwell,敏捷软件需求:团队、项目和企业的精益需求实践 (2010)。
  • 巴里·博姆和理查德·特纳,平衡敏捷性和纪律性:困惑者指南 (2003)。

敏捷的其他部分呢:结对编程、每日 scrum、sprints、烧毁图表等等?我没有尝试过其中的大部分,但我猜它们是有效的工作方式。与拥有一个强大的团队、与客户一起工作以获得正确的需求以及持续集成的效果相比,无论您做它们还是其他事情,都不会对项目产生太大的影响。这是三大趋势。

章节总结

  • 对于 PHP/MySQL 项目,您可以将您的需求文档分为 17 个部分(参见前面的详细内容)。
  • 最初的需求应该确定项目的范围,但不一定包括所有的细节。
  • 用例是需求中最重要的部分。
  • 在问题跟踪器中记录所有需求变更。
  • 将需求保存在引用需求变更问题的文本文件中。
  • 定期修改需求文档以包含变更,但仍然引用相关的问题。
  • 敏捷软件开发是一个很好的方法,但是它应该在项目的开始和整个过程中通过计划/分析/设计来增强。

三、平台和工具

如果你建造了空中楼阁,你的工作不一定会失败;那是他们应该在的地方。现在把地基放在它们下面。

—亨利·大卫·梭罗

如果城堡是需求,那么它们下面的基础就是实现它们的平台。该平台有四个主要部分:操作系统、web 服务器、数据库(MySQL)和语言编译器/解释器(PHP)。那在服务器上。因为这些是 web 应用,所以它们是从另一个平台访问的,客户端有两个主要部分:操作系统和浏览器。您在运行您的开发工具的第三方平台上进行开发。

在本章中,我将介绍所有三个平台,并讨论每个组件的各种选择。我还谈到了开发人员工具和将正在运行的应用更新到新版本的棘手工作。这构成了本书其余部分的基础,其余部分是关于开发应用本身的。

如您所见,我喜欢主流技术,平台和工具也是如此。它们要么已经安装,要么很容易安装,并得到书籍和网站的良好支持,几乎所有的错误都在咬我之前被压扁了。

客户端-服务器架构

图 3-1 显示了一个典型的 PHP/MySQL 客户端-服务器架构,以及用于构建和测试它的开发平台。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-1 。客户端-服务器-开发架构

服务器上涉及两个进程(或任务):数据库(对我们来说是 MySQL)和 web 服务器(通常是 Apache 或 Microsoft IIS)。PHP 处理器在 web 服务器的控制下运行,并执行组成应用的 PHP 文件。服务器框中的四个标签对应着所谓 LAMP 栈的元素:操作系统(Linux)、web 服务器(Apache)、数据库(MySQL)和语言(PHP)。正如我将要解释的,第一个不一定是 Linux,第二个不一定是 Apache。一般来说,最后两个不一定是 MySQL 和 PHP,但它们在本书中,因为这是我们的重点。

通常有许多应用在客户端运行,但是我们只关心连接到运行 PHP 应用的 web 服务器的浏览器。

因为您是开发人员,所以您也关心开发平台,它至少由两个基本的应用组成:一个可以创建和修改 PHP 文件的编辑器和一个可以将这些文件复制到 web 服务器的传输实用程序,通常是 FTP(文件传输协议)或 SFTP(安全文件传输协议)实用程序,有时内置于编辑器中。

在开发系统上复制整个服务器平台是很方便的,这样 PHP 文件就可以被编辑器直接访问,这样你就可以在本地运行应用。为此,有必要在开发计算机上安装一个服务器平台,然后在该计算机上打开一个浏览器与应用进行交互。图 3-2 说明了这一过程。当应用准备好部署时,一个 FTP 实用程序将 PHP 文件复制到远程服务器,如图图 3-1 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-2 。具有本地服务器的开发系统

这是对正在发生的事情的高度概括。本章的其余部分是关于细节的。

服务器平台

当然,服务器平台运行在操作系统上,而在操作系统上运行着 web 服务器和数据库系统 MySQL。对我们来说,web 服务器是用 PHP 编程的,我将给出为什么这几乎总是我的选择,以及许多其他人的选择的原因。

灯组

“LAMP”是 Linux-Apache-MySQL-PHP 栈的一个聪明的术语,但严格来说它不是一个栈,因为,虽然 web 服务器肯定运行在操作系统下,PHP 运行在 web 服务器上,但数据库直接运行在操作系统上,独立于 web 服务器。(另外两种流行的语言也是以字母 P 开头的:PERL 和 Python,所以有时候 LAMP 中的 P 就是指其中的一种。)

我对术语“LAMP”还有另一个挑剔之处:在实践中,操作系统(OS)是什么形式的 UNIX 并没有太大的区别——就我作为应用开发人员而言,BSD 和 Solaris 的行为类似于 Linux,尽管在如何管理、购买和部署这些系统方面存在显著差异。Windows 也是一个常用的服务器操作系统,尽管这个选择会对你的应用有所影响。

最后,根据w3techs.com的数据,虽然 Apache 运行着超过 60%的网站,但剩下的 40%大部分被微软的 IIS 和 Nginx 瓜分。与操作系统一样,您的编码不会受到太大影响,但是与应用如何设置相关的一些问题会受到影响,我将对此进行解释。

我所有的例子都是针对类 UNIX 操作系统和 Apache 的,我会确保在关键的时候我清楚这一点。

所以,本质上,我们关心的是 LAMP 的 MP 部分,P 代表 PHP。本书中 99%的内容适用于任何运行 PHP/MySQL 的平台。

服务器操作系统

根据到w3techs.com的数据,65%的网站运行在某种形式的 UNIX 上,另外 35%运行在 Windows 上。外面真的没有别的东西了。(他们单独列出了 Mac OS,但就 PHP/MySQL 开发而言是 UNIX。)

我将各种 UNIX 变体称为 *nix 系统。购买、支付、安装和管理操作系统是不同的,这取决于它实际上是何种形式的nix。Linux、BSD、Solaris 和其他版本是不同的,甚至各种 Linux 发行版(Red Hat、Ubuntu、Debian 等)之间也有差异。).但是,从 PHP 程序来看,所有的nix 系统都是一样的。你所要担心的是你是在其中一个上还是在 Windows 上。

*nix/Windows 在 PHP 级别上有四个不同之处。

  1. 路径和文件名的差异。
  2. 文本文件中不同的行尾。
  3. 影响一些 PHP 函数的 API(应用接口)差异。它们在 PHP 文档中有明确的说明。
  4. 从 PHP 程序或直接从 shell 或命令处理器执行的命令行。

主要路径和文件名的区别如下:

  • Windows 总是不区分大小写,但*nix 通常不是。为了确保你的程序在任一个上运行,总是要区分大小写。如果文件名是login.php,就不要用Login.PHP来指代。
  • 在绝对路径中,您可能必须在 Windows 上使用驱动器号(例如,D:/site/login.php)。
  • 在大多数 PHP 函数中,Windows 接受路径中的正斜杠,但是在用户交互提供的路径中或者当您从文件中读取路径时,您可能会得到一个反斜杠。每当我在 Windows 上输入路径时,我通常会将反斜杠转换成正斜杠。

原生 Windows 文本文件使用回车/换行符(\r\n)作为行尾,其中nix 只使用一个换行符。然而,这两种格式在两种系统上都是通用的,所以这实际上不是一个nix/Windows 问题;这是一个你需要一直关注的问题。

我在运行于 Mac OS (a *nix 系统)和 Windows 系统上的本地应用中处理了很多 Mac OS 和 Windows 系统的差异,但从未在我的 PHP/MySQL 应用中处理过,因为我设法避免在 Windows 服务器上运行。然而,你的生活可能没那么简单。

如果你从众多商业共享主机公司中的一家获得商业虚拟主机,他们几乎总是会使用 Linux 或 BSD,有时 Windows 是额外收费的选择。坚持使用更便宜的 Linux 或 BSD 主机。

网络服务器

尽管 Apache 也可以在 Windows 上运行,但在*nix 系统上,您几乎总是将 Apache 用作 web 服务器,在 Windows 系统上,您总是将 IIS 用作 web 服务器。

Apache 配置很难学,但是对 PHP/MySQL 程序员来说有两个好处。

  • 除了偶尔编辑一个.htaccess文件来为一个目录建立选项之外,很少需要直接使用 Apache。
  • Apache 的应用如此广泛,以至于如果你在谷歌上搜索你正在处理的任何问题,你通常都会找到答案。你不会一个人受苦。

撇开可用性问题不谈,Apache 高效、可靠、廉价、文档完善且无处不在,因此它是我的首选 web 服务器,优势非常明显。

Apache 的主要接口是它所使用的文件系统。每个网站在服务器上都有一个文档根目录,你的 PHP 文件需要放在这个根目录下,或者它的子目录中。例如,在我的主网站basepath.com上,文档根目录是

/home/rochkind/public_html

如果我用 FTP 实用程序将文件login.php复制到那个目录,我可以通过请求 URL 从浏览器运行那个 PHP 程序。

http://basepath.com/login.php

我通常在我的网站上运行许多应用,所以我把它们放在文档根目录下的子目录中,然后通过域名后面的路径将用户定向到一个 URL。例如,我的站点 Classic Cameras 位于/home/rochkind/public_html/ClassicCameras,因此 URL 是http://basepath.com/ClassicCameras。(试试吧——这是一个真实的网站。)

我可以将 Apache 配置为将 URL http://ClassicCameras.basepath.com指向站点/home/rochkind/public_html/ClassicCameras,但是我通常不会这么做,因为链接总是出现在某个网页或电子邮件中,我的客户并不真正关心 URL 的外观。

如我所说,*nix 系统通常使用区分大小写的文件名,因此,虽然 URL 的域部分总是不区分大小写,但路径部分却不区分。所以,http://basepath.com/classiccameras是行不通的。如果这是一个问题,您也可以创建那个目录,这样您的网站上就有了大写和全小写的目录,然后用一个名为 index.php 的 PHP 文件将小写的目录重定向到真实的目录,如下所示:

<?php header('Location: http://basepath.com/ClassicCameras/ '); ?>

classiccameras重定向到ClassicCameras的一种更优雅的方式是将 Apache 配置为在根目录下名为.htaccess的文件中放置一个重写规则,其中包含以下内容:

RewriteEngine on
RewriteRule ^classiccameras/?$ ClassicCameras/

这意味着,如果域名(basepath.com)后面的 URL 匹配classiccameras(可选地后跟一个斜杠),它应该被替换为ClassicCameras/,这是一个区分大小写的名称。(在正则表达式符号中,^$在开头和结尾锚定匹配,以防止只匹配用户键入的部分内容。)

总结这一节:有时您必须在某种程度上处理 Apache(或您的 web 服务器),但在大多数情况下,您可以不去管它,完全用 PHP 做您需要做的一切。

数据库系统

有许多用于 web 应用的 SQL 数据库系统,我使用过所有主要的系统,包括 Microsoft SQL Server、Oracle、IBM DB2、PostgreSQL,当然还有 MySQL。前三个是优秀的商业系统。PostgreSQL 是一个开源系统,其起源比 MySQL 早得多,但它的使用不如 MySQL 广泛,尽管许多托管公司提供它作为一个选项。

在过去,MySQL 支持如此有限的 SQL 形式,以至于对于被 Oracle 或 PostgreSQL 等更完整的系统宠坏的数据库专业人员来说,使用它很烦人。但是最近的版本改变了这一点,我现在发现它拥有我想要的一切,除了检查条件。我在第四章中对此进行了更深入的探讨。

我更喜欢 MySQL 的原因很简单,如果我只使用一套平台技术,我会觉得生活更容易,而且因为 MySQL 总是在那里,并且工作得非常好,它总是我的第一选择。我在这本书里说的关于数据库的很多内容也适用于其他书,但是我认为如果这本书只讨论一个数据库系统,它会更容易阅读,也更有用。我在前两章中提到过几次的 Rgrade 应用使用了 Oracle,这是一个很好的工具。它很贵,但是在我到那里之前,理查森学区已经标准化了。对理查森来说是好事。

Sun Microsystems 在 2008 年收购了 MySQL,大约两年后甲骨文收购了 Sun,所以现在,有点讽刺的是,甲骨文拥有 MySQL。尽管有人担心 Oracle 可能会忽视 MySQL 的开发和/或支持,以免影响 Oracle 的销售,但它并没有这样做,MySQL 仍然一如既往地可行。尽管如此,这种情况还是让人有些不安,所以 MySQL 的原始作者们采用了开源的 MySQL 代码,并开发了一个名为 MariaDB 的兼容系统,旨在与它实现二进制兼容。由于 MySQL 仍然是托管公司和云服务器最广泛支持的版本,所以我用的是这个版本。

尽你所能确保你使用的至少是 MySQL 5.5 版本,因为这是我在本书中假设你拥有的版本。这是 MySQL 在 2013 年年中撰写本文时的状态。当然很快就要上 5.6 了,以此类推。如果您的应用是新的,从最新的稳定版本开始。

非常高性能的网站不会向数据库发送 SQL 查询,因为这需要太多的处理时间,并且很难缓存结果以供其他查询重用。他们使用所谓的 NoSQL 数据库,如 MongoDB 或 CouchDB。使用它们超出了本书的范围,所以我不会深入讨论它们。对于普通的 web 应用,您希望使用 SQL。MySQL 的性能将绰绰有余。

服务器编程语言

几年前,在一次联邦法庭诉讼中,我作为一名技术专家被免职,当时有人问我使用过什么编程语言。我想我一定有超过 30 个名字,因为我从 1967 年就开始编程了。我已经使用了所有主要的工具,以及许多外来的工具,比如 APL、Algol、B(C 的前身)、LISP、Lua、PL/I、Scratch 和 SNOBOL4。我用大多数流行的网站语言编程,比如 Java、PERL 和 Python。

那么,为什么是 PHP 呢?毫无疑问,它远不如 Java、Python 或 Ruby 优雅。在每个变量名前使用一个$是荒谬的。函数名乱七八糟。我想自从我开始使用这种语言以来,他们已经改变了推荐的 MySQL 函数至少三四次。(我将在本书中使用 PDO。)语言本身还可以,但是做同样事情的方式太多,显得臃肿。

好吧,这就是不好的地方。这是好东西。

  • PHP 一直都在。我从来没有发现一个托管公司不提供它。Java 有时是额外付费的选择,如果它可用的话,而 Python 和 Ruby 通常是不可用的。PERL 和 PHP 一样普遍,但它是一种更糟糕的语言。
  • 它很快。它被广泛使用,因此有很多优化,尤其是在与 Apache 一起使用时。
  • 它有一个非凡的扩展集合,允许它处理几乎任何 web 应用。
  • 每个网络服务(亚马逊、脸书、Flickr 等。)有一个 PHP 接口。如果不支持 PHP,他们知道他们的支持是不完整的。相比之下,他们可以忽略 Python 和 Ruby。
  • PHP 社区是所有编程语言中最大的,这意味着有书(像这本!)、培训课程、论坛,以及像stackoverflow.com(我的最爱)这样的支持网站上的无数帖子。

基于这些原因,w3techs.com报告称,PHP 在几乎 80%的网站上使用。(.NET 是 20%,Java 是 4%;有些网站使用多种语言。)

所以,既然我可以并且已经使用了几乎所有存在过的语言,那么 PHP 的答案是,它使用起来足够愉快,总是可用,非常好的支持,并且几乎总是有一个函数来做需要做的事情。我喜欢把事情做完。

你还将使用另外三种语言,因为 web 应用开发人员总是使用至少四种语言。另外三个是

  • HTML(包括 CSS),
  • JavaScript,以及
  • SQL,与数据库对话。

HTML 和 JavaScript 在浏览器中运行;从不在服务器上。SQL 从你的 PHP 程序传递到数据库,或者有时直接在数据库上使用,所以它是一种服务器语言。

客户端平台

您对服务器有一定的控制权,甚至可能是完全的控制权,但对客户端却没有。也许对于内部网站来说是这样,但是,即使这样,员工还是会在家或在路上访问您的应用。IT(信息技术)力量可以让它成为他们所希望的 Windows 商店,但他们不可能将 iPhones 和 iPads 拒之门外。您可能希望通过只支持有限数量的客户端平台来使事情变得简单,但是您不能。

客户端操作系统

幸运的是,作为应用开发人员,您并不关心客户端操作系统。自然,它对大多数用户来说都很重要,至少在他们选择电脑的时候是如此:Mac、Android 或 iOS 手机或平板电脑、Windows 电脑、ChromeBook 等等。但是,除了客户端路径名看起来像什么之外,实际上没有任何操作系统特定的东西影响浏览器与 PHP 应用的交互,甚至那些路径名也很少传递给 web 应用,因为 web 应用对它们无能为力。

因此,所有的客户端兼容性问题都将与浏览器相关。

浏览器

真是一团糟!有五种广泛使用的浏览器:Chrome、Internet Explorer、Firefox、Safari 和 Opera,按受欢迎程度排列。这仍将约 10%的市场留给了其他公司。此外,还有多个版本在使用。当浏览器已经安装在操作系统上时,如 Internet Explorer (Windows)或 Safari (Mac OS),使用的版本很可能与计算机一样旧,因为许多用户从未升级他们的操作系统,甚至不知道浏览器是一个独立的应用。不厌其烦地安装浏览器(Chrome、Firefox 或 Opera)的人更有可能不时地更新它。

Opera 的使用份额只有 2%,所以如果你想的话,你可以只支持四种浏览器。

处理浏览器变体

因为浏览器中使用的 HTML 和 JavaScript 在最近几年发展如此之快,浏览器即使是一两个旧版本的行为也可能与新版本大相径庭。考虑到广泛使用的大多数浏览器可能有三到四个版本,如果你想确保你的应用为 90%的用户工作,你将有大约 20 种浏览器变体要处理。

有几种方法可以解决这个头疼的问题。

  1. 坚持使用简单的 HTML 而不是 JavaScript。多年来,我在书桌旁放了一本非常旧的 HTML 3.2 参考书,并把自己局限于此。这在一段时间内是可以的,但是现在用户期望更复杂的网站,所以你必须使用最新的 HTML,否则你的网站会看起来很笨重。
  2. 见鬼的 90%。限制您支持的浏览器和/或版本的数量。这对于您组织内部使用的内部应用或者用户很少的应用来说可能是可以的。
  3. 忽略问题。当用户抱怨时,尝试修复 bug。如果你不能解决它,假装你从未听说过它。
  4. 使用一种可以解决浏览器不兼容问题的技术,比如 jQuery。

第三种选择是最常见的,除非网站是真正的一流网站(例如,亚马逊、脸书和雅虎)。第四个是迄今为止最好的;这正是 jQuery 要解决的问题,而且它做得很好。

jQuery 的大部分使用将独立于 PHP 代码,因为它将用于没有混合 PHP 代码的页面的静态部分。尽管如此,它是应用的一部分,所以你有责任把它做好。

即使您使用 jQuery,也要小心声称支持您没有测试过的任何浏览器/版本组合。大多数网站和应用对此保持沉默,这可能就是原因。建立所有的测试环境并在网站发生变化时运行测试需要做大量的工作。(参见“获取用于测试的浏览器”一节)

一个简单的想法是,如果你的团队有几个成员,让每个人在开发过程中使用不同的浏览器,因为这是大多数错误被发现的时候。理想情况下,一个或多个用户正在使用 Windows,并且可以使用 Internet Explorer。Windows 也运行所有其他流行的浏览器,包括 Safari。除了 Internet Explorer,MAC 电脑运行所有的浏览器,而在流行的浏览器中,Linux 只运行 Chrome、Firefox 和 Opera。如果只是你,你可以试着在不同的日子运行不同的浏览器。

Chrome、Safari 和 Opera 使用相同的渲染引擎(HTML/JavaScript 处理器)WebKit,因此它们比任何一个都更兼容 Firefox 或 Internet Explorer,后者使用自己的渲染引擎。但是,即使共享渲染引擎的浏览器在其他方面也有所不同,所以您仍然必须在浏览器本身上进行测试。此外,Chrome 和 Opera 正在转向一个新的渲染引擎,Blink,这是从 WebKit 中分出来的,所以事情将开始出现更大的分歧。

如果您的测试资源有限,但您的应用是供公众使用的,因此必须在所有流行的浏览器上运行,您可能会像我一样,因为我通常处于这种情况:我使用运行在 Mac 上的 Chrome 进行开发,并经常使用 Safari 进行测试,Safari 也在那里。我在附近放了一台 Windows 电脑,这样我就可以用 Internet Explorer 进行测试,我把它保存在版本 8 中,假设微软会保持版本 9 和 10 与版本 8 的大部分兼容,它已经做到了。我从来不用 Firefox 或 Opera,或者其他版本的 Chrome、Safari 或 Windows 进行测试。这对我有用,因为我的网站在开发 HTML 和 JavaScript 的程度上相当谨慎,尽管我使用 CSS。但不要只是模仿我。您的应用是不同的,您的浏览器需求也是不同的。

在第二章中,我解释了在需求中限制浏览器选择的好处。现在你知道为什么了:需求中的任何东西都可能是客户验收测试的一部分,这意味着你也必须对它进行测试。最好少点,多点。

浏览器扩展

我在这里有这个部分,所以我可以说:不!!!没有 Flash,没有 SilverLight,没有 Air,没有 ActiveX,没有 Java。原因如下。

  • 他们并不总是可用的。比如:iPads 和 iPhones 不运行 Flash。没有非 Windows 计算机运行 ActiveX。(不管怎么说,也不是没有很大的麻烦。)
  • 用户将不会安装正确的扩展或版本,他们不会知道他们没有,你的应用将会失败。
  • 它们使得用浏览器测试驱动程序(如 Selenium)进行测试变得非常复杂。
  • 大多数扩展都存在安全问题。其他客户端技术也是如此,但是你处理的越少,你就越有可能堵住漏洞。
  • 你不需要任何扩展。现代的 HTML 可以做过去只有通过扩展才能完成的事情,比如播放视频。

JavaScript 不是一个扩展;这是 HTML 的标准部分。(以防你不知道,JavaScript 和 Java 是完全不同的语言,在浏览器中扮演着完全不同的角色。)

请注意,我只是针对运行在浏览器中的客户端 Java 发出警告。客户端应用,如 NetBeans,我将在本章后面讨论,有时是用 Java 编写的,这完全没问题,与浏览器中的 Java 无关。服务器上的 Java 也无关,也 OK。只是作为浏览器插件,Java 是有问题的。

获取用于测试的浏览器

验证你的应用在特定的浏览器版本上工作的唯一可靠的方法是在那个版本上测试它。正如我提到的,这可能有很多版本,20 个或更多。因为大多数浏览器一次只允许安装一个版本,而且让大量的计算机在周围是不切实际的,所以有三种选择。

  1. 打破常规,运行多个版本,即使供应商不支持它们。稍微在网上搜索一下,你会发现很多这样做的窍门。
  2. 以正确的方式运行虚拟机。您可以在同一个虚拟机上运行不同的浏览器,只是不能运行同一个浏览器的不同版本(除非您执行#1),因此您不需要为每个浏览器和版本运行单独的虚拟机。微软在modern.ie提供随时可用的虚拟机映像。(那是一个使用爱尔兰顶级域名的网址。聪明吧。)
  3. 使用为您运行浏览器版本的浏览器测试服务。例如,saucelabs.com在 iOS、Android、Windows、Mac OS、Linux for Chrome、Internet Explorer、Firefox、Safari 和 Opera 上提供了超过 160 种设备/浏览器/版本组合。酱油实验室有一项免费服务,每月给你大约 200 分钟的测试时间。酱油实验室不是唯一的选择;这一行有很多行头。

如果你运行自己的虚拟机,你可以从oldapps.com获得所有旧版本的浏览器,或者,对于 Windows,从modern.ie获得。

另一种选择是使用内置于最新版本的 Internet Explorer 中的兼容模式,这种选择不如用实际的浏览器进行测试好。例如,版本 10 允许您使用模拟版本 7、8 和 9 的模式进行测试,在较小的程度上还可以模拟版本 5。(因为某些原因没有 6 版。)

客户端编程语言

您希望在从 PHP 应用发送到浏览器的代码中使用的语言是 HTML、CSS 和 JavaScript,它们是现代网站的标准。一些 JavaScript 将采取调用 jQuery 函数的形式,但那仍然是 JavaScript,而不是新语言。不要使用任何其他客户端语言。无论如何,如果你不使用任何浏览器扩展,你就做不到,我已经尽力让你远离了。

(我在前一章中使用了一点 Markdown,但那是在服务器上。在那个例子中,进入浏览器的是纯 HTML。)

如果你很小心并且做了大量的测试,你可以使用最新的 CSS 特性,同时仍然允许你的网站在旧的浏览器上正常运行。例如,我的网站basepath.com使用了圆角矩形和虚线,但是它们在一些浏览器上显示为普通矩形和实线。这仍然适用于我的设计,只是没那么漂亮了。CSS 通常都是这样,这也是为什么所有的样式都应该用 CSS 而不是 HTML 来完成的原因之一。

开发平台和工具

正如我所说的,最小的开发平台是一台运行文本编辑器、FTP 实用程序和浏览器的计算机。几乎任何一台计算机都足够了,因为这三种工具都是内置的。但是你不想要最小的。你整天都在你的开发平台上,所以你希望它比这好得多。

开发操作系统

如果你的开发操作系统就像你的生产服务器操作系统——或者都是nix 或者都是 Windows——你最好能够在开发过程中找到操作系统的依赖关系(如果有的话)。此外,如果您的生产 web 服务器是 IIS,您将无法在您的开发系统上运行它,除非它是 Windows。(记住 Mac OS 是nix 系统。)

然而,即使您的生产系统是*nix,让您的开发系统成为 Windows 也有一个好处:您可以使用 Internet Explorer 作为您的主要开发浏览器,这是我前面提到的优点,您也可以安装所有其他流行的浏览器。这可能比保持开发和生产操作系统不变更重要。

如果您的开发团队中有几个人,并且生产系统是nix,那么让团队中的一些人使用 Windows 作为开发系统,而其他人使用nix 是有意义的。如果生产系统是 Windows,每个人都可以使用 Windows,因为所有主要的浏览器都在 Windows 上运行。

这里有一个更简洁的建议:至少有一个开发者应该使用 Windows。如果生产 OS 是nix,有些开发者应该也会用nix。

如果只有你们一个人,那就更简单了:使用 Windows 进行开发。

当然,我非常清楚,如果你可以自由选择,如果你是为自己工作,你就会完全无视我的建议,选择你最喜欢的系统。我从未遇到过对此没有强烈意见的开发人员。我只是想说出利弊。我也不希望看到随着越来越多的开发者被 MAC 所吸引,Internet Explorer 被忽视,就像过去几年发生的那样。

对我来说,我做的不是很好:我在 Mac 上开发,只是打开 Windows 进行测试。

无论您选择什么,我在本节中讨论的所有开发工具都可以在 Windows、Mac OS 和 Linux 上运行,Apache 也是如此,所以这不会成为您决策的一个因素。

安装 Web 服务器、MySQL 和 PHP

正如我稍后详述的, 您可以选择一个已经安装了 web 服务器、MySQL 和 PHP 的生产托管服务,但是,对于开发,您可能必须自己安装它们。

Apache 和 PHP 是 MAC 自带的,但是,据我所知,PHP 已经过时了。你得自己下载安装 MySQL。(以前是 MAC 自带的,现在没有了。)你可以从mamp.info网站免费下载一个软件,然后把这三个软件安装在一起。(XAMPP 是另一个选择。)启动并运行之后,确保默认的 PHP 路径(/usr/bin/php)链接到 MAMP 路径,在终端中键入类似以下的命令(您的 MAMP 路径可能不同):

sudo mv /usr/bin/php /usr/bin/php-old
sudo ln /Applications/MAMP/bin/php/php5.4.10/bin/php /usr/bin/php

每次更新 Mac OS 时,不要忘记再次执行此操作。

Windows 自带 IIS,但没有 Apache、PHP 或 MySQL。我还没有找到一个相当于 MAMP 的 Windows 和 IIS 版本;我找到的那个有过期的部件。所以,这就是我们要做的。

  1. 从控制面板上程序和功能小程序的“打开或关闭 Windows 功能”部分安装 IIS。
  2. 按照 PHP 网站php.net/manual/en/install.windows.php上的说明安装 PHP for IIS。棘手的部分是让它与 IIS 一起工作,但这样做的说明是存在的。
  3. dev.mysql.com安装 MySQL。MySQL 不用配置 IIS 或者 PHP 一旦安装并启动,您就可以连接到它。

如果你想在 Windows 上运行 Apache,你很幸运:在wampserver.com有一个比上面描述的 MAMP 更容易使用的 WAMP。它自动设置 Apache、PHP 和 MySQL,并提供一个任务栏通知图标来管理它们。(XAMPP 也有空。)

如果你的开发系统是 Linux,你就无所畏惧了,对吧?所以,你可以自己安装栈的 AMP 部分。谷歌“在 Linux 上安装 LAMP”获取说明。如果您愿意,可以用您的发行版替换“Linux”,如“在 Red Hat 上安装灯”

您可以验证 PHP 和 MySQL 正在使用清单 3-1 中显示的程序。

清单 3-1 。验证安装工作的程序

define('DB_HOST', 'localhost');
define('DB_PORT', '3306');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', '...');
try {
    $dsn = 'mysql:host=' . DB_HOST . ';port=' . DB_PORT;
    new PDO($dsn, DB_USERNAME, DB_PASSWORD);
}
catch (PDOException $e) {
    die($e->getMessage());
}
echo "<p>PHP/MySQL is working!";
phpinfo();

这个程序实例化了一个新的连接到 MySQL 的 PDO 对象。如果程序运行,你有 PHP。如果创建了对象,您就拥有了 MySQL 并可以连接到它。如果实例化失败,PDO 类将抛出异常。

如果一切顺利,当你从浏览器访问这个 PHP 程序时,你将得到类似于图 3-3 的东西。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-3 。验证的输出

如果一切都不顺利,脚本将无法正确执行,这意味着以下情况之一是错误的:

  • web 服务器没有运行。
  • URL 是错误的,很可能是因为 PHP 文件没有正确地位于 web 服务器的文档根目录中。
  • PHP 没有安装在 web 服务器上。在这种情况下,您可能会看到您的 PHP 代码显示在浏览器中,就好像它是一个坏的 HTML。
  • 您将看到一条 PHP 错误消息,因为脚本中有一个错误。
  • 您将看到一条错误消息,因为您未能连接到 MySQL。

最后两个问题是值得考虑的,因为它们意味着至少 web 服务器和 PHP 是可以的。然后你只要追查一下为什么连不上 MySQL,大概是以下原因之一:

  • MySQL 服务器没有运行。
  • 它不允许来自localhost的连接。这是不寻常的,但值得检查。
  • 用户名和/或密码错误。安装过程中可能会要求您输入默认用户名 root 的密码。否则,密码可能为空(即根本不需要密码)。

随着开发栈的安装和运行——LAMP、MAMP、WAMP、WIMP、XAMPP 等等——你已经准备好设置你的开发工具了。

编辑器和 ide

多年来,我一直使用 IDE(集成开发环境)为 Windows 或 Mac OS (Visual Studio 或 Xcode)开发本地应用,但对于 PHP 开发来说只是一个文本编辑器,通常是 Mac OS 的 BBEdit。BBEdit 提供了 IDE 的许多功能,例如基于项目的组织(多个文件收集在一起)和语法突出显示,以及功能强大和设计良好的文本编辑。它没有代码完成(根据您输入的片段建议替代方案)、弹出文档、调试或单元测试。

所有这些缺失的特性和更多的特性都由 PHP 可用的 ide 提供,包括免费的和商业的。好像有两大:Eclipse 和 NetBeans。两者都是最初为 Java 开发的 ide,但是它们的架构足够灵活,可以适应 PHP(和其他语言)。

我发现 NetBeans 更容易设置,因为它提供了您需要的大部分东西,而使用 Eclipse,您必须安装一些插件才能开始。我还发现 NetBeans 更容易使用,尽管我确信一旦您了解 Eclipse,它也会变得容易。因此,虽然我将使用 NetBeans 来说明用 IDE 开发 PHP,但我并不推荐它胜过 Eclipse。我说这比较容易上手。也有一些 PHP 的商业 ide 看起来很受欢迎,但是我还没有尝试过。您可以从netbeans.org/features/php下载已经为 PHP 配置的 NetBeans。

图 3-4 显示了在 NetBeans 中正在开发的清单 3-1 中的测试程序。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-4 。NetBeans 窗口

在本书中,我不打算详细解释如何使用 NetBeans 或任何其他 IDE,因为这类信息在网上很容易找到。

传输文件

你不必把 PHP 文件转移到任何地方,在开发平台上测试它们,这就是为什么你想在那里有一个 PHP/MySQL 栈。我在 web 服务器的文档根目录下开发。您永远不会在生产服务器上这样做,但是从任何地方都无法访问我的开发系统,甚至从我的本地网络也无法访问,所以我用最方便的方式使用它。

但是,您必须将文件传输到生产服务器,在那里对它们进行测试,并最终将它们提供给任何用户,也许是全世界的用户。为此,你通常会使用 FTP,这种文件传输协议甚至比网络还要古老,或者它更安全的替代品,SFTP。如果可以的话使用 SFTP,但是有些服务器不支持,所以对他们来说是 FTP。

如果您正在使用版本控制(参见“版本控制”一节),请确保在上传到服务器之前提交更改,这样您就知道您正在使用应用的受控版本进行测试。

所有主要的操作系统都内置了 FTP。或者你可以使用一个免费的 FTP 工具,比如 FileZilla。如果你的文本编辑器有 FTP,你可以使用它。但是,如果您使用的是 NetBeans,它内置了 FTP 和 SFTP,这是一个不错的选择。(Eclipse 也有,但是,正如 Eclipse 经常出现的情况一样,您必须找到并安装一个插件。)

您可以从设置您的服务器的任何人那里获得您需要的 FTP 或 SFTP 参数—服务器名称、用户名、密码。(参见图 3-5 。)然后,在您使用的任何 FTP/SFTP 客户端(对我来说是 NetBeans)上将它们输入到远程连接设置中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-5 。从主机提供商处收到的显示 FTP 凭据的电子邮件摘录

无论如何,要确保 FTP/SFTP 客户端足够智能,只传输发生变化的文件。有时这种聪明取决于文件修改时间,如果本地和远程时钟不同步,这可能会导致问题。使用内置 FTP/SFTP 的 IDE(如 NetBeans)的一个优点是,它知道文件是否发生了更改,而不必查询服务器来猜测。

当你需要更新一个新版本的网站时,你不希望在现有的基础上复制文件。我将在“安装新版本”一节中告诉您该怎么做

调试工具

如果您将 Xdebug 扩展安装到 PHP 中,您可以从 NetBeans 内部执行交互式调试,使用您所熟悉的所有功能,比如断点、单步执行、变量值显示等等。WampServer 是我用来在 Windows 上安装开发栈的,它附带了已经安装的 Xdebug。

测试工具

您肯定希望使用像 PHPUnit 这样的单元测试工具。它是几个可用的 PHP 单元测试工具之一,但它是我使用的工具,因为 NetBeans 直接支持它,这使得创建和运行单元测试非常容易。

另一个值得研究的工具是 Selenium,它可以运行驱动 web 浏览器的测试。这是针对整个系统的测试,因为它可以驱动整个应用。

版本控制

这里有一个有趣的事实:我发明了第一个源代码控制系统。我没开玩笑!20 世纪 70 年代初,在贝尔实验室的时候,我开发了源代码控制系统(SCCS),,所有后续系统的先驱,包括 RCS、CVS、SourceSafe、Subversion、Git。你可以从basepath.com/site/docs.php下载我 1975 年在第一届 IEEE 软件工程会议上发表的论文。

当然,没人再用 SCCS 了。或者,几乎没有人——IBM 将它作为其 CMVC 系统的关键组件,也许在内部仍然如此。(当我在 2000 年代中期作为专家证人检查 IBM 源代码时,IBM 就是这样做的。)

今天流行的系统,至少在开源社区,似乎是 Subversion、Mercurial 和 Git。我相信它们都是不错的选择,这并不是说你不会在网上找到关于哪个更好的激烈争论。所有这些都由 NetBeans 直接支持,不需要插件,尽管您必须自己安装系统。

Mercurial 和 Git 是分布式系统,这意味着每个开发人员都有自己的存储库,当开发人员准备好这样做时,存储库就会与中央存储库同步。它们适合由几十个开发人员进行广泛分布的开发,对于像 Linux 这样的大型开源项目也是如此。(Git 是由 Linux 创建者 Linus Torvalds 创建的。)当您同步时,如果其他人已经更新了您尝试更新的存储库部分,您可能会发现不一致,在这种情况下,您必须在同步完成之前解决问题。

Subversion 有一个中央存储库,当您需要一个新文件或者需要签入您正在处理的文件的变更时,您可以随时访问它。对于小团队来说,这是一个很好的方法,因为每个人都知道最新的变化在哪里。对于大型团队来说,分布式系统可能更好。(我敢肯定,颠覆拥护者不会同意。)

比较版本控制系统让我头疼,所以在我真正开始之前我会停下来。使用你工作的地方已经存在的,或者,如果你是一个人,开始新的(或者准备抛弃你一直在使用的东西),就使用 Mercurial。

或者 Git。

或者颠覆。

问题跟踪器

我在第一章的中谈到了问题跟踪器。你想要一个支持问题和客户支持(例如,跟踪电子邮件),负担得起的(免费的肯定是),并且有一个可以从 PHP 访问的数据库,我在第二章第的清单 2-3 中展示了一个例子。

NetBeans 提供了对 Bugzilla 和 JIRA 的内置支持,这两个我都没用过。

托管替代方案

到目前为止,我唯一深入讨论过的虚拟主机是你的开发系统。但是生产服务器才是真正重要的。

最重要的规则是,你不应该在自己的设备上托管自己的网站,除非你的名字是亚马逊、谷歌、苹果、脸书或其他同样大而富有的人。托管是一项专业的、高要求的、技术先进的活动,应该总是留给全职(在这种情况下意味着 24/7)的专业人员。您不想应对全天候的人员配备、不间断的电力、物理安全性、不间断可用性、异地备份和网络安全。所以你不用托管一个网站,你选择一个托管服务。

在许多情况下,您不会是选择的人,因为您正在构建的应用将在现有的托管平台上运行。我为理查森学区建立的成绩单系统 Rgrade 就是这种情况。该学区有自己的 IT 人员和自己的服务器机房,并且为我的应用添加了一些盒子。我从来没有见过那些电脑,我甚至不确定我见过它们所在的服务器机房。我可以通过 FTP 和终端访问它们,这正是我所需要的。尽管它们是由我的客户拥有和运营的,但就我而言,它们是在网络空间的某个地方,具体在哪里,我不知道也不关心。我为科罗拉多大学世界事务会议建立的系统也是如此。我很确定服务器就在校园的某个地方。

将托管服务分成几类是有帮助的。

  • 真实机器上的内部托管,就像我刚刚提到的两个。有时这些会在不同的网站上为不同的用户共享(比如 CWA 系统),有时则不会(比如 Rgrade)。
  • 真实机器上的商业共享主机服务,从免费到每月 100 美元或更多,包括中间的几乎所有金额。我每个月为basepath.com支付大约 9 美元,包括无限存储和无限带宽。
  • 云服务器上的虚拟机。例如亚马逊网络服务、微软 Azure 和 Rackspace Cloud。(IBM、谷歌和许多其他公司也这么做。)这被称为基础设施即服务(IaaS)。
  • 云服务器上运行您定义的应用的应用平台,称为平台即服务(PaaS)。例子有亚马逊弹性豆茎和谷歌应用引擎。

在某些情况下,所有四个类别的主机服务为你设置了 PHP 和 MySQL。否则,您需要自己安装和设置 web 服务器、MySQL 和 PHP,就像您有自己的真实机器一样,这是您在获得虚拟机时通常要做的事情。这并不太坏,一旦你掌握了它,你就会有很大的灵活性。你永远不会因为获得最新版本的软件、添加用户或任何你想要的东西而受到你的主机服务的摆布。这是你的(虚拟)机器。

我将稍微讨论一下商业共享托管服务,然后报告我在一些云服务器上的体验,包括 IaaS 和 PaaS。

商业共享托管服务

你已经看过超级碗比赛期间那些令人发指的广告了。他们是最大的商业共享主机提供商,或者接近它。但 Go Daddy 只是数百家甚至数千家此类供应商中的一家。它们都提供一些计划,从免费到每月不到 5 美元甚至更多,取决于你有权期待的服务和容量。

我尝试的免费服务,只是为了好玩,是freewebhostingarea.com。反应似乎真的很慢,即使是我的玩具网站。出于某种原因,NetBeans 内部的 FTP 速度甚至更慢。一些 PHP 工具似乎受到了审查,比如phpinfo函数,它什么也不做。从我关于phpinfo不工作的电子邮件没有得到回复来看,客户支持似乎不存在。事情是这样的:即使是免费的,我也得到了 PHP 和 MySQL。你总是会得到 PHP 和 MySQL,这就是为什么它们是实现 web 应用的绝佳选择的一个原因。(然而,您并不总能获得您需要的特定特性;稍后会详细介绍。)

A2 主机,我已经用了basepath.com,是一个更严肃的服务,不像免费的网络主机。(如果你付钱给他们,他们也可能是认真的;我没有尝试去发现。)我的网站处理不了多少流量,但对于我所需要的,响应能力非常好,正常运行时间和客户支持也是如此。没有抱怨。我每月只付 9 美元!

我在 A2 主持工作了大约七年。之前,我曾被另一家公司托管,但我离开了那家公司,因为没有人会回复我的支持邮件。这是一个问题,当你做得很便宜的时候。

我用这些低价得到的服务叫做共享托管。我在一台有未知数量的其他客户的计算机上,当流量进来时,我们争夺资源。电脑就是电脑。无法扩展,没有负载平衡器,根本没有可扩展性。如果你自己的网站流量很大,这通常是你想要的,或者同一台电脑上的其他人的网站流量很大,事情就完了。对于basepath.com,无所谓。对于您的应用,可能是这样的。

共享主机总是限制你能做什么,这可能会阻止你的应用运行。例如,在第四章中,我将解释为什么数据库触发器是验证数据的好方法,但是,由于 MySQL 的设计方式,您需要“超级”权限来创建触发器,而这在共享主机中有时是不允许的。这是一个严重的限制。

A2 主机和大多数其他公司提供更高价格的计划,提供更多的资源。例如,每月大约 90 美元,你可以获得 16 个虚拟 CPU(中央处理器)和 SSD(固态硬盘)存储,保证容量远远超过我花区区 9 美元所能获得的。

托管可扩展性

有了 A2 托管或任何类似的提供商,即使每月 90 美元,你仍然有可用资源的上限。您想要的是从小规模开始并根据需要增长的服务。

扩展主机容量有两种方式: upout 。向上扩展意味着获得更大的计算机,以更高的带宽连接到互联网。这就是当你告诉 A2 主机将你的网站转移到更高成本的服务时会发生的事情。你需要一段时间来决定你想这么做,A2 也需要几天时间来把你转移过去。与此同时,你的网站将会很慢,如果它能工作的话。然后,如果需求下降,你就要为超出需求的产能买单。

横向扩展意味着计算机的大小保持不变,但是您拥有更多的计算机。这对于 web 应用非常有用,因为除了连接到公共数据库之外,每个会话都是相互独立的。即使对于 Rgrade,该学区也购买了一台 Cisco 负载平衡器,它接收 HTTP 请求,并根据哪台应用服务器负载较轻,将请求分配给其中一台应用服务器。负载平衡器足够聪明,能够理解 PHP 会话,确保包含大量 HTTP 请求的给定会话保持在同一台服务器上。(重要的是,因为我将 PHP 设置为将会话数据存储在一个不在计算机之间共享的临时文件中。)负载平衡器不限于两台,因此我们可以通过添加应用服务器轻松扩展容量。几个月后,我们发现我们高估了所需的容量,It 人员将一台应用服务器转移到别处使用。这就是负载平衡器提供的灵活性。

数据库不容易在计算机之间分配。如果有必要的话,我们可以这样做,因为这是当时 Oracle 的一个特性,我们也在使用它。然而,这远没有添加负载平衡器那么简单。MySQL 根本不能很好地扩展到多台服务器,所以你必须自己考虑分割数据库,并在设计访问时考虑这种分割。

用户、组和权限

在开始设置虚拟服务器之前,我需要解释一下如何处理用户、组和权限。这适用于运行在*nix 系统上的 Apache 即 LA in LAMP。在这一节的最后,我会对 Apache 或 IIS 在 Windows 上的运行做一些评论。

首先,简要介绍一下*nix 上的用户、组和权限,以防您不熟悉。

  • 一个用户对应一个登录名。用户也被组织成组,通常只由一个用户组成,但也可能由几个用户组成。
  • 每个进程都作为一个用户(通常是启动它的登录用户)和一个组(通常是该用户的组)运行。正是这个用户和组决定了进程是否有权限读取、写入或执行目录(文件夹)或文件。
  • 执行文件意味着将它作为程序运行。执行目录意味着在路径中使用它。
  • 每个目录和文件都有一个所有者用户和组。这些最初与创建目录或文件的过程相同,但是它们可以被改变。
  • 每个目录和文件都有三种权限:用户、组和其他人。总共有九个权限。
  • 八进制(以 8 为基数)数字用于表示目录或文件的权限。例如,权限 754 等于 9 位 111101100。一次考虑他们三个,所有者有 111,意思是读+写+执行,组有 101,意思是读+执行,其他人有 100,意思是读。
  • 确定进程是否可以对目录或文件执行操作(读、写或执行)的算法如下:如果进程用户和目录/文件用户匹配,则使用用户位。如果它们不匹配,但组匹配,则使用组位。如果用户和组都不匹配,则使用其他位。

最初,当您第一次在一个*nix 系统上直接安装 Apache 时(例如,不是用 MAMP 这样的系统),您可能会发现设置如下:

  • 网站所在的文档根被设置为/var/www,并包含一个示例文件index.html。该目录和文件的用户和组被设置为 root 用户,只有 root 用户拥有对它们的写权限。(文档根和用户根是不相关的。)
  • Apache 和 PHP 使用设置为用户 www-data 的用户和组运行。Apache 和 PHP 不能写入文档根目录,但是它们可以读取它,所以如果作为 root 用户,您在那里建立一个网站,并授予其他人对目录和文件的读取权限,以及对目录的执行权限,Apache 和 PHP 就可以运行该网站。
  • 当您使用 SFTP 登录以更新网站时,SFTP 进程的用户和组将被设置为您的登录名。对于 Ubuntu Linux 服务器,初始用户登录是 Ubuntu,这是一个不能写入文档根目录的用户和组,这使得 SFTP 无法使用。

因此,必须改变初始设置。有各种方法可以改变它,但是最简单的方法是更改 Apache 配置,将文档根目录设置为您登录的子目录,可能是一个名为www的目录,您应该在登录时创建它,如下所示:

mkdir www

这个文档根目录的完整路径类似于/home/ubuntu/www。它将拥有权限 775,这意味着您和您的组(最初只有您)可以读取、写入或执行它,而其他人只能读取和执行它。网站中的文件(HTML、PHP、CSS 或其他文件)应该具有权限 664,因为它们不需要被执行。(PHP 文件不是由*nix 系统执行的;它们只是被 PHP 处理器读取。)

现在,Apache 和 PHP 仍然作为 www-data 运行,但这没关系,因为你已经允许其他人访问网站,只是不写入它。因为你的登录(ubuntu 或者其他什么)拥有所有的目录和文件,SFTP 会工作得很好。

现在一切都很好,直到 PHP 需要写入一个目录,例如,当它想要创建一个 PDF 文件供下载时,它可能会这样做,正如我在第七章中演示的那样。由于 PHP 是作为组 www-data 运行的,允许它写入目录的最简单方法是将目录的组改为 www-data。例如,如果目录installation是由登录为 ubuntu 的 SFTP 创建的,那么它的用户和组就是 ubuntu,正如您从ls命令的输出中看到的。

drwxrwxr-x 5 ubuntu ubuntu 4096 Apr 26 14:17 installation

您可以使用以下命令更改该组:

sudo chgrp www-data installation

并且ls命令输出变为

drwxrwxr-x 5 ubuntu www-data 4096 Apr 26 14:17 installation

SFTP 现在可以写入目录,因为用户是 ubuntu,PHP 也可以,因为它的组是 www-data。

还有一个问题:在“安装新版本”一节中,PHP 将把文件复制到一个目录中,然后这些文件将拥有一个用户和一组 www-data,权限为 664,这意味着作为 ubuntu 运行的 SFTP 将无法更新它们。当然,您可以使用 SSH 终端登录并更改文件的用户和组,但是这太麻烦了。Web 应用应该自己运行,不需要终端人员的干预。

这个问题有几种解决方法。

  • 对于许多共享主机服务,比如我在basepath.com中使用的那个,使用了一种叫做虚拟主机的东西,这样 Apache 和 PHP 就像作为我的用户和组(rochkind)运行一样。所有的目录和文件都归我所有,Apache 和 PHP 以我的身份运行,SFTP 以我的身份运行,所以一切都很好。
  • 如果你控制了服务器,不管是真实的计算机还是虚拟的计算机,你都可以设置虚拟主机,或者使用一个名为“suEXEC”的 Apache 工具来安排 PHP 以你的身份运行,类似于共享主机服务。然而,这很难做到,尤其是如果你是新手的话,我不打算在本书中深入探讨。
  • 如果你有一个虚拟服务器,它存在的唯一目的是运行你的网站,除了你没有其他用户,除了 Apache、SFTP 和 SSH 没有其他应用,除了你没有人可以登录。因此,你可以很容易地在共享系统上做一些会带来安全问题的事情:只需改变 Apache 的配置,让它真正像你一样运行,跳过虚拟主机或 suEXEC 的复杂性。(MAMP 也是这样设置的。)

要让 Apache 像你一样运行——比如 Ubuntu——有三个简单的步骤。首先,作为 root 用户,编辑配置文件以更改用户和组。对于 Ubuntu 来说,这个文件是/etc/apache2/envvars,你要换行

export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data

export APACHE_RUN_USER=ubuntu
export APACHE_RUN_GROUP=ubuntu

第二,改变文件的所有者/var/lock/apache2

sudo chown ubuntu /var/lock/apache2

第三,重启 Apache

sudo /etc/init.d/apache2 restart

再说一遍,这样我就不会收到讨厌的邮件了,这在一个有多个用户的服务器上运行不止一个用户的网站不是一个好主意,但是对于一个单一用途的虚拟服务器,或者对于一个没有外部访问的开发系统来说,这是完全安全的。

总之,不管怎样,如果 PHP 和 SFTP 能够编写相同的目录和文件,最好让它们以相同的用户身份运行。

Windows 没有组,对于 Apache 来说,没有像 suEXEC 这样的东西。你的选择是

  • 以网站所有者的相同用户身份运行 web 服务器,Apache 或 IIS,或者
  • 使用身份模拟,对于 IIS,大致相当于*nix 上的 suEXEC。

云服务器

云服务器分为两大类:你想做什么就做什么的虚拟机(IaaS),以及提供你需要的设施(例如 PHP 和 MySQL)并随着负载增加而扩展的应用平台(PaaS)。一般来说(也有例外),IaaS 系统提供了更多的灵活性,而 PaaS 系统更容易设置并提供自动扩展,而且它们有时是免费的。

对于 IaaS,您可以说,“我想要一台虚拟机。”使用 PaaS,您可以说,“这是我的应用—帮我运行它。”

正如您将在接下来的三个部分中看到的,我让 LAMP stacks 在 Amazon、Microsoft 和 Rackspace 的 IaaS 云上相当容易地运行,尽管这涉及到下载和安装软件、使用 SFTP 和 SSH 终端会话登录、查找和更改配置文件以及发现 IP(互联网协议)地址。我不确定这对一个专业程序员来说有多难(这本书的所有读者都是这样,对吧?)谁是 Linux 新手,因为我不典型。我从 1972 年就开始使用类 UNIX 系统,甚至还写过几本高级 UNIX 书籍(查看我的高级 UNIX 编程)。不过,如果你非常仔细地阅读说明,自由地使用谷歌搜索细节,并不断尝试,你一定会成功。我听说过 Rackspace 的技术支持,所以,如果你是这方面的新手,你可以先试试 Rackspace。亚马逊和微软可能不太愿意帮忙,因为他们没那么饥渴。

这里讨论的所有云服务器都允许轻松地向上扩展(更大的计算机)和向外扩展(更多的计算机,带有负载平衡器)。例如,一旦您得到了想要的 Amazon EC2 实例,管理控制台上一个名为“Launch More Like This”的菜单项允许您生成一个或多个额外的服务器,可能运行在更大的虚拟计算机上。你可以从亚马逊所说的微型开始,相当于一个内核和 613 MB 的内存,一直到 16 个内核和 117 GB 的内存。这是在扩大规模。要向外扩展,您需要生成额外的、可能更小的服务器,并添加一个负载平衡器来在它们之间分配负载。

我将介绍一个 PaaS、Amazon Elastic Beanstalk 和三个 IaaS 系统、Amazon EC2、Microsoft Azure 和 Rackspace Cloud Server 的入门知识。然后我会说几句关于 Google App Engine 的话,这是一个 PaaS,在本书即将出版时才开始支持 PHP。

亚马逊弹性豆茎

明白吗?你爬上豆茎到达云端。

我认为 A2 托管的任何运行basepath.com的计算机都是虚拟的,因为我永远不会看到它,也不知道它在哪个城市,但它不是虚拟的:它是一台真实的计算机,有固定的限制,他们永远不会让我超过,除非我注册一个更昂贵的计划。更糟糕的是,我把它分享给其他网站,没有人知道他们会做什么来攫取资源。我能做的也很有限。例如,如果我想允许 URL 作为文件名(比如说在file_get_contents中),我不能——A2 主机不允许。正如我提到,还有 MySQL 触发器的限制。

亚马逊弹性计算云(更广为人知的名称是 EC2)并非如此。你得到了一台看似真实的计算机,名为实例、,你可以以 root 用户身份登录,并以任何你想要的方式进行配置。但是,它是一台虚拟计算机,运行在亚马逊数据中心的某个服务器上。你可以根据需要扩展它,但它不能超越亚马逊拥有的最大服务器,即大约 16 个内核和 117 GB 的内存。相当大,但仍有限制。

您可以添加另一个服务来提供负载平衡,以便在 EC2 实例之间分配会话。对于数据库,您可以在 EC2 实例上运行 MySQL,因为您拥有(虚拟)机器,所以您可以使用虚拟 MySQL 数据库,Amazon 称之为关系数据库服务(RDS)。(RDS 还支持 Oracle 和 Microsoft SQL Server。)

其结果是,随着流量的增加,web 服务器几乎无止境地、完全自动地扩展。如果流量减少,它就会缩小。您只需为您使用的东西付费。

问题在于,尽管亚马逊在基于网络的管理界面上做得相当不错,但设置起来还是需要很多工作。几年前,亚马逊用弹性豆茎让这变得简单多了。现在建立一个弹性服务真的很容易,有了所有的东西:负载平衡、Apache、MySQL 和 PHP。用云的行话来说,这一切都是为您完成的,它是 PaaS,而不是 IaaS。

为了演示使用 Elastic Beanstalk 是多么容易,我将展示如何从头开始设置它。(在下一节中,我将对一个普通的 EC2 IaaS 实例做同样的事情,没有结霜。)

首先,你需要一个亚马逊网络服务账户,你可以在aws.amazon.com获得。(如果您是新客户,您可以免费构建和使用一个弹性 Beanstalk 服务器一年。)一旦你有了这些,你进入 AWS 管理控制台,点击 Elastic Beanstalk 链接,然后你被要求选择你的平台,如图 3-6 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-6 。选择弹性豆茎平台

我选择了 PHP 5.4,它将我带到了 Elastic Beanstalk 控制面板,在那里我花了几分钟时间观察了创建环境的进度,包括各种组件(EC2、负载平衡器等。)得到了配置和启动。当一切都准备好时,我看到了图 3-7 中的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-7 。弹性豆茎应用示例

示例应用只显示一个样板网页。我想要自己的应用。所以,我点击了你在图 3-7 顶部看到的上传新版本按钮,上传了一个包含来自清单 3-1 的 PHP 文件的压缩文件,但是修改后使用了 Elastic Beanstalk 放入环境中的数据库参数,而不是在 PHP 文件本身中对它们进行硬编码。清单 3-2 展示了这些修改。(你可以上传压缩的源文件或者直接从 Git 上传。)

清单 3-2 。为弹性豆茎修改的测试程序

define('DB_HOST', $_SERVER['RDS_HOSTNAME']);
define('DB_PORT', $_SERVER['RDS_PORT']);
define('DB_USERNAME', $_SERVER['RDS_USERNAME']);
define('DB_PASSWORD', $_SERVER['RDS_PASSWORD']);
try {
    $dsn = 'mysql:host=' . DB_HOST . ';port=' . DB_PORT;
    new PDO($dsn, DB_USERNAME, DB_PASSWORD);
}
catch (PDOException $e) {
    die($e->getMessage());
}
echo "<p>PHP/MySQL is working!";
phpinfo();

我点击上传新版本按钮时得到的上传面板如我填写的图 3-8 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-8 。弹性豆茎版上传面板

安装新版本花了几分钟时间。当我在浏览器中测试 PHP 文件时,它失败了,屏幕上显示如下内容:

SQLSTATE[HY000] [2002] No such file or directory

问题是我没有请求 RDS 数据库——默认情况下不会有。因此,我点击了你在图 3-7 底部看到的编辑配置链接,这将我带到配置编辑器,在那里我点击了数据库选项卡,如图图 3-9 所示,并创建了一个 MySQL 数据库。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-9 。弹性豆茎配置编辑器

这次它成功了,给出了图 3-10 中的输出,除了 PHP 版本之外,它看起来就像图 3-3 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-10 。在弹性豆茎上运行的测试程序

Elastic Beanstalk 及其负载均衡器比只使用 EC2 机器更昂贵。对于最小的 EC2 机器,假设最小的带宽和存储,带 RDS 的 Elastic Beanstalk 大约是每月 54 美元。EC2 机器如果没有使其具有弹性的服务,价格大约为 15 美元,所以您需要为负载平衡器和 RDS 支付很多费用。只有一台服务器的负载均衡没有任何意义(没什么好平衡的),所以这些价格意义不大。随着 EC2 服务器越来越多、越来越大,负载平衡在总成本中所占的比重并不是很大,因为它的成本是相当固定的,不依赖于它所处理的服务器数量。您也不必使用 RDS,因为您可以在 EC2 机器上免费安装 MySQL。简而言之,弹性豆茎没有经济意义,除非你有一个快速增长的大型网站。您所支付的是应对这种增长的能力。如果你不期望快速成长,你就不需要有弹性的豆茎。

因为您可以单独获取其组件,所以 Elastic Beanstalk 是一种 PaaS,它仍然提供 IaaS 的灵活性。其他 PaaS 产品,如谷歌的应用引擎,要封闭得多。

亚马逊 EC2

为了了解如何将 EC2 实例(没有所有弹性 Beanstalk extras 的 IaaS)配置为 LAMP 堆栈,我从 AWS 管理控制台启动了一个 EC2 实例,从可用类型菜单中选择 Ubuntu。

使用 EC2,您必须在启动映像时指定一个 SSL 密钥对文件,因为仅用密码通过 SSH 或 SFTP 访问它是不够的。从 AWS 管理控制台的 EC2 部分创建一个密钥对,然后将其下载到开发计算机上。将它放在您喜欢的任何地方,这样您就可以从您的 SSH 终端和 SFTP 应用中引用它。为了使用它,您必须确保它是不可写的,因为客户端将拒绝使用一个密钥对文件,除非它是受保护的。当我使用 Elastic Beanstalk 实例时,我已经建立了一个密钥对文件,所以我只使用那个文件。(一个就够了。)如果您在没有指定您下载的密钥对文件的情况下启动映像,您将不得不终止它并重新开始。

接下来,您必须确保启动 EC2 实例的安全组允许 HTTP (web)访问。

下载了密钥对并设置了安全组后,就可以启动实例了。在它启动后,您可以通过在终端中键入的ssh命令从*nix 开发系统连接到它,如下所示:

ssh -i /Users/marc/.ssh/keypair1.pem \
ubuntu@ec2-54-242-132-25.compute-1.amazonaws.com

它显示为分布在两行上,但它是一个命令行;反斜杠是外壳延续字符。如果您的开发系统是 Windows,您可以使用免费的 PuTTY 应用进行 SSH 访问。

一旦我进入 shell,我就输入命令

sudo apt-get update

更新可用软件的目录,然后使用命令

sudo tasksel

给我一个简单的交互式安装灯堆的方法,如图图 3-11 所示。请注意我对第四个选项“灯服务器”的选择其他星号已经在那里;如果我删除了任何组件,该组件将被卸载。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-11 。运行 tasksel 的 EC2 实例

LAMP 安装后,网站就可以运行了,这是我通过浏览器验证的。然后从 SSH 会话中,我编辑 Apache 配置将文档根目录改为我登录目录的子目录,上传清单 3-1 所示的测试程序,得到我想要的,如图图 3-12 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-12 。在 EC2 实例上运行的测试程序

Microsoft Azure

Azure 是微软的云服务。微软称之为 Windows Azure,但你可以用它来设置 Linux 虚拟机,以及 Windows。(亚马逊和 Rackspace 也支持 Windows。)

为了试用 Azure,我设置了一个运行 Ubuntu Linux 的小型虚拟机(IaaS)。我享受了 90 天的免费试用,但如果我不得不为这台机器付费,每月大约需要 15 美元,与亚马逊最小的 EC2 机器一样。

只需点击几下就可以得到我的 Ubuntu 服务器,除了 L 部分,没有安装 LAMP 堆栈的任何部分。我能够轻松地连接 SFTP(运行的服务器)并登录到 SSH shell,而不需要使用密钥对文件,这是 Amazon EC2 要求我做的。在 shell 中,我可以用sudo命令得到一个超级用户(root)提示符。

微软有一个很好的帮助页面,解释了如何安装 LAMP 堆栈的其余部分,基本上是通过 SSH shell 执行一个命令。

apt-get install apache2 mysql-server php5 php5-mysql \
libapache2-mod-auth-mysql libapache2-mod-php5 php5-xsl php5-gd php-pear

事实证明,这和使用可视化安装工具tasksel一样简单。然后我启动了 Apache 服务器。

sudo /etc/init.d/apache2 restart

但是 Azure 分配的 URL,rochkind.cloudapp.net,没用。搜索了一下,我发现我忘了为端口 80(web 服务器的默认端口)创建一个端点,所以我从 Azure 管理门户创建了这个端点。然后网站工作了。(这类似于我在 EC2 中遇到的问题,当时我忘记将 HTTP 添加到安全组中。)

然后,由于 Apache 文档根对于我的用户登录来说是不可写的,我不得不像对 EC2 一样更改文档根。然后我上传了测试程序(清单 3-1 ),一切正常,如图图 3-13 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-13 。在 Azure Ubuntu 服务器上运行的测试程序

你通常不会认为微软是 Linux 服务器领域的大腕,但它恰恰表明了云计算的竞争力,以及它对主要参与者的重要性。

我用微软的 PaaS 产品做了一点试验,微软称之为网站,它包括 PHP 和 MySQL。如果你有一个小网站,它们是免费的,但是它们有共享主机的限制,这使得它们不适合我在这本书里关注的那种应用。我会选择虚拟机。

机架空间

接下来是 Rackspace,它提供了许多与亚马逊和微软相同的云功能。正如我在 Azure 上做的那样,我注册了最小的 Ubuntu 服务器,这将花费我大约 16 美元一个月,与亚马逊 EC2 和微软 Azure 差不多。

一旦我的虚拟机设置好了,Rackspace 就给了我需要的 SSH 命令,这是我必须为亚马逊和微软自己解决的问题。事实上,点击他们网页上的链接,我就直接进入了 Mac OS 终端应用。当然,不能上网,因为这是一台全新的机器。

我执行了用于 Azure 服务器的相同的apt-get命令,它工作了。这一次,端口 80 已经处于活动状态,所以对 Apache 的 web 访问马上就可以工作了。

与 SFTP 的接触也奏效了。测试程序也是如此,表明 PHP 和 MySQL 都在运行。我的浏览器显示的内容类似于图 3-12 ,因为灯组是相同的。所以,再一次,成功!

谷歌应用引擎

Google App Engine,一个 PaaS,已经出来一段时间了,但是只支持 Java、Python 和 Go(一种 Google 发明的语言)。就在我完成这一章的时候,它宣布了对 PHP 的支持,所以我可以稍微了解一下。关于谷歌应用引擎的一些值得注意的事情:

  • 与其他供应商不同,它为您提供了一个完整的开发平台,包括一个运行 PHP 和 MySQL 的 web 服务器,因此您可以在本地进行测试。
  • 小应用是免费的,而且,在典型的谷歌时尚中,免费层相当慷慨,所以对于小网站你不必付费。然而,这不包括 MySQL,谷歌称之为云 SQL 谷歌为此收费。
  • 与亚马逊 Elastic Beanstalk 不同,App Engine 是一个封闭的系统。我需要在 PHP 中添加 cURL 扩展,这样我就可以运行第三方库(Twilio,我在第六章的中讨论过),但是它不被支持,也没有办法添加。相比之下,Amazon EC2 只需要一个命令:sudo apt-get install php5-curl

就在谷歌宣布 PHP 应用引擎的同一周,谷歌推出了计算引擎产品。这是一个运行 Linux(仅)虚拟机的 IaaS。我没有玩过它,但是我确信它的设置和其他云供应商的设置是相似的。因为它是 IaaS,而不是 PaaS,所以不会有任何功能限制。

云服务器总结

以下是我在尝试将灯堆栈放在云服务器上时学到的东西:

  • Amazon Elastic Beanstalk 很容易安装,因为灯堆已经为您安装好了,并提供了很大的弹性,但如果您不需要这种弹性,它会很贵。
  • 我研究的其他 PaaS 系统,Azure 网站和 Google App Engine,都像 Elastic Beanstalk 一样容易设置,但是它们有一些你无法回避的限制。然而,对于小网站来说,它们是免费的。
  • EC2、Azure、Rackspace 和 Google 都提供了虚拟机(IaaS ),在容量和操作系统选择方面有很多选项。如果您升级到多台服务器,它们也都具有负载平衡功能。
  • 所有虚拟服务器的成本都差不多,至少对于我的适度需求来说是这样。您可能会发现它们之间的细微差别,这取决于您如何设置它们以及您使用什么资源。
  • 用 Ubuntu 下载和安装 LAMP 栈并不太难,但是您可能不得不摆弄安全组、端点和密钥对文件。您还需要知道如何编辑 Apache 配置文件,但无论如何这是一项有用的技能。
  • Rackspace 比 EC2 或 Azure 更容易设置,据说它有很好的支持,所以如果你是新手,它可能是最好的选择。Azure 提供 90 天免费,亚马逊提供一年。

云服务器不像商业共享主机服务那样容易上手,商业共享主机服务已经为你设置好了,但是云服务器提供了对服务器的完全控制,如果你有技能利用它,或者如果你想发展这些技能,这将带来巨大的好处。

底线:如果你有一个重要的应用,使用虚拟机(IaaS)。封闭的 PaaS 系统最终会阻止你让你的应用按照你想要的方式工作。

安装新版本

假设您已经开发了您的 PHP/MySQL 应用,将它上传到生产服务器,在那里进行测试,并向乐于使用它的用户开放。您已经在您的开发系统上开发了一个新版本,现在是时候上传该版本进行测试和发布了。你是怎么做到的?有几种错误的方式,和往常一样,至少有一种正确的方式。首先,错误的方式。

做错了

安装新版本的一种方法是关闭服务,用一个只输出 HTML 页面的文件替换主index.php文件,说明服务不可用,上传新版本,主页面暂时命名为index-new.php,然后,一旦它被签出,将其重命名为index.php,使应用再次可用。这是可行的,但是在你准备好新版本的时候,让应用退出服务可能是不可接受的。这不是 1970 年的大型机应用,而是万维网!

另一个想法是把新版本上传到正在运行的版本上,而不是先在服务器上测试。这有两个问题。

  • 你不能跳过服务器上的测试。它和你当地的开发体系有太多的不同。很多事情都可能出错。
  • 由于上传文件至少需要 10 秒或更长时间,任何运行该应用的人都会看到新旧文件的混杂。他们得到什么系统是不确定的。你更希望他们运行你设计的东西。

您可以通过上传到一个不同的顶级目录来解决第一个问题,比如说一个名为stage的目录。假设生产目录名为prod。在stage测试完新版本后,你把prod改名为prod-old,把stage改名为prod。听起来相当不错,它解决了无法在生产服务器上测试新版本的问题,但仍然有两个问题,一个明显,一个不明显。

  • 虽然重命名几乎是瞬间完成的,但是仍然有可能有人在prod已重命名而stage尚未重命名的情况下访问应用,这会给他们一个 404(“未找到”)错误。
  • 尽管你已经在应用中不厌其烦地只使用相对文件引用,但它们仍然会得到新旧页面的混杂。

下面是对第二个问题的解释:在 PHP 中,一个类似

require_once 'file.php'

被视为相对于封闭脚本的目录,或者,如果在那里找不到,则被视为相对于当前目录。(通常在*nix 系统中,比如从 shell 中,相对路径总是相对于当前目录。)但是对于 HTML 来说就不是这样了,如果你有类似

<a href='file.php'>link</a>

HTML 是在浏览器中处理的,在客户端,而不是在服务器上,没有文件或当前目录的概念。(客户端操作系统可能有一个当前目录,但浏览器会忽略它。)在浏览器中,相对路径是相对于浏览器在请求中使用的 URL,该请求产生它正在处理的 HTML。此 URL 通常显示在浏览器窗口顶部的 URL 字段中。

例如,假设浏览器正在显示 URL

http://basepath.com/ClassicCameras/login.php

该页面包含以下 HTML 片段:

<a href='signup.php'>Sign up for new account</a>

浏览器不会在login.php的父目录中寻找signup.php。它对login.php一无所知,也不知道它在哪个目录下。它只知道它请求了显示在其 URL 字段中的 URL,并且有一些 HTML 通过网络传输过来。因此,它所能做的就是字符串操作:获取那个 URL,用signup.php替换最后一个组件,并发出另一个请求,这次是

http://basepath.com/ClassicCameras/signup.php

我知道这看起来很迂腐,因为看起来发生的事情和在父目录中查找是一样的,但真正发生的是一个全新的绝对 URL 导致了对另一个 HTML 页面的请求。

那么,我的观点是什么?只是如果你在一个会话还在页面间导航的时候把目录prod改成了prod-old,那么这个会话将不会停留在它之前所在的那个树中,这个树现在被命名为prod-old。它将切换并开始从新目录prod获取页面,因为prod位于它用来形成 URL 请求的 URL 中。

为了证明这一点,我在我的网站上放了两个目录,dir1dir2。每个都包含file1.phpfile2.php两个文件,但四个文件的内容不同,如图图 3-14 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-14 。两个目录,每个目录包含两个文件

现在,如果我在我的浏览器中输入 URL http://localhost/dir1/file1.php,我会得到图 3-15 左侧窗口中显示的内容,这就是你所期望的图 3-14 中显示的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-15 。浏览器中显示的两个 HTML 页面

然后,如果我点击链接,我会看到图 3-15 中右边显示的内容。现在我点击返回按钮回到第一个窗口,再次显示在图 3-16 左侧。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-16 。浏览器中显示的另外两个 HTML 页面

假设这是应用的版本 1,放在目录dir1中。我现在准备在目录dir2中安装版本 2,我一直在测试它。我巧妙地通过重命名目录来安装新版本,而不是通过复制文件这种愚蠢的方式。

mv dir1 dir1-old
mv dir2 dir1

我的用户从版本 1 开始,看着图 3-16 中左边显示的窗口,点击链接,得到右边显示的内容。这是第二版页面!上面写着dir2,2 版也是这么写的。(再次查看图 3-14 。)为什么?因为当浏览器看到指向file2.php的链接时,它会产生一个包含dir1的 URL,并将其发送给服务器。这时我已经更改了目录名,并且dir1包含了dir2曾经包含的内容。关键是浏览器没有停留在原来的目录内,现在改名为dir1-old。它有效地切换到了新的目录,从而切换到了新的版本。

如果你再看看图 3-15 和图 3-16 ,尤其是显示在小窗口顶部的网址,看起来没什么奇怪的。两次都是从dir1file1.phpdir1file2.php的链接。错误在于认为它会神奇地留在同一个目录中。但是,当所有的浏览器都必须处理组成它所请求的 URL 的字符串时,这怎么可能呢?

因此,重命名目录并不比在运行的版本上复制好多少。别忘了,在重命名期间,一些不幸的用户可能会得到 404 错误。

哎呀!您不能关闭应用,不能复制文件,也不能重命名生产目录。你到底是怎么安装第二版的?继续读。

做正确的事

下面是如何安装新版本:当你上传第一个版本到服务器进行测试时,它会进入一个名为stage的目录,即暂存目录。当你准备好部署它的时候,你给stage一个新的唯一的名字,比如说v1366988331。那是你给用户的网址。(其实送给用户太诡异了,但是忍着点;我会尽快解决这个问题。)用户可以永远和以前的v1366988331呆在一起,因为你永远不会把它拿走。好吧,如果你关心它占用的空间,也许一周之后,但是这个想法是用户可以完全在v1366988331完成他或她的会话。

一旦你将stage重命名为v1366988331,你就将v1366988331的内容复制回stage,它已经不存在了,因为你重命名了它。这只是为了让您有一个临时目录可以使用。因为除了开发人员没有人使用stage,所以复制需要多长时间或者它是否是原子的并不重要。如果需要一两分钟,你不在乎。

你开发下一个版本,把它全部或部分上传到stage上,你认为合适的话,测试它,最终决定是时候部署它了。你制定了另一个独特的名字,这一次,说,v1366988366。请注意,这是一个更大的数字,意味着更新的版本。这很重要,你必须确保这些数字是这样生成的。这次您将stage重命名为v1366988366,然后像以前一样,将v1366988366复制回stage

现在,在某种程度上,我将很快解释,启动会话的用户将获得最新版本。任何仍在旧版本中处理页面的人仍然可以不受干扰地这样做,因为您从未接触过那个目录。

与我之前展示的混乱命名相比,它的不同之处在于,您没有重命名生产目录,因此您可以重用生产名称。您不断生成新名称,唯一被重命名的目录是stage

好的,明白了吗?现在我将把它合并到一些简单的 PHP 文件中,让它自动工作。顶层文件命名为index.php;和往常一样,这是任何使用公共 URL 的人默认都可以访问的。这就是为什么那些v…的名字并不重要。它会计算出将用户传递到哪个版本。因为应用中的所有链接都是相对的,用户将停留在他或她开始时的版本中。文件index.php不在一个版本中,但也不必在,因为它的内容永远不需要改变。图 3-17 显示了目录布局,可以看到index.php在任何版本之外。(我将很快解释类似定位的install.php文件。)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-17 。暂存和版本目录

清单 3-3 显示了所有应用的顶层 PHP 文件index.php

清单 3-3 。通用顶级 PHP 文件

$code_dir = 'stage';
if (empty($_REQUEST['stage'])) {
    foreach (scandir('.', 1) as $f)
        if (preg_match('/^v[0-9]{10}$/', $f)) {
            $code_dir = $f;
            break;
        }
}
require_once "$code_dir/login.php";

变量$code_dir将保存版本目录,或者是stage,如果参数stage=1出现在 URL 上,这将在测试期间出现,或者是最新的v...目录。为了找到最新版本,它会扫描index.php所在的目录,以找到编号最高的目录。然后在循环之后,它引入一些 PHP 代码,这样就可以向用户呈现一个正确的初始页面。注意,login.php的内容在一个版本或暂存目录中,这是我们想要的,因为它很可能必须从一个版本改变到另一个版本。由于index.php中没有 HTML,它可以保持不变。

你必须编写login.php,这样它就可以嵌入到index.php中或者独立运行,在它自己的 URL 中。在第一种情况下,浏览器显示的是应用目录,这里是MyApp。在第二种情况下,浏览器位于版本或暂存目录中。HTML 中的相对引用必须双向工作。我稍后将展示您如何做到这一点的细节。

为什么我使用require_once引入login.php而不是改变头,这是将一个 PHP 文件传递到另一个文件的更常见的方式?即,如下所示:

header("Location: $code_dir/login.php");

如果我这样做了,用户在浏览器 URL 字段中输入的类似于http://basepath.com/MyApp的 URL 将被替换为指向login.php的 URL,因为位置头是由浏览器处理的,而不是服务器。键入的 URL 会立即被替换为类似

http://basepath.com/MyApp/v1366988366/login.php

并且用户永远不会有机会将公共 URL 拖动到书签。当用户进入应用时,带有v1366988366的内部 URL 仍然会出现在浏览器中,但这没关系——用户已经习惯了复杂的 URL 出现在浏览器中。(看看亚马逊产生的怪物。)它只是我们希望保持干净的初始公共 URL。

用户保存其中一个内部 URL 以备后用并没有太大的危险,因为,正如我在本书后面所展示的,除了那些可被注销用户使用的文件之外,所有 PHP 文件都要求它们在一个 PHP 会话中,而获得一个会话的唯一方法是以login.php开始。用户仍然可以保存内部书签,但它们是不可操作的,所以他们会学会不去打扰。

继续这个例子,清单 3-4 显示了一个login.php存根文件。

清单 3-4 。登录存根

<!DOCTYPE html>
<html>
<head>
<title>Login Page</title>
</head>
<body>
<h1>Login Page Stub</h1>
<p>[login form goes here]
<p>
<?php
    $dir = empty($code_dir) ? '' : "$code_dir/";
    echo "<a href='{$dir}start.php'>Login</a>";
?>
</body>
</html>

通常,在允许执行到start.php之前,会有一个登录表单和检查用户名和密码的处理,但是在这个存根中,表单丢失了,用户所要做的就是单击Login链接。如果该文件嵌入在清单 3-3 所示的index.php文件中,则$code_dir被定义并用于形成链接的相对引用中,因为解析该相对引用的浏览器位于应用目录中(图 3-17 中的MyApp)。但是,如果从应用内部直接调用login.php,浏览器的 URL 位于版本或暂存目录,因此相对引用是普通的start.php。正如我所说的,login.php是唯一需要这种处理的文件。文件start.php和所有其他应用文件都不会。

如果您的应用不需要登录,或者您有一些第一个屏幕而不是登录页面,该怎么办?好的,没问题。把login.php换成你喜欢的。重点是,第一页必须嵌入到index.php中,并且可以直接访问。

为了验证应用中的页面确实在它们的目录中,清单 3-5 和 3-6 显示了另外两个页面:一个起始页(start.php,链接到 from login.php,另一个页面,链接到 from start.php

清单 3-5 。起始页存根

<h1>Start Page Stub</h1>
<?php
echo "<p>PHP_SELF: {$_SERVER['PHP_SELF']}<p>";
echo "<p><a href='another.php'>Another</a>";
?>

清单 3-6 。另一页存根

<h1>Another Page Stub</h1>
<?php
echo "<p>PHP_SELF: {$_SERVER['PHP_SELF']}";
echo "<p><a href='start.php'>Start</a>";
?>

现在,这是 MyApp 在我的开发系统上的应用。应用 URL 调出登录页面,如图图 3-18 所示。请注意浏览器访问的 URL,这是 NetBeans 在开发过程中使用的 URL。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-18 。登录页面存根运行

正如我所说的,有了这个存根,你所要做的就是点击Login链接登录,这将带你到起始页,如图 3-19 所示。现在来看看 URL,它表明应用在临时版本中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-19 。开始运行页面存根

点击Another链接进入另一个页面,仍然是暂存版本,如图图 3-20 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-20 。另一个页面存根正在运行

假设 MyApp 已经过测试,可以部署了。目录stage被重命名为版本目录,因此index.php将转到那里,除非指定了参数stage=1,就像在开发期间一样。install.php程序位于应用目录中,如图图 3-17 所示,在清单 3-7 中。

清单 3-7 。版本安装程序

echo "<p>Installing production version";
$dir = 'v' . str_pad(time(), 10, '0', STR_PAD_LEFT);
if (!rename('stage', $dir))
    die('<p>Rename failed');
copy_dir($dir, 'stage');
echo "<p>Production version installed ($dir)";

function copy_dir($from, $to) {
    @mkdir($to);
    $d = dir($from);
    while (($f = $d->read()) !== false)
        if ($f[0] != '.') {
            $from_path = "$from/$f";
            $to_path = "$to/$f";
            if (is_dir($from_path))
                copy_dir($from_path, $to_path);
            else if (!copy($from_path, $to_path))
                die('<p>Copy failed');
        }
}

版本目录由“v”构成,后跟自 1970 年 1 月 1 日以来的时间(以秒为单位),在 2286 年之前一直是十位数,因此按词汇顺序排列版本目录名称也将按版本号顺序排列,这有助于查找最新版本。这就是index.php程序所做的(清单 3-3 )。

确定版本目录名称后,程序接下来会重命名登台目录。这一个操作就完成了安装,新版本就可以使用了。任何通过index.php访问应用的用户都将获得该版本。重命名是原子性的,所以用户不可能得到格式错误的目录名。

为了方便开发人员,程序接下来会将新版本复制回一个新的stage目录,这就是copy_dir函数的作用。我会让你自己算出这个函数。(提示:当前目录和父目录以句点开头,需要跳过。)

图 3-21 显示install.php正在运行(注意网址)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3-21 。安装新版本

安装了生产版本后,引用应用目录MyApp执行index.php,它选择最新版本,在本例中为v1367077979。添加stage=1运行 staging 版本。每当暂存目录中的新版本准备好用于生产时,install.php安装它,并且index.php开始引用它。因为版本一直存在,所以没有用户会从一个版本切换到另一个版本。因为安装是通过重命名而不是复制来完成的,所以用户不会得到部分更新的版本。正是我们想要的!

然而,有两个小问题需要解决。首先,我们不希望任何人都运行 staging 版本,如果他们发现世界上每个 PHP 开发人员都读过这本书,并采用我的方法安装新版本,他们就会运行 staging 版本。解决这个问题的简单方法是将stage参数的值设为某个随机数,而不是 1,然后在index.php中检查这个数。因此,要运行暂存版本,您必须使用如下 URL

http://basepath.com/MyApp?stage=459810329

第二个问题类似,但是对于install.php。你不希望任何人都能安装新版本。同样,一个简单的解决方案是需要一个密码作为参数,或者更简单,只需将文件命名为类似于install-83950471.php的名称,这样就没有人能猜出它的名称。(Apache 应该总是被设置为禁止目录列表,所以名字是不可发现的。)

章节总结

  • 三个平台很重要:服务器、客户机和开发。为了方便开发,该平台包含了其他两个平台。
  • 服务器操作系统可以是 UNIX 版本(nix)或 Windows。如果是nix,web 服务器一般会是 Apache。如果是 Windows,可以是 Apache,也可以是 IIS,但通常会是 IIS。
  • 在本书中,唯一关心的数据库是 MySQL,主要编程语言是 PHP,尽管您还必须处理 HTML、JavaScript 和 SQL。
  • 在大多数情况下,支持的重要浏览器是 Chrome、Internet Explorer、Firefox、Safari 和 Opera。
  • 由于 Internet Explorer 只能在 Windows 上运行,所以选择 Windows 作为您的开发系统是有意义的,至少对于您团队中的一名成员来说是如此。
  • 要处理浏览器和版本变体,使用 jQuery 来掩盖差异,并确保使用所有相关的浏览器和版本进行测试。
  • 使用 IDE 进行开发很方便。NetBeans 和 Eclipse 是两个不错的选择,都是免费的,NetBeans 更容易上手。
  • 安装 Xdebug 进行调试,安装 PHPUnit 进行单元测试。(也有其他选择。)两者都受 NetBeans 和 Eclipse 支持。
  • 使用 Subversion、Git 或 Mercurial 进行版本控制。(也有其他选择。)NetBeans 和 Eclipse 都支持这三者。
  • 最简单的生产主机是商业共享主机服务,但是基于云的虚拟机提供了更多的灵活性和性能保证。对于后者,你很可能必须自己安装灯组的放大器部分。
  • 要在生产服务器上安装 PHP 应用的新版本,请在一个临时目录中进行测试,然后在准备好部署它时将该目录重命名为一个惟一的版本名。安排应用自动运行最新版本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值