PHP-开发者工具基础知识-一-

85 阅读34分钟

PHP 开发者工具基础知识(一)

原文:PHP Development Tool Essentials

协议:CC BY-NC-SA 4.0

一、版本控制

如果您还没有在您的项目中使用某种类型的版本控制,那么您绝对应该现在就开始。无论您是一个单独的开发人员还是一个更大团队的一部分,版本控制为任何项目,无论大小,都提供了许多好处。

如果你不熟悉什么是版本控制,它是一个用来捕获和记录项目中文件变化的系统。它为您提供了这些更改的可视化历史记录,使您能够回过头来查看是谁做了更改,他们更改了什么文件和更改的内容,他们何时做了更改,以及通过读取他们的提交消息,为什么更改。此外,它还为您提供了一种机制来隔离代码中的变更,称为分支(稍后将详细介绍)。

有许多可用的版本控制系统,有些是免费的和开放源码的,有些是专有的和需要许可的。出于本章的目的,我们将关注免费的开源 Git。Git 最初是由 Linus Torvalds 为 Linux 内核项目开发的。Git 是一个分布式版本控制系统(DVCS ),它允许您将您的存储库的许多副本(镜像)分发给团队的其他成员,以便能够跟踪变更。这意味着每个拥有存储库克隆的人在克隆时都拥有系统的完整工作副本。Git 是为了简单、快速和完全可分发而构建的。

这一章的目的是给你一个 Git 的概述,涵盖足够的信息让你开始每天在你的项目中使用它。因为我只有一章来介绍这一点,所以我将只触及 Git 最常用功能的表面。然而,这应该足够让你舒服地使用它了。要更完整、更深入地了解 Git,请查看由斯科特·沙孔和本·施特劳布撰写的 Pro Git,可从 Apress 获得。

使用 Git

要开始使用 Git,首先需要在您的系统上安装它。访问 http://git-scm.com 并下载适用于您的操作系统的二进制文件,即可获得适用于 Mac OS X、Windows、Linux 和 Solaris 的二进制文件。除此之外,使用 Debian/Ubuntu 上的yum包管理器或apt-get,Git 也可用于 RedHat/CentOS 系统。在 Mac OS X 上,你可以通过安装 Xcode 命令行工具来获得它。在这篇手稿中,我们将使用 Linux 版本的 Git。

Git 配置

现在 Git 已经安装好了,让我们通过在 Git 配置工具中设置您的姓名和电子邮件地址来进行最少量的配置,以便为您所做的提交显示这些信息(提交是将新版本的代码放入存储库中的行为)。我们可以使用git config工具来完成这项工作:

$ git config --global user.name "Chad Russell"
$ git config --global user.email chad@intuitivereason.com

我们可以通过再次使用git config工具来验证这些设置,这次使用属性键作为我们想要检查的设置:

$ git config user.name
Chad Russell
$ git config user.email
chad@intuitivereason.com

请注意,您将在 Windows 和 Unix 环境中运行相同的配置命令。

初始化您的存储库

要创建您的第一个 Git 存储库,您只需使用git init命令。这将在您的源代码目录中初始化一个空的 Git 存储库。初始化之后,就可以对新的存储库执行第一次提交了。对于这个例子,我们有一个空目录,我们将在其中初始化,然后我们将添加一个README文件,最后我们将添加并提交文件到我们的新存储库。

请记住,Git 将根据调用 Git 命令的目录启动。例如,如果你在

C:\Program Files (x86)\Git

那么结果将会是

Initialized empty Git repository in C:/Program Files (x86)/Git/bin/.git/

随着本书的进展,我们将使用以下存储库和目录来跟踪我们将使用的各种代码示例:

$ git init
Initialized empty Git repository in /Apress/source/.git/

初始提交

既然我们已经初始化了我们的空存储库,我们将向它添加一个非常基本的README文件,然后执行我们的初始提交:

$ echo "This is our README." > README.md

现在,如果我们使用git status来查看我们的存储库的当前状态,我们将会看到我们现在有一个未被跟踪的文件,这意味着这个文件还没有被添加到我们的存储库中或者被 Git 跟踪变更。您可以在任何时候使用git status来查看您的存储库的工作分支的状态:

$ git status
On branch master

Initial commit

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        README.md

现在,我们将使用git add将文件添加到我们的存储库中:

$ git add README.md

如果我们再次查看git status,我们会看到我们的文件已经被添加,但是还没有提交(保存)到存储库:

$ git status
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   README.md

最后,我们将提交我们的更改,我们的新README现在将在我们的存储库中,并被跟踪以了解未来的更改:

$ git commit -m "Initial commit. Added our README"
[master (root-commit) e504d64] Initial commit. Added our README
 1 file changed, 1 insertion(+)
 create mode 100644 README.md

我们可以从从git commit收到的消息中看到,我们的提交被保存了。现在,如果我们再检查一次git status,我们会看到我们目前没有其他要提交的内容:

$ git status
On branch master
nothing to commit, working directory clean

暂存更改

我们的存储库中有我们最初跟踪的文件,并且已经看到了如何向 Git 添加一个新文件来进行跟踪。现在让我们更改README,然后准备并提交此更改。

我已经在README.md文件中添加了一个变更,修改了我们添加的初始文本,使其信息稍微丰富一些。让我们再次运行git status,看看它会显示什么:

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   README.md

它显示我们的README被修改了,但是还没有提交。我们通过使用git add命令来做到这一点。我们将添加它并再次检查状态:

$ git add README.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   README.md

最后,我们将进行新的提交,这将结束我们对文件所做的更改:

$ git commit -m "Updated README"
[master ca476b6] Updated README
 1 file changed, 1 insertion(+), 1 deletion(-)

Note

在 Windows 环境中,分段更改的配置可能会有所不同。

查看历史记录

有了我们刚刚做的所有更改,能够返回并查看我们的存储库的历史是很有帮助的。最简单的方法之一是使用git log命令。当没有参数传递给它时,git log将显示您的存储库中的所有提交,从您最近的更改开始,并从那里按时间顺序递减:

$ git log
commit ca476b6c41721cb74181085fd24a40e48ed991ab
Author: Chad Russell <chad@intuitivereason.com>
Date:   Tue Mar 31 12:25:36 2015 -0400

    Updated README

commit dc56de647ea8edb80037a2fc5e522eec32eca626
Author: Chad Russell <chad@intuitivereason.com>
Date:   Tue Mar 31 10:52:23 2015 -0400

    Initial commit. Added our README

有许多选项和参数可以传递给git log。您可以通过传入一个数字作为参数来限制结果的数量;您可以只查看特定文件的结果;您甚至可以使用--pretty参数和许多不同的选项来更改输出格式。例如,如果我们只想查看对我们的README.md文件的最后一次提交,并把提交总结到一行中,我们可以使用下面的代码:

$ git log -1 --pretty=oneline -- README.md
ca476b6c41721cb74181085fd24a40e48ed991ab Updated README

为了分解这个命令,我们告诉它限制为-1一个结果,使用oneline pretty 格式,并且-- README.md只用于我们的README.md文件。

Note

到目前为止,您最常用的命令将是git addgit commitgit loggit pullgit push。这些命令添加文件,将文件提交到存储库,从远程源提取更改,或者将本地更改推送到远程源(例如托管存储库—稍后将详细介绍)。然而,Git 提供了许多附加命令和子命令来执行各种任务。要查看完整的命令列表,您可以使用git --help,并使用git --help a显示可用的子命令。

忽略特定文件

在您的项目中,通常会有许多您不希望 Git 跟踪的文件和目录。Git 通过一个名为 Git Ignore 的文件提供了一种简单的指定方法。gitignore。您可以将这些文件保存在项目中的任何地方,但是通常您会从项目根目录开始创建。

创建并保存该文件后,可以在 IDE 或文本编辑器中编辑它,并添加想要忽略的文件和/或路径。现在,我想忽略我的 IDE PHP storm 创建的设置文件。PHPStorm 创建了一个名为.idea的目录,其中存储了许多特定于我的 IDE 对该项目的设置的文件。我们绝对不希望它出现在我们的存储库中,因为它与项目并不特别相关,而且它可能会给克隆这段代码并使用 PHPStorm 的其他开发人员带来问题。我们的初始。gitignore文件现在看起来像这样:

# Our main project .gitignore file
.idea/*

现在,我们有两条路线;第一个是注释,可以使用数字符号#将其添加到文件中的任何位置。第二行告诉 Git 忽略.idea文件夹和其中的任何内容,使用星号表示通配符匹配。然后,我们希望将此文件提交到我们的存储库,以便将它分发给其他任何可能克隆此存储库并对其做出贡献的人。

随着项目的增长,您有了不想要的新文件或目录,只需继续添加到这个文件中。其他经常被忽略的项目是包含密码或其他系统特定信息的配置文件、临时文件(如缓存、其他媒体)或项目不直接需要的资源,甚至是开发团队内部维护的资源。

删除文件

有时,您需要从存储库中删除文件。根据您的意图,有几种不同的方法来删除文件。

如果您想从存储库和您的本地工作副本中完全删除一个文件,那么您可以使用git rm命令来执行这个任务。如果您使用操作系统或 IDE 从本地副本中删除该文件,那么它将显示为需要提交的已删除文件。

让我们来看看。首先,我们将创建一个简单的文本文件添加到我们的存储库中,提交它,然后删除它:

$ touch DELETEME
$ git add DELETEME
$ git commit -m "Adding a file that we plan on deleting"
[master 5464914] Adding a file that we plan on deleting
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 DELETEME
$ git rm DELETEME
rm 'DELETEME'
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    DELETEME

$ git commit -m "Removed our temporary file"
[master 6e2722b] Removed our temporary file
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 DELETEME

$ git status
On branch master
nothing to commit, working directory clean

现在,我们将首先在本地系统上删除它,然后从 Git 中删除它,然后我们将提交更改:

$ touch DELETEME
$ git add DELETEME
$ git commit -m "Adding another temporary file to delete"
[master b84ad4f] Adding another temporary file to delete
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 DELETEME
$ rm DELETEME
$ git status
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        deleted:    DELETEME

no changes added to commit (use "git add" and/or "git commit -a")
$ git rm DELETEME
rm 'DELETEME'
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    DELETEME

$ git commit -m "Removing our second temporary file"
[master e980b99] Removing our second temporary file
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 DELETEME

最后,您可能会发现您想从 Git 中删除一个文件,这样它就不再被跟踪,但是您想在本地保存这个文件。也许您不小心提交了一个已经添加到您的.gitignore文件中的配置文件;出于显而易见的原因,您希望将其从 Git 中移除,但保留在本地。为此,您将使用--cache选项和git rm命令:

$ touch DELETEME
$ git add DELETEME
$ git commit -m "Adding a temporary file to delete one more time"
[master f819350] Adding a temporary file to delete one more time
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 DELETEME
$ git rm --cached DELETEME
rm 'DELETEME'
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    DELETEME

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        DELETEME

$ git commit -m "Removed temporary file just in the repository"
[master 26e0445] Removed temporary file just in the repository
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 DELETEME
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

        DELETEME

分支和合并

分支是一种机制,它允许您将代码变更的各个部分分离到某种子库中。合并是将这些代码重新组合在一起的方法。例如,假设您有一个主线存储库,大多数开发都是在这个存储库下进行的。然后,您需要在应用中构建一套全新的功能,但是您仍然需要对现有的代码库进行各种不相关的更改和错误修复。通过为这个新功能创建一个单独的分支,您可以继续对您的主线代码进行更改并跟踪您的更改,并单独处理新功能的更改。一旦您准备好将这个更改集成到您的主代码中,您将执行一个合并,这将把您的更改合并到主线分支中。

然而,请注意,Git 分支不像通往 Subversion ( git svn)分支的桥梁,因为svn分支仅用于捕获偶尔的大规模开发工作,而 Git 分支更多地集成到我们的日常工作流中。

首先,让我们创建一个分支来探索这一功能:

$ git branch branch-example
$ git checkout branch-example
Switched to branch 'branch-example'

我们用第一条命令创建了新的分支,名为branch-example。第二个命令告诉 Git 切换到那个分支,以便开始工作并跟踪那里的变化。分支之间的切换通过git checkout命令完成。现在,我们将为这个新分支创建一个测试文件并提交它:

$ touch test.php
$ git add test.php
$ git commit -m 'Added a test file to our branch'

如果我们切换回初始分支(主分支),我们会看到该文件不在那里:

$ git checkout master
Switched to branch 'master'
$ ls
README.md
$ git log
commit e504d64a544d6a1c09df795c60d883344bb8cca8
Author: Chad Russell <chad@intuitivereason.com>
Date:   Thu Feb 26 10:23:18 2015 -0500

    Initial commit. Added our README

合并

一旦我们准备好让测试分支中的变更出现在主分支中,我们将需要执行一个合并。当执行合并时,Git 将比较两个分支中的变更,并尝试自动将变更合并在一起。在变更冲突的情况下,意味着相同的代码行在两个分支中都被更改了,这将需要您手动干预来解决冲突。一旦解决,这将作为另一个提交被跟踪,并且您可以完成您的合并。

让我们将我们的branch-example变更合并到主分支中:

$ git merge branch-example
Updating e504d64..a6b7d2d
Fast-forward
 test.php | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test.php

既然我们已经合并了这个,在这种情况下我们不再需要我们的branch-example。我们可以再次使用git branch命令简单地删除它:

$ git branch -d branch-example

存储更改

在处理您的项目时,很多时候您可能需要在准备好提交您正在处理的内容之前从远程存储库中提取变更,或者您可能需要在准备好提交并且不想丢失您的变更之前切换到另一个分支来做一些其他的工作。这就是git stash命令派上用场的地方。

要保存您的更改,您只需调用git stash命令。您可以通过传入list子命令来查看您保存的 stashes,并且您可以通过使用apply子命令来重新应用更改。让我们来看看它的实际应用:

$ git status
On branch master

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   test.php

$ git stash
Saved working directory and index state WIP on master: 08e9d29 adding a test file
HEAD is now at 08e9d29 adding a test file
$ git status
On branch master

nothing to commit, working directory clean

您可以看到,我们对test.php进行了尚未提交的更改;调用git stash之后,我们现在有了一个干净的工作目录。看这里:

$ git stash list
stash@{0}: WIP on master: 08e9d29 adding a test file
$ git stash apply
On branch master

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   test.php

no changes added to commit (use "git add" and/or "git commit -a")
$ git status
On branch master

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   test.php

我们可以使用git stash list查看我们保存的库存。我们可以在调用git stash apply后重新应用它,并在我们的工作目录中看到它。默认情况下,调用git stash apply将应用列表中最近的存储。如果您想申请特定的库存,那么您必须提供您在调用git stash list时看到的库存编号。以前面的列表输出为例,我们将使用以下内容:

$ git stash apply stash@{0}

磨尖

Git 中的标记允许您用标签标记任何给定的提交,以供将来参考。例如,您可以使用它来标记代码的特定版本或开发过程中的其他重要里程碑。

Git 提供了两种不同的标记类型。还有轻量级标签,它只是指向提交的标签。相反,带注释的标签是完整的校验和对象,包含加标签的人的姓名和电子邮件,并且可以包括消息。强烈建议您总是使用带注释的标签,除非您需要临时标记一些东西,在这种情况下,轻量级标签就可以了。

轻量级标签

让我们创建一个简单的轻量级标记来演示,然后删除它并创建一个带注释的标记。

创建初始轻量级标签:

$ git tag v0.0.1

现在显示标签的详细信息:

$ git show v0.0.1
commit a6b7d2dcc5b4a5a407620e6273f9bf6848d18d3d
Author: Chad Russell <chad@intuitivereason.com>
Date:   Thu Feb 26 10:44:11 2015 -0500

    Added a test file to our branch

diff --git a/test.php b/test.php
new file mode 100644
index 0000000..e69de29

我们可以使用-d选项删除标签:

$ git tag -d v0.0.1
Deleted tag 'v0.0.1' (was a6b7d2d)

注释标签

现在创建带注释的版本:

$ git tag -a v0.0.1 -m "Initial Release"

Show the details of the annotated tag:
$ git show v0.0.1
tag v0.0.1
Tagger: Chad Russell <chad@intuitivereason.com>
Date:   Sun Mar 15 18:54:46 2015 -0400

Initial Release

commit a6b7d2dcc5b4a5a407620e6273f9bf6848d18d3d
Author: Chad Russell <chad@intuitivereason.com>
Date:   Thu Feb 26 10:44:11 2015 -0500

    Added a test file to our branch

diff --git a/test.php b/test.php
new file mode 100644
index 0000000..e69de29

如您所见,在带注释的版本中,我们有创建标记的人的日期、姓名和电子邮件。

撤消更改

有时,您可能会意外地提交想要撤消的内容,或者您可能想要将本地工作副本重置为上次提交或存储库历史中给定提交时的状态。撤销 Git 中的更改可以分解为以下几种方式:

  • 改进
  • 联合国实习方案
  • 文件重置
  • 软复位
  • 混合复位
  • 硬重置
改进

通过更改提交消息或添加额外文件来撤销之前的提交,可以使用带有git--amend选项来完成。例如,假设您有两个文件要提交,而您不小心只提交了其中一个。您可以将想要提交的另一个文件附加到同一个文件中,甚至可以使用- amend选项更改提交消息,如下所示:

$ git add second.php
$ git commit -m "Updated commit message" --amend

联合国实习方案

此操作将取消暂存已暂存但尚未提交的文件。卸载文件使用了git reset命令。例如,假设您意外暂存了两个要提交的文件,但您现在只想暂存并提交其中一个文件。您可以使用git reset命令和文件名来卸载它,如下所示:

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   first.php
        new file:   second.php

$ git reset HEAD second.php
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   first.php

Untracked files:
  (use "git add <file>..." to include in what will be 

committed)

        second.php

文件重置

文件重置意味着将您对文件的工作更改恢复到最近一次提交或您指定的较早提交。要将文件重置为最近一次提交,您将使用git checkout:

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   first.php

$ git checkout -- first.php
$ git status
On branch master
nothing to commit, working directory clean

如果您想将文件重置回特定的提交,您可以使用git reset以及提交散列和文件名,如下所示:

$ git reset a659a55 first.php

软复位

软重置会将您的存储库索引重置回最近一次提交或指定的提交,并且保持您的工作更改不变,而暂存您的文件。使用带有git reset--soft选项调用它。您可以指定要重置的提交哈希,也可以省略提交哈希,它将重置为最近的提交:

$ git reset –soft 26e0445

混合复位

与软重置非常相似,混合重置会将您的存储库索引重置回最近一次提交或指定的提交,而不会影响您正在进行的更改。但是,它会从转移中删除任何文件。这是只使用git resetgit reset以及提交散列的默认操作,例如:

$ git reset 26e0445

硬重置

最后,硬复位是所有选项中最危险的,所以只有在绝对必要时才使用它。这是在放弃所有工作和暂存更改的同时,将存储库硬重置回给定的提交。此操作不可撤销,因此在执行之前,请确保您知道要执行此操作:

$ git reset --hard e504337
HEAD is now at e504337 Added our first .gitignore file

云中的版本控制:Bitbucket 和 GitHub

使用 Git 时,拥有一个远程托管的存储库是常见的做法,而不仅仅是为了您自己的个人项目。虽然有许多不同的方式来实现这一点,但有两种非常流行的服务允许您在云中托管存储库。输入 Bitbucket ( http://bitbucket.com )和 GitHub ( http://github.com )。

这些服务都提供免费和付费计划。这两种服务提供的免费计划的最大区别是 Bitbucket 允许无限量的私有存储库,仅限于五个合作者,GitHub 只提供公共免费存储库。一旦您拥有了这些服务中的一个帐户并创建了您的存储库,您将在本地 Git 配置中定义这个远程存储库,以允许您向它推送和从中提取更改。

Bitbucket

让我们从 Bitbucket 开始吧。当您第一次访问他们的网站时,您将创建一个新帐户。创建您的帐户后,您将登录并获得设置新存储库的选项。在我们继续创建我们的存储库之前,我们可以做一个简单的步骤来简化与这个存储库的交互,这就是添加一个 SSH 密钥来进行身份验证。

Bitbucket 最重要的一个优点是它是 JIRA 集成的,并且支持 Mercurial。

SSH 密钥

您可以向您的 Bitbucket 帐户添加一个 SSH 密钥,这将允许您从本地机器上的 Git 中与它进行交互,而不必反复输入您的 Bitbucket 密码。为此,导航到管理帐户部分,然后找到“SSH 密钥”在这里,您可以从本地机器添加一个 SSH 密钥,该密钥将在您的帐户下使用任何远程存储库时用作授权。如果您从未设置过 SSH 密钥,那么在 Mac OS X 和 Linux 上,以及在 Windows 上使用 Git Bash,都可以很容易地完成。

在 Mac OS X 或 Linux 中,打开一个终端,或者在 Windows 上打开一个 Git Bash 提示符,然后发出以下命令并回答它提出的几个问题:

$ ssh-keygen -t rsa -C "chad@intuitivereason.com"

强烈建议您接受它提供的默认值,包括存储新密钥对的路径。

一旦这些步骤完成,您将创建一个公钥和私钥对。找到公钥(使用从ssh-keygen过程显示的路径),并使用您喜欢的文本编辑器打开它。然后,您将把公钥的内容复制到您的 Bitbucket 帐户并保存它。这将完成您帐户的密钥设置。

创建您的第一个远程存储库

有了 Bitbucket 帐户中的 SSH,现在就可以创建存储库了。首先,单击顶部导航栏中的“创建”按钮,这将带您进入创建表单。输入有关项目的信息,然后单击创建存储库。这将把您带到存储库配置页面。这将为您提供一些关于如何处理这个新存储库的选项。如果您还没有创建您的本地存储库,正如我们之前所做的,那么您可以使用“从头创建”选项。然而,在我们的例子中,我们希望将我们当前的存储库推到这个新的远程 Git 存储库。按照此屏幕上提供的说明,让我们链接我们的存储库,并对其进行第一次推送,以将我们的当前代码复制到其中:

$ git remote add origin git@bitbucket.org:intuitivereason/pro-php-mysql.git
$ git push -u origin --all
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (6/6), 524 bytes | 0 bytes/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To git@bitbucket.org:intuitivereason/pro-php-mysql.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from origin.

现在我们已经成功地推送了它,我们可以点击 Bitbucket 上的“Source”图标来查看我们的代码。

开源代码库

如果不使用 Bitbucket,你可能想使用 GitHub,当比较它们时,我们可以说它们有非常不同的计费结构,它们在历史查看器和协作功能上也有所不同。

使用 Github 的步骤非常相似。您首先找到并单击 New Repository 按钮,然后它会向您显示一个 Repository Create 表单,类似于我们使用 Bitbucket 时的表单。在这里,您将填写表单并创建存储库。在下一个屏幕上,您将看到基于这是一个新的还是现有的存储库的说明,就像 Bitbucket 一样。我们可以像使用 Bitbucket 一样添加这个存储库:

$ git remote add origin git@github.com:intuitivereason/pro-php-mysql.git
$ git push -u origin --all

推动、拉动和冲突解决

正如我们最初将代码推送到新的远程存储库一样,我们将使用git push命令继续将代码推送到新的远程存储库,并利用git pull命令从存储库中拉出代码,这些代码可能是其他人在我们上次拉出后提交的。

例如,假设您邀请另一位开发人员与您合作一个项目。您从远程存储库中取出最新版本,然后做一些工作。Developer 2 做了同样的事情,但是在您之前提交并把他的更改推送到远程存储库。当您进入 push 时,您将收到来自 Git 的消息,告诉您您的存储库版本落后了。然后,您将使用git pull命令获取任何更改,并尝试自动合并和重置您的更改。然后,您可以将您的更改推送到存储库。在使用存储库时,你们都将继续这种模式。

如果你们两个人都在处理样本文件,并且有重叠的修改,Git 将无法确定哪个版本是正确的。这叫冲突。您必须手动解决冲突,然后提交解决的更改并推回到远程存储库。

转到工具

有许多工具可以让使用 Git 变得更容易。许多 ide 提供了与 Git 的集成,Bitbucket 和 Github 都提供了自己的 GUI 工具,使得使用 Git 更加容易。

PHPStorm

如果您不熟悉 PHPStorm,它是一个流行的跨平台(Linux、Mac OS X 和 Windows) PHP IDE,由 JetBrains 开发。它是我在各种例子中使用的 IDE 然而,要做这本书里的任何练习,你不一定要有 PHPStorm。

您可以从以下网址下载 PHP Storm:

https://www.jetbrains.com/phpstorm/download/

如果在 PHPStorm 的项目根文件夹中有一个 Git 存储库,它会自动检测它并提供菜单项来执行许多不同的操作,如图 1-1 所示。

A332793_1_En_1_Fig1_HTML.jpg

图 1-1。

PHPStorm Git menu entries

在这里,我们可以看到,在查看这些选项时,我们可以在test.php文件上执行许多操作。从这里我们可以提交或添加文件,如果它还没有被添加的话。我们还可以做一个比较,将本地文件与存储库中的相同版本或者远程存储库中的最新版本进行比较。我们还可以将它与我们存储库中的另一个分支进行比较,或者查看这个文件的所有变更的历史。另一个功能是“恢复”功能,它允许您将文件中任何未提交的更改快速恢复到上次本地提交时的状态。

图 1-2 显示了存储库子菜单条目及其在该子菜单中提供的操作。

A332793_1_En_1_Fig2_HTML.jpg

图 1-2。

Git Repository sub-menu entry in PHPStorm

从这个条目中,我们可以查看分支、标记、合并、隐藏或取消隐藏我们当前的变更,或者从远程存储库获取、拉取或推送变更。

sourcetree(资源分区)

SourceTree 应用是一个面向 Windows 和 Mac 用户的免费 Git 客户端,由运行 Bitbucket 的 Atlassian 公司开发。这是一个非常强大的 Git GUI 客户端,使得本地和远程的 Git 库的工作和交互变得非常容易。

安装源代码树

SourceTree 可以通过访问 http://www.sourcetreeapp.com 下载。下载完成后,运行安装程序并按照说明将其安装到您的开发机器上。首次运行 SourceTree 时,它会提示您使用现有的 Bitbucket 或 GitHub 帐户登录。您可以登录或跳过这一步。如果您选择登录,您将能够在 SourceTree 的主书签/浏览器窗口中看到您的远程存储库。您可以随时选择在以后添加这些关联帐户之一。

添加存储库

要将 Git 存储库添加到 SourceTree,您将单击 New Repository 按钮,并选择是克隆现有的远程存储库、添加现有的本地存储库,还是创建新的本地或远程存储库(图 1-3 )。

A332793_1_En_1_Fig3_HTML.jpg

图 1-3。

Adding a new repository to SourceTree

通过选择“添加现有的本地存储库”,添加您在本章前面创建的新存储库这将让您导航到存储库初始化的目录,然后单击 Add 按钮。该存储库现在将在 SourceTree 书签/浏览器窗口中可见。只需双击存储库的名称即可调出完整的 GUI(图 1-4 )。

A332793_1_En_1_Fig4_HTML.jpg

图 1-4。

SourceTree repository view

从这里开始,您可以继续使用 Git 完成我们到目前为止讨论过的所有操作和其他操作,包括提交、分支、合并、推送到远程存储库和从远程存储库拉出等等。

GitHub GUI

GitHub 也有自己的免费 Git GUI。它使用起来非常干净和直观,但是缺少 SourceTree 中的一些高级特性。然而,如果你正在寻找一个好的、干净的接口来使用 Git,它绝对值得一看。

安装 GitHub GUI

和 SourceTree 一样,GitHub GUI 对 Windows 和 Mac 用户都可用。Mac 用户可以通过访问 https://mac.github.com 下载,Windows 用户可以通过访问 https://windows.github.com 下载。下载并安装后,GitHub 将引导您完成安装过程。

添加存储库

GitHub GUI 的一个有趣的特性是,它可以在您的系统上找到 Git 存储库,并在安装过程中向您提供这些存储库,还可以选择将它们导入到 GitHub GUI 中,开始在 GitHub GUI 中使用它们。如果您选择不这样做,也可以在以后使用菜单项添加或创建新的存储库。一旦您的存储库被添加到 GitHub GUI 中,您将看到存储库视图,如图 1-5 所示。

A332793_1_En_1_Fig5_HTML.jpg

图 1-5。

GitHub GUI repository view

gitg

gitg 是一个开源的 Git GUI,由 Gnome 基金会开发,只面向 Linux 用户。

安装 gitg

gitg 可以使用yumapt-get进行安装。gitg 不太能提供 SourceTree、GitHub 甚至 PHPStorm IDE 中的功能和可用性。然而,它确实为在 Linux 系统上浏览或搜索存储库的历史提供了一个很好的界面。

添加存储库

要使用 gitg 添加一个存储库,您可以点击“齿轮”图标,它会显示一个打开本地存储库或者克隆远程存储库的子菜单。添加完成后,您可以点击打开存储库视图,如图 1-6 所示。

A332793_1_En_1_Fig6_HTML.jpg

图 1-6。

gitg repository view

摘要

在这一章中,我们介绍了 Git 分布式版本控制系统(DVCS)。我们讨论了在日常开发中使用 Git 的基础知识,比如添加和提交变更、合并和分支。我们还介绍了使用流行的 Github 和 Bitbucket 服务处理远程存储库。然后,我们讨论了如何与其他开发人员合作,以及如何管理提交文件的冲突解决,然后讨论了一些可以用来简化 Git 工作的工具。希望您现在已经掌握了 Git 的知识,并且能够马上在您的项目中使用它!

在下一章中,我们将讨论虚拟化开发环境。

二、虚拟化开发环境

创建虚拟化开发环境允许您为特定项目形成封装的环境,这些项目可以有完全相同版本的操作系统、PHP、web 服务器、数据库、库、设置等。就像真的一样。这些环境使一切相互隔离,并且可以根据需要轻松地销毁和重新创建。这提供了许多好处:

  • 能够在各种 PHP 版本上运行多个项目,以匹配它们的生产版本,而无需尝试在您的开发机器上运行它们。
  • 当试图安装一个库、改变一个设置等时,没有机会在你的开发机器上的任何配置上搞砸任何事情。
  • 能够拍摄您的环境的快照,以便轻松恢复。

在这一章中,当我们研究虚拟化您的开发环境时,我们将只关注如何使用 vantage,这是一个用于构建完整的、可分发的开发环境的工具。

传统上,有两种设置开发环境的方法:

  • 客户端和服务器进程运行在同一台机器上。
  • 客户端和服务器运行在不同的机器上,这模拟了最终用户执行部署的应用的方式。

我们将看看使用虚拟化环境的好处,如何获得和设置 vagger,以及如何配置您的第一个环境。在本章结束时,您应该能够在运行一个简单的命令:vagrant up后轻松启动并运行您自己的虚拟机。

流浪汉简介

很有可能你以前听说过或者甚至看到过使用流浪者。如前所述,vagger 是一个用于构建完整的、可复制的、可分发的开发环境的工具。

Note

流浪者是在麻省理工学院许可下发布的开源软件。

它通过遵循一致的配置模式来做到这一点,允许您定义指令集来使用 vagger 的特定语言配置您的虚拟环境。这些指令存储在一个名为 Vagrantfile 的文件中,由于它只是文本,所以可以很容易地将其添加到项目的源代码控制库中,从而允许对这些环境配置进行版本控制,并允许在其他开发人员之间轻松分发。

最重要的是,我们可以把一个完整的流浪场景分成四个部分:

  • provider——这是一个虚拟平台,你的流浪者设置将在这个平台上运行。由于 vagger 不提供任何实际的虚拟化,它依赖于提供者来为您提供这种功能。默认情况下,流浪者支持 VirtualBox。但是,您可以使用许多其他提供商,如 Docker、VMWare、Parallels、Hyper-V,甚至是 AWS 和 DigitalOcean 等云提供商。
  • 盒子——盒子是用来建立你的流浪者设置的虚拟机镜像。他们可以被任何人和任何流浪者使用。有越来越多的公共流浪盒可供您使用,有些是基本操作系统安装,有些是预配置灯栈或其他配置和语言。除了公开可用的文件外,您还可以创建自己的流浪文件箱,这些文件箱可以公开共享,也可以仅供您和/或您的团队私人使用。
  • 流浪者文件–流浪者的配置文件。
  • Provisioners——Provisioners 用于在运行vagrant up进程时,允许您自动安装软件、更改配置和执行其他操作。有很多 provisioners 是由 vagger 支持的,但是为了这本书,我们将着眼于 Bash、Puppet 和 Ansible。

安装流浪者和 VirtualBox

在你使用 vagger 做任何事情之前,你必须首先安装一个虚拟机提供者,比如我们将在这些例子中使用的免费开源的 VirtualBox。你当然也需要安装流浪者本身。

VirtualBox 可以从其网站下载,网址为 https://www.virtualbox.org 。只需下载适用于您的操作系统的安装包,并按照安装程序上的说明进行安装。

和 VirtualBox 一样,流浪者可以从其网站 http://www.vagrantup.com 下载。下载适用于您的操作系统的安装程序包,并按照安装程序上的说明进行安装。一旦安装完毕,vagrant命令将在您的终端中可用。

在这本书里,我们将在 Linux 环境下使用流浪者。

流浪的命令

所有发布给 vagger 的命令都是通过终端中的vagrant命令来完成的。让我们来看看我们拥有的命令选项列表:

$ vagrant -h
Usage: vagrant [options] <command> [<args>]

    -v, --version   Print the version and exit.
    -h, --help      Print this help.

Common commands:
     box             manages boxes: installation, removal, etc.
     connect         connect to a remotely shared Vagrant environment
     destroy         stops and deletes all traces of the vagrant machine
     global-status   outputs status Vagrant environments for this user
     halt            stops the vagrant machine
     help            shows the help for a subcommand
     hostmanager     
     init            initializes a new Vagrant environment by creating a Vagrantfile
     login           log in to HashiCorp’s Atlas
     package         packages a running Vagrant environment into a box
     plugin          manages plugins: install, uninstall, update, etc.
     provision       provisions the Vagrant machine
     push            deploys code in this environment to a configured destination
     rdp             connects to machine via RDP
     reload          restarts Vagrant machine, loads new Vagrantfile configuration
     resume          resume a suspended Vagrant machine
     share           share your Vagrant environment with anyone in the world
     ssh             connects to machine via SSH
     ssh-config      outputs OpenSSH valid configuration to connect to the machine
     status          outputs status of the Vagrant machine
     suspend         suspends the machine
     up              starts and provisions the Vagrant environment

     version         prints current and latest Vagrant version

For help on any individual commands, run `vagrant COMMAND -h`

Additional subcommands are available, but are either more advanced or are not commonly used. To see all subcommands, run the command `vagrant list-commands`.

正如您可以从该命令输出的最后一点信息中看到的,还有其他子命令可供我们使用。为了本章的目的,我们将把重点放在最常用的命令和子命令上。

设置我们的第一个环境

安装了 VirtualBox 和 vagger 之后,开始运行你的第一个 vagger 环境是一个相对简单的过程。至少,你所需要的是一个基本的流浪者文件和一个选择的流浪者盒子。

首先,我们将使用一个基本的 Ubuntu 14.04 来设置一个最小安装。细读 Hashicorp(流浪背后的官方公司)社区盒子库目录,位于 https://atlas.hashicorp.com/boxes/search ,我看到我们要用的盒子是ubuntu/trusty64。使用两个命令,我们将初始化我们的流浪者设置,下载盒子,安装它,然后引导我们的新虚拟机(VM)。

你要做的第一件事是在VAGRANT_HOME环境变量中定义流浪者的主目录。这可以通过在 bash 中执行以下命令轻松完成:

$ export VAGRANT_HOME=/some/shared/directory

让我们为这个正在设置的 travel 实例创建一个新文件夹,然后我们将初始化这个 travel 设置:

$ mkdir VagrantExample1
$ cd VagrantExample1
$ vagrant init ubuntu/trusty64

您应该会看到一条返回的消息,告诉您已经在您的目录中放置了一个流浪者文件,您已经准备好运行vagrant up。在我们这样做之前,让我们看一下最初生成的流浪者文件:

# All Vagrant configuration is done below. The "2" in Vagrant.configure
  # configures the configuration version (we support older styles for
  # backward compatibility). Please don’t change it unless you know what
  # you're doing.
Vagrant.configure(2) do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://atlas.hashicorp.com/search.
  config.vm.box = "ubuntu/trusty64"

  # Disable automatic box update checking. If you disable this, then
  # boxes will only be checked for updates when the user runs
  # `vagrant box outdated`. This is not recommended.
  # config.vm.box_check_update = false

  # Create a forwarded port mapping, which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  # config.vm.network "forwarded_port", guest: 80, host: 8080

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  # config.vm.network "private_network", ip: "192.168.33.10"

  # Create a public network, which generally matches to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  # config.vm.network "public_network"

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  # config.vm.synced_folder "../data", "/vagrant_data"

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:
  #
  # config.vm.provider "virtualbox" do |vb|
  #   # Display the VirtualBox GUI when booting the machine
  #   vb.gui = true
  #
  #   # Customize the amount of memory on the VM:
  #   vb.memory = "1024"
  # end
  #
  # View the documentation for the provider you are using for more
  # information on available options.

  # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
  # such as FTP and Heroku are also available. See the documentation at
  # https://docs.vagrantup.com/v2/push/atlas.html for more information.
  # config.push.define "atlas" do |push|
  #   push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
  # end

  # Enable provisioning with a shell script. Additional provisioners such as
  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
  # documentation for more information about their specific syntax and use.
  # config.vm.provision "shell", inline <<-SHELL
  #   sudo apt-get install apache2
  # SHELL

end

如您所见,这里的大多数选项都被注释掉了。唯一不是的配置选项行是:

config.vm.box = "ubuntu/trusty64"  

这一行告诉 vagger 使用我们用vagrant init命令指定的盒子。

初始虚拟机设置

我们现在准备发布下一个也是最后一个命令vagrant up。这将首次引导我们的虚拟机,并按照我们的指示进行任何初始设置(配置)。现在,这只是一个基本的系统,所以它将下载我们第一次选择的机器并导入它,然后只需设置初始 SSH 密钥并使机器对我们可用。看这里:

$ vagrant up --provider virtualbox

当它下载并打开这个初始框时,你会看到相当多的输出。最后几行让您知道这是一个成功,可以使用了:

==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Mounting shared folders...
    default: /vagrant => /Apress/VagrantExample1

我们现在有了一个运行 Ubuntu 14.04 的新虚拟机。我们可以通过ssh连接到这个虚拟机,就像在任何其他 Linux 机器上一样。在使用 travel 时,我们通过发出vagrant ssh命令来实现这一点:

$ vagrant ssh
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-45-generic x86_64)

 ...

vagrant@vagrant-ubuntu-trusty-64:∼$

流浪者用户是每个框中设置的默认用户。该用户拥有完全的sudo权限,不需要任何额外的密码。

Note

记得运行命令vagrant –help来获得你可以使用的全部命令列表。

共享文件夹

默认情况下,vagger 会将你的项目文件夹与你的 VM 中的/vagrant目录共享。这允许您在开发机器上轻松地编辑直接位于项目中的文件,并看到这些更改立即反映在 VM 中。这种方法的一个典型应用是在你的 travel box 上设置 Apache,并将站点根文件夹指向/vagrant目录中的某个地方。此外,您可以使用默认的流浪者文件中的config.vm.synced_folder配置参数来指定额外的共享目录。

建立关系网

vagger 提供了多个选项来配置您的虚拟机的网络设置。使用config.vm.network方法调用控制所有网络选项。最基本的用法是使用转发端口,将内部端口(如用于常规 HTTP 流量的端口 80)映射到主机上的端口。例如,以下配置行将使您的虚拟机的常规 web 流量在http://localhost:8080可访问:

config.vm.network "forwarded_port", guest: 80, host: 8080

如果您希望指定一个私有 IP 地址,从这个地址您可以访问本地网络上的整个虚拟机,那么您可以使用config.vm.network "private_network"方法调用:

config.vm.network "private_network", ip: "192.168.56.102"

虚拟机设置

如果你想改变你的虚拟机正在使用的内存或 CPU 的数量,你可以使用我们的以config.vm.provider " virtualbox " do |vb|开始的浮动文件部分。您会注意到已经有两个条目被注释掉了,一个设置 Virtualbox GUI 设置,另一个设置内存。如果我们想把内存和默认的虚拟 CPU 改成 2048 MB 的内存和 2 个 CPU,我们可以在我们的流浪者文件的这个部分下添加以下内容:

config.vm.provider "virtualbox" do |vb|
    # Customize the amount of memory on the VM:
    vb.memory = "2048"

    # 2 virtual CPU’s
    vb.cpus = 2
end

在我们应用此更改之前,让我们检查一下我们的虚拟机当前显示的内容:

$ vagrant ssh
vagrant@vagrant-ubuntu-trusty-64:∼free -m
             total       used       free     shared    buffers     cached
Mem:           489        331        158          0         12        207
-/+ buffers/cache:        112        377
Swap:            0          0          0

vagrant@vagrant-ubuntu-trusty-64:∼$ cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 58
model name      : Intel(R) Core(TM) i7-3740QM CPU @ 2.70GHz
stepping        : 9
microcode       : 0x19
cpu MHz         : 2700.450
cache size      : 6144 KB
physical id     : 0
siblings        : 1
core id         : 0
cpu cores       : 1
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 5
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx rdtscp lm constant_tsc rep_good nopl pni monitor ssse3 lahf_lm

bogomips        : 5400.90
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:

我们可以通过运行vagrant reload命令将这些更改应用到我们的 Vagrantfile,这与执行vagrant halt关闭 VM,然后执行 vagger up 以启动它备份是一样的:

$ vagrant reload

现在,让我们再次ssh检查我们的虚拟机内存和 CPU 设置:

$ vagrant ssh
vagrant@vagrant-ubuntu-trusty-64:∼$ free -m
             total       used       free     shared    buffers     cached
Mem:          2001        208       1793          0         11         77
-/+ buffers/cache:        120       1881
Swap:            0          0          0

vagrant@vagrant-ubuntu-trusty-64:∼$ cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 58
model name      : Intel(R) Core(TM) i7-3740QM CPU @ 2.70GHz
stepping        : 9
microcode       : 0x19
cpu MHz         : 2702.438
cache size      : 6144 KB
physical id     : 0
siblings        : 2
core id         : 0
cpu cores       : 2
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 5
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl pni ssse3 lahf_lm
bogomips        : 5404.87
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:

Processor       : 1
vendor_id       : GenuineIntel
cpu family      : 6
model           : 58
model name      : Intel(R) Core(TM) i7-3740QM CPU @ 2.70GHz
stepping        : 9
microcode       : 0x19
cpu MHz         : 2702.438
cache size      : 6144 KB
physical id     : 0
siblings        : 2
core id         : 1
cpu cores       : 2
apicid          : 1
initial apicid  : 1
fpu             : yes
fpu_exception   : yes
cpuid level     : 5
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl pni ssse3 lahf_lm
bogomips        : 5404.87
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:

删除虚拟机

现在,就像我们设置这个虚拟机一样简单,让我们使用另一个简单的命令来销毁它及其所有痕迹,vagrant destroy:

$ vagrant destroy
    default: Are you sure you want to destroy the 'default' VM? [y/N] y
==> default: Forcing shutdown of VM...
==> default: Destroying VM and associated drives...

就这样,我们的流浪 VM 没了。然而,我们的流浪者文件仍然是完整的,并且 VM 可以通过简单地发出另一个vagrant up而被恢复。

默认流浪灯箱

我们前面的例子只是一台基本的、没有安装 Apache、MySQL 或 PHP 的裸机。如果您正在为 PHP 开发设置这个框,这不是很有帮助,除非您想推出自己的定制配置。

幸运的是,有许多社区提供的流浪者盒子已经预先配置了 Apache、MySQL 和 PHP,还有一些已经安装了流行的 PHP 框架和平台,如 Laravel、Drupal 等。

使用前面提到的 Atlas community repository 目录或Vagrantbox.es目录( http://www.vagrantbox.es/ ),您可以搜索并找到一个适合您的盒子,而无需任何其他配置更改。

使用 Ansible、Bash 和 Puppet 的高级配置

正如您从我们最初的例子中所看到的,让一个 VM 启动并运行起来非常容易。然而,当设置一个完整的开发环境来反映我们的生产设置时,仅仅一个基本的虚拟机对我们来说没有多大用处。如果你找不到一个已经配置了 LAMP 的 vagger box,那么每次你设置一个新的 VM 时都必须手动安装和配置 Apache、MySQL 和 PHP,这使得 vagger 变得没什么用处。

同样常见的是,即使已经设置了 LAMP,在初始设置之后还需要运行许多配置操作,比如将 Apache 指向框架的不同公共文件夹,或者为应用设置数据库。这就是使用一个受支持的供应器的高级配置派上用场的地方。

流浪者支持许多提供者。为了这一章,我们将看看 Ansible、Bash 和 Puppet。如果您只熟悉 Bash,那么开始使用它是最容易的。然而,有许多预配置的包可用于 Ansible(剧本)、Chef(食谱/烹饪书)和 Puppet(模块),即使在 Bash 中使用基本命令,这些包也会大大减少您执行这些任务所需的时间。

Bash (Shell)供应器

让我们从一个简单的例子开始,使用一个简单的 bash 脚本安装 Apache、MySQL 和 PHP。整个流浪者设置由两个文件组成:流浪者文件和我们的 bash 脚本。我们将把这个脚本称为provision.sh。这个脚本将使用apt-get安装 Apache、MySQL 和 PHP 的 Ubuntu repo 版本。

我们在我们的 Vagrantfile 中使用下面一行来告诉 vagger 使用 Bash 作为 provisioner,然后使用provision.sh脚本:

config.vm.provision :shell, :path => "provision.sh"

我们的provision.sh脚本的内容如下:

#!/bin/sh

set -e # Exit script immediately on first error.
set -x # Print commands and their arguments as they are executed.

export DEBIAN_FRONTEND=noninteractive

# Do an update first
sudo apt-get -y update

# Install Apache
sudo apt-get -y install apache2

# Install PHP
sudo apt-get -y install php5 php5-mysql php5-cli php5-common

# Install MySQL
echo mysql-server mysql-server/root_password password 123 | sudo debconf-set-selections
echo mysql-server mysql-server/root_password_again password 123 | sudo debconf-set-selections
sudo apt-get -y install mysql-server-5.6

# Restart Apache & MySQL to ensure they're running
sudo service apache2 restart
sudo service mysql restart

正如您所看到的,使用这个脚本,我们只需运行与手动设置虚拟机时相同的命令;然而,我们自动化了这个过程,因为 vagger 可以为我们运行 Bash 命令。

傀儡供应者

Puppet 是一个配置管理系统,它允许我们创建非常专业的浮动配置。通过在您的项目中包含特定的 Puppet 模块,Puppet 可以用于形成许多不同类型的流浪者配置。可以从 https://forge.puppetlabs.com/ 的傀儡锻造点获得这些模块。您使用的每个模块都有几个到许多不同的配置选项,以便根据您的具体需求定制环境。开始定制时,您应该参考每个模块的自述文件,以了解您可以使用哪些选项。

对于这个例子,从 Puppet Forge 下载 Apache、MySQL 和 PHP 清单,并按照网站上推荐的层次结构组织它们。您还应该从 Puppet Forge 下载一些必需的依赖项。我们将使用这些来设置一个带有 Apache、MySQL 和 PHP 的 VM,就像我们的 Bash 示例一样。

Note

清单是告诉 Puppet 如何处理所有模块的指令。

我们将把木偶清单放在默认位置,流浪者会在那里寻找它,在manifests/default.pp下面。首先,更新游民档案,告诉游民我们现在使用 Puppet 作为供应器:

config.vm.provision "puppet" do |puppet|
      puppet.manifests_path = "manifests"
      puppet.manifest_file  = "default.pp"
      puppet.module_path = "modules"

end

我们的目录结构如图 2-1 所示。

A332793_1_En_2_Fig1_HTML.jpg

图 2-1。

Puppet Vagrant directory structure

位于主manifests目录下的default.pp文件是告诉 Puppet 为我们的 VM 安装和配置什么的文件。您可以在这里定义设置所需的各种配置选项。为了这个例子,我保持了配置的简单和简洁:

# Update apt-get
exec { 'apt-get update':
  command => 'apt-get update',
  path    => '/usr/bin/',
  timeout => 60,
  tries   => 3
}

class { 'apt':
  always_apt_update => true
}

# Install puppet in our VM
package {
  [
    'puppet',
  ]:
    ensure  => 'installed',
    require => Exec['apt-get update'],
}

# Install Apache, set webroot path
class { 'apache':
  docroot => '/var/www/html',
  mpm_module => 'prefork'
}

# Install MySQL, setting a default root password
class { '::mysql::server':
  root_password           => '123',
  remove_default_accounts => true
}

# Install PHP and two PHP modules
class { 'php':
  service => 'apache2'
}
php::module { 'cli': }
php::module { 'mysql': }

# Install and configure mod_php for our Apache install
include apache::mod::php

正如您所看到的,这里发生的事情比我们在 Bash 脚本中的要多一些;然而,只需添加一些额外的配置参数,就可以进行配置更改和特定的安装设置,这种能力和灵活性使 Puppet 成为复杂设置的最佳选择。

可预见的供应

Ansible 是一个自动化工具,可用于多种类型的自治任务,并不仅限于与 vagger 一起使用。有了流浪者,我们可以使用它和剧本来自动设置和配置我们的流浪者机器。Ansible 剧本只是一个 YAML 文件,指示 Ansible 执行什么操作。

Note

您可能需要考虑在您正在配置的机器上运行 Ansible,因为这比使用安装脚本的组合要快。

使用 Ansible 比使用 Puppet 轻量级得多,因为不需要下载或包含各种模块来执行您需要的任务,并且来宾 VM 不需要安装任何特殊的东西。唯一的要求是运行 vagger 的主机必须安装 Ansible。各种操作系统的安装说明可以在 http://docs.ansible.com/intro_installation.html#getting-ansible 的可翻译文档中找到。

对于这个例子,我们将配置一个非常简单的 Ansible 剧本,在我们的流浪者机器上设置 Apache、MySQL 和 PHP,就像我们的 Bash 和 Puppet 例子一样。首先,我们必须指示 vagger 使用 Ansible 作为供应器,并提供我们的剧本文件的名称:

config.vm.provision "ansible" do |ansible|
      ansible.playbook = "playbook.yml"
end

然后我们指示 Ansible 安装 Apache、MySQL 和 PHP:

- hosts: all
  sudo: true
  tasks:
    - name: Update apt cache
      apt: update_cache=yes
    - name: Install Apache
      apt: name=apache2 state=present
    - name: Install MySQL
      apt: name=mysql-server state=present
    - name: Install PHP

      apt: name=php5 state=present

即使这个配置看起来很简单,也不要让它骗了你;Ansible 非常强大,可以执行复杂的配置。我们可以像使用 Puppet 一样,通过使用 Ansible 模板、变量、包含等等来组织和配置更复杂的设置,从而轻松地进行配置定制

高级配置结论

正如您所看到的,利用 provisioners 来自动完成构建环境的任务,使得设置开发环境比一遍又一遍地手工完成要容易得多。每个置备程序都有不同的方法来完成这些任务,为您和您的项目或环境提供了一系列选择和灵活性。

配置工具

现在,我们已经更好地了解了一些核心配置设置和可用的 provisioners,让我们看看两个配置工具,旨在使这些环境的设置更加容易。

Note

这两个工具目前都在开发中,所以它们都在不断地变化和发展。根据我的经验,它们可以让你快速启动并运行,但是它们也有周期性的问题和弱点。

木偶

这个名为“puffet”的工具使用 Puppet 作为配置语言,并提供了一个易于使用的 GUI 来配置您的环境。

访问木偶

您可以通过访问 https://puphpet.com 来访问该工具,如图 2-2 所示。

A332793_1_En_2_Fig2_HTML.jpg

图 2-2。

PuPHPet web-based Puppet configuration tool

PuPHPet 是在 GitHub 上公开托管的,是开源的,任何人都可以对它做出贡献。该工具的工作原理是生成一个清单 YAML 文件,以及构建和配置新的 VM 环境所需的各个 Puppet 模块。您可以直接使用它生成的配置,也可以根据需要进行修改和调整。

设置和使用木偶配置

一旦你完成了 PuPHPet 上的每个设置选项,你就可以下载你的定制配置了。这个下载包包含一个流浪者文件和一个puphpet目录,该目录包含您的环境所需的所有必要的 Puppet 清单和模块。

只需将这两项复制到您的项目目录中,您就可以运行vagrant up来设置和供应这个环境了。

Tip

需要注意的是,PuPHPet 生成的配置设置的一个很好的特性是在files目录下的文件结构。这个目录由另外四个目录组成,这允许您创建脚本,这些脚本将在每次启动时执行一次,等等。例如,您可以使用execute来执行安装后清理,运行定制命令来提供 PHP 应用特定的依赖项(如composer install),以及设置数据库数据等。

显形的

这是一个更新的工具,它使用 Ansible 代替 Puppet 作为供应语言。它类似于 puhppet,但是目前它还没有使用 pupppet 时所有的功能。它也在 GitHub 上公开托管,是开源的,任何人都可以为之做出贡献(图 2-3 )。

A332793_1_En_2_Fig3_HTML.jpg

图 2-3。

Phansible web-based Ansible configuration tool

就像使用 PuPHPet 一样,一旦您完成了 Phansible 上的每个设置选项,您就可以下载您的定制配置了。这个下载还包括一个流浪者文件和一个包含了playbook.yml文件的ansible目录。它还包含其他几个可以和 Ansible 一起使用的项目,这些项目是我们在前面的基本 Ansible 示例中没有用到的(比如前面提到的模板)。

Phansible 可在以下网址找到:

http://phansible.com/

流浪插件

随着你开始越来越多地使用 travel,你会周期性地需要额外的功能,而这些功能并不是 travel 提供给你的。幸运的是,流浪者有一个插件系统,很多时候插件的存在正是为了做你需要的事情。

使用vagrant plugin install plugin-name-here子命令可以非常容易地安装浮动插件。这里有几个有用的插件,当你开始选择使用 vagger 作为你的开发环境时,你会发现它们很有用:

  • 流浪者主机管理器——这个插件管理主机上的hosts文件,允许你轻松地指定一个临时的hosts条目,映射到你的虚拟机的 IP 地址。这允许您使用类似于生产地址的东西来轻松地设置对开发环境的访问。因此,如果你有 www.someproduct.com ,你可以设置类似dev.someproduct.comwww.someproduct.dev 的东西,并使用流浪者主机管理器自动将其添加到你的hosts文件中。它将在vagrant uphalt命令期间为您添加和删除该条目。这个插件在为你的虚拟机指定你自己的私有网络 IP 地址时非常有用。关于这个插件的更多信息可以在这里找到: https://github.com/smdahlen/vagrant-hostmanager
  • 流浪者共享——这个默认安装的插件允许你在任何地方使用 HashiCorp 的免费帐户与任何人共享你的环境。
  • 流浪图书管理员木偶——这个插件允许使用Librarian-Puppet安装木偶模块。
  • 浮动 Ansible Local–该插件允许您将 Ansible 用作您的供应器,但允许 Ansible 从来宾 VM 中运行,而不是让依赖 Ansible 的主机安装 ansi ble。
  • 提供商——虽然这不是一个特定的插件,但是有许多不同的插件允许在其他提供商上运行 Vagrant,例如 Parallels、KVM、AWS、DigitalOcean 等等。

一个完整的流浪插件列表你可以查看这个网页: http://vagrant-lists.github.io/

摘要

有了 vagger 的介绍,在你的开发过程中使用虚拟机是非常有意义的。希望本文涵盖的主题不仅向您展示了这一价值,还为您提供了在下一个项目甚至现有项目中快速启动并运行它所需的一切。在下一章,我们将讨论编程标准,以定义如何组织你的代码。

三、编程标准

编程标准是如何在任何给定的项目中构建代码的定义集。编程标准适用于从命名约定和空格到变量名、左括号和右括号位置等等的一切。我们使用编程标准作为在整个项目中保持代码统一的一种方式,不管从事这项工作的开发人员有多少。如果您曾经不得不在变量名、类名或方法名等不一致的项目中工作,那么您就会体验到在不符合编程标准的代码中工作是什么感觉。现在想象一下,如果您知道在整个项目中应该以什么样的方式构建代码,那么遵循和编写代码会变得多么容易?

这些年来,有许多 PHP 代码标准的受欢迎程度和流行程度时好时坏。有梨的标准,很详细;Zend 框架标准,由 Zend 推动;在过去五年左右的时间里,我们看到了由一个叫做 PHP-FIG 的开发人员委员会创建的标准。

虽然这一章的重点是 PHP-FIG,但是你选择的标准没有对错之分。从这一章中得到的重要启示是,至少遵循一些标准是很重要的!即使你创造了你自己的,或者决定创造你自己的变体,与已经流行的现有变体有一点偏离,只要选择一个并跟随它。

我们将看看 PHP-FIG 标准团体以及他们开发和推广的标准。我们还将研究一些工具,您可以使用这些工具来帮助在整个项目开发过程中正确使用给定的标准。

看一看 PHP-FIG

PHP-FIG ( php-fig.org)是 PHP Framework inter operability Group,它是最初在 2009 年 phptek 会议上成立的一个小组,目的是为 PHP 框架创建一个标准团体。它已经从五个创始成员发展到二十个,并且发布了几个标准。

PHP 标准建议是:

  • PSR-0 -自动装弹机标准
  • PSR-1 -基本编程标准
  • PSR-2 -编程风格指南
  • PSR-3 记录器接口
  • PSR-4 -自动装载机标准

在这一章中,我们将看看 PSR-1 和 PSR-2,它们代表 PHP 标准建议 1 和 2。这些标准相当简单,易于遵循,如果您愿意,甚至可以作为创建自己的编程标准的坚实基础。

PSR-1 基本编程标准

本标准的完整规范见 http://www.php-fig.org/psr/psr-1/ 。在撰写本文时,这是当前的标准。本节旨在为您提供该标准的概述以及遵循该标准的一些基本示例。该标准分为以下结构:

  • 文件
  • 命名空间和类名
  • 类常数、属性和方法
文件

本节描述了 PSR-1 下文件的标准定义。

PHP 标签

PHP 代码必须使用<?php标签或<?=格式的短 echo 标签。其他标记都是不可接受的,即使您在 PHP 配置中启用了短标记。

字符编程

PHP 代码必须只使用不带字节顺序标记(BOM)的 UTF-8。在很大程度上,这不是你需要担心的。除非你是在除了用于编程的文本编辑器(如 SublimeText、TextWrangler、Notepad++等)之外的其他工具中编写代码。)或 IDE,它不是应该自动包含的东西。不允许这样做的最大原因是,当用 PHP 包含可能有 BOM 的文件时,或者当您试图设置头时,这可能会导致问题,因为在设置头之前,它将被视为输出。

副作用

这个标准规定 PHP 文件要么声明新的符号(类、函数、常量等)。)或执行有副作用的逻辑,但决不能两者兼而有之。他们使用术语副作用来表示与声明类、函数或方法、常量等不直接相关的执行逻辑。因此,换句话说,一个文件不应该既声明一个函数又执行那个函数,如下面的例子所示,这样可以加强更好的代码分离。

<?php

// Execution of code
myFunction();

// Declaration of function
function myFunction() {
  // Do something here
}

命名空间和类名

PSR-1 中命名空间和类名的标准定义如下:

  • 命名空间和类必须遵循自动加载 PSR,该标准目前是自动加载标准 PSR-0 或改进的自动加载标准 PSR-4。按照这些标准,一个类总是单独存在于一个文件中(在一个文件中不能声明多个类),并且它至少包含一个级别的名称空间。
  • 必须使用StudlyCaps声明类名。

下面是一个类文件的示例:

<?php

namespace Apress\PhpDevTools;

class MyClass
{
  // methods here
}

类常数、属性和方法

在这个标准下,术语类是指所有的类、接口和特征。

常数

类常量必须使用下划线作为分隔符全部大写声明。

性能

当涉及到代码中的属性时,标准是相当灵活的。由您决定使用$StudlyCaps$camelCase$under_score属性名;如果它们不在彼此的范围内,您可以混合使用它们。因此,如果您的范围是供应商、包、类或方法级别,只要在给定的范围内保持一致即可。然而,我认为最好是找到一个,并在你的所有代码中坚持它。当你在类之间切换时,这将使代码的一致性和可读性变得更加容易。

方法

方法名必须使用camelCase()来声明。让我们来看看这个正在使用的标准:

<?php

namespace Apress\PhpDevTools;

class MyClass
{
    const VERSION = '1.0';

    public $myProperty;

    public function myMethod()
    {
        $this->myProperty = true;
    }
}

这就概括了 PSR-1 基本编程标准的全部内容。正如您所看到的,它非常简单,容易理解,并且在您第一次通读之后就很容易掌握。现在,让我们看看 PSR-2,这是编程风格指南。

PSR-2 编程风格指南

本指南是 PSR 协议 1 的延伸和扩展,涵盖了其他编程结构的标准。这是一个比 PSR-1 更长的读数。本指南是通过检查各种 PHP-FIG 成员项目之间的共性而制定的。本标准的完整规范见 http://www.php-fig.org/psr/psr-2/ 。与 PSR-1 一样,在撰写本文时,这也是当前的标准。本节旨在为您提供该标准的概述以及遵循该标准的一些基本示例。该标准分为以下结构:

  • 一般
  • 命名空间和使用声明
  • 类、属性和方法
  • 控制结构
  • 关闭
一般

除了以下规则外,为了符合新 PSR 协议,准则还必须遵循新 PSR 协议中概述的所有规则。

文件

所有 PHP 文件都必须使用 Unix 换行行结尾,必须以一个空行结尾,如果文件只包含 PHP,则必须省略关闭?>标记。

线

线条的标准遵循许多规则。前三个规则处理线路长度:

  • 线的长度不能有硬性限制。
  • 必须有 120 个字符的软限制。
  • 每行不应超过 80 个字符,如果超过 80 个字符,则应分成多行。

最后两条规则似乎有点自相矛盾,但我相信它们背后的推理是,一般来说,80 个字符是代码行的主要标准。关于为什么是 80,有很多很多的争论,但是大多数人认为这是所有设备和屏幕尺寸中最可读的长度。120 的软限制更多的是一种视觉提醒,即您已经通过了 80 到 120,这在大多数 ide 和文本编辑器上以各种屏幕尺寸查看也很容易阅读,但是偏离了广泛接受的 80。这里没有硬性限制规则,因为可能偶尔会出现这样的情况,您需要在一行中包含您所需要的内容,并且它超过了 80 和 120 个字符的限制。

其余的行规则是:

  • 非空白行的末尾不能有尾随空白。
  • 可以添加空行来提高可读性并指示相关的代码块。这真的很有帮助,这样你所有的代码就不会一起运行了。
  • 每行只能有一条语句。
刻痕

这条规则规定你必须使用四个空格,并且永远不要使用制表符。我一直支持在制表符上使用空格,几乎任何代码编辑器或 IDE 都可以很容易地将空格映射到您的 tab 键,这使得这条规则更容易遵循。

关键字和 true、false 和 null

这条规则规定所有的关键字都必须小写,常量truefalsenull也是如此。

命名空间和使用声明

该标准陈述了关于使用名称空间和名称空间声明的以下内容:

  • 在命名空间声明后必须有一个空行。
  • 任何使用声明都必须跟在命名空间声明之后。
  • 每个声明只能有一个 use 关键字。因此,即使您可以很容易地在 PHP 中用逗号分隔定义多个声明,您也必须每行都有一个声明,并且每个声明都有一个 use 声明。
  • 在 use 块之后必须有一个空行。
类、属性和方法

在这些规则中,术语类是指所有的类、接口和特征。

班级
  • extendsimplements关键字必须在与类名相同的行中声明。
  • 类的左大括号必须在它自己的行上,右大括号必须出现在类体之后的下一行。
  • 工具列表可以拆分成多行,每一行缩进一次。执行此操作时,列表中的第一项必须出现在下一行,并且每行只能有一个接口。
性能
  • 必须在类中的所有属性上声明可见性(公共、私有或受保护)。
  • 不能使用关键字var来声明属性。
  • 每个语句不能声明一个以上的属性。
  • 属性名不应以单个下划线为前缀来表示受保护或私有的可见性。Pear 编程标准强制实施了这种做法,所以您很有可能见过使用这种方法的代码。
方法
  • 可见性(公共、私有或受保护)必须在类中的所有方法上声明,就像属性一样。
  • 方法名不应以单个下划线为前缀来表示受保护的或私有的可见性。就像属性一样,您很可能见过这样编写的代码;但是,它不符合新 PSR 协议。
  • 方法名不能在方法名后用空格声明,并且左大括号必须在它自己的行上,右大括号必须在方法体后面的下一行上。左括号之后或右括号之前不应使用空格。
方法参数
  • 在方法参数列表中,每个逗号前不能有空格,但每个逗号后必须有一个空格。
  • 带有默认值的方法参数必须放在参数列表的末尾。
  • 您可以将方法参数列表拆分成多行,每一行都缩进一次。使用这种方法时,列表中的第一项必须在下一行,并且每行只能有一个参数。
  • 如果使用拆分参数列表,右括号和左大括号必须放在一起,各占一行,中间有一个空格。
抽象、最终和静态
  • 如果存在,抽象和最终声明必须在可见性声明之前。
  • 如果存在,静态声明必须在可见性声明之后。
方法和函数调用

当调用方法或函数时,方法或函数名和左括号之间不能有空格。左括号之后或右括号之前不得有空格。在参数列表中,每个逗号前不能有空格,但每个逗号后必须有一个空格。

论点单也可以拆分成多行,后续的每一行缩进一次。这样做时,列表中的第一项必须在下一行,并且每行只能有一个参数。

控制结构

控制结构有几个通用的样式规则。它们如下:

  • 控制结构关键字后必须有一个空格。
  • 左括号之后或右括号之前不得有空格。
  • 右括号和左大括号之间必须有一个空格,右大括号必须在正文之后的下一行。
  • 结构体必须缩进一次。
  • 每个结构的主体必须用大括号括起来。这绝对是一个非常有用的规则,因为它创建了控制结构的一致性并增加了可读性,并且没有大括号,即使它允许用于单行语句或使用 PHP 替代语法时,有时也会导致代码可读性较差。

接下来的几条规则对于下面的控制结构来说基本上都是一样的。让我们来看看它们中每一个的简单例子。

如果,否则,否则

这建立在先前规则的基础上,并声明控制结构应该将elseelseif放置在与来自先前主体的右括号相同的线上。此外,你应该总是使用elseif而不是else if,这样关键字都是单个单词。例如,这是一个完全兼容的if结构:

<?php

if ($a === true) {

} elseif ($b === true {

} else {

}

开关,外壳

case语句必须从switch关键字、break关键字或其他终止关键字(return, exit, die等)开始缩进一次。)必须缩进到与case正文相同的级别。当在非空的箱体中故意发生跌落时,必须有诸如// no break的注释。以下示例是一个兼容的开关结构:

<?php

switch ($a) {
    case 1:
        echo "Here we are.";
        break;
    case 2:
        echo "This is a fall through";
        // no break
    case 3:
        echo "Using a different terminating keyword";
        return 1;
    default: 

        // our default case
        break;
}

一边,一边做

whiledo while结构类似于ifswitch结构放置支撑和间隔:

<?php

while ($a < 10) {
    // do something
}

do {
    // something
} while ($a < 10);

新 PSR 协议文档显示,for语句的格式应如下例所示。根据他们列出的内容以及他们控制结构的一般规则,有一点还不清楚,那就是在下面的例子中,在$i = 0$i < 10之间是否需要空格。删除空格并使用 PSR-2 验证对 PHP 代码嗅探器运行它将导致它通过验证,因此这由您根据自己的喜好决定。以下两个示例都符合新 PSR 协议:

<?php

for ($i = 0; $i < 10; $i++) {
    // do something
}

for ($j=0; $j<10; $i++) {
    // do something
}

为每一个

符合新 PSR 协议的foreach语句的结构应如下例所示。与for语句不同,如果使用=>赋值来分隔键和值对,则需要空格:

<?php

foreach ($array as $a) {
    // do something
}

foreach ($array as $key => $value) {
    // do something
}

尝试,抓住(最后)

控制结构规则中的最后一个是try catch块。一个try catch块应该看起来像下面的例子。PSR-2 标准不包括任何关于finally块的内容(PHP 5.5 和更高版本),但是如果使用它,你应该遵循与try块相同的结构:

<?php

try {
    // try something
} catch (ExceptionType $e) {
    // catch exception
} finally {
    // added a finally block
}

关闭

闭包,也称为匿名函数,对于 PSR-2 标准有许多规则要遵循。它们非常类似于我们对于函数、方法和控制结构的规则。这主要是因为闭包是匿名函数,所以它们与函数和方法之间的相似性使它们接近相同。新 PSR 协议的规则如下:

  • 闭包必须在关键字function后和关键字use前后各有一个空格。
  • 左大括号必须在同一行,右大括号必须在下一行,跟在主体后面,就像函数、方法和控制结构一样。
  • 参数列表或变量列表的左括号后不能有空格,参数列表或变量列表的右括号前也不能有空格。同样,这与函数和方法是一样的。
  • 参数列表或变量列表中的每个逗号前不能有空格,参数列表或变量列表中的每个逗号后必须有一个空格。
  • 带有默认值的闭包参数必须放在参数列表的末尾,就像常规函数和方法一样。

以下是一些符合 PSR 新协议的闭包的例子:

<?php

// Basic closure
$example = function () {
    // function code body
};

// Closure with arguments
$example2 = function ($arg1, $arg2) {
    // function code body
};

// Closure inheriting variables
$example3 = function () use ($var1, $var2) {
    // function code body
};

就像函数和方法一样,参数列表和变量列表可以拆分成多行。适用于函数和方法的规则同样适用于闭包。

最后,如果闭包作为参数直接在函数或方法调用中使用,它仍然必须遵循和使用相同的格式规则。例如:

<?php

$myClass->method(
    $arg1,
    function () {
        // function code body
    }
);

这些规则总结了 PSR-2 编程风格指南。正如你所看到的,它们建立在 PSR 协议中规定的基本规则之上,并且大多数都建立在彼此的规则之上,有许多共同点。

PSR 的遗漏-2

PSR-2 标准有意忽略了许多元素(尽管随着时间的推移,这些项目最终可能会成为规范的一部分)。根据新 PSR 协议规范,这些遗漏包括但不限于以下内容:

  • 全局变量和全局常数的声明
  • 功能声明
  • 操作和分配
  • 行间对齐
  • 注释和文档块
  • 类名前缀和后缀
  • 最佳实践

用 PHP 代码嗅探器检查编程标准

拥有编程标准是一件很棒的事情,在线资源,例如 PHP-FIG 在 PSR-1 和 PSR-2 上提供的文档,可以帮助你做出正确的选择,使你的代码符合标准。然而,仍然很容易忘记规则或输入错误的东西使其无效,或者也许你是团队的一部分,不可能对每个人的代码进行代码审查以确保他们所有的提交都是符合的。这就是拥有一个每个人都可以轻松运行的代码验证器的好地方,甚至可以将它集成到自动化过程中,以确保所有代码都符合 PSR-1 和 PSR-2,甚至符合您选择的另一种编程标准。

这样的工具是存在的,它被称为 PHP 代码嗅探器,也被 Squizlabs 称为 PHP_CodeSniffer。PHP_CodeSniffer 是一组两个 PHP 脚本。第一个是phpcs,当运行时,它将对 PHP 文件(以及 JavaScript 和 CSS)进行标记,以检测是否违反了已定义的编程标准。第二个脚本是phpcbf,可以用来自动纠正编程标准违规。

PHP_CodeSniffer 可以通过多种不同的方式安装。您可以下载这两个命令的 Phar 文件,可以使用 Pear 安装,也可以使用 Composer 安装。以下是每种安装方法的步骤。

  1. 下载并执行 Phar 文件:

    $ curl -OL https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar
    
    $ curl -OL https://squizlabs.github.io/PHP_CodeSniffer/phpcbf.phar
    
    
  2. 如果您已经安装了 PEAR,您可以使用 Pear 安装程序来安装它。这是通过以下命令完成的:

    $ pear install PHP_CodeSniffer
    
    
  3. 最后,如果您使用并熟悉 Composer(使用 Composer 在第四章中有介绍),那么您可以使用下面的命令在系统范围内安装它:

    $ composer global require "squizlabs/php_codesniffer=*"
    
    

Note

PHP 代码嗅探器的完整在线文档可以在 https://github.com/squizlabs/PHP_CodeSniffer/wiki 找到。

使用 PHP_CodeSniffer

一旦你安装了 PHP_CodeSniffer,你可以通过命令行或者直接在一些 ide 中使用它,比如 PHP Storm 或者 NetBeans。从命令行使用它是开始使用它的最快方法。您可以使用它来验证单个文件或整个目录。

Note

PHP_CodeSniffer 的一个先决条件是机器上安装了 PEAR 包管理器。

现在,您可以在本书随附的代码库中的“第三章”分支中找到两个名为invalid.phpvalid.php的文件。我们将针对这些文件测试 PHP_CodeSniffer:

$ phpcs --standard=PSR1,PSR2 invalid.php

FILE: /Apress/source/invalid.php
----------------------------------------------------------------------
FOUND 10 ERRORS AFFECTING 5 LINES
----------------------------------------------------------------------
  3 | ERROR | [ ] Each class must be in a namespace of at least one
    |       |     level (a top-level vendor name)
  3 | ERROR | [x] Opening brace of a class must be on the line after
    |       |     the definition
  4 | ERROR | [ ] Class constants must be uppercase; expected VERSION
    |       |     but found version
  6 | ERROR | [ ] The var keyword must not be used to declare a

    |       |     property
  6 | ERROR | [ ] Visibility must be declared on property "$Property"
  8 | ERROR | [ ] Method name "ExampleClass::ExampleMethod" is not in
    |       |     camel caps format
  8 | ERROR | [ ] Expected "function abc(...)"; found "function abc
    |       |     (...)"
  8 | ERROR | [x] Expected 0 spaces before opening parenthesis; 1
    |       |     found
  8 | ERROR | [x] Opening brace should be on a new line
 11 | ERROR | [x] Expected 1 newline at end of file; 0 found
----------------------------------------------------------------------
PHPCBF CAN FIX THE 4 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------

从这个输出中我们可以看到,根据 PSR-1 和 PSR-2 标准进行验证时,检测到了十种不同的错误。您可以传入用于验证的不同标准,甚至可以传入由逗号分隔的多个标准,如本例中使用的 PSR1 和 PSR2。此外,在十个错误中,有四个被标记为可以使用 PHP 代码美化和修复工具自动修复,或者被称为phpcbf工具。我们现在可以对该文件运行phpcbf,并再次尝试验证,看看它是否能修复它:

$ phpcbf --standard=PSR1,PSR2 invalid.php
Changing into directory /Apress/source
Processing invalid.php [PHP => 52 tokens in 11 lines]... DONE in 4ms (4 fixable violations)
        => Fixing file: 0/4 violations remaining [made 3 passes]... DONE in 7ms
Patched 1 file

如您所见,phpcbf的用法和phpcs一样,您可以传入一个标准列表来进行校正,然后传入文件名。现在,再次对文件运行验证程序:

$ phpcs --standard=PSR1,PSR2 invalid.php

FILE: /Apress/source/invalid.php
----------------------------------------------------------------------
FOUND 5 ERRORS AFFECTING 4 LINES
----------------------------------------------------------------------
 3 | ERROR | Each class must be in a namespace of at least one level
   |       | (a top-level vendor name)
 5 | ERROR | Class constants must be uppercase; expected VERSION but
   |       | found version
 7 | ERROR | The var keyword must not be used to declare a property
 7 | ERROR | Visibility must be declared on property "$Property"
 9 | ERROR | Method name "ExampleClass::ExampleMethod" is not in
   |       | camel caps format
----------------------------------------------------------------------

在运行phpcbf之后运行测试,我们看到它在修复另一个问题时实际上修复了一个额外的问题,所以现在只发现了五个错误。现在,如果我们对我们的valid.php文件运行它,这是完全有效的,我们将看到一个有效的结果看起来像什么:

$ phpcs --standard=PSR1,PSR2 valid.php
$

对于 100%有效的文件,没有来自phpcs的输出,表明它是有效的。现在,如果我们想对我们的整个目录运行它,我们需要做的就是将它指向我们的文件所在的source目录。然而,这样做并看到大目录中每个文件的错误可能真的很难通读。

为了帮助解决这个问题,PHP_CodeSniffer 还提供了一个总结报告功能,可以总结每个文件以及在每个文件中发现的错误和警告的数量。它通过传入-–report=summary参数来调用。与直接对有效文件运行它一样,如果没有问题,它将不会在摘要中列出:

$ phpcs --report=summary --standard=PSR1,PSR2 source

PHP CODE SNIFFER REPORT SUMMARY
----------------------------------------------------------------------
FILE                                                  ERRORS  WARNINGS
----------------------------------------------------------------------
.../Apress/source/invalid.php             5       0
----------------------------------------------------------------------
A TOTAL OF 5 ERRORS AND 0 WARNINGS WERE FOUND IN 1 FILES
----------------------------------------------------------------------

PHP_CodeSniffer 配置

有许多不同的配置选项和方法来配置 PHP_CodeSniffer。本章不讨论所有这些选项,因此前面列出的在线文档是找到所有可用选项的最佳资源。但是,让我们看看几个不同的选项,以及如何设置它们。

Note

如果需要,PHP_codeSniffer 也可以以批处理模式运行。

可以使用- config-set参数更改默认配置。例如,要将默认检查标准更改为 PSR-1 和 PSR-2,而不是phpcs默认使用的 PEAR 标准,可以这样设置:

$ phpcs --config-set default_standard PSR1,PSR2
Config value "default_standard" added successfully

您也可以使用phpcs.xml文件直接在项目中指定默认配置选项。如果您在一个没有任何其他参数的目录中运行phpcs,将会用到它。这里有一个例子:

<?xml version="1.0"?>
<ruleset name="Apress_PhpDevTools">
    <description>The default phpcs configuration for Chapter 3.</description>

    <file>invalid.php</file>
    <file>valid.php</file>

    <arg name="report" value="summary"/>

    <rule ref="PSR1"/>
    <rule ref="PSR2"/>
</ruleset>

在这个文件中,指定了要检查的文件,以及我们想要使用的规则。使用多个<rule />标签指定多个规则。

PHP_CodeSniffer 自定义标准

如果您有自己的标准,或者已经采用了 PSR-1 和 PSR-2 的大部分标准,但决定偏离这里或那里的规则,您可以创建自己的自定义标准供phpcs使用。它基于 PSR-1 和 PSR-2 标准,并且只覆盖您希望偏离的部分。这是使用一个ruleset.xml文件完成的,然后使用- standard参数与phpcs一起使用,就像任何其他编程标准一样。

至少,ruleset.xml文件有一个名称和一个描述,格式和我们创建的phpcs.xml文件一样。然而,仅仅有名称和描述并不能为phpcs提供任何指令来覆盖现有的规则集。对于这个例子,假设我们想要改变标准,不限制方法名为camelCase。这可以通过如下配置来实现:

<?xml version="1.0"?>
<ruleset name="Apress PhpDevTools CustomStandard">
    <description>A custom standard based on PSR-1 and PSR-2</description>

    <!-- Don't restrict method names to camelCase -->
    <rule ref="PSR1">
        <exclude name="PSR1.Methods.CamelCapsMethodName"/>
    </rule>

    <!-- Additionally, include the PSR-2 rulesets -->
    <rule ref="PSR2"/>

</ruleset>

使用这个规则集,我们看到我们需要做的就是为我们的规则定义一个名称,包括我们的标准所基于的规则集,然后指定我们想要从这些规则集中排除的规则。现在,如果我们对我们的invalid.php文件运行验证,我们将看到错误从五个下降到四个,因为方法名冲突已经消失,因为我们的新标准没有将其限制为camelCase:

$ phpcs --standard=custom_ruleset.xml invalid.php

FILE: /Apress/source/invalid.php
----------------------------------------------------------------------
FOUND 4 ERRORS AFFECTING 3 LINES
----------------------------------------------------------------------
 3 | ERROR | Each class must be in a namespace of at least one level
   |       | (a top-level vendor name)
 5 | ERROR | Class constants must be uppercase; expected VERSION but
   |       | found version
 7 | ERROR | The var keyword must not be used to declare a property
 7 | ERROR | Visibility must be declared on property "$Property"
----------------------------------------------------------------------

PHP_CodeSniffer IDE 集成

如前所述,一些 ide(如 PHPStorm 和 NetBeans)有办法直接在其中集成 PHP_CodeSniffer。为这些 ide 配置它的确切过程可能会随着它们各自的供应商发布新版本而改变,所以我们在这里不做介绍。在撰写本文时,在线文档中介绍了为 PHPStorm 设置这一功能的步骤。

在我的 PHPStorm 安装中,我配置了 PHP_CodeSniffer 并设置为 PSR-1 和 PSR-2 标准。有了这个配置,如果我写的任何代码偏离了这些标准,我就可以从 PHPStorm 得到即时反馈,在违规的代码行下面有一条黄色的曲线,如图 3-1 所示。

A332793_1_En_3_Fig1_HTML.jpg

图 3-1。

Real-time PSR violation detection and hits in PHPStorm

您还可以对文件运行验证,并直接在 PHPStorm 中查看检查规则,如图 3-2 所示。

A332793_1_En_3_Fig2_HTML.jpg

图 3-2。

PHP_CodeSniffer validation results within PHPStorm

使用 phpDocumentor 编写代码文档

并非所有的编程标准都提供了关于代码注释的规则。例如,PSR-1 和 PSR-2 没有设置评论规则。然而,同样重要的是建立一个标准,让参与项目的每个人在评论时都遵循这个标准。

可以说,PHP 最流行的格式之一是 DocBlock 格式,它与提供关于类、方法、函数或其他结构元素的信息结合使用。当与 phpDocumentor 项目结合使用时,您可以为整个项目自动生成代码文档,并为所有开发人员提供一个简单的参考。

另一个常用的代码文档工具是 PHPXref ( phpxref.sourceforge.net)。通常,PHPDocumentor 和 PHPXref 主要有两个不同的目标:

  • phpDocumentor 主要用于从源代码中生成各种不同格式的真实文档。
  • PHPXref 工具主要用于帮助开发人员浏览大型 PHP 项目的代码文档。

安装 phpDocumentor

phpDocumentor 有几种不同的安装方式。可以下载 Phar 文件直接执行,可以用 Pear 安装,也可以用 Composer 安装。以下是每种安装方法的步骤。

  • 首先,你要检查梨的先决条件:

    http://pear.php.net/manual/en/installation.php
    
    
  • 下载并执行 Phar 文件:

    $ curl -OL http://www.phpdoc.org/phpDocumentor.phar
    
    
  • 如果您已经安装了 PEAR,您可以使用 Pear 安装程序来安装它。这是通过以下命令完成的:

    $ pear channel-discover pear.phpdoc.org
    $ pear install phpdoc/phpDocumentor
    
    
  • 最后,您可以使用 Composer 通过以下命令在系统范围内安装它:

    $ composer global require "phpdocumentor/phpdocumentor:2.*"
    
    

使用 phpDocumentor

如前所述,phpDocumentor 应该用于记录代码中的结构元素。phpDocumentor 识别以下结构元素:

  • 功能
  • 常数
  • 班级
  • 接口
  • 特征
  • 类别常数
  • 性能
  • 方法

要为这些元素中的任何一个创建一个文档块,你必须总是以完全相同的方式格式化它们它们将总是在元素之前,你将总是为每个元素创建一个块,并且在文档块和元素开始之间不应该有其他注释。

DocBlocks 的格式总是包含在名为 DocComment 的注释类型中。文档注释以/**开始,以*/结束。中间的每一行都应该以一个星号(*)开始。以下是我们之前创建的示例类的 DocBlock 示例:

/**
 * Class ExampleClass
 *
 * This is an example of a class that is PSR-1 and PSR-2 compliant. Its only
 * function is to provide an example of how a class and its various properties
 * and methods should be formatted.
 *
 * @package Apress\PhpDevTools
 */
class ExampleClass
{
    const VERSION = '1.0';

    public $exampleProp;

    public function exampleMethod()
    {
        $this->$exampleProp = true;
    }
}

正如我们在这个例子中看到的,一个文档块被分成三个不同的部分:

  • Summary——如果可能的话,summary 行应该是一行,并且仅仅是对我们正在记录的元素的快速总结。
  • 描述-描述更深入地描述了有助于了解我们元素的信息。如果有和/或需要,此处应包括背景信息或其他文字参考。描述区域还可以利用 Markdown 标记语言来样式化文本,并提供列表甚至代码示例。
  • 标签/注释——最后,标签和注释部分提供了一个地方来提供关于我们的元素的有用的、统一的元信息。所有标签和注释都以“at”符号(@)开始,并且各占一行。流行的标签包括方法或函数上可用的参数、返回类型,甚至元素的作者。在前面的例子中,我们使用 package 标签来记录我们的类所属的包。

Note

文档块的每个部分都是可选的;但是,没有摘要行的描述是不存在的。

让我们扩展前面的示例,并为示例类的每个结构元素提供文档块:

<?php

namespace Apress\PhpDevTools;

/**
 * Class ExampleClass
 *
 * This is an example of a class that is PSR-1 and PSR-2 compliant. Its only
 * function is to provide an example of how a class and its various properties
 * and methods should be formatted.
 *

 * @package Apress\PhpDevTools
 * @author Chad Russell <chad@intuitivereason.com>
 */
class ExampleClass
{
    /**
     * Class version constant
     */
    const VERSION = '1.0';

    /**
     * Class example property
     *
     * @var $exampleProp
     */
    public $exampleProp;

    /**
     * Class example method
     *
     * This method is used to show as an example how to format a method that is
     * PSR-1 & PSR-2 compliant.
     *
     * @param bool $value This is used to set our example property.
     */
    public function exampleMethod($value)
    {
        $this->$exampleProp = $value;
    }

    /**
     * Gets the version of our class
     *
     * @return string Version number
     */
    public function classVersion()
    {
        return self::VERSION;
    }
}

现在我们已经扩展了我们的示例,您可以看到使用了几个独特的标记,以及如何根据需要将这三个部分混合在一起的示例。关于可供 phpDocumentor 使用的标签的完整列表,请参见位于 http://www.phpdoc.org/docs/latest/index.html 的完整 phpDocumentor 在线文档。

运行 phpDocumentor

除了在使用 phpDocumentor 和 DocBlock 格式时为您的项目提供漂亮、统一的代码注释之外,您现在还可以轻松地生成代码文档,将您的注释转换为文档资源。一旦安装了 phpDocumentor,只需运行它来生成这个文档。

生成第一组文档只需要三个命令行选项中的两个。选项包括:

  • -d–指定您想要记录的项目目录。
  • -f–指定项目中要记录的一个或多个文件。
  • -t–指定生成和保存文档的目标位置。

对于这个示例,我们将针对之前的一个示例类运行它:

$ phpdoc -f valid.php -t doc

这里,我们告诉 phpDocumentor 运行文件valid.php,并将文档保存在一个名为doc的新文件夹中。如果我们查看新的doc文件夹,我们会看到新文档所需的许多不同的文件夹和资产。您可以通过打开在 web 浏览器中生成的index.html文件来查看它。我们可以在图 3-3 中看到示例类的页面。

A332793_1_En_3_Fig3_HTML.jpg

图 3-3。

phpDocumentor-generated class documentation

非结构性注释

最后,由于 phpDocumentor 只提供了结构化注释,所以建议您为自己的编程标准建立扩展到非结构化注释的准则。例如,Pear 编程标准提供了一个通用的经验法则,这是一个很好的策略。在他们的建议下,你应该总是注释那些你不想描述的代码段,或者那些你可能会忘记功能的代码段,如果你以后不得不回来的话。

建议您使用 C 风格的注释(/* */)或 C++注释(//)。不鼓励使用 Perl/Shell 风格的注释(#),即使 PHP 支持它。

摘要

在这一章中,我们讨论了为你的项目使用编程标准的好处。我们深入研究了 PHP-FIG PHP 标准建议以及遵循这些标准的一些代码示例。我们介绍了如何使用 PHP_CodeSniffer 工具作为代码的验证器,以确保您和您的团队成员遵循既定的标准。最后,我们讨论了使用 phpDocumentor 项目和 DocBlock 格式的代码注释和文档。

下一章我们将讨论框架。