Create a custom generator
Create a generator when your team repeatedly writes the same class shape. This example adds make:report to vendor/bin/foundation using a stub, a command class, and the project’s tooling provider.
Install the CLI using the Foundation CLI installation guide. This example assumes the project’s Composer autoload.psr-4 mapping is Plugin\\ → src/. If you are new to providers, see Register Service Providers.
Create the report stub
Section titled “Create the report stub”Create foundation/stubs/report/report.stub:
Define the generator
Section titled “Define the generator”Create src/Tooling/Commands/Report_Command.php:
NAME identifies the console command. CONFIG_KEY selects its project settings, and DEFAULT_NAMESPACE is a suffix relative to the application’s Composer namespace: Reports produces Plugin\\Reports. The stub path may be project-relative or absolute.
The base supplies the name argument, --namespace, --path, PHP validation, and file creation. Keep its inherited constructor; define the constants and stub() in your generator.
Register the command
Section titled “Register the command”Create src/Tooling/Tooling_Provider.php:
Foundation automatically loads this provider when its executable runs. Add future commands to the same collection and keep their dependency bindings in this provider. Command names and aliases must be unique; startup reports conflicting commands rather than replacing one.
Run from the project root:
This creates src/Reports/Sales_Report.php in Plugin\\Reports. List commands or inspect the generator’s options with:
Generation refuses to overwrite existing files. Edit the existing class or choose another name. If a class name, namespace, or stub is invalid, correct the reported input and retry.
Configuration
Section titled “Configuration”Change the generated namespace
Section titled “Change the generated namespace”Add the following setting to the project’s optional root config.php, alongside existing application settings:
The same command now creates classes in src/Exports/. Foundation loads this shared configuration once at startup. See Configure the Container for environment values and configuration shared with the application.
Override a single invocation
Section titled “Override a single invocation”--namespace takes precedence over project configuration. Without --path, the directory follows the most specific runtime Composer PSR-4 mapping. An explicit path selects the output directory for that invocation; keep Composer’s mapping consistent with the generated namespace.
Customize the tooling setup
Section titled “Customize the tooling setup”See Project tooling to select a custom provider file, keep tooling under development autoloading, or load prerequisite package providers.
Commands that perform other tasks can extend Symfony’s Command and contribute to the same CliProvider::COMMANDS collection. Supply their constructor dependencies using the normal provider binding APIs. Use an ordinary Symfony command for specialized generation workflows that need additional arguments, coordinated files, or source editing.
Package generated code
Section titled “Package generated code”Install the CLI with --dev. Generated application classes belong in normal Composer autoload mappings, and any packages they use belong in require. Exclude local tooling and stubs from production archives through the project’s .gitattributes when appropriate.
Testing
Section titled “Testing”Run the generator in a disposable project using its normal Composer mapping. Check the default output location, a configured namespace, and an explicit override. Run the same command twice and confirm the second attempt fails while preserving the first file.
For automated command tests, use Symfony’s CommandTester with the command resolved through a tooling container. Test vendor/bin/foundation in a subprocess to verify provider lookup, configuration loading, and command registration together.