CLI · Winter Console

The di command

At startup the framework walks the project and finds its classes — that is the most expensive part of booting. di manages the cache of that walk: it builds it ahead of time, shows its contents and deletes it. It also builds the proxies for #[Async].

Actions build · clean · show · asyncCache in the system temp directoryDEBUG=true cache disabled

Why

Walking the project reads every .php and includes those that declare a class. On an average project that is tens of milliseconds and extra megabytes in the master process’s memory — the process every worker is then forked from.

di build does that work once, ahead of time, and puts the result in a file. At startup the application reads the prepared list instead of walking the disk.

Actions

Action What it does
build Walks the project once: writes the cache, generates the #[Async] proxies, checks correctness
clean Deletes the cache and every generated proxy
show [pattern] Prints the classes from the cache; the pattern filters by FQCN substring
async [pattern] Prints the #[Async] methods and whether a proxy was built for each
bash
php call di build
php call di clean
php call di show
php call di show Repository     # only those containing "Repository"
php call di async

build before starting

The main scenario is warming it up in a container, before the application starts. That is exactly how the image cfg docker creates works:

sh
if ! su-exec winter php /var/www/html/call di build; then
  echo "entrypoint: 'call di build' failed — refusing to start" >&2
  exit 1
fi
exec su-exec winter php /var/www/html/call run --port="$PORT"

`build` is also a check

The command does not merely store a list: it parses every #[Async] method and generates a proxy for it. A method that cannot be proxied that way fails the build — here, with a comprehensible message and exit code 1, rather than in production on the first call.

That is why it goes into the entrypoint before startup: a broken application gives one intelligible line in docker logs instead of a container crash-looping.

build additionally warns when a class with #[Async] methods is created with new — the proxy will not apply then. The check is textual and does not see new $class or factories, so it is a warning specifically: it does not fail the build.

Where the cache lives

By default, in the system temp directory rather than in the project:

text
/tmp/flytachi.winter.volatile.<project-name>/di.php      the class list
/tmp/flytachi.winter.volatile.<project-name>/async/     the #[Async] proxies

The exact paths are printed by php call help di. Generated code does not end up in the image and does not survive a machine reboot — that is a deliberate choice.

The practical consequence: cleaning storage/ to reset the cache is pointless; di clean is what does that.

With `DEBUG=true` the cache is not used at all

In development mode the application walks the project on every start, so that a new class is picked up without a manual rebuild. A di build you made is simply ignored in that mode.

If changes are “not visible” after a di build, check DEBUG first.

show without a cache

If there is no cache, show does not fail: it performs a live walk, prints the result and warns that what it showed was not written to disk. That keeps the command useful in development too.

async

bash
php call di async

It shows every method marked #[Async] and the state of each one’s proxy. This is how to answer the question “why does this method run synchronously”: if no proxy was built, the call goes through directly.

Next