非我发明 综合症

非我发明 综合症是一种反模式,指人们不愿意使用、购买或者接受某种产品、研究成果或者知识,不是出于技术或者法律等因素,而只是因为它源自其他地方。

密码学可以给我们上一堂关于软件的非常重要的课;对于 Kerckhoffs 原理尤其如此。该原则指出:

"即使除了密钥之外,系统的一切信息都是公开的,密码系统也应该是安全的"。

克劳德·香农以香农格言的形式重新表述了这一点:

"在设计系统时,应假定敌人会立即完全熟悉这些系统"。

通俗地说,要想拥有一个安全的系统,就不能因为没有人知道它是如何实现的就不安全("通过隐蔽实现安全")。如果你想通过隐蔽性来保护你的钱,你就会把它埋在树下,希望没人会发现它。而当你使用真正的安全机制时,比如把钱放在银行的保险柜里,你可以把安全系统的每一个细节都作为公开信息,但只要安全系统是真正安全的,你实际上只需要对保险柜的钥匙保密,其他细节都可以公开。如果有人发现了你保险箱的钥匙,你只需更改密码即可,而如果有人真的发现了你的钱被埋在树下,你就必须把钱挖出来,再找其他地方放。

只靠隐蔽来实现安全是个坏主意(话虽如此,但也不总是坏主意)。如你所知,当你在数据库中存储密码时,你应该使用一种单向加密算法,即 散列算法,以确保如果数据库被盗,没有人能利用数据库中的数据找到用户的原始密码。当然,在现实中,你不应该只对密码进行散列,而应该对密码进行加盐处理,并使用 PBKDF2 或 BCrypt 等算法,但本书并不是关于密码安全的。

然而,现实情况是,有时当开发人员真的费心对密码进行散列时,他们会决定创建自己的密码散列函数,这些函数很容易被逆转,而且只能通过别人不知道算法的隐蔽性来保证安全。这就是 "不是在这里发明的"(NIH)综合症的一个完美例子;开发人员不是使用一个备受推崇的密码散列库,而是决定创建自己的密码散列库,假装自己是一个密码学家,却不了解这种决定的安全影响。

值得庆幸的是,PHP 现在可以轻松地对密码进行散列处理;password_hash 函数和 password_verify 函数甚至可以告诉你何时需要重新计算散列。不过,我还是想说点题外话。

那么什么是 NIH 综合症呢?NIH 综合症是指组织或个人开发人员对自身能力的虚假自豪感导致他们构建自己的解决方案,而不是采用优秀的第三方解决方案。重新发明轮子不仅成本高昂、没有必要,而且会增加不必要的维护开销;它还可能非常不安全。

也就是说,如果解决方案是闭源和锁定的,那么避免使用它们可能是个好主意。这样做还可以避免被供应商锁定和限制业务灵活性。

美国国家卫生研究院综合症依赖于现有解决方案的优秀和不负众望。使用第三方库不能成为不审查其代码质量的借口。

为开源解决方案做贡献是缓解这些问题的好方法。现有库还有改进空间?fork 它,提出修改建议,将其合并进来。没有能实现你想要的功能的库吗?那么你可以考虑编写自己的库并将其发布。

在结束本节内容时,我想说的是,世界已经变得多姿多彩;人们不再寻求一种技术堆栈来满足他们所有的愿望;如今,人们追求的是最适合工作的工具。值得思考的是,如何利用这一事实为自己谋利。

使用 Composer 的第三方依赖性

Composer 让管理第三方依赖变得非常简单。在 第 1 章 为什么说 '优秀的 PHP 开发人员' 不是一个贬义词 中,我简要介绍了如何使用 Composer 进行自动加载。自 PHP 5.1.2 起,自动加载就作为一项核心功能得到了支持,但 Composer 最棒的地方在于它还可以用于依赖管理。Composer 可以根据你指定的版本限制,有效地获取你需要的依赖项。

让我们从下面的 composer.json 文件开始:

{
    "autoload": {
        "psr-4": {
            "IcyApril\\ChapterOne": "src/"
        }
    }
}

因此,让我们引入一个依赖项:

{
    "autoload": {
        "psr-4": {
            "IcyApril\\ChapterOne": "src/"
        }
    },
    "require": {
        "guzzlehttp/guzzle": "^6.1"
    }
}

请注意,我们所做的只是添加一个 require 参数,指定我们需要的软件。无需手动将文件粘贴到项目或根目录,也无需在 Git 中使用子模块!

在本例中,我们引入了 Guzzle,这是一个 PHP HTTP 库。

Composer 默认从一个名为 Packergist 的中央仓库中查询软件仓库,该仓库汇总了您可以从不同版本控制系统(如 GitHub、BitBucket 或其他仓库主机)安装的软件包。如果你愿意,Packergist 就像一本电话簿,将 Composer 对软件包的请求与代码库连接起来。

也就是说,Composer 不仅支持 Packergist 代码库。本着开放源代码的精神,它支持一系列 VCS 系统(如 Git/SVN)的版本库,无论它们托管在哪里。

我们来看看下面的 composer.json 文件:

{
    "autoload": {
        "psr-4": {
            "IcyApril\\ChapterTwo": "src/"
        }
    }
}

让我来演示一下如何从 BitBucket 包含一个版本库,而无需将其放在 Packergist 上:

{
    "autoload": {
        "psr-4": {
            "IcyApril\\ChapterOne": "src/"
        }
    },
    "require": {
        "IcyApril/my-private-repo": "dev-master"
    },
    "repositories": [
        {
            "type": "vcs",
            "url": "git@bitbucket.org:IcyApril/my-private-repo.git"
        }
    ]
}

就是这么简单!您实际上只需指定要从中提取的存储库,Composer 就会完成其余的工作。使用其他版本控制系统也同样简单:

{
    "autoload": {
        "psr-4": {
            "IcyApril\\ChapterOne": "src/"
        }
    },
    "require": {
        "IcyApril/myLibrary": "@dev"
    },
    "repositories": [
        {
            "type": "vcs",
            "url": "http://svn.example.com/path/to/myLibrary"
        }
    ]
}

相当厚颜无耻的是,Composer 甚至可以支持 PEAR PHP 存储库:

{
    "autoload": {
        "psr-4": {
            "IcyApril\\ChapterOne": "src/"
        }
    },
    "require": {
        "pear-pear2.php.net/PEAR2_Text_Markdown": "*",
        "pear-pear2/PEAR2_HTTP_Request": "*"
    },
    "repositories": [
        {
            "type": "pear",
            "url": "https://pear2.php.net"
        }
    ]
}

要在修改了 composer.json 文件后更新依赖关系,只需运行 composer update 即可。

请注意,仅使用 composer dump-autoload 无法更新外部依赖关系。原因是 dump-autoload 只能更新自动加载器的类图。它基本上只会更新需要自动加载的类列表,而不会引入新的依赖关系。

使用 Composer 拉入依赖库时,Git 有时会提示您需要生成 GitHub 认证密钥。这是因为,如果您在本地计算机上安装了 Git,Composer 就会通过版本控制系统克隆并拉入依赖项;但偶尔,如果它从 GitHub 抓取软件源,您可能会遇到速度限制。如果出现这种情况,也不必惊慌。Composer 会指导你如何获取 API 密钥,这样你就可以在没有速率限制的情况下继续使用。

解决这个问题的简单方法是生成一个本地 SSH 密钥,然后将公钥放入 GitHub 账户。这样,当你从 GitHub 克隆到本地机器时,就不会面临任何速率限制,也不用费心设置 API 密钥了。

要在 Linux/Mac OS X 机器上生成 SSH 密钥,只需运行 ssh-keygen 命令,就能创建公钥和私钥,用于 SSH 验证,包括 Github 或 BitBucket。这些密钥(通常)会存储在 ~/.ssh 目录中,注意其中的斜杠(~ 代表你的主目录)。因此,要将密钥打印到终端窗口,请运行 cat ~/.ssh/id_rsa.pub 命令。请注意,.pub 后缀表示 id_rsa.pub 是可以公开共享的公钥。不得共享私钥,私钥通常只命名为 id_rsa。在 Windows 系统中,可以使用名为 PuttyGen 的图形用户界面工具生成公钥和私钥。

拿到公钥和私钥后,只需访问 GitHub 网站,进入设置菜单中的 SSH 密钥页面,粘贴密钥并保存,就可以把它们放到 GitHub 中了。

在后续更新中,composer update 会更新到 composer.json 中定义的所有依赖项的最新版本。如果你不想这样做,还有一个选择:运行 Composer dump-autoload 只会重新生成需要包含在项目中的 PSR-0/PSR-4 类的列表(例如,你添加、删除或重命名了一些类)。

Composer 还支持私有资源库,让您可以有效管理多个项目中的代码重用。另一个主要优势是 Composer 会自动生成一个锁文件,你可以将其与项目一起提交。这样,当您使用版本控制系统提交时,就能有效管理在特定时间点安装的依赖项的精确版本。

Composer 可以轻松有效地管理第三方依赖库。一些重要的库已经可以通过 Composer 使用,例如 PHPUnit,但还有其他一些很棒的库可以让你的生活更轻松。在 Composer 上,我最喜欢的两个数据库库是 Eloquent(来自 Laravel 的数据库 ORM 系统,网址是 illuminate/database)和 Phinx(migration/seeding 系统,网址是 robmorgan/phinx)。除此之外,Packergist 还为各种 API 提供了一些优秀的 SDK(谷歌发布了它的一些 SDK,还有一些更具体的 SDK,如用于从 PHP 应用程序发送短信的 Twilio SDK)。

Composer 允许您为特定环境指定依赖关系;假设您只想在开发环境中引入 PHPUnit,那就不成问题了!