Composer 脚本与钩子 #

脚本概述 #

Composer 脚本允许你在 Composer 执行过程中运行自定义命令,用于自动化各种开发任务。

基本配置 #

json
{
    "scripts": {
        "test": "phpunit",
        "build": "php build.php",
        "clear-cache": "rm -rf cache/*"
    }
}

运行脚本 #

bash
# 运行脚本
composer test

# 或使用 run-script
composer run-script test

# 传递参数
composer test -- --filter=MyTest

# 运行多个脚本
composer run-script test build

脚本类型 #

命令行脚本 #

直接执行命令行命令:

json
{
    "scripts": {
        "clear-cache": "rm -rf cache/*",
        "migrate": "php artisan migrate",
        "seed": "php artisan db:seed"
    }
}

PHP 回调 #

调用 PHP 类的静态方法:

json
{
    "scripts": {
        "post-install-cmd": "MyVendor\\MyClass::postInstall",
        "post-update-cmd": "MyVendor\\MyClass::postUpdate"
    }
}

PHP 类实现:

php
<?php
namespace MyVendor;

class MyClass
{
    public static function postInstall(Event $event)
    {
        $event->getIO()->write("Post install script executed!");
    }

    public static function postUpdate(Event $event)
    {
        $event->getIO()->write("Post update script executed!");
    }
}

组合脚本 #

组合多个脚本:

json
{
    "scripts": {
        "test": "phpunit",
        "lint": "phpcs --standard=PSR12 src/",
        "fix": "phpcbf --standard=PSR12 src/",
        "check": [
            "@lint",
            "@test"
        ]
    }
}

事件钩子 #

事件类型 #

Composer 支持以下事件:

事件 触发时机
pre-install-cmd install 命令之前
post-install-cmd install 命令之后
pre-update-cmd update 命令之前
post-update-cmd update 命令之后
pre-status-cmd status 命令之前
post-status-cmd status 命令之后
pre-package-install 包安装之前
post-package-install 包安装之后
pre-package-update 包更新之前
post-package-update 包更新之后
pre-package-uninstall 包卸载之前
post-package-uninstall 包卸载之后
pre-autoload-dump 自动加载生成之前
post-autoload-dump 自动加载生成之后
post-root-package-install 根包安装之后
post-create-project-cmd create-project 之后

事件配置 #

json
{
    "scripts": {
        "pre-install-cmd": "MyVendor\\MyClass::preInstall",
        "post-install-cmd": [
            "MyVendor\\MyClass::postInstall",
            "php artisan optimize"
        ],
        "pre-update-cmd": "MyVendor\\MyClass::preUpdate",
        "post-update-cmd": "MyVendor\\MyClass::postUpdate",
        "pre-autoload-dump": "MyVendor\\MyClass::preAutoloadDump",
        "post-autoload-dump": [
            "MyVendor\\MyClass::postAutoloadDump",
            "php artisan package:discover"
        ]
    }
}

常用脚本示例 #

测试相关 #

json
{
    "scripts": {
        "test": "phpunit",
        "test-coverage": "phpunit --coverage-html coverage",
        "test-parallel": "paratest --processes 4",
        "test-watch": "phpunit-watcher watch"
    }
}

代码质量 #

json
{
    "scripts": {
        "lint": "phpcs --standard=PSR12 src/",
        "lint-fix": "phpcbf --standard=PSR12 src/",
        "stan": "phpstan analyse src/",
        "mess": "phpmd src/ text cleancode,codesize,controversial,design,naming,unusedcode",
        "check": [
            "@lint",
            "@stan",
            "@test"
        ]
    }
}

清理缓存 #

json
{
    "scripts": {
        "clear-cache": "rm -rf cache/*",
        "clear-all": [
            "rm -rf cache/*",
            "rm -rf storage/logs/*",
            "rm -rf storage/framework/views/*"
        ]
    }
}

数据库操作 #

json
{
    "scripts": {
        "migrate": "php artisan migrate",
        "migrate-fresh": "php artisan migrate:fresh --seed",
        "seed": "php artisan db:seed",
        "db-reset": [
            "php artisan migrate:fresh",
            "php artisan db:seed"
        ]
    }
}

构建与部署 #

json
{
    "scripts": {
        "build": [
            "@composer install --no-dev --optimize-autoloader",
            "php artisan config:cache",
            "php artisan route:cache",
            "php artisan view:cache"
        ],
        "deploy": [
            "@build",
            "php artisan optimize"
        ]
    }
}

开发环境 #

json
{
    "scripts": {
        "serve": "php artisan serve",
        "watch": "npm run watch",
        "dev": [
            "composer install",
            "npm install",
            "npm run dev"
        ]
    }
}

事件对象 #

Event 类 #

php
<?php
namespace Composer\Script;

use Composer\Composer;
use Composer\IO\IOInterface;

class Event
{
    public function getName(): string {}
    public function getComposer(): Composer {}
    public function getIO(): IOInterface {}
    public function isDevMode(): bool {}
    public function getOriginatingEvent(): ?Event {}
    public function getArguments(): array {}
    public function getFlags(): array {}
}

使用示例 #

php
<?php
namespace App\Scripts;

use Composer\Script\Event;
use Composer\IO\IOInterface;

class InstallHandler
{
    public static function postInstall(Event $event)
    {
        $io = $event->getIO();
        $composer = $event->getComposer();

        $io->write("<info>Running post-install script...</info>");

        if ($event->isDevMode()) {
            $io->write("Development mode enabled");
        }

        $arguments = $event->getArguments();
        if (!empty($arguments)) {
            $io->write("Arguments: " . implode(', ', $arguments));
        }
    }
}

PackageEvent 类 #

用于包相关事件:

php
<?php
namespace App\Scripts;

use Composer\Installer\PackageEvent;

class PackageHandler
{
    public static function postPackageInstall(PackageEvent $event)
    {
        $package = $event->getOperation()->getPackage();
        $io = $event->getIO();

        $io->write("<info>Installed: {$package->getName()}</info>");
        $io->write("Version: {$package->getVersion()}");
    }
}

脚本引用 #

引用其他脚本 #

使用 @ 前缀引用其他脚本:

json
{
    "scripts": {
        "lint": "phpcs src/",
        "test": "phpunit",
        "check": [
            "@lint",
            "@test"
        ],
        "ci": [
            "@check",
            "@build"
        ]
    }
}

引用 Composer 命令 #

json
{
    "scripts": {
        "update-deps": "@composer update --with-dependencies",
        "install-prod": "@composer install --no-dev --optimize-autoloader",
        "dump-autoload": "@composer dump-autoload --optimize"
    }
}

条件脚本 #

根据环境执行 #

php
<?php
namespace App\Scripts;

use Composer\Script\Event;

class EnvironmentHandler
{
    public static function postInstall(Event $event)
    {
        $io = $event->getIO();

        if ($event->isDevMode()) {
            $io->write("Development environment setup");
            self::setupDevEnvironment($io);
        } else {
            $io->write("Production environment setup");
            self::setupProdEnvironment($io);
        }
    }

    private static function setupDevEnvironment($io)
    {
        $io->write("Setting up development tools...");
    }

    private static function setupProdEnvironment($io)
    {
        $io->write("Optimizing for production...");
    }
}

交互式脚本 #

php
<?php
namespace App\Scripts;

use Composer\Script\Event;

class InteractiveHandler
{
    public static function configure(Event $event)
    {
        $io = $event->getIO();

        $name = $io->ask("What is your name? ", "Guest");
        $io->write("Hello, $name!");

        $confirm = $io->askConfirmation("Do you want to continue? ", true);
        if (!$confirm) {
            $io->write("Aborted.");
            return;
        }

        $choice = $io->select(
            "Choose an option: ",
            ['Option 1', 'Option 2', 'Option 3'],
            0
        );
        $io->write("You selected: Option " . ($choice + 1));
    }
}

脚本描述 #

添加描述信息 #

json
{
    "scripts": {
        "test": "phpunit",
        "test-description": "Run the test suite",
        "lint": "phpcs src/",
        "lint-description": "Check code style"
    }
}

使用 scripts-descriptions #

json
{
    "scripts": {
        "test": "phpunit",
        "lint": "phpcs src/",
        "build": "php build.php"
    },
    "scripts-descriptions": {
        "test": "Run the test suite",
        "lint": "Check code style against PSR-12",
        "build": "Build the production assets"
    }
}

查看描述:

bash
composer run-script --list

跳过脚本 #

命令行跳过 #

bash
# 跳过所有脚本
composer install --no-scripts

# 只跳过特定脚本
composer install --no-plugins

配置跳过 #

json
{
    "config": {
        "platform": {
            "php": "8.1"
        }
    },
    "scripts": {
        "post-install-cmd": [
            "@php artisan package:discover --ansi"
        ]
    }
}

实际应用场景 #

Laravel 项目 #

json
{
    "scripts": {
        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi"
        ],
        "post-update-cmd": [
            "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
        ],
        "post-root-package-install": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ]
    }
}

Symfony 项目 #

json
{
    "scripts": {
        "auto-scripts": {
            "cache:clear": "symfony-cmd",
            "assets:install %PUBLIC_DIR%": "symfony-cmd"
        },
        "post-install-cmd": [
            "@auto-scripts"
        ],
        "post-update-cmd": [
            "@auto-scripts"
        ]
    }
}

自定义项目 #

json
{
    "scripts": {
        "post-install-cmd": [
            "App\\Scripts\\InstallHandler::postInstall"
        ],
        "post-update-cmd": [
            "App\\Scripts\\InstallHandler::postUpdate"
        ],
        "test": "phpunit",
        "lint": "phpcs src/",
        "stan": "phpstan analyse src/",
        "check": [
            "@lint",
            "@stan",
            "@test"
        ],
        "build": [
            "@composer install --no-dev --optimize-autoloader",
            "@check"
        ]
    },
    "scripts-descriptions": {
        "test": "Run unit tests",
        "lint": "Check code style",
        "stan": "Run static analysis",
        "check": "Run all checks (lint, stan, test)",
        "build": "Build for production"
    }
}

调试脚本 #

详细输出 #

bash
# 增加详细程度
composer install -vvv

# 显示脚本执行
composer run-script test -v

脚本调试 #

php
<?php
namespace App\Scripts;

use Composer\Script\Event;

class DebugHandler
{
    public static function debug(Event $event)
    {
        $io = $event->getIO();

        $io->write("<info>Debug Information:</info>");
        $io->write("Event: " . $event->getName());
        $io->write("Dev Mode: " . ($event->isDevMode() ? 'Yes' : 'No'));
        $io->write("Working Dir: " . getcwd());

        $arguments = $event->getArguments();
        $io->write("Arguments: " . print_r($arguments, true));
    }
}

最佳实践 #

1. 使用描述性名称 #

json
{
    "scripts": {
        "test:unit": "phpunit --testsuite Unit",
        "test:feature": "phpunit --testsuite Feature",
        "test:coverage": "phpunit --coverage-html coverage"
    }
}

2. 组合脚本 #

json
{
    "scripts": {
        "lint": "phpcs src/",
        "test": "phpunit",
        "check": [
            "@lint",
            "@test"
        ],
        "ci": [
            "@check",
            "@build"
        ]
    }
}

3. 使用 PHP 类处理复杂逻辑 #

php
<?php
namespace App\Scripts;

use Composer\Script\Event;

class ScriptHandler
{
    public static function build(Event $event)
    {
        $io = $event->getIO();

        self::clearCache($io);
        self::optimizeAutoloader($io);
        self::generateAssets($io);

        $io->write("<info>Build completed!</info>");
    }

    private static function clearCache($io)
    {
        $io->write("Clearing cache...");
    }

    private static function optimizeAutoloader($io)
    {
        $io->write("Optimizing autoloader...");
    }

    private static function generateAssets($io)
    {
        $io->write("Generating assets...");
    }
}

4. 提供有意义的输出 #

php
<?php
$io->write("<info>Success message</info>");
$io->write("<comment>Warning message</comment>");
$io->write("<error>Error message</error>");

下一步 #

现在你已经掌握了脚本与钩子,接下来学习 高级特性 了解私有仓库、平台配置等高级功能!

最后更新:2026-03-28