Vyžadování typu public pro property s atributem Persistent – Trait implementace

harvalikjan
Člen | 2
+
+3
-

Strávil jsem nepřiměřenou dobu :D hledáním problému v mém kódu, který by způsoboval to, že property nebyla persistentní mezi dotazy. Myslel jsem si že problém byl někde v Ajaxu ale ve skutečnosti jsem omylem nadefinoval typ property na private.

Bylo by super kdyby Nette takovou chybu automaticky hlásil :) Než to však někdo šikovný udělá, budu používat traitu, kterou jsem si na to napsal :D, vložil jsem si jí do BasePresenteru a mám od tohoto problému klid. Tak doufám že se někomu z Vás také hodí nebo někoho schopného inspiruje k řešení přímo v Nette ;)

<?php

declare(strict_types=1);

namespace App\Core;

trait RequirePublicTypeForPropertyWithPersistentAttribute
{
    // Attaches a callback to the startup event of the presenter.
    public function injectRequirePublicTypeForPropertyWithPersistentAttribute(): void
    {
        $this->onStartup[] = function () {

            $reflection = new \ReflectionClass($this);
            foreach ($reflection->getProperties() as $prop) {
                foreach ($prop->getAttributes() as $attr) {
                    if($attr->getName() === 'Nette\Application\Attributes\Persistent' && !$prop->isPublic()){
                        throw new \Exception("Property with Persistent Attribute '" . $prop->getName() . "' is type '" . $prop->getType() . "' but type public is required");
                    }
                }
            }

        };
    }
}
<?php

namespace App\UI;

use App\Core\RequirePublicTypeForPropertyWithPersistentAttribute;
use Nette;

class BasePresenter extends Nette\Application\UI\Presenter
{
    use RequirePublicTypeForPropertyWithPersistentAttribute;
}
Infanticide0
Člen | 73
+
+3
-

Pravděpodobně je ten čas zanedbatelný, ale i tak bych to raději viděl spuštěný jen jednou po buildu containeru (má vliv debugMode?).
Neznám Nette\DI tak dobře, abych zaručil, že je to přesně takhle správně (funguje mi to).

Persistent properties jdou použít jen na UI\Presenter a UI\Control třídách, takže je hledám přes UI\Component předka.

	class CheckPersistentPropsExtension extends Nette\DI\CompilerExtension
	{
		public function afterCompile(Nette\PhpGenerator\ClassType $class)
		{
			foreach ($this->getContainerBuilder()->findByType(Nette\Application\UI\Component::class) as $component) {
				$reflection = new \ReflectionClass($component->getType());
				foreach ($reflection->getProperties() as $prop) {
					foreach ($prop->getAttributes() as $attr) {
						if($attr->getName() === Persistent::class && !$prop->isPublic()){
							throw new \Exception("Property with Persistent Attribute '" . $prop->getName() . "' is type '" . $prop->getType() . "' but type public is required");
						}
					}
				}
			}
		}
	}
emololftw
Člen | 82
+
0
-

U sebe na projektu kontroluji viditelnost property kdyz maj attribut/anotaci persistent a v pripade ze neni public hodi warning. Nechtel jsem delat PR na takovou vec, pokladal jsem to za mou nepozornost. Jak koukam tak nejsem jediny.

harvalikjan
Člen | 2
+
+1
-

Děkuji Infanticide0 za posunutí dále :D, potvrzuji že řešení funguje. Je super že to funguje prostě v celém projektu. Chtělo by to asi pouze ještě upravit tu chybu:

throw new \Exception("Property with Persistent Attribute '" . $prop->getName() . "' is type '" . $prop->getType() . "' but type public is required");

na

throw new \Exception("Property with Persistent Attribute '" . $prop->getName() . "' is not public which is required");

protože informace že property je například int, je naprosto irelevantní a možná i zavádějící, tam problém není.

GEpic
Člen | 566
+
0
-

V Nette se používá přímo Persistent Parameters, pak bych hlášku upravil a zjednodušil takto:

throw new \Exception("Persistent parameter '" . $prop->getName() . "' must be public");
jiri.pudil
Nette Blogger | 1029
+
+4
-

Tohle zní jako skvělý kandidát na pravidlo v phpstan/phpstan-nette ;)

David Grudl
Nette Core | 8173
+
+5
-

Při sestavování kontaineru by takový check dělat šel, doplním to.