Skip to content

Commit 6d9cbb4

Browse files
authored
Merge branch 'main' into fix-process-dependency
2 parents 6be4915 + b2e0c75 commit 6d9cbb4

File tree

108 files changed

+5175
-571
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+5175
-571
lines changed

CHANGELOG.md

Lines changed: 75 additions & 135 deletions
Large diffs are not rendered by default.

UPGRADING.md

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,3 @@
11
## 2.0
22

3-
Tempest comes with automated upgrades for most of our breaking changes, powered by [Rector](https://getrector.com/). If you prefer to manually upgrade your project, you can review the list of all breaking changes further down this document.
4-
5-
**If you don't have Rector installed in your project, please do so:**
6-
7-
1. `composer require rector/rector --dev` to require rector as a dev dependency
8-
2. Run `vendor/bin/rector` to create a default rector config file
9-
10-
Next, update Tempest; it's important to add the `--no-scripts` flag to prevent any errors from being thrown while updating Tempest.
11-
12-
```sh
13-
composer require tempest/framework:^2.0 --no-scripts
14-
```
15-
16-
Then you should add the `tempest2.php` set to your Rector config file:
17-
18-
```php
19-
return RectorConfig::configure()
20-
// …
21-
->withSets([__DIR__ . '/vendor/tempest/framework/packages/upgrade/src/tempest2.php']);
22-
```
23-
24-
Then run `vendor/bin/rector` to update all your project files. After that, run `tempest discovery:clear` and `tempest key:generate`. The `tempest key:generate` command must only be done once per environment.
25-
26-
Finally, carefully review and test your project, and make sure to read through the list of breaking changes below:
27-
28-
### Breaking changes
29-
30-
- **`Tempest\Database\Id` is now called `Tempest\Database\PrimaryKey`** (https://github.com/tempestphp/tempest-framework/pull/1458)
31-
- **The value property of `Tempest\Database\PrimaryKey` has been renamed from `id` to `value`** (https://github.com/tempestphp/tempest-framework/pull/1458)
32-
- **`Tempest\CommandBus\AsyncCommand` is now called `Tempest\CommandBus\Async`** (https://github.com/tempestphp/tempest-framework/pull/1507)
33-
- **You cannot longer declare view components via the `<x-component name="x-my-component">` tag. All files using this syntax must remove the wrapping `<x-component` tag and instead rename the filename to `x-my-component.view.php` (https://github.com/tempestphp/tempest-framework/pull/1439)**
34-
- **Validation rule names were updated** (https://github.com/tempestphp/tempest-framework/pull/1444)
35-
- **The `DatabaseMigration` interface was split into two** (https://github.com/tempestphp/tempest-framework/pull/1513)
36-
- **`\Tempest\uri` and `\Tempest\is_current_uri` are both moved to the `\Tempest\Router` namespace**
37-
- Cookies are now encrypted by default (https://github.com/tempestphp/tempest-framework/pull/1447) and developers must run `tempest key:generate` once per environment
38-
- Changes in view component variable scoping rules might affect view files (https://github.com/tempestphp/tempest-framework/pull/1435)
39-
- The validator now requires the translator, and should always be injected instead of manually created (https://github.com/tempestphp/tempest-framework/pull/1444)
40-
41-
The changes in **bold** are automated when you use Rector.
3+
Read the 2.0 upgrade guide here: [https://tempestphp.com/blog/tempest-2](https://tempestphp.com/blog/tempest-2).

bin/release

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ function triggerSubsplit(): void
209209
);
210210

211211
if (! $response->status->isSuccessful()) {
212-
throw new Exception('Failed to update branch ruleset.');
212+
throw new Exception('Failed to trigger subsplit.');
213213
}
214214
}
215215

@@ -425,7 +425,10 @@ try {
425425
handler: fn () => updateBranchProtection(enabled: true),
426426
);
427427

428-
triggerSubsplit();
428+
$console->task(
429+
label: 'Trigger subsplit',
430+
handler: fn () => triggerSubsplit(),
431+
);
429432

430433
$console->success(sprintf(
431434
'Released <em>%1$s</em>. The <href="https://github.com/tempestphp/tempest-framework/releases/tag/%1$s">GitHub release</href> will be created automatically in a few seconds.',

bun.lock

Lines changed: 323 additions & 207 deletions
Large diffs are not rendered by default.

composer.json

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"league/commonmark": "^2.7",
2121
"league/flysystem": "^3.29.1",
2222
"league/mime-type-detection": "^1.16",
23+
"league/oauth2-client": "^2.8",
2324
"monolog/monolog": "^3.7.0",
2425
"nette/php-generator": "^4.1.6",
2526
"nikic/php-parser": "^5.3",
@@ -45,9 +46,11 @@
4546
"voku/portable-ascii": "^2.0.3"
4647
},
4748
"require-dev": {
49+
"adam-paterson/oauth2-slack": "^1.1",
4850
"aws/aws-sdk-php": "^3.338.0",
4951
"azure-oss/storage-blob-flysystem": "^1.2",
50-
"carthage-software/mago": "^1.0.0-beta.16",
52+
"carthage-software/mago": "1.0.0-beta.24",
53+
"depotwarehouse/oauth2-twitch": "^1.3",
5154
"guzzlehttp/psr7": "^2.6.1",
5255
"league/flysystem-aws-s3-v3": "^3.25.1",
5356
"league/flysystem-ftp": "^3.25.1",
@@ -57,23 +60,34 @@
5760
"league/flysystem-read-only": "^3.25.1",
5861
"league/flysystem-sftp-v3": "^3.25.1",
5962
"league/flysystem-ziparchive": "^3.25.1",
63+
"league/oauth2-facebook": "^2.0",
64+
"league/oauth2-github": "^3.1",
65+
"league/oauth2-google": "^4.0",
66+
"league/oauth2-instagram": "^3.0",
67+
"league/oauth2-linkedin": "^5.1",
6068
"masterminds/html5": "^2.9",
6169
"microsoft/azure-storage-blob": "^1.5",
6270
"mikey179/vfsstream": "^2.0@dev",
6371
"nesbot/carbon": "^3.8",
6472
"nyholm/psr7": "^1.8",
73+
"patrickbussmann/oauth2-apple": "^0.3",
6574
"phpat/phpat": "^0.11.0",
6675
"phpbench/phpbench": "84.x-dev",
6776
"phpstan/phpstan": "^2.0",
6877
"phpunit/phpunit": "^12.2.3",
6978
"predis/predis": "^3.0.0",
79+
"riskio/oauth2-auth0": "^2.4",
80+
"smolblog/oauth2-twitter": "^1.0",
7081
"spatie/phpunit-snapshot-assertions": "^5.1.8",
7182
"spaze/phpstan-disallowed-calls": "^4.0",
83+
"stevenmaguire/oauth2-microsoft": "^2.2",
7284
"symfony/amazon-mailer": "^7.2.0",
7385
"symfony/postmark-mailer": "^7.2.6",
7486
"symplify/monorepo-builder": "^11.2",
7587
"tempest/blade": "dev-main",
76-
"twig/twig": "^3.16"
88+
"thenetworg/oauth2-azure": "^2.2",
89+
"twig/twig": "^3.16",
90+
"wohali/oauth2-discord-new": "^1.2"
7791
},
7892
"replace": {
7993
"tempest/auth": "self.version",
@@ -179,13 +193,15 @@
179193
"packages/support/src/Regex/functions.php",
180194
"packages/support/src/Str/constants.php",
181195
"packages/support/src/Str/functions.php",
196+
"packages/support/src/Uri/functions.php",
182197
"packages/support/src/functions.php",
183198
"packages/view/src/functions.php",
184199
"packages/vite/src/functions.php"
185200
]
186201
},
187202
"autoload-dev": {
188203
"psr-4": {
204+
"Tempest\\Auth\\Tests\\": "packages/auth/tests",
189205
"Tempest\\Cache\\Tests\\": "packages/cache/tests",
190206
"Tempest\\Clock\\Tests\\": "packages/clock/tests",
191207
"Tempest\\CommandBus\\Tests\\": "packages/command-bus/tests",

docs/0-getting-started/01-introduction.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,13 @@ The benefit of starting from scratch like Tempest did is having a clean slate. T
2626
Just to name a couple of examples, Tempest uses property hooks:
2727

2828
```php
29-
interface DatabaseMigration
29+
interface MigratesUp
3030
{
3131
public string $name {
3232
get;
3333
}
3434

35-
public function up(): ?QueryStatement;
36-
37-
public function down(): ?QueryStatement;
35+
public function up(): QueryStatement;
3836
}
3937
```
4038

docs/1-essentials/02-views.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ To create a view component, create a `.view.php` file that starts with `x-`. The
243243

244244
### Using view components
245245

246-
All views may include a views components. In order to do so, you may simply use a component's name as a tag, including the `x-` prefix:
246+
All views may include a view component. In order to do so, you may simply use a component's name as a tag, including the `x-` prefix:
247247

248248
```html app/home.view.php
249249
<x-base :title="$this->post->title">
@@ -253,7 +253,7 @@ All views may include a views components. In order to do so, you may simply use
253253
</x-base>
254254
```
255255

256-
The example above demonstrates how to pass data to a component using an [expression attribute](#expression-attributes), as well as how pass elements as children if that component where the `<x-slot />` tag is used.
256+
The example above demonstrates how to pass data to a component using an [expression attribute](#expression-attributes), as well as how to pass elements as children if that component where the `<x-slot />` tag is used.
257257

258258
### Attributes in components
259259

@@ -301,7 +301,7 @@ Similarly, the `id` attribute will always replace an existing `id` attribute on
301301

302302
An `$attributes` variable is accessible within view components. This variable is an array that contains all attributes passed to the component, except expression attributes.
303303

304-
Note that attributes names use `{txt}kebab-case`.
304+
Note that attribute names use `{txt}kebab-case`.
305305

306306
```html x-badge.view.php
307307
<span class="px-2 py-1 rounded-md text-sm bg-gray-100 text-gray-900">
@@ -348,7 +348,7 @@ A view component's slot can define a default value, which will be used when a vi
348348

349349
### Named slots
350350

351-
When a single slot is not enough, names can be attached to them. When using a component with named slot, you may use the `<x-slot>` tag with a `name` attribute to render content in a named outlet:
351+
When a single slot is not enough, names can be attached to them. When using a component with a named slot, you may use the `<x-slot>` tag with a `name` attribute to render content in a named outlet:
352352

353353
```html x-base.view.php
354354
<html lang="en">
@@ -544,7 +544,7 @@ Iconify has a large collection of icon sets, which you may browse using the [Ic
544544

545545
Tempest has built-in support for [Vite](https://vite.dev/), the most popular front-end development server and build tool. You may read more about [asset bundling](../2-features/05-asset-bundling.md) in the dedicated documentation.
546546

547-
This component simply inject registered entrypoints where it is called.
547+
This component simply injects registered entrypoints where it is called.
548548

549549
```html x-base.view.php
550550
<html lang="en">

docs/1-essentials/03-database.md

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -452,17 +452,16 @@ When you're persisting objects to the database, you'll need table to store its d
452452

453453
Thanks to [discovery](../4-internals/02-discovery), `.sql` files and classes implementing the {b`Tempest\Database\DatabaseMigration`} interface are automatically registered as migrations, which means they can be stored anywhere.
454454

455-
```php app/CreateBookTable.php
456-
use Tempest\Database\DatabaseMigration;
455+
```php
456+
use Tempest\Database\MigratesUp;
457457
use Tempest\Database\QueryStatement;
458458
use Tempest\Database\QueryStatements\CreateTableStatement;
459-
use Tempest\Database\QueryStatements\DropTableStatement;
460459

461-
final class CreateBookTable implements DatabaseMigration
460+
final class CreateBookTable implements MigratesUp
462461
{
463462
public string $name = '2024-08-12_create_book_table';
464463

465-
public function up(): QueryStatement|null
464+
public function up(): QueryStatement
466465
{
467466
return new CreateTableStatement('books')
468467
->primary()
@@ -471,11 +470,6 @@ final class CreateBookTable implements DatabaseMigration
471470
->datetime('published_at', nullable: true)
472471
->belongsTo('books.author_id', 'authors.id');
473472
}
474-
475-
public function down(): QueryStatement|null
476-
{
477-
return new DropTableStatement('books');
478-
}
479473
}
480474
```
481475

@@ -493,6 +487,26 @@ The file name of `{txt}.sql` migrations and the `{txt}{:hl-type:$name:}` propert
493487

494488
Note that when using migration classes combined with query statements, Tempest will take care of the SQL dialect for you, there's support for MySQL, Postgresql, and SQLite. When using raw sql files, you'll have to pick a hard-coded SQL dialect, depending on your database requirements.
495489

490+
### Up- and down migrations
491+
492+
Tempest's recommendation is to only use up-migrations, which move the database's schema forward. There is also the option to create down-migrations, migrations that can roll back the schema of the database to a previous state. Dealing with down migrations is tricky, though, especially in production environments. That's why you need to explicitly implement another interface to do so: {`Tempest\Database\MigratesDown`}.
493+
494+
```php
495+
use Tempest\Database\MigratesDown;
496+
use Tempest\Database\QueryStatement;
497+
use Tempest\Database\QueryStatements\DropTableStatement;
498+
499+
final class CreateBookTable implements MigratesDown
500+
{
501+
public string $name = '2024-08-12_drop_book_table';
502+
503+
public function down(): QueryStatement
504+
{
505+
return new DropTableStatement('books');
506+
}
507+
}
508+
```
509+
496510
### Applying migrations
497511

498512
A few [console commands](../3-console/02-building-console-commands) are provided to work with migrations. They are used to apply, rollback, or erase and re-apply them. When deploying your application to production, you should use the `php tempest migrate:up` to apply the latest migrations.
@@ -501,7 +515,7 @@ A few [console commands](../3-console/02-building-console-commands) are provided
501515
{:hl-comment:# Applies migrations that have not been run in the current environment:}
502516
./tempest migrate:up
503517

504-
{:hl-comment:# Rolls back every migration:}
518+
{:hl-comment:# Execute the down migrations:}
505519
./tempest migrate:down
506520

507521
{:hl-comment:# Drops all tables and rerun migrate:up:}

docs/1-essentials/05-container.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ final readonly class BookController
335335

336336
While constructor injection is almost always the preferred way to go, Tempest also offers the ability to inject values straight into properties, without them being requested by the constructor.
337337

338-
You may mark any property—public, protected, or private—with the `#[Inject]` attribute. Whenever a class instance is resolved via the container, its properties marked for injection will be provided the right value.
338+
You may mark any property—public, protected, or private—with the `#[Inject]` attribute. Whenever a class instance is resolved via the container, its properties marked for injection will be provided the right value. Tagged singletons may also be injected using the optional `tag` parameter of the `#[Inject]` attribute.
339339

340340
```php Tempest/Console/src/HasConsole.php
341341
use Tempest\Container\Inject;
@@ -386,6 +386,50 @@ class MyCommand
386386

387387
For these edge cases, it's nicer to make the trait self-contained without having to rely on constructor injection. That's why injected properties are supported.
388388

389+
## Decorators
390+
391+
The container supports the [decorator pattern](https://refactoring.guru/design-patterns/decorator), which allows you to wrap objects and add new behavior to them at runtime without changing their structure. This is particularly useful for adding cross-cutting concerns like logging, caching, validation, or authentication.
392+
393+
To create a decorator, you need to:
394+
395+
1. Use the {b`#[Tempest\Container\Decorates]`} attribute on your decorator class
396+
2. Implement the same interface as the class you're decorating
397+
3. Accept the decorated object as a constructor parameter
398+
399+
```php app/Cache/CacheRepository.php
400+
use Tempest\Container\Decorates;
401+
402+
#[Decorates(Repository::class)]
403+
final readonly class CacheRepository implements Repository
404+
{
405+
public function __construct(
406+
private Repository $repository,
407+
private Cache $cache,
408+
) {}
409+
410+
public function findById(int $id): ?Book
411+
{
412+
return $this->cache->resolve(
413+
key: "book.{$id}",
414+
callback: fn () => $this->repository->find($id)
415+
);
416+
}
417+
418+
public function save(Book $book): Book
419+
{
420+
$this->cache->delete("book.{$book->id}");
421+
422+
return $this->repository->save($book);
423+
}
424+
}
425+
```
426+
427+
When you request the `Repository` from the container, Tempest will automatically wrap the original implementation with your decorator. The decorated object (the original `Repository`) is injected into the decorator's constructor.
428+
429+
:::info
430+
Decorators are discovered automatically through Tempest's [discovery](../4-internals/02-discovery.md), so you don't need to manually register them.
431+
:::
432+
389433
## Proxy loading
390434

391435
The container supports lazy loading of dependencies using the `#[Proxy]` attribute. Using this attribute on a property (that has `#[Inject]`) or a constructor parameter

0 commit comments

Comments
 (0)