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