Rev 51 | Rev 57 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
27 | PointedEar | 1 | <?php |
2 | |||
51 | PointedEar | 3 | namespace PointedEars\PHPX; |
4 | |||
52 | PointedEar | 5 | function autoload ($class) |
6 | { |
||
7 | if (\strpos($class, '..')) |
||
8 | { |
||
9 | throw new \InvalidArgumentException( |
||
10 | 'Refusing to load unsafe class ' . $class); |
||
11 | } |
||
27 | PointedEar | 12 | |
52 | PointedEar | 13 | require_once \preg_replace('#\\\#', '/', |
14 | \preg_replace('#^' . \preg_quote(__NAMESPACE__) .'#', __DIR__, $class) |
||
15 | ) . '.php'; |
||
16 | } |
||
17 | |||
18 | \spl_autoload_register(__NAMESPACE__ . '\\autoload'); |
||
19 | |||
27 | PointedEar | 20 | /** |
21 | * Basic application class |
||
22 | * |
||
23 | * @author Thomas Lahn |
||
24 | */ |
||
25 | class Application |
||
26 | { |
||
27 | /** |
||
28 | * Relative path to the controllers directory |
||
29 | * @var string |
||
30 | */ |
||
31 | protected $_controllerPath = 'application/controllers'; |
||
45 | PointedEar | 32 | |
27 | PointedEar | 33 | /** |
34 | * Default controller of the application |
||
35 | * @var string |
||
36 | */ |
||
37 | protected $_defaultController = 'Index'; |
||
45 | PointedEar | 38 | |
27 | PointedEar | 39 | /** |
40 | * Registry key for the default database of the application |
||
41 | * @var string |
||
42 | */ |
||
43 | protected $_defaultDatabase; |
||
45 | PointedEar | 44 | |
27 | PointedEar | 45 | /** |
46 | * Currently active controller of this application |
||
47 | * @var Controller |
||
48 | */ |
||
49 | protected $_currentController; |
||
45 | PointedEar | 50 | |
27 | PointedEar | 51 | /** |
52 | * Singleton |
||
53 | * |
||
54 | * @var Application |
||
55 | */ |
||
56 | private static $_instance; |
||
45 | PointedEar | 57 | |
52 | PointedEar | 58 | protected function __construct () |
27 | PointedEar | 59 | { |
60 | /* Singleton pattern */ |
||
61 | } |
||
45 | PointedEar | 62 | |
27 | PointedEar | 63 | /** |
64 | * Gets a reference to the <code>Application</code> instance |
||
65 | * |
||
66 | * @param Application $instance |
||
67 | * The instance to be used as application. The default is a new |
||
68 | * application. This parameter is ignored if the application was |
||
69 | * already initialized. |
||
70 | * @return Application |
||
71 | */ |
||
52 | PointedEar | 72 | public static function getInstance (Application $instance = null) |
27 | PointedEar | 73 | { |
31 | PointedEar | 74 | if (self::$_instance === null) |
27 | PointedEar | 75 | { |
76 | self::$_instance = ($instance === null) ? new self() : $instance; |
||
77 | } |
||
45 | PointedEar | 78 | |
27 | PointedEar | 79 | return self::$_instance; |
80 | } |
||
45 | PointedEar | 81 | |
31 | PointedEar | 82 | /** |
83 | * Getter for properties |
||
84 | * |
||
85 | * @param string $name |
||
86 | * @throws ModelPropertyException |
||
87 | * @return mixed |
||
88 | */ |
||
52 | PointedEar | 89 | public function __get ($name) |
31 | PointedEar | 90 | { |
91 | /* Support for Object-Relational Mappers */ |
||
52 | PointedEar | 92 | if (\strpos($name, 'persistent') === 0) |
31 | PointedEar | 93 | { |
52 | PointedEar | 94 | $class = \get_class($this); |
34 | PointedEar | 95 | return $class::${$name}; |
31 | PointedEar | 96 | } |
45 | PointedEar | 97 | |
52 | PointedEar | 98 | $method = 'get' . \ucfirst($name); |
45 | PointedEar | 99 | |
52 | PointedEar | 100 | if (\method_exists($this, $method)) |
31 | PointedEar | 101 | { |
102 | return $this->$method(); |
||
103 | } |
||
45 | PointedEar | 104 | |
52 | PointedEar | 105 | if (\property_exists($this, "_$name")) |
31 | PointedEar | 106 | { |
107 | return $this->{"_$name"}; |
||
108 | } |
||
45 | PointedEar | 109 | |
31 | PointedEar | 110 | return $this->$name; |
111 | } |
||
45 | PointedEar | 112 | |
27 | PointedEar | 113 | /** |
31 | PointedEar | 114 | * Setter for properties |
115 | * |
||
116 | * @param string $name |
||
117 | * @param mixed $value The new property value before assignment |
||
118 | * @throws ModelPropertyException |
||
119 | */ |
||
52 | PointedEar | 120 | public function __set ($name, $value) |
31 | PointedEar | 121 | { |
52 | PointedEar | 122 | $method = 'set' . \ucfirst($name); |
45 | PointedEar | 123 | |
52 | PointedEar | 124 | if (\method_exists($this, $method)) |
31 | PointedEar | 125 | { |
126 | return $this->$method($value); |
||
127 | } |
||
45 | PointedEar | 128 | |
52 | PointedEar | 129 | if (\property_exists($this, "_$name")) |
31 | PointedEar | 130 | { |
131 | $this->{"_$name"} = $value; |
||
132 | return $this->{"_$name"}; |
||
133 | } |
||
45 | PointedEar | 134 | |
31 | PointedEar | 135 | /* NOTE: Attempts to set other properties are _silently_ _ignored_ */ |
136 | } |
||
45 | PointedEar | 137 | |
31 | PointedEar | 138 | /** |
27 | PointedEar | 139 | * Runs the application, setting up session management and |
140 | * constructing the controller indicated by the URI |
||
141 | */ |
||
52 | PointedEar | 142 | public function run () |
27 | PointedEar | 143 | { |
144 | $this->startSession(); |
||
45 | PointedEar | 145 | |
27 | PointedEar | 146 | $controller = self::getParam('controller', $_REQUEST); |
147 | if (!$controller) |
||
148 | { |
||
149 | $controller = $this->_defaultController; |
||
150 | } |
||
151 | |||
52 | PointedEar | 152 | $controller = \ucfirst($controller); |
45 | PointedEar | 153 | |
27 | PointedEar | 154 | $controller = $controller . 'Controller'; |
155 | require_once "{$this->_controllerPath}/{$controller}.php"; |
||
156 | $this->_currentController = new $controller(); |
||
45 | PointedEar | 157 | |
27 | PointedEar | 158 | return $this; |
159 | } |
||
160 | |||
52 | PointedEar | 161 | protected function startSession () |
27 | PointedEar | 162 | { |
52 | PointedEar | 163 | \session_start(); |
27 | PointedEar | 164 | } |
45 | PointedEar | 165 | |
27 | PointedEar | 166 | /** |
167 | * Gets a request parameter |
||
168 | * |
||
169 | * @param string $key |
||
170 | * Key to look up in the array |
||
171 | * @param array $array |
||
172 | * Array where to look up <var>$key</var>. |
||
173 | * The default is <code>$_GET</code>. |
||
174 | * @return mixed |
||
175 | * <code>null</code> if there is no such <var>$key</var> |
||
176 | * in <var>$array</var> |
||
177 | */ |
||
52 | PointedEar | 178 | public static function getParam ($key, array $array = null) |
27 | PointedEar | 179 | { |
31 | PointedEar | 180 | if ($array === null) |
27 | PointedEar | 181 | { |
182 | $array = $_GET; |
||
183 | } |
||
45 | PointedEar | 184 | |
27 | PointedEar | 185 | return isset($array[$key]) ? $array[$key] : null; |
186 | } |
||
45 | PointedEar | 187 | |
27 | PointedEar | 188 | /** |
189 | * Registers a database |
||
190 | * |
||
191 | * @param string $key |
||
192 | * @param Database $database |
||
45 | PointedEar | 193 | * @return string Registry key |
194 | * @see Application::setDefaultDatabase() |
||
27 | PointedEar | 195 | */ |
51 | PointedEar | 196 | public function registerDatabase ($key, Db\Database $database) |
27 | PointedEar | 197 | { |
198 | Registry::set($key, $database); |
||
45 | PointedEar | 199 | return $key; |
27 | PointedEar | 200 | } |
201 | |||
202 | /** |
||
203 | * Sets the default database |
||
45 | PointedEar | 204 | * @param string Registry key to refer to the {@link Database} |
27 | PointedEar | 205 | */ |
45 | PointedEar | 206 | public function setDefaultDatabase ($key) |
27 | PointedEar | 207 | { |
208 | $this->_defaultDatabase = $key; |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * Sets the current controller for this application |
||
213 | * |
||
214 | * @param Controller $controller |
||
215 | * @return Application |
||
216 | */ |
||
52 | PointedEar | 217 | public function setCurrentController (Controller $controller) |
27 | PointedEar | 218 | { |
219 | $this->_currentController = $controller; |
||
220 | return $this; |
||
221 | } |
||
45 | PointedEar | 222 | |
27 | PointedEar | 223 | /** |
224 | * Returns the current controller for this application |
||
225 | * |
||
226 | * @return Controller |
||
227 | */ |
||
52 | PointedEar | 228 | public function getCurrentController () |
27 | PointedEar | 229 | { |
230 | return $this->_currentController; |
||
231 | } |
||
232 | |||
233 | /** |
||
234 | * Returns the default database for this application |
||
235 | * |
||
236 | * @return Database |
||
237 | */ |
||
52 | PointedEar | 238 | public function getDefaultDatabase () |
27 | PointedEar | 239 | { |
240 | return Registry::get($this->_defaultDatabase); |
||
241 | } |
||
242 | |||
243 | /** |
||
35 | PointedEar | 244 | * Returns a relative URI-reference for an action of the |
27 | PointedEar | 245 | * application |
246 | * |
||
247 | * @param string[optional] $controller |
||
248 | * @param string[optional] $action |
||
249 | * @param int[optional] $id |
||
250 | */ |
||
52 | PointedEar | 251 | public function getURL ($controller = null, $action = null, $id = null) |
27 | PointedEar | 252 | { |
253 | /* Apache module */ |
||
254 | $url = self::getParam('SCRIPT_URL', $_SERVER); |
||
255 | if ($url === null) |
||
256 | { |
||
257 | /* FastCGI */ |
||
258 | $url = self::getParam('URL', $_SERVER); |
||
259 | if ($url === null) |
||
260 | { |
||
31 | PointedEar | 261 | /* Server/PHP too old, compute URI */ |
262 | $url = self::getParam('REQUEST_URI', $_SERVER); |
||
52 | PointedEar | 263 | if (\preg_match('/^[^?]+/', $url, $matches) > 0) |
31 | PointedEar | 264 | { |
265 | $url = $matches[0]; |
||
266 | } |
||
267 | else |
||
268 | { |
||
269 | /* Has .php in it, but at least it works */ |
||
270 | $url = self::getParam('SCRIPT_NAME', $_SERVER); |
||
271 | if ($url === null) |
||
272 | { |
||
52 | PointedEar | 273 | throw new \Exception( |
31 | PointedEar | 274 | 'None of $_SERVER["SCRIPT_URL"], $_SERVER["URL"],' |
275 | . ' $_SERVER["REQUEST_URI"], or $_SERVER["SCRIPT_NAME"]' |
||
276 | . ' is available, cannot continue.'); |
||
277 | } |
||
278 | } |
||
27 | PointedEar | 279 | } |
280 | } |
||
45 | PointedEar | 281 | |
31 | PointedEar | 282 | $query = (($controller !== null) ? 'controller=' . $controller : '') |
283 | . (($action !== null) ? '&action=' . $action : '') |
||
284 | . (($id !== null) ? '&id=' . $id : ''); |
||
45 | PointedEar | 285 | |
27 | PointedEar | 286 | return $url . ($query ? '?' . $query : ''); |
287 | } |
||
45 | PointedEar | 288 | |
27 | PointedEar | 289 | /** |
290 | * Performs a server-side redirect within the application |
||
291 | */ |
||
52 | PointedEar | 292 | public static function redirect ($query = '') |
27 | PointedEar | 293 | { |
294 | $script_uri = self::getParam('SCRIPT_URI', $_SERVER); |
||
31 | PointedEar | 295 | if ($script_uri === null) |
27 | PointedEar | 296 | { |
297 | /* Server/PHP too old, compute URI */ |
||
52 | PointedEar | 298 | if (\preg_match('/^[^?]+/', |
27 | PointedEar | 299 | self::getParam('REQUEST_URI', $_SERVER), $matches) > 0) |
300 | { |
||
301 | $query_prefix = $matches[0]; |
||
302 | } |
||
303 | else |
||
304 | { |
||
305 | /* Has .php in it, but at least it works */ |
||
306 | $query_prefix = self::getParam('SCRIPT_NAME', $_SERVER); |
||
307 | } |
||
308 | |||
309 | /* TODO: Let user decide which ports map to which URI scheme */ |
||
310 | $script_uri = (self::getParam('SERVER_PORT', $_SERVER) == 443 |
||
311 | ? 'https://' |
||
312 | : 'http://') |
||
313 | . self::getParam('HTTP_HOST', $_SERVER) |
||
314 | . $query_prefix; |
||
315 | } |
||
316 | |||
52 | PointedEar | 317 | \header('Location: ' . $script_uri |
27 | PointedEar | 318 | . ($query ? (substr($query, 0, 1) === '?' ? '' : '?') . $query : '')); |
319 | } |
||
52 | PointedEar | 320 | } |