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