Skip to content

📄 composer.json 操作

直接读写 composer.json 文件,管理依赖、脚本、自动加载、config 与顶级属性——不经过 composer require 命令,纯文件操作。

Composer Skills 把对 composer.json 的所有结构化操作集中在 composer_json.go,并提供「读 → 改 → 写」原子模式的高层方法(AddRequireAddScriptAddAutoloadSetConfigSetProperty 等)。配套的便捷方法 ReadComposerJson / ReadComposerLock(位于 convenience.go)提供另一套更轻量的数据结构,便于纯查询场景使用。

何时使用

  • 🧱 脚手架工具程序化生成 composer.json:先 ReadComposerJSON 拿模板,再 SetProperty 填字段,最后 WriteComposerJSON
  • ➕ 给已有项目追加依赖与脚本,且不想触发 composer require 的安装流程(只想改文件)。
  • 🔧 CI 里批量改 configprocess-timeoutvendor-dir 等)。
  • 🧪 单元测试用 SetMockComposerJSON 注入假数据,避免依赖真实文件。

结构化类型

ComposerJSON

composer_json.go 中的完整结构,对应 composer.json 全部标准字段。

go
type ComposerJSON struct {
    Name                string                 `json:"name,omitempty"`
    Description         string                 `json:"description,omitempty"`
    Type                string                 `json:"type,omitempty"`
    Keywords            []string               `json:"keywords,omitempty"`
    Homepage            string                 `json:"homepage,omitempty"`
    License             interface{}            `json:"license,omitempty"`
    Authors             []map[string]string    `json:"authors,omitempty"`
    Support             map[string]string      `json:"support,omitempty"`
    Require             map[string]string      `json:"require,omitempty"`
    RequireDev          map[string]string      `json:"require-dev,omitempty"`
    Suggest             map[string]string      `json:"suggest,omitempty"`
    Autoload            map[string]interface{} `json:"autoload,omitempty"`
    AutoloadDev         map[string]interface{} `json:"autoload-dev,omitempty"`
    Repositories        map[string]interface{} `json:"repositories,omitempty"`
    Config              map[string]interface{} `json:"config,omitempty"`
    Scripts             map[string]interface{} `json:"scripts,omitempty"`
    ScriptsDescriptions map[string]string      `json:"scripts-descriptions,omitempty"`
    Extra               map[string]interface{} `json:"extra,omitempty"`
    Bin                 []string               `json:"bin,omitempty"`
    Archive             map[string]interface{} `json:"archive,omitempty"`
    NonFeatureBranches  []string               `json:"non-feature-branches,omitempty"`
    MinimumStability    string                 `json:"minimum-stability,omitempty"`
    PreferStable        bool                   `json:"prefer-stable,omitempty"`
    Replace             map[string]string      `json:"replace,omitempty"`
    Conflict            map[string]string      `json:"conflict,omitempty"`
    Provide             map[string]string      `json:"provide,omitempty"`
}

License 是 interface{}

License 字段类型为 interface{},因为 Composer 允许它是字符串("MIT")或字符串数组(["MIT", "BSD-2-Clause"])。

ComposerJsonData / ComposerLockData

convenience.go 中的轻量结构,用于纯文件查询(不写回)。ComposerJsonData 字段集与 ComposerJSON 略有差异(如 Repositories[]interface{} 而非 map)。ComposerLockData 对应 composer.lock 的根。

go
type ComposerJsonData struct {
    Name             string                 `json:"name,omitempty"`
    Description      string                 `json:"description,omitempty"`
    Type             string                 `json:"type,omitempty"`
    Keywords         []string               `json:"keywords,omitempty"`
    Require          map[string]string      `json:"require,omitempty"`
    RequireDev       map[string]string      `json:"require-dev,omitempty"`
    Autoload         map[string]interface{} `json:"autoload,omitempty"`
    Scripts          map[string]interface{} `json:"scripts,omitempty"`
    Config           map[string]interface{} `json:"config,omitempty"`
    // ... 其余字段见 convenience.go
}

type ComposerLockData struct {
    ContentHash      string            `json:"content-hash,omitempty"`
    Packages         []LockPackageData `json:"packages,omitempty"`
    PackagesDev      []LockPackageData `json:"packages-dev,omitempty"`
    Platform         map[string]string `json:"platform,omitempty"`
    PlatformDev      map[string]string `json:"platform-dev,omitempty"`
    PluginApiVersion string            `json:"plugin-api-version,omitempty"`
}

type LockPackageData struct {
    Name       string            `json:"name"`
    Version    string            `json:"version"`
    Type       string            `json:"type,omitempty"`
    License    []string          `json:"license,omitempty"`
    Abandoned  interface{}       `json:"abandoned,omitempty"`
    // ... 其余字段见 convenience.go
}

方法签名

composer_json.go(写回型)

方法签名说明
📖 ReadComposerJSONfunc (c *Composer) ReadComposerJSON() (*ComposerJSON, error)读取并解析 composer.json
💾 WriteComposerJSONfunc (c *Composer) WriteComposerJSON(composerJSON *ComposerJSON) error写回 composer.json
➕ AddRequirefunc (c *Composer) AddRequire(packageName, version string, isDev bool) error追加依赖到 require / require-dev
➖ RemoveRequirefunc (c *Composer) RemoveRequire(packageName string, isDev bool) error移除依赖
📜 AddScriptfunc (c *Composer) AddScript(name string, script interface{}, description string) error添加脚本及可选描述
🗑️ RemoveScriptfunc (c *Composer) RemoveScript(name string) error移除脚本及描述
🧩 AddAutoloadfunc (c *Composer) AddAutoload(type_ string, namespace string, paths interface{}, isDev bool) error添加自动加载规则
⚙️ SetConfigfunc (c *Composer) SetConfig(key string, value interface{}) error设置 config 字段
🔍 GetConfigfunc (c *Composer) GetConfig(key string) (interface{}, error)读取 config 字段
🏷️ SetPropertyfunc (c *Composer) SetProperty(property string, value interface{}) error设置顶级属性

convenience.go(纯查询型)

方法签名说明
📖 ReadComposerJsonfunc (c *Composer) ReadComposerJson() (*ComposerJsonData, error)读取为轻量结构
📂 ReadComposerJsonFilefunc ReadComposerJsonFile(filePath string) (*ComposerJsonData, error)按路径读取(包级函数)
🔒 ReadComposerLockfunc (c *Composer) ReadComposerLock() (*ComposerLockData, error)读取 composer.lock
🔒 ReadComposerLockFilefunc ReadComposerLockFile(filePath string) (*ComposerLockData, error)按路径读取 lock

参数说明

AddRequire / RemoveRequire

参数类型说明
packageNamestring包名,如 symfony/console
versionstring版本约束,如 ^5.0
isDevbooltrue 写入 require-dev,否则 require

AddScript

参数类型说明
namestring脚本名,如 post-install-cmd
scriptinterface{}字符串命令、字符串数组或 PHP 类调用
descriptionstring描述(空串则不写 scripts-descriptions

AddAutoload

参数类型说明
type_stringpsr-4 / psr-0 / classmap / files
namespacestring命名空间,如 App\
pathsinterface{}路径字符串或字符串数组
isDevbooltrue 写入 autoload-dev

SetProperty

参数类型说明
propertystring仅支持 name/description/type/keywords/homepage/license/minimum-stability/prefer-stable
valueinterface{}与属性匹配的值类型

示例

程序化初始化一个新项目

go
package main

import (
	"fmt"
	"log"

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

func main() {
	comp, err := composer.New(composer.DefaultOptions())
	if err != nil {
		log.Fatal(err)
	}

	// 从空配置开始
	cj := &composer.ComposerJSON{}
	cj.Name = "acme/widget"
	cj.Description = "一个超棒的小部件库"
	cj.Type = "library"
	cj.License = "MIT"
	cj.Authors = []map[string]string{
		{"name": "Acme Team", "email": "dev@acme.io"},
	}

	// 写出初始 composer.json
	if err := comp.WriteComposerJSON(cj); err != nil {
		log.Fatal(err)
	}

	// 追加依赖与脚本
	if err := comp.AddRequire("symfony/console", "^6.0", false); err != nil {
		log.Fatal(err)
	}
	if err := comp.AddRequire("phpunit/phpunit", "^10.0", true); err != nil {
		log.Fatal(err)
	}
	if err := comp.AddScript("test", "phpunit", "运行测试套件"); err != nil {
		log.Fatal(err)
	}

	// PSR-4 自动加载
	if err := comp.AddAutoload("psr-4", "Acme\\Widget\\", "src/", false); err != nil {
		log.Fatal(err)
	}

	fmt.Println("✅ composer.json 已生成")
}

读取并修改 config

go
cj, err := comp.ReadComposerJSON()
if err != nil {
	if err == composer.ErrComposerJSONNotFound {
		log.Fatal("当前目录没有 composer.json")
	}
	log.Fatal(err)
}
fmt.Printf("项目: %s\n", cj.Name)

// 改进程超时
if err := comp.SetConfig("process-timeout", 600); err != nil {
	log.Fatal(err)
}

// 读回
v, _ := comp.GetConfig("process-timeout")
fmt.Printf("process-timeout = %v\n", v)

用便利函数只读查询

go
// 轻量结构,适合只读场景
data, err := comp.ReadComposerJson()
if err != nil {
	log.Fatal(err)
}
fmt.Printf("直接依赖: %d\n", len(data.Require))

lock, err := comp.ReadComposerLock()
if err != nil {
	log.Fatal(err)
}
fmt.Printf("已安装包: %d\n", len(lock.Packages))

按绝对路径读取(不依赖工作目录)

go
data, err := composer.ReadComposerJsonFile("/srv/apps/myapp/composer.json")
if err != nil {
	log.Fatal(err)
}
fmt.Println(data.Name)

进阶

两套读 API 的区别

  • ReadComposerJSON()composer_json.go)→ *ComposerJSON,字段全、可写回,支持 mock。
  • ReadComposerJson()convenience.go)→ *ComposerJsonData,字段更聚焦于查询,无 mock 支持。

需要 WriteComposerJSON 时用前者;只读统计时用后者更轻量。

AddRequire 不执行安装

AddRequire 只改 composer.json 文件,不会触发 composer install。要真正安装依赖,调用后需额外执行 comp.Install(false, false)RequirePackage(走 composer require 命令)。

SetProperty 的硬编码白名单

SetProperty 用 switch 硬编码了支持的属性名。传 binauthors 等未列出的属性会返回 unsupported property 错误。如需设置这些字段,请直接操作 *ComposerJSON 后调用 WriteComposerJSON

测试用 Mock

SetMockComposerJSON / ClearMockComposerJSON 可让 ReadComposerJSON 返回注入的假数据,单测时无需真实文件。注意:便捷版的 ReadComposerJson 不受此 mock 影响。

基于 MIT 许可证发布