- Programatically modify
PHPFile
s andLaravelFile
s with an intuiutive top level read/write API - Read/write on classes, framework- and language constructs using
FileQueryBuilders
andAbstractSyntaxTreeQueryBuilders
- Extract Application entity schemas
- Add
Snippet
s with an inline PHP Template engine
composer require ajthinking/archetype
Requires UNIX filesystem, PHP >= 7 and Laravel >= 7
use PHPFile;
// Create new files
PHPFile::make()->class('acme/Product.php')
->use('Shippable')
->add()->trait('Acme\Traits\Shippable')
->public()->property('stock', -1)
->save();
// Modify existing files
PHPFile::load('app/Models/User.php')
->className('NewClassName')
->save();
use LaravelFile; // extends PHPFile
// Expanding on our User model
LaravelFile::user()
->add()->use(['App\Traits\Dumpable', 'App\Contracts\PlayerInterface'])
->add()->implements('PlayerInterface')
->add()->trait('Dumpable')
->table('gdpr_users')
->add()->fillable('nickname')
->remove()->hidden()
->empty()->casts()
->hasMany('App\Game')
->belongsTo('App\Guild')
->save();
Result:
Filter and retrieve a set of files to interact with.
// find files with the query builder
PHPFile::in('database/migrations')
->where('extends', 'Migration')
->andWhere('className', 'like', 'Create')
->get() // returns Collection of PHPFiles
// Quickly find the Laravel User file
$file = LaravelFile::user();
// Quickly find Laravel specific files
LaravelFile::models()->get();
LaravelFile::controllers()->get();
LaravelFile::serviceProviders()->get();
// ...
As seen in the previous examples we can query and manipulate nodes with simple or primitive values, such as strings and arrays. However, if we want to perform custom or more in dept queries we must use the ASTQueryBuilder
.
Example: how can we fetch explicit column names in a migration file?
LaravelFile::load('database/migrations/2014_10_12_000000_create_users_table.php')
->astQuery() // get a ASTQueryBuilder
->method()
->where('name->name', 'up')
->staticCall()
->where('class', 'Schema')
->where('name->name', 'create')
->args
->closure()
->stmts
->methodCall()
->where('var->name', 'table')
->args
->value
->value
->get(); // exit ASTQueryBuilder, get a Collection
The ASTQueryBuilder examines all possible paths and automatically terminates those that cant complete the query:
The ASTQueryBuilder relies entirely on nikic/php-parser. Available query methods mirror the PhpParser
types and properties. To understand this syntax better you may want to tinker with dd($file->ast())
while building your queries. Basic conventions are listed below.
- Traverse into nodes by using methods (
method()
,staticCall()
...) - Traverse into node properties by accessing properties (
args
,stmts
...) - Filter results with
where(...)
- Resolving matching paths with
get()
ASTQueryBuilder
also supports removing, replacing and injecting nodes 🔧
// Replace a node property
$file->astQuery()
->class()
->name
->replaceProperty('name', $newClassName)
->commit() // updates the file's AST
->end() // exit query
->save()
Use the LaravelSchema
class to get an app schema.
use Archetype\Schema\LaravelSchema;
LaravelSchema::get();
{
"entities": [
{
"model": "App\\User",
"table": "users",
"columns": {
"id": {
"name": "id",
"type": {},
"default": null,
"notnull": true,
"length": null,
"precision": 10,
"scale": 0,
"fixed": false,
"unsigned": false,
"autoincrement": true,
"columnDefinition": null,
"comment": null
},
"name": {
"name": "name",
"type": {},
"default": null,
"notnull": true,
"length": null,
"precision": 10,
"scale": 0,
"fixed": false,
"unsigned": false,
"autoincrement": false,
"columnDefinition": null,
"comment": null,
"collation": "BINARY"
}
}
}
],
"strategy_used": "Archetype\\Schema\\Strategies\\FromDatabase",
"log": []
}
Schema feature is under construction ⚠
Let's make a snippet for a method we want to insert. Start by creating a file storage/archetype/snippets/my-stuff.php
like shown below. In the file, we put our template code including any encapsuling constructs (in our case we will have to put a class since methods only exists inside classes). Name anything you want to be configurable with a handle for instance '___TARGET_CLASS___'
. Even your snippet name itself may be a handle as long as it is unique.
<?php
/**
* Optionally use FAKE names to silence IDE warnings
*/
use Archetype\Support\FakeName;
use Archetype\Support\FakeName as ANY;
use Archetype\Support\FakeName as ___TARGET_CLASS___;
/**
* This is just a placeholder class where we can add our snippets
*/
class _ extends FakeName
{
/**
* ___DOC_BLOCK___
*/
public function mySpecialMethod($arg)
{
$want = abs($arg);
return $this->doSomethingWith(___TARGET_CLASS___::class, 'my template')
->use(ANY::thing(new static('you' . $want)));
}
}
Your snippet is then instantly available anywhere in your code:
use Archetype\Support\Snippet;
// Get the snippet
Snippet::mySpecialMethod()
// Pass an array of replacement pairs to replace any handles:
Snippet::mySpecialMethod([
'___DOC_BLOCK___' => 'Inserted with archetype :)',
'___TARGET_CLASS___' => 'App\Rocket'
]);
// Integrated example
PHPFile::load('app/Models/User.php')
->addMethod(
Snippet::mySpecialMethod([
// replacement pairs ...
])
)->save();
ℹ️ The
Snippet
class currently only supports templates on class methods.
You may use either of the following
// Using class
(new \Archetype\PHPFile)->load('...');
// Using facade
PHPFile::load('...');
// Using facade explicitly
use Archetype\Facades\PHPFile;
PHPFile::load('...'); // Using facade explicitly
If a file can't be parsed, a FileParseError
will be thrown. This can happen if you try to explicitly load the file but also when performing queries matching problematic files.
To see all offending files run php artisan archetype:errors
. To ignore files with problems, put them in config/archetype.php
-> ignored_paths
.
In general this package assumes code to be parsed follows guidellines and conventions from PSR and Laravel. Some examples are listed below.
-
Can't use group use syntax (
use Something\{X, Y};
) -
Assumes one class per file
-
Assumes no multiple/grouped property declarations (
protected $a, $b = 1;
)
php artisan vendor:publish --provider="Archetype\ServiceProvider"
git clone git@github.com:ajthinking/archetype.git
cd archetype
composer install
./vendor/bin/phpunit tests
PRs and issues are welcome. Have a look at the Trello board for planned features.
MIT
- Built with nikic/php-parser
- PSR Printing fixes borrowed from tcopestake/PHP-Parser-PSR-2-pretty-printer
- Schema extractor based on mpociot/laravel-test-factory-helper