Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1618f1a
output buffering handlers return string
dg Aug 1, 2025
8cb3c90
opened 4.0-dev
dg Dec 14, 2023
025a723
removed support for Latte 2
dg May 26, 2024
5d8060b
removed compatibility for old class names
dg Oct 15, 2021
27de4d4
added type hints (BC break)
dg Feb 8, 2024
926ae02
RouteList: array access is deprecated
dg Oct 15, 2021
6d8a9df
Presenter::handleInvalidLink() -> processInvalidLink() (BC break)
dg May 12, 2024
028d21e
Revert "UI\PresenterComponent: removed references created by loadStat…
dg Oct 24, 2022
a0caaab
Component: method checkRequirements() is called for createComponent<N…
dg Apr 20, 2024
5423ce1
Component: only UI components can be added to presenter/component (BC…
dg Apr 7, 2024
40cb029
deprecated magic properties except for $template & $payload (BC break)
dg Sep 24, 2021
7a66238
Component::link() & etc uses variadic parameter
dg Oct 19, 2021
833a92b
Component::getParameter() $default is deprecated
dg Jun 21, 2022
16307f4
LinkGenerator: query part in link is deprecated
dg Apr 19, 2024
049b0af
Presenter: removed constructor (BC break!)
dg Dec 11, 2023
94cff0f
@annotations are deprecated (BC break)
dg Apr 19, 2024
5f0685a
PresenterFactoryCallback: refreshes page instead of creating presente…
dg Apr 28, 2024
752fe3d
LatteFactory: $control is passed to create() (BC break)
dg Jun 11, 2024
914105f
PresenterFactory: default mapping is App\Presentation\*\**Presenter (…
dg Apr 14, 2024
1a9c65d
🔧 fix: ensure link is created with correct parameters
alfred-cerny Sep 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"nette/forms": "^3.2",
"nette/robot-loader": "^4.0",
"nette/security": "^3.2",
"latte/latte": "^2.10.2 || ^3.0.18",
"latte/latte": "^3.0.18",
"tracy/tracy": "^2.9",
"mockery/mockery": "^1.6@stable",
"phpstan/phpstan-nette": "^2.0@stable",
Expand All @@ -42,7 +42,7 @@
"nette/di": "<3.2",
"nette/forms": "<3.2",
"nette/schema": "<1.3",
"latte/latte": "<2.7.1 || >=3.0.0 <3.0.18 || >=3.2",
"latte/latte": "<3.0.18 || >=3.2",
"tracy/tracy": "<2.9"
},
"autoload": {
Expand All @@ -58,7 +58,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
"dev-master": "4.0-dev"
}
}
}
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Nette Application MVC
=====================

[![Downloads this Month](https://img.shields.io/packagist/dm/nette/application.svg)](https://packagist.org/packages/nette/application)
[![Tests](https://github.com/nette/application/actions/workflows/tests.yml/badge.svg?branch=v3.2)](https://github.com/nette/application/actions)
[![Tests](https://github.com/nette/application/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/nette/application/actions)
[![Latest Stable Version](https://poser.pugx.org/nette/application/v/stable)](https://github.com/nette/application/releases)
[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/application/blob/master/license.md)

Expand Down
1 change: 1 addition & 0 deletions src/Application/LinkGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ public static function parseDestination(string $destination): array
}

if (!empty($matches['query'])) {
trigger_error("Link format is obsolete, use arguments instead of query string in '$destination'.", E_USER_DEPRECATED);
parse_str(substr($matches['query'], 1), $args);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Application/PresenterFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class PresenterFactory implements IPresenterFactory
{
/** @var array[] of module => splited mask */
private array $mapping = [
'*' => ['', '*Module\\', '*Presenter'],
'*' => ['App\Presentation\\', '*\\', '**Presenter'],
'Nette' => ['NetteModule\\', '*\\', '*Presenter'],
];

Expand Down
10 changes: 5 additions & 5 deletions src/Application/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
/**
* Presenter request.
*
* @property string $presenterName
* @property array $parameters
* @property array $post
* @property array $files
* @property string|null $method
* @property-deprecated string $presenterName
* @property-deprecated array $parameters
* @property-deprecated array $post
* @property-deprecated array $files
* @property-deprecated string|null $method
*/
final class Request
{
Expand Down
5 changes: 1 addition & 4 deletions src/Application/Routers/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
namespace Nette\Application\Routers;

use Nette;
use function interface_exists, is_string, lcfirst, preg_replace, rawurlencode, str_replace, strlen, strncmp, strrpos, strtolower, strtr, substr, ucwords;
use function is_string, lcfirst, preg_replace, rawurlencode, str_replace, strlen, strncmp, strrpos, strtolower, strtr, substr, ucwords;


/**
Expand Down Expand Up @@ -188,6 +188,3 @@ public static function path2presenter(string $s): string
return $s;
}
}


interface_exists(Nette\Application\IRouter::class);
15 changes: 11 additions & 4 deletions src/Application/Routers/RouteList.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

use JetBrains\PhpStorm\Language;
use Nette;
use function count, interface_exists, is_int, is_string, strlen, strncmp, substr;
use function count, is_int, is_string, strlen, strncmp, substr;


/**
Expand Down Expand Up @@ -96,6 +96,13 @@ public function getModule(): ?string
*/
public function offsetSet($index, $router): void
{
if ($router instanceof Route) {
trigger_error('Usage `$router[] = new Route(...)` is deprecated, use `$router->addRoute(...)`.', E_USER_DEPRECATED);
} else {
$class = getclass($router);
trigger_error("Usage `\$router[] = new $class` is deprecated, use `\$router->add(new $class)`.", E_USER_DEPRECATED);
}

if ($index === null) {
$this->add($router);
} else {
Expand All @@ -110,6 +117,7 @@ public function offsetSet($index, $router): void
*/
public function offsetGet($index): Nette\Routing\Router
{
trigger_error('Usage `$route = $router[...]` is deprecated, use `$router->getRouters()`.', E_USER_DEPRECATED);
if (!$this->offsetExists($index)) {
throw new Nette\OutOfRangeException('Offset invalid or out of range');
}
Expand All @@ -123,6 +131,7 @@ public function offsetGet($index): Nette\Routing\Router
*/
public function offsetExists($index): bool
{
trigger_error('Usage `isset($router[...])` is deprecated.', E_USER_DEPRECATED);
return is_int($index) && $index >= 0 && $index < count($this->getRouters());
}

Expand All @@ -133,13 +142,11 @@ public function offsetExists($index): bool
*/
public function offsetUnset($index): void
{
trigger_error('Usage `unset($router[$index])` is deprecated, use `$router->modify($index, null)`.', E_USER_DEPRECATED);
if (!$this->offsetExists($index)) {
throw new Nette\OutOfRangeException('Offset invalid or out of range');
}

$this->modify($index, null);
}
}


interface_exists(Nette\Application\IRouter::class);
3 changes: 0 additions & 3 deletions src/Application/Routers/SimpleRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,3 @@ public function __construct(array|string $defaults = [])
parent::__construct($defaults);
}
}


interface_exists(Nette\Application\IRouter::class);
95 changes: 50 additions & 45 deletions src/Application/UI/Component.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
namespace Nette\Application\UI;

use Nette;
use function array_key_exists, array_slice, class_exists, func_get_arg, func_get_args, func_num_args, get_debug_type, is_array, link, method_exists, sprintf, trigger_error;
use function array_key_exists, count, func_get_arg, func_num_args, get_debug_type, is_array, link, method_exists, sprintf, trigger_error;


/**
Expand All @@ -20,8 +20,8 @@
* other child components, and interact with user. Components have properties
* for storing their status, and responds to user command.
*
* @property-read Presenter $presenter
* @property-read bool $linkCurrent
* @property-deprecated Presenter $presenter
* @property-deprecated bool $linkCurrent
*/
abstract class Component extends Nette\ComponentModel\Container implements SignalReceiver, StatePersistent, \ArrayAccess
{
Expand Down Expand Up @@ -72,18 +72,27 @@ public function getUniqueId(): string
}


public function addComponent(
Nette\ComponentModel\IComponent $component,
?string $name,
?string $insertBefore = null,
): static
{
if (!$component instanceof SignalReceiver && !$component instanceof StatePersistent) {
throw new Nette\InvalidStateException("Component '$name' of type " . get_debug_type($component) . ' is not intended to be used in the Presenter.');
}

return parent::addComponent($component, $name, $insertBefore = null);
}


protected function createComponent(string $name): ?Nette\ComponentModel\IComponent
{
if (method_exists($this, $method = 'createComponent' . $name)) {
(new AccessPolicy($this, new \ReflectionMethod($this, $method)))->checkAccess();
}
$res = parent::createComponent($name);
if ($res && !$res instanceof SignalReceiver && !$res instanceof StatePersistent) {
$type = $res::class;
trigger_error("It seems that component '$name' of type $type is not intended to be used in the Presenter.");
(new AccessPolicy($this, $rm = new \ReflectionMethod($this, $method)))->checkAccess();
$this->checkRequirements($rm);
}

return $res;
return parent::createComponent($name);
}


Expand Down Expand Up @@ -162,9 +171,9 @@ public function loadState(array $params): void
));
}

$this->$name = $params[$name];
$this->$name = &$params[$name];
} else {
$params[$name] = $this->$name ?? null;
$params[$name] = &$this->$name;
}
}

Expand Down Expand Up @@ -226,6 +235,7 @@ public function saveStatePartial(array &$params, ComponentReflection $reflection
final public function getParameter(string $name): mixed
{
if (func_num_args() > 1) {
trigger_error(__METHOD__ . '() parameter $default is deprecated, use operator ??', E_USER_DEPRECATED);
$default = func_get_arg(1);
}
return $this->params[$name] ?? $default ?? null;
Expand All @@ -237,7 +247,7 @@ final public function getParameter(string $name): mixed
*/
final public function getParameters(): array
{
return $this->params;
return array_map(fn($item) => $item, $this->params);
}


Expand Down Expand Up @@ -282,49 +292,49 @@ public static function formatSignalMethod(string $signal): string
/**
* Generates URL to presenter, action or signal.
* @param string $destination in format "[//] [[[module:]presenter:]action | signal! | this] [#fragment]"
* @param array|mixed $args
* @param mixed ...$args
* @throws InvalidLinkException
*/
public function link(string $destination, $args = []): string
public function link(string $destination, ...$args): string
{
try {
$args = func_num_args() < 3 && is_array($args)
? $args
: array_slice(func_get_args(), 1);
$args = count($args) === 1 && is_array($args[0] ?? null)
? $args[0]
: $args;
return $this->getPresenter()->getLinkGenerator()->link($destination, $args, $this, 'link');

} catch (InvalidLinkException $e) {
return $this->getPresenter()->handleInvalidLink($e);
return $this->getPresenter()->processInvalidLink($e);
}
}


/**
* Returns destination as Link object.
* @param string $destination in format "[//] [[[module:]presenter:]action | signal! | this] [#fragment]"
* @param array|mixed $args
* @param mixed ...$args
*/
public function lazyLink(string $destination, $args = []): Link
public function lazyLink(string $destination, ...$args): Link
{
$args = func_num_args() < 3 && is_array($args)
? $args
: array_slice(func_get_args(), 1);
$args = count($args) === 1 && is_array($args[0] ?? null)
? $args[0]
: $args;
return new Link($this, $destination, $args);
}


/**
* Determines whether it links to the current page.
* @param ?string $destination in format "[[[module:]presenter:]action | signal! | this]"
* @param array|mixed $args
* @param mixed ...$args
* @throws InvalidLinkException
*/
public function isLinkCurrent(?string $destination = null, $args = []): bool
public function isLinkCurrent(?string $destination = null, ...$args): bool
{
if ($destination !== null) {
$args = func_num_args() < 3 && is_array($args)
? $args
: array_slice(func_get_args(), 1);
$args = count($args) === 1 && is_array($args[0] ?? null)
? $args[0]
: $args;
$this->getPresenter()->getLinkGenerator()->createRequest($this, $destination, $args, 'test');
}

Expand All @@ -335,15 +345,14 @@ public function isLinkCurrent(?string $destination = null, $args = []): bool
/**
* Redirect to another presenter, action or signal.
* @param string $destination in format "[//] [[[module:]presenter:]action | signal! | this] [#fragment]"
* @param array|mixed $args
* @return never
* @param mixed ...$args
* @throws Nette\Application\AbortException
*/
public function redirect(string $destination, $args = []): void
public function redirect(string $destination, ...$args): never
{
$args = func_num_args() < 3 && is_array($args)
? $args
: array_slice(func_get_args(), 1);
$args = count($args) === 1 && is_array($args[0] ?? null)
? $args[0]
: $args;
$presenter = $this->getPresenter();
$presenter->saveGlobalState();
$presenter->redirectUrl($presenter->getLinkGenerator()->link($destination, $args, $this, 'redirect'));
Expand All @@ -353,15 +362,14 @@ public function redirect(string $destination, $args = []): void
/**
* Permanently redirects to presenter, action or signal.
* @param string $destination in format "[//] [[[module:]presenter:]action | signal! | this] [#fragment]"
* @param array|mixed $args
* @return never
* @param mixed ...$args
* @throws Nette\Application\AbortException
*/
public function redirectPermanent(string $destination, $args = []): void
public function redirectPermanent(string $destination, ...$args): never
{
$args = func_num_args() < 3 && is_array($args)
? $args
: array_slice(func_get_args(), 1);
$args = count($args) === 1 && is_array($args[0] ?? null)
? $args[0]
: $args;
$presenter = $this->getPresenter();
$presenter->redirectUrl(
$presenter->getLinkGenerator()->link($destination, $args, $this, 'redirect'),
Expand All @@ -379,6 +387,3 @@ public function error(string $message = '', int $httpCode = Nette\Http\IResponse
throw new Nette\Application\BadRequestException($message, $httpCode);
}
}


class_exists(PresenterComponent::class);
Loading