Tempest features a unique concept called discovery. Tempest will scan your code and find out what to do with it: from controller routes to event handlers, from console commands to dependency initializers; Tempest will detect everything without you having to write a single line of configuration or bootstrap code.
final readonly class BookController { // … #[Post('/books')] public function store(CreateBookRequest $request): Response { $book = map($request)->to(Book::class)->save(); return new Redirect([self::class, 'show'], book: $book->id); } }
Tempest dares to reimagine templating in PHP with a clean and modern front-end engine, inspired by clean and modern front-end frameworks. Do you prefer something tried and tested? Tempest has support for Blade as well!
<x-base :title="$this->seo->title"> <ul> <li :foreach="$this->posts as $post"> {{ $post->title }} <span :if="$this->showDate($post)"> <x-tag> {{ $post->date }} </x-tag> </span> </li> </ul> </x-base>
Define models with simple, clean code. Complete with built-in validation, relations, migrations, and more. We don't try to reinvent database querying from scratch, so whenever you need a complex query, you can write SQL directly.
final class Book implements DatabaseModel { use IsDatabaseModel; public function __construct( #[Length(min: 1, max: 120)] public string $title, public ?Author $author = null, /** @var \App\Modules\Books\Models\Chapter[] */ public array $chapters = [], ) {} }
Tempest has already managed to become something more than an exercise, and you seem to have the experience, mentality and passion to lead its future to much greater heights.
– Reddit
Just define your command, handle input dynamically, and deliver instant feedback. Tempest doesn't bother you with complex command definitions, just write your PHP code, and Tempest will figure out the rest. Interactive console components included!
final readonly class InteractiveCommand { use HasConsole; #[ConsoleCommand('hello:world')] public function __invoke(string $name, bool $continue = false): void { $this->writeln("Hello {$name}!"); if (! $continue && ! $this->confirm('Are you sure about this?')) { return; } $framework = $this->ask( question: 'What\'s your favourite framework?', options: [ 'Tempest', 'Laravel', 'Symfony', ], ); $this->success($framework); } }
Get static pages up and running right out of the box with zero hassle. Simply define your routes, and Tempest takes care of the rest — fetching the content and rendering it dynamically.
final readonly class DocsController { #[StaticPage(DocsDataProvider::class)] #[Get('/{category}/{slug}')] public function __invoke(string $category, string $slug): View { /* … */ } }
tempest static:generate - /docs/framework/01-getting-started > /home/forge/tempest.stitcher.io/public/docs/framework/01-getting-started.html - /docs/framework/02-the-container > /home/forge/tempest.stitcher.io/public/docs/framework/02-the-container.html - /docs/framework/03-controllers > /home/forge/tempest.stitcher.io/public/docs/framework/03-controllers.html - /* … */ Done
Tempest is a work of art 👌
– Twitter
Configuration objects instead of arrays for easy autocompletion and injection, caching, a powerful dependency container at its heart, autowiring everywhere, there's so much to show. Take a look!
final readonly class MarkdownInitializer implements Initializer { public function initialize(Container $container): MarkdownConverter { $environment = new Environment(); $highlighter = (new Highlighter(new CssTheme())); $highlighter ->addLanguage(new TempestViewLanguage()) ->addLanguage(new TempestConsoleWebLanguage()) ->addLanguage(new ExtendedJsonLanguage()); $environment ->addExtension(new CommonMarkCoreExtension()) ->addExtension(new FrontMatterExtension()) ->addRenderer(FencedCode::class, new CodeBlockRenderer($highlighter)) ->addRenderer(Code::class, new InlineCodeBlockRenderer($highlighter)); return new MarkdownConverter($environment); } }