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

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 格式的代码注释和文档。

下一章我们将讨论框架。

四、依赖项管理

依赖关系管理是一个系统,用于轻松管理(安装、使用、更新、卸载)项目中的库依赖关系。声明中的关键词是容易。很长一段时间,PHP 中的依赖管理实际上是不存在的。

当然,PEAR 从 1999 年就已经存在了,但是它不适合在应用中提供简单的依赖管理。它用于服务器范围内的全局安装包(想想apt-getyum),任何使用过 PEAR 的 XML 结构来创建包的人都可以证明它缺乏易用性。这就是 Composer 及其配套 Packagist 发挥作用的地方。

作曲家和包装家

Composer 是一个命令行工具,它是为 PHP 应用中的简单依赖管理而创建的。您用一个简单的 JSON 文件格式定义应用的依赖项,Composer 将为您安装和更新这些包。它的灵感来自 npm 和 Bundler,它们分别是 Node.js 和 Ruby 的包管理器,并借鉴了它们的许多特性和概念。

安装作曲者

安装 Composer 有两种不同的方式。您可以将它本地安装到您的项目中,或者作为系统范围的可执行文件全局安装。如果你只是第一次下载它或者没有权限在系统范围内安装它,那么本地安装就可以了。但是,最好的方法是全局安装它,这样您就有了一个安装的 Composer 版本,并且可以用于同一服务器上的所有项目,而不必维护不同的安装和版本。

在本地

要在本地安装 Composer,只需在项目目录中运行以下命令:

$ curl -sS https://getcomposer.org/installer | php

一旦安装程序运行,它会将composer.phar二进制文件下载到您的项目中。然后,您可以通过运行php composer.phar来执行它。

世界上

要将 Composer 作为系统范围的可执行文件安装,首先要下载composer.phar,然后将它移动到您在类 Unix 系统(Linux/OS X 等)上的路径中的某个位置。):

$ curl -sS https://getcomposer.org/installer | php
$ sudo mv composer.phar /usr/local/bin/composer

您现在可以通过在命令行上键入composer来执行 Composer。

Note

如果您使用的是 Windows 系统,那么您可以通过访问getcomposer.org下载 Windows Composer 安装程序,将其全局安装到您的系统上。

包装设计师

Packagist 是默认的 Composer 存储库。它是 PHP 软件包的公共集合,可以通过 Composer 安装。您可以通过访问packagist.org来访问 Packagist,在这里您可以轻松地搜索可用的软件包。通过引用版本和包名,Composer 知道从哪里下载您在项目中指定的代码。截至撰写本文时,它包含了超过 67,000 个注册包,近 314,000 个版本,并且自 2012 年 4 月以来已经安装了超过 10 亿个包!

除了作为开发者寻找他们希望安装的包的信息的可搜索资源之外,包作者可以很容易地将他们的项目提交给 Packagist,这样其他人也可以将 Composer 用于该项目。

一旦您的软件包成功注册到 Packagist,您就可以在您的 Bitbucket 或 GitHub 帐户中启用服务挂钩,并在您推送至远程存储库时立即更新您的软件包。

使用作曲家

在项目中开始使用 Composer 的唯一要求是安装 Composer 并创建项目的composer.json文件。该文件列出了您的项目依赖项,以及其他可能的元数据。

composer.json 文件

在最基本的形式中,唯一需要的是使用require键。对于我们的例子,我们将安装 PHPUnit 框架。我们将像这样创建项目的composer.json文件:

{
  "require": {
    "phpunit/phpunit": "4.8.4"
  }
}

现在我们将使用 Composer 安装这个框架,如下所示:

$ composer install

运行该命令后,我们将看到 Composer 的输出,它下载并安装 PHPUnit 所需的所有依赖项,如下所示:

$ composer install
Loading composer repositories with package information
Installing dependencies (including require-dev)
  - Installing sebastian/version (1.0.6)
    Loading from cache

  - Installing sebastian/global-state (1.0.0)
    Loading from cache

  - Installing sebastian/recursion-context (1.0.1)
    Downloading: 100%         

Composer 完成安装后,您会看到它生成了一个锁文件和自动加载文件,这些文件是将 PHPUnit 自动加载到您的应用中所需要的。您还将拥有一个名为vendor的新文件夹,其中包含 Composer 刚刚安装的所有软件包。

安装附加软件包

现在,当您需要将额外的包安装到您的应用中时,您只需将新需求的条目添加到您的composer.json文件中,并再次运行composer install。或者,您可以使用composer require命令,它将把条目添加到您的composer.json文件中,并在一个命令中安装它。例如,我们的应用需求之一可能是发送电子邮件,我们希望使用流行的 SwiftMailer 库。为此,我们首先在packagist.com上查找 SwiftMailer 并找到包名,然后运行以下命令:

$ composer require swiftmailer/swiftmailer
Using version ⁵.4 for swiftmailer/swiftmailer
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing swiftmailer/swiftmailer (v5.4.1)
    Downloading: 100%         

Writing lock file
Generating autoload files

从输出中,我们可以看到它更新了我们的composer.json文件,安装了 SwiftMailer 库包,更新了锁文件,并生成了新的自动加载文件。如果您查看composer.json文件,您现在会看到它被更新以反映 SwiftMailer:

{

  "require": {
    "phpunit/phpunit": "4.8.4",
    "swiftmailer/swiftmailer": "⁵.4"
  }
}

对于文件composer.json中的新条目,您可能注意到的一件事是 SwiftMailer 的版本号,它包括脱字符(^)操作符。我们将在几个部分中更深入地研究 Composer 版本,但现在这相当于告诉未来的composer update命令,我们总是想要最新的稳定版本,即>=5.4 < 6.0。如果我们想更具体地指定一个版本,就像 PHPUnit 例子中那样,那么我们可以有选择地将一个版本传递给composer require命令,就像这样:

$ composer require swiftmailer/swiftmailer 5.4

移除包

Composer 使向您的应用添加新库变得容易,并且它使删除不再需要的包变得同样容易。有两种不同的方法可以完成,要么从你的composer.json文件中手动删除包声明并运行composer update,要么利用composer remove命令。

如果我们决定删除之前安装的 PHPUnit 库,我们可以用这个命令来完成:

$ composer remove phpunit/phpunit
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Removing phpunit/phpunit (4.8.4)
Writing lock file
Generating autoload files

需求与需求开发

Composer 提供了两种不同的请求包的方法。第一种方法是使用require声明,正如前面所有的例子所示。第二种是通过使用require-dev声明。您应该总是使用require,除非某个包只需要用于您的应用的开发,而不需要用于在生产中运行应用。一个主要的例子是单元测试库,比如 PHPUnit。例如,现在让我们将 PHPUnit 重新添加到我们的应用中,但是这次指定它只在使用require-dev的开发中需要:

$ composer require phpunit/phpunit --dev
Using version ⁴.8 for phpunit/phpunit
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing phpunit/phpunit (4.8.8)
    Downloading: 100%         

phpunit/phpunit suggests installing phpunit/php-invoker (∼1.1)
Writing lock file
Generating autoload files

现在,如果您查看composer.json文件,您会看到phpunit已经被添加回来,但是这次是在require-dev声明下:

{
  "require": {
    "swiftmailer/swiftmailer": "⁵.4"
  },
  "require-dev": {
    "phpunit/phpunit": "⁴.8"
  }
}

编写器锁定文件

Composer 锁定文件是 Composer 已安装到您的项目中的确切版本的列表。这会将您的项目锁定在这些特定的版本中。将这个锁文件和composer.json文件一起添加到您的 Git 存储库中是很重要的,这样任何从事您的项目的人都将使用相同的包版本。这一点也很重要,因为如果您在您的试运行或生产环境中使用任何类型的部署方案,您可以利用composer install来确保这些环境已经安装了与您开发应用所针对的完全相同的软件包版本。这也确保了将来对这些库的更新可以在您的应用开发中完成,提交到您的存储库中,并准确地分发到这些其他环境中。这也消除了存储 Composer 已经安装在源代码存储库中的所有各种包的需要。

Note

因为您现在使用两个 Composer 文件来跟踪依赖文件和版本,所以没有必要将vendor文件夹提交并维护到您的 Git 存储库中。您应该将vendor目录添加到您的.gitignore文件中。

半自动的

当使用 Composer 时,它会生成一个自动加载文件vendor/autoload.php,用于自动加载您用 Composer 安装的所有库和包。只需将这个自动加载文件包含在您的应用中(确保到vendor/autoload.php文件的正确路径),所有已安装的包都将对您可用。

require __DIR__ . '/../vendor/autoload.php';

现在,如果我们想使用我们之前安装的 SwiftMailer 库,我们可以简单地调用它:

// Create the SwiftMailer Transport
$transport = Swift_MailTransport::newInstance();

// Create a Mailer instance with our Transport
$mailer = Swift_Mailer::newInstance($transport);

// Create our message
$message = Swift_Message::newInstance('Learning Composer')
    ->setFrom(array('john@doemain.tld' => 'John Doe'))
    ->setTo(array('jane@doemain.tld' => 'Jane Doe'))
    ->setBody('Composer is wonderful!');

// Send our message!
$result = $mailer->send($message); 

附加自动装载

除了自动加载所有已安装的 Composer 库包之外,您还可以将 Composer 自动加载器用于您自己的应用代码。为此,使用composer.json文件中的“自动加载”字段。

例如,如果您将自己的应用代码存储在一个名为src的文件夹中,您可以将以下条目添加到您的composer.json文件中:

{
  "autoload": {
    "psr-4": { "MyApplication\\": "src/" }
  },
  "require": {
    "swiftmailer/swiftmailer": "⁵.4"
  }
}

这告诉 Composer 为“MyApplication”名称空间注册一个 PSR-4(PHP-FIG 自动加载标准)自动加载器。现在,要让 Composer 更新vendor/autoload.php,您需要运行dump-autoload命令:

$ composer dump-autoload
Generating autoload files

除了 PSR-4 自动加载,Composer 还支持 PSR-0 自动加载、类别映射生成和文件包含作为有效的自动加载方法。然而,PSR-4 是使用 Composer 自动加载的推荐方法,因为它易于使用。

自动装载机优化

虽然开发环境不需要,但是强烈建议在生成用于生产的 Composer 自动加载程序时,使用内置的自动加载程序优化器。您的应用性能提升 30%并不罕见,尤其是当您的应用在 Composer 自动加载文件上花费大量时间时。

有两种不同的方法来生成优化的自动加载器。第一种是使用带有-o参数的dump-autoload命令:

$ composer dump-autoload -o
Generating optimized autoload files

例如,可以将此命令设置为在试运行和生产环境部署中运行,以便在开发中使用标准自动加载程序,而在测试和生产中使用优化版本。

除了通过dump-autoload命令生成优化的自动加载程序之外,您还可以在您的composer.json文件中指定它,以便您总是生成优化的版本。这是通过使用config指令完成的:

{
  "autoload": {
    "psr-4": {
      "MyApplication\\": "src/"
    }
  },
  "require": {
    "swiftmailer/swiftmailer": "⁵.4"
  },
  "require-dev": {
    "phpunit/phpunit": "⁴.8"
  },
  "config": {
    "optimize-autoloader": true
  }
}

包版本

Composer 在定义应用中安装的给定软件包的版本时提供了很大的灵活性。本质上,你可以将定义分为三类:基本约束、下一个有意义的发布和稳定性。

基本限制

确切的

使用基本约束,您可以告诉 Composer 通过只指定数字来安装一个精确的版本,比如1.2.4。这将确保您的应用总是使用这个确切的版本,不管composer update运行了多少次。

"require": {
    "vendor/packagea": "1.5.4"
  }

范围

Composer 允许使用比较运算符来指定应用的有效版本范围。有效的运算符是>、> =、AND 和OR逻辑。用空格或逗号分隔范围用于表示AND,用双管||表示OR。以下是一些有效的例子:

"require": {
    "vendor/packagea": ">1.5",
    "vendor/packageb": ">2.0 <3.0",
    "vendor/packagec": ">2.0,<3.0",
    "vendor/packaged": ">1.0 <1.5 || >= 1.7"
}

通配符

除了特定的版本和范围之外,还可以通过在版本声明中使用通配符来指定版本号模式。例如,如果我们想要一个包的 4.2 分支的任何子版本,它将被指定为:

"require": {
    "vendor/packagea": "4.2.*"
}

范围连字符

另一种指定范围的方法是使用连字符。使用连字符表示法时,连字符右侧的部分版本号被视为通配符。因此,考虑下面的例子:

"require": {
    "vendor/packagea": "1.5 – 2.0",
    "vendor/packageb": "2.0 – 2.1.0"
  }

本例中的packagea相当于>=1.5 <2.1。由于右侧的版本号被视为通配符,Composer 将其视为2.0.*

本例中的packageb相当于>=2.0 <=2.1. 0

下一个重要版本

Composer 中有两个不同的操作符可以用来定义版本限制,直到给定软件包的下一个重要发行版。

波浪号

使用波浪号操作符您可以定义一个您希望应用使用的最低版本标记,同时保护您不必更新到软件包的下一个重要版本(例如,下一个 x.0 版本)。考虑以下示例:

"require": {
    "vendor/packagea": "∼2.5"
}

该声明与指定>= 2.5 but <3.0相同。您也可以通过将您的需求定义为以下内容,在子版本级别对此进行定义:

"require": {
    "vendor/packagea": "∼2.5.2"
}

该声明与指定>= 2.5.2 but < 2.6.0相同。

脱字号

脱字符操作符^的工作方式与波浪号操作符非常相似,只是略有不同。它应该总是通过坚持更接近语义版本来允许不间断的更新。考虑以下示例:

"require": {
    "vendor/packagea": "².5.2"
}

该声明与指定>= 2.5.2 but <3.0相同。如您所见,这与波浪号略有不同,波浪号会阻止它更新到 2.6.0。最后,关于小于 1.0 的包,插入符号提供了一点额外的安全性,不允许如此大范围的版本更新:

"require": {
    "vendor/packagea": "∼0.5"
}

该声明与指定>= 0.5.0 but <0.6.0相同。

稳定性

当试图理解 Composer 将要安装的软件包的稳定性时,Composer 文档会变得相当混乱。阅读 Composer 文档的“版本”部分会让您认为 Composer 可能会仅仅根据您在指定版本时使用的约束来随机选择一个包的开发版本。

虽然这在技术上是正确的,但只有当您在composer.json文件中指定最小稳定性为dev时,这才适用。默认情况下,Composer 将始终选择稳定包,除非您使用require部分下的-dev后缀明确告诉它,或者您已经将最小稳定性配置定义为dev

更新包

到目前为止,我们已经介绍了如何使用 Composer 安装和删除软件包,以及如何指定您的应用所依赖的软件包的版本和稳定性。您将使用 Composer 进行的最后一个主要操作是更新您现有的库。这是使用composer update命令执行的:

$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files

默认情况下,运行composer update时,会执行一些动作。首先,如果您对您的composer.json文件做了任何手动更改来添加或删除一个包,它将会处理这些并安装或删除给定的包。此外,如果您的任何软件包版本没有锁定到一个确切的版本,它将寻找任何更新,并根据您的版本规范安装它们。最后,它将重新生成自动加载文件和锁定文件,并完成其操作。

有许多选项可以传递给composer update。例如,通过传递--dry-run,您可以看到 Composer 在实际执行之前会做什么。你可以选择通过--no-dev,这将导致它跳过更新或安装任何在require-dev声明下定义的包。您也可以定义您希望它更新的特定包,而不更新您的composer.json文件中定义的所有包。您可以通过向它传递一个或多个包来实现这一点,例如:

$ composer update swiftmailer/swiftmailer guzzlehttp/guzzle

全局安装软件包

Composer 可以用于全局管理和安装包,类似于 PEAR。这对于全局安装某些实用程序,甚至对于维护 Composer 本身的全局安装更新都很有用。

例如,如果我们想要更新 Composer 的全局版本,我们将运行以下命令:

$ sudo composer self-update

如果我们想安装一个像 PHPUnit 这样的实用程序,我们可以使用这样的命令:

$ composer global require phpunit/phpunit
Changed current directory to /home/vagrant/.config/composer
You are running composer with xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug
Using version ⁵.2 for phpunit/phpunit
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
...
Writing lock file
Generating autoload files

执行该命令时,请注意紧随其后的一行:Changed current directory to。这告诉您它将在/home/vagrant/.config/composer/vendor/下安装 PHPUnit 及其依赖项。我们当前登录的用户是“流浪者”这就是它选择这个目录的原因。为了便于执行,您需要将该目录添加到全局路径中。如果您的主目录中有一个.bashrc.bash_profile文件,请相应地调整以下命令。在我的例子中,我有一个.bashrc文件,所以我将使用它:

$ cd ∼/
$ echo 'export PATH="$PATH:$HOME/.config/composer/vendor/bin"' >> ∼/.bashrc

现在,通过注销并重新登录或使用source命令,重新加载以获取路径更改:

$ source .bashrc

现在可以执行 phpunit 了:

$ phpunit --version
PHPUnit 5.2.9 by Sebastian Bergmann and contributors.

梨和梨

正如在这一章的介绍中提到的,PEAR(PHP 扩展和应用库)曾经是唯一一个试图创建一个分布式系统的方法,用来在你的 PHP 应用中提供库。然而,PEAR 在许多不同的领域都有不足,这为创建更好的依赖管理工具铺平了道路,比如 Composer。

PEAR 确实取得了一些巨大的成功,过去是,现在仍然被许多不同的库和包使用,并且仍然是 PECL 用来安装 PHP 扩展的系统。过去几年 PEAR2 和 Pyrus 的创建旨在解决 PEAR 的一些缺点,但是它们没有看到 Composer 一直享有的牵引力和广泛的社区采用和开发。因此,在撰写本文时,PEAR2 和 Pyrus 已经处于 alpha 状态超过四年了。

还有人用梨吗?

这个问题的答案在我看来,在其他开发者看来,并且基于 Pear 下载统计页面上可用的当前下载统计数据,既有是也有否。Pear 的 PHP7 兼容版本自 2015 年 10 月首次发布以来,截至本文撰写之时,已有超过 650,000 次下载。有无数的旧 PHP 应用仍然依赖于各种 Pear 包,因此它仍然在使用这些包。我相信,基于我们的日常开发,以及 Packagist 上可用库数量的不断增长和大量开源平台转向使用和支持 Composer (Zend Framework 2、Symfony Framework、Drupal 8、Magento 2 等)。),Pear 作为库管理器和在应用中安装依赖项的使用正在迅速减少。

耦合逻辑

尽管总体上 Pear 的使用在减少,PHP 扩展社区库,更广为人知的名字是 PECL,今天仍然非常活跃。它是 PHP 扩展的公共存储库,通常用于安装开发所需的库。PECL 使用 Pear 来安装它的库,这在查看pecl命令的源代码时是显而易见的:

#!/bin/sh

# first find which PHP binary to use
if test "x$PHP_PEAR_PHP_BIN" != "x"; then
  PHP="$PHP_PEAR_PHP_BIN"

else

  if test "/usr/bin/php" = '@'php_bin'@'; then
    PHP=php
  else
    PHP="/usr/bin/php"
  fi

fi

# then look for the right pear include dir
if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
  INCDIR=$PHP_PEAR_INSTALL_DIR
  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
else
  if test "/usr/share/php" = '@'php_dir'@'; then
    INCDIR=`dirname $0`
    INCARG=""
  else
    INCDIR="/usr/share/php"
    INCARG="-d include_path=/usr/share/php"
  fi
fi

exec $PHP -C -n -q $INCARG -d date.timezone=UTC -d output_buffering=1 -d variables_order=EGPCS -d safe_mode=0 -d register_argc_argv="On" $INCDIR/peclcmd.php "$@"

现在,让我们看看在pecl命令的最后一行中引用的peclcmd.php的源代码:

<?php
/**
 * PEAR, the PHP Extension and Application Repository
 *
 * Command line interface
 *

 * PHP versions 4 and 5
 *
 * @category   pear
 * @package    PEAR
 * @author     Stig Bakken <ssb@php.net>
 * @author     Tomas V.V.Cox <cox@idecnet.com>
 * @copyright  1997-2009 The Authors
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
 * @link       http://pear.php.net/package/PEAR
 */

/**
 * @nodep Gtk
 */
//the space is needed for Windows include paths with trailing backslash
// http://pear.php.net/bugs/bug.php?id=19482
if ('/usr/share/php ' != '@'.'include_path'.'@ ') {
    ini_set('include_path', trim('/usr/share/php '). PATH_SEPARATOR .get_include_path());
    $raw = false;
} else {

    // this is a raw, uninstalled pear, either a cvs checkout or php distro
    $raw = true; 

}
define('PEAR_RUNTYPE', 'pecl');
require_once 'pearcmd.php';
/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * mode: php
 * End:
 */
// vim600:syn=php

?>

正如我们在这里看到的,pecl命令只不过是对pear命令的包装。每次你使用pecl安装一个新的 PHP 扩展或者更新一个现有的pecl扩展的时候,pear都会被使用。正因为如此,它使得下载、编译和安装所需的 PHP 扩展变得非常容易。例如,如果我们想安装 APC 用户域扩展APCu,我们只需执行以下命令:

$ sudo pecl install apcu
downloading apcu-5.1.3.tgz ...
Starting to download apcu-5.1.3.tgz (108,422 bytes)
.........................done: 108,422 bytes
39 source files, building
running: phpize
Configuring for:
PHP Api Version:         20151012
Zend Module Api No:      20151012
Zend Extension Api No:   320151012
...

install ok: channel://pecl.php.net/apcu-5.1.3

就这么简单,PECL 和 PEAR,连同phpize,已经下载、编译并安装了APCu扩展。

Note

如果您使用的 PHP 版本可以从您的操作系统的存储库中获得(yum / apt-get),那么您可以首先检查是否已经有一个 PECL 扩展可以直接从存储库中安装。这不需要使用 PECL。

我应该用梨还是梨?

纯粹基于 Pyrus 的当前开发状态和活动,以及 Composer 和 Packagist 软件包的许多好处和可用性,我认为 PEAR 或 Pyrus 不再是当今新开发中使用的最佳选择。

由于 PEAR 的全局安装特性及其提供的管理,它仍然是一个有用的工具,在某些情况下,它仍然是为某些实用程序安装系统范围的依赖项的唯一工具。让我们看一个使用 PEAR 在开发机器上安装 PHP CodeSniffer 工具的例子。

使用 PEAR 安装全局实用程序

$ sudo pear install PHP_CodeSniffer
downloading PHP_CodeSniffer-2.5.1.tgz ...
Starting to download PHP_CodeSniffer-2.5.1.tgz (484,780 bytes)
....................done: 484,780 bytes
install ok: channel://pear.php.net/PHP_CodeSniffer-2.5.1

PHP 代码嗅探器现在可以立即供您使用。您可以测试它是否像这样工作:

$ phpcs --version
PHP_CodeSniffer version 2.5.1 (stable) by Squiz (http://www.squiz.net)

如果在尝试运行前面的命令时收到警告,比如PHP Warning:  include_once(PHP/CodeSniffer/CLI.php): failed to open stream: No such file or directory,这意味着 PHP CLI 的php.ini不存在,或者没有将 PEAR 安装路径添加到php路径中。您可以通过以下步骤进行检查,首先检查安装在您系统上的pearinclude路径:

$ pear config-get php_dir
/usr/lib/php/pear

现在检查您的 PHP CLI 正在使用哪个配置文件:

$ php --ini
Configuration File (php.ini) Path: /etc/php/7.0/cli
Loaded Configuration File:         /etc/php/7.0/cli/php.ini

这将给出比前面的代码更多的输出,但是您想要寻找Loaded Configuration File行。

现在,采用前面列出的配置文件的路径,检查 PHP include路径:

$ php -c /etc/php/7.0/cli/php.ini -r 'echo get_include_path()."\n";'
.:/usr/share/php:

因此,如您所见,include路径不包括pear include路径。为了解决这个问题,我们将打开php.ini并将其添加到include_path指令中,如下所示:

include_path = ".:/usr/share/php:/usr/lib/php/pear"

如果您尝试再次执行phpcs命令,它现在会执行,因为它知道从哪里包含文件。

摘要

在这一章中,我们介绍了 Composer 和 Packagist,以及如何一起使用它们来管理应用的依赖关系。我们从头到尾介绍了立即使用 Composer 所需的一切,以及在管理应用的依赖关系时使用 Composer 的各种日常交互。我们还了解了 PEAR 及其在当今 PHP 开发中的作用。

我希望您在阅读完这一章后,对 Composer 的使用和它对您的应用开发的影响有一个非常清晰的了解,并且您现在有资源和能力马上使用它。

五、框架

即使你对 PHP 非常陌生,你也可能已经偶然发现了一些 PHP 框架。Symfony、Zend、Laravel、Yii 和 CakePHP 只是你可以使用的一些流行的选择。

当我在 1999 年第一次开始用 PHP 开发时,这些选项都不可用。那时候,PHP 应用是逻辑、HTML、JavaScript、SQL 查询以及更多分散在成百上千个文件中的东西的混合体。几年后,一些 PHP 开发框架在 2005-2006 年间开始成形,其中一些至今仍然存在并蓬勃发展(例如 Symfony 和 Zend Framework)。

诚然,我最初抵制使用这些新框架之一的想法。此时,我已经开始为我的站点和应用开发某种类型的已定义结构,因为我试图模仿 MVC 风格的结构,并将某种类型的分离和组织引入到我周围正在开发的疯狂中。我不想丢掉它,去学习一些对 PHP 来说非常新的东西。然而,随着这些框架开始成熟,社区支持开始建立,我意识到我没有一个好的理由继续坚持下去。我一头扎进了这些流行的框架中,并且从未后悔过这个决定。

为什么要使用框架?

这是我经常被那些还没有和一个人一起工作的人问到的问题。一个框架有什么特别之处,以至于我应该使用它,而不是自己做独立的 PHP 开发?好处有很多:

  • 所有站点和应用都熟悉的定义好的结构
  • 一个为框架代码库的改进做出贡献的社区,可以回答之前已经问过和回答过的问题(堆栈溢出,有人吗?)
  • 一套预先开发的功能,您不必为每个应用重新开发
  • 您可以使用模块、库和插件来立即添加附加功能
  • 更好的可测试性,有可能与 PHPUnit 集成进行单元测试
  • 与 ORM 的现有集成
  • 在应用中预先建立设计模式的使用。通常,使用一个框架意味着你不得不至少在某种程度上遵循它的范例,这可以产生更好的结构和组织的代码
  • 可重用和可维护的代码目的

关于 PHP 和框架的更多信息,请参考phpframeworks.com

Note

框架不是任何 PHP 开发的必需品。但是,它们是一个非常有价值的工具,可以帮助您构建更好的应用。

那么,框架是什么样子的呢?让我们深入研究一些广受欢迎和社区支持的框架。对于每个框架示例,我们将了解:

  • 安装是多么容易
  • 框架的总体结构
  • 如何让一个简单的动作和控制器工作
  • 如何进行简单的数据库调用并显示结果

Zend 框架 2

我们将从查看 Zend Framework 2 开始。Zend Framework 2,俗称 ZF2,是由 Zend Technologies 创建的第二代企业级框架。Zend Technologies 是由 Andi Gutmans 和 Zeev Suraski 创办的公司,自从拉斯马斯·勒德尔夫最初创建 PHP 以来,他们为 PHP 的发展做出了很大贡献。ZF2 标榜自己是模块化的、安全的、可扩展的、高性能的、企业就绪的,并得到了一个庞大而活跃的社区基础的支持。正如其模块化一样,ZF2 依赖于 Composer,由许多组件组成,所有组件都可以通过 Packagist 获得。

安装 ZF2

安装和运行 Zend Framework 2 非常简单快速。

Note

由于框架实现的 PHP 5.3+中的新特性,以及许多组件的主要重写,ZF2 与 ZF1 不向后兼容。

对于这个例子,我们将使用 Composer 安装 ZF2 框架应用,它可以在 GitHub ( https://github.com/zendframework/ZendSkeletonApplication )上获得:

$ composer create-project -n -sdev zendframework/skeleton-application zf2

该命令将在名为zf2的文件夹中安装 ZF2 框架应用。当它完成时,你应该在新创建的zf2文件夹中看到一些目录(图 5-1 )。

A332793_1_En_5_Fig1_HTML.jpg

图 5-1。

Directories in the newly created zf2 folder following installation of the ZF2 skeleton framework application

每个文件夹都有特定的用途,并作为任何 ZF2 应用的基础结构。这些文件夹的功能如下:

  • config–所有的全局应用配置文件都可以在这个目录中找到。诸如定义应用中的模块、数据库配置、日志配置和应用缓存配置都包含在这里。
  • data–该文件夹是存储应用数据的地方。缓存文件、日志文件和其他类似的文件都存储在这里。
  • modulemodule文件夹是您的所有应用逻辑所在的位置。在这里,您可以找到组成您的应用的所有各种模块。我们很快就会看到module文件夹的结构。
  • public——public文件夹是你的应用的网络根目录。在您的 web 服务器配置中,文档根目录将设置为该文件夹。这里存储了所有面向公众的资产,如 JavaScript、CSS、图像、字体等。
  • vendor–该文件夹包含您在应用中安装的所有第三方模块。这是通过 Composer 安装的任何东西在应用中的默认位置。

您刚刚安装的 ZF2 框架应用附带了一个方便的浮动文件,可以让您快速启动运行框架应用的虚拟机。正如我们在第二章中提到的,要启动并运行它,你只需要运行:

$ vagrant up

安装完成后,您应该会在浏览器中看到应用的框架(图 5-2 )。

A332793_1_En_5_Fig2_HTML.jpg

图 5-2。

The ZF2 skeleton framework displayed in a browser

这个例子的目的是探索在每个框架中定义一个控制器和动作并执行一个简单的数据库查询是多么容易。ZF2 框架应用已经为我们定义了一个控制器和动作,这就是你在图 5-2 中看到的欢迎页面。当我们解构module目录布局时,让我们看看组成这个显示的代码。

组件

ZF2 中的模块是您在应用中划分功能组的地方。ZF2 是围绕模块化系统的概念构建的。例如,如果您的应用具有面向用户的一面、一个管理员和一组批处理进程,那么每一个都可以被分离到它们自己的模块中。对于我们的例子,如果你展开module目录,你会看到默认的应用模块包含在框架应用中(图 5-3 )。

A332793_1_En_5_Fig3_HTML.jpg

图 5-3。

The default Application module contained in the skeleton app

这是 ZF2 模块的默认结构;每个文件夹都包含应用的重要部分,如下所示:

  • config–这里是放置模块特定配置的地方。定义了诸如路线、控制器和视图模板之类的东西。
  • 这是你的模块的翻译文件所在的地方。框架应用使用 ZendI18n 模块,并使用.po文件来提供文本翻译。
  • 这是绝大多数模块代码所在的地方。该文件夹包含构成模块的控制器、窗体、服务和其他应用逻辑。
  • viewview文件夹包含您的应用视图,即 MVC 中的“V”。默认情况下,ZF2 使用.phtml文件,这是应用表示层的纯 PHP 模板方法。
  • Module.php -这个文件包含了module类,这是 ZF2 为了实例化你的模块所期望的唯一的东西。在本模块中,您可以执行注册监听器、附加配置、自动加载器等活动。

控制器

控制器将应用动作与视图联系起来。框架应用包含使控制器和动作工作所需的最低限度:

class IndexController extends AbstractActionController
{
    public function indexAction()
    {
        return new ViewModel();
    }
}

ZF2 中控制器类和文件的命名约定分别是ControllerNameControllerControllerNameController.phpControllerName部分必须以大写字母开头。控制器中定义的每个动作都是定义为actionNameAction的公共方法。动作必须以小写字母开头。这遵循类和方法的命名约定的 PSR-1 标准。

查看前面示例中的indexAction方法,包含的唯一代码是返回ViewModel()的实例化。ZF2 中的ViewModel负责为您的应用设置和调用合适的视图模板,以及设置视图变量和某些您可用的选项。默认情况下,ViewModel()将使用与您的动作同名的视图。

以下是控制器和视图模型的简单示例:

use Zend\View\Model\ViewModel

;

数据库ˌ资料库

对于我们的示例,我们将创建一个简单的表,其中包含用户的姓名和电子邮件地址,然后检索它并将其显示在我们的视图中。尽管 ZF2 支持强大的对象关系映射器(ORM ),比如 Doctrine,但我们将使用 ZF2 中可用的数据库提取层,称为 Zend\Db。

首先,让我们创建简单的数据库表,并用一些简单的数据填充它:

CREATE TABLE user (
   id int(11) NOT NULL auto_increment,
   name varchar(100) NOT NULL,
   email varchar(100) NOT NULL,
   PRIMARY KEY (id)
 );

INSERT INTO user VALUES
(null,'Bob Jones','bob.jones@example.tld'),
(null,'Sally Hendrix','sallyh@example.tld'),
(null,'Carl Davidson','cdavidson@example.tld');

凭据配置

为了能够连接到我们的新数据库,我们需要向 ZF2 提供数据库名称、类型、用户名和密码的配置信息。这是在名为global.phplocal.php的两个文件的全局config文件夹中完成的。在这里,我们还将配置ServiceManager,我们将使用它将所有东西连接在一起,然后使它对我们的应用可用:

global.php
return array(
    'db' => [
        'driver' => 'Pdo',
        'dsn' => 'mysql:dbname=app;host=localhost',
        'driver_options' => [
            PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
        ], 

    ],
    'service_manager' => [
        'factories' => [
            'Zend\Db\Adapter\Adapter'
            => 'Zend\Db\Adapter\AdapterServiceFactory',
        ],
    ],
);

local.php
return array(
    'db' => [
        'username' => 'YOUR_DB_USERNAME_HERE',
        'password' => 'YOUR_DB_USERNAME_PASSWORD_HERE',
    ],
);

模型

接下来,我们将创建我们的模型层。本例的模型层将包含一个非常简单的表(实体)表示和另一个与 Zend\Db TableGateway交互的类,该类将执行我们的选择查询。

首先,我们的实体,它位于src/Application/Model/Entity/User.php:

namespace Application\Model\Entity;

class User
{
    public $id;
    public $name;
    public $email;

    public function exchangeArray($data)
    {
        $this->id = (!empty($data['id'])) ? $data['id'] : null;
        $this->name = (!empty($data['name'])) ? $data['name'] : null;
        $this->email  = (!empty($data['email'])) ? $data['email'] : null;
    }
}

这使用了exchangeArray,您可能还记得它是 PHP 标准库(SPL)的一部分,将传递给它的数据映射到构成我们的表的三个方法。

接下来,在src/Application/Model/User.php下找到与 Zend TableGateway交互的类:

namespace Application\Model;

use Zend\Db\TableGateway\TableGateway;

class User
{

    protected $tableGateway;

    public function __construct(TableGateway $tableGateway)
    {
        $this->tableGateway = $tableGateway;
    }

    public function fetchAll()
    {
        $results = $this->tableGateway->select();
        return $results;
    }
}

服务经理

我们使用 ZF2 服务管理器来允许我们的新实体作为服务在我们的控制器中被调用。我们通过向Module.php添加代码来做到这一点:

public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'Application\Model\User' =>  function($sm) {
                    $tableGateway = $sm->get('UserTableGateway');
                    $table = new User($tableGateway);
                    return $table;
                },
                'UserTableGateway' => function ($sm) {
                    $dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
                    $resultSetPrototype = new ResultSet();
                    $resultSetPrototype->setArrayObjectPrototype(new UserEntity());
                    return new TableGateway('user', $dbAdapter, null, $resultSetPrototype);
                },
            ),
        ); 

    }

视角

ZF2 中的视图名称和文件夹结构通常遵循模块名称空间名称、控制器名称和动作。

Note

视图层的组件可能是变量、容器、视图模型、呈现器等。

以下是我们在 skeleton 应用中使用的示例代码:

view (folder containing views)
- application (Namespace name)
  - index (controller name)
    - index.phtml (Action name)

ZF2 中的视图模板采用纯 PHP 方法,而不是单独的模板语言,并且默认使用.phtml扩展名。如果您查看示例应用中的index.phtml文件,您会注意到 HTML 和简单 PHP 的混合。

查询和显示

最后一步是将我们的新users表作为服务加载,查询整个表,并显示结果。我们将首先实例化ServiceLocator,它用于在我们的应用中查找和加载服务。然后我们让它专门加载我们的User类并返回实例化的对象。看这里:

<?php

namespace Application\Controller;

use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;

class IndexController extends AbstractActionController
{
    protected $user;

    public function indexAction()
    {
        if (!$this->user) {
            $sm = $this->getServiceLocator();
            $this->user = $sm->get('Application\Model\User');
        }

        $users = $this->user->fetchAll();

        return new ViewModel([
            'users' => $users,
        ]);
    }
}

在这段代码中,我们获得了 ZF2 服务定位器的一个实例,并使用get方法来检索我们的User模型。接下来,我们查询这个表,并使用 ZF2 ViewModel对象将结果传递给我们的模板。

在我们看来,我们将添加一个新的 div 和 table,并在我们的操作中使用ViewModel迭代传递的结果:

<div class="row">
    <div class="col-md-12">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">Users</h3>
            </div>
            <div class="panel-body">
                <table class="table table-striped">
                    <thead>
                    <tr>
                        <th>Name</th>
                        <th>Email</th>
                    </tr>
                    </thead>
                    <tbody
                    <?php foreach ($users as $album): ?>
                        <tr>
                            <td><?php echo $this->escapeHtml($album->name); ?></td>
                            <td><?php echo $this->escapeHtml($album->email); ?></td>
                        </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        </div>
    </div>

</div>

现在,当我们再次加载我们的页面时,我们将看到刚才查询的表的结果通过我们的视图模板显示出来(图 5-4 )。

A332793_1_En_5_Fig4_HTML.jpg

Figure 5-4.

第二号交响曲

我们要看的下一个框架是 Symfony,特别是 Symfony 2 (SF2)。Symfony 的历史和 Zend Framework 一样长,是另一个坚实、可靠、面向企业的框架,由一个庞大、充满活力的社区提供支持。Symfony 由 SensioLabs 支持,由杨奇煜·波登西耶于 2004 年末创建,旨在为 Sensio 更快地创建网站。在创建它后不久,他决定开源它,11 年后,我们在这里有一个数千人的社区支持一个伟大的框架。

Note

截至发稿时,Symfony 3 刚刚发布。本教程重点介绍 Symfony 2 和当前可用的 Symfony 演示应用。

安装 SF2

虽然有几种安装 Symfony 2 (SF2)的方法,但目前的最佳做法是使用 Symfony 安装程序。要使用安装程序,只需运行适合您的操作系统的命令。

Linux 和 OS X

$ sudo curl -LsS https://symfony.com/installer -o /usr/local/bin/symfony
$ sudo chmod a+x /usr/local/bin/symphony

这将创建一个全局 symfony 命令,可以在系统中的任何地方执行。

Windows 操作系统

转到您的项目目录并执行以下命令:

c:\> php -r "readfile('https://symfony.com/installer');" > symphony

安装演示

一旦安装了 Symfony 安装程序,我们就可以安装 Symfony 演示应用了。这个应用将提供 SF2 中控制器、动作和数据库查询的功能演示。要安装此演示,只需键入:

$ symfony demo

Tip

本书提供的示例代码包含一个类似于我们的 ZF2 项目提供的用于 Symfony 演示的浮动文件。

应用目录结构

一旦安装成功,你应该在新创建的symphony _demo文件夹中看到一些目录(图 5-5 )。

A332793_1_En_5_Fig5_HTML.jpg

图 5-5。

Directories in the newly created symphony_demo folder

就像 ZF2 一样,每个文件夹都有特定的用途;它们是任何 SF2 应用的基础结构。这些文件夹的功能如下:

  • app–这是 Symfony 的核心文件夹,因为它包含所有配置、日志、缓存文件、AppKernel 和自动加载程序,还可以包含其他关键数据,如视图和翻译文件。
  • bin–该文件夹是存储应用数据的地方。缓存文件、日志文件和其他类似的文件都存储在这里。
  • src—module文件夹是您所有应用逻辑的所在。Symfony 应用逻辑被划分成“包”在这里,您将拥有组成您的应用的所有不同的包。这与我们看到的 ZF2 的模块文件夹非常相似。我们很快就会看到一个包的文件夹结构。
  • vendor–该文件夹包含您在应用中安装的所有第三方模块。这是通过 Composer 安装的任何东西在应用中的默认位置。
  • web——web文件夹是您的应用的 web 根目录,就像public文件夹是 ZF2 的根目录一样。这是您的 web 服务器配置将设置为文档根的内容。这里存储了所有面向公众的资产,如 JavaScript、CSS、图像、字体等。

安装并运行演示程序后,您应该会在浏览器中看到演示应用(图 5-6 )。

A332793_1_En_5_Fig6_HTML.jpg

图 5-6。

The demo application running in a browser

SF2 演示应用已经为我们定义了工作控制器、动作和数据库查询,如果您单击欢迎页面上的 Browse Application 按钮,就会看到这个示例应用。当我们解构 bundle 和 app 目录布局时,让我们来看看组成这个功能的代码。

就像 ZF2 中的模块一样,SF2 中的包是您在应用中划分功能组的地方。

以我们的演示应用为例,您看到的主要应用包含在AppBundle包中。允许您在每个演示应用页面上查看源代码的功能包含在一个单独的包中,CodeExplorerBundle。对于我们的例子,如果你展开AppBundle目录,你会看到组成演示应用的许多目录(图 5-7 )。

A332793_1_En_5_Fig7_HTML.jpg

图 5-7。

The expanded AppBundle directory showing the directories that make up the demo app

因为这是一个完整的演示应用,所以在这个包中还设置了许多其他组件。就本例而言,我们将只关注几个关键部分,如下所示:

  • 控制器——这是一个包中包含的所有控制器所在的位置。
  • entity——它在 MVC (model)中主要充当“M ”,因为它包含所有将数据库映射到代码的数据库实体。
  • AppBundle.php–与 ZF2 module.php类似,这个文件包含AppBundle类,它将包中包含的代码转换成功能 Symfony 代码。

您可能会注意到,这个包中没有我们的视图模板。虽然你的应用视图可以放在你的包中,而且在过去,根据定义的 Symfony 最佳实践,这是保存它们的正常位置,但是最好将它们放在app/Resources/views目录的app文件夹中。

控制器

正如我们在 ZF2 中所探讨的,控制器将应用动作与视图联系起来。演示应用包含几个控制器,但是在这个例子中我们只关注BlogControllerindexAction:

public function indexAction($page)
{
    $query = $this->getDoctrine()->getRepository('AppBundle:Post')->queryLatest();

    $paginator = $this->get('knp_paginator');
    $posts = $paginator->paginate($query, $page, Post::NUM_ITEMS);
    $posts->setUsedRoute('blog_index_paginated');

    return $this->render('blog/index.html.twig', array('posts' => $posts));
}

与 ZF2 一样,SF2 控制器命名约定遵循控制器文件名和类名的 StudlyCaps,每个动作都在 camelCase 中定义,同样遵循 PSR-1 标准。

SF2 使用render方法来定义和呈现视图模板,并将任何数据传递给模板进行解释和处理。从前面的代码中我们可以看到,这个操作正在呈现名为index.html.twig的视图模板,它位于 blog 目录中。

数据库ˌ资料库

Symfony 2 不包括我们之前看到的数据库抽象层,比如 Zend\Db。默认情况下,SF2 被配置为使用 Doctrine,这是一个强大的对象关系映射器(ORM)库。虽然我们为 ZF2 示例创建了一个模型(实体)层,但是 Symfony 演示应用中已经存在一些实体。我们的indexAction正在调用的博文示例的实体位于AppBundle/Entity目录中,名为Post.php

除了提供实体之外,我们的演示应用还利用了数据库表post的存储库。Doctrine 中的存储库允许您定义在数据库上执行定制查询的方法。在我们的indexAction中,它调用了PostRepositoryqueryLatest()方法。让我们来看看组成这个方法的代码:

public function queryLatest()
{
    return $this->getEntityManager()
        ->createQuery('
            SELECT p
            FROM AppBundle:Post p
            WHERE p.publishedAt <= :now
            ORDER BY p.publishedAt DESC
        ')
        ->setParameter('now', new \DateTime())
    ;
}

这种方法利用了教条查询语言(DQL ),非常类似于常规 SQL。它在语法上等效于以下 SQL:

SELECT p.*
FROM post p
WHERE p.published_at <= NOW()
ORDER BY p.published_at DESC

这个特定的代码返回一个 Doctrine 实体管理器对象,该对象包含按发布日期降序排列的文章数据。indexAction通过以下代码进行查询:

$query = $this->getDoctrine()->getRepository('AppBundle:Post')->queryLatest();

这段代码获取教条对象,加载位于AppBundle中的Post存储库,最后调用前面的queryLatest()方法。然后,这将被移交给演示应用用来提供结果分页的另一个库,最后,应用通过使用以下代码行将post数据变量传递给 Twig 模板:

return $this->render('blog/index.html.twig', array('posts' => $posts));

Tip

ZF2 也可以被配置为作为 ORM 使用 Doctrine,而不是使用 Zend 数据库抽象库。

视角

SF2 中的视图名称和文件夹结构通常遵循控制器名称和动作。对于我们从演示应用中检查的示例代码,如下所示:

view (folder containing views)
- blog (controller name)
  - index.html.twig (Action name)

默认情况下,Symfony 使用 Twig 模板引擎。Twig 是一种轻量级但功能强大的模板语言,它使用简单的语法并将模板解析为纯 PHP 文件。

Note

虽然 Symfony 支持纯 PHP 模板,就像 Zend Framework 2 一样,但人们认为 Twig 将成为 Symfony Framework 3 唯一官方支持的模板引擎。

显示结果

我们的演示应用的最后一步是处理 Symfony render方法传入的数据,并将其显示在博客索引视图模板中。让我们来看看模板中处理这个问题的代码块:

{% for post in posts %}
    <article class="post">
        <h2>
            <a href="{{ path('blog_post', { slug: post.slug }) }}">
                {{ post.title }}
            </a>
        </h2>

        {{ post.summary|md2html }}
    </article>
{% else %}
    <div class="well">{{ 'post.no_posts_found'|trans }}</div>
{% endfor %}

这段代码使用了标准 PHP foreach的 Twig 等价物,就像我们在 ZF2 示例中使用的一样。然而,如果通过else语句在post变量中没有可用的数据,Twig for方法有一个自动处理的约定。

拉维尔 5 号

我们要看的最后一个 PHP 框架是 Laravel。Laravel 是较新的框架之一,但它在 PHP 社区中迅速流行起来,因为它干净、快速且易于使用。

Laravel 最重要的特性之一是它非常可配置、可扩展,是一个非常有用的刀片模板引擎。Laravel 是由 Taylor Otwell 创建的,旨在为一个名为 CodeIgniter 的过时 PHP 框架提供一种高级替代方案。Laravel 的第一个测试版是在 2011 年 6 月。

安装 Laravel 5

正如其他框架一样,有几种方法可以安装 Laravel。推荐的方式是通过 Composer。出于本练习的目的,我们将使用 Laravel quickstart 项目和 Laravel Homestead 游民箱。Homestead 是一个完全配置好的流浪者盒子,带有 PHP7 和运行 Laravel 所需的所有系统要求。

首先,克隆快速启动项目:

$ git clone https://github.com/laravel/quickstart-basic laravel

现在安装所有的依赖项:

$ cd laravel

$ composer install

接下来,安装宅基地。这将为您提供工具来生成运行 Laravel Homestead 框的流浪者文件:

$ composer require laravel/homestead --dev

$ php vendor/bin/homestead make

现在调出新框:

$ vagrant up

最后,ssh 进入 new box 并运行数据库迁移脚本,为 Laravel quickstart 应用安装示例数据库:

$ vagrant ssh

$ cd laravel

$ php artisan migrate

使用Homestead.yaml配置文件中生成的设置构建家园框。如果您打开这个文件,您将看到为这个新的流浪者虚拟机定义的 IP。在这种情况下,默认设置为 192.168.10.10。如果您在浏览器中加载该网站,您应该会看到快速启动应用页面(图 5-8 )。

A332793_1_En_5_Fig8_HTML.jpg

图 5-8。

The quickstart app page displayed in a browser

应用目录结构

如果您查看安装 Laravel quickstart 项目的目录,您会看到组成 Laravel 应用的各种文件夹(图 5-9 )。

A332793_1_En_5_Fig9_HTML.jpg

图 5-9。

The folders that comprise a Laravel application

与我们研究的其他框架一样,每个目录都存储了应用的一个特定部分。以下是三个最重要的目录:

  • app——这是所有代码存在的地方。
  • bootstrap——它主要充当 MVC(模型)中的“M ”,因为它包含所有将数据库映射到代码的数据库实体。
  • config——类似于 ZF2 module.php文件,这个文件包含了AppBundle类,它将包中包含的代码转换成功能 Symfony 代码。

应用逻辑

与 Zend Framework 或 Symfony 不同,在基本的 Laravel 应用中,没有通过模块或捆绑包的方式进行划分。通过在 Laravel 应用文件夹下创建单独的文件夹,并相应地命名底层代码,可以实现有点类似的方法,但这与其他框架不同,不是必需的。

控制器和路线

在 Laravel 中,有两种方法可以提供 MVC 应用的控制器层。最简单的方法是使用app/Http/routes.php文件并声明一个匿名函数。这是我们正在研究的 quickstart 应用所采用的方法,如下所示:

Route::get('/', function () {
    return view('tasks', [
        'tasks' => Task::orderBy('created_at', 'asc')->get()
    ]);
});  

如果您想将它移到一个控制器中,您可以将您的控制器添加到app/Http/Controllers目录中,并在routes.php文件中定义控制器:

namespace App\Http\Controllers;

use App\User;
use App\Http\Controllers\Controller;

class TaskController extends Controller
{
    public function tasks()
    {
        $tasks = Task::orderBy('created_at', 'asc')->get();

        return view('tasks', ['tasks' => $tasks]);
    }
}

现在我们在routes.php中定义路线:

Route::get('/', TaskController@tasks);

这条路径告诉 Laravel 使用IndexController并执行tasks方法。

数据库ˌ资料库

Laravel 在基础安装中包含了自己的基于 ActiveRecord 的对象关系映射(ORM)库实现。雄辩被吹捧为简单易用。与前面的例子不同,每个数据库都用一个模型类表示,而不是使用实体。正如您可以从包含的Task模型中看到的,所需的代码非常少:

namespace App;

use Illuminate\Database\Eloquent\Model;

class Task extends Model
{
    //
}

默认情况下,Laravel 将尝试使用模型类名的复数小写版本作为它所代表的数据库表名。在本例中,应该是tasks。它还期望每个表都有一个名为id的主键列以及两个名为created_atupdated_at的时间戳列。当视图中的所有任务都被检索到时,这些将被使用。

视角

Laravel 中的所有视图都存储在resources/views目录下。视图模板可以任意排列,例如在主views目录下的子目录中。Laravel 提供了使用纯 PHP 模板或 Blade 的能力,Blade 是 Laravel 创建的一种模板语言。快速启动应用的视图是用 Blade 编写的,可以在resources/views/tasks.blade.php下找到。

显示结果

让我们再来看看在routes.php文件中声明的匿名函数:

return view('tasks', [
        'tasks' => Task::orderBy('created_at', 'asc')->get()
    ]);

这将使用带有 concertive 的get()方法来检索由必需的created_at列排序的所有任务。这相当于运行以下 SQL 语句:

SELECT * FROM tasks ORDER BY created_at ASC

最后,通过调用view()方法并将数据传递给它,代码将数据交给模板使用。如果我们看一下模板,我们可以看到 Blade 语法检查变量$tasks,中是否有任何结果,如果有,它用一个foreach循环遍历它们:

<!-- Current Tasks -->
@if (count($tasks) > 0)
<div class="panel panel-default">
    <div class="panel-heading">
        Current Tasks
    </div>

    <div class="panel-body">
        <table class="table table-striped task-table">
            <thead>
            <th>Task</th>
            <th>&nbsp;</th>
            </thead>
            <tbody>
            @foreach ($tasks as $task)
            <tr>
                <td class="table-text">
                    <div>{{ $task->name }}</div>
                </td>

                <!-- Task Delete Button -->
                <td>
                    <form action="/task/{{ $task->id }}" method="POST">
                        {{ csrf_field() }}
                        {{ method_field('DELETE') }}

                        <button type="submit" class="btn btn-danger">
                            <i class="fa fa-btn fa-trash"></i>Delete
                        </button>
                    </form>
                </td>
            </tr>
            @endforeach
            </tbody>
        </table>
    </div>
</div>
@endif

微观框架

当您希望框架提供的结构和开发速度时,微框架是一种替代方案,但它比传统的完整框架提供的“花哨”和开销更少。

PHP 有许多不同的微框架。以下是一些目前流行的选择:

  • silex——这是 Sensio Labs 的一个微框架,基于几个不同的 Symfony 组件。
  • lumen——这是 Laravel 的一个微观框架,基于 Laravel 的一些基础。
  • slim——这被认为是目前最小最快的 PHP 微框架之一。
  • 这是一个开源的 PHP 全栈框架,是作为 C 扩展编写的。
  • Yii -这是一个开源的、面向对象的、基于组件的 MVC PHP 框架。

何时使用微框架

关于什么时候使用微框架,什么时候使用完整框架,没有硬性规定。这完全是个人决定,可能会因项目而异。顾名思义,微框架通常被认为是用于小型项目的,但是没有什么可以阻止你将它用于任何规模的项目。与任何框架一样,您需要仔细权衡所构建内容的范围、大小和功能,并在此基础上做出决定。通读任何给定框架的文档和特性,无论它是微观的还是完整的,都会让您对该框架能为您提供什么有重要的了解。

使用微框架

那么用微框架开发是什么样子的呢?让我们深入研究一下一个非常简单的“hello world”示例,它使用了我之前列出的三个框架。在每个例子中,我们只需安装框架并定义一个简单的路由和控制器来打印“hello world”。

Tip

本书提供的示例代码包含一个基本的浮动文件,该文件提供了一个简单的 VM 来运行微框架示例。

Silex

要开始使用 Silex,我们首先要安装它。使用 Composer 是简单且值得推荐的方法。为此,让我们创建一个需要 Silex 的composer.json文件:

{
  "require": {
    "silex/silex": "∼1.3"
  }
}

现在,我们运行composer install:

$ composer install
Loading composer repositories with package information
Installing dependencies (including require-dev)
...
Writing lock file
Generating autoload files

就这样 Silex 现在已经设置好了,可以使用了。要创建这个极其简单的示例,我们只需创建一个包含 Silex autoloader 的文件,定义路由,执行 Silex,并显示一个简单的 HTML 响应。让我们来看看实现这一点所需的代码:

<?php

require_once __DIR__.'/../vendor/autoload.php';

// Initialize Silex
$app = new Silex\Application();

// Define a route and anonymous function for our "controller"
$app->get('/hello-world', function () {
    return '<h1>Hello World!</h1>';
});

$app->run();

现在,如果我们在浏览器中访问/hello-world,我们会看到“Hello World!”输出到屏幕上。当然,Silex 比简单的路由和 HTML 响应更强大。Silex 提供了 Symfony 为您提供的许多其他功能和服务,包括:

  • Twig 服务提供商,因此您也可以利用 Silex 中 Twig 模板的功能。
  • 动态路由
  • 使用教条的数据库交互
  • 表单、验证和会话处理
  • 日志、PHPUnit 集成等等

流明

名单上的下一个是卢蒙。我们可以通过首先安装 Lumen 安装程序或者使用composer create-project来安装 Lumen。对于我们的例子,我们将使用composer create-project并在 Laravel Homestead 流浪者盒子上运行这个例子,就像我们在前面的 Laravel 例子中所做的那样:

$ composer create-project --prefer-dist laravel/lumen lumen
Installing laravel/lumen (v5.2.1)
...
Writing lock file
Generating autoload files

现在,安装了 Lumen 之后,让我们添加 Homestead 流浪者配置:

$ composer require laravel/homestead --dev

$ php vendor/bin/homestead make

我们现在可以启动我们的流浪者盒子,并测试我们简单的“hello world”示例。正如 Laravel 一样,我们在app/Http/routes.php下定义路线。创建我们的示例的语法看起来几乎与 Silex 中的完全一样:

<?php

// Define our hello world route
$app->get('/hello-world', function () {
    return '<h1>Hello World!</h1>';
});

正如您在这里可能注意到的,这个例子和 Silex 例子之间最大的区别是没有包含和执行 Silex 的run方法的自动加载器。这是因为这一切都发生在public/index.php下定义的前端控制器中:

<?php

$app = require __DIR__.'/../bootstrap/app.php';

$app->run();

如您所见,这与 Silex 微框架的结构几乎完全相同。这两者之间的主要区别是框架的底层组件。如果您已经偏好 Symfony 或 Laravel,这将有助于您在这两个框架之间做出更容易的决定。

微小的

我们名单上的最后一个是斯利姆。要安装 Slim,我们将使用 Composer,就像我们使用 Silex 一样。我们将创建一个composer.json文件,然后运行composer install:

{
  "require": {
    "slim/slim": "³.0"
  }
}

$ composer install
Loading composer repositories with package information
...
Writing lock file
Generating autoload files

Now that Slim is installed, we'll define a single file to which we'll pass our request. This will define the route as well as an anonymous function to perform the "hello world" response:

<?php

use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;

// Include Slim autoloader
require_once __DIR__.'/../vendor/autoload.php';

// Initialize Slim
$app = new Slim\App();

// Define a route and anonymous function to serve as a controller
$app->get('/hello-world', function (Request $request, Response $response) {
    $response->getBody()->write("Hello World!");

    return $response;
});

$app->run();

正如您在这里看到的,这也非常类似于 Silex。我们包含了 Slim autoloader,初始化 Slim,定义我们的路线并传入所需的RequestResponse对象,返回我们的文本,最后执行 Slim。

如果您浏览 Slim 的源代码和文档,您会很快注意到它肯定比 Silex 或 Lumen 要轻得多。它确实提供了一些附加功能,比如在你的应用中使用 Twig 模板,但是你会注意到它缺少其他一些现成的默认功能,比如数据库交互。尽管在选择要使用的微框架时应该考虑到这一点,但应该注意的是,由于 Slim 使用了 Composer 以及 Composer 提供的模块化,您可以快速且相当容易地利用 ORM,如 Doctrine 或 Laravel 的口才。

摘要

在这一章中,我们讨论了使用 PHP 开发框架给你带来的好处,以及如何快速地使用一些最流行的完整框架和微框架。尽管我们只是触及了使用框架的表面,但希望你现在对它们是如何工作的有了更好的理解,并且能够马上开始在你的项目中使用。

posted @ 2024-08-03 11:25  绝不原创的飞龙  阅读(2)  评论(0编辑  收藏  举报