Skip to content

Commit e2e1411

Browse files
authored
Merge branch 'main' into feat/decorators
2 parents 55465d0 + a572b26 commit e2e1411

File tree

399 files changed

+9787
-3328
lines changed

Some content is hidden

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

399 files changed

+9787
-3328
lines changed

.github/workflows/coding-conventions.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ jobs:
2525
2626
- name: Run Mago
2727
run: |
28-
./vendor/bin/mago fmt --dry-run
29-
./vendor/bin/mago lint --reporting-format=github
28+
composer fmt -- --check
29+
composer lint -- --reporting-format=github
3030
3131
- name: Setup Bun
3232
uses: oven-sh/setup-bun@v2

CHANGELOG.md

Lines changed: 84 additions & 126 deletions
Large diffs are not rendered by default.

UPGRADING.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 2.0
2+
3+
Read the 2.0 upgrade guide here: [https://tempestphp.com/blog/tempest-2](https://tempestphp.com/blog/tempest-2).

bin/release

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ function updateBranchProtection(bool $enabled): void
186186
);
187187

188188
if (! $response->status->isSuccessful()) {
189+
lw($response->body);
189190
throw new Exception('Failed to update branch ruleset.');
190191
}
191192
}
@@ -208,7 +209,7 @@ function triggerSubsplit(): void
208209
);
209210

210211
if (! $response->status->isSuccessful()) {
211-
throw new Exception('Failed to update branch ruleset.');
212+
throw new Exception('Failed to trigger subsplit.');
212213
}
213214
}
214215

@@ -391,7 +392,10 @@ try {
391392
);
392393

393394
// Disable protection
394-
updateBranchProtection(enabled: false);
395+
$console->task(
396+
label: 'Disabling branch protection',
397+
handler: fn () => updateBranchProtection(enabled: false),
398+
);
395399

396400
// Push tags
397401
$console->task(
@@ -415,8 +419,16 @@ try {
415419
},
416420
);
417421

418-
updateBranchProtection(enabled: true);
419-
triggerSubsplit();
422+
// Enable branch protection
423+
$console->task(
424+
label: 'Enabling branch protection',
425+
handler: fn () => updateBranchProtection(enabled: true),
426+
);
427+
428+
$console->task(
429+
label: 'Trigger subsplit',
430+
handler: fn () => triggerSubsplit(),
431+
);
420432

421433
$console->success(sprintf(
422434
'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.',

cliff.toml

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,13 @@ skip_tags = "(0\\.0\\.\\d)|1\\.0\\.0-alpha\\.1"
4949
commit_parsers = [
5050
{ field = "breaking", pattern = "true", group = "<!-- -1 -->🚨 Breaking changes" },
5151
{ message = "^feat", group = "<!-- 0 -->🚀 Features" },
52-
{ message = "^fix", group = "<!-- 1 -->🐛 Bug fixes" },
53-
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
54-
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
55-
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
52+
{ message = "^perf", group = "<!-- 1 -->⚡ Performance" },
53+
{ message = "^fix", group = "<!-- 2 -->🐛 Bug fixes" },
54+
{ body = ".*security", group = "<!-- 3 -->🛡️ Security" },
55+
{ message = "^revert", group = "<!-- 4 -->⏪ Revert" },
56+
{ message = "^docs", group = "<!-- 5 -->📚 Documentation", skip = true },
57+
{ message = "^refactor", group = "<!-- 6 -->🚜 Refactor", skip = true },
58+
{ message = "^test", group = "<!-- 7 -->🧪 Tests", skip = true },
5659
{ message = "^style", skip = true },
57-
{ message = "^test", group = "<!-- 6 -->🧪 Tests" },
58-
{ message = "^chore\\(release\\): prepare for", skip = true },
59-
{ message = "^chore\\(deps.*\\)", skip = true },
60-
{ message = "^chore\\(pr\\)", skip = true },
61-
{ message = "^chore\\(pull\\)", skip = true },
62-
{ message = "^chore: update changelog", skip = true },
6360
{ message = "^chore|^ci", skip = true },
64-
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
65-
{ message = "^revert", group = "<!-- 9 -->⏪ Revert" },
6661
]

composer.json

Lines changed: 29 additions & 8 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",
@@ -32,6 +33,7 @@
3233
"psr/http-factory": "^1.0",
3334
"psr/http-message": "^1.0|^2.0",
3435
"psr/log": "^3.0.0",
36+
"rector/rector": "^2.1",
3537
"symfony/cache": "^7.3",
3638
"symfony/mailer": "^7.2.6",
3739
"symfony/process": "^7.3",
@@ -44,9 +46,11 @@
4446
"voku/portable-ascii": "^2.0.3"
4547
},
4648
"require-dev": {
49+
"adam-paterson/oauth2-slack": "^1.1",
4750
"aws/aws-sdk-php": "^3.338.0",
4851
"azure-oss/storage-blob-flysystem": "^1.2",
49-
"carthage-software/mago": "0.26.1",
52+
"carthage-software/mago": "1.0.0-beta.24",
53+
"depotwarehouse/oauth2-twitch": "^1.3",
5054
"guzzlehttp/psr7": "^2.6.1",
5155
"league/flysystem-aws-s3-v3": "^3.25.1",
5256
"league/flysystem-ftp": "^3.25.1",
@@ -56,24 +60,34 @@
5660
"league/flysystem-read-only": "^3.25.1",
5761
"league/flysystem-sftp-v3": "^3.25.1",
5862
"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",
5968
"masterminds/html5": "^2.9",
6069
"microsoft/azure-storage-blob": "^1.5",
6170
"mikey179/vfsstream": "^2.0@dev",
6271
"nesbot/carbon": "^3.8",
6372
"nyholm/psr7": "^1.8",
73+
"patrickbussmann/oauth2-apple": "^0.3",
6474
"phpat/phpat": "^0.11.0",
6575
"phpbench/phpbench": "84.x-dev",
6676
"phpstan/phpstan": "^2.0",
6777
"phpunit/phpunit": "^12.2.3",
6878
"predis/predis": "^3.0.0",
69-
"rector/rector": "^2.0-rc2",
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",
@@ -103,6 +117,7 @@
103117
"tempest/router": "self.version",
104118
"tempest/storage": "self.version",
105119
"tempest/support": "self.version",
120+
"tempest/upgrade": "self.version",
106121
"tempest/validation": "self.version",
107122
"tempest/view": "self.version",
108123
"tempest/vite": "self.version"
@@ -143,6 +158,7 @@
143158
"Tempest\\Router\\": "packages/router/src",
144159
"Tempest\\Storage\\": "packages/storage/src",
145160
"Tempest\\Support\\": "packages/support/src",
161+
"Tempest\\Upgrade\\": "packages/upgrade/src",
146162
"Tempest\\Validation\\": "packages/validation/src",
147163
"Tempest\\View\\": "packages/view/src",
148164
"Tempest\\Vite\\": "packages/vite/src"
@@ -177,13 +193,15 @@
177193
"packages/support/src/Regex/functions.php",
178194
"packages/support/src/Str/constants.php",
179195
"packages/support/src/Str/functions.php",
196+
"packages/support/src/Uri/functions.php",
180197
"packages/support/src/functions.php",
181198
"packages/view/src/functions.php",
182199
"packages/vite/src/functions.php"
183200
]
184201
},
185202
"autoload-dev": {
186203
"psr-4": {
204+
"Tempest\\Auth\\Tests\\": "packages/auth/tests",
187205
"Tempest\\Cache\\Tests\\": "packages/cache/tests",
188206
"Tempest\\Clock\\Tests\\": "packages/clock/tests",
189207
"Tempest\\CommandBus\\Tests\\": "packages/command-bus/tests",
@@ -208,6 +226,7 @@
208226
"Tempest\\Router\\Tests\\": "packages/router/tests",
209227
"Tempest\\Storage\\Tests\\": "packages/storage/tests",
210228
"Tempest\\Support\\Tests\\": "packages/support/tests",
229+
"Tempest\\Upgrade\\Tests\\": "packages/upgrade/tests",
211230
"Tempest\\Validation\\Tests\\": "packages/validation/tests",
212231
"Tempest\\View\\Tests\\": "packages/view/tests",
213232
"Tempest\\Vite\\Tests\\": "packages/vite/tests",
@@ -225,8 +244,9 @@
225244
"scripts": {
226245
"phpunit": "@php -d memory_limit=2G vendor/bin/phpunit --display-warnings --display-skipped --display-deprecations --display-errors --display-notices",
227246
"coverage": "vendor/bin/phpunit --coverage-html build/reports/html --coverage-clover build/reports/clover.xml",
228-
"mago:fmt": "vendor/bin/mago fmt && vendor/bin/mago lint --fix --potentially-unsafe --fmt",
229-
"mago:lint": "vendor/bin/mago lint --minimum-level=note",
247+
"fmt": "vendor/bin/mago fmt",
248+
"lint:fix": "vendor/bin/mago lint --fix --format-after-fix",
249+
"lint": "vendor/bin/mago lint --potentially-unsafe --minimum-fail-level=note",
230250
"phpstan": "vendor/bin/phpstan analyse src tests --memory-limit=1G",
231251
"rector": "vendor/bin/rector process --no-ansi",
232252
"merge": "php -d\"error_reporting = E_ALL & ~E_DEPRECATED\" vendor/bin/monorepo-builder merge",
@@ -236,13 +256,14 @@
236256
"./bin/release"
237257
],
238258
"qa": [
239-
"composer mago:fmt",
259+
"composer fmt",
260+
"composer lint:fix",
261+
"composer lint",
240262
"composer merge",
263+
"composer rector",
241264
"./bin/validate-packages",
242265
"./tempest discovery:clear --no-interaction",
243-
"composer rector",
244266
"composer phpunit",
245-
"composer mago:lint",
246267
"composer phpstan"
247268
]
248269
}

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/01-routing.md

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,10 @@ public function docsRedirect(string $path): Redirect
162162

163163
## Generating URIs
164164

165-
Tempest provides a `\Tempest\uri` function that can be used to generate an URI to a controller method. This function accepts the FQCN of the controller or a callable to a method as its first argument, and named parameters as [the rest of its arguments](https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list).
165+
Tempest provides a `\Tempest\uri` function that can be used to generate a URI to a controller method. This function accepts the FQCN of the controller or a callable to a method as its first argument, and named parameters as [the rest of its arguments](https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list).
166166

167167
```php
168-
use function Tempest\uri;
168+
use function Tempest\Router\uri;
169169

170170
// Invokable classes can be referenced directly:
171171
uri(HomeController::class);
@@ -181,15 +181,62 @@ uri([AircraftController::class, 'show'], id: $aircraft->id);
181181
```
182182

183183
:::info
184-
Note that Tempest does not have named routes, and currently doesn't plan on adding them. However, if you have an argument for them, feel free to hop on our [Discord server](/discord){:ssg-ignore="true"} to discuss it.
184+
URI-related methods are also available by injecting the {b`Tempest\Router\UriGenerator`} class into your controller.
185185
:::
186186

187-
## Matching the current URI
187+
### Signed URIs
188188

189-
To determine whether the current request matches a specific controller action, Tempest provides the `\Tempest\is_current_uri` function. This function accepts the same arguments as `uri`, and returns a boolean.
189+
A signed URI may be used to ensure that the URI was not modified after it was created. This is useful for implementing login links, or other endpoints that need protection against tampering.
190+
191+
To create a signed URI, you may use the `signed_uri` function. This function accepts the same arguments as `uri`, and returns the URI with a `signature` parameter:
192+
193+
```php
194+
use function Tempest\Router\signed_uri;
195+
196+
signed_uri(
197+
action: [MailingListController::class, 'unsubscribe'],
198+
email: $email
199+
);
200+
```
201+
202+
Alternatively, you may use `temporary_signed_uri` to provide a duration after which the signed URI will expire, providing an extra layer of security.
203+
204+
```php
205+
use function Tempest\Router\temporary_signed_uri;
206+
207+
temporary_signed_uri(
208+
action: PasswordlessAuthenticationController::class,
209+
duration: Duration::minutes(10),
210+
userId: $userId
211+
);
212+
```
213+
214+
To ensure the validity of a signed URL, you should call the `hasValidSignature` method on the {`Tempest\Router\UriGenerator`} class.
215+
216+
```php
217+
final class PasswordlessAuthenticationController
218+
{
219+
public function __construct(
220+
private readonly UriGenerator $uri,
221+
) {}
222+
223+
public function __invoke(Request $request): Response
224+
{
225+
if (! $this->uri->hasValidSignature($request)) {
226+
return new Invalid();
227+
}
228+
229+
// ...
230+
}
231+
}
232+
```
233+
234+
### Matching the current URI
235+
236+
To determine whether the current request matches a specific controller action, Tempest provides the `is_current_uri` function. This function accepts the same arguments as `uri`, and returns a boolean.
190237

191238
```php
192-
use function Tempest\is_current_uri;
239+
use function Tempest\Router\is_current_uri;
193240

194241
// Current URI is: /aircraft/1
195242

@@ -243,7 +290,7 @@ Once you have created a request class, you may simply inject it into a controlle
243290
use Tempest\Router\Post;
244291
use Tempest\Http\Responses\Redirect;
245292
use function Tempest\map;
246-
use function Tempest\uri;
293+
use function Tempest\Router\uri;
247294

248295
final readonly class AirportController
249296
{

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">

0 commit comments

Comments
 (0)