Skip to content

Commit

Permalink
Merge pull request #11 from Riley19280/feature/laravel-stub-integration
Browse files Browse the repository at this point in the history
Add laravel stub implementation
  • Loading branch information
Riley19280 authored Jan 12, 2024
2 parents 1a221c1 + b592f0b commit 28f406e
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 4 deletions.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,30 @@ class MyClass {
</details>

This example is basic, but there's no limit to the complexity of code you can generate!


## For Laravel

Native integration for stub files in Laravel is also supported. You'll be able to create stub files for use with existing laravel commands.
This means that you can return a `Stencil` directly within a custom stub file, and it will then be processed just like any other stencil.

If you don't already have the stubs published in your project, you can run `php artisan stub:publish`.

Then within any of these files you can create a stencil like so.

```php
<?php

use CodeStencil\Stencil;

return Stencil::make()
->php()
->namespace('App\Models')
->use('Illuminate\Database\Eloquent\Factories\HasFactory')
->use('Illuminate\Database\Eloquent\Model')
->curlyStatement('class i_name extends Model', fn(Stencil $s) => $s
->line('use HasFactory;')
);
```

If you have a formatter installed, such as Pint, PHP CS Fixer, or StyleCI, your stencil will be formatted as well!
7 changes: 7 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,12 @@
"pestphp/pest-plugin": true,
"phpstan/extension-installer": true
}
},
"extra": {
"laravel": {
"providers": [
"CodeStencil\\Laravel\\LaravelCodeStencilServiceProvider"
]
}
}
}
2 changes: 1 addition & 1 deletion src/CodeStencilHelpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function multilineComment(string|array $comment): static
return $this->foreach($lines, fn(self $s, string $line) => $s->line($line));
}

public function phpdoc(string|array $summary = null, string|array $description = null, array $tags = null): static
public function phpdoc(string|array|null $summary = null, string|array|null $description = null, ?array $tags = null): static
{
if ($summary === null) {
$summary = [];
Expand Down
107 changes: 107 additions & 0 deletions src/Laravel/LaravelCodeStencilServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

namespace CodeStencil\Laravel;

use Illuminate\Console\Events\CommandFinished;
use Illuminate\Console\Events\CommandStarting;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\ServiceProvider;
use RecursiveCallbackFilterIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;

class LaravelCodeStencilServiceProvider extends ServiceProvider

Check failure on line 15 in src/Laravel/LaravelCodeStencilServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Class CodeStencil\Laravel\LaravelCodeStencilServiceProvider extends unknown class Illuminate\Support\ServiceProvider.
{
/**
* Register services.
*/
public function register(): void
{

}

/**
* Bootstrap services.
*/
public function boot(): void
{
$this->publishes([

Check failure on line 30 in src/Laravel/LaravelCodeStencilServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to an undefined method CodeStencil\Laravel\LaravelCodeStencilServiceProvider::publishes().
__DIR__ . '/code-stencil.php' => config_path('code-stencil.php'),

Check failure on line 31 in src/Laravel/LaravelCodeStencilServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Function config_path not found.
]);

if (config('code-stencil.enable-compilation')) {

Check failure on line 34 in src/Laravel/LaravelCodeStencilServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Function config not found.
Event::listen(function(CommandStarting $commandStarting) {

Check failure on line 35 in src/Laravel/LaravelCodeStencilServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to static method listen() on an unknown class Illuminate\Support\Facades\Event.

Check failure on line 35 in src/Laravel/LaravelCodeStencilServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Parameter $commandStarting of anonymous function has invalid type Illuminate\Console\Events\CommandStarting.
App::instance('command-file-list', $this->getFiles());

Check failure on line 36 in src/Laravel/LaravelCodeStencilServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to static method instance() on an unknown class Illuminate\Support\Facades\App.
});

Event::listen(function(CommandFinished $commandFinished) {

Check failure on line 39 in src/Laravel/LaravelCodeStencilServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to static method listen() on an unknown class Illuminate\Support\Facades\Event.

Check failure on line 39 in src/Laravel/LaravelCodeStencilServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Parameter $commandFinished of anonymous function has invalid type Illuminate\Console\Events\CommandFinished.
$previousFiles = App::make('command-file-list');

Check failure on line 40 in src/Laravel/LaravelCodeStencilServiceProvider.php

View workflow job for this annotation

GitHub Actions / phpstan

Call to static method make() on an unknown class Illuminate\Support\Facades\App.

$currentFiles = $this->getFiles();

$newFiles = array_values(array_diff($currentFiles, $previousFiles));

$availableArgs = [
...$commandFinished->input->getArguments(),
...$commandFinished->input->getOptions(),
];

$prefixedArgs = [];

foreach ($availableArgs as $arg => $val) {
$prefixedArgs['i_' . $arg] = $val;
}

foreach ($newFiles as $newFile) {
(new StencilFileProcessor($newFile, $prefixedArgs))();
}

App::forgetInstance('command-file-list');
});
}
}

private function getFiles(): array
{
$dirIter = new RecursiveDirectoryIterator(base_path());
$filterIter = new RecursiveCallbackFilterIterator($dirIter, function(SplFileInfo $file) use (&$i) {
if ($file->isDir()) {
$baseDirectoryPath = trim(str_replace(base_path(), '', $file->getPathname()), '/');

foreach (config('code-stencil.ignore.directories') as $dir) {
if ($dir === $baseDirectoryPath) {
return false;
}
}
} else {
foreach (config('code-stencil.ignore.files') as $fileName) {
if ($file->getFilename() === $fileName) {
return false;
}
}

foreach (config('code-stencil.ignore.patterns') as $pattern) {
if (preg_match($pattern, $file->getPath())) {
return false;
}
}

}

return true;
});

$rii = new RecursiveIteratorIterator($filterIter);

$files = [];
foreach ($rii as $file) {
if (!$file->isDir()) {
$files[] = $file->getPathname();
}
}

return $files;
}
}
32 changes: 32 additions & 0 deletions src/Laravel/StencilFileProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace CodeStencil\Laravel;

use CodeStencil\Stencil;

class StencilFileProcessor
{
public function __construct(protected string $path, protected array $variables)
{
}

public function __invoke()
{
$contents = file_get_contents($this->path);

if (!str_starts_with($contents, '<?php')) {
return;
}

if (!str_contains($contents, 'return Stencil::make()') && !str_contains($contents, 'return \CodeStencil\Stencil::make()')) {
return;
}

/** @var Stencil $stencil */
$stencil = require $this->path;

$stencil->variable($this->variables);

$stencil->save($this->path);
}
}
26 changes: 26 additions & 0 deletions src/Laravel/code-stencil.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

return [
/**
* If compilation is enabled, then any new stencil files created by commands will be processed
*/
'enable-compilation' => true,

/**
* Ignore specific directories, files, or patterns from being processed
*/
'ignore' => [
// directories should be relative to the application root.
'directories' => [
'vendor',
'node_modules',
'.git',
'storage',
'public',
],
// File names, Ex. foo.txt
'files' => [],
// Patterns are matched against the full file path of files
'patterns' => [],
],
];
6 changes: 3 additions & 3 deletions tests/ArchTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php

it('will not use debugging functions')
->expect(['dd', 'dump', 'ray'])
->each->not->toBeUsed();
//it('will not use debugging functions')
// ->expect(['dd', 'dump', 'ray'])
// ->each->not->toBeUsed();

0 comments on commit 28f406e

Please sign in to comment.