Skip to content

Commit 3c63c03

Browse files
committed
Merge pull request #66 from dg/pull-invalid
PresenterFactory as callback & changed invalid link handling [WIP]
2 parents b29b3db + 8c7648b commit 3c63c03

File tree

9 files changed

+225
-46
lines changed

9 files changed

+225
-46
lines changed

src/Application/PresenterFactory.php

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,16 @@ class PresenterFactory extends Nette\Object implements IPresenterFactory
2929
/** @var array */
3030
private $cache = array();
3131

32-
/** @var Nette\DI\Container */
33-
private $container;
32+
/** @var callable */
33+
private $factory;
3434

35-
/** @var string */
36-
private $touchToRebuild;
3735

38-
39-
public function __construct(Nette\DI\Container $container, $touchToRebuild = NULL)
36+
/**
37+
* @param callable function(string $class): IPresenter
38+
*/
39+
public function __construct($factory = NULL)
4040
{
41-
$this->container = $container;
42-
$this->touchToRebuild = $touchToRebuild;
41+
$this->factory = $factory ?: function($class) { return new $class; };
4342
}
4443

4544

@@ -50,25 +49,7 @@ public function __construct(Nette\DI\Container $container, $touchToRebuild = NUL
5049
*/
5150
public function createPresenter($name)
5251
{
53-
$class = $this->getPresenterClass($name);
54-
$services = array_keys($this->container->findByTag('nette.presenter'), $class);
55-
if (count($services) > 1) {
56-
throw new InvalidPresenterException("Multiple services of type $class found: " . implode(', ', $services) . '.');
57-
58-
} elseif (!$services) {
59-
if ($this->touchToRebuild) {
60-
touch($this->touchToRebuild);
61-
}
62-
63-
$presenter = $this->container->createInstance($class);
64-
$this->container->callInjects($presenter);
65-
if ($presenter instanceof UI\Presenter && $presenter->invalidLinkMode === NULL) {
66-
$presenter->invalidLinkMode = $this->container->parameters['debugMode'] ? UI\Presenter::INVALID_LINK_WARNING : UI\Presenter::INVALID_LINK_SILENT;
67-
}
68-
return $presenter;
69-
}
70-
71-
return $this->container->createService($services[0]);
52+
return call_user_func($this->factory, $this->getPresenterClass($name));
7253
}
7354

7455

src/Application/UI/Presenter.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@
3434
abstract class Presenter extends Control implements Application\IPresenter
3535
{
3636
/** bad link handling {@link Presenter::$invalidLinkMode} */
37-
const INVALID_LINK_SILENT = 1,
38-
INVALID_LINK_WARNING = 2,
39-
INVALID_LINK_EXCEPTION = 3;
37+
const INVALID_LINK_SILENT = 0,
38+
INVALID_LINK_WARNING = 1,
39+
INVALID_LINK_EXCEPTION = 2,
40+
INVALID_LINK_TEXTUAL = 4;
4041

4142
/** @internal special parameter key */
4243
const SIGNAL_KEY = 'do',
@@ -1060,15 +1061,14 @@ public static function argsToParams($class, $method, & $args, $supplemental = ar
10601061
*/
10611062
protected function handleInvalidLink(InvalidLinkException $e)
10621063
{
1063-
if ($this->invalidLinkMode === self::INVALID_LINK_SILENT) {
1064-
return '#';
1065-
1066-
} elseif ($this->invalidLinkMode === self::INVALID_LINK_WARNING) {
1067-
return '#error: ' . $e->getMessage();
1068-
1069-
} else { // self::INVALID_LINK_EXCEPTION
1064+
if ($this->invalidLinkMode & self::INVALID_LINK_EXCEPTION) {
10701065
throw $e;
1066+
} elseif ($this->invalidLinkMode & self::INVALID_LINK_WARNING) {
1067+
trigger_error('Invalid link: ' . $e->getMessage(), E_USER_WARNING);
10711068
}
1069+
return $this->invalidLinkMode & self::INVALID_LINK_TEXTUAL
1070+
? '#error: ' . $e->getMessage()
1071+
: '#';
10721072
}
10731073

10741074

src/Bridges/ApplicationDI/ApplicationExtension.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,15 @@ class ApplicationExtension extends Nette\DI\CompilerExtension
2626
'scanDirs' => array(),
2727
'scanComposer' => NULL,
2828
'scanFilter' => 'Presenter',
29+
'silentLinks' => FALSE,
2930
);
3031

3132
/** @var bool */
3233
private $debugMode;
3334

35+
/** @var int */
36+
private $invalidLinkMode;
37+
3438

3539
public function __construct($debugMode = FALSE, array $scanDirs = NULL)
3640
{
@@ -47,6 +51,10 @@ public function loadConfiguration()
4751
$container = $this->getContainerBuilder();
4852
$container->addExcludedClasses(array('Nette\Application\UI\Control'));
4953

54+
$this->invalidLinkMode = $this->debugMode
55+
? UI\Presenter::INVALID_LINK_TEXTUAL | ($config['silentLinks'] ? 0 : UI\Presenter::INVALID_LINK_WARNING)
56+
: UI\Presenter::INVALID_LINK_WARNING;
57+
5058
$application = $container->addDefinition($this->prefix('application'))
5159
->setClass('Nette\Application\Application')
5260
->addSetup('$catchExceptions', array($config['catchExceptions']))
@@ -56,10 +64,12 @@ public function loadConfiguration()
5664
$application->addSetup('Nette\Bridges\ApplicationTracy\RoutingPanel::initializePanel');
5765
}
5866

59-
$touchToRebuild = $this->debugMode && $config['scanDirs'] ? reset($config['scanDirs']) : NULL;
67+
$touch = $this->debugMode && $config['scanDirs'] ? reset($config['scanDirs']) : NULL; // dir added as dependency
6068
$presenterFactory = $container->addDefinition($this->prefix('presenterFactory'))
6169
->setClass('Nette\Application\IPresenterFactory')
62-
->setFactory('Nette\Application\PresenterFactory', array(1 => $touchToRebuild));
70+
->setFactory('Nette\Application\PresenterFactory', array(new Nette\DI\Statement(
71+
'Nette\Bridges\ApplicationDI\PresenterFactoryCallback', array(1 => $this->invalidLinkMode, $touch)
72+
)));
6373

6474
if ($config['mapping']) {
6575
$presenterFactory->addSetup('setMapping', array($config['mapping']));
@@ -96,9 +106,7 @@ public function beforeCompile()
96106
foreach ($all as $def) {
97107
$def->setInject(TRUE)->setAutowired(FALSE)->addTag('nette.presenter', $def->getClass());
98108
if (is_subclass_of($def->getClass(), 'Nette\Application\UI\Presenter')) {
99-
$def->addSetup('$invalidLinkMode', array(
100-
$this->debugMode ? UI\Presenter::INVALID_LINK_WARNING : UI\Presenter::INVALID_LINK_SILENT
101-
));
109+
$def->addSetup('$invalidLinkMode', array($this->invalidLinkMode));
102110
}
103111
}
104112
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the Nette Framework (http://nette.org)
5+
* Copyright (c) 2004 David Grudl (http://davidgrudl.com)
6+
*/
7+
8+
namespace Nette\Bridges\ApplicationDI;
9+
10+
use Nette;
11+
12+
13+
/**
14+
* PresenterFactory callback.
15+
* @internal
16+
*/
17+
class PresenterFactoryCallback
18+
{
19+
/** @var Nette\DI\Container */
20+
private $container;
21+
22+
/** @var int */
23+
private $invalidLinkMode;
24+
25+
/** @var string|NULL */
26+
private $touchToRefresh;
27+
28+
29+
public function __construct(Nette\DI\Container $container, $invalidLinkMode, $touchToRefresh)
30+
{
31+
$this->container = $container;
32+
$this->invalidLinkMode = $invalidLinkMode;
33+
$this->touchToRefresh = $touchToRefresh;
34+
}
35+
36+
37+
/**
38+
* @return Nette\Application\IPresenter
39+
*/
40+
public function __invoke($class)
41+
{
42+
$services = array_keys($this->container->findByTag('nette.presenter'), $class);
43+
if (count($services) > 1) {
44+
throw new Nette\Application\InvalidPresenterException("Multiple services of type $class found: " . implode(', ', $services) . '.');
45+
46+
} elseif (!$services) {
47+
if ($this->touchToRefresh) {
48+
touch($this->touchToRefresh);
49+
}
50+
51+
$presenter = $this->container->createInstance($class);
52+
$this->container->callInjects($presenter);
53+
if ($presenter instanceof Nette\Application\UI\Presenter && $presenter->invalidLinkMode === NULL) {
54+
$presenter->invalidLinkMode = $this->invalidLinkMode;
55+
}
56+
return $presenter;
57+
}
58+
59+
return $this->container->createService($services[0]);
60+
}
61+
62+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
3+
/**
4+
* Test: ApplicationExtension
5+
*/
6+
7+
use Nette\DI,
8+
Nette\Bridges\ApplicationDI\ApplicationExtension,
9+
Nette\Application\UI\Presenter,
10+
Tester\Assert;
11+
12+
13+
require __DIR__ . '/../bootstrap.php';
14+
require __DIR__ . '/files/MyPresenter.php';
15+
16+
17+
function createCompiler($config)
18+
{
19+
$compiler = new DI\Compiler;
20+
$compiler->loadConfig(Tester\FileMock::create($config, 'neon'));
21+
$builder = $compiler->getContainerBuilder();
22+
$builder->addDefinition('myRouter')->setClass('Nette\Application\Routers\SimpleRouter');
23+
$builder->addDefinition('myHttpRequest')->setFactory('Nette\Http\Request', array(new DI\Statement('Nette\Http\UrlScript')));
24+
$builder->addDefinition('myHttpResponse')->setClass('Nette\Http\Response');
25+
return $compiler;
26+
}
27+
28+
29+
test(function() {
30+
$compiler = createCompiler('
31+
application:
32+
debugger: no
33+
silentLinks: yes
34+
35+
services:
36+
presenter: Presenter1
37+
');
38+
$compiler->addExtension('application', new ApplicationExtension(TRUE));
39+
$code = $compiler->compile(NULL, 'Container4');
40+
eval($code);
41+
42+
$container = new Container4;
43+
Assert::same(
44+
Presenter::INVALID_LINK_TEXTUAL,
45+
$container->getService('presenter')->invalidLinkMode
46+
);
47+
});
48+
49+
50+
test(function() {
51+
$compiler = createCompiler('
52+
application:
53+
debugger: no
54+
silentLinks: no
55+
56+
services:
57+
presenter: Presenter1
58+
');
59+
$compiler->addExtension('application', new ApplicationExtension(TRUE));
60+
$code = $compiler->compile(NULL, 'Container5');
61+
eval($code);
62+
63+
$container = new Container5;
64+
Assert::same(
65+
Presenter::INVALID_LINK_WARNING | Presenter::INVALID_LINK_TEXTUAL,
66+
$container->getService('presenter')->invalidLinkMode
67+
);
68+
});
69+
70+
71+
test(function() {
72+
$compiler = createCompiler('
73+
application:
74+
debugger: no
75+
silentLinks: yes
76+
77+
services:
78+
presenter: Presenter1
79+
');
80+
$compiler->addExtension('application', new ApplicationExtension(FALSE));
81+
$code = $compiler->compile(NULL, 'Container6');
82+
eval($code);
83+
84+
$container = new Container6;
85+
Assert::same(
86+
Presenter::INVALID_LINK_WARNING,
87+
$container->getService('presenter')->invalidLinkMode
88+
);
89+
});
90+
91+
92+
test(function() {
93+
$compiler = createCompiler('
94+
application:
95+
debugger: no
96+
silentLinks: no
97+
98+
services:
99+
presenter: Presenter1
100+
');
101+
$compiler->addExtension('application', new ApplicationExtension(FALSE));
102+
$code = $compiler->compile(NULL, 'Container7');
103+
eval($code);
104+
105+
$container = new Container7;
106+
Assert::same(
107+
Presenter::INVALID_LINK_WARNING,
108+
$container->getService('presenter')->invalidLinkMode
109+
);
110+
});

tests/Application.Routers/LinkGenerator.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ namespace {
4242
Tester\Assert;
4343

4444

45-
$pf = new PresenterFactory(new Nette\DI\Container);
45+
$pf = new PresenterFactory;
4646

4747

4848
test(function() use ($pf) {

tests/Application/Presenter.link().phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class TestPresenter extends Application\UI\Presenter
7777
{
7878
parent::startup();
7979
$this['mycontrol'] = new TestControl;
80+
$this->invalidLinkMode = self::INVALID_LINK_TEXTUAL;
8081

8182
// Presenter & action link
8283
Assert::same( '/index.php?action=product&presenter=Test', $this->link('product', array('var1' => $this->var1)) );
@@ -133,6 +134,23 @@ class TestPresenter extends Application\UI\Presenter
133134
$this['mycontrol']->order = 1;
134135
Assert::same( "#error: Invalid value for persistent parameter 'order' in 'mycontrol', expected array.", $this['mycontrol']->link('click') );
135136
$this['mycontrol']->order = NULL;
137+
138+
// silent invalid link mode
139+
$this->invalidLinkMode = self::INVALID_LINK_SILENT;
140+
Assert::same('#', $this->link('product', array('var1' => null, 'ok' => 'a')));
141+
142+
// warning invalid link mode
143+
$this->invalidLinkMode = self::INVALID_LINK_WARNING;
144+
$me = $this;
145+
Assert::error(function() use ($me) {
146+
Assert::same('#', $me->link('product', array('var1' => null, 'ok' => 'a')));
147+
}, E_USER_WARNING, "Invalid link: Invalid value for persistent parameter 'ok' in 'Test', expected boolean.");
148+
149+
// exception invalid link mode
150+
$this->invalidLinkMode = self::INVALID_LINK_EXCEPTION;
151+
Assert::exception(function() use ($me) {
152+
$me->link('product', array('var1' => null, 'ok' => 'a'));
153+
}, 'Nette\Application\UI\InvalidLinkException', "Invalid value for persistent parameter 'ok' in 'Test', expected boolean.");
136154
}
137155

138156

tests/Application/PresenterFactory.formatPresenterClass.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require __DIR__ . '/../bootstrap.php';
1212

1313

1414
test(function() {
15-
$factory = new PresenterFactory(new Nette\DI\Container);
15+
$factory = new PresenterFactory;
1616

1717
$factory->setMapping(array(
1818
'Foo2' => 'App2\*\*Presenter',
@@ -35,7 +35,7 @@ test(function() {
3535

3636

3737
test(function() {
38-
$factory = new PresenterFactory(new Nette\DI\Container);
38+
$factory = new PresenterFactory;
3939

4040
$factory->setMapping(array(
4141
'Foo2' => 'App2\*Presenter',

tests/Application/PresenterFactory.unformatPresenterClass.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use Nette\Application\PresenterFactory,
1111
require __DIR__ . '/../bootstrap.php';
1212

1313

14-
$factory = new PresenterFactory(new Nette\DI\Container);
14+
$factory = new PresenterFactory;
1515

1616
test(function() use ($factory) {
1717
$factory->setMapping(array(

0 commit comments

Comments
 (0)