├── README.md ├── Slim ├── Exception │ ├── Pass.php │ ├── RequestSlash.php │ └── Stop.php ├── Http │ ├── Cookie.php │ ├── CookieJar.php │ ├── Request.php │ ├── Response.php │ └── Uri.php ├── Log.php ├── Logger.php ├── Route.php ├── Router.php ├── Session │ ├── Flash.php │ ├── Handler.php │ └── Handler │ │ └── Cookies.php ├── Slim.php └── View.php ├── aprs_func.php ├── index.php └── templates ├── main.php └── passcode.php /README.md: -------------------------------------------------------------------------------- 1 | PHP-APRS-Passcode 2 | ================= 3 | 4 | Technical example on how to generate APRS network passcodes required for mobile applications using PHP an [Example](http://apps.magicbug.co.uk/passcode/) is available to try it out. -------------------------------------------------------------------------------- /Slim/Exception/Pass.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Pass Exception 35 | * 36 | * This Exception will cause the Router::dispatch method 37 | * to skip the current matching route and continue to the next 38 | * matching route. If no subsequent routes are found, a 39 | * HTTP 404 Not Found response will be sent to the client. 40 | * 41 | * @package Slim 42 | * @author Josh Lockhart 43 | * @since Version 1.0 44 | */ 45 | class Slim_Exception_Pass extends Exception {} -------------------------------------------------------------------------------- /Slim/Exception/RequestSlash.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Request Slash Exception 35 | * 36 | * This Exception is thrown when Slim detects a matching route 37 | * (defined with a trailing slash) and the HTTP request 38 | * matches the route but does not have a trailing slash. This 39 | * exception will be caught in `Slim::run` and trigger a 301 redirect 40 | * to the same resource URI with a trailing slash. 41 | * 42 | * @package Slim 43 | * @author Josh Lockhart 44 | * @since Version 1.0 45 | */ 46 | class Slim_Exception_RequestSlash extends Exception {} -------------------------------------------------------------------------------- /Slim/Exception/Stop.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Stop Exception 35 | * 36 | * This Exception is thrown when the Slim application needs to abort 37 | * processing and return control flow to the outer PHP script. 38 | * 39 | * @package Slim 40 | * @author Josh Lockhart 41 | * @since Version 1.0 42 | */ 43 | class Slim_Exception_Stop extends Exception {} -------------------------------------------------------------------------------- /Slim/Http/Cookie.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Cookie 35 | * 36 | * Object-oriented representation of a Cookie to be sent in an HTTP response 37 | * 38 | * @package Slim 39 | * @author Josh Lockhart 40 | * @since Version 1.0 41 | */ 42 | class Slim_Http_Cookie { 43 | 44 | /** 45 | * @var string 46 | */ 47 | protected $name; 48 | 49 | /** 50 | * @var string 51 | */ 52 | protected $value; 53 | 54 | /** 55 | * @var int UNIX timestamp 56 | */ 57 | protected $expires; 58 | 59 | /** 60 | * @var string 61 | */ 62 | protected $path; 63 | 64 | /** 65 | * @var string 66 | */ 67 | protected $domain; 68 | 69 | /** 70 | * @var bool 71 | */ 72 | protected $secure; 73 | 74 | /** 75 | * @var bool 76 | */ 77 | protected $httponly; 78 | 79 | /** 80 | * Constructor 81 | * @param string $name The cookie name 82 | * @param string $value The cookie value 83 | * @param mixed $time The duration of the cookie; 84 | * If integer, should be a UNIX timestamp; 85 | * If string, converted to UNIX timestamp with `strtotime`; 86 | * @param string $path The path on the server in which the cookie will be available on 87 | * @param string $domain The domain that the cookie is available to 88 | * @param bool $secure Indicates that the cookie should only be transmitted over a secure 89 | * HTTPS connection from the client 90 | * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol 91 | * @return void 92 | */ 93 | public function __construct( $name, $value = null, $expires = 0, $path = null, $domain = null, $secure = false, $httponly = false ) { 94 | $this->setName($name); 95 | $this->setValue($value); 96 | $this->setExpires($expires); 97 | $this->setPath($path); 98 | $this->setDomain($domain); 99 | $this->setSecure($secure); 100 | $this->setHttpOnly($httponly); 101 | } 102 | 103 | /** 104 | * Get cookie name 105 | * @return string 106 | */ 107 | public function getName() { 108 | return $this->name; 109 | } 110 | 111 | /** 112 | * Set cookie name 113 | * @param string $name 114 | * @return void 115 | */ 116 | public function setName( $name ) { 117 | $this->name = (string)$name; 118 | } 119 | 120 | /** 121 | * Get cookie value 122 | * @return string 123 | */ 124 | public function getValue() { 125 | return $this->value; 126 | } 127 | 128 | /** 129 | * Set cookie value 130 | * @param string $value 131 | * @return void 132 | */ 133 | public function setValue( $value ) { 134 | $this->value = (string)$value; 135 | } 136 | 137 | /** 138 | * Get cookie expiration time 139 | * @return int UNIX timestamp 140 | */ 141 | public function getExpires() { 142 | return $this->expires; 143 | } 144 | 145 | /** 146 | * Set cookie expiration time 147 | * @param string|int Cookie expiration time 148 | * @return void 149 | */ 150 | public function setExpires( $time ) { 151 | $this->expires = is_string($time) ? strtotime($time) : (int)$time; 152 | } 153 | 154 | /** 155 | * Get cookie path 156 | * @return string 157 | */ 158 | public function getPath() { 159 | return $this->path; 160 | } 161 | 162 | /** 163 | * Set cookie path 164 | * @param string $path 165 | * @return void 166 | */ 167 | public function setPath( $path ) { 168 | $this->path = (string)$path; 169 | } 170 | 171 | /** 172 | * Get cookie domain 173 | * @return string 174 | */ 175 | public function getDomain() { 176 | return $this->domain; 177 | } 178 | 179 | /** 180 | * Set cookie domain 181 | * @param string $domain 182 | * @return void 183 | */ 184 | public function setDomain( $domain ) { 185 | $this->domain = (string)$domain; 186 | } 187 | 188 | /** 189 | * Is cookie sent only if SSL/HTTPS is used? 190 | * @return bool 191 | */ 192 | public function getSecure() { 193 | return $this->secure; 194 | } 195 | 196 | /** 197 | * Set whether cookie is sent only if SSL/HTTPS is used 198 | * @param bool $secure 199 | * @return void 200 | */ 201 | public function setSecure( $secure ) { 202 | $this->secure = (bool)$secure; 203 | } 204 | 205 | /** 206 | * Is cookie sent with HTTP protocol only? 207 | * @return bool 208 | */ 209 | public function getHttpOnly() { 210 | return $this->httponly; 211 | } 212 | 213 | /** 214 | * Set whether cookie is sent with HTTP protocol only 215 | * @param bool $httponly 216 | * @return void 217 | */ 218 | public function setHttpOnly( $httponly ) { 219 | $this->httponly = (bool)$httponly; 220 | } 221 | 222 | } -------------------------------------------------------------------------------- /Slim/Http/CookieJar.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Cooke Jar 35 | * 36 | * Used to manage signed, encrypted Cookies. Provides: 37 | * 38 | * - Cookie integrity and authenticity with HMAC 39 | * - Confidentiality with symmetric encryption 40 | * - Protection from replay attack if using SSL or TLS 41 | * - Protection from interception if using SSL or TLS 42 | * 43 | * This code was originally called "BigOrNot_CookieManager" and written by 44 | * Matthieu Huguet released under "CopyLeft" license. I have cleaned up the 45 | * code formatting to conform with Slim Framework contributor guidelines and 46 | * added additional code where necessary to play nice with Slim Cookie objects. 47 | * 48 | * Requirements: 49 | * 50 | * - libmcrypt > 2.4.x 51 | * 52 | * @author Matthies Huguet 53 | */ 54 | class Slim_Http_CookieJar { 55 | 56 | /** 57 | * @var string Server secret key 58 | */ 59 | protected $_secret = ''; 60 | 61 | /** 62 | * @var int Cryptographic algorithm used to encrypt cookies data 63 | */ 64 | protected $_algorithm = MCRYPT_RIJNDAEL_256; 65 | 66 | /** 67 | * @var int Cryptographic mode (CBC, CFB ...) 68 | */ 69 | protected $_mode = MCRYPT_MODE_CBC; 70 | 71 | /** 72 | * @var resource mcrypt module resource 73 | */ 74 | protected $_cryptModule = null; 75 | 76 | /** 77 | * @var bool Enable high confidentiality for cookie value (symmetric encryption) 78 | */ 79 | protected $_highConfidentiality = true; 80 | 81 | /** 82 | * @var bool Enable SSL support 83 | */ 84 | protected $_ssl = false; 85 | 86 | /** 87 | * @var array[Cookie] Cookie objects 88 | */ 89 | protected $_cookies = array(); 90 | 91 | /** 92 | * Constructor 93 | * 94 | * Initialize cookie manager and mcrypt module. 95 | * 96 | * @param string $secret Server's secret key 97 | * @param array $config 98 | * @throws Exception If secret key is empty 99 | * @throws Exception If unable to open mcypt module 100 | */ 101 | public function __construct( $secret, $config = null ) { 102 | if ( empty($secret) ) { 103 | throw new Exception('You must provide a secret key'); 104 | } 105 | $this->_secret = $secret; 106 | if ( $config !== null && !is_array($config) ) { 107 | throw new Exception('Config must be an array'); 108 | } 109 | if ( is_array($config) ) { 110 | if ( isset($config['high_confidentiality']) ) { 111 | $this->_highConfidentiality = $config['high_confidentiality']; 112 | } 113 | if ( isset($config['mcrypt_algorithm']) ) { 114 | $this->_algorithm = $config['mcrypt_algorithm']; 115 | } 116 | if ( isset($config['mcrypt_mode']) ) { 117 | $this->_mode = $config['mcrypt_mode']; 118 | } 119 | if ( isset($config['enable_ssl']) ) { 120 | $this->_ssl = $config['enable_ssl']; 121 | } 122 | } 123 | if ( extension_loaded('mcrypt') ) { 124 | $this->_cryptModule = mcrypt_module_open($this->_algorithm, '', $this->_mode, ''); 125 | if ( $this->_cryptModule === false ) { 126 | throw new Exception('Error while loading mcrypt module'); 127 | } 128 | } 129 | } 130 | 131 | /** 132 | * Get the high confidentiality mode 133 | * 134 | * @return bool TRUE if cookie data encryption is enabled, or FALSE if it isn't 135 | */ 136 | public function getHighConfidentiality() { 137 | return $this->_highConfidentiality; 138 | } 139 | 140 | /** 141 | * Enable or disable cookie data encryption 142 | * 143 | * @param bool $enable TRUE to enable, FALSE to disable 144 | * @return CookieJar 145 | */ 146 | public function setHighConfidentiality( $enable ) { 147 | $this->_highConfidentiality = (bool)$enable; 148 | return $this; 149 | } 150 | 151 | /** 152 | * Get the SSL status (enabled or disabled?) 153 | * 154 | * @return bool TRUE if SSL support is enabled, or FALSE if it isn't 155 | */ 156 | public function getSSL() { 157 | return $this->_ssl; 158 | } 159 | 160 | /** 161 | * Enable SSL support (not enabled by default) 162 | * 163 | * Pro: Protect against replay attack 164 | * Con: Cookie's lifetime is limited to SSL session's lifetime 165 | * 166 | * @param bool $enable TRUE to enable, FALSE to disable 167 | * @return CookieJar 168 | */ 169 | public function setSSL( $enable ) { 170 | $this->_ssl = (bool)$enable; 171 | return $this; 172 | } 173 | 174 | /** 175 | * Get Cookies for Response 176 | * 177 | * @author Josh Lockhart 178 | * @return array[Cookie] 179 | */ 180 | public function getResponseCookies() { 181 | return $this->_cookies; 182 | } 183 | 184 | /** 185 | * Get Cookie with name for Response 186 | * 187 | * @author Josh Lockhart 188 | * @param string $cookiename The name of the Cookie 189 | * @return Cookie|null Cookie, or NULL if Cookie with name not found 190 | */ 191 | public function getResponseCookie( $cookiename ) { 192 | return isset($this->_cookies[$cookiename]) ? $this->_cookies[$cookiename] : null; 193 | } 194 | 195 | /** 196 | * Set a secure cookie 197 | * 198 | * @param string $name Cookie name 199 | * @param string $value Cookie value 200 | * @param string $username User identifier 201 | * @param integer $expire Expiration time 202 | * @param string $path Cookie path 203 | * @param string $domain Cookie domain 204 | * @param bool $secure When TRUE, send the cookie only on a secure connection 205 | * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol 206 | */ 207 | public function setCookie( $cookiename, $value, $username, $expire = 0, $path = '/', $domain = '', $secure = false, $httponly = null ) { 208 | $secureValue = extension_loaded('mcrypt') ? $this->_secureCookieValue($value, $username, $expire) : $value; 209 | $this->setClassicCookie($cookiename, $secureValue, $expire, $path, $domain, $secure, $httponly); 210 | } 211 | 212 | /** 213 | * Delete a cookie 214 | * 215 | * @param string $name Cookie name 216 | * @param string $path Cookie path 217 | * @param string $domain Cookie domain 218 | * @param bool $secure When TRUE, send the cookie only on a secure connection 219 | * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol 220 | */ 221 | public function deleteCookie( $name, $path = '/', $domain = '', $secure = false, $httponly = null ) { 222 | $expire = 315554400; /* 1980-01-01 */ 223 | $this->_cookies[$name] = new Slim_Http_Cookie($name, '', $expire, $path, $domain, $secure, $httponly); 224 | //setcookie($name, '', $expire, $path, $domain, $secure, $httponly); 225 | } 226 | 227 | /** 228 | * Get a secure cookie value 229 | * 230 | * Verify the integrity of cookie data and decrypt it. If the cookie 231 | * is invalid, it can be automatically destroyed (default behaviour) 232 | * 233 | * @param string $cookiename Cookie name 234 | * @param bool $delete Destroy the cookie if invalid? 235 | * @return string|false The Cookie value, or FALSE if Cookie invalid 236 | */ 237 | public function getCookieValue( $cookiename, $deleteIfInvalid = true ) { 238 | if ( $this->cookieExists($cookiename) ) { 239 | if ( extension_loaded('mcrypt') ) { 240 | $cookieValues = explode('|', $_COOKIE[$cookiename]); 241 | if ( (count($cookieValues) === 4) && ($cookieValues[1] == 0 || $cookieValues[1] >= time()) ) { 242 | $key = hash_hmac('sha1', $cookieValues[0] . $cookieValues[1], $this->_secret); 243 | $cookieData = base64_decode($cookieValues[2]); 244 | if ( $cookieData !== '' && $this->getHighConfidentiality() ) { 245 | $data = $this->_decrypt($cookieData, $key, md5($cookieValues[1])); 246 | } else { 247 | $data = $cookieData; 248 | } 249 | if ( $this->_ssl && isset($_SERVER['SSL_SESSION_ID']) ) { 250 | $verifKey = hash_hmac('sha1', $cookieValues[0] . $cookieValues[1] . $data . $_SERVER['SSL_SESSION_ID'], $key); 251 | } else { 252 | $verifKey = hash_hmac('sha1', $cookieValues[0] . $cookieValues[1] . $data, $key); 253 | } 254 | if ( $verifKey == $cookieValues[3] ) { 255 | return $data; 256 | } 257 | } 258 | } else { 259 | return $_COOKIE[$cookiename]; 260 | } 261 | } 262 | if ( $deleteIfInvalid ) { 263 | $this->deleteCookie($cookiename); 264 | } 265 | return false; 266 | } 267 | 268 | /** 269 | * Send a classic (unsecure) cookie 270 | * 271 | * @param string $name Cookie name 272 | * @param string $value Cookie value 273 | * @param integer $expire Expiration time 274 | * @param string $path Cookie path 275 | * @param string $domain Cookie domain 276 | * @param bool $secure When TRUE, send the cookie only on a secure connection 277 | * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol 278 | */ 279 | public function setClassicCookie( $cookiename, $value, $expire = 0, $path = '/', $domain = '', $secure = false, $httponly = null ) { 280 | /* httponly option is only available for PHP version >= 5.2 */ 281 | if ( $httponly === null ) { 282 | $this->_cookies[$cookiename] = new Slim_Http_Cookie($cookiename, $value, $expire, $path, $domain, $secure); 283 | //setcookie($cookiename, $value, $expire, $path, $domain, $secure); 284 | } else { 285 | $this->_cookies[$cookiename] = new Slim_Http_Cookie($cookiename, $value, $expire, $path, $domain, $secure, $httponly); 286 | //setcookie($cookiename, $value, $expire, $path, $domain, $secure, $httponly); 287 | } 288 | } 289 | 290 | /** 291 | * Verify if a cookie exists 292 | * 293 | * @param string $cookiename 294 | * @return bool TRUE if cookie exist, or FALSE if not 295 | */ 296 | public function cookieExists($cookiename) { 297 | return isset($_COOKIE[$cookiename]); 298 | } 299 | 300 | /** 301 | * Secure a cookie value 302 | * 303 | * The initial value is transformed with this protocol: 304 | * 305 | * secureValue = username|expire|base64((value)k,expire)|HMAC(user|expire|value,k) 306 | * where k = HMAC(user|expire, sk) 307 | * and sk is server's secret key 308 | * (value)k,md5(expire) is the result an cryptographic function (ex: AES256) on "value" with key k and initialisation vector = md5(expire) 309 | * 310 | * @param string $value Unsecure value 311 | * @param string $username User identifier 312 | * @param integer $expire Expiration time 313 | * @return string Secured value 314 | */ 315 | protected function _secureCookieValue( $value, $username, $expire ) { 316 | if ( is_string($expire) ) { 317 | $expire = strtotime($expire); 318 | } 319 | $key = hash_hmac('sha1', $username . $expire, $this->_secret); 320 | if ( $value !== '' && $this->getHighConfidentiality() ) { 321 | $encryptedValue = base64_encode($this->_encrypt($value, $key, md5($expire))); 322 | } else { 323 | $encryptedValue = base64_encode($value); 324 | } 325 | if ( $this->_ssl && isset($_SERVER['SSL_SESSION_ID']) ) { 326 | $verifKey = hash_hmac('sha1', $username . $expire . $value . $_SERVER['SSL_SESSION_ID'], $key); 327 | } else { 328 | $verifKey = hash_hmac('sha1', $username . $expire . $value, $key); 329 | } 330 | $result = array($username, $expire, $encryptedValue, $verifKey); 331 | return implode('|', $result); 332 | } 333 | 334 | /** 335 | * Encrypt a given data with a given key and a given initialisation vector 336 | * 337 | * @param string $data Data to crypt 338 | * @param string $key Secret key 339 | * @param string $iv Initialisation vector 340 | * @return string Encrypted data 341 | */ 342 | protected function _encrypt( $data, $key, $iv ) { 343 | $iv = $this->_validateIv($iv); 344 | $key = $this->_validateKey($key); 345 | mcrypt_generic_init($this->_cryptModule, $key, $iv); 346 | $res = @mcrypt_generic($this->_cryptModule, $data); 347 | mcrypt_generic_deinit($this->_cryptModule); 348 | return $res; 349 | } 350 | 351 | /** 352 | * Decrypt a given data with a given key and a given initialisation vector 353 | * 354 | * @param string $data Data to crypt 355 | * @param string $key Secret key 356 | * @param string $iv Initialisation vector 357 | * @return string Encrypted data 358 | */ 359 | protected function _decrypt( $data, $key, $iv ) { 360 | $iv = $this->_validateIv($iv); 361 | $key = $this->_validateKey($key); 362 | mcrypt_generic_init($this->_cryptModule, $key, $iv); 363 | $decryptedData = mdecrypt_generic($this->_cryptModule, $data); 364 | $res = str_replace("\x0", '', $decryptedData); 365 | mcrypt_generic_deinit($this->_cryptModule); 366 | return $res; 367 | } 368 | 369 | /** 370 | * Validate Initialization vector 371 | * 372 | * If given IV is too long for the selected mcrypt algorithm, it will be truncated 373 | * 374 | * @param string $iv Initialization vector 375 | * @return string 376 | */ 377 | protected function _validateIv($iv) { 378 | $ivSize = mcrypt_enc_get_iv_size($this->_cryptModule); 379 | if ( strlen($iv) > $ivSize ) { 380 | $iv = substr($iv, 0, $ivSize); 381 | } 382 | return $iv; 383 | } 384 | 385 | /** 386 | * Validate key 387 | * 388 | * If given key is too long for the selected mcrypt algorithm, it will be truncated 389 | * 390 | * @param string $key key 391 | * @param string 392 | */ 393 | protected function _validateKey($key) { 394 | $keySize = mcrypt_enc_get_key_size($this->_cryptModule); 395 | if ( strlen($key) > $keySize ) { 396 | $key = substr($key, 0, $keySize); 397 | } 398 | return $key; 399 | } 400 | 401 | } 402 | -------------------------------------------------------------------------------- /Slim/Http/Request.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Request 35 | * 36 | * Object-oriented representation of an HTTP request. This class 37 | * is responsible for parsing the raw HTTP request into a format 38 | * usable by the Slim application. 39 | * 40 | * This class will automatically remove slashes from GET, POST, PUT, 41 | * and Cookie data if magic quotes are enabled. 42 | * 43 | * @package Slim 44 | * @author Josh Lockhart 45 | * @author Kris Jordan 46 | * @since Version 1.0 47 | */ 48 | class Slim_Http_Request { 49 | 50 | const METHOD_HEAD = 'HEAD'; 51 | const METHOD_GET = 'GET'; 52 | const METHOD_POST = 'POST'; 53 | const METHOD_PUT = 'PUT'; 54 | const METHOD_DELETE = 'DELETE'; 55 | const METHOD_OPTIONS = 'OPTIONS'; 56 | const METHOD_OVERRIDE = '_METHOD'; 57 | 58 | /** 59 | * @var string Request method (ie. "GET", "POST", "PUT", "DELETE", "HEAD") 60 | */ 61 | protected $method; 62 | 63 | /** 64 | * @var array Key-value array of HTTP request headers 65 | */ 66 | protected $headers; 67 | 68 | /** 69 | * @var array Names of additional headers to parse from the current 70 | * HTTP request that are not prefixed with "HTTP_" 71 | */ 72 | protected $additionalHeaders = array('content-type', 'content-length', 'php-auth-user', 'php-auth-pw', 'auth-type', 'x-requested-with'); 73 | 74 | /** 75 | * @var array Key-value array of cookies sent with the 76 | * current HTTP request 77 | */ 78 | protected $cookies; 79 | 80 | /** 81 | * @var array Key-value array of HTTP GET parameters 82 | */ 83 | protected $get; 84 | 85 | /** 86 | * @var array Key-value array of HTTP POST parameters 87 | */ 88 | protected $post; 89 | 90 | /** 91 | * @var array Key-value array of HTTP PUT parameters 92 | */ 93 | protected $put; 94 | 95 | /** 96 | * @var string Raw body of HTTP request 97 | */ 98 | protected $body; 99 | 100 | /** 101 | * @var string Content type of HTTP request 102 | */ 103 | protected $contentType; 104 | 105 | /** 106 | * @var string Resource URI (ie. "/person/1") 107 | */ 108 | protected $resource; 109 | 110 | /** 111 | * @var string The root URI of the Slim application without trailing slash. 112 | * This will be "" if the app is installed at the web 113 | * document root. If the app is installed in a 114 | * sub-directory "/foo", this will be "/foo". 115 | */ 116 | protected $root; 117 | 118 | /** 119 | * Constructor 120 | */ 121 | public function __construct() { 122 | $this->method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : false; 123 | $this->headers = $this->loadHttpHeaders(); 124 | $this->body = @file_get_contents('php://input'); 125 | $this->get = self::stripSlashesIfMagicQuotes($_GET); 126 | $this->post = self::stripSlashesIfMagicQuotes($_POST); 127 | $this->put = self::stripSlashesIfMagicQuotes($this->loadPutParameters()); 128 | $this->cookies = self::stripSlashesIfMagicQuotes($_COOKIE); 129 | $this->root = Slim_Http_Uri::getBaseUri(true); 130 | $this->resource = Slim_Http_Uri::getUri(true); 131 | $this->checkForHttpMethodOverride(); 132 | } 133 | 134 | /** 135 | * Is this a GET request? 136 | * @return bool 137 | */ 138 | public function isGet() { 139 | return $this->method === self::METHOD_GET; 140 | } 141 | 142 | /** 143 | * Is this a POST request? 144 | * @return bool 145 | */ 146 | public function isPost() { 147 | return $this->method === self::METHOD_POST; 148 | } 149 | 150 | /** 151 | * Is this a PUT request? 152 | * @return bool 153 | */ 154 | public function isPut() { 155 | return $this->method === self::METHOD_PUT; 156 | } 157 | 158 | /** 159 | * Is this a DELETE request? 160 | * @return bool 161 | */ 162 | public function isDelete() { 163 | return $this->method === self::METHOD_DELETE; 164 | } 165 | 166 | /** 167 | * Is this a HEAD request? 168 | * @return bool 169 | */ 170 | public function isHead() { 171 | return $this->method === self::METHOD_HEAD; 172 | } 173 | 174 | /** 175 | * Is this a OPTIONS request? 176 | * @return bool 177 | */ 178 | public function isOptions() { 179 | return $this->method === self::METHOD_OPTIONS; 180 | } 181 | 182 | /** 183 | * Is this a XHR request? 184 | * @return bool 185 | */ 186 | public function isAjax() { 187 | return ( $this->params('isajax') || $this->headers('X_REQUESTED_WITH') === 'XMLHttpRequest' ); 188 | } 189 | 190 | /** 191 | * Fetch a PUT|POST|GET parameter value 192 | * 193 | * The preferred method to fetch the value of a 194 | * PUT, POST, or GET parameter (searched in that order). 195 | * 196 | * @param string $key The paramter name 197 | * @return string|null The value of parameter, or NULL if parameter not found 198 | */ 199 | public function params( $key ) { 200 | foreach ( array('put', 'post', 'get') as $dataSource ) { 201 | $source = $this->$dataSource; 202 | if ( isset($source[(string)$key]) ) { 203 | return $source[(string)$key]; 204 | } 205 | } 206 | return null; 207 | } 208 | 209 | /** 210 | * Fetch GET parameter(s) 211 | * @param string $key Name of parameter 212 | * @return array|string|null All parameters, parameter value if $key 213 | * and parameter exists, or NULL if $key 214 | * and parameter does not exist. 215 | */ 216 | public function get( $key = null ) { 217 | return $this->arrayOrArrayValue($this->get, $key); 218 | } 219 | 220 | /** 221 | * Fetch POST parameter(s) 222 | * @param string $key Name of parameter 223 | * @return array|string|null All parameters, parameter value if $key 224 | * and parameter exists, or NULL if $key 225 | * and parameter does not exist. 226 | */ 227 | public function post( $key = null ) { 228 | return $this->arrayOrArrayValue($this->post, $key); 229 | } 230 | 231 | /** 232 | * Fetch PUT parameter(s) 233 | * @param string $key Name of parameter 234 | * @return array|string|null All parameters, parameter value if $key 235 | * and parameter exists, or NULL if $key 236 | * and parameter does not exist. 237 | */ 238 | public function put( $key = null ) { 239 | return $this->arrayOrArrayValue($this->put, $key); 240 | } 241 | 242 | /** 243 | * Fetch COOKIE value(s) 244 | * @param string $key The cookie name 245 | * @return array|string|null All parameters, parameter value if $key 246 | * and parameter exists, or NULL if $key 247 | * and parameter does not exist. 248 | */ 249 | public function cookies( $key = null ) { 250 | return $this->arrayOrArrayValue($this->cookies, $key); 251 | } 252 | 253 | /** 254 | * Get HTTP request header 255 | * @param string $key The header name 256 | * @return array|string|null All parameters, parameter value if $key 257 | * and parameter exists, or NULL if $key 258 | * and parameter does not exist. 259 | */ 260 | public function headers( $key = null ) { 261 | return is_null($key) ? $this->headers : $this->arrayOrArrayValue($this->headers, $this->convertHttpHeaderName($key)); 262 | } 263 | 264 | /** 265 | * Get HTTP request body 266 | * @return string|false String, or FALSE if body could not be read 267 | */ 268 | public function getBody() { 269 | return $this->body; 270 | } 271 | 272 | /** 273 | * Get HTTP method 274 | * @return string 275 | */ 276 | public function getMethod() { 277 | return $this->method; 278 | } 279 | 280 | /** 281 | * Get HTTP request content type 282 | * @return string 283 | */ 284 | public function getContentType() { 285 | if ( !isset($this->contentType) ) { 286 | $contentType = 'application/x-www-form-urlencoded'; 287 | $header = $this->headers('CONTENT_TYPE'); 288 | if ( !is_null($header) ) { 289 | $headerParts = preg_split('/\s*;\s*/', $header); 290 | $contentType = $headerParts[0]; 291 | } 292 | $this->contentType = $contentType; 293 | } 294 | return $this->contentType; 295 | } 296 | 297 | /** 298 | * Get HTTP request resource URI 299 | * @return string 300 | */ 301 | public function getResourceUri() { 302 | return $this->resource; 303 | } 304 | 305 | /** 306 | * Get HTTP request root URI 307 | * @return string 308 | */ 309 | public function getRootUri() { 310 | return $this->root; 311 | } 312 | 313 | /** 314 | * Fetch array or array value 315 | * @param array $array 316 | * @param string $key 317 | * @return array|mixed Array if key is null, else array value 318 | */ 319 | protected function arrayOrArrayValue( array &$array, $key = null ) { 320 | return is_null($key) ? $array : $this->arrayValueForKey($array, $key); 321 | } 322 | 323 | /** 324 | * Fetch value from array 325 | * @return mixed|null 326 | */ 327 | protected function arrayValueForKey( array &$array, $key ) { 328 | return isset($array[(string)$key]) ? $array[(string)$key] : null; 329 | } 330 | 331 | /** 332 | * Strip slashes from string or array of strings 333 | * @param array|string $rawData 334 | * @return array|string 335 | */ 336 | public static function stripSlashesIfMagicQuotes( $rawData ) { 337 | if ( get_magic_quotes_gpc() ) { 338 | return is_array($rawData) ? array_map(array('self', 'stripSlashesIfMagicQuotes'), $rawData) : stripslashes($rawData); 339 | } else { 340 | return $rawData; 341 | } 342 | } 343 | 344 | /** 345 | * Get PUT parameters 346 | * @return array Key-value array of HTTP request PUT parameters 347 | */ 348 | protected function loadPutParameters() { 349 | if ( $this->getContentType() === 'application/x-www-form-urlencoded' ) { 350 | $input = is_string($this->body) ? $this->body : ''; 351 | if ( function_exists('mb_parse_str') ) { 352 | mb_parse_str($input, $output); 353 | } else { 354 | parse_str($input, $output); 355 | } 356 | return $output; 357 | } else { 358 | return array(); 359 | } 360 | } 361 | 362 | /** 363 | * Get HTTP request headers 364 | * @return array Key-value array of HTTP request headers 365 | */ 366 | protected function loadHttpHeaders() { 367 | $headers = array(); 368 | foreach ( $_SERVER as $key => $value ) { 369 | $key = $this->convertHttpHeaderName($key); 370 | if ( strpos($key, 'http-') === 0 || in_array($key, $this->additionalHeaders) ) { 371 | $name = str_replace('http-', '', $key); 372 | $headers[$name] = $value; 373 | } 374 | } 375 | return $headers; 376 | } 377 | 378 | /** 379 | * Convert HTTP header name 380 | * @return string 381 | */ 382 | protected function convertHttpHeaderName( $name ) { 383 | return str_replace('_', '-', strtolower($name)); 384 | } 385 | 386 | /** 387 | * Check for HTTP request method override 388 | * 389 | * Because traditional web browsers do not support PUT and DELETE 390 | * HTTP methods, we use a hidden form input field to 391 | * mimic PUT and DELETE requests. We check for this override here. 392 | * 393 | * @return void 394 | */ 395 | protected function checkForHttpMethodOverride() { 396 | if ( isset($this->post[self::METHOD_OVERRIDE]) ) { 397 | $this->method = $this->post[self::METHOD_OVERRIDE]; 398 | unset($this->post[self::METHOD_OVERRIDE]); 399 | if ( $this->isPut() ) { 400 | $this->put = $this->post; 401 | } 402 | } 403 | } 404 | 405 | } -------------------------------------------------------------------------------- /Slim/Http/Response.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Response 35 | * 36 | * Object-oriented representation of an HTTP response that is 37 | * returned to the client. This class is responsible for: 38 | * 39 | * - HTTP response status 40 | * - HTTP response body 41 | * - HTTP response headers 42 | * - HTTP response cookies 43 | * 44 | * @package Slim 45 | * @author Josh Lockhart 46 | * @author Kris Jordan 47 | * @since Version 1.0 48 | */ 49 | class Slim_Http_Response { 50 | 51 | /** 52 | * @var Slim_Http_Request 53 | */ 54 | protected $request; 55 | 56 | /** 57 | * @var string 58 | */ 59 | protected $httpVersion = '1.1'; 60 | 61 | /** 62 | * @var int HTTP status code 63 | */ 64 | protected $status = 200; 65 | 66 | /** 67 | * @var array Key-value array of HTTP response headers 68 | */ 69 | protected $headers = array(); 70 | 71 | /** 72 | * @var string HTTP response body 73 | */ 74 | protected $body = ''; 75 | 76 | /** 77 | * @var int Length of HTTP response body 78 | */ 79 | protected $length = 0; 80 | 81 | /** 82 | * @var array HTTP response codes and messages 83 | */ 84 | protected static $messages = array( 85 | //Informational 1xx 86 | 100 => '100 Continue', 87 | 101 => '101 Switching Protocols', 88 | //Successful 2xx 89 | 200 => '200 OK', 90 | 201 => '201 Created', 91 | 202 => '202 Accepted', 92 | 203 => '203 Non-Authoritative Information', 93 | 204 => '204 No Content', 94 | 205 => '205 Reset Content', 95 | 206 => '206 Partial Content', 96 | //Redirection 3xx 97 | 300 => '300 Multiple Choices', 98 | 301 => '301 Moved Permanently', 99 | 302 => '302 Found', 100 | 303 => '303 See Other', 101 | 304 => '304 Not Modified', 102 | 305 => '305 Use Proxy', 103 | 306 => '306 (Unused)', 104 | 307 => '307 Temporary Redirect', 105 | //Client Error 4xx 106 | 400 => '400 Bad Request', 107 | 401 => '401 Unauthorized', 108 | 402 => '402 Payment Required', 109 | 403 => '403 Forbidden', 110 | 404 => '404 Not Found', 111 | 405 => '405 Method Not Allowed', 112 | 406 => '406 Not Acceptable', 113 | 407 => '407 Proxy Authentication Required', 114 | 408 => '408 Request Timeout', 115 | 409 => '409 Conflict', 116 | 410 => '410 Gone', 117 | 411 => '411 Length Required', 118 | 412 => '412 Precondition Failed', 119 | 413 => '413 Request Entity Too Large', 120 | 414 => '414 Request-URI Too Long', 121 | 415 => '415 Unsupported Media Type', 122 | 416 => '416 Requested Range Not Satisfiable', 123 | 417 => '417 Expectation Failed', 124 | 422 => '422 Unprocessable Entity', 125 | 423 => '423 Locked', 126 | //Server Error 5xx 127 | 500 => '500 Internal Server Error', 128 | 501 => '501 Not Implemented', 129 | 502 => '502 Bad Gateway', 130 | 503 => '503 Service Unavailable', 131 | 504 => '504 Gateway Timeout', 132 | 505 => '505 HTTP Version Not Supported' 133 | ); 134 | 135 | /** 136 | * @var CookieJar Manages Cookies to be sent with this Response 137 | */ 138 | protected $cookieJar; 139 | 140 | /** 141 | * Constructor 142 | */ 143 | public function __construct( Slim_Http_Request $req ) { 144 | $this->request = $req; 145 | $this->header('Content-Type', 'text/html'); 146 | } 147 | 148 | /** 149 | * Set and/or get the HTTP response version 150 | * @param string $version 151 | * @return void 152 | * @throws InvalidArgumentException If argument is not a valid HTTP version 153 | */ 154 | public function httpVersion( $version = null ) { 155 | if ( $version ) { 156 | $version = (string)$version; 157 | if ( $version === '1.0' || $version === '1.1' ) { 158 | $this->httpVersion = $version; 159 | } else { 160 | throw new InvalidArgumentException('Invalid HTTP version in Response object'); 161 | } 162 | } 163 | return $this->httpVersion; 164 | } 165 | 166 | /** 167 | * Set and/or get the HTTP response status code 168 | * @param int $status 169 | * @return int 170 | * @throws InvalidArgumentException If argument is not a valid HTTP status code 171 | */ 172 | public function status( $status = null ) { 173 | if ( !is_null($status) ) { 174 | if ( !in_array(intval($status), array_keys(self::$messages)) ) { 175 | throw new InvalidArgumentException('Cannot set Response status. Provided status code "' . $status . '" is not a valid HTTP response code.'); 176 | } 177 | $this->status = intval($status); 178 | } 179 | return $this->status; 180 | } 181 | 182 | /** 183 | * Get HTTP response headers 184 | * @return array 185 | */ 186 | public function headers() { 187 | return $this->headers; 188 | } 189 | 190 | /** 191 | * Get and/or set an HTTP response header 192 | * @param string $key The header name 193 | * @param string $value The header value 194 | * @return string|null The header value, or NULL if header not set 195 | */ 196 | public function header( $key, $value = null ) { 197 | if ( !is_null($value) ) { 198 | $this->headers[$key] = $value; 199 | } 200 | return isset($this->headers[$key]) ? $this->headers[$key] : null; 201 | } 202 | 203 | /** 204 | * Set the HTTP response body 205 | * @param string $body The new HTTP response body 206 | * @return string The new HTTP response body 207 | */ 208 | public function body( $body = null ) { 209 | if ( !is_null($body) ) { 210 | $this->body = ''; 211 | $this->length = 0; 212 | $this->write($body); 213 | } 214 | return $this->body; 215 | } 216 | 217 | /** 218 | * Append the HTTP response body 219 | * @param string $body Content to append to the current HTTP response body 220 | * @return string The updated HTTP response body 221 | */ 222 | public function write( $body ) { 223 | $body = (string)$body; 224 | $this->length += strlen($body); 225 | $this->body .= $body; 226 | $this->header('Content-Length', $this->length); 227 | return $body; 228 | } 229 | 230 | /** 231 | * Set cookie jar 232 | * @param Slim_Http_CookieJar $cookieJar 233 | * @return void 234 | */ 235 | public function setCookieJar( Slim_Http_CookieJar $cookieJar ) { 236 | $this->cookieJar = $cookieJar; 237 | } 238 | 239 | /** 240 | * Get cookie jar 241 | * @return Slim_Http_CookieJar 242 | */ 243 | public function getCookieJar() { 244 | return $this->cookieJar; 245 | } 246 | 247 | /** 248 | * Finalize response headers before response is sent 249 | * @return void 250 | */ 251 | public function finalize() { 252 | if ( in_array($this->status, array(204, 304)) ) { 253 | $this->body(''); 254 | unset($this->headers['Content-Type']); 255 | } 256 | } 257 | 258 | /** 259 | * Get message for HTTP status code 260 | * @return string|null 261 | */ 262 | public static function getMessageForCode( $status ) { 263 | return isset(self::$messages[$status]) ? self::$messages[$status] : null; 264 | } 265 | 266 | /** 267 | * Can this HTTP response have a body? 268 | * @return bool 269 | */ 270 | public function canHaveBody() { 271 | return ( $this->status < 100 || $this->status >= 200 ) && $this->status != 204 && $this->status != 304; 272 | } 273 | 274 | /** 275 | * Send headers for HTTP response 276 | * @return void 277 | */ 278 | protected function sendHeaders() { 279 | //Finalize response 280 | $this->finalize(); 281 | 282 | if ( substr(PHP_SAPI, 0, 3) === 'cgi') { 283 | //Send Status header if running with fastcgi 284 | header('Status: ' . self::getMessageForCode($this->status())); 285 | } else { 286 | //Else send HTTP message 287 | header(sprintf('HTTP/%s %s', $this->httpVersion, self::getMessageForCode($this->status()))); 288 | } 289 | 290 | //Send headers 291 | foreach ( $this->headers() as $name => $value ) { 292 | header("$name: $value"); 293 | } 294 | 295 | //Send cookies 296 | foreach ( $this->getCookieJar()->getResponseCookies() as $name => $cookie ) { 297 | setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpires(), $cookie->getPath(), $cookie->getDomain(), $cookie->getSecure(), $cookie->getHttpOnly()); 298 | } 299 | 300 | //Flush all output to client 301 | flush(); 302 | } 303 | 304 | /** 305 | * Send HTTP response 306 | * 307 | * This method will set Response headers, set Response cookies, 308 | * and `echo` the Response body to the current output buffer. 309 | * 310 | * @return void 311 | */ 312 | public function send() { 313 | if ( !headers_sent() ) { 314 | $this->sendHeaders(); 315 | } 316 | if ( $this->canHaveBody() && $this->request->isHead() === false ) { 317 | echo $this->body; 318 | } 319 | } 320 | 321 | } -------------------------------------------------------------------------------- /Slim/Http/Uri.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Uri 35 | * 36 | * Parses base uri and application uri from Request. 37 | * 38 | * @package Slim 39 | * @author Josh Lockhart 40 | * @since Version 1.0 41 | */ 42 | class Slim_Http_Uri { 43 | 44 | /** 45 | * @var string "https" or "http" 46 | */ 47 | protected static $scheme; 48 | 49 | /** 50 | * @var string 51 | */ 52 | protected static $baseUri; 53 | 54 | /** 55 | * @var string 56 | */ 57 | protected static $uri; 58 | 59 | /** 60 | * @var string The URI query string, excluding leading "?" 61 | */ 62 | protected static $queryString; 63 | 64 | /** 65 | * Get Base URI without trailing slash 66 | * @param bool $reload Force reparse the base URI? 67 | * @return string 68 | */ 69 | public static function getBaseUri( $reload = false ) { 70 | if ( $reload || is_null(self::$baseUri) ) { 71 | $requestUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : $_SERVER['PHP_SELF']; //Full Request URI 72 | $scriptName = $_SERVER['SCRIPT_NAME']; //Script path from docroot 73 | $baseUri = strpos($requestUri, $scriptName) === 0 ? $scriptName : str_replace('\\', '/', dirname($scriptName)); 74 | self::$baseUri = rtrim($baseUri, '/'); 75 | } 76 | return self::$baseUri; 77 | } 78 | 79 | /** 80 | * Get URI with leading slash 81 | * @param bool $reload Force reparse the URI? 82 | * @return string 83 | * @throws RuntimeException If unable if unable to determine URI 84 | */ 85 | public static function getUri( $reload = false ) { 86 | if ( $reload || is_null(self::$uri) ) { 87 | $uri = ''; 88 | if ( !empty($_SERVER['PATH_INFO']) ) { 89 | $uri = $_SERVER['PATH_INFO']; 90 | } else { 91 | if ( isset($_SERVER['REQUEST_URI']) ) { 92 | $uri = parse_url(self::getScheme() . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], PHP_URL_PATH); 93 | } else if ( isset($_SERVER['PHP_SELF']) ) { 94 | $uri = $_SERVER['PHP_SELF']; 95 | } else { 96 | throw new RuntimeException('Unable to detect request URI'); 97 | } 98 | } 99 | if ( self::getBaseUri() !== '' && strpos($uri, self::getBaseUri()) === 0 ) { 100 | $uri = substr($uri, strlen(self::getBaseUri())); 101 | } 102 | self::$uri = '/' . ltrim($uri, '/'); 103 | } 104 | return self::$uri; 105 | } 106 | 107 | /** 108 | * Get URI Scheme 109 | * @param bool $reload For reparse the URL scheme? 110 | * @return string "https" or "http" 111 | */ 112 | public static function getScheme( $reload = false ) { 113 | if ( $reload || is_null(self::$scheme) ) { 114 | self::$scheme = ( empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off' ) ? 'http' : 'https'; 115 | } 116 | return self::$scheme; 117 | } 118 | 119 | /** 120 | * Get URI Query String 121 | * @param bool $reload For reparse the URL query string? 122 | * @return string 123 | */ 124 | public static function getQueryString( $reload = false ) { 125 | if ( $reload || is_null(self::$queryString) ) { 126 | self::$queryString = $_SERVER['QUERY_STRING']; 127 | } 128 | return self::$queryString; 129 | } 130 | 131 | } -------------------------------------------------------------------------------- /Slim/Log.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Log Adapter 35 | * 36 | * This is an adapter for your own custom Logger. This adapter assumes 37 | * your custom Logger provides the following public instance methods: 38 | * 39 | * debug( mixed $object ) 40 | * info( mixed $object ) 41 | * warn( mixed $object ) 42 | * error( mixed $object ) 43 | * fatal( mixed $object ) 44 | * 45 | * This class assumes nothing else about your custom Logger, so you are free 46 | * to use Apache's Log4PHP logger or any other log class that, at the 47 | * very least, implements the five public instance methods shown above. 48 | * 49 | * @package Slim 50 | * @author Josh Lockhart 51 | * @since Version 1.0 52 | */ 53 | class Slim_Log { 54 | 55 | /** 56 | * @var mixed An object that implements expected Logger interface 57 | */ 58 | protected $logger; 59 | 60 | /** 61 | * @var bool Enable logging? 62 | */ 63 | protected $enabled; 64 | 65 | /** 66 | * Constructor 67 | */ 68 | public function __construct() { 69 | $this->enabled = true; 70 | } 71 | 72 | /** 73 | * Enable or disable logging 74 | * @param bool $enabled 75 | * @return void 76 | */ 77 | public function setEnabled( $enabled ) { 78 | if ( $enabled ) { 79 | $this->enabled = true; 80 | } else { 81 | $this->enabled = false; 82 | } 83 | } 84 | 85 | /** 86 | * Is logging enabled? 87 | * @return bool 88 | */ 89 | public function isEnabled() { 90 | return $this->enabled; 91 | } 92 | 93 | /** 94 | * Log debug message 95 | * @param mixed $object 96 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 97 | */ 98 | public function debug( $object ) { 99 | return isset($this->logger) && $this->isEnabled() ? $this->logger->debug($object) : false; 100 | } 101 | 102 | /** 103 | * Log info message 104 | * @param mixed $object 105 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 106 | */ 107 | public function info( $object ) { 108 | return isset($this->logger) && $this->isEnabled() ? $this->logger->info($object) : false; 109 | } 110 | 111 | /** 112 | * Log warn message 113 | * @param mixed $object 114 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 115 | */ 116 | public function warn( $object ) { 117 | return isset($this->logger) && $this->isEnabled() ? $this->logger->warn($object) : false; 118 | } 119 | 120 | /** 121 | * Log error message 122 | * @param mixed $object 123 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 124 | */ 125 | public function error( $object ) { 126 | return isset($this->logger) && $this->isEnabled() ? $this->logger->error($object) : false; 127 | } 128 | 129 | /** 130 | * Log fatal message 131 | * @param mixed $object 132 | * @return mixed|false What the Logger returns, or false if Logger not set or not enabled 133 | */ 134 | public function fatal( $object ) { 135 | return isset($this->logger) && $this->isEnabled() ? $this->logger->fatal($object) : false; 136 | } 137 | 138 | /** 139 | * Set Logger 140 | * @param mixed $logger 141 | * @return void 142 | */ 143 | public function setLogger( $logger ) { 144 | $this->logger = $logger; 145 | } 146 | 147 | /** 148 | * Get Logger 149 | * @return mixed 150 | */ 151 | public function getLogger() { 152 | return $this->logger; 153 | } 154 | 155 | } -------------------------------------------------------------------------------- /Slim/Logger.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Logger 35 | * 36 | * A simple Logger that writes to a daily-unique log file in 37 | * a user-specified directory. By default, this class will write log 38 | * messages for all log levels; the log level may be changed to filter 39 | * unwanted log messages from the log file. 40 | * 41 | * @package Slim 42 | * @author Josh Lockhart 43 | * @since Version 1.0 44 | */ 45 | class Slim_Logger { 46 | 47 | /** 48 | * @var array Log levels 49 | */ 50 | protected $levels = array( 51 | 0 => 'FATAL', 52 | 1 => 'ERROR', 53 | 2 => 'WARN', 54 | 3 => 'INFO', 55 | 4 => 'DEBUG' 56 | ); 57 | 58 | /** 59 | * @var string Absolute path to log directory with trailing slash 60 | */ 61 | protected $directory; 62 | 63 | /** 64 | * Constructor 65 | * @param string $directory Absolute or relative path to log directory 66 | * @param int $level The maximum log level reported by this Logger 67 | */ 68 | public function __construct( $directory, $level = 4 ) { 69 | $this->setDirectory($directory); 70 | $this->setLevel($level); 71 | } 72 | 73 | /** 74 | * Set log directory 75 | * @param string $directory Absolute or relative path to log directory 76 | * @return void 77 | */ 78 | public function setDirectory( $directory ) { 79 | $realPath = realpath($directory); 80 | if ( $realPath ) { 81 | $this->directory = rtrim($realPath, '/') . '/'; 82 | } else { 83 | $this->directory = false; 84 | } 85 | } 86 | 87 | /** 88 | * Get log directory 89 | * @return string|false Absolute path to log directory with trailing slash 90 | */ 91 | public function getDirectory() { 92 | return $this->directory; 93 | } 94 | 95 | /** 96 | * Set log level 97 | * @param int The maximum log level reported by this Logger 98 | * @return void 99 | * @throws InvalidArgumentException If level specified is not 0, 1, 2, 3, 4 100 | */ 101 | public function setLevel( $level ) { 102 | $theLevel = (int)$level; 103 | if ( $theLevel >= 0 && $theLevel <= 4 ) { 104 | $this->level = $theLevel; 105 | } else { 106 | throw new InvalidArgumentException('Invalid Log Level. Must be one of: 0, 1, 2, 3, 4.'); 107 | } 108 | } 109 | 110 | /** 111 | * Get log level 112 | * @return int 113 | */ 114 | public function getLevel() { 115 | return $this->level; 116 | } 117 | 118 | /** 119 | * Log debug data 120 | * @param mixed $data 121 | * @return void 122 | */ 123 | public function debug( $data ) { 124 | $this->log($data, 4); 125 | } 126 | 127 | /** 128 | * Log info data 129 | * @param mixed $data 130 | * @return void 131 | */ 132 | public function info( $data ) { 133 | $this->log($data, 3); 134 | } 135 | 136 | /** 137 | * Log warn data 138 | * @param mixed $data 139 | * @return void 140 | */ 141 | public function warn( $data ) { 142 | $this->log($data, 2); 143 | } 144 | 145 | /** 146 | * Log error data 147 | * @param mixed $data 148 | * @return void 149 | */ 150 | public function error( $data ) { 151 | $this->log($data, 1); 152 | } 153 | 154 | /** 155 | * Log fatal data 156 | * @param mixed $data 157 | * @return void 158 | */ 159 | public function fatal( $data ) { 160 | $this->log($data, 0); 161 | } 162 | 163 | /** 164 | * Get absolute path to current daily log file 165 | * @return string 166 | */ 167 | public function getFile() { 168 | return $this->getDirectory() . strftime('%Y-%m-%d') . '.log'; 169 | } 170 | 171 | /** 172 | * Log data to file 173 | * @param mixed $data 174 | * @param int $level 175 | * @return void 176 | * @throws RuntimeException If log directory not found or not writable 177 | */ 178 | protected function log( $data, $level ) { 179 | $dir = $this->getDirectory(); 180 | if ( $dir == false || !is_dir($dir) ) { 181 | throw new RuntimeException("Log directory '$dir' invalid."); 182 | } 183 | if ( !is_writable($dir) ) { 184 | throw new RuntimeException("Log directory '$dir' not writable."); 185 | } 186 | if ( $level <= $this->getLevel() ) { 187 | $this->write(sprintf("[%s] %s - %s\r\n", $this->levels[$level], date('c'), (string)$data)); 188 | } 189 | } 190 | 191 | /** 192 | * Persist data to log 193 | * @param string Log message 194 | * @return void 195 | */ 196 | protected function write( $data ) { 197 | @file_put_contents($this->getFile(), $data, FILE_APPEND | LOCK_EX); 198 | } 199 | 200 | } -------------------------------------------------------------------------------- /Slim/Route.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Route 35 | * 36 | * @package Slim 37 | * @author Josh Lockhart 38 | * @since Version 1.0 39 | */ 40 | class Slim_Route { 41 | 42 | /** 43 | * @var string The route pattern (ie. "/books/:id") 44 | */ 45 | protected $pattern; 46 | 47 | /** 48 | * @var mixed The callable associated with this route 49 | */ 50 | protected $callable; 51 | 52 | /** 53 | * @var array Conditions for this route's URL parameters 54 | */ 55 | protected $conditions = array(); 56 | 57 | /** 58 | * @var array Default conditions applied to all Route instances 59 | */ 60 | protected static $defaultConditions = array(); 61 | 62 | /** 63 | * @var string The name of this route (optional) 64 | */ 65 | protected $name; 66 | 67 | /** 68 | * @var array Key-value array of URL parameters 69 | */ 70 | protected $params = array(); 71 | 72 | /** 73 | * @var array HTTP methods supported by this Route 74 | */ 75 | protected $methods = array(); 76 | 77 | /** 78 | * @var Slim_Router The Router to which this Route belongs 79 | */ 80 | protected $router; 81 | 82 | /** 83 | * @var array[Callable] Middleware 84 | */ 85 | protected $middleware = array(); 86 | 87 | /** 88 | * Constructor 89 | * @param string $pattern The URL pattern (ie. "/books/:id") 90 | * @param mixed $callable Anything that returns TRUE for is_callable() 91 | */ 92 | public function __construct( $pattern, $callable ) { 93 | $this->setPattern($pattern); 94 | $this->setCallable($callable); 95 | $this->setConditions(self::getDefaultConditions()); 96 | } 97 | 98 | /** 99 | * Set default route conditions for all instances 100 | * @param array $defaultConditions 101 | * @return void 102 | */ 103 | public static function setDefaultConditions( array $defaultConditions ) { 104 | self::$defaultConditions = $defaultConditions; 105 | } 106 | 107 | /** 108 | * Get default route conditions for all instances 109 | * @return array 110 | */ 111 | public static function getDefaultConditions() { 112 | return self::$defaultConditions; 113 | } 114 | 115 | /** 116 | * Get route pattern 117 | * @return string 118 | */ 119 | public function getPattern() { 120 | return $this->pattern; 121 | } 122 | 123 | /** 124 | * Set route pattern 125 | * @param string $pattern 126 | * @return void 127 | */ 128 | public function setPattern( $pattern ) { 129 | $this->pattern = str_replace(')', ')?', (string)$pattern); 130 | } 131 | 132 | /** 133 | * Get route callable 134 | * @return mixed 135 | */ 136 | public function getCallable() { 137 | return $this->callable; 138 | } 139 | 140 | /** 141 | * Set route callable 142 | * @param mixed $callable 143 | * @return void 144 | */ 145 | public function setCallable($callable) { 146 | $this->callable = $callable; 147 | } 148 | 149 | /** 150 | * Get route conditions 151 | * @return array 152 | */ 153 | public function getConditions() { 154 | return $this->conditions; 155 | } 156 | 157 | /** 158 | * Set route conditions 159 | * @param array $conditions 160 | * @return void 161 | */ 162 | public function setConditions( array $conditions ) { 163 | $this->conditions = $conditions; 164 | } 165 | 166 | /** 167 | * Get route name 168 | * @return string|null 169 | */ 170 | public function getName() { 171 | return $this->name; 172 | } 173 | 174 | /** 175 | * Set route name 176 | * @param string $name 177 | * @return void 178 | */ 179 | public function setName( $name ) { 180 | $this->name = (string)$name; 181 | $this->router->cacheNamedRoute($this->name, $this); 182 | } 183 | 184 | /** 185 | * Get route parameters 186 | * @return array 187 | */ 188 | public function getParams() { 189 | return $this->params; 190 | } 191 | 192 | /** 193 | * Add supported HTTP method(s) 194 | * @return void 195 | */ 196 | public function setHttpMethods() { 197 | $args = func_get_args(); 198 | $this->methods = $args; 199 | } 200 | 201 | /** 202 | * Get supported HTTP methods 203 | * @return array 204 | */ 205 | public function getHttpMethods() { 206 | return $this->methods; 207 | } 208 | 209 | /** 210 | * Append supported HTTP methods 211 | * @return void 212 | */ 213 | public function appendHttpMethods() { 214 | $args = func_get_args(); 215 | $this->methods = array_merge($this->methods, $args); 216 | } 217 | 218 | /** 219 | * Append supported HTTP methods (alias for Route::appendHttpMethods) 220 | * @return Slim_Route 221 | */ 222 | public function via() { 223 | $args = func_get_args(); 224 | $this->methods = array_merge($this->methods, $args); 225 | return $this; 226 | } 227 | 228 | /** 229 | * Detect support for an HTTP method 230 | * @return bool 231 | */ 232 | public function supportsHttpMethod( $method ) { 233 | return in_array($method, $this->methods); 234 | } 235 | 236 | /** 237 | * Get router 238 | * @return Slim_Router 239 | */ 240 | public function getRouter() { 241 | return $this->router; 242 | } 243 | 244 | /** 245 | * Set router 246 | * @param Slim_Router $router 247 | * @return void 248 | */ 249 | public function setRouter( Slim_Router $router ) { 250 | $this->router = $router; 251 | } 252 | 253 | /** 254 | * Get middleware 255 | * @return array[Callable] 256 | */ 257 | public function getMiddleware() { 258 | return $this->middleware; 259 | } 260 | 261 | /** 262 | * Set middleware 263 | * 264 | * This method allows middleware to be assigned to a specific Route. 265 | * If the method argument `is_callable` (including callable arrays!), 266 | * we directly append the argument to `$this->middleware`. Else, we 267 | * assume the argument is an array of callables and merge the array 268 | * with `$this->middleware`. Even if non-callables are included in the 269 | * argument array, we still merge them; we lazily check each item 270 | * against `is_callable` during Route::dispatch(). 271 | * 272 | * @param Callable|array[Callable] 273 | * @return Slim_Route 274 | * @throws InvalidArgumentException If argument is not callable or not an array 275 | */ 276 | public function setMiddleware( $middleware ) { 277 | if ( is_callable($middleware) ) { 278 | $this->middleware[] = $middleware; 279 | } else if ( is_array($middleware) ) { 280 | $this->middleware = array_merge($this->middleware, $middleware); 281 | } else { 282 | throw new InvalidArgumentException('Route middleware must be callable or an array of callables'); 283 | } 284 | return $this; 285 | } 286 | 287 | /** 288 | * Matches URI? 289 | * 290 | * Parse this route's pattern, and then compare it to an HTTP resource URI 291 | * This method was modeled after the techniques demonstrated by Dan Sosedoff at: 292 | * 293 | * http://blog.sosedoff.com/2009/09/20/rails-like-php-url-router/ 294 | * 295 | * @param string $resourceUri A Request URI 296 | * @return bool 297 | */ 298 | public function matches( $resourceUri ) { 299 | //Extract URL params 300 | preg_match_all('@:([\w]+)@', $this->pattern, $paramNames, PREG_PATTERN_ORDER); 301 | $paramNames = $paramNames[0]; 302 | 303 | //Convert URL params into regex patterns, construct a regex for this route 304 | $patternAsRegex = preg_replace_callback('@:[\w]+@', array($this, 'convertPatternToRegex'), $this->pattern); 305 | if ( substr($this->pattern, -1) === '/' ) { 306 | $patternAsRegex = $patternAsRegex . '?'; 307 | } 308 | $patternAsRegex = '@^' . $patternAsRegex . '$@'; 309 | 310 | //Cache URL params' names and values if this route matches the current HTTP request 311 | if ( preg_match($patternAsRegex, $resourceUri, $paramValues) ) { 312 | array_shift($paramValues); 313 | foreach ( $paramNames as $index => $value ) { 314 | $val = substr($value, 1); 315 | if ( isset($paramValues[$val]) ) { 316 | $this->params[$val] = urldecode($paramValues[$val]); 317 | } 318 | } 319 | return true; 320 | } else { 321 | return false; 322 | } 323 | } 324 | 325 | /** 326 | * Convert a URL parameter (ie. ":id") into a regular expression 327 | * @param array URL parameters 328 | * @return string Regular expression for URL parameter 329 | */ 330 | protected function convertPatternToRegex( $matches ) { 331 | $key = str_replace(':', '', $matches[0]); 332 | if ( array_key_exists($key, $this->conditions) ) { 333 | return '(?P<' . $key . '>' . $this->conditions[$key] . ')'; 334 | } else { 335 | return '(?P<' . $key . '>[a-zA-Z0-9_\-\.\!\~\*\\\'\(\)\:\@\&\=\$\+,%]+)'; 336 | } 337 | } 338 | 339 | /** 340 | * Set route name 341 | * @param string $name The name of the route 342 | * @return Slim_Route 343 | */ 344 | public function name( $name ) { 345 | $this->setName($name); 346 | return $this; 347 | } 348 | 349 | /** 350 | * Merge route conditions 351 | * @param array $conditions Key-value array of URL parameter conditions 352 | * @return Slim_Route 353 | */ 354 | public function conditions( array $conditions ) { 355 | $this->conditions = array_merge($this->conditions, $conditions); 356 | return $this; 357 | } 358 | 359 | /** 360 | * Dispatch route 361 | * 362 | * This method invokes this route's callable. If middleware is 363 | * registered for this route, each callable middleware is invoked in 364 | * the order specified. 365 | * 366 | * This method is smart about trailing slashes on the route pattern. 367 | * If this route's pattern is defined with a trailing slash, and if the 368 | * current request URI does not have a trailing slash but otherwise 369 | * matches this route's pattern, a Slim_Exception_RequestSlash 370 | * will be thrown triggering an HTTP 301 Permanent Redirect to the same 371 | * URI _with_ a trailing slash. This Exception is caught in the 372 | * `Slim::run` loop. If this route's pattern is defined without a 373 | * trailing slash, and if the current request URI does have a trailing 374 | * slash, this route will not be matched and a 404 Not Found 375 | * response will be sent if no subsequent matching routes are found. 376 | * 377 | * @return bool Was route callable invoked successfully? 378 | * @throws Slim_Exception_RequestSlash 379 | */ 380 | public function dispatch() { 381 | if ( substr($this->pattern, -1) === '/' && substr($this->router->getRequest()->getResourceUri(), -1) !== '/' ) { 382 | throw new Slim_Exception_RequestSlash(); 383 | } 384 | //Invoke middleware 385 | foreach ( $this->middleware as $mw ) { 386 | if ( is_callable($mw) ) { 387 | call_user_func($mw); 388 | } 389 | } 390 | //Invoke callable 391 | if ( is_callable($this->getCallable()) ) { 392 | call_user_func_array($this->callable, array_values($this->params)); 393 | return true; 394 | } 395 | return false; 396 | } 397 | 398 | } -------------------------------------------------------------------------------- /Slim/Router.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Router 35 | * 36 | * Responsible for registering route paths with associated callables. 37 | * When a Slim application is run, the Router finds a matching Route for 38 | * the current HTTP request, and if a matching route is found, executes 39 | * the Route's associated callable passing it parameters from the Request URI. 40 | * 41 | * @package Slim 42 | * @author Josh Lockhart 43 | * @since Version 1.0 44 | */ 45 | class Slim_Router implements IteratorAggregate { 46 | 47 | /** 48 | * @var Slim_Http_Request 49 | */ 50 | protected $request; 51 | 52 | /** 53 | * @var array Lookup hash of routes, keyed by Request method 54 | */ 55 | protected $routes; 56 | 57 | /** 58 | * @var array Lookup hash of named routes, keyed by route name 59 | */ 60 | protected $namedRoutes; 61 | 62 | /** 63 | * @var array Array of routes that match the Request method and URL 64 | */ 65 | protected $matchedRoutes; 66 | 67 | /** 68 | * @var mixed Callable to be invoked if no matching routes are found 69 | */ 70 | protected $notFound; 71 | 72 | /** 73 | * @var mixed Callable to be invoked if application error 74 | */ 75 | protected $error; 76 | 77 | /** 78 | * Constructor 79 | * @param Slim_Http_Request $request The HTTP request object 80 | */ 81 | public function __construct( Slim_Http_Request $request ) { 82 | $this->request = $request; 83 | $this->routes = array(); 84 | } 85 | 86 | /** 87 | * Get Iterator 88 | * @return ArrayIterator 89 | */ 90 | public function getIterator() { 91 | return new ArrayIterator($this->getMatchedRoutes()); 92 | } 93 | 94 | /** 95 | * Get Request 96 | * @return Slim_Http_Request 97 | */ 98 | public function getRequest() { 99 | return $this->request; 100 | } 101 | 102 | /** 103 | * Set Request 104 | * @param Slim_Http_Request $req 105 | * @return void 106 | */ 107 | public function setRequest( Slim_Http_Request $req ) { 108 | $this->request = $req; 109 | } 110 | 111 | /** 112 | * Return routes that match the current request 113 | * @return array[Slim_Route] 114 | */ 115 | public function getMatchedRoutes( $reload = false ) { 116 | if ( $reload || is_null($this->matchedRoutes) ) { 117 | $this->matchedRoutes = array(); 118 | foreach ( $this->routes as $route ) { 119 | if ( $route->matches($this->request->getResourceUri()) ) { 120 | $this->matchedRoutes[] = $route; 121 | } 122 | } 123 | } 124 | return $this->matchedRoutes; 125 | } 126 | 127 | /** 128 | * Map a route to a callback function 129 | * @param string $pattern The URL pattern (ie. "/books/:id") 130 | * @param mixed $callable Anything that returns TRUE for is_callable() 131 | * @return Slim_Route 132 | */ 133 | public function map( $pattern, $callable ) { 134 | $route = new Slim_Route($pattern, $callable); 135 | $route->setRouter($this); 136 | $this->routes[] = $route; 137 | return $route; 138 | } 139 | 140 | /** 141 | * Cache named route 142 | * @param string $name The route name 143 | * @param Slim_Route $route The route object 144 | * @throws RuntimeException If a named route already exists with the same name 145 | * @return void 146 | */ 147 | public function cacheNamedRoute( $name, Slim_Route $route ) { 148 | if ( isset($this->namedRoutes[(string)$name]) ) { 149 | throw new RuntimeException('Named route already exists with name: ' . $name); 150 | } 151 | $this->namedRoutes[$name] = $route; 152 | } 153 | 154 | /** 155 | * Get URL for named route 156 | * @param string $name The name of the route 157 | * @param array Associative array of URL parameter names and values 158 | * @throws RuntimeException If named route not found 159 | * @return string The URL for the given route populated with the given parameters 160 | */ 161 | public function urlFor( $name, $params = array() ) { 162 | if ( !isset($this->namedRoutes[(string)$name]) ) { 163 | throw new RuntimeException('Named route not found for name: ' . $name); 164 | } 165 | $pattern = $this->namedRoutes[(string)$name]->getPattern(); 166 | $search = $replace = array(); 167 | foreach ( $params as $key => $value ) { 168 | $search[] = ':' . $key; 169 | $replace[] = $value; 170 | } 171 | $pattern = str_replace($search, $replace, $pattern); 172 | //Remove remnants of unpopulated, trailing optional pattern segments 173 | return preg_replace(array( 174 | '@\(\/?:.+\/??\)\??@', 175 | '@\?|\(|\)@' 176 | ), '', $this->request->getRootUri() . $pattern); 177 | } 178 | 179 | /** 180 | * Register a 404 Not Found callback 181 | * @param mixed $callable Anything that returns TRUE for is_callable() 182 | * @return mixed 183 | */ 184 | public function notFound( $callable = null ) { 185 | if ( is_callable($callable) ) { 186 | $this->notFound = $callable; 187 | } 188 | return $this->notFound; 189 | } 190 | 191 | /** 192 | * Register a 500 Error callback 193 | * @param mixed $callable Anything that returns TRUE for is_callable() 194 | * @return mixed 195 | */ 196 | public function error( $callable = null ) { 197 | if ( is_callable($callable) ) { 198 | $this->error = $callable; 199 | } 200 | return $this->error; 201 | } 202 | 203 | } -------------------------------------------------------------------------------- /Slim/Session/Flash.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Flash Messaging 35 | * 36 | * This class enables Flash messaging. Messages are persisted in $_SESSION 37 | * with a user-defined key. 38 | * 39 | * USAGE: 40 | * 41 | * 1. Set Flash message to be shown on the next request 42 | * 43 | * Slim::flash('error', 'The object could not be saved'); 44 | * 45 | * 2. Set Flash message to be shown on the current request 46 | * 47 | * Slim::flashNow('error', 'The object could not be saved'); 48 | * 49 | * 3. Keep old Flash messages for the next request 50 | * 51 | * Slim::flashKeep(); 52 | * 53 | * @package Slim 54 | * @author Josh Lockhart 55 | * @since Version 1.3 56 | */ 57 | class Slim_Session_Flash implements ArrayAccess { 58 | 59 | /** 60 | * @var string Key used to identify flash information in $_SESSION array 61 | */ 62 | protected $sessionKey = 'flash'; 63 | 64 | /** 65 | * @var array[array] Storage for flash messages 66 | */ 67 | protected $messages = array( 68 | 'prev' => array(), //flash messages from prev request 69 | 'next' => array(), //flash messages for next request 70 | 'now' => array() //flash messages for current request 71 | ); 72 | 73 | /** 74 | * Constructor 75 | * 76 | * Establishes Flash session key and loads existing 77 | * Flash messages from the $_SESSION. 78 | * 79 | * @param string $sessionKey 80 | * @return void 81 | */ 82 | public function __construct( $sessionKey = null ) { 83 | if ( !is_null($sessionKey) ) { 84 | $this->setSessionKey($sessionKey); 85 | } 86 | $this->load(); 87 | } 88 | 89 | /** 90 | * Set the $_SESSION key used to access Flash messages 91 | * @param string $key 92 | * @throws RuntimeException If session key is null 93 | * @return Slim_Session_Flash 94 | */ 95 | public function setSessionKey( $key ) { 96 | if ( is_null($key) ) { 97 | throw new RuntimeException('Session key cannot be null'); 98 | } 99 | $this->sessionKey = (string)$key; 100 | return $this; 101 | } 102 | 103 | /** 104 | * Get the $_SESSION key used to access Flash messages 105 | * @return string 106 | */ 107 | public function getSessionKey() { 108 | return $this->sessionKey; 109 | } 110 | 111 | /** 112 | * Set a Flash message for the current request 113 | * @param string $key 114 | * @param string $value 115 | * @return Slim_Session_Flash 116 | */ 117 | public function now( $key, $value ) { 118 | $this->messages['now'][(string)$key] = $value; 119 | return $this->save(); 120 | } 121 | 122 | /** 123 | * Set a Flash message for the next request 124 | * @param string $key 125 | * @param string $value 126 | * @return Slim_Session_Flash 127 | */ 128 | public function set( $key, $value ) { 129 | $this->messages['next'][(string)$key] = $value; 130 | return $this->save(); 131 | } 132 | 133 | /** 134 | * Get Flash messages intended for the current request's View 135 | * @return array[String] 136 | */ 137 | public function getMessages() { 138 | return array_merge($this->messages['prev'], $this->messages['now']); 139 | } 140 | 141 | /** 142 | * Load Flash messages from $_SESSION 143 | * @return Slim_Session_Flash 144 | */ 145 | public function load() { 146 | $this->messages['prev'] = isset($_SESSION[$this->sessionKey]) ? $_SESSION[$this->sessionKey] : array(); 147 | return $this; 148 | } 149 | 150 | /** 151 | * Transfer Flash messages from the previous request 152 | * so they are available to the next request. 153 | * @return Slim_Session_Flash 154 | */ 155 | public function keep() { 156 | foreach ( $this->messages['prev'] as $key => $val ) { 157 | $this->messages['next'][$key] = $val; 158 | } 159 | return $this->save(); 160 | } 161 | 162 | /** 163 | * Save Flash messages to $_SESSION 164 | * @return Slim_Session_Flash 165 | */ 166 | public function save() { 167 | $_SESSION[$this->sessionKey] = $this->messages['next']; 168 | return $this; 169 | } 170 | 171 | /***** ARRAY ACCESS INTERFACE *****/ 172 | 173 | public function offsetExists( $offset ) { 174 | $messages = $this->getMessages(); 175 | return isset($messages[$offset]); 176 | } 177 | 178 | public function offsetGet( $offset ) { 179 | $messages = $this->getMessages(); 180 | return isset($messages[$offset]) ? $messages[$offset] : null; 181 | } 182 | 183 | public function offsetSet( $offset, $value ) { 184 | $this->now($offset, $value); 185 | } 186 | 187 | public function offsetUnset( $offset ) { 188 | unset($this->messages['prev'][$offset]); 189 | unset($this->messages['now'][$offset]); 190 | } 191 | 192 | } -------------------------------------------------------------------------------- /Slim/Session/Handler.php: -------------------------------------------------------------------------------- 1 | app = $app; 58 | return session_set_save_handler( 59 | array($this, 'open'), 60 | array($this, 'close'), 61 | array($this, 'read'), 62 | array($this, 'write'), 63 | array($this, 'destroy'), 64 | array($this, 'gc') 65 | ); 66 | } 67 | 68 | /** 69 | * Open session 70 | * 71 | * @param string $savePath 72 | * @param string $sessionName 73 | * @return mixed 74 | */ 75 | abstract public function open( $savePath, $sessionName ); 76 | 77 | /** 78 | * Close session 79 | * 80 | * @return mixed 81 | */ 82 | abstract public function close(); 83 | 84 | /** 85 | * Read session data with ID 86 | * 87 | * @param string $id The session identifier 88 | * @return string 89 | */ 90 | abstract public function read( $id ); 91 | 92 | /** 93 | * Write session data with ID 94 | * 95 | * The "write" handler is not executed until after the output stream is 96 | * closed. Thus, output from debugging statements in the "write" handler 97 | * will never be seen in the browser. If debugging output is necessary, it 98 | * is suggested that the debug output be written to a file instead. 99 | * 100 | * @param string $id The session identifier 101 | * @param mixed $sessionData The session data 102 | * @return mixed 103 | */ 104 | abstract public function write( $id, $sessionData ); 105 | 106 | /** 107 | * Destroy session with ID 108 | * 109 | * @param string $id The session identifier 110 | * @return mixed 111 | */ 112 | abstract public function destroy( $id ); 113 | 114 | /** 115 | * Session garbage collection 116 | * 117 | * Executed when the PHP session garbage collector is invoked; should 118 | * remove all session data older than the `$maxLifetime`. 119 | * 120 | * @param int $maxLifetime 121 | * @return mixed 122 | */ 123 | abstract public function gc( $maxLifetime ); 124 | 125 | } -------------------------------------------------------------------------------- /Slim/Session/Handler/Cookies.php: -------------------------------------------------------------------------------- 1 | app->getEncryptedCookie($id); 57 | } 58 | 59 | public function write( $id, $sessionData ) { 60 | $this->app->setEncryptedCookie($id, $sessionData, 0); 61 | } 62 | 63 | public function destroy( $id ) { 64 | $this->app->deleteCookie($id); 65 | } 66 | 67 | public function gc( $maxLifetime ) { 68 | return true; //Not used 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /Slim/Slim.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | //Ensure PHP session IDs only use the characters [a-z0-9] 34 | ini_set('session.hash_bits_per_character', 4); 35 | ini_set('session.hash_function', 0); 36 | 37 | //Slim's Encryted Cookies rely on libmcyrpt and these two constants. 38 | //If libmycrpt is unavailable, we ensure the expected constants 39 | //are available to avoid errors. 40 | if ( !defined('MCRYPT_RIJNDAEL_256') ) { 41 | define('MCRYPT_RIJNDAEL_256', 0); 42 | } 43 | if ( !defined('MCRYPT_MODE_CBC') ) { 44 | define('MCRYPT_MODE_CBC', 0); 45 | } 46 | 47 | //This determines which errors are reported by PHP. By default, all 48 | //errors (including E_STRICT) are reported. 49 | error_reporting(E_ALL | E_STRICT); 50 | 51 | //This tells PHP to auto-load classes using Slim's autoloader; this will 52 | //only auto-load a class file located in the same directory as Slim.php 53 | //whose file name (excluding the final dot and extension) is the same 54 | //as its class name (case-sensitive). For example, "View.php" will be 55 | //loaded when Slim uses the "View" class for the first time. 56 | spl_autoload_register(array('Slim', 'autoload')); 57 | 58 | //PHP 5.3 will complain if you don't set a timezone. If you do not 59 | //specify your own timezone before requiring Slim, this tells PHP to use UTC. 60 | if ( @date_default_timezone_set(date_default_timezone_get()) === false ) { 61 | date_default_timezone_set('UTC'); 62 | } 63 | 64 | /** 65 | * Slim 66 | * 67 | * @package Slim 68 | * @author Josh Lockhart 69 | * @since Version 1.0 70 | */ 71 | class Slim { 72 | 73 | /** 74 | * @var array[Slim] 75 | */ 76 | protected static $apps = array(); 77 | 78 | /** 79 | * @var string 80 | */ 81 | protected $name; 82 | 83 | /** 84 | * @var Slim_Http_Request 85 | */ 86 | protected $request; 87 | 88 | /** 89 | * @var Slim_Http_Response 90 | */ 91 | protected $response; 92 | 93 | /** 94 | * @var Slim_Router 95 | */ 96 | protected $router; 97 | 98 | /** 99 | * @var Slim_View 100 | */ 101 | protected $view; 102 | 103 | /** 104 | * @var Slim_Log 105 | */ 106 | protected $log; 107 | 108 | /** 109 | * @var array Key-value array of application settings 110 | */ 111 | protected $settings; 112 | 113 | /** 114 | * @var string The application mode 115 | */ 116 | protected $mode; 117 | 118 | /** 119 | * @var array Plugin hooks 120 | */ 121 | protected $hooks = array( 122 | 'slim.before' => array(array()), 123 | 'slim.before.router' => array(array()), 124 | 'slim.before.dispatch' => array(array()), 125 | 'slim.after.dispatch' => array(array()), 126 | 'slim.after.router' => array(array()), 127 | 'slim.after' => array(array()) 128 | ); 129 | 130 | /** 131 | * Slim auto-loader 132 | * 133 | * This method lazy-loads class files when a given class if first used. 134 | * Class files must exist in the same directory as this file and be named 135 | * the same as its class definition (excluding the dot and extension). 136 | * 137 | * @return void 138 | */ 139 | public static function autoload( $class ) { 140 | if ( strpos($class, 'Slim') !== 0 ) { 141 | return; 142 | } 143 | $file = dirname(__FILE__) . '/' . str_replace('_', DIRECTORY_SEPARATOR, substr($class,5)) . '.php'; 144 | if ( file_exists($file) ) { 145 | require $file; 146 | } 147 | } 148 | 149 | /***** INITIALIZATION *****/ 150 | 151 | /** 152 | * Constructor 153 | * @param array $userSettings 154 | * @return void 155 | */ 156 | public function __construct( $userSettings = array() ) { 157 | //Merge application settings 158 | $this->settings = array_merge(array( 159 | //Mode 160 | 'mode' => 'development', 161 | //Logging 162 | 'log.enable' => false, 163 | 'log.logger' => null, 164 | 'log.path' => './logs', 165 | 'log.level' => 4, 166 | //Debugging 167 | 'debug' => true, 168 | //View 169 | 'templates.path' => './templates', 170 | 'view' => 'Slim_View', 171 | //Settings for all cookies 172 | 'cookies.lifetime' => '20 minutes', 173 | 'cookies.path' => '/', 174 | 'cookies.domain' => '', 175 | 'cookies.secure' => false, 176 | 'cookies.httponly' => false, 177 | //Settings for encrypted cookies 178 | 'cookies.secret_key' => 'CHANGE_ME', 179 | 'cookies.cipher' => MCRYPT_RIJNDAEL_256, 180 | 'cookies.cipher_mode' => MCRYPT_MODE_CBC, 181 | 'cookies.encrypt' => true, 182 | 'cookies.user_id' => 'DEFAULT', 183 | //Session handler 184 | 'session.handler' => new Slim_Session_Handler_Cookies(), 185 | 'session.flash_key' => 'flash', 186 | //HTTP 187 | 'http.version' => null 188 | ), $userSettings); 189 | 190 | //Determine application mode 191 | $this->getMode(); 192 | 193 | //Setup HTTP request and response handling 194 | $this->request = new Slim_Http_Request(); 195 | $this->response = new Slim_Http_Response($this->request); 196 | $this->response->setCookieJar(new Slim_Http_CookieJar($this->settings['cookies.secret_key'], array( 197 | 'high_confidentiality' => $this->settings['cookies.encrypt'], 198 | 'mcrypt_algorithm' => $this->settings['cookies.cipher'], 199 | 'mcrypt_mode' => $this->settings['cookies.cipher_mode'], 200 | 'enable_ssl' => $this->settings['cookies.secure'] 201 | ))); 202 | $this->response->httpVersion($this->settings['http.version']); 203 | $this->router = new Slim_Router($this->request); 204 | 205 | //Start session if not already started 206 | if ( session_id() === '' ) { 207 | $sessionHandler = $this->config('session.handler'); 208 | if ( $sessionHandler instanceof Slim_Session_Handler ) { 209 | $sessionHandler->register($this); 210 | } 211 | session_cache_limiter(false); 212 | session_start(); 213 | } 214 | 215 | //Setup view with flash messaging 216 | $this->view($this->config('view'))->setData('flash', new Slim_Session_Flash($this->config('session.flash_key'))); 217 | 218 | //Set app name 219 | if ( !isset(self::$apps['default']) ) { 220 | $this->setName('default'); 221 | } 222 | 223 | //Set global Error handler after Slim app instantiated 224 | set_error_handler(array('Slim', 'handleErrors')); 225 | } 226 | 227 | /** 228 | * Get application mode 229 | * @return string 230 | */ 231 | public function getMode() { 232 | if ( !isset($this->mode) ) { 233 | if ( isset($_ENV['SLIM_MODE']) ) { 234 | $this->mode = (string)$_ENV['SLIM_MODE']; 235 | } else { 236 | $envMode = getenv('SLIM_MODE'); 237 | if ( $envMode !== false ) { 238 | $this->mode = $envMode; 239 | } else { 240 | $this->mode = (string)$this->config('mode'); 241 | } 242 | } 243 | } 244 | return $this->mode; 245 | } 246 | 247 | /***** NAMING *****/ 248 | 249 | /** 250 | * Get Slim application with name 251 | * @param string $name The name of the Slim application to fetch 252 | * @return Slim|null 253 | */ 254 | public static function getInstance( $name = 'default' ) { 255 | return isset(self::$apps[(string)$name]) ? self::$apps[(string)$name] : null; 256 | } 257 | 258 | /** 259 | * Set Slim application name 260 | * @param string $name The name of this Slim application 261 | * @return void 262 | */ 263 | public function setName( $name ) { 264 | $this->name = $name; 265 | self::$apps[$name] = $this; 266 | } 267 | 268 | /** 269 | * Get Slim application name 270 | * @return string|null 271 | */ 272 | public function getName() { 273 | return $this->name; 274 | } 275 | 276 | /***** LOGGING *****/ 277 | 278 | /** 279 | * Get application Log (lazy-loaded) 280 | * @return Slim_Log 281 | */ 282 | public function getLog() { 283 | if ( !isset($this->log) ) { 284 | $this->log = new Slim_Log(); 285 | $this->log->setEnabled($this->config('log.enable')); 286 | $logger = $this->config('log.logger'); 287 | if ( $logger ) { 288 | $this->log->setLogger($logger); 289 | } else { 290 | $this->log->setLogger(new Slim_Logger($this->config('log.path'), $this->config('log.level'))); 291 | } 292 | } 293 | return $this->log; 294 | } 295 | 296 | /***** CONFIGURATION *****/ 297 | 298 | /** 299 | * Configure Slim for a given mode 300 | * 301 | * This method will immediately invoke the callable if 302 | * the specified mode matches the current application mode. 303 | * Otherwise, the callable is ignored. This should be called 304 | * only _after_ you initialize your Slim app. 305 | * 306 | * @param string $mode 307 | * @param mixed $callable 308 | * @return void 309 | */ 310 | public function configureMode( $mode, $callable ) { 311 | if ( $mode === $this->getMode() && is_callable($callable) ) { 312 | call_user_func($callable); 313 | } 314 | } 315 | 316 | /** 317 | * Configure Slim Settings 318 | * 319 | * This method defines application settings and acts as a setter and a getter. 320 | * 321 | * If only one argument is specified and that argument is a string, the value 322 | * of the setting identified by the first argument will be returned, or NULL if 323 | * that setting does not exist. 324 | * 325 | * If only one argument is specified and that argument is an associative array, 326 | * the array will be merged into the existing application settings. 327 | * 328 | * If two arguments are provided, the first argument is the name of the setting 329 | * to be created or updated, and the second argument is the setting value. 330 | * 331 | * @param string|array $name If a string, the name of the setting to set or retrieve. Else an associated array of setting names and values 332 | * @param mixed $value If name is a string, the value of the setting identified by $name 333 | * @return mixed The value of a setting if only one argument is a string 334 | */ 335 | public function config( $name, $value = null ) { 336 | if ( func_num_args() === 1 ) { 337 | if ( is_array($name) ) { 338 | $this->settings = array_merge($this->settings, $name); 339 | } else { 340 | return in_array($name, array_keys($this->settings)) ? $this->settings[$name] : null; 341 | } 342 | } else { 343 | $this->settings[$name] = $value; 344 | } 345 | } 346 | 347 | /***** ROUTING *****/ 348 | 349 | /** 350 | * Add GET|POST|PUT|DELETE route 351 | * 352 | * Adds a new route to the router with associated callable. This 353 | * route will only be invoked when the HTTP request's method matches 354 | * this route's method. 355 | * 356 | * ARGUMENTS: 357 | * 358 | * First: string The URL pattern (REQUIRED) 359 | * In-Between: mixed Anything that returns TRUE for `is_callable` (OPTIONAL) 360 | * Last: mixed Anything that returns TRUE for `is_callable` (REQUIRED) 361 | * 362 | * The first argument is required and must always be the 363 | * route pattern (ie. '/books/:id'). 364 | * 365 | * The last argument is required and must always be the callable object 366 | * to be invoked when the route matches an HTTP request. 367 | * 368 | * You may also provide an unlimited number of in-between arguments; 369 | * each interior argument must be callable and will be invoked in the 370 | * order specified before the route's callable is invoked. 371 | * 372 | * USAGE: 373 | * 374 | * Slim::get('/foo'[, middleware, middleware, ...], callable); 375 | * 376 | * @param array (See notes above) 377 | * @return Slim_Route 378 | */ 379 | protected function mapRoute($args) { 380 | $pattern = array_shift($args); 381 | $callable = array_pop($args); 382 | $route = $this->router->map($pattern, $callable); 383 | if ( count($args) > 0 ) { 384 | $route->setMiddleware($args); 385 | } 386 | return $route; 387 | } 388 | 389 | /** 390 | * Add generic route without associated HTTP method 391 | * @see Slim::mapRoute 392 | * @return Slim_Route 393 | */ 394 | public function map() { 395 | $args = func_get_args(); 396 | return $this->mapRoute($args); 397 | } 398 | 399 | /** 400 | * Add GET route 401 | * @see Slim::mapRoute 402 | * @return Slim_Route 403 | */ 404 | public function get() { 405 | $args = func_get_args(); 406 | return $this->mapRoute($args)->via(Slim_Http_Request::METHOD_GET, Slim_Http_Request::METHOD_HEAD); 407 | } 408 | 409 | /** 410 | * Add POST route 411 | * @see Slim::mapRoute 412 | * @return Slim_Route 413 | */ 414 | public function post() { 415 | $args = func_get_args(); 416 | return $this->mapRoute($args)->via(Slim_Http_Request::METHOD_POST); 417 | } 418 | 419 | /** 420 | * Add PUT route 421 | * @see Slim::mapRoute 422 | * @return Slim_Route 423 | */ 424 | public function put() { 425 | $args = func_get_args(); 426 | return $this->mapRoute($args)->via(Slim_Http_Request::METHOD_PUT); 427 | } 428 | 429 | /** 430 | * Add DELETE route 431 | * @see Slim::mapRoute 432 | * @return Slim_Route 433 | */ 434 | public function delete() { 435 | $args = func_get_args(); 436 | return $this->mapRoute($args)->via(Slim_Http_Request::METHOD_DELETE); 437 | } 438 | 439 | /** 440 | * Add OPTIONS route 441 | * @see Slim::mapRoute 442 | * @return Slim_Route 443 | */ 444 | public function options() { 445 | $args = func_get_args(); 446 | return $this->mapRoute($args)->via(Slim_Http_Request::METHOD_OPTIONS); 447 | } 448 | 449 | /** 450 | * Not Found Handler 451 | * 452 | * This method defines or invokes the application-wide Not Found handler. 453 | * There are two contexts in which this method may be invoked: 454 | * 455 | * 1. When declaring the handler: 456 | * 457 | * If the $callable parameter is not null and is callable, this 458 | * method will register the callable to be invoked when no 459 | * routes match the current HTTP request. It WILL NOT invoke the callable. 460 | * 461 | * 2. When invoking the handler: 462 | * 463 | * If the $callable parameter is null, Slim assumes you want 464 | * to invoke an already-registered handler. If the handler has been 465 | * registered and is callable, it is invoked and sends a 404 HTTP Response 466 | * whose body is the output of the Not Found handler. 467 | * 468 | * @param mixed $callable Anything that returns true for is_callable() 469 | * @return void 470 | */ 471 | public function notFound( $callable = null ) { 472 | if ( !is_null($callable) ) { 473 | $this->router->notFound($callable); 474 | } else { 475 | ob_start(); 476 | $customNotFoundHandler = $this->router->notFound(); 477 | if ( is_callable($customNotFoundHandler) ) { 478 | call_user_func($customNotFoundHandler); 479 | } else { 480 | call_user_func(array($this, 'defaultNotFound')); 481 | } 482 | $this->halt(404, ob_get_clean()); 483 | } 484 | } 485 | 486 | /** 487 | * Error Handler 488 | * 489 | * This method defines or invokes the application-wide Error handler. 490 | * There are two contexts in which this method may be invoked: 491 | * 492 | * 1. When declaring the handler: 493 | * 494 | * If the $argument parameter is callable, this 495 | * method will register the callable to be invoked when an uncaught 496 | * Exception is detected, or when otherwise explicitly invoked. 497 | * The handler WILL NOT be invoked in this context. 498 | * 499 | * 2. When invoking the handler: 500 | * 501 | * If the $argument parameter is not callable, Slim assumes you want 502 | * to invoke an already-registered handler. If the handler has been 503 | * registered and is callable, it is invoked and passed the caught Exception 504 | * as its one and only argument. The error handler's output is captured 505 | * into an output buffer and sent as the body of a 500 HTTP Response. 506 | * 507 | * @param mixed $argument Callable|Exception 508 | * @return void 509 | */ 510 | public function error( $argument = null ) { 511 | if ( is_callable($argument) ) { 512 | //Register error handler 513 | $this->router->error($argument); 514 | } else { 515 | //Invoke error handler 516 | ob_start(); 517 | $customErrorHandler = $this->router->error(); 518 | if ( is_callable($customErrorHandler) ) { 519 | call_user_func_array($customErrorHandler, array($argument)); 520 | } else { 521 | call_user_func_array(array($this, 'defaultError'), array($argument)); 522 | } 523 | $this->halt(500, ob_get_clean()); 524 | } 525 | } 526 | 527 | /***** ACCESSORS *****/ 528 | 529 | /** 530 | * Get the Request object 531 | * @return Slim_Http_Request 532 | */ 533 | public function request() { 534 | return $this->request; 535 | } 536 | 537 | /** 538 | * Get the Response object 539 | * @return Slim_Http_Response 540 | */ 541 | public function response() { 542 | return $this->response; 543 | } 544 | 545 | /** 546 | * Get the Router object 547 | * @return Slim_Router 548 | */ 549 | public function router() { 550 | return $this->router; 551 | } 552 | 553 | /** 554 | * Get and/or set the View 555 | * 556 | * This method declares the View to be used by the Slim application. 557 | * If the argument is a string, Slim will instantiate a new object 558 | * of the same class. If the argument is an instance of View or a subclass 559 | * of View, Slim will use the argument as the View. 560 | * 561 | * If a View already exists and this method is called to create a 562 | * new View, data already set in the existing View will be 563 | * transferred to the new View. 564 | * 565 | * @param string|Slim_View $viewClass The name of a Slim_View class; 566 | * An instance of Slim_View; 567 | * @return Slim_View 568 | */ 569 | public function view( $viewClass = null ) { 570 | if ( !is_null($viewClass) ) { 571 | $existingData = is_null($this->view) ? array() : $this->view->getData(); 572 | if ( $viewClass instanceOf Slim_View ) { 573 | $this->view = $viewClass; 574 | } else { 575 | $this->view = new $viewClass(); 576 | } 577 | $this->view->appendData($existingData); 578 | } 579 | return $this->view; 580 | } 581 | 582 | /***** RENDERING *****/ 583 | 584 | /** 585 | * Render a template 586 | * 587 | * Call this method within a GET, POST, PUT, DELETE, NOT FOUND, or ERROR 588 | * callable to render a template whose output is appended to the 589 | * current HTTP response body. How the template is rendered is 590 | * delegated to the current View. 591 | * 592 | * @param string $template The name of the template passed into the View::render method 593 | * @param array $data Associative array of data made available to the View 594 | * @param int $status The HTTP response status code to use (Optional) 595 | * @return void 596 | */ 597 | public function render( $template, $data = array(), $status = null ) { 598 | $templatesPath = $this->config('templates.path'); 599 | //Legacy support 600 | if ( is_null($templatesPath) ) { 601 | $templatesPath = $this->config('templates_dir'); 602 | } 603 | $this->view->setTemplatesDirectory($templatesPath); 604 | if ( !is_null($status) ) { 605 | $this->response->status($status); 606 | } 607 | $this->view->appendData($data); 608 | $this->view->display($template); 609 | } 610 | 611 | /***** HTTP CACHING *****/ 612 | 613 | /** 614 | * Set Last-Modified HTTP Response Header 615 | * 616 | * Set the HTTP 'Last-Modified' header and stop if a conditional 617 | * GET request's `If-Modified-Since` header matches the last modified time 618 | * of the resource. The `time` argument is a UNIX timestamp integer value. 619 | * When the current request includes an 'If-Modified-Since' header that 620 | * matches the specified last modified time, the application will stop 621 | * and send a '304 Not Modified' response to the client. 622 | * 623 | * @param int $time The last modified UNIX timestamp 624 | * @throws SlimException Returns HTTP 304 Not Modified response if resource last modified time matches `If-Modified-Since` header 625 | * @throws InvalidArgumentException If provided timestamp is not an integer 626 | * @return void 627 | */ 628 | public function lastModified( $time ) { 629 | if ( is_integer($time) ) { 630 | $this->response->header('Last-Modified', date(DATE_RFC1123, $time)); 631 | if ( $time === strtotime($this->request->headers('IF_MODIFIED_SINCE')) ) $this->halt(304); 632 | } else { 633 | throw new InvalidArgumentException('Slim::lastModified only accepts an integer UNIX timestamp value.'); 634 | } 635 | } 636 | 637 | /** 638 | * Set ETag HTTP Response Header 639 | * 640 | * Set the etag header and stop if the conditional GET request matches. 641 | * The `value` argument is a unique identifier for the current resource. 642 | * The `type` argument indicates whether the etag should be used as a strong or 643 | * weak cache validator. 644 | * 645 | * When the current request includes an 'If-None-Match' header with 646 | * a matching etag, execution is immediately stopped. If the request 647 | * method is GET or HEAD, a '304 Not Modified' response is sent. 648 | * 649 | * @param string $value The etag value 650 | * @param string $type The type of etag to create; either "strong" or "weak" 651 | * @throws InvalidArgumentException If provided type is invalid 652 | * @return void 653 | */ 654 | public function etag( $value, $type = 'strong' ) { 655 | 656 | //Ensure type is correct 657 | if ( !in_array($type, array('strong', 'weak')) ) { 658 | throw new InvalidArgumentException('Invalid Slim::etag type. Expected "strong" or "weak".'); 659 | } 660 | 661 | //Set etag value 662 | $value = '"' . $value . '"'; 663 | if ( $type === 'weak' ) $value = 'W/'.$value; 664 | $this->response->header('ETag', $value); 665 | 666 | //Check conditional GET 667 | if ( $etagsHeader = $this->request->headers('IF_NONE_MATCH')) { 668 | $etags = preg_split('@\s*,\s*@', $etagsHeader); 669 | if ( in_array($value, $etags) || in_array('*', $etags) ) $this->halt(304); 670 | } 671 | 672 | } 673 | 674 | /***** COOKIES *****/ 675 | 676 | /** 677 | * Set a normal, unencrypted Cookie 678 | * 679 | * @param string $name The cookie name 680 | * @param mixed $value The cookie value 681 | * @param mixed $time The duration of the cookie; 682 | * If integer, should be UNIX timestamp; 683 | * If string, converted to UNIX timestamp with `strtotime`; 684 | * @param string $path The path on the server in which the cookie will be available on 685 | * @param string $domain The domain that the cookie is available to 686 | * @param bool $secure Indicates that the cookie should only be transmitted over a secure 687 | * HTTPS connection to/from the client 688 | * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol 689 | * @return void 690 | */ 691 | public function setCookie( $name, $value, $time = null, $path = null, $domain = null, $secure = null, $httponly = null ) { 692 | $time = is_null($time) ? $this->config('cookies.lifetime') : $time; 693 | $path = is_null($path) ? $this->config('cookies.path') : $path; 694 | $domain = is_null($domain) ? $this->config('cookies.domain') : $domain; 695 | $secure = is_null($secure) ? $this->config('cookies.secure') : $secure; 696 | $httponly = is_null($httponly) ? $this->config('cookies.httponly') : $httponly; 697 | $this->response->getCookieJar()->setClassicCookie($name, $value, $time, $path, $domain, $secure, $httponly); 698 | } 699 | 700 | /** 701 | * Get the value of a Cookie from the current HTTP Request 702 | * 703 | * Return the value of a cookie from the current HTTP request, 704 | * or return NULL if cookie does not exist. Cookies created during 705 | * the current request will not be available until the next request. 706 | * 707 | * @param string $name 708 | * @return string|null 709 | */ 710 | public function getCookie( $name ) { 711 | return $this->request->cookies($name); 712 | } 713 | 714 | /** 715 | * Set an encrypted Cookie 716 | * 717 | * @param string $name The cookie name 718 | * @param mixed $value The cookie value 719 | * @param mixed $time The duration of the cookie; 720 | * If integer, should be UNIX timestamp; 721 | * If string, converted to UNIX timestamp with `strtotime`; 722 | * @param string $path The path on the server in which the cookie will be available on 723 | * @param string $domain The domain that the cookie is available to 724 | * @param bool $secure Indicates that the cookie should only be transmitted over a secure 725 | * HTTPS connection from the client 726 | * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol 727 | * @return void 728 | */ 729 | public function setEncryptedCookie( $name, $value, $time = null, $path = null, $domain = null, $secure = null, $httponly = null ) { 730 | $time = is_null($time) ? $this->config('cookies.lifetime') : $time; 731 | $path = is_null($path) ? $this->config('cookies.path') : $path; 732 | $domain = is_null($domain) ? $this->config('cookies.domain') : $domain; 733 | $secure = is_null($secure) ? $this->config('cookies.secure') : $secure; 734 | $httponly = is_null($httponly) ? $this->config('cookies.httponly') : $httponly; 735 | $userId = $this->config('cookies.user_id'); 736 | $this->response->getCookieJar()->setCookie($name, $value, $userId, $time, $path, $domain, $secure, $httponly); 737 | } 738 | 739 | /** 740 | * Get the value of an encrypted Cookie from the current HTTP request 741 | * 742 | * Return the value of an encrypted cookie from the current HTTP request, 743 | * or return NULL if cookie does not exist. Encrypted cookies created during 744 | * the current request will not be available until the next request. 745 | * 746 | * @param string $name 747 | * @return string|null 748 | */ 749 | public function getEncryptedCookie( $name ) { 750 | $value = $this->response->getCookieJar()->getCookieValue($name); 751 | return ($value === false) ? null : $value; 752 | } 753 | 754 | /** 755 | * Delete a Cookie (for both normal or encrypted Cookies) 756 | * 757 | * Remove a Cookie from the client. This method will overwrite an existing Cookie 758 | * with a new, empty, auto-expiring Cookie. This method's arguments must match 759 | * the original Cookie's respective arguments for the original Cookie to be 760 | * removed. If any of this method's arguments are omitted or set to NULL, the 761 | * default Cookie setting values (set during Slim::init) will be used instead. 762 | * 763 | * @param string $name The cookie name 764 | * @param string $path The path on the server in which the cookie will be available on 765 | * @param string $domain The domain that the cookie is available to 766 | * @param bool $secure Indicates that the cookie should only be transmitted over a secure 767 | * HTTPS connection from the client 768 | * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol 769 | * @return void 770 | */ 771 | public function deleteCookie( $name, $path = null, $domain = null, $secure = null, $httponly = null ) { 772 | $path = is_null($path) ? $this->config('cookies.path') : $path; 773 | $domain = is_null($domain) ? $this->config('cookies.domain') : $domain; 774 | $secure = is_null($secure) ? $this->config('cookies.secure') : $secure; 775 | $httponly = is_null($httponly) ? $this->config('cookies.httponly') : $httponly; 776 | $this->response->getCookieJar()->deleteCookie( $name, $path, $domain, $secure, $httponly ); 777 | } 778 | 779 | /***** HELPERS *****/ 780 | 781 | /** 782 | * Get the Slim application's absolute directory path 783 | * 784 | * This method returns the absolute path to the Slim application's 785 | * directory. If the Slim application is installed in a public-accessible 786 | * sub-directory, the sub-directory path will be included. This method 787 | * will always return an absolute path WITH a trailing slash. 788 | * 789 | * @return string 790 | */ 791 | public function root() { 792 | return rtrim($_SERVER['DOCUMENT_ROOT'], '/') . rtrim($this->request->getRootUri(), '/') . '/'; 793 | } 794 | 795 | /** 796 | * Stop 797 | * 798 | * Send the current Response as is and stop executing the Slim 799 | * application. The thrown exception will be caught by the Slim 800 | * custom exception handler which exits this script. 801 | * 802 | * @throws Slim_Exception_Stop 803 | * @return void 804 | */ 805 | public function stop() { 806 | $flash = $this->view->getData('flash'); 807 | if ( $flash ) { 808 | $flash->save(); 809 | } 810 | session_write_close(); 811 | $this->response->send(); 812 | throw new Slim_Exception_Stop(); 813 | } 814 | 815 | /** 816 | * Halt 817 | * 818 | * Halt the application and immediately send an HTTP response with a 819 | * specific status code and body. This may be used to send any type of 820 | * response: info, success, redirect, client error, or server error. 821 | * If you need to render a template AND customize the response status, 822 | * you should use Slim::render() instead. 823 | * 824 | * @param int $status The HTTP response status 825 | * @param string $message The HTTP response body 826 | * @return void 827 | */ 828 | public function halt( $status, $message = '') { 829 | if ( ob_get_level() !== 0 ) { 830 | ob_clean(); 831 | } 832 | $this->response->status($status); 833 | $this->response->body($message); 834 | $this->stop(); 835 | } 836 | 837 | /** 838 | * Pass 839 | * 840 | * This method will cause the Router::dispatch method to ignore 841 | * the current route and continue to the next matching route in the 842 | * dispatch loop. If no subsequent mathing routes are found, 843 | * a 404 Not Found response will be sent to the client. 844 | * 845 | * @throws Slim_Exception_Pass 846 | * @return void 847 | */ 848 | public function pass() { 849 | if ( ob_get_level() !== 0 ) { 850 | ob_clean(); 851 | } 852 | throw new Slim_Exception_Pass(); 853 | } 854 | 855 | /** 856 | * Set the HTTP response Content-Type 857 | * @param string $type The Content-Type for the Response (ie. text/html) 858 | * @return void 859 | */ 860 | public function contentType( $type ) { 861 | $this->response->header('Content-Type', $type); 862 | } 863 | 864 | /** 865 | * Set the HTTP response status code 866 | * @param int $status The HTTP response status code 867 | * @return void 868 | */ 869 | public function status( $code ) { 870 | $this->response->status($code); 871 | } 872 | 873 | /** 874 | * Get the URL for a named Route 875 | * @param string $name The route name 876 | * @param array $params Key-value array of URL parameters 877 | * @throws RuntimeException If named route does not exist 878 | * @return string 879 | */ 880 | public function urlFor( $name, $params = array() ) { 881 | return $this->router->urlFor($name, $params); 882 | } 883 | 884 | /** 885 | * Redirect 886 | * 887 | * This method immediately redirects to a new URL. By default, 888 | * this issues a 302 Found response; this is considered the default 889 | * generic redirect response. You may also specify another valid 890 | * 3xx status code if you want. This method will automatically set the 891 | * HTTP Location header for you using the URL parameter and place the 892 | * destination URL into the response body. 893 | * 894 | * @param string $url The destination URL 895 | * @param int $status The HTTP redirect status code (Optional) 896 | * @throws InvalidArgumentException If status parameter is not a valid 3xx status code 897 | * @return void 898 | */ 899 | public function redirect( $url, $status = 302 ) { 900 | if ( $status >= 300 && $status <= 307 ) { 901 | $this->response->header('Location', (string)$url); 902 | $this->halt($status, (string)$url); 903 | } else { 904 | throw new InvalidArgumentException('Slim::redirect only accepts HTTP 300-307 status codes.'); 905 | } 906 | } 907 | 908 | /***** FLASH *****/ 909 | 910 | /** 911 | * Set flash message for subsequent request 912 | * @param string $key 913 | * @param mixed $value 914 | * @return void 915 | */ 916 | public function flash( $key, $value ) { 917 | $this->view->getData('flash')->set($key, $value); 918 | } 919 | 920 | /** 921 | * Set flash message for current request 922 | * @param string $key 923 | * @param mixed $value 924 | * @return void 925 | */ 926 | public function flashNow( $key, $value ) { 927 | $this->view->getData('flash')->now($key, $value); 928 | } 929 | 930 | /** 931 | * Keep flash messages from previous request for subsequent request 932 | * @return void 933 | */ 934 | public function flashKeep() { 935 | $this->view->getData('flash')->keep(); 936 | } 937 | 938 | /***** HOOKS *****/ 939 | 940 | /** 941 | * Assign hook 942 | * @param string $name The hook name 943 | * @param mixed $callable A callable object 944 | * @param int $priority The hook priority; 0 = high, 10 = low 945 | * @return void 946 | */ 947 | public function hook( $name, $callable, $priority = 10 ) { 948 | if ( !isset($this->hooks[$name]) ) { 949 | $this->hooks[$name] = array(array()); 950 | } 951 | if ( is_callable($callable) ) { 952 | $this->hooks[$name][(int)$priority][] = $callable; 953 | } 954 | } 955 | 956 | /** 957 | * Invoke hook 958 | * @param string $name The hook name 959 | * @param mixed $hookArgs (Optional) Argument for hooked functions 960 | * @return mixed 961 | */ 962 | public function applyHook( $name, $hookArg = null ) { 963 | if ( !isset($this->hooks[$name]) ) { 964 | $this->hooks[$name] = array(array()); 965 | } 966 | if( !empty($this->hooks[$name]) ) { 967 | // Sort by priority, low to high, if there's more than one priority 968 | if ( count($this->hooks[$name]) > 1 ) { 969 | ksort($this->hooks[$name]); 970 | } 971 | foreach( $this->hooks[$name] as $priority ) { 972 | if( !empty($priority) ) { 973 | foreach($priority as $callable) { 974 | $hookArg = call_user_func($callable, $hookArg); 975 | } 976 | } 977 | } 978 | return $hookArg; 979 | } 980 | } 981 | 982 | /** 983 | * Get hook listeners 984 | * 985 | * Return an array of registered hooks. If `$name` is a valid 986 | * hook name, only the listeners attached to that hook are returned. 987 | * Else, all listeners are returned as an associative array whose 988 | * keys are hook names and whose values are arrays of listeners. 989 | * 990 | * @param string $name A hook name (Optional) 991 | * @return array|null 992 | */ 993 | public function getHooks( $name = null ) { 994 | if ( !is_null($name) ) { 995 | return isset($this->hooks[(string)$name]) ? $this->hooks[(string)$name] : null; 996 | } else { 997 | return $this->hooks; 998 | } 999 | } 1000 | 1001 | /** 1002 | * Clear hook listeners 1003 | * 1004 | * Clear all listeners for all hooks. If `$name` is 1005 | * a valid hook name, only the listeners attached 1006 | * to that hook will be cleared. 1007 | * 1008 | * @param string $name A hook name (Optional) 1009 | * @return void 1010 | */ 1011 | public function clearHooks( $name = null ) { 1012 | if ( !is_null($name) && isset($this->hooks[(string)$name]) ) { 1013 | $this->hooks[(string)$name] = array(array()); 1014 | } else { 1015 | foreach( $this->hooks as $key => $value ) { 1016 | $this->hooks[$key] = array(array()); 1017 | } 1018 | } 1019 | } 1020 | 1021 | /***** RUN SLIM *****/ 1022 | 1023 | /** 1024 | * Run the Slim application 1025 | * 1026 | * This method is the "meat and potatoes" of Slim and should be the last 1027 | * method called. This fires up Slim, invokes the Route that matches 1028 | * the current request, and returns the response to the client. 1029 | * 1030 | * This method will invoke the Not Found handler if no matching 1031 | * routes are found. 1032 | * 1033 | * This method will also catch any unexpected Exceptions thrown by this 1034 | * application; the Exceptions will be logged to this application's log 1035 | * and rethrown to the global Exception handler. 1036 | * 1037 | * @return void 1038 | */ 1039 | public function run() { 1040 | try { 1041 | try { 1042 | $this->applyHook('slim.before'); 1043 | ob_start(); 1044 | $this->applyHook('slim.before.router'); 1045 | $dispatched = false; 1046 | $httpMethod = $this->request()->getMethod(); 1047 | $httpMethodsAllowed = array(); 1048 | foreach ( $this->router as $route ) { 1049 | if ( $route->supportsHttpMethod($httpMethod) ) { 1050 | try { 1051 | $this->applyHook('slim.before.dispatch'); 1052 | $dispatched = $route->dispatch(); 1053 | $this->applyHook('slim.after.dispatch'); 1054 | if ( $dispatched ) { 1055 | break; 1056 | } 1057 | } catch ( Slim_Exception_Pass $e ) { 1058 | continue; 1059 | } 1060 | } else { 1061 | $httpMethodsAllowed = array_merge($httpMethodsAllowed, $route->getHttpMethods()); 1062 | } 1063 | } 1064 | if ( !$dispatched ) { 1065 | if ( $httpMethodsAllowed ) { 1066 | $this->response()->header('Allow', implode(' ', $httpMethodsAllowed)); 1067 | $this->halt(405); 1068 | } else { 1069 | $this->notFound(); 1070 | } 1071 | } 1072 | $this->response()->write(ob_get_clean()); 1073 | $this->applyHook('slim.after.router'); 1074 | $this->view->getData('flash')->save(); 1075 | session_write_close(); 1076 | $this->response->send(); 1077 | $this->applyHook('slim.after'); 1078 | } catch ( Slim_Exception_RequestSlash $e ) { 1079 | $this->redirect($this->request->getRootUri() . $this->request->getResourceUri() . '/', 301); 1080 | } catch ( Exception $e ) { 1081 | if ( $e instanceof Slim_Exception_Stop ) throw $e; 1082 | $this->getLog()->error($e); 1083 | if ( $this->config('debug') === true ) { 1084 | $this->halt(500, self::generateErrorMarkup($e->getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString())); 1085 | } else { 1086 | $this->error($e); 1087 | } 1088 | } 1089 | } catch ( Slim_Exception_Stop $e ) { 1090 | //Exit application context 1091 | } 1092 | } 1093 | 1094 | /***** EXCEPTION AND ERROR HANDLING *****/ 1095 | 1096 | /** 1097 | * Handle errors 1098 | * 1099 | * This is the global Error handler that will catch reportable Errors 1100 | * and convert them into ErrorExceptions that are caught and handled 1101 | * by each Slim application. 1102 | * 1103 | * @param int $errno The numeric type of the Error 1104 | * @param string $errstr The error message 1105 | * @param string $errfile The absolute path to the affected file 1106 | * @param int $errline The line number of the error in the affected file 1107 | * @return true 1108 | * @throws ErrorException 1109 | */ 1110 | public static function handleErrors( $errno, $errstr = '', $errfile = '', $errline = '' ) { 1111 | if ( error_reporting() & $errno ) { 1112 | throw new ErrorException($errstr, $errno, 0, $errfile, $errline); 1113 | } 1114 | return true; 1115 | } 1116 | 1117 | /** 1118 | * Generate markup for error message 1119 | * 1120 | * This method accepts details about an error or exception and 1121 | * generates HTML markup for the 500 response body that will 1122 | * be sent to the client. 1123 | * 1124 | * @param string $message The error message 1125 | * @param string $file The absolute file path to the affected file 1126 | * @param int $line The line number in the affected file 1127 | * @param string $trace A stack trace of the error 1128 | * @return string 1129 | */ 1130 | protected static function generateErrorMarkup( $message, $file = '', $line = '', $trace = '' ) { 1131 | $body = '

The application could not run because of the following error:

'; 1132 | $body .= "

Details:

Message: $message
"; 1133 | if ( $file !== '' ) $body .= "File: $file
"; 1134 | if ( $line !== '' ) $body .= "Line: $line
"; 1135 | if ( $trace !== '' ) $body .= '

Stack Trace:

' . nl2br($trace); 1136 | return self::generateTemplateMarkup('Slim Application Error', $body); 1137 | } 1138 | 1139 | /** 1140 | * Generate default template markup 1141 | * 1142 | * This method accepts a title and body content to generate 1143 | * an HTML page. This is primarily used to generate the layout markup 1144 | * for Error handlers and Not Found handlers. 1145 | * 1146 | * @param string $title The title of the HTML template 1147 | * @param string $body The body content of the HTML template 1148 | * @return string 1149 | */ 1150 | protected static function generateTemplateMarkup( $title, $body ) { 1151 | $html = "$title"; 1152 | $html .= "

$title

"; 1153 | $html .= $body; 1154 | $html .= ''; 1155 | return $html; 1156 | } 1157 | 1158 | /** 1159 | * Default Not Found handler 1160 | * @return void 1161 | */ 1162 | protected function defaultNotFound() { 1163 | echo self::generateTemplateMarkup('404 Page Not Found', '

The page you are looking for could not be found. Check the address bar to ensure your URL is spelled correctly. If all else fails, you can visit our home page at the link below.

Visit the Home Page'); 1164 | } 1165 | 1166 | /** 1167 | * Default Error handler 1168 | * @return void 1169 | */ 1170 | protected function defaultError() { 1171 | echo self::generateTemplateMarkup('Error', '

A website error has occured. The website administrator has been notified of the issue. Sorry for the temporary inconvenience.

'); 1172 | } 1173 | 1174 | } -------------------------------------------------------------------------------- /Slim/View.php: -------------------------------------------------------------------------------- 1 | 6 | * @copyright 2011 Josh Lockhart 7 | * @link http://www.slimframework.com 8 | * @license http://www.slimframework.com/license 9 | * @version 1.5.0 10 | * 11 | * MIT LICENSE 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 28 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 29 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 30 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | */ 32 | 33 | /** 34 | * Slim View 35 | * 36 | * The View is responsible for rendering and/or displaying a template. 37 | * It is recommended that you subclass View and re-implement the 38 | * `View::render` method to use a custom templating engine such as 39 | * Smarty, Twig, Mustache, etc. It is important that `View::render` 40 | * `return` the final template output. Do not `echo` the output. 41 | * 42 | * @package Slim 43 | * @author Josh Lockhart 44 | * @since Version 1.0 45 | */ 46 | class Slim_View { 47 | 48 | /** 49 | * @var array Key-value array of data available to the template 50 | */ 51 | protected $data = array(); 52 | 53 | /** 54 | * @var string Absolute or relative path to the templates directory 55 | */ 56 | protected $templatesDirectory; 57 | 58 | /** 59 | * Constructor 60 | * 61 | * This is empty but may be overridden in a subclass 62 | */ 63 | public function __construct() {} 64 | 65 | /** 66 | * Get data 67 | * @param string $key 68 | * @return array|mixed|null All View data if no $key, value of datum 69 | * if $key, or NULL if $key but datum 70 | * does not exist. 71 | */ 72 | public function getData( $key = null ) { 73 | if ( !is_null($key) ) { 74 | return isset($this->data[$key]) ? $this->data[$key] : null; 75 | } else { 76 | return $this->data; 77 | } 78 | } 79 | 80 | /** 81 | * Set data 82 | * 83 | * This method is overloaded to accept two different method signatures. 84 | * You may use this to set a specific key with a specfic value, 85 | * or you may use this to set all data to a specific array. 86 | * 87 | * USAGE: 88 | * 89 | * View::setData('color', 'red'); 90 | * View::setData(array('color' => 'red', 'number' => 1)); 91 | * 92 | * @param string|array 93 | * @param mixed Optional. Only use if first argument is a string. 94 | * @return void 95 | * @throws InvalidArgumentException If incorrect method signature 96 | */ 97 | public function setData() { 98 | $args = func_get_args(); 99 | if ( count($args) === 1 && is_array($args[0]) ) { 100 | $this->data = $args[0]; 101 | } else if ( count($args) === 2 ) { 102 | $this->data[(string)$args[0]] = $args[1]; 103 | } else { 104 | throw new InvalidArgumentException('Cannot set View data with provided arguments. Usage: `View::setData( $key, $value );` or `View::setData([ key => value, ... ]);`'); 105 | } 106 | } 107 | 108 | /** 109 | * Append data to existing View data 110 | * @param array $data 111 | * @return void 112 | */ 113 | public function appendData( array $data ) { 114 | $this->data = array_merge($this->data, $data); 115 | } 116 | 117 | /** 118 | * Get templates directory 119 | * @return string|null Path to templates directory without trailing slash 120 | */ 121 | public function getTemplatesDirectory() { 122 | return $this->templatesDirectory; 123 | } 124 | 125 | /** 126 | * Set templates directory 127 | * @param string $dir 128 | * @return void 129 | * @throws RuntimeException If directory is not a directory or does not exist 130 | */ 131 | public function setTemplatesDirectory( $dir ) { 132 | if ( !is_dir($dir) ) { 133 | throw new RuntimeException('Cannot set View templates directory to: ' . $dir . '. Directory does not exist.'); 134 | } 135 | $this->templatesDirectory = rtrim($dir, '/'); 136 | } 137 | 138 | /** 139 | * Display template 140 | * 141 | * This method echoes the rendered template to the current output buffer 142 | * 143 | * @param string $template Path to template file relative to templates directoy 144 | * @return void 145 | */ 146 | public function display( $template ) { 147 | echo $this->render($template); 148 | } 149 | 150 | /** 151 | * Render template 152 | * @param string $template Path to template file relative to templates directory 153 | * @return string Rendered template 154 | * @throws RuntimeException If template does not exist 155 | */ 156 | public function render( $template ) { 157 | extract($this->data); 158 | $templatePath = $this->getTemplatesDirectory() . '/' . ltrim($template, '/'); 159 | if ( !file_exists($templatePath) ) { 160 | throw new RuntimeException('View cannot render template `' . $templatePath . '`. Template does not exist.'); 161 | } 162 | ob_start(); 163 | require $templatePath; 164 | return ob_get_clean(); 165 | } 166 | 167 | } -------------------------------------------------------------------------------- /aprs_func.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | get('/', function () { 12 | 13 | // Require HTML Template 14 | require('templates/main.php'); 15 | }); 16 | 17 | // Generate Passcode 18 | $app->post('/passcode', function () { 19 | // Include function to generate APRS Code 20 | require('aprs_func.php'); 21 | 22 | // Store Passcode 23 | $passcode = aprspass($_POST['callsign']); 24 | 25 | // Load Page 26 | require('templates/passcode.php'); 27 | }); 28 | 29 | 30 | // Run slim 31 | $app->run(); 32 | 33 | ?> -------------------------------------------------------------------------------- /templates/main.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | APRS Passcode Generator 6 | 25 | 26 | 27 | 28 |
29 | 30 |

APRS Passcode Generator

31 | 32 | 33 |
34 | 35 | 36 | 37 |
38 | 39 | 40 |

Technical Example of Passcode Generation using PHP

41 |

Source code available on Github

42 |
43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /templates/passcode.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | APRS Passcode Generator - Passcode 7 | 29 | 30 | 31 | 32 |
33 | 34 |

APRS Passcode Generator

35 | 36 |

Your Passcode:

37 | 38 |

Generate a new Passcode

39 | 40 |

Techical Example of Passcode Generation using PHP

41 |

Source code available on Github

42 |
43 | 44 | 45 | --------------------------------------------------------------------------------