codger / generate
Code generator, base framework
Requires
- php: >=8.1
- monolyth/cliff: ^0.7.1
- twig/twig: ^2|^3
Requires (Dev)
- toast/unit: ^2.0
- dev-master
- 0.10.3
- 0.10.2
- 0.10.1
- 0.10.0
- 0.9.10
- 0.9.9
- 0.9.8
- 0.9.7
- 0.9.6
- 0.9.5
- 0.9.4
- 0.9.3
- 0.9.2
- 0.9.1
- 0.9.0
- 0.8.5
- 0.8.4
- 0.8.3
- 0.8.2
- 0.8.1
- 0.8.0
- 0.7.2
- 0.7.1
- 0.7.0
- 0.6.15
- 0.6.14
- 0.6.13
- 0.6.12
- 0.6.11
- 0.6.10
- 0.6.9
- 0.6.8
- 0.6.7
- 0.6.6
- 0.6.5
- 0.6.4
- 0.6.3
- 0.6.2
- 0.6.1
- 0.6.0
- 0.5.5
- 0.5.4
- 0.5.3
- 0.5.2
- 0.5.1
- 0.5.0
- 0.4.4
- 0.4.3
- 0.4.2
- 0.4.1
- 0.4.0
- 0.3.1
- 0.3.0
- 0.2.1
- 0.2.0
- 0.1.0
- dev-dependabot/composer/twig/twig-3.11.2
- dev-develop
This package is auto-updated.
Last update: 2024-12-06 21:09:10 UTC
README
CODe GEneratoR, base framework
In any software project adhering to some form of standards (be it your own or those "mandated" by a framework) there will be lots of boilerplate code. E.g. in an MVC setting you'll (pratically) always have models, views and controllers for each component that in their core are similar. An example would be if you use Doctrine - the entities are generated based upon your database schema directly.
Codger aims to offer code generation tools that take this principle a step further, allowing you to specify so-called recipes for artbitrary code generation.
Although Codger itself uses PHP and Twig, the generated code can theoretically be in any language. As an example, a recipe for Chef code is included.
Installation
$ composer require --dev codger/generate
Typically you'll install a more specific package like
codger/php
, which hascodger/generate
as a dependency.
Usage
$ vendor/bin/codger name-of-recipe some additional arguments --or --options
The name of the recipe should be resolvable to a PHP class name. The rules for this are as follows:
- slashes or semicolons are converted to backslashes (namespace separator);
- characters following a hyphen are uppercased;
- other characters are lowercased, barring the first which is uppercased.
Additionally, all recipes are prefixed with the Codger
namespace. This allows
you to easily group them in a directory outside of your regular source code,
e.g. a ./recipes
folder.
Thus, a recipe monolyth:some-test
would resolve to the namespace
Codger\\Monolyth\\SomeTest
.
Default options
All Codger recipes support 2 default options as defined in the
Codger\Generate\DefaultOptions
trait:
--output-dir=/some/path
. Supply this to actually attempt to write generated files to disk; the default is to dump to screen for manual inspection.--replace
. If this flag is set, existing files will be overwritten without warning (the default is to prompt for overwrite, dump or skip).
Whether or not shorthand flags exist depends on your recipe's other options.
Writing recipes
All recipes are regular PHP classes extending Codger\Generate\Recipe
. The main
work should be done inside the __invoke
method. Codger recipes extend the
Monolyth\Cliff\Command
class, so (string) parameters to invoke are treated as
CLI operands. Hence, a recipe called Codger\Foo
with an __invoke
signature
of (string $name)
would be called as vendor/bin/codger foo myname
.
As noted before, Composer should be able to autoload the recipes. E.g., add an
autoload-dev
property to your composer.json
for something like
"Codger\\MyNamespace\\": "./recipes"
.
Inside the __invoke
method, your recipe should do its stuff. What that is
depends on what you want to happen, of course, but generally a recipe should at
least call output()
to specify what it is generating, or call delegate
to
specify it needs to delegate tasks to a sub-recipe.
<?php namespace Codger\MyNamespace; use Codger\Generate\Recipe; class MyRecipe extends Recipe { public function __invoke() { // Do stuff... } }
Setting the Twig environment
Codger uses Twig internally to convert recipes to actual code. This means you
will need to set your Twig environment since we can't guesstimate how your
code is organised. Do this using the setTwigEnvironment
method on the recipe:
<?php // ... $this->setTwigEnvironment($twig); // ...
Failure to do so will cause Codger to exit with status code 5 on rendering. Note that a "master recipe" that only delegates stuff will not need to call this.
Converting arguments
Use the Codger\Generate\Language
class to convert arguments for various uses.
E.g., a PHP module Foo\Bar
might be written to src/Foo/Bar.php
. The
Language
helper class defines a number of methods to make this easier:
<?php use Codger\Generate\Language; echo Language::pluralize('city'); // cities echo Language::singular('cities'); // city echo Language::convert('Foo\Bar', Language::TYPE_CSS_IDENTIFIER); // foo-bar
The following TYPE_
constants are currently available:
TYPE_PHP_NAMESPACE
: Foo\BarTYPE_TABLE
: foo_barTYPE_VARIABLE
: fooBarTYPE_PATH
: Foo/BarTYPE_URL
: foo/barTYPE_CSS_IDENTIFIER
: foo-barTYPE_ANGULAR_MODULE
: foo.barTYPE_ANGULAR_COMPONENT
: fooBarTYPE_ANGULAR_TAG
: foo-bar
For backwards compatibility, the type TYPE_NAMESPACE
currently acts as an
alias for TYPE_PHP_NAMESPACE
, but it is deprecated and will raise a warning
when used. It will be removed in a future release, so it is recommended to use
TYPE_PHP_NAMESPACE
as of version 0.7.0.
Delegating tasks
Some recipes will want to make use of other recipes. This way you can "chain"
recipes together to build more complex recipes. Delegating is done by calling
the Codger\Generate\Recipe::delegate
method.
The first argument is the name of the recipe to delegate to. The optional second argument is an array of arguments to pass to the delegated recipe, as if it were called from the CLI.
User feedback
Via the Codger\Generate\InOutTrait
recipes provide the info
and error
methods which can be used to offer additional information. This is useful if
(obviously) something wrong-ish happened during recipe execution, but also for
notes about code that cannot be generated directly into a file (e.g. because
your recipe defines additional routes which can't be safely appended to an
existing routing file).
User input and conditionals
Often a recipe will want to offer various options or ask for user input that you
don't want to or cannot specify on the command line as arguments. Recipes offer
two convenience methods for this: ask
and options
.
ask
ing questions
The ask
method is meant for open-ended input, e.g. database credentials. Its
first argument is the question string, the second a callback. The callback is
called with a single argument: the answer string given. It is up to the recipe's
author to validate the answer inside the callback:
<?php $recipe->ask("What is your name?", function (string $answer) { $this->info("Hello, $answer."); });
Note that the callback is bound to the recipe, so inside it you can simply use
$this
to refer to it.
Offering options
The options
method is meant for providing the user with a simple list of
options to select from (the most simple case being 'yes/no'). Like ask
its
first argument is the question. The second argument is an array or hash of
options, and the third is the callback (which works like ask
).
Unlike ask
, the options
method validates the answer given. It should either
be a key in the passed $options
array, or a full answer present in it.
The answer passed to the callback is always the key in the array.
<?php $recipe->options("Would you like fries with that?", ['Y' => 'Yes', 'n' => 'no'], function (string $answer) { if ($answer == 'Y') { $this->info('Yummy!'); } else { $this->info('A very healthy choice.'); } });