Framework

Repository Author Commit message Committed SHA  
 
assets by dg DIExtension: mapper can be referenced as @service in mapping

A bare service reference like `cloud: @s3mapper` in the assets mapping now
resolves to that service used directly as the mapper (previously it was
mistaken for a path and failed). Entity notation `@s3mapper()` and class
names keep working. Matches the documented 'Service reference' format.

latte by dg added |json attribute modifier

|json is an attribute-only modifier: as the last modifier of a dynamic HTML
attribute it JSON-encodes the value via HtmlHelpers::formatJsonAttribute() with
smart-quoting, overriding the default class/aria/data/bool handling. Anywhere
else (outside an attribute, or not as the last modifier) |json is an unknown
filter.

formatJsonAttribute() is extracted from formatDataAttribute() and shared.

latte by dg ExpressionAttributeNode: no longer mutates the modifier
latte by dg Linter: added static checking of template paths

New TemplateReferenceCheck validates that statically named templates
referenced by {include}, {import}, {extends}/{layout}, {embed file},
{sandbox} and {include block from} exist, via the engine's loader. A loader
that cannot resolve references (a bare StringLoader) is silently skipped.
Registered as a built-in check.

latte by dg Linter: rewritten as first-class checks driven by the Linter

A lint check is now a Latte\Linting\Check that inspects the parsed template
and returns Issue objects, run and owned by the Linter instead of riding the
compiler pass pipeline via trigger_error. The engine only compiles; the
Linter parses each file, runs the checks (each isolated in try/catch so a
faulty check cannot abort the file or mask the others) and reports the
collected issues. The symbol validation becomes SymbolCheck; the deprecated
LinterExtension is now an inert no-op. Third parties add checks via addCheck().

assets by dg AssetNode: render full HTML element only in HTML text context

{asset} and {preload} used to render the complete HTML element in every
escaper state except html/attr, so inside <style> or <script> contents
(html/raw) and in css/js/text templates they injected a whole <link>/<script>
element into CSS or JS code, breaking it:

<style>
.hero { background: url('{asset 'bg.jpg'}'); }
/* url('<link rel="preload" href=...>') */
</style>

Now the element is rendered only in the html text state and everywhere
else just the URL is printed, matching the existing behavior inside
HTML attributes.

latte by dg Linter: moved into Latte\Linting namespace

Latte\Tools\Linter and Latte\Tools\LinterExtension are relocated to the
Latte\Linting namespace under src/Latte/Linting. The original class names
are kept as deprecated subclasses in a single src/compatibility.php for
backward compatibility, so existing scripts keep working. No behavior change.

latte by dg spaceless: rewritten as streaming WhitespaceMinifier

The new minifier understands HTML structure and works in three modes
driven by the content type:

- HTML: whitespace around whitespace-insensitive elements (div, p, li,
br, ...) is removed entirely on both sides, other runs collapse to
a single space; whitespace between attributes collapses while quoted
attribute values stay verbatim; content of pre, textarea, script and
style passes through untouched; comments, doctype, CDATA and PI pass
through verbatim
- XML: all tags are treated as whitespace-insensitive
- text (and js/css/ical): spaces and tabs collapse to a single space,
newlines are preserved including their count; */attr subcontexts
collapse everything to a single space

It runs as an ob_start() handler processing 4kB chunks: only an
undecidable tail (split tag, comment or closing raw-text tag) is held
back, pending whitespace is tracked as a flag, so output is streamed
and byte-identical for any chunking.

{spaceless} now uses a process-wide singleton with a nesting counter;
nested blocks are ignored (the outer handler minifies everything),
which also fixes the state collision of the former static $strip.
Using {spaceless} inside raw-text elements or attributes is a compile
error; <script n:spaceless> keeps its content verbatim.

BC breaks:
- Filters::strip(), spacelessHtml(), spacelessHtmlHandler() and
spacelessText() have been removed; the |spaceless filter is handled
by Filters::spaceless(), |strip remains an obsolete alias
- quoted attribute values are no longer collapsed
- word separation around inline tags is preserved (`</i> bb` is no
longer glued)
- text mode preserves newlines and normalizes \r\n to \n
- leading/trailing whitespace of the region is trimmed
- compiled template cache must be purged after upgrade

latte by dg added TagParser::consumeCommaBeforeArguments()
latte by daun implicit default block for {embed} (#419)
latte by dg cs
forms by dg Form: submission detection redesigned around SubmissionSource objects

A form no longer inherits its way of receiving submitted data, it is
given a source:

- interface SubmissionSource { receiveData(Form): ?array; prepare(Form) }
- Sources\HttpSource - reads the form's own HTTP request: method match,
Sec-Fetch origin policy (?FetchSite $allowedOrigin, null disables the
check), POST+FILES/GET merge and the '_form_' tracker of a named form,
which it installs itself in the prepare() render hook and validates
against the form name at receive time
- Sources\NullSource - never submitted; used by Repeater item templates
and Blueprint dummy forms instead of the former anonymous-class trick,
so introspective code gets false from isSubmitted() instead of hacks

Form gains setSubmissionSource() and a protected createDefaultSource()
seam: the default HttpSource materializes lazily at the first submission
query or render, so a bare `new Form` keeps its behavior. Descendants
anchored elsewhere (Nette\Application\UI\Form) return null from
createDefaultSource() and set their source later; the write-once setter
runs a catch-up loadHttpData() pass so that controls attached before the
source load their values. The presenter signal field of UI\Form is the
same kind of marker as the tracker and moves to the prepare() hook of
its source on the application side.

Behavior notes: the tracker is installed at render time, so it renders
after user fields and $form[TrackerId] exists only after
fireRenderEvents(); Form::$httpRequest was replaced by injecting
`new HttpSource($request)`; the cross-origin flag lives in the source,
allowCrossOrigin() delegates to it.

forms by dg Form: initialize() removed, the HTTP request resolves lazily per instance (BC break)

The static method was a leftover of the SameSite-cookie era, when it had
to run before headers were sent to plant the _nss cookie; after the
switch to Sec-Fetch protection its only real callers were tests
resetting the static default request between test blocks.

The request is now created from globals lazily per form instance, which
also removes the shared static state, so tests need no resets at all.
An explicit request is injected via the $httpRequest property.

forms by dg added Repeater example
forms by dg netteForms: added repeater support
forms by dg Latte: added {repeater}, n:repeater-item, n:repeater-add & n:repeater-remove tags
forms by dg added Repeater: container with dynamically repeating items
forms by dg deprecated Form::getHttpData(), BaseControl::getHttpData() and Helpers::extractHttpData()
forms by dg Form, Container: submitted data are accessed layer by layer via getSubmittedData() & getSubmittedValue() (BC break)
forms by dg fixed PHPStan errors: setValue() accepts mixed, simplified Container::addComponent()
forms by dg netteForms: restructured package, includes UMD and ESM (BC break)
forms by dg netteForms: removed distribution files (BC break)
forms by dg Latte: {formContext}, {formContainer}, {formPrint} & {formClassPrint) are deprecated
forms by dg BaseControl::$disabled is bool, added $disabledChoices
forms by dg Container::getControls() returns list instead of iterator with names (BC break)
forms by dg Container: only Control/Container can be added to form (BC break)
forms by dg added type hints (BC break)
forms by dg added #[\Deprecated] attributes and trigger_error() to deprecated members
forms by dg InputNode, LabelNode: deprecate missing comma before arguments

Extends the {form} deprecation to {input} and {label}, which share the
same "name[:part] followed by bare arguments without a comma" syntax.
Triggers E_USER_DEPRECATED when a token follows the name (and optional
:part) without a separating comma, pointing to the offending column;
the tag still compiles. A genuinely malformed {input foo: class=>val}
emits the deprecation alongside the existing CompileException, which is
correct: the comma really is missing (canonical: {input foo:, class=>val}).

All three Forms tags route through Latte's shared
TagParser::consumeCommaBeforeArguments() helper, so {form} is converted
from its inline check too -- no duplicated logic.

forms by dg removed deprecated CsrfProtection control and related code

Deleted CsrfProtection class, its tests, ProtectorId constant, and validation message.
The addProtection() method now only triggers a deprecation warning.

forms by dg removed compatibility for old class names
forms by dg Latte: removed BC alias for the removed formsStack provider

Reverts the formsStack compatibility alias. Runtime::begin() no longer
takes the template $global nor binds $global->formsStack, and the {form}/
<form n:name> tags stop passing global: to it. Intended for the next
major version, where the legacy formsStack provider support is dropped
for good.

Last synchronization: 2026-07-07 18:03:09