A port of the concept of goaop/framework — an aspect-oriented programming framework — from PHP to Go.
AOP extends the language with the ability to intercept method executions declaratively, without touching the intercepted code: cross-cutting concerns (logging, caching, transactions, metrics, security checks) live in aspects, and a weaver wires them into the application.
The PHP framework performs load-time source weaving: it intercepts class
loading, rewrites the class into a __AopProxied trait, and generates a proxy
class that runs an interceptor chain around the original method bodies. All
pointcut matching and advice ordering happen statically at weave time; the
generated code contains zero runtime checks.
Go has no runtime class loading, so this port moves the same pipeline entirely to build time:
- The
goaopCLI loads your packages withgo/packages(full syntax + type information). - Aspects are discovered by
//aop:comment directives — the Go analogue of PHP attributes. - Pointcut expressions (the same DSL, adapted to Go syntax) are matched
statically against packages, receiver types, and methods — two-stage, like
the PHP
Pointcutcontract: context first to prune cheaply, then members. - For every matched method the weaver writes a shadow copy of the source
file where only the declaration identifier is renamed
(
Greet→__aop_Greet) — the analogue of the class → trait rewrite — and generates azz_goaop_woven.gofile per package containing a proxy method with the original name that runs the ordered interceptor chain, terminating in the renamed original. - An
overlay.jsonmaps original file paths to the shadow tree, and the build runs asgo build -overlay— original sources are never modified.
goaop run ./examples/demo # weave + run
go run ./examples/demo # plain run, no aspects — zero runtime intrusion
The engine (contracts, pointcut matcher, rewriter) is independent from the
overlay driver, so a transparent -toolexec driver can be added later without
redesign.
//aop:aspect
type LoggingAspect struct{}
//aop:pointcut("execution(exported **.Service.*(*))")
func (a *LoggingAspect) serviceMethods() {}
//aop:before("serviceMethods")
func (a *LoggingAspect) LogEntry(inv aop.MethodInvocation) {
log.Printf("calling %s args=%v", inv, inv.Arguments())
}
//aop:around("execution(**.Service.Greet(*))", order=10)
func (a *LoggingAspect) Time(inv aop.MethodInvocation) error {
start := time.Now()
err := inv.Proceed()
log.Printf("%s took %s", inv, time.Since(start))
return err
}
//aop:afterthrowing("serviceMethods")
func (a *LoggingAspect) LogError(inv aop.MethodInvocation, err error) {
log.Printf("%s failed: %v", inv, err)
}Advice kinds and their exact semantics are ported from the PHP framework:
| Kind | Semantics |
|---|---|
before |
advice runs, then the chain proceeds |
after |
chain proceeds; advice runs in a defer (i.e. finally — also on panic) |
around |
advice fully controls the invocation and calls Proceed() itself |
afterthrowing |
fires on a non-nil trailing error result or a panic (wrapped as *aop.PanicError, re-panicked after the advice) |
Advice ordering (before → after → around, ties broken by order=N) is decided
at weave time and baked into the generated chain.
The PHP DSL adapted to Go:
execution(exported **.Service.Greet*(*))
execution(github.com/acme/**/demo.*Repository.save|update(*))
within(github.com/acme/app/**) && !within(**/internal/**)
LoggingAspect.serviceMethods // named pointcut reference
**matches across/and.;*matches one identifier / path segment;?one character;|is alternation inside a name.- The last two
.-separated components are the receiver-type and method patterns; everything before is the package import-path pattern (omit it to match any package). exported/unexportedreplace PHP'spublic/protected|private.&&,||,!combine pointcuts; named pointcuts declared with//aop:pointcutcan be referenced byAspect.nameornamewithin the same aspect.- The argument list is always
(*)in this prototype, as in the PHP framework.
Prototype. Method-execution interception only; property access, function, constructor/init interception and introductions from the PHP framework are out of scope for now (property interception is not portable to Go at all).
| Package | Role |
|---|---|
aop |
runtime contracts: joinpoints, interceptor chain, registry |
aop/pointcut |
weave-time pointcut model, combinators, expression parser |
weaver |
driver-independent engine: discovery, matching, orchestration |
weaver/rewrite |
source rename + proxy code generation |
weaver/overlay |
shadow tree + go build -overlay driver |
cmd/goaop |
the CLI: `goaop build |