# Go! AOP: source weaving for PHP, with no extensions and no eval

> Go! AOP implements aspect-oriented programming for PHP by rewriting your source into static cached PHP files at build time, which is why it needs no PECL extension and makes no eval calls. The catch is the same as any weaver: the transformed code has to be regenerated and shipped with your application.

**goaop/framework** — :gem: Go! AOP PHP - modern aspect-oriented framework for the new level of software development

- Repository: https://github.com/goaop/framework
- Website: https://go.aopphp.com
- Stars: 1,669 · Forks: 167
- Language: PHP
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/goaop-framework

## Weaving happens before your application starts

Most AOP systems hook into method calls at runtime. Go! AOP does not. It takes your source, applies matching aspects to it, and writes out transformed PHP files that get cached and then executed normally.

The README is explicit about the consequences, and they are the design's main selling point. There are zero `eval()` calls and no `create_function()` usage, and all transformations produce static PHP files that can be reviewed, scanned by security tools and audited. No code is generated during a request.

The second consequence is that interception is not runtime indirection. Advices are woven as first-class callables pointing straight at your aspect methods, joinpoints are resolved at compile time and cached into the generated code, and there is no `__call` indirection or proxy object wrapping your instances. The README states zero overhead for non-intercepted methods, since untouched code is untouched.

The cost is the one every source weaver carries: your build now has a step, and a stale cache is a bug rather than an inefficiency.

## It covers the method kinds other PHP AOP tools skip

The support table is where this project differentiates itself from the alternatives built on PHP extensions.

Interception of public and protected methods is supported, and so is interception of static and final methods, interception of methods in `final` classes, interception of trait methods, and interception of enum methods on PHP 8.1 and later. Before, After and Around hook types are all available.

Private methods are intercepted too. That single row removes the usual escape hatch, because a common response to a limitation like this is to make the method protected or public purely so a framework can reach it.

Property interception arrives with PHP 8.4 through native property hooks on generated proxy classes, and the README is precise about the boundaries. Supported targets are properties declared in the woven class itself, plus inherited public and protected properties from parent classes. The list of what is deliberately skipped is longer: `static`, `readonly` and already-hooked properties, inherited `final` properties, and inherited `private` properties.

For array-typed properties, the proxy emits only a by-reference `&get` hook and no `set`, specifically so that indirect modifications like `array_push($this->items, ...)` remain valid. That is a level of detail you only find in a project that has actually thought about what breaks.

## Pointcuts read like English and cache like a compiler

Pointcut expressions use a readable grammar rather than a proprietary syntax. The README's example is `execution(public **->save*(*))`, targeting public methods named `save` followed by anything, on any class in the hierarchy. You can match by visibility, name patterns, attributes and class hierarchy in a single expression.

Caching is where the pointcut system pays off. Advices are baked into the generated file as first-class callables, and joinpoints are resolved at compile time, so there is no reflection cost and no lazy advisor lookup on each call. Aspects are initialised once and cached, which the README says leaves virtually zero initialisation overhead on subsequent requests.

Two runtime integrations matter for production. OPcache support is first class, because transformed files are plain PHP and get optimised by the opcode cache like anything else, which is one of the practical reasons a weaver is preferable to a proxy approach. XDebug support is claimed as a differentiator: since no unreadable proxy code is generated, you can set breakpoints in your aspects or in the original classes and step through normally.

The stated start-up cost is a few milliseconds, measured by the project's own claim rather than by anything you can verify from the README.

## Type safety is treated as a feature, not a stretch goal

The codebase passes PHPStan at level 10, which the README describes as the strictest analysis level, and there are several separate baseline files at the root: `phpstan-baseline.php`, `phpstan-baseline-strict.php`, `phpstan-baseline-tests.php` and `phpstan-baseline-demos.php`.

That arrangement is more interesting than the badge. One baseline for the library, one for its own strict checks, one for tests and one for demos means each area is held to its own bar rather than everything being exempted against a single list. `phpstan.neon` sits alongside them, plus `.php-cs-fixer.dist.php` for formatting and `phpunit.xml.dist` for tests.

For a framework that generates code into your project, this matters more than usual. Generated PHP that nobody type checks is how type errors appear in your application instead of in the tool. Here the claim is that the weaving output is ordinary typed PHP, which is a consequence of the no-eval, static-file design rather than a separate feature.

Installation is a single Composer step, since the README frames the framework as pure PHP with no compilation and no system dependencies, and Packagist carries the package under the same coordinates as the repository:

```bash
composer require goaop/framework
```

The `bin/` directory at the root of the repository is where the command line entry points live, which is the other thing you will want after the require: the cache has to be warmed so the woven files exist before your application runs.

The repository also ships `demos/`, `docs/`, `bin/`, `src/` and `tests/`, and the presence of `UPGRADE-4.0.md` tells you a major version boundary is documented, which suggests the 3.x line is being prepared for a transition.

## Version requirements and the gap between 3.0 and 3.1

The README badge states a minimum PHP version of 8.4, and that is a hard floor rather than a recommendation. The property interception feature depends on native PHP 8.4 property hooks, and the repository carries dedicated limitation documents for both 8.4 and 8.5, with the 8.5 file described as covering audit results.

Release history shows an unusual pattern. Version 3.0.0 was published on 2021-04-05, followed by a long gap and then 3.1.0 and 3.1.1 on 2026-05-14. The last push was on 2026-10-14, so the project is being worked on now.

Read that together with the PHP 8.4 requirement and you get the likely shape of the intervening years: the library sat on an older PHP floor while the ecosystem moved, and the recent releases are the result of modernising it to require 8.4 and adding property interception. That is a reasonable inference from the version numbers and the badge, but the README does not state a migration path from 3.0 to 3.1, and `UPGRADE-4.0.md` implies there is more to come.

For an evaluator, the practical consequence is that you should treat 3.1.x as a recent release on a codebase with a long quiet period behind it, and read the changelog rather than assuming continuity.

## Against extension-based AOP and against composer middleware

The traditional PHP AOP approach uses an extension such as php-aop, runkit or uopz. The README names them and dismisses them in one line: no PECL extensions required.

The difference is deployment, not capability. Extensions give you interception at runtime with no build step, which is exactly why they need native code and why they break across PHP versions and hosting environments. Go! AOP trades that for a `composer require` and a warm step. If you deploy to shared hosting where you cannot install extensions, that trade is forced. If you control the whole environment and already run extensions, the trade is less clear.

The second comparison is middleware, and for PHP this is the more common answer. Framework middleware handles logging, authentication and caching around requests. What AOP offers that middleware does not is the ability to target individual methods, including private, static and final ones, and to modify arguments and return values through Around advice. If your cross-cutting concern is per-request, middleware is the better tool and this framework is overkill. If it is per-method, the pointcut grammar is what you actually need.

The third option is plain traits or decorators, which need no machinery at all and are easier to read. They lose when the same behaviour must apply to hundreds of methods you do not control, which is the case the README is written for.

## Conclusion

Adopt Go! AOP if you maintain a PHP codebase with a cross-cutting concern repeated across hundreds of methods and you can run a cache warm step in your deploy pipeline. Do not adopt it for a small project or if your deployment forbids generated files, since the value only appears at scale and the mechanism is a build artifact you must manage. Verify first that your PHP version is 8.4 or newer, which the README states as the minimum and which the property interception feature depends on.

## FAQ

### Does Go! AOP require any PHP extensions?

No. The README states it needs no PECL or PIE extensions and dismisses php-aop, runkit and uopz by name, describing the implementation as pure PHP installed with `composer require`, with no compilation and no system dependencies.

### Can Go! AOP intercept private, static and final methods?

Yes. The support table lists interception of public and protected methods, static and final methods, methods in `final` classes, trait methods, enum methods on PHP 8.1 and later, and private methods, with Before, After and Around hook types.

### What PHP version does Go! AOP require?

The README badge states a minimum of PHP 8.4. Property interception relies on native PHP 8.4 property hooks, and the repository documents limitations for both PHP 8.4 and PHP 8.5.

### Does Go! AOP use eval or generate code at runtime?

No. The README states there are zero `eval()` calls and no `create_function()` usage, and that all transformations produce static PHP files which can be reviewed and audited, with no hidden code generation at runtime.

### What limitations does Go! AOP have with PHP 8.4 property hooks?

Property interception covers properties declared in the woven class and inherited public or protected ones. It intentionally skips `static`, `readonly` and already-hooked properties, inherited `final` properties and inherited `private` properties. Array-typed properties get only a by-reference `&get` hook so indirect modifications stay valid.

## Sources

- [goaop/framework on GitHub](https://github.com/goaop/framework)
- [License: MIT](https://github.com/goaop/framework/blob/master/LICENSE)
- [Project website](https://go.aopphp.com)
- [README](https://github.com/goaop/framework/blob/master/README.md)
- [Releases](https://github.com/goaop/framework/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/goaop-framework
