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