Skip to content

Support for properties in NEON statement syntax in section services - PART 3 #339

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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": "^2.0",
"phpstan/phpstan-nette": "^1.0",
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.1",
"latte/latte": "<3.0.18",
"tracy/tracy": "<2.9"
},
"autoload": {
Expand All @@ -55,7 +55,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 @@ -242,6 +242,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 @@ -19,7 +19,7 @@ class PresenterFactory implements IPresenterFactory
{
/** @var array[] of module => splited mask */
private array $mapping = [
'*' => ['', '*Module\\', '*Presenter'],
'*' => ['App\\UI\\', '*\\', '**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 @@ -15,11 +15,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
3 changes: 0 additions & 3 deletions src/Application/Routers/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
return null;
}

$presenter = $params[self::PresenterKey] ?? null;

Check failure on line 87 in src/Application/Routers/Route.php

View workflow job for this annotation

GitHub Actions / PHPStan

Offset 'presenter' on array on left side of ?? always exists and is not nullable.
if (isset($this->getMetadata()[self::ModuleKey], $params[self::ModuleKey]) && is_string($presenter)) {
$params[self::PresenterKey] = $params[self::ModuleKey] . ':' . $params[self::PresenterKey];
}
Expand Down Expand Up @@ -187,6 +187,3 @@
return $s;
}
}


interface_exists(Nette\Application\IRouter::class);
13 changes: 10 additions & 3 deletions src/Application/Routers/RouteList.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/**
* The router broker.
*/
class RouteList extends Nette\Routing\RouteList implements Nette\Routing\Router, \ArrayAccess

Check failure on line 19 in src/Application/Routers/RouteList.php

View workflow job for this annotation

GitHub Actions / PHPStan

The @implements tag of class Nette\Application\Routers\RouteList describes IteratorAggregate but the class implements: Nette\Routing\Router, ArrayAccess
{
private const PresenterKey = 'presenter';

Expand Down Expand Up @@ -75,7 +75,7 @@

public function withModule(string $module): static
{
$router = new static;

Check failure on line 78 in src/Application/Routers/RouteList.php

View workflow job for this annotation

GitHub Actions / PHPStan

Unsafe usage of new static().
$router->module = $module . ':';
$router->parent = $this;
$this->add($router);
Expand All @@ -95,6 +95,13 @@
*/
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);

Check failure on line 101 in src/Application/Routers/RouteList.php

View workflow job for this annotation

GitHub Actions / PHPStan

Function getclass not found.
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 @@ -109,6 +116,7 @@
*/
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 @@ -122,6 +130,7 @@
*/
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 @@ -132,13 +141,11 @@
*/
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 @@ -38,6 +38,3 @@ public function __construct(array|string $defaults = [])
parent::__construct($defaults);
}
}


interface_exists(Nette\Application\IRouter::class);
93 changes: 49 additions & 44 deletions src/Application/UI/Component.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,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 All @@ -41,7 +41,7 @@
$throw = func_get_arg(0);
}

return $this->lookup(Presenter::class, throw: $throw ?? true);

Check failure on line 44 in src/Application/UI/Component.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Nette\Application\UI\Component::getPresenter() should return Nette\Application\UI\Presenter|null but returns Nette\ComponentModel\IComponent|null.
}


Expand All @@ -50,7 +50,7 @@
*/
public function getPresenterIfExists(): ?Presenter
{
return $this->lookup(Presenter::class, throw: false);

Check failure on line 53 in src/Application/UI/Component.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Nette\Application\UI\Component::getPresenterIfExists() should return Nette\Application\UI\Presenter|null but returns Nette\ComponentModel\IComponent|null.
}


Expand All @@ -71,18 +71,27 @@
}


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 @@ -161,9 +170,9 @@
));
}

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

Expand Down Expand Up @@ -193,7 +202,7 @@

} elseif (
array_key_exists($name, $params) // nulls are skipped
|| (isset($meta['since']) && !isset($tree[$meta['since']])) // not related

Check failure on line 205 in src/Application/UI/Component.php

View workflow job for this annotation

GitHub Actions / PHPStan

Offset 'since' on array{def: mixed, type: string, since: string} in isset() always exists and is not nullable.
|| !isset($this->$name)
) {
continue;
Expand Down Expand Up @@ -225,6 +234,7 @@
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 @@ -236,7 +246,7 @@
*/
final public function getParameters(): array
{
return $this->params;
return array_map(fn($item) => $item, $this->params);
}


Expand Down Expand Up @@ -281,49 +291,49 @@
/**
* 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 @@ -334,15 +344,14 @@
/**
* 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 @@ -352,15 +361,14 @@
/**
* 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 @@ -378,6 +386,3 @@
throw new Nette\Application\BadRequestException($message, $httpCode);
}
}


class_exists(PresenterComponent::class);
21 changes: 14 additions & 7 deletions src/Application/UI/ComponentReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@

/**
* Helpers for Presenter & Component.
* @property-read string $name
* @property-read string $fileName
* @property-deprecated string $name
* @property-deprecated string $fileName
* @internal
*/
final class ComponentReflection extends \ReflectionClass
Expand All @@ -27,7 +27,7 @@ final class ComponentReflection extends \ReflectionClass


/**
* Returns array of class properties that are public and have attribute #[Persistent] or #[Parameter] or annotation @persistent.
* Returns array of class properties that are public and have attribute #[Persistent] or #[Parameter].
* @return array<string, array{def: mixed, type: string, since: ?string}>
*/
public function getParameters(): array
Expand Down Expand Up @@ -74,7 +74,7 @@ public function getParameters(): array


/**
* Returns array of persistent properties. They are public and have attribute #[Persistent] or annotation @persistent.
* Returns array of persistent properties. They are public and have attribute #[Persistent].
* @return array<string, array{def: mixed, type: string, since: string}>
*/
public function getPersistentParams(): array
Expand Down Expand Up @@ -149,6 +149,7 @@ public function getSignalMethod(string $signal): ?\ReflectionMethod

/**
* Returns an annotation value.
* @deprecated
*/
public static function parseAnnotation(\Reflector $ref, string $name): ?array
{
Expand All @@ -166,12 +167,20 @@ public static function parseAnnotation(\Reflector $ref, string $name): ?array
}
}

$alt = match ($name) {
'persistent' => '#[Nette\Application\Attributes\Persistent]',
'deprecated' => '#[Nette\Application\Attributes\Deprecated]',
'crossOrigin' => '#[Nette\Application\Attributes\Request(sameOrigin: false)]',
default => 'alternative'
};
trigger_error("Annotation @$name is deprecated, use $alt (used in " . Nette\Utils\Reflection::toString($ref) . ')', E_USER_DEPRECATED);
return $res;
}


/**
* Has class specified annotation?
* @deprecated
*/
public function hasAnnotation(string $name): bool
{
Expand All @@ -181,6 +190,7 @@ public function hasAnnotation(string $name): bool

/**
* Returns an annotation value.
* @deprecated
*/
public function getAnnotation(string $name): mixed
{
Expand Down Expand Up @@ -214,6 +224,3 @@ public static function combineArgs(\ReflectionFunctionAbstract $method, array $a
return ParameterConverter::toArguments($method, $args);
}
}


class_exists(PresenterComponentReflection::class);
Loading
Loading