Skip to content

Commit d18ebd0

Browse files
committed
UI\Form: adapted to the SubmissionSource model of nette/forms 4.0
The form no longer overrides receiveHttpData(), isAnchored() and beforeRender(); instead it overrides createDefaultSource() to return null and sets the @internal FormSubmissionSource when anchored to a presenter. The source detects submission via the 'submit' signal, reads the application request and installs the hidden signal field in its prepare() render hook; it resolves the presenter from the form lazily, so a form re-anchored to another presenter keeps working. The catch-up pass of setSubmissionSource() replaces the manual loadHttpData() loop in validateParent(). The form owns its cross-origin state (private ?FetchSite $allowedOrigin + allowCrossOrigin()) independently of nette/forms; the same-origin check intentionally stays in signalReceived(), where a failure is reported via Presenter::detectedCsrf(). Requires nette/forms 4.0.
1 parent cac4a88 commit d18ebd0

4 files changed

Lines changed: 56 additions & 60 deletions

File tree

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
"nette/tester": "^2.6",
3030
"nette/caching": "^3.2",
3131
"nette/di": "^3.2",
32-
"nette/forms": "^3.2 || ^4.0",
32+
"nette/forms": "^4.0",
3333
"nette/robot-loader": "^4.0",
3434
"nette/security": "^3.2",
3535
"latte/latte": "^3.1.4",
@@ -43,7 +43,7 @@
4343
"conflict": {
4444
"nette/caching": "<3.2",
4545
"nette/di": "<3.2",
46-
"nette/forms": "<3.2",
46+
"nette/forms": "<4.0",
4747
"nette/schema": "<1.3",
4848
"latte/latte": "<3.1.4 || >=3.2",
4949
"tracy/tracy": "<2.12"

phpstan.neon

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,6 @@ parameters:
9595
count: 1
9696
path: src/Application/UI/Presenter.php
9797

98-
# Nette Forms compatibility - methods differ between versions
99-
-
100-
identifier: method.notFound
101-
path: src/Application/UI/Form.php
102-
10398
# Control::$template->flashes is set dynamically for flash messages
10499
-
105100
identifier: property.notFound

src/Application/UI/Form.php

Lines changed: 3 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ class Form extends Nette\Forms\Form implements SignalReceiver
1717
{
1818
/** @var array<callable(static): void> Occurs when form is attached to presenter */
1919
public array $onAnchor = [];
20-
21-
/** which Sec-Fetch-Site values are accepted for submission; null disables the check */
2220
private ?Nette\Http\FetchSite $allowedOrigin = Nette\Http\FetchSite::SameOrigin;
2321

2422

@@ -30,9 +28,7 @@ public function __construct(?Nette\ComponentModel\IContainer $parent = null, ?st
3028

3129

3230
/**
33-
* Forward compatibility with nette/forms 4.0: no default submission source materializes,
34-
* the form is anchored via the presenter and detects submission in receiveHttpData().
35-
* Under nette/forms 3.x the method is unused.
31+
* The form is anchored via a presenter; the source is set when the form is attached to it.
3632
*/
3733
protected function createDefaultSource(): ?Nette\Forms\SubmissionSource
3834
{
@@ -53,13 +49,8 @@ protected function validateParent(Nette\ComponentModel\IContainer $parent): void
5349
$this->setAction(new Link($presenter, 'this'));
5450
}
5551

56-
$controls = $this->getControls();
57-
if (iterator_count($controls) && $this->isSubmitted()) {
58-
foreach ($controls as $control) {
59-
if (!$control->isDisabled()) {
60-
$control->loadHttpData();
61-
}
62-
}
52+
if (!$this->isAnchored()) { // the write-once source survives re-anchoring, it resolves the current presenter itself
53+
$this->setSubmissionSource(new FormSubmissionSource); // lets already attached controls load their values
6354
}
6455

6556
Nette\Utils\Arrays::invoke($this->onAnchor, $this);
@@ -94,15 +85,6 @@ public function hasPresenter(): bool
9485
}
9586

9687

97-
/**
98-
* Tells if the form is anchored.
99-
*/
100-
public function isAnchored(): bool
101-
{
102-
return (bool) $this->getPresenter(throw: false);
103-
}
104-
105-
10688
/**
10789
* Disables the same-origin (Sec-Fetch) CSRF check, allowing cross-origin form submissions.
10890
*/
@@ -119,38 +101,6 @@ public function disableSameSiteProtection(): void
119101
}
120102

121103

122-
/**
123-
* Internal: returns submitted HTTP data or null when form was not submitted.
124-
*/
125-
protected function receiveHttpData(): ?array
126-
{
127-
$presenter = $this->getPresenter();
128-
if (!$presenter->isSignalReceiver($this, 'submit')) {
129-
return null;
130-
}
131-
132-
$request = $presenter->getRequest();
133-
if ($request->isMethod('forward') || $request->isMethod('post') !== $this->isMethod('post')) {
134-
return null;
135-
}
136-
137-
return $this->isMethod('post')
138-
? Nette\Utils\Arrays::mergeTree($request->getPost(), $request->getFiles())
139-
: $request->getParameters();
140-
}
141-
142-
143-
protected function beforeRender(): void
144-
{
145-
parent::beforeRender();
146-
$key = ($this->isMethod('post') ? '_' : '') . Presenter::SignalKey;
147-
if (!isset($this[$key]) && $this->getAction() !== '') {
148-
$do = $this->lookupPath(Presenter::class) . self::NameSeparator . 'submit';
149-
$this[$key] = (new Nette\Forms\Controls\HiddenField($do))->setOmitted();
150-
}
151-
}
152-
153-
154104
/********************* interface SignalReceiver ****************d*g**/
155105

156106

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php declare(strict_types=1);
2+
3+
/**
4+
* This file is part of the Nette Framework (https://nette.org)
5+
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6+
*/
7+
8+
namespace Nette\Application\UI;
9+
10+
use Nette;
11+
12+
13+
/**
14+
* Source of submitted data for forms anchored to a presenter: detects submission via the 'submit' signal
15+
* and reads the application request of the presenter the form is currently attached to. The same-origin
16+
* CSRF check intentionally stays in Form::signalReceived(), where a failure is reported to the presenter.
17+
* Experimental.
18+
* @internal
19+
*/
20+
final class FormSubmissionSource implements Nette\Forms\SubmissionSource
21+
{
22+
public function receiveData(Nette\Forms\Form $form): ?array
23+
{
24+
$presenter = $form->lookup(Presenter::class);
25+
if (!$presenter->isSignalReceiver($form, 'submit')) {
26+
return null;
27+
}
28+
29+
$request = $presenter->getRequest();
30+
if ($request->isMethod('forward') || $request->isMethod('post') !== $form->isMethod('post')) {
31+
return null;
32+
}
33+
34+
return $form->isMethod('post')
35+
? Nette\Utils\Arrays::mergeTree($request->getPost(), $request->getFiles())
36+
: $request->getParameters();
37+
}
38+
39+
40+
/**
41+
* Installs the hidden field carrying the 'submit' signal.
42+
*/
43+
public function prepare(Nette\Forms\Form $form): void
44+
{
45+
$key = ($form->isMethod('post') ? '_' : '') . Presenter::SignalKey;
46+
if (!isset($form[$key]) && $form->getAction() !== '') {
47+
$do = $form->lookupPath(Presenter::class) . Nette\ComponentModel\IComponent::NameSeparator . 'submit';
48+
$form[$key] = (new Nette\Forms\Controls\HiddenField($do))->setOmitted();
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)