1111
1212use Composer \Autoload \ClassLoader ;
1313use Nette ;
14+ use Nette \Application \Attributes ;
1415use Nette \Application \UI ;
1516use Nette \DI \Definitions ;
1617use Nette \Schema \Expect ;
18+ use Nette \Utils \Reflection ;
1719use Tracy ;
1820
1921
@@ -24,6 +26,7 @@ final class ApplicationExtension extends Nette\DI\CompilerExtension
2426{
2527 private readonly array $ scanDirs ;
2628 private int $ invalidLinkMode ;
29+ private array $ checked = [];
2730
2831
2932 public function __construct (
@@ -135,6 +138,7 @@ public function beforeCompile(): void
135138
136139 $ counter = 0 ;
137140 foreach ($ this ->findPresenters () as $ class ) {
141+ $ this ->checkPresenter ($ class );
138142 if (empty ($ all [$ class ])) {
139143 $ all [$ class ] = $ builder ->addDefinition ($ this ->prefix ((string ) ++$ counter ))
140144 ->setType ($ class );
@@ -247,4 +251,41 @@ public static function generateNewPresenterFileContents(string $file, ?string $c
247251
248252 return $ res . "use Nette; \n\n\nclass $ class extends Nette \\Application \\UI \\Presenter \n{ \n\$END \$\n} \n" ;
249253 }
254+
255+
256+ private function checkPresenter (string $ class ): void
257+ {
258+ if (!is_subclass_of ($ class , UI \Presenter::class) || isset ($ this ->checked [$ class ])) {
259+ return ;
260+ }
261+ $ this ->checked [$ class ] = true ;
262+
263+ $ rc = new \ReflectionClass ($ class );
264+ if ($ rc ->getParentClass ()) {
265+ $ this ->checkPresenter ($ rc ->getParentClass ()->getName ());
266+ }
267+
268+ foreach ($ rc ->getProperties () as $ rp ) {
269+ if (($ rp ->getAttributes ($ attr = Attributes \Parameter::class) || $ rp ->getAttributes ($ attr = Attributes \Persistent::class))
270+ && (!$ rp ->isPublic () || $ rp ->isStatic () || $ rp ->isReadOnly ())
271+ ) {
272+ throw new Nette \InvalidStateException (sprintf ('Property %s: attribute %s can be used only with public non-static property. ' , Reflection::toString ($ rp ), $ attr ));
273+ }
274+ }
275+
276+ $ re = $ class ::formatActionMethod ('' ) . '.| ' . $ class ::formatRenderMethod ('' ) . '.| ' . $ class ::formatSignalMethod ('' ) . '. ' ;
277+ foreach ($ rc ->getMethods () as $ rm ) {
278+ if (preg_match ("#^(?!handleInvalidLink)( $ re)# " , $ rm ->getName ()) && (!$ rm ->isPublic () || $ rm ->isStatic ())) {
279+ throw new Nette \InvalidStateException (sprintf ('Method %s: this method must be public non-static. ' , Reflection::toString ($ rm )));
280+ } elseif (preg_match ('#^createComponent.# ' , $ rm ->getName ()) && ($ rm ->isPrivate () || $ rm ->isStatic ())) {
281+ throw new Nette \InvalidStateException (sprintf ('Method %s: this method must be non-private non-static. ' , Reflection::toString ($ rm )));
282+ } elseif ($ rm ->getAttributes (Attributes \Requires::class, \ReflectionAttribute::IS_INSTANCEOF )
283+ && !preg_match ("#^ $ re|createComponent.# " , $ rm ->getName ())
284+ ) {
285+ throw new Nette \InvalidStateException (sprintf ('Method %s: attribute %s can be used only with action, render, handle or createComponent methods. ' , Reflection::toString ($ rm ), Attributes \Requires::class));
286+ } elseif ($ rm ->getAttributes (Attributes \Deprecated::class) && !preg_match ("#^ $ re# " , $ rm ->getName ())) {
287+ throw new Nette \InvalidStateException (sprintf ('Method %s: attribute %s can be used only with action, render or handle methods. ' , Reflection::toString ($ rm ), Attributes \Deprecated::class));
288+ }
289+ }
290+ }
250291}
0 commit comments