🛠️ 核心运行
本页讲解 pkg/composer 的"地基"——如何创建 Composer 实例、如何执行任意子命令、如何检测与升级 composer 本身。这些方法定义在 composer.go 与 version.go 中,是所有其它方法(Install、RequirePackage 等)的底层依赖。
🧱 核心类型
Composer 结构体
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 结构体
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 时会自动安装再重新检测。
签名
func New(options Options) (*Composer, error)参数
| 参数 | 类型 | 说明 |
|---|---|---|
options | Options | 自定义 Composer 实例的选项 |
返回值
| 类型 | 说明 |
|---|---|
*Composer | 创建的 Composer 实例 |
error | 创建失败时返回 ErrComposerNotFound 或 ErrComposerInstallation |
示例
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,是最常用的构造入口。
签名
func DefaultOptions() Options返回值
| 类型 | 说明 |
|---|---|
Options | 默认配置:WorkingDir=""、AutoInstall=true、DefaultTimeout=10*time.Minute |
示例
options := composer.DefaultOptions()
comp, err := composer.New(options)为什么不直接 Options{}
直接构造 Options{} 会让 AutoInstall=false、DefaultTimeout=0(即不超时),容易踩坑。始终用 DefaultOptions() 作为起点再按需覆盖。
📁 SetWorkingDir
设置 composer 命令的工作目录。所有后续命令都会在此目录下执行。
签名
func (c *Composer) SetWorkingDir(dir string)参数
| 参数 | 类型 | 说明 |
|---|---|---|
dir | string | 要设置的工作目录路径 |
示例
comp.SetWorkingDir("/path/to/php/project")
// 之后所有 comp.Install() / comp.Run(...) 都在该目录执行进阶
- 对应的 getter 是
GetWorkingDir() string。 - 一次性在别的目录执行单条命令,可用
InstallWithWorkingDir(workingDir, noDev, optimize)(见 依赖管理),它会在执行后恢复原工作目录。
🌐 SetEnv
设置执行 composer 命令时的环境变量,常用于配置 HTTP 代理、COMPOSER_HOME 或身份验证信息。
签名
func (c *Composer) SetEnv(env []string)参数
| 参数 | 类型 | 说明 |
|---|---|---|
env | []string | 环境变量数组,格式为 ["KEY=VALUE", ...] |
示例
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)见 环境变量模块(SetProcessTimeout、DisableInteraction等)。
▶️ Run
执行任意 composer 子命令并返回输出,是 SDK 的核心方法。默认使用 10 分钟超时。
签名
func (c *Composer) Run(args ...string) (string, error)参数
| 参数 | 类型 | 说明 |
|---|---|---|
args | ...string | 命令参数,第一个参数是 composer 子命令(如 "install"、"require") |
返回值
| 类型 | 说明 |
|---|---|
string | 命令的合并输出(stdout+stderr) |
error | 失败时返回包裹了 ErrCommandExecution 的错误 |
示例
// 执行 "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 命令,适合可能长时间运行的命令。
签名
func (c *Composer) RunWithTimeout(timeout time.Duration, args ...string) (string, error)参数
| 参数 | 类型 | 说明 |
|---|---|---|
timeout | time.Duration | 命令执行的最大超时时间 |
args | ...string | 命令参数 |
返回值
| 类型 | 说明 |
|---|---|
string | 命令的合并输出 |
error | 失败或超时时返回错误 |
示例
// 执行可能需要很长时间的安装命令,设置 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 命令,提供最大控制权——可超时、可取消、可携带截止时间。
签名
func (c *Composer) RunWithContext(ctx context.Context, args ...string) (string, error)参数
| 参数 | 类型 | 说明 |
|---|---|---|
ctx | context.Context | 上下文,可用于取消或设置超时 |
args | ...string | 命令参数 |
返回值
| 类型 | 说明 |
|---|---|
string | 命令的合并输出 |
error | 失败时返回包裹了 ErrCommandExecution 的错误 |
示例
// 创建可以手动取消的上下文
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)
}
}进阶
RunWithContext是Run与RunWithTimeout的底层实现,也是 mock 注入的入口:会先调用getMockOutput(args...),命中即返回 mock 输出。- 命令通过
exec.CommandContext(ctx, c.executablePath, args...)构造,ctx取消时会向子进程发送终止信号。 - 工作目录、环境变量在此处注入:
cmd.Dir = c.workingDir、cmd.Env = c.env。
🔍 GetExecutablePath
返回当前实例使用的 composer 可执行文件路径。
签名
func (c *Composer) GetExecutablePath() string返回值
| 类型 | 说明 |
|---|---|
string | composer 可执行文件的完整路径,未设置时为空串 |
示例
execPath := comp.GetExecutablePath()
fmt.Printf("使用的 Composer 可执行文件: %s\n", execPath)✅ IsInstalled
检查当前实例是否已指向有效的 composer 可执行文件路径。仅检查路径是否非空,不会实际运行 composer 验证。
签名
func (c *Composer) IsInstalled() bool返回值
| 类型 | 说明 |
|---|---|
bool | 路径非空返回 true,否则 false |
示例
if comp.IsInstalled() {
fmt.Println("Composer 已安装")
} else {
fmt.Println("Composer 未安装")
}注意
IsInstalled 只判断 executablePath != ""。如果想确认 composer 真能跑起来,调用 GetVersion() 或 GetVersionInfo() 更可靠。
🚀 SelfUpdate
把 composer 自身更新到最新版本,相当于执行 composer self-update。
签名
func (c *Composer) SelfUpdate() error返回值
| 类型 | 说明 |
|---|---|
error | 更新失败时返回 ErrSelfUpdateFailed 包裹的错误 |
示例
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 还提供几个查询方法:
| 方法 | 签名 | 说明 |
|---|---|---|
GetWorkingDir | func (c *Composer) GetWorkingDir() string | 获取当前工作目录 |
GetEnv | func (c *Composer) GetEnv() []string | 获取当前环境变量 |
以及包级测试辅助函数(生产代码一般不用):
| 函数 | 签名 | 说明 |
|---|---|---|
SetupMockOutput | func SetupMockOutput(command, output string, err error) | 为特定命令设置模拟输出 |
ClearMockOutputs | func ClearMockOutputs() | 清除所有模拟输出 |
🧭 下一步
掌握核心运行后,接下来学习: