1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Application;
9:
10: use Nette;
11:
12:
13: 14: 15:
16: class PresenterFactory implements IPresenterFactory
17: {
18: use Nette\SmartObject;
19:
20:
21: private $mapping = [
22: '*' => ['', '*Module\\', '*Presenter'],
23: 'Nette' => ['NetteModule\\', '*\\', '*Presenter'],
24: ];
25:
26:
27: private $cache = [];
28:
29:
30: private $factory;
31:
32:
33: 34: 35:
36: public function __construct(callable $factory = null)
37: {
38: $this->factory = $factory ?: function ($class) { return new $class; };
39: }
40:
41:
42: 43: 44: 45: 46:
47: public function createPresenter($name)
48: {
49: return call_user_func($this->factory, $this->getPresenterClass($name));
50: }
51:
52:
53: 54: 55: 56: 57: 58:
59: public function getPresenterClass(&$name)
60: {
61: if (isset($this->cache[$name])) {
62: return $this->cache[$name];
63: }
64:
65: if (!is_string($name) || !Nette\Utils\Strings::match($name, '#^[a-zA-Z\x7f-\xff][a-zA-Z0-9\x7f-\xff:]*\z#')) {
66: throw new InvalidPresenterException("Presenter name must be alphanumeric string, '$name' is invalid.");
67: }
68:
69: $class = $this->formatPresenterClass($name);
70: if (!class_exists($class)) {
71: throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' was not found.");
72: }
73:
74: $reflection = new \ReflectionClass($class);
75: $class = $reflection->getName();
76:
77: if (!$reflection->implementsInterface(IPresenter::class)) {
78: throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is not Nette\\Application\\IPresenter implementor.");
79: } elseif ($reflection->isAbstract()) {
80: throw new InvalidPresenterException("Cannot load presenter '$name', class '$class' is abstract.");
81: }
82:
83: $this->cache[$name] = $class;
84:
85: if ($name !== ($realName = $this->unformatPresenterClass($class))) {
86: trigger_error("Case mismatch on presenter name '$name', correct name is '$realName'.", E_USER_WARNING);
87: $name = $realName;
88: }
89:
90: return $class;
91: }
92:
93:
94: 95: 96: 97:
98: public function setMapping(array $mapping)
99: {
100: foreach ($mapping as $module => $mask) {
101: if (is_string($mask)) {
102: if (!preg_match('#^\\\\?([\w\\\\]*\\\\)?(\w*\*\w*?\\\\)?([\w\\\\]*\*\w*)\z#', $mask, $m)) {
103: throw new Nette\InvalidStateException("Invalid mapping mask '$mask'.");
104: }
105: $this->mapping[$module] = [$m[1], $m[2] ?: '*Module\\', $m[3]];
106: } elseif (is_array($mask) && count($mask) === 3) {
107: $this->mapping[$module] = [$mask[0] ? $mask[0] . '\\' : '', $mask[1] . '\\', $mask[2]];
108: } else {
109: throw new Nette\InvalidStateException("Invalid mapping mask for module $module.");
110: }
111: }
112: return $this;
113: }
114:
115:
116: 117: 118: 119: 120: 121:
122: public function formatPresenterClass($presenter)
123: {
124: $parts = explode(':', $presenter);
125: $mapping = isset($parts[1], $this->mapping[$parts[0]])
126: ? $this->mapping[array_shift($parts)]
127: : $this->mapping['*'];
128:
129: while ($part = array_shift($parts)) {
130: $mapping[0] .= str_replace('*', $part, $mapping[$parts ? 1 : 2]);
131: }
132: return $mapping[0];
133: }
134:
135:
136: 137: 138: 139: 140: 141:
142: public function unformatPresenterClass($class)
143: {
144: foreach ($this->mapping as $module => $mapping) {
145: $mapping = str_replace(['\\', '*'], ['\\\\', '(\w+)'], $mapping);
146: if (preg_match("#^\\\\?$mapping[0]((?:$mapping[1])*)$mapping[2]\\z#i", $class, $matches)) {
147: return ($module === '*' ? '' : $module . ':')
148: . preg_replace("#$mapping[1]#iA", '$1:', $matches[1]) . $matches[3];
149: }
150: }
151: return null;
152: }
153: }
154: