Skip to content

🛠️ 核心运行

本页讲解 pkg/composer 的"地基"——如何创建 Composer 实例、如何执行任意子命令、如何检测与升级 composer 本身。这些方法定义在 composer.goversion.go 中,是所有其它方法(InstallRequirePackage 等)的底层依赖。

🧱 核心类型

Composer 结构体

go
type Composer struct {
    executablePath string        // composer 可执行文件路径
    workingDir    string        // 工作目录
    autoInstall   bool          // 未找到时是否自动安装
    installer     *installer.Installer
    detector      *detector.Detector
    env           []string      // 环境变量(KEY=VALUE 格式)
    defaultTimeout time.Duration
}

Composer 是结构体而非接口,所有方法都挂在 *Composer 上。你通常不直接构造它,而是通过 New 创建。

Options 结构体

go
type Options struct {
    ExecutablePath string              // 显式指定 composer 路径
    WorkingDir     string              // 工作目录
    AutoInstall    bool                // 未找到时是否自动安装
    Installer      *installer.Installer
    Detector       *detector.Detector
    Env            []string
    DefaultTimeout time.Duration
}

🛠️ New

创建一个新的 Composer 实例。如果未指定 ExecutablePath,会先用 detector.Detect() 检测系统中的 composer;检测不到且 AutoInstall=true 时会自动安装再重新检测。

签名

go
func New(options Options) (*Composer, error)

参数

参数类型说明
optionsOptions自定义 Composer 实例的选项

返回值

类型说明
*Composer创建的 Composer 实例
error创建失败时返回 ErrComposerNotFoundErrComposerInstallation

示例

go
package main

import (
    "log"
    "time"

    "github.com/scagogogo/composer-skills/pkg/composer"
)

func main() {
    options := composer.DefaultOptions()
    options.WorkingDir = "/path/to/project"
    options.DefaultTimeout = 30 * time.Minute
    comp, err := composer.New(options)
    if err != nil {
        log.Fatalf("初始化 Composer 失败: %v", err)
    }
    _ = comp
}

进阶

  • 显式指定 ExecutablePath 时,New 会用 os.Stat 校验文件存在(测试模式下跳过)。
  • 若想完全跳过自动安装,把 AutoInstall 设为 false,此时检测失败直接返回 ErrComposerNotFound
  • 想一步到位(检测+安装+创建实例),用 composer.QuickSetup(workingDir, true)

⚙️ DefaultOptions

返回带默认配置的 Options,是最常用的构造入口。

签名

go
func DefaultOptions() Options

返回值

类型说明
Options默认配置:WorkingDir=""AutoInstall=trueDefaultTimeout=10*time.Minute

示例

go
options := composer.DefaultOptions()
comp, err := composer.New(options)

为什么不直接 Options{}

直接构造 Options{} 会让 AutoInstall=falseDefaultTimeout=0(即不超时),容易踩坑。始终用 DefaultOptions() 作为起点再按需覆盖。


📁 SetWorkingDir

设置 composer 命令的工作目录。所有后续命令都会在此目录下执行。

签名

go
func (c *Composer) SetWorkingDir(dir string)

参数

参数类型说明
dirstring要设置的工作目录路径

示例

go
comp.SetWorkingDir("/path/to/php/project")
// 之后所有 comp.Install() / comp.Run(...) 都在该目录执行

进阶

  • 对应的 getter 是 GetWorkingDir() string
  • 一次性在别的目录执行单条命令,可用 InstallWithWorkingDir(workingDir, noDev, optimize)(见 依赖管理),它会在执行后恢复原工作目录。

🌐 SetEnv

设置执行 composer 命令时的环境变量,常用于配置 HTTP 代理、COMPOSER_HOME 或身份验证信息。

签名

go
func (c *Composer) SetEnv(env []string)

参数

参数类型说明
env[]string环境变量数组,格式为 ["KEY=VALUE", ...]

示例

go
comp.SetEnv([]string{
    "HTTP_PROXY=http://proxy.example.com:8080",
    "HTTPS_PROXY=http://proxy.example.com:8080",
    "COMPOSER_HOME=/custom/composer/home",
})

进阶

  • 对应的 getter 是 GetEnv() []string
  • SetEnv整体替换而非追加。如果要继承当前进程环境,请用 os.Environ() 拼接后再传入。
  • 更细粒度的环境变量操作(如 COMPOSER_PROCESS_TIMEOUT)见 环境变量模块SetProcessTimeoutDisableInteraction 等)。

▶️ Run

执行任意 composer 子命令并返回输出,是 SDK 的核心方法。默认使用 10 分钟超时。

签名

go
func (c *Composer) Run(args ...string) (string, error)

参数

参数类型说明
args...string命令参数,第一个参数是 composer 子命令(如 "install""require"

返回值

类型说明
string命令的合并输出(stdout+stderr)
error失败时返回包裹了 ErrCommandExecution 的错误

示例

go
// 执行 "composer show"
output, err := comp.Run("show")
if err != nil {
    log.Fatalf("执行命令失败: %v", err)
}
fmt.Println(output)

// 执行带参数的命令
output, err = comp.Run("require", "symfony/console", "--dev")

进阶

  • Run 内部调用 RunWithTimeout(c.defaultTimeout, args...)
  • 测试模式下,RunWithContext 会先查 mockCommandOutput 命中则直接返回,不真正执行 composer。
  • 需要超时控制用 RunWithTimeout,需要取消能力用 RunWithContext

⏱️ RunWithTimeout

在指定超时时间内执行 composer 命令,适合可能长时间运行的命令。

签名

go
func (c *Composer) RunWithTimeout(timeout time.Duration, args ...string) (string, error)

参数

参数类型说明
timeouttime.Duration命令执行的最大超时时间
args...string命令参数

返回值

类型说明
string命令的合并输出
error失败或超时时返回错误

示例

go
// 执行可能需要很长时间的安装命令,设置 30 分钟超时
output, err := comp.RunWithTimeout(30*time.Minute, "install")
if err != nil {
    log.Fatalf("安装超时或失败: %v", err)
}

进阶

  • 实现上 RunWithTimeout 创建一个 context.WithTimeout 后转调 RunWithContext
  • 超时返回的 error 可用 errors.Is(err, context.DeadlineExceeded) 判断。

🧩 RunWithContext

在指定 context.Context 中执行 composer 命令,提供最大控制权——可超时、可取消、可携带截止时间。

签名

go
func (c *Composer) RunWithContext(ctx context.Context, args ...string) (string, error)

参数

参数类型说明
ctxcontext.Context上下文,可用于取消或设置超时
args...string命令参数

返回值

类型说明
string命令的合并输出
error失败时返回包裹了 ErrCommandExecution 的错误

示例

go
// 创建可以手动取消的上下文
ctx, cancel := context.WithCancel(context.Background())

// 在另一个 goroutine 中根据条件取消
go func() {
    time.Sleep(5 * time.Second)
    cancel()
}()

// 执行命令
output, err := comp.RunWithContext(ctx, "update")
if err != nil {
    if errors.Is(err, context.Canceled) {
        fmt.Println("命令被取消")
    } else {
        log.Fatalf("执行命令失败: %v", err)
    }
}

进阶

  • RunWithContextRunRunWithTimeout 的底层实现,也是 mock 注入的入口:会先调用 getMockOutput(args...),命中即返回 mock 输出。
  • 命令通过 exec.CommandContext(ctx, c.executablePath, args...) 构造,ctx 取消时会向子进程发送终止信号。
  • 工作目录、环境变量在此处注入:cmd.Dir = c.workingDircmd.Env = c.env

🔍 GetExecutablePath

返回当前实例使用的 composer 可执行文件路径。

签名

go
func (c *Composer) GetExecutablePath() string

返回值

类型说明
stringcomposer 可执行文件的完整路径,未设置时为空串

示例

go
execPath := comp.GetExecutablePath()
fmt.Printf("使用的 Composer 可执行文件: %s\n", execPath)

IsInstalled

检查当前实例是否已指向有效的 composer 可执行文件路径。仅检查路径是否非空,不会实际运行 composer 验证。

签名

go
func (c *Composer) IsInstalled() bool

返回值

类型说明
bool路径非空返回 true,否则 false

示例

go
if comp.IsInstalled() {
    fmt.Println("Composer 已安装")
} else {
    fmt.Println("Composer 未安装")
}

注意

IsInstalled 只判断 executablePath != ""。如果想确认 composer 真能跑起来,调用 GetVersion()GetVersionInfo() 更可靠。


🚀 SelfUpdate

把 composer 自身更新到最新版本,相当于执行 composer self-update

签名

go
func (c *Composer) SelfUpdate() error

返回值

类型说明
error更新失败时返回 ErrSelfUpdateFailed 包裹的错误

示例

go
err := comp.SelfUpdate()
if err != nil {
    log.Fatalf("更新 Composer 失败: %v", err)
}
fmt.Println("Composer 已更新到最新版本")

进阶

  • 想在更新时拿到进度回调,用 SelfUpdateWithProgress()(位于 auto_install.go),返回 (string, error),其中字符串是新版本号。
  • 更新前可用 GetVersion() 记录旧版本,更新后再对比。
  • SelfUpdate 需要对 composer 可执行文件有写权限,常需 sudo(见 环境变量模块EnableSuperuser)。

📊 相关的辅助方法

除上述核心方法外,composer.go 还提供几个查询方法:

方法签名说明
GetWorkingDirfunc (c *Composer) GetWorkingDir() string获取当前工作目录
GetEnvfunc (c *Composer) GetEnv() []string获取当前环境变量

以及包级测试辅助函数(生产代码一般不用):

函数签名说明
SetupMockOutputfunc SetupMockOutput(command, output string, err error)为特定命令设置模拟输出
ClearMockOutputsfunc ClearMockOutputs()清除所有模拟输出

🧭 下一步

掌握核心运行后,接下来学习:

  • 📦 依赖管理Install / Update / DumpAutoload 全家桶
  • 🔍 包操作RequirePackage / Remove / Show / Search
  • 📊 版本信息GetVersion / GetVersionInfo 结构化版本

基于 MIT 许可证发布