-
-
Notifications
You must be signed in to change notification settings - Fork 106
/
Copy pathPresenterFactory.php
137 lines (108 loc) · 3.47 KB
/
PresenterFactory.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\Application;
use Nette;
/**
* Default presenter loader.
*/
class PresenterFactory implements IPresenterFactory
{
/** @var array[] of module => splited mask */
private array $mapping = [
'*' => ['App\\UI\\', '*\\', '**Presenter'],
'Nette' => ['NetteModule\\', '*\\', '*Presenter'],
];
private array $aliases = [];
private array $cache = [];
/** @var callable */
private $factory;
/**
* @param ?callable(string): IPresenter $factory
*/
public function __construct(?callable $factory = null)
{
$this->factory = $factory ?: fn(string $class): IPresenter => new $class;
}
/**
* Creates new presenter instance.
*/
public function createPresenter(string $name): IPresenter
{
return ($this->factory)($this->getPresenterClass($name));
}
/**
* Generates and checks presenter class name.
* @throws InvalidPresenterException
*/
public function getPresenterClass(string &$name): string
{
if (isset($this->cache[$name])) {
return $this->cache[$name];
}
$class = $this->formatPresenterClass($name);
if (!class_exists($class)) {
throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' was not found.");
}
$reflection = new \ReflectionClass($class);
$class = $reflection->getName();
if (!$reflection->implementsInterface(IPresenter::class)) {
throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is not Nette\\Application\\IPresenter implementor.");
} elseif ($reflection->isAbstract()) {
throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is abstract.");
}
return $this->cache[$name] = $class;
}
/**
* Sets mapping as pairs [module => mask]
*/
public function setMapping(array $mapping): static
{
foreach ($mapping as $module => $mask) {
if (is_string($mask)) {
if (!preg_match('#^\\\\?([\w\\\\]*\\\\)?(\w*\*\w*?\\\\)?([\w\\\\]*\*\*?\w*)$#D', $mask, $m)) {
throw new Nette\InvalidStateException("Invalid mapping mask '$mask'.");
}
$this->mapping[$module] = [$m[1], $m[2] ?: '*Module\\', $m[3]];
} elseif (is_array($mask) && count($mask) === 3) {
$this->mapping[$module] = [$mask[0] ? $mask[0] . '\\' : '', $mask[1] . '\\', $mask[2]];
} else {
throw new Nette\InvalidStateException("Invalid mapping mask for module $module.");
}
}
return $this;
}
/**
* Formats presenter class name from its name.
* @internal
*/
public function formatPresenterClass(string $presenter): string
{
if (!Nette\Utils\Strings::match($presenter, '#^[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff:]*$#D')) {
throw new InvalidPresenterException("Presenter name must be alphanumeric string, '$presenter' is invalid.");
}
$parts = explode(':', $presenter);
$mapping = isset($parts[1], $this->mapping[$parts[0]])
? $this->mapping[array_shift($parts)]
: $this->mapping['*'];
while ($part = array_shift($parts)) {
$mapping[0] .= strtr($mapping[$parts ? 1 : 2], ['**' => "$part\\$part", '*' => $part]);
}
return $mapping[0];
}
/**
* Sets pairs [alias => destination]
*/
public function setAliases(array $aliases): static
{
$this->aliases = $aliases;
return $this;
}
public function getAlias(string $alias): string
{
return $this->aliases[$alias] ?? throw new Nette\InvalidStateException("Link alias '$alias' was not found.");
}
}