Skip to content

Commit 1c281b1

Browse files
Merge branch 'main' into fix/xsrf-header-from-cookie-not-working
2 parents 7534e87 + 2e5e4b1 commit 1c281b1

File tree

14 files changed

+185
-25
lines changed

14 files changed

+185
-25
lines changed

.github/workflows/create-gh-release.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,10 @@ jobs:
4040
- name: Broadcast release on Discord
4141
env:
4242
DISCORD_WEBHOOK: ${{ secrets.DISCORD_RELEASE_WEBHOOK_URL }}
43-
RELEASE_BODY: ${{ steps.generate_release_notes.outputs.changelog }}
4443
RELEASE_URL: https://github.com/${{ github.repository }}/releases/tag/${{ github.ref_name }}
4544
RELEASE_TAG: ${{ github.ref_name }}
4645
run: |
47-
processed_body=$(echo "$RELEASE_BODY" | perl -0777 -pe 's/\n*### (\S+) (.+?)\n+/\n### `\1` \2\n/g')
46+
processed_body=$(cat release_notes.txt | perl -0777 -pe 's/\n*### (\S+) (.+?)\n+/\n### `\1` \2\n/g')
4847
final_content=$(printf "%s\n%s" "-# Read the [GitHub release](<${RELEASE_URL}>)." "$processed_body")
4948
jq -n \
5049
--arg content "$final_content" \

CHANGELOG.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,31 @@
22

33
All notable changes to this project will be documented in this file.
44

5-
## [2.2.1](https://github.com/tempestphp/tempest-framework/compare/v2.2.0..2.2.1) — 2025-10-03
5+
## [2.3.1](https://github.com/tempestphp/tempest-framework/compare/v2.3.0..2.3.1) — 2025-10-07
6+
7+
### 🐛 Bug fixes
8+
9+
- **view**: support void tag rendering for XML files (#1621) ([a395534](https://github.com/tempestphp/tempest-framework/commit/a3955340171d2ae8eb4a35934418906e40026c34))
10+
11+
12+
## [2.3.0](https://github.com/tempestphp/tempest-framework/compare/v2.2.1..v2.3.0) — 2025-10-06
13+
14+
### 🚀 Features
15+
16+
- **console**: support variadic argument (#1572) ([b5f4185](https://github.com/tempestphp/tempest-framework/commit/b5f41858d6674efca092d71f3504323392a103f4))
17+
- **container**: support decorators (#1537) ([2d29bd5](https://github.com/tempestphp/tempest-framework/commit/2d29bd5452e2363b4e44b5218df9a9291f1798cf))
18+
- **http**: support database-based sessions (#1605) ([174044c](https://github.com/tempestphp/tempest-framework/commit/174044c7726d8d804465474ab2905c43cc0113df))
19+
- **view**: parse RSS feeds with tempest/view (#1617) ([7398040](https://github.com/tempestphp/tempest-framework/commit/7398040393997d886f59fb4a8ba6d351ee56f9ac))
20+
21+
### 🐛 Bug fixes
22+
23+
- **database**: handle loading circular eager relations (#1556) ([b2e0c75](https://github.com/tempestphp/tempest-framework/commit/b2e0c75c67eb46458d4c8c32f9ae289b4a7ad930))
24+
- **database**: multiple select fields in one statement (#1603) ([cd51bcf](https://github.com/tempestphp/tempest-framework/commit/cd51bcf9a8c9380d216295178517d4202b40561c))
25+
- **http**: improve failed request exception messages (#1598) ([a84ce29](https://github.com/tempestphp/tempest-framework/commit/a84ce292496175de43a3685aece3210863a1fc2d))
26+
- **http**: publish migration during database session driver installation (#1606) ([2d6fa1b](https://github.com/tempestphp/tempest-framework/commit/2d6fa1b843deaddc998f0cd59c9abbc5e03e982c))
27+
28+
29+
## [2.2.1](https://github.com/tempestphp/tempest-framework/compare/v2.2.0..v2.2.1) — 2025-10-03
630

731
### 🐛 Bug fixes
832

docs/1-essentials/01-routing.md

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -341,24 +341,22 @@ The JSON encoded header is available for when you're building APIs with Tempest.
341341
```html app/x-error.view.php
342342
<?php
343343
use Tempest\Http\Session\Session;
344+
use Tempest\Validation\Validator;
344345
use function Tempest\get;
345346

346-
/** @var Tempest\Validation\Rule[]|null $errors */
347-
$errors = get(Session::class)->get(Session::VALIDATION_ERRORS)[$name ?? null] ?? null;
348-
?>
349-
350-
<div :if="$errors !== null" :class="$class ?? ''">
351-
<div :foreach="$errors as $error">
352-
<div :if="is_array($error->message())">
353-
<div :foreach="$error->message() as $message">
354-
{{ $message }}
355-
</div>
356-
</div>
357-
<div :else>
358-
{{ $error->message() }}
359-
</div>
360-
</div>
361-
</div>
347+
/** @var Session $session */
348+
$session = get(Session::class);
349+
350+
/** @var Validator $validator */
351+
$validator = get(Validator::class);
352+
353+
$errors = $session->getErrorsFor($name ?? '');
354+
355+
?><ul :if="$errors !== []" :class="$class ?? ''">
356+
<li :foreach="$errors as $error">
357+
{{ $validator->getErrorMessage($error) }}
358+
</li>
359+
</ul>
362360
```
363361

364362
This view component will be discovered and can then be used to display validation errors likes so:

packages/core/src/Kernel.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
interface Kernel
1010
{
11-
public const string VERSION = '2.2.1';
11+
public const string VERSION = '2.3.1';
1212

1313
public string $root {
1414
get;

packages/view/src/Elements/ElementFactory.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,24 @@ public function __construct(
2525
private readonly Container $container,
2626
) {}
2727

28+
private(set) bool $isHtml = false;
29+
2830
public function setViewCompiler(TempestViewCompiler $compiler): self
2931
{
3032
$this->compiler = $compiler;
3133

3234
return $this;
3335
}
3436

37+
public function withIsHtml(bool $isHtml): self
38+
{
39+
$clone = $this->clone();
40+
41+
$clone->isHtml = $isHtml;
42+
43+
return $clone;
44+
}
45+
3546
public function make(Token $token): ?Element
3647
{
3748
return $this->makeElement(
@@ -103,6 +114,7 @@ private function makeElement(Token $token, ?Element $parent): ?Element
103114
$element = new GenericElement(
104115
token: $token,
105116
tag: $token->tag,
117+
isHtml: $this->isHtml,
106118
attributes: $attributes,
107119
);
108120
}

packages/view/src/Elements/GenericElement.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ final class GenericElement implements Element, WithToken
1717
public function __construct(
1818
public readonly Token $token,
1919
private readonly string $tag,
20+
private readonly bool $isHtml,
2021
array $attributes,
2122
) {
2223
$this->attributes = $attributes;
@@ -55,7 +56,11 @@ public function compile(): string
5556

5657
// Void elements
5758
if (is_void_tag($this->tag)) {
58-
return "<{$this->tag}{$attributes}>";
59+
if ($this->isHtml) {
60+
return "<{$this->tag}{$attributes}>";
61+
} else {
62+
return "<{$this->tag}{$attributes} />";
63+
}
5964
}
6065

6166
return "<{$this->tag}{$attributes}>{$content}</{$this->tag}>";

packages/view/src/Parser/TempestViewAst.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,14 @@ public function __construct(
1212
private(set) TokenCollection $tokens = new TokenCollection(),
1313
) {}
1414

15+
private(set) bool $isHtml = true;
16+
1517
public function add(Token $token): self
1618
{
19+
if ($token->type === TokenType::XML) {
20+
$this->isHtml = false;
21+
}
22+
1723
$this->tokens->add($token);
1824

1925
return $this;

packages/view/src/Parser/TempestViewCompiler.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,10 @@ private function mapToElements(TempestViewAst $ast): array
121121
{
122122
$elements = [];
123123

124+
$elementFactory = $this->elementFactory->withIsHtml($ast->isHtml);
125+
124126
foreach ($ast as $token) {
125-
$element = $this->elementFactory->make($token);
127+
$element = $elementFactory->make($token);
126128

127129
if ($element === null) {
128130
continue;

packages/view/src/Parser/TempestViewLexer.php

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@ public function lex(): TokenCollection
2323
$tokens = [];
2424

2525
while ($this->current) {
26-
if ($this->comesNext('<?')) {
26+
if ($this->comesNext('<?xml')) {
27+
$tokens[] = $this->lexXml();
28+
} elseif ($this->comesNext('<?')) {
2729
$tokens[] = $this->lexPhp();
2830
} elseif ($this->comesNext('<!--')) {
2931
$tokens[] = $this->lexComment();
3032
} elseif ($this->comesNext('<!doctype') || $this->comesNext('<!DOCTYPE')) {
3133
$tokens[] = $this->lexDocType();
34+
} elseif ($this->comesNext('<![CDATA')) {
35+
$tokens = [...$tokens, ...$this->lexCharacterData()];
3236
} elseif ($this->comesNext('<')) {
3337
$tokens = [...$tokens, ...$this->lexTag()];
3438
} else {
@@ -153,6 +157,19 @@ private function lexTag(): array
153157
return $tokens;
154158
}
155159

160+
private function lexXml(): Token
161+
{
162+
$buffer = '';
163+
164+
while ($this->seek(2) !== '?>' && $this->current !== null) {
165+
$buffer .= $this->consume();
166+
}
167+
168+
$buffer .= $this->consume(2);
169+
170+
return new Token($buffer, TokenType::XML);
171+
}
172+
156173
private function lexPhp(): Token
157174
{
158175
$buffer = '';
@@ -192,4 +209,22 @@ private function lexDoctype(): Token
192209

193210
return new Token($buffer, TokenType::DOCTYPE);
194211
}
212+
213+
private function lexCharacterData(): array
214+
{
215+
$tokens = [
216+
new Token($this->consumeIncluding('<![CDATA['), TokenType::CHARACTER_DATA_OPEN),
217+
];
218+
219+
$buffer = '';
220+
221+
while ($this->seek(3) !== ']]>' && $this->current !== null) {
222+
$buffer .= $this->consume();
223+
}
224+
225+
$tokens[] = new Token($buffer, TokenType::CONTENT);
226+
$tokens[] = new Token($this->consume(3), TokenType::CHARACTER_DATA_CLOSE);
227+
228+
return $tokens;
229+
}
195230
}

packages/view/src/Parser/TokenType.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ enum TokenType
1313
case CLOSING_TAG;
1414
case COMMENT;
1515
case PHP;
16+
case XML;
1617
case CONTENT;
1718
case DOCTYPE;
19+
case CHARACTER_DATA_OPEN;
20+
case CHARACTER_DATA_CLOSE;
1821
}

0 commit comments

Comments
 (0)