Added function to generate the passcode

This commit is contained in:
Peter Goodhall 2012-11-22 21:19:06 +00:00
parent 6f14b71386
commit 6f03be8cf6
21 changed files with 2858 additions and 2174 deletions

View File

@ -2,12 +2,11 @@
/** /**
* Slim - a micro PHP 5 framework * Slim - a micro PHP 5 framework
* *
* @author Josh Lockhart <info@slimframework.com> * @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart * @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com * @link http://www.slimframework.com
* @license http://www.slimframework.com/license * @license http://www.slimframework.com/license
* @version 2.0.0 * @version 1.5.0
* @package Slim
* *
* MIT LICENSE * MIT LICENSE
* *
@ -30,7 +29,6 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
namespace Slim\Exception;
/** /**
* Pass Exception * Pass Exception
@ -41,10 +39,7 @@ namespace Slim\Exception;
* HTTP 404 Not Found response will be sent to the client. * HTTP 404 Not Found response will be sent to the client.
* *
* @package Slim * @package Slim
* @author Josh Lockhart * @author Josh Lockhart <info@joshlockhart.com>
* @since 1.0.0 * @since Version 1.0
*/ */
class Pass extends \Exception class Slim_Exception_Pass extends Exception {}
{
}

View File

@ -2,12 +2,11 @@
/** /**
* Slim - a micro PHP 5 framework * Slim - a micro PHP 5 framework
* *
* @author Josh Lockhart <info@slimframework.com> * @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart * @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com * @link http://www.slimframework.com
* @license http://www.slimframework.com/license * @license http://www.slimframework.com/license
* @version 2.0.0 * @version 1.5.0
* @package Slim
* *
* MIT LICENSE * MIT LICENSE
* *
@ -30,7 +29,6 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
namespace Slim\Exception;
/** /**
* Request Slash Exception * Request Slash Exception
@ -42,10 +40,7 @@ namespace Slim\Exception;
* to the same resource URI with a trailing slash. * to the same resource URI with a trailing slash.
* *
* @package Slim * @package Slim
* @author Josh Lockhart * @author Josh Lockhart <info@joshlockhart.com>
* @since 1.0.0 * @since Version 1.0
*/ */
class RequestSlash extends \Exception class Slim_Exception_RequestSlash extends Exception {}
{
}

View File

@ -2,12 +2,11 @@
/** /**
* Slim - a micro PHP 5 framework * Slim - a micro PHP 5 framework
* *
* @author Josh Lockhart <info@slimframework.com> * @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart * @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com * @link http://www.slimframework.com
* @license http://www.slimframework.com/license * @license http://www.slimframework.com/license
* @version 2.0.0 * @version 1.5.0
* @package Slim
* *
* MIT LICENSE * MIT LICENSE
* *
@ -30,7 +29,6 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
namespace Slim\Exception;
/** /**
* Stop Exception * Stop Exception
@ -39,10 +37,7 @@ namespace Slim\Exception;
* processing and return control flow to the outer PHP script. * processing and return control flow to the outer PHP script.
* *
* @package Slim * @package Slim
* @author Josh Lockhart * @author Josh Lockhart <info@joshlockhart.com>
* @since 1.0.0 * @since Version 1.0
*/ */
class Stop extends \Exception class Slim_Exception_Stop extends Exception {}
{
}

222
Slim/Http/Cookie.php Normal file
View File

@ -0,0 +1,222 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.5.0
*
* MIT LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Cookie
*
* Object-oriented representation of a Cookie to be sent in an HTTP response
*
* @package Slim
* @author Josh Lockhart <info@joshlockhart.com>
* @since Version 1.0
*/
class Slim_Http_Cookie {
/**
* @var string
*/
protected $name;
/**
* @var string
*/
protected $value;
/**
* @var int UNIX timestamp
*/
protected $expires;
/**
* @var string
*/
protected $path;
/**
* @var string
*/
protected $domain;
/**
* @var bool
*/
protected $secure;
/**
* @var bool
*/
protected $httponly;
/**
* Constructor
* @param string $name The cookie name
* @param string $value The cookie value
* @param mixed $time The duration of the cookie;
* If integer, should be a UNIX timestamp;
* If string, converted to UNIX timestamp with `strtotime`;
* @param string $path The path on the server in which the cookie will be available on
* @param string $domain The domain that the cookie is available to
* @param bool $secure Indicates that the cookie should only be transmitted over a secure
* HTTPS connection from the client
* @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol
* @return void
*/
public function __construct( $name, $value = null, $expires = 0, $path = null, $domain = null, $secure = false, $httponly = false ) {
$this->setName($name);
$this->setValue($value);
$this->setExpires($expires);
$this->setPath($path);
$this->setDomain($domain);
$this->setSecure($secure);
$this->setHttpOnly($httponly);
}
/**
* Get cookie name
* @return string
*/
public function getName() {
return $this->name;
}
/**
* Set cookie name
* @param string $name
* @return void
*/
public function setName( $name ) {
$this->name = (string)$name;
}
/**
* Get cookie value
* @return string
*/
public function getValue() {
return $this->value;
}
/**
* Set cookie value
* @param string $value
* @return void
*/
public function setValue( $value ) {
$this->value = (string)$value;
}
/**
* Get cookie expiration time
* @return int UNIX timestamp
*/
public function getExpires() {
return $this->expires;
}
/**
* Set cookie expiration time
* @param string|int Cookie expiration time
* @return void
*/
public function setExpires( $time ) {
$this->expires = is_string($time) ? strtotime($time) : (int)$time;
}
/**
* Get cookie path
* @return string
*/
public function getPath() {
return $this->path;
}
/**
* Set cookie path
* @param string $path
* @return void
*/
public function setPath( $path ) {
$this->path = (string)$path;
}
/**
* Get cookie domain
* @return string
*/
public function getDomain() {
return $this->domain;
}
/**
* Set cookie domain
* @param string $domain
* @return void
*/
public function setDomain( $domain ) {
$this->domain = (string)$domain;
}
/**
* Is cookie sent only if SSL/HTTPS is used?
* @return bool
*/
public function getSecure() {
return $this->secure;
}
/**
* Set whether cookie is sent only if SSL/HTTPS is used
* @param bool $secure
* @return void
*/
public function setSecure( $secure ) {
$this->secure = (bool)$secure;
}
/**
* Is cookie sent with HTTP protocol only?
* @return bool
*/
public function getHttpOnly() {
return $this->httponly;
}
/**
* Set whether cookie is sent with HTTP protocol only
* @param bool $httponly
* @return void
*/
public function setHttpOnly( $httponly ) {
$this->httponly = (bool)$httponly;
}
}

401
Slim/Http/CookieJar.php Normal file
View File

@ -0,0 +1,401 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.5.0
*
* MIT LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Cooke Jar
*
* Used to manage signed, encrypted Cookies. Provides:
*
* - Cookie integrity and authenticity with HMAC
* - Confidentiality with symmetric encryption
* - Protection from replay attack if using SSL or TLS
* - Protection from interception if using SSL or TLS
*
* This code was originally called "BigOrNot_CookieManager" and written by
* Matthieu Huguet released under "CopyLeft" license. I have cleaned up the
* code formatting to conform with Slim Framework contributor guidelines and
* added additional code where necessary to play nice with Slim Cookie objects.
*
* Requirements:
*
* - libmcrypt > 2.4.x
*
* @author Matthies Huguet <http://bigornot.blogspot.com/2008/06/security-cookies-and-rest.html>
*/
class Slim_Http_CookieJar {
/**
* @var string Server secret key
*/
protected $_secret = '';
/**
* @var int Cryptographic algorithm used to encrypt cookies data
*/
protected $_algorithm = MCRYPT_RIJNDAEL_256;
/**
* @var int Cryptographic mode (CBC, CFB ...)
*/
protected $_mode = MCRYPT_MODE_CBC;
/**
* @var resource mcrypt module resource
*/
protected $_cryptModule = null;
/**
* @var bool Enable high confidentiality for cookie value (symmetric encryption)
*/
protected $_highConfidentiality = true;
/**
* @var bool Enable SSL support
*/
protected $_ssl = false;
/**
* @var array[Cookie] Cookie objects
*/
protected $_cookies = array();
/**
* Constructor
*
* Initialize cookie manager and mcrypt module.
*
* @param string $secret Server's secret key
* @param array $config
* @throws Exception If secret key is empty
* @throws Exception If unable to open mcypt module
*/
public function __construct( $secret, $config = null ) {
if ( empty($secret) ) {
throw new Exception('You must provide a secret key');
}
$this->_secret = $secret;
if ( $config !== null && !is_array($config) ) {
throw new Exception('Config must be an array');
}
if ( is_array($config) ) {
if ( isset($config['high_confidentiality']) ) {
$this->_highConfidentiality = $config['high_confidentiality'];
}
if ( isset($config['mcrypt_algorithm']) ) {
$this->_algorithm = $config['mcrypt_algorithm'];
}
if ( isset($config['mcrypt_mode']) ) {
$this->_mode = $config['mcrypt_mode'];
}
if ( isset($config['enable_ssl']) ) {
$this->_ssl = $config['enable_ssl'];
}
}
if ( extension_loaded('mcrypt') ) {
$this->_cryptModule = mcrypt_module_open($this->_algorithm, '', $this->_mode, '');
if ( $this->_cryptModule === false ) {
throw new Exception('Error while loading mcrypt module');
}
}
}
/**
* Get the high confidentiality mode
*
* @return bool TRUE if cookie data encryption is enabled, or FALSE if it isn't
*/
public function getHighConfidentiality() {
return $this->_highConfidentiality;
}
/**
* Enable or disable cookie data encryption
*
* @param bool $enable TRUE to enable, FALSE to disable
* @return CookieJar
*/
public function setHighConfidentiality( $enable ) {
$this->_highConfidentiality = (bool)$enable;
return $this;
}
/**
* Get the SSL status (enabled or disabled?)
*
* @return bool TRUE if SSL support is enabled, or FALSE if it isn't
*/
public function getSSL() {
return $this->_ssl;
}
/**
* Enable SSL support (not enabled by default)
*
* Pro: Protect against replay attack
* Con: Cookie's lifetime is limited to SSL session's lifetime
*
* @param bool $enable TRUE to enable, FALSE to disable
* @return CookieJar
*/
public function setSSL( $enable ) {
$this->_ssl = (bool)$enable;
return $this;
}
/**
* Get Cookies for Response
*
* @author Josh Lockhart <info@joshlockhart.com>
* @return array[Cookie]
*/
public function getResponseCookies() {
return $this->_cookies;
}
/**
* Get Cookie with name for Response
*
* @author Josh Lockhart <info@joshlockhart.com>
* @param string $cookiename The name of the Cookie
* @return Cookie|null Cookie, or NULL if Cookie with name not found
*/
public function getResponseCookie( $cookiename ) {
return isset($this->_cookies[$cookiename]) ? $this->_cookies[$cookiename] : null;
}
/**
* Set a secure cookie
*
* @param string $name Cookie name
* @param string $value Cookie value
* @param string $username User identifier
* @param integer $expire Expiration time
* @param string $path Cookie path
* @param string $domain Cookie domain
* @param bool $secure When TRUE, send the cookie only on a secure connection
* @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol
*/
public function setCookie( $cookiename, $value, $username, $expire = 0, $path = '/', $domain = '', $secure = false, $httponly = null ) {
$secureValue = extension_loaded('mcrypt') ? $this->_secureCookieValue($value, $username, $expire) : $value;
$this->setClassicCookie($cookiename, $secureValue, $expire, $path, $domain, $secure, $httponly);
}
/**
* Delete a cookie
*
* @param string $name Cookie name
* @param string $path Cookie path
* @param string $domain Cookie domain
* @param bool $secure When TRUE, send the cookie only on a secure connection
* @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol
*/
public function deleteCookie( $name, $path = '/', $domain = '', $secure = false, $httponly = null ) {
$expire = 315554400; /* 1980-01-01 */
$this->_cookies[$name] = new Slim_Http_Cookie($name, '', $expire, $path, $domain, $secure, $httponly);
//setcookie($name, '', $expire, $path, $domain, $secure, $httponly);
}
/**
* Get a secure cookie value
*
* Verify the integrity of cookie data and decrypt it. If the cookie
* is invalid, it can be automatically destroyed (default behaviour)
*
* @param string $cookiename Cookie name
* @param bool $delete Destroy the cookie if invalid?
* @return string|false The Cookie value, or FALSE if Cookie invalid
*/
public function getCookieValue( $cookiename, $deleteIfInvalid = true ) {
if ( $this->cookieExists($cookiename) ) {
if ( extension_loaded('mcrypt') ) {
$cookieValues = explode('|', $_COOKIE[$cookiename]);
if ( (count($cookieValues) === 4) && ($cookieValues[1] == 0 || $cookieValues[1] >= time()) ) {
$key = hash_hmac('sha1', $cookieValues[0] . $cookieValues[1], $this->_secret);
$cookieData = base64_decode($cookieValues[2]);
if ( $cookieData !== '' && $this->getHighConfidentiality() ) {
$data = $this->_decrypt($cookieData, $key, md5($cookieValues[1]));
} else {
$data = $cookieData;
}
if ( $this->_ssl && isset($_SERVER['SSL_SESSION_ID']) ) {
$verifKey = hash_hmac('sha1', $cookieValues[0] . $cookieValues[1] . $data . $_SERVER['SSL_SESSION_ID'], $key);
} else {
$verifKey = hash_hmac('sha1', $cookieValues[0] . $cookieValues[1] . $data, $key);
}
if ( $verifKey == $cookieValues[3] ) {
return $data;
}
}
} else {
return $_COOKIE[$cookiename];
}
}
if ( $deleteIfInvalid ) {
$this->deleteCookie($cookiename);
}
return false;
}
/**
* Send a classic (unsecure) cookie
*
* @param string $name Cookie name
* @param string $value Cookie value
* @param integer $expire Expiration time
* @param string $path Cookie path
* @param string $domain Cookie domain
* @param bool $secure When TRUE, send the cookie only on a secure connection
* @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol
*/
public function setClassicCookie( $cookiename, $value, $expire = 0, $path = '/', $domain = '', $secure = false, $httponly = null ) {
/* httponly option is only available for PHP version >= 5.2 */
if ( $httponly === null ) {
$this->_cookies[$cookiename] = new Slim_Http_Cookie($cookiename, $value, $expire, $path, $domain, $secure);
//setcookie($cookiename, $value, $expire, $path, $domain, $secure);
} else {
$this->_cookies[$cookiename] = new Slim_Http_Cookie($cookiename, $value, $expire, $path, $domain, $secure, $httponly);
//setcookie($cookiename, $value, $expire, $path, $domain, $secure, $httponly);
}
}
/**
* Verify if a cookie exists
*
* @param string $cookiename
* @return bool TRUE if cookie exist, or FALSE if not
*/
public function cookieExists($cookiename) {
return isset($_COOKIE[$cookiename]);
}
/**
* Secure a cookie value
*
* The initial value is transformed with this protocol:
*
* secureValue = username|expire|base64((value)k,expire)|HMAC(user|expire|value,k)
* where k = HMAC(user|expire, sk)
* and sk is server's secret key
* (value)k,md5(expire) is the result an cryptographic function (ex: AES256) on "value" with key k and initialisation vector = md5(expire)
*
* @param string $value Unsecure value
* @param string $username User identifier
* @param integer $expire Expiration time
* @return string Secured value
*/
protected function _secureCookieValue( $value, $username, $expire ) {
if ( is_string($expire) ) {
$expire = strtotime($expire);
}
$key = hash_hmac('sha1', $username . $expire, $this->_secret);
if ( $value !== '' && $this->getHighConfidentiality() ) {
$encryptedValue = base64_encode($this->_encrypt($value, $key, md5($expire)));
} else {
$encryptedValue = base64_encode($value);
}
if ( $this->_ssl && isset($_SERVER['SSL_SESSION_ID']) ) {
$verifKey = hash_hmac('sha1', $username . $expire . $value . $_SERVER['SSL_SESSION_ID'], $key);
} else {
$verifKey = hash_hmac('sha1', $username . $expire . $value, $key);
}
$result = array($username, $expire, $encryptedValue, $verifKey);
return implode('|', $result);
}
/**
* Encrypt a given data with a given key and a given initialisation vector
*
* @param string $data Data to crypt
* @param string $key Secret key
* @param string $iv Initialisation vector
* @return string Encrypted data
*/
protected function _encrypt( $data, $key, $iv ) {
$iv = $this->_validateIv($iv);
$key = $this->_validateKey($key);
mcrypt_generic_init($this->_cryptModule, $key, $iv);
$res = @mcrypt_generic($this->_cryptModule, $data);
mcrypt_generic_deinit($this->_cryptModule);
return $res;
}
/**
* Decrypt a given data with a given key and a given initialisation vector
*
* @param string $data Data to crypt
* @param string $key Secret key
* @param string $iv Initialisation vector
* @return string Encrypted data
*/
protected function _decrypt( $data, $key, $iv ) {
$iv = $this->_validateIv($iv);
$key = $this->_validateKey($key);
mcrypt_generic_init($this->_cryptModule, $key, $iv);
$decryptedData = mdecrypt_generic($this->_cryptModule, $data);
$res = str_replace("\x0", '', $decryptedData);
mcrypt_generic_deinit($this->_cryptModule);
return $res;
}
/**
* Validate Initialization vector
*
* If given IV is too long for the selected mcrypt algorithm, it will be truncated
*
* @param string $iv Initialization vector
* @return string
*/
protected function _validateIv($iv) {
$ivSize = mcrypt_enc_get_iv_size($this->_cryptModule);
if ( strlen($iv) > $ivSize ) {
$iv = substr($iv, 0, $ivSize);
}
return $iv;
}
/**
* Validate key
*
* If given key is too long for the selected mcrypt algorithm, it will be truncated
*
* @param string $key key
* @param string
*/
protected function _validateKey($key) {
$keySize = mcrypt_enc_get_key_size($this->_cryptModule);
if ( strlen($key) > $keySize ) {
$key = substr($key, 0, $keySize);
}
return $key;
}
}

View File

@ -2,12 +2,11 @@
/** /**
* Slim - a micro PHP 5 framework * Slim - a micro PHP 5 framework
* *
* @author Josh Lockhart <info@slimframework.com> * @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart * @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com * @link http://www.slimframework.com
* @license http://www.slimframework.com/license * @license http://www.slimframework.com/license
* @version 2.0.0 * @version 1.5.0
* @package Slim
* *
* MIT LICENSE * MIT LICENSE
* *
@ -30,20 +29,24 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
namespace Slim\Http;
/** /**
* Slim HTTP Request * Request
* *
* This class provides a human-friendly interface to the Slim environment variables; * Object-oriented representation of an HTTP request. This class
* environment variables are passed by reference and will be modified directly. * is responsible for parsing the raw HTTP request into a format
* usable by the Slim application.
*
* This class will automatically remove slashes from GET, POST, PUT,
* and Cookie data if magic quotes are enabled.
* *
* @package Slim * @package Slim
* @author Josh Lockhart * @author Josh Lockhart <info@joshlockhart.com>
* @since 1.0.0 * @author Kris Jordan <http://www.github.com/KrisJordan>
* @since Version 1.0
*/ */
class Request class Slim_Http_Request {
{
const METHOD_HEAD = 'HEAD'; const METHOD_HEAD = 'HEAD';
const METHOD_GET = 'GET'; const METHOD_GET = 'GET';
const METHOD_POST = 'POST'; const METHOD_POST = 'POST';
@ -53,533 +56,350 @@ class Request
const METHOD_OVERRIDE = '_METHOD'; const METHOD_OVERRIDE = '_METHOD';
/** /**
* @var array * @var string Request method (ie. "GET", "POST", "PUT", "DELETE", "HEAD")
*/ */
protected static $formDataMediaTypes = array('application/x-www-form-urlencoded'); protected $method;
/** /**
* @var array * @var array Key-value array of HTTP request headers
*/ */
protected $env; protected $headers;
/**
* @var array Names of additional headers to parse from the current
* HTTP request that are not prefixed with "HTTP_"
*/
protected $additionalHeaders = array('content-type', 'content-length', 'php-auth-user', 'php-auth-pw', 'auth-type', 'x-requested-with');
/**
* @var array Key-value array of cookies sent with the
* current HTTP request
*/
protected $cookies;
/**
* @var array Key-value array of HTTP GET parameters
*/
protected $get;
/**
* @var array Key-value array of HTTP POST parameters
*/
protected $post;
/**
* @var array Key-value array of HTTP PUT parameters
*/
protected $put;
/**
* @var string Raw body of HTTP request
*/
protected $body;
/**
* @var string Content type of HTTP request
*/
protected $contentType;
/**
* @var string Resource URI (ie. "/person/1")
*/
protected $resource;
/**
* @var string The root URI of the Slim application without trailing slash.
* This will be "" if the app is installed at the web
* document root. If the app is installed in a
* sub-directory "/foo", this will be "/foo".
*/
protected $root;
/** /**
* Constructor * Constructor
* @param array $env
* @see \Slim\Environment
*/ */
public function __construct($env) public function __construct() {
{ $this->method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : false;
$this->env = $env; $this->headers = $this->loadHttpHeaders();
} $this->body = @file_get_contents('php://input');
$this->get = self::stripSlashesIfMagicQuotes($_GET);
/** $this->post = self::stripSlashesIfMagicQuotes($_POST);
* Get HTTP method $this->put = self::stripSlashesIfMagicQuotes($this->loadPutParameters());
* @return string $this->cookies = self::stripSlashesIfMagicQuotes($_COOKIE);
*/ $this->root = Slim_Http_Uri::getBaseUri(true);
public function getMethod() $this->resource = Slim_Http_Uri::getUri(true);
{ $this->checkForHttpMethodOverride();
return $this->env['REQUEST_METHOD'];
} }
/** /**
* Is this a GET request? * Is this a GET request?
* @return bool * @return bool
*/ */
public function isGet() public function isGet() {
{ return $this->method === self::METHOD_GET;
return $this->getMethod() === self::METHOD_GET;
} }
/** /**
* Is this a POST request? * Is this a POST request?
* @return bool * @return bool
*/ */
public function isPost() public function isPost() {
{ return $this->method === self::METHOD_POST;
return $this->getMethod() === self::METHOD_POST;
} }
/** /**
* Is this a PUT request? * Is this a PUT request?
* @return bool * @return bool
*/ */
public function isPut() public function isPut() {
{ return $this->method === self::METHOD_PUT;
return $this->getMethod() === self::METHOD_PUT;
} }
/** /**
* Is this a DELETE request? * Is this a DELETE request?
* @return bool * @return bool
*/ */
public function isDelete() public function isDelete() {
{ return $this->method === self::METHOD_DELETE;
return $this->getMethod() === self::METHOD_DELETE;
} }
/** /**
* Is this a HEAD request? * Is this a HEAD request?
* @return bool * @return bool
*/ */
public function isHead() public function isHead() {
{ return $this->method === self::METHOD_HEAD;
return $this->getMethod() === self::METHOD_HEAD;
} }
/** /**
* Is this a OPTIONS request? * Is this a OPTIONS request?
* @return bool * @return bool
*/ */
public function isOptions() public function isOptions() {
{ return $this->method === self::METHOD_OPTIONS;
return $this->getMethod() === self::METHOD_OPTIONS;
} }
/** /**
* Is this an AJAX request? * Is this a XHR request?
* @return bool * @return bool
*/ */
public function isAjax() public function isAjax() {
{ return ( $this->params('isajax') || $this->headers('X_REQUESTED_WITH') === 'XMLHttpRequest' );
if ($this->params('isajax')) {
return true;
} elseif (isset($this->env['X_REQUESTED_WITH']) && $this->env['X_REQUESTED_WITH'] === 'XMLHttpRequest') {
return true;
} else {
return false;
}
} }
/** /**
* Is this an XHR request? (alias of Slim_Http_Request::isAjax) * Fetch a PUT|POST|GET parameter value
* @return bool *
* The preferred method to fetch the value of a
* PUT, POST, or GET parameter (searched in that order).
*
* @param string $key The paramter name
* @return string|null The value of parameter, or NULL if parameter not found
*/ */
public function isXhr() public function params( $key ) {
{ foreach ( array('put', 'post', 'get') as $dataSource ) {
return $this->isAjax(); $source = $this->$dataSource;
if ( isset($source[(string)$key]) ) {
return $source[(string)$key];
}
} }
/**
* Fetch GET and POST data
*
* This method returns a union of GET and POST data as a key-value array, or the value
* of the array key if requested; if the array key does not exist, NULL is returned.
*
* @param string $key
* @return array|mixed|null
*/
public function params($key = null)
{
$union = array_merge($this->get(), $this->post());
if ($key) {
if (isset($union[$key])) {
return $union[$key];
} else {
return null; return null;
} }
} else {
return $union; /**
} * Fetch GET parameter(s)
* @param string $key Name of parameter
* @return array|string|null All parameters, parameter value if $key
* and parameter exists, or NULL if $key
* and parameter does not exist.
*/
public function get( $key = null ) {
return $this->arrayOrArrayValue($this->get, $key);
} }
/** /**
* Fetch GET data * Fetch POST parameter(s)
* * @param string $key Name of parameter
* This method returns a key-value array of data sent in the HTTP request query string, or * @return array|string|null All parameters, parameter value if $key
* the value of the array key if requested; if the array key does not exist, NULL is returned. * and parameter exists, or NULL if $key
* * and parameter does not exist.
*/
public function post( $key = null ) {
return $this->arrayOrArrayValue($this->post, $key);
}
/**
* Fetch PUT parameter(s)
* @param string $key Name of parameter
* @return array|string|null All parameters, parameter value if $key
* and parameter exists, or NULL if $key
* and parameter does not exist.
*/
public function put( $key = null ) {
return $this->arrayOrArrayValue($this->put, $key);
}
/**
* Fetch COOKIE value(s)
* @param string $key The cookie name
* @return array|string|null All parameters, parameter value if $key
* and parameter exists, or NULL if $key
* and parameter does not exist.
*/
public function cookies( $key = null ) {
return $this->arrayOrArrayValue($this->cookies, $key);
}
/**
* Get HTTP request header
* @param string $key The header name
* @return array|string|null All parameters, parameter value if $key
* and parameter exists, or NULL if $key
* and parameter does not exist.
*/
public function headers( $key = null ) {
return is_null($key) ? $this->headers : $this->arrayOrArrayValue($this->headers, $this->convertHttpHeaderName($key));
}
/**
* Get HTTP request body
* @return string|false String, or FALSE if body could not be read
*/
public function getBody() {
return $this->body;
}
/**
* Get HTTP method
* @return string
*/
public function getMethod() {
return $this->method;
}
/**
* Get HTTP request content type
* @return string
*/
public function getContentType() {
if ( !isset($this->contentType) ) {
$contentType = 'application/x-www-form-urlencoded';
$header = $this->headers('CONTENT_TYPE');
if ( !is_null($header) ) {
$headerParts = preg_split('/\s*;\s*/', $header);
$contentType = $headerParts[0];
}
$this->contentType = $contentType;
}
return $this->contentType;
}
/**
* Get HTTP request resource URI
* @return string
*/
public function getResourceUri() {
return $this->resource;
}
/**
* Get HTTP request root URI
* @return string
*/
public function getRootUri() {
return $this->root;
}
/**
* Fetch array or array value
* @param array $array
* @param string $key * @param string $key
* @return array|mixed|null * @return array|mixed Array if key is null, else array value
*/ */
public function get($key = null) protected function arrayOrArrayValue( array &$array, $key = null ) {
{ return is_null($key) ? $array : $this->arrayValueForKey($array, $key);
if (!isset($this->env['slim.request.query_hash'])) {
$output = array();
if (function_exists('mb_parse_str') && !isset($this->env['slim.tests.ignore_multibyte'])) {
mb_parse_str($this->env['QUERY_STRING'], $output);
} else {
parse_str($this->env['QUERY_STRING'], $output);
} }
$this->env['slim.request.query_hash'] = Util::stripSlashesIfMagicQuotes($output);
/**
* Fetch value from array
* @return mixed|null
*/
protected function arrayValueForKey( array &$array, $key ) {
return isset($array[(string)$key]) ? $array[(string)$key] : null;
} }
if ($key) {
if (isset($this->env['slim.request.query_hash'][$key])) { /**
return $this->env['slim.request.query_hash'][$key]; * Strip slashes from string or array of strings
* @param array|string $rawData
* @return array|string
*/
public static function stripSlashesIfMagicQuotes( $rawData ) {
if ( get_magic_quotes_gpc() ) {
return is_array($rawData) ? array_map(array('self', 'stripSlashesIfMagicQuotes'), $rawData) : stripslashes($rawData);
} else { } else {
return null; return $rawData;
}
} else {
return $this->env['slim.request.query_hash'];
} }
} }
/** /**
* Fetch POST data * Get PUT parameters
* * @return array Key-value array of HTTP request PUT parameters
* This method returns a key-value array of data sent in the HTTP request body, or
* the value of a hash key if requested; if the array key does not exist, NULL is returned.
*
* @param string $key
* @return array|mixed|null
* @throws \RuntimeException If environment input is not available
*/ */
public function post($key = null) protected function loadPutParameters() {
{ if ( $this->getContentType() === 'application/x-www-form-urlencoded' ) {
if (!isset($this->env['slim.input'])) { $input = is_string($this->body) ? $this->body : '';
throw new \RuntimeException('Missing slim.input in environment variables'); if ( function_exists('mb_parse_str') ) {
} mb_parse_str($input, $output);
if (!isset($this->env['slim.request.form_hash'])) {
$this->env['slim.request.form_hash'] = array();
if ($this->isFormData() && is_string($this->env['slim.input'])) {
$output = array();
if (function_exists('mb_parse_str') && !isset($this->env['slim.tests.ignore_multibyte'])) {
mb_parse_str($this->env['slim.input'], $output);
} else { } else {
parse_str($this->env['slim.input'], $output); parse_str($input, $output);
} }
$this->env['slim.request.form_hash'] = Util::stripSlashesIfMagicQuotes($output); return $output;
} else { } else {
$this->env['slim.request.form_hash'] = Util::stripSlashesIfMagicQuotes($_POST); return array();
}
}
if ($key) {
if (isset($this->env['slim.request.form_hash'][$key])) {
return $this->env['slim.request.form_hash'][$key];
} else {
return null;
}
} else {
return $this->env['slim.request.form_hash'];
} }
} }
/** /**
* Fetch PUT data (alias for \Slim\Http\Request::post) * Get HTTP request headers
* @param string $key * @return array Key-value array of HTTP request headers
* @return array|mixed|null
*/ */
public function put($key = null) protected function loadHttpHeaders() {
{
return $this->post($key);
}
/**
* Fetch DELETE data (alias for \Slim\Http\Request::post)
* @param string $key
* @return array|mixed|null
*/
public function delete($key = null)
{
return $this->post($key);
}
/**
* Fetch COOKIE data
*
* This method returns a key-value array of Cookie data sent in the HTTP request, or
* the value of a array key if requested; if the array key does not exist, NULL is returned.
*
* @param string $key
* @return array|string|null
*/
public function cookies($key = null)
{
if (!isset($this->env['slim.request.cookie_hash'])) {
$cookieHeader = isset($this->env['COOKIE']) ? $this->env['COOKIE'] : '';
$this->env['slim.request.cookie_hash'] = Util::parseCookieHeader($cookieHeader);
}
if ($key) {
if (isset($this->env['slim.request.cookie_hash'][$key])) {
return $this->env['slim.request.cookie_hash'][$key];
} else {
return null;
}
} else {
return $this->env['slim.request.cookie_hash'];
}
}
/**
* Does the Request body contain parseable form data?
* @return bool
*/
public function isFormData()
{
$method = isset($this->env['slim.method_override.original_method']) ? $this->env['slim.method_override.original_method'] : $this->getMethod();
return ($method === self::METHOD_POST && is_null($this->getContentType())) || in_array($this->getMediaType(), self::$formDataMediaTypes);
}
/**
* Get Headers
*
* This method returns a key-value array of headers sent in the HTTP request, or
* the value of a hash key if requested; if the array key does not exist, NULL is returned.
*
* @param string $key
* @param mixed $default The default value returned if the requested header is not available
* @return mixed
*/
public function headers($key = null, $default = null)
{
if ($key) {
$key = strtoupper($key);
$key = str_replace('-', '_', $key);
$key = preg_replace('@^HTTP_@', '', $key);
if (isset($this->env[$key])) {
return $this->env[$key];
} else {
return $default;
}
} else {
$headers = array(); $headers = array();
foreach ($this->env as $key => $value) { foreach ( $_SERVER as $key => $value ) {
if (strpos($key, 'slim.') !== 0) { $key = $this->convertHttpHeaderName($key);
$headers[$key] = $value; if ( strpos($key, 'http-') === 0 || in_array($key, $this->additionalHeaders) ) {
$name = str_replace('http-', '', $key);
$headers[$name] = $value;
} }
} }
return $headers; return $headers;
} }
}
/** /**
* Get Body * Convert HTTP header name
* @return string * @return string
*/ */
public function getBody() protected function convertHttpHeaderName( $name ) {
{ return str_replace('_', '-', strtolower($name));
return $this->env['slim.input'];
} }
/** /**
* Get Content Type * Check for HTTP request method override
* @return string *
* Because traditional web browsers do not support PUT and DELETE
* HTTP methods, we use a hidden form input field to
* mimic PUT and DELETE requests. We check for this override here.
*
* @return void
*/ */
public function getContentType() protected function checkForHttpMethodOverride() {
{ if ( isset($this->post[self::METHOD_OVERRIDE]) ) {
if (isset($this->env['CONTENT_TYPE'])) { $this->method = $this->post[self::METHOD_OVERRIDE];
return $this->env['CONTENT_TYPE']; unset($this->post[self::METHOD_OVERRIDE]);
} else { if ( $this->isPut() ) {
return null; $this->put = $this->post;
}
} }
} }
/**
* Get Media Type (type/subtype within Content Type header)
* @return string|null
*/
public function getMediaType()
{
$contentType = $this->getContentType();
if ($contentType) {
$contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType);
return strtolower($contentTypeParts[0]);
} else {
return null;
}
}
/**
* Get Media Type Params
* @return array
*/
public function getMediaTypeParams()
{
$contentType = $this->getContentType();
$contentTypeParams = array();
if ($contentType) {
$contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType);
$contentTypePartsLength = count($contentTypeParts);
for ($i = 1; $i < $contentTypePartsLength; $i++) {
$paramParts = explode('=', $contentTypeParts[$i]);
$contentTypeParams[strtolower($paramParts[0])] = $paramParts[1];
}
}
return $contentTypeParams;
}
/**
* Get Content Charset
* @return string|null
*/
public function getContentCharset()
{
$mediaTypeParams = $this->getMediaTypeParams();
if (isset($mediaTypeParams['charset'])) {
return $mediaTypeParams['charset'];
} else {
return null;
}
}
/**
* Get Content-Length
* @return int
*/
public function getContentLength()
{
if (isset($this->env['CONTENT_LENGTH'])) {
return (int) $this->env['CONTENT_LENGTH'];
} else {
return 0;
}
}
/**
* Get Host
* @return string
*/
public function getHost()
{
if (isset($this->env['HOST'])) {
if (strpos($this->env['HOST'], ':') !== false) {
$hostParts = explode(':', $this->env['HOST']);
return $hostParts[0];
}
return $this->env['HOST'];
} else {
return $this->env['SERVER_NAME'];
}
}
/**
* Get Host with Port
* @return string
*/
public function getHostWithPort()
{
return sprintf('%s:%s', $this->getHost(), $this->getPort());
}
/**
* Get Port
* @return int
*/
public function getPort()
{
return (int) $this->env['SERVER_PORT'];
}
/**
* Get Scheme (https or http)
* @return string
*/
public function getScheme()
{
return $this->env['slim.url_scheme'];
}
/**
* Get Script Name (physical path)
* @return string
*/
public function getScriptName()
{
return $this->env['SCRIPT_NAME'];
}
/**
* LEGACY: Get Root URI (alias for Slim_Http_Request::getScriptName)
* @return string
*/
public function getRootUri()
{
return $this->getScriptName();
}
/**
* Get Path (physical path + virtual path)
* @return string
*/
public function getPath()
{
return $this->getScriptName() . $this->getPathInfo();
}
/**
* Get Path Info (virtual path)
* @return string
*/
public function getPathInfo()
{
return $this->env['PATH_INFO'];
}
/**
* LEGACY: Get Resource URI (alias for Slim_Http_Request::getPathInfo)
* @return string
*/
public function getResourceUri()
{
return $this->getPathInfo();
}
/**
* Get URL (scheme + host [ + port if non-standard ])
* @return string
*/
public function getUrl()
{
$url = $this->getScheme() . '://' . $this->getHost();
if (($this->getScheme() === 'https' && $this->getPort() !== 443) || ($this->getScheme() === 'http' && $this->getPort() !== 80)) {
$url .= sprintf(':%s', $this->getPort());
}
return $url;
}
/**
* Get IP
* @return string
*/
public function getIp()
{
if (isset($this->env['X_FORWARDED_FOR'])) {
return $this->env['X_FORWARDED_FOR'];
} elseif (isset($this->env['CLIENT_IP'])) {
return $this->env['CLIENT_IP'];
}
return $this->env['REMOTE_ADDR'];
}
/**
* Get Referrer
* @return string|null
*/
public function getReferrer()
{
if (isset($this->env['REFERER'])) {
return $this->env['REFERER'];
} else {
return null;
}
}
/**
* Get Referer (for those who can't spell)
* @return string|null
*/
public function getReferer()
{
return $this->getReferrer();
}
/**
* Get User Agent
* @return string|null
*/
public function getUserAgent()
{
if (isset($this->env['USER_AGENT'])) {
return $this->env['USER_AGENT'];
} else {
return null;
}
}
} }

View File

@ -2,12 +2,11 @@
/** /**
* Slim - a micro PHP 5 framework * Slim - a micro PHP 5 framework
* *
* @author Josh Lockhart <info@slimframework.com> * @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart * @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com * @link http://www.slimframework.com
* @license http://www.slimframework.com/license * @license http://www.slimframework.com/license
* @version 2.0.0 * @version 1.5.0
* @package Slim
* *
* MIT LICENSE * MIT LICENSE
* *
@ -30,40 +29,54 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
namespace Slim\Http;
/** /**
* Response * Response
* *
* This is a simple abstraction over top an HTTP response. This * Object-oriented representation of an HTTP response that is
* provides methods to set the HTTP status, the HTTP headers, * returned to the client. This class is responsible for:
* and the HTTP body. *
* - HTTP response status
* - HTTP response body
* - HTTP response headers
* - HTTP response cookies
* *
* @package Slim * @package Slim
* @author Josh Lockhart * @author Josh Lockhart <info@joshlockhart.com>
* @since 1.0.0 * @author Kris Jordan <http://github.com/KrisJordan>
* @since Version 1.0
*/ */
class Response implements \ArrayAccess, \Countable, \IteratorAggregate class Slim_Http_Response {
{
/**
* @var Slim_Http_Request
*/
protected $request;
/**
* @var string
*/
protected $httpVersion = '1.1';
/** /**
* @var int HTTP status code * @var int HTTP status code
*/ */
protected $status; protected $status = 200;
/** /**
* @var \Slim\Http\Headers List of HTTP response headers * @var array Key-value array of HTTP response headers
*/ */
protected $header; protected $headers = array();
/** /**
* @var string HTTP response body * @var string HTTP response body
*/ */
protected $body; protected $body = '';
/** /**
* @var int Length of HTTP response body * @var int Length of HTTP response body
*/ */
protected $length; protected $length = 0;
/** /**
* @var array HTTP response codes and messages * @var array HTTP response codes and messages
@ -120,340 +133,189 @@ class Response implements \ArrayAccess, \Countable, \IteratorAggregate
); );
/** /**
* Constructor * @var CookieJar Manages Cookies to be sent with this Response
* @param string $body The HTTP response body
* @param int $status The HTTP response status
* @param \Slim\Http\Headers|array $header The HTTP response headers
*/ */
public function __construct($body = '', $status = 200, $header = array()) protected $cookieJar;
{
$this->status = (int) $status; /**
$headers = array(); * Constructor
foreach ($header as $key => $value) { */
$headers[$key] = $value; public function __construct( Slim_Http_Request $req ) {
} $this->request = $req;
$this->header = new Headers(array_merge(array('Content-Type' => 'text/html'), $headers)); $this->header('Content-Type', 'text/html');
$this->body = '';
$this->write($body);
} }
/** /**
* Get and set status * Set and/or get the HTTP response version
* @param int|null $status * @param string $version
* @return int * @return void
* @throws InvalidArgumentException If argument is not a valid HTTP version
*/ */
public function status($status = null) public function httpVersion( $version = null ) {
{ if ( $version ) {
if (!is_null($status)) { $version = (string)$version;
$this->status = (int) $status; if ( $version === '1.0' || $version === '1.1' ) {
$this->httpVersion = $version;
} else {
throw new InvalidArgumentException('Invalid HTTP version in Response object');
}
}
return $this->httpVersion;
} }
/**
* Set and/or get the HTTP response status code
* @param int $status
* @return int
* @throws InvalidArgumentException If argument is not a valid HTTP status code
*/
public function status( $status = null ) {
if ( !is_null($status) ) {
if ( !in_array(intval($status), array_keys(self::$messages)) ) {
throw new InvalidArgumentException('Cannot set Response status. Provided status code "' . $status . '" is not a valid HTTP response code.');
}
$this->status = intval($status);
}
return $this->status; return $this->status;
} }
/** /**
* Get and set header * Get HTTP response headers
* @param string $name Header name * @return array
* @param string|null $value Header value
* @return string Header value
*/ */
public function header($name, $value = null) public function headers() {
{ return $this->headers;
}
/**
* Get and/or set an HTTP response header
* @param string $key The header name
* @param string $value The header value
* @return string|null The header value, or NULL if header not set
*/
public function header( $key, $value = null ) {
if ( !is_null($value) ) { if ( !is_null($value) ) {
$this[$name] = $value; $this->headers[$key] = $value;
} }
return isset($this->headers[$key]) ? $this->headers[$key] : null;
return $this[$name];
} }
/** /**
* Get headers * Set the HTTP response body
* @return \Slim\Http\Headers * @param string $body The new HTTP response body
* @return string The new HTTP response body
*/ */
public function headers() public function body( $body = null ) {
{
return $this->header;
}
/**
* Get and set body
* @param string|null $body Content of HTTP response body
* @return string
*/
public function body($body = null)
{
if ( !is_null($body) ) { if ( !is_null($body) ) {
$this->write($body, true); $this->body = '';
$this->length = 0;
$this->write($body);
} }
return $this->body; return $this->body;
} }
/** /**
* Get and set length * Append the HTTP response body
* @param int|null $length
* @return int
*/
public function length($length = null)
{
if (!is_null($length)) {
$this->length = (int) $length;
}
return $this->length;
}
/**
* Append HTTP response body
* @param string $body Content to append to the current HTTP response body * @param string $body Content to append to the current HTTP response body
* @param bool $replace Overwrite existing response body?
* @return string The updated HTTP response body * @return string The updated HTTP response body
*/ */
public function write($body, $replace = false) public function write( $body ) {
{ $body = (string)$body;
if ($replace) { $this->length += strlen($body);
$this->body = $body; $this->body .= $body;
} else { $this->header('Content-Length', $this->length);
$this->body .= (string) $body; return $body;
}
$this->length = strlen($this->body);
return $this->body;
} }
/** /**
* Finalize * Set cookie jar
* * @param Slim_Http_CookieJar $cookieJar
* This prepares this response and returns an array * @return void
* of [status, headers, body]. This array is passed to outer middleware
* if available or directly to the Slim run method.
*
* @return array[int status, array headers, string body]
*/ */
public function finalize() public function setCookieJar( Slim_Http_CookieJar $cookieJar ) {
{ $this->cookieJar = $cookieJar;
}
/**
* Get cookie jar
* @return Slim_Http_CookieJar
*/
public function getCookieJar() {
return $this->cookieJar;
}
/**
* Finalize response headers before response is sent
* @return void
*/
public function finalize() {
if ( in_array($this->status, array(204, 304)) ) { if ( in_array($this->status, array(204, 304)) ) {
unset($this['Content-Type'], $this['Content-Length']); $this->body('');
unset($this->headers['Content-Type']);
return array($this->status, $this->header, '');
} else {
return array($this->status, $this->header, $this->body);
} }
} }
/**
* Set cookie
*
* Instead of using PHP's `setcookie()` function, Slim manually constructs the HTTP `Set-Cookie`
* header on its own and delegates this responsibility to the `Slim_Http_Util` class. This
* response's header is passed by reference to the utility class and is directly modified. By not
* relying on PHP's native implementation, Slim allows middleware the opportunity to massage or
* analyze the raw header before the response is ultimately delivered to the HTTP client.
*
* @param string $name The name of the cookie
* @param string|array $value If string, the value of cookie; if array, properties for
* cookie including: value, expire, path, domain, secure, httponly
*/
public function setCookie($name, $value)
{
Util::setCookieHeader($this->header, $name, $value);
}
/**
* Delete cookie
*
* Instead of using PHP's `setcookie()` function, Slim manually constructs the HTTP `Set-Cookie`
* header on its own and delegates this responsibility to the `Slim_Http_Util` class. This
* response's header is passed by reference to the utility class and is directly modified. By not
* relying on PHP's native implementation, Slim allows middleware the opportunity to massage or
* analyze the raw header before the response is ultimately delivered to the HTTP client.
*
* This method will set a cookie with the given name that has an expiration time in the past; this will
* prompt the HTTP client to invalidate and remove the client-side cookie. Optionally, you may
* also pass a key/value array as the second argument. If the "domain" key is present in this
* array, only the Cookie with the given name AND domain will be removed. The invalidating cookie
* sent with this response will adopt all properties of the second argument.
*
* @param string $name The name of the cookie
* @param array $value Properties for cookie including: value, expire, path, domain, secure, httponly
*/
public function deleteCookie($name, $value = array())
{
Util::deleteCookieHeader($this->header, $name, $value);
}
/**
* Redirect
*
* This method prepares this response to return an HTTP Redirect response
* to the HTTP client.
*
* @param string $url The redirect destination
* @param int $status The redirect HTTP status code
*/
public function redirect ($url, $status = 302)
{
$this->status = $status;
$this['Location'] = $url;
}
/**
* Helpers: Empty?
* @return bool
*/
public function isEmpty()
{
return in_array($this->status, array(201, 204, 304));
}
/**
* Helpers: Informational?
* @return bool
*/
public function isInformational()
{
return $this->status >= 100 && $this->status < 200;
}
/**
* Helpers: OK?
* @return bool
*/
public function isOk()
{
return $this->status === 200;
}
/**
* Helpers: Successful?
* @return bool
*/
public function isSuccessful()
{
return $this->status >= 200 && $this->status < 300;
}
/**
* Helpers: Redirect?
* @return bool
*/
public function isRedirect()
{
return in_array($this->status, array(301, 302, 303, 307));
}
/**
* Helpers: Redirection?
* @return bool
*/
public function isRedirection()
{
return $this->status >= 300 && $this->status < 400;
}
/**
* Helpers: Forbidden?
* @return bool
*/
public function isForbidden()
{
return $this->status === 403;
}
/**
* Helpers: Not Found?
* @return bool
*/
public function isNotFound()
{
return $this->status === 404;
}
/**
* Helpers: Client error?
* @return bool
*/
public function isClientError()
{
return $this->status >= 400 && $this->status < 500;
}
/**
* Helpers: Server Error?
* @return bool
*/
public function isServerError()
{
return $this->status >= 500 && $this->status < 600;
}
/**
* Array Access: Offset Exists
*/
public function offsetExists( $offset )
{
return isset($this->header[$offset]);
}
/**
* Array Access: Offset Get
*/
public function offsetGet( $offset )
{
if (isset($this->header[$offset])) {
return $this->header[$offset];
} else {
return null;
}
}
/**
* Array Access: Offset Set
*/
public function offsetSet($offset, $value)
{
$this->header[$offset] = $value;
}
/**
* Array Access: Offset Unset
*/
public function offsetUnset($offset)
{
unset($this->header[$offset]);
}
/**
* Countable: Count
*/
public function count()
{
return count($this->header);
}
/**
* Get Iterator
*
* This returns the contained `\Slim\Http\Headers` instance which
* is itself iterable.
*
* @return \Slim\Http\Headers
*/
public function getIterator()
{
return $this->header;
}
/** /**
* Get message for HTTP status code * Get message for HTTP status code
* @return string|null * @return string|null
*/ */
public static function getMessageForCode($status) public static function getMessageForCode( $status ) {
{ return isset(self::$messages[$status]) ? self::$messages[$status] : null;
if (isset(self::$messages[$status])) { }
return self::$messages[$status];
/**
* Can this HTTP response have a body?
* @return bool
*/
public function canHaveBody() {
return ( $this->status < 100 || $this->status >= 200 ) && $this->status != 204 && $this->status != 304;
}
/**
* Send headers for HTTP response
* @return void
*/
protected function sendHeaders() {
//Finalize response
$this->finalize();
if ( substr(PHP_SAPI, 0, 3) === 'cgi') {
//Send Status header if running with fastcgi
header('Status: ' . self::getMessageForCode($this->status()));
} else { } else {
return null; //Else send HTTP message
header(sprintf('HTTP/%s %s', $this->httpVersion, self::getMessageForCode($this->status())));
}
//Send headers
foreach ( $this->headers() as $name => $value ) {
header("$name: $value");
}
//Send cookies
foreach ( $this->getCookieJar()->getResponseCookies() as $name => $cookie ) {
setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpires(), $cookie->getPath(), $cookie->getDomain(), $cookie->getSecure(), $cookie->getHttpOnly());
}
//Flush all output to client
flush();
}
/**
* Send HTTP response
*
* This method will set Response headers, set Response cookies,
* and `echo` the Response body to the current output buffer.
*
* @return void
*/
public function send() {
if ( !headers_sent() ) {
$this->sendHeaders();
}
if ( $this->canHaveBody() && $this->request->isHead() === false ) {
echo $this->body;
} }
} }
} }

131
Slim/Http/Uri.php Normal file
View File

@ -0,0 +1,131 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.5.0
*
* MIT LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Uri
*
* Parses base uri and application uri from Request.
*
* @package Slim
* @author Josh Lockhart <info@joshlockhart.com>
* @since Version 1.0
*/
class Slim_Http_Uri {
/**
* @var string "https" or "http"
*/
protected static $scheme;
/**
* @var string
*/
protected static $baseUri;
/**
* @var string
*/
protected static $uri;
/**
* @var string The URI query string, excluding leading "?"
*/
protected static $queryString;
/**
* Get Base URI without trailing slash
* @param bool $reload Force reparse the base URI?
* @return string
*/
public static function getBaseUri( $reload = false ) {
if ( $reload || is_null(self::$baseUri) ) {
$requestUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : $_SERVER['PHP_SELF']; //Full Request URI
$scriptName = $_SERVER['SCRIPT_NAME']; //Script path from docroot
$baseUri = strpos($requestUri, $scriptName) === 0 ? $scriptName : str_replace('\\', '/', dirname($scriptName));
self::$baseUri = rtrim($baseUri, '/');
}
return self::$baseUri;
}
/**
* Get URI with leading slash
* @param bool $reload Force reparse the URI?
* @return string
* @throws RuntimeException If unable if unable to determine URI
*/
public static function getUri( $reload = false ) {
if ( $reload || is_null(self::$uri) ) {
$uri = '';
if ( !empty($_SERVER['PATH_INFO']) ) {
$uri = $_SERVER['PATH_INFO'];
} else {
if ( isset($_SERVER['REQUEST_URI']) ) {
$uri = parse_url(self::getScheme() . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], PHP_URL_PATH);
} else if ( isset($_SERVER['PHP_SELF']) ) {
$uri = $_SERVER['PHP_SELF'];
} else {
throw new RuntimeException('Unable to detect request URI');
}
}
if ( self::getBaseUri() !== '' && strpos($uri, self::getBaseUri()) === 0 ) {
$uri = substr($uri, strlen(self::getBaseUri()));
}
self::$uri = '/' . ltrim($uri, '/');
}
return self::$uri;
}
/**
* Get URI Scheme
* @param bool $reload For reparse the URL scheme?
* @return string "https" or "http"
*/
public static function getScheme( $reload = false ) {
if ( $reload || is_null(self::$scheme) ) {
self::$scheme = ( empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off' ) ? 'http' : 'https';
}
return self::$scheme;
}
/**
* Get URI Query String
* @param bool $reload For reparse the URL query string?
* @return string
*/
public static function getQueryString( $reload = false ) {
if ( $reload || is_null(self::$queryString) ) {
self::$queryString = $_SERVER['QUERY_STRING'];
}
return self::$queryString;
}
}

View File

@ -2,12 +2,11 @@
/** /**
* Slim - a micro PHP 5 framework * Slim - a micro PHP 5 framework
* *
* @author Josh Lockhart <info@slimframework.com> * @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart * @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com * @link http://www.slimframework.com
* @license http://www.slimframework.com/license * @license http://www.slimframework.com/license
* @version 2.0.0 * @version 1.5.0
* @package Slim
* *
* MIT LICENSE * MIT LICENSE
* *
@ -30,14 +29,12 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
namespace Slim;
/** /**
* Log * Log Adapter
* *
* This is the primary logger for a Slim application. You may provide * This is an adapter for your own custom Logger. This adapter assumes
* a Log Writer in conjunction with this Log to write to various output * your custom Logger provides the following public instance methods:
* destinations (e.g. a file). This class provides this interface:
* *
* debug( mixed $object ) * debug( mixed $object )
* info( mixed $object ) * info( mixed $object )
@ -45,75 +42,39 @@ namespace Slim;
* error( mixed $object ) * error( mixed $object )
* fatal( mixed $object ) * fatal( mixed $object )
* *
* This class assumes only that your Log Writer has a public `write()` method * This class assumes nothing else about your custom Logger, so you are free
* that accepts any object as its one and only argument. The Log Writer * to use Apache's Log4PHP logger or any other log class that, at the
* class may write or send its argument anywhere: a file, STDERR, * very least, implements the five public instance methods shown above.
* a remote web API, etc. The possibilities are endless.
* *
* @package Slim * @package Slim
* @author Josh Lockhart * @author Josh Lockhart <info@joshlockhart.com>
* @since 1.0.0 * @since Version 1.0
*/ */
class Log class Slim_Log {
{
const FATAL = 0;
const ERROR = 1;
const WARN = 2;
const INFO = 3;
const DEBUG = 4;
/** /**
* @var array * @var mixed An object that implements expected Logger interface
*/ */
protected static $levels = array( protected $logger;
self::FATAL => 'FATAL',
self::ERROR => 'ERROR',
self::WARN => 'WARN',
self::INFO => 'INFO',
self::DEBUG => 'DEBUG'
);
/** /**
* @var mixed * @var bool Enable logging?
*/
protected $writer;
/**
* @var bool
*/ */
protected $enabled; protected $enabled;
/**
* @var int
*/
protected $level;
/** /**
* Constructor * Constructor
* @param mixed $writer
*/ */
public function __construct($writer) public function __construct() {
{
$this->writer = $writer;
$this->enabled = true; $this->enabled = true;
$this->level = self::DEBUG;
}
/**
* Is logging enabled?
* @return bool
*/
public function getEnabled()
{
return $this->enabled;
} }
/** /**
* Enable or disable logging * Enable or disable logging
* @param bool $enabled * @param bool $enabled
* @return void
*/ */
public function setEnabled($enabled) public function setEnabled( $enabled ) {
{
if ( $enabled ) { if ( $enabled ) {
$this->enabled = true; $this->enabled = true;
} else { } else {
@ -121,52 +82,11 @@ class Log
} }
} }
/**
* Set level
* @param int $level
* @throws \InvalidArgumentException If invalid log level specified
*/
public function setLevel($level)
{
if (!isset(self::$levels[$level])) {
throw new \InvalidArgumentException('Invalid log level');
}
$this->level = $level;
}
/**
* Get level
* @return int
*/
public function getLevel()
{
return $this->level;
}
/**
* Set writer
* @param mixed $writer
*/
public function setWriter($writer)
{
$this->writer = $writer;
}
/**
* Get writer
* @return mixed
*/
public function getWriter()
{
return $this->writer;
}
/** /**
* Is logging enabled? * Is logging enabled?
* @return bool * @return bool
*/ */
public function isEnabled() public function isEnabled() {
{
return $this->enabled; return $this->enabled;
} }
@ -175,9 +95,8 @@ class Log
* @param mixed $object * @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled * @return mixed|false What the Logger returns, or false if Logger not set or not enabled
*/ */
public function debug($object) public function debug( $object ) {
{ return isset($this->logger) && $this->isEnabled() ? $this->logger->debug($object) : false;
return $this->log($object, self::DEBUG);
} }
/** /**
@ -185,9 +104,8 @@ class Log
* @param mixed $object * @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled * @return mixed|false What the Logger returns, or false if Logger not set or not enabled
*/ */
public function info($object) public function info( $object ) {
{ return isset($this->logger) && $this->isEnabled() ? $this->logger->info($object) : false;
return $this->log($object, self::INFO);
} }
/** /**
@ -195,9 +113,8 @@ class Log
* @param mixed $object * @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled * @return mixed|false What the Logger returns, or false if Logger not set or not enabled
*/ */
public function warn($object) public function warn( $object ) {
{ return isset($this->logger) && $this->isEnabled() ? $this->logger->warn($object) : false;
return $this->log($object, self::WARN);
} }
/** /**
@ -205,9 +122,8 @@ class Log
* @param mixed $object * @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled * @return mixed|false What the Logger returns, or false if Logger not set or not enabled
*/ */
public function error($object) public function error( $object ) {
{ return isset($this->logger) && $this->isEnabled() ? $this->logger->error($object) : false;
return $this->log($object, self::ERROR);
} }
/** /**
@ -215,23 +131,25 @@ class Log
* @param mixed $object * @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled * @return mixed|false What the Logger returns, or false if Logger not set or not enabled
*/ */
public function fatal($object) public function fatal( $object ) {
{ return isset($this->logger) && $this->isEnabled() ? $this->logger->fatal($object) : false;
return $this->log($object, self::FATAL);
} }
/** /**
* Log message * Set Logger
* @param mixed The object to log * @param mixed $logger
* @param int The message level * @return void
* @return int|false
*/ */
protected function log($object, $level) public function setLogger( $logger ) {
{ $this->logger = $logger;
if ($this->enabled && $this->writer && $level <= $this->level) {
return $this->writer->write($object, $level);
} else {
return false;
} }
/**
* Get Logger
* @return mixed
*/
public function getLogger() {
return $this->logger;
} }
} }

200
Slim/Logger.php Normal file
View File

@ -0,0 +1,200 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.5.0
*
* MIT LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Logger
*
* A simple Logger that writes to a daily-unique log file in
* a user-specified directory. By default, this class will write log
* messages for all log levels; the log level may be changed to filter
* unwanted log messages from the log file.
*
* @package Slim
* @author Josh Lockhart <info@joshlockhart.com>
* @since Version 1.0
*/
class Slim_Logger {
/**
* @var array Log levels
*/
protected $levels = array(
0 => 'FATAL',
1 => 'ERROR',
2 => 'WARN',
3 => 'INFO',
4 => 'DEBUG'
);
/**
* @var string Absolute path to log directory with trailing slash
*/
protected $directory;
/**
* Constructor
* @param string $directory Absolute or relative path to log directory
* @param int $level The maximum log level reported by this Logger
*/
public function __construct( $directory, $level = 4 ) {
$this->setDirectory($directory);
$this->setLevel($level);
}
/**
* Set log directory
* @param string $directory Absolute or relative path to log directory
* @return void
*/
public function setDirectory( $directory ) {
$realPath = realpath($directory);
if ( $realPath ) {
$this->directory = rtrim($realPath, '/') . '/';
} else {
$this->directory = false;
}
}
/**
* Get log directory
* @return string|false Absolute path to log directory with trailing slash
*/
public function getDirectory() {
return $this->directory;
}
/**
* Set log level
* @param int The maximum log level reported by this Logger
* @return void
* @throws InvalidArgumentException If level specified is not 0, 1, 2, 3, 4
*/
public function setLevel( $level ) {
$theLevel = (int)$level;
if ( $theLevel >= 0 && $theLevel <= 4 ) {
$this->level = $theLevel;
} else {
throw new InvalidArgumentException('Invalid Log Level. Must be one of: 0, 1, 2, 3, 4.');
}
}
/**
* Get log level
* @return int
*/
public function getLevel() {
return $this->level;
}
/**
* Log debug data
* @param mixed $data
* @return void
*/
public function debug( $data ) {
$this->log($data, 4);
}
/**
* Log info data
* @param mixed $data
* @return void
*/
public function info( $data ) {
$this->log($data, 3);
}
/**
* Log warn data
* @param mixed $data
* @return void
*/
public function warn( $data ) {
$this->log($data, 2);
}
/**
* Log error data
* @param mixed $data
* @return void
*/
public function error( $data ) {
$this->log($data, 1);
}
/**
* Log fatal data
* @param mixed $data
* @return void
*/
public function fatal( $data ) {
$this->log($data, 0);
}
/**
* Get absolute path to current daily log file
* @return string
*/
public function getFile() {
return $this->getDirectory() . strftime('%Y-%m-%d') . '.log';
}
/**
* Log data to file
* @param mixed $data
* @param int $level
* @return void
* @throws RuntimeException If log directory not found or not writable
*/
protected function log( $data, $level ) {
$dir = $this->getDirectory();
if ( $dir == false || !is_dir($dir) ) {
throw new RuntimeException("Log directory '$dir' invalid.");
}
if ( !is_writable($dir) ) {
throw new RuntimeException("Log directory '$dir' not writable.");
}
if ( $level <= $this->getLevel() ) {
$this->write(sprintf("[%s] %s - %s\r\n", $this->levels[$level], date('c'), (string)$data));
}
}
/**
* Persist data to log
* @param string Log message
* @return void
*/
protected function write( $data ) {
@file_put_contents($this->getFile(), $data, FILE_APPEND | LOCK_EX);
}
}

View File

@ -2,12 +2,11 @@
/** /**
* Slim - a micro PHP 5 framework * Slim - a micro PHP 5 framework
* *
* @author Josh Lockhart <info@slimframework.com> * @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart * @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com * @link http://www.slimframework.com
* @license http://www.slimframework.com/license * @license http://www.slimframework.com/license
* @version 2.0.0 * @version 1.5.0
* @package Slim
* *
* MIT LICENSE * MIT LICENSE
* *
@ -30,23 +29,23 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
namespace Slim;
/** /**
* Route * Route
*
* @package Slim * @package Slim
* @author Josh Lockhart, Thomas Bley * @author Josh Lockhart <info@joshlockhart.com>
* @since 1.0.0 * @since Version 1.0
*/ */
class Route class Slim_Route {
{
/** /**
* @var string The route pattern (e.g. "/books/:id") * @var string The route pattern (ie. "/books/:id")
*/ */
protected $pattern; protected $pattern;
/** /**
* @var mixed The route callable * @var mixed The callable associated with this route
*/ */
protected $callable; protected $callable;
@ -56,7 +55,7 @@ class Route
protected $conditions = array(); protected $conditions = array();
/** /**
* @var array Default conditions applied to all route instances * @var array Default conditions applied to all Route instances
*/ */
protected static $defaultConditions = array(); protected static $defaultConditions = array();
@ -70,33 +69,27 @@ class Route
*/ */
protected $params = array(); protected $params = array();
/**
* @var array value array of URL parameter names
*/
protected $paramNames = array();
/**
* @var array key array of URL parameter names with + at the end
*/
protected $paramNamesPath = array();
/** /**
* @var array HTTP methods supported by this Route * @var array HTTP methods supported by this Route
*/ */
protected $methods = array(); protected $methods = array();
/** /**
* @var array[Callable] Middleware to be run before only this route instance * @var Slim_Router The Router to which this Route belongs
*/
protected $router;
/**
* @var array[Callable] Middleware
*/ */
protected $middleware = array(); protected $middleware = array();
/** /**
* Constructor * Constructor
* @param string $pattern The URL pattern (e.g. "/books/:id") * @param string $pattern The URL pattern (ie. "/books/:id")
* @param mixed $callable Anything that returns TRUE for is_callable() * @param mixed $callable Anything that returns TRUE for is_callable()
*/ */
public function __construct($pattern, $callable) public function __construct( $pattern, $callable ) {
{
$this->setPattern($pattern); $this->setPattern($pattern);
$this->setCallable($callable); $this->setCallable($callable);
$this->setConditions(self::getDefaultConditions()); $this->setConditions(self::getDefaultConditions());
@ -105,9 +98,9 @@ class Route
/** /**
* Set default route conditions for all instances * Set default route conditions for all instances
* @param array $defaultConditions * @param array $defaultConditions
* @return void
*/ */
public static function setDefaultConditions(array $defaultConditions) public static function setDefaultConditions( array $defaultConditions ) {
{
self::$defaultConditions = $defaultConditions; self::$defaultConditions = $defaultConditions;
} }
@ -115,8 +108,7 @@ class Route
* Get default route conditions for all instances * Get default route conditions for all instances
* @return array * @return array
*/ */
public static function getDefaultConditions() public static function getDefaultConditions() {
{
return self::$defaultConditions; return self::$defaultConditions;
} }
@ -124,35 +116,33 @@ class Route
* Get route pattern * Get route pattern
* @return string * @return string
*/ */
public function getPattern() public function getPattern() {
{
return $this->pattern; return $this->pattern;
} }
/** /**
* Set route pattern * Set route pattern
* @param string $pattern * @param string $pattern
* @return void
*/ */
public function setPattern($pattern) public function setPattern( $pattern ) {
{ $this->pattern = str_replace(')', ')?', (string)$pattern);
$this->pattern = $pattern;
} }
/** /**
* Get route callable * Get route callable
* @return mixed * @return mixed
*/ */
public function getCallable() public function getCallable() {
{
return $this->callable; return $this->callable;
} }
/** /**
* Set route callable * Set route callable
* @param mixed $callable * @param mixed $callable
* @return void
*/ */
public function setCallable($callable) public function setCallable($callable) {
{
$this->callable = $callable; $this->callable = $callable;
} }
@ -160,17 +150,16 @@ class Route
* Get route conditions * Get route conditions
* @return array * @return array
*/ */
public function getConditions() public function getConditions() {
{
return $this->conditions; return $this->conditions;
} }
/** /**
* Set route conditions * Set route conditions
* @param array $conditions * @param array $conditions
* @return void
*/ */
public function setConditions(array $conditions) public function setConditions( array $conditions ) {
{
$this->conditions = $conditions; $this->conditions = $conditions;
} }
@ -178,72 +167,33 @@ class Route
* Get route name * Get route name
* @return string|null * @return string|null
*/ */
public function getName() public function getName() {
{
return $this->name; return $this->name;
} }
/** /**
* Set route name * Set route name
* @param string $name * @param string $name
* @return void
*/ */
public function setName($name) public function setName( $name ) {
{
$this->name = (string)$name; $this->name = (string)$name;
$this->router->cacheNamedRoute($this->name, $this);
} }
/** /**
* Get route parameters * Get route parameters
* @return array * @return array
*/ */
public function getParams() public function getParams() {
{
return $this->params; return $this->params;
} }
/**
* Set route parameters
* @param array $params
*/
public function setParams($params)
{
$this->params = $params;
}
/**
* Get route parameter value
* @param string $index Name of URL parameter
* @return string
* @throws \InvalidArgumentException If route parameter does not exist at index
*/
public function getParam($index)
{
if (!isset($this->params[$index])) {
throw new \InvalidArgumentException('Route parameter does not exist at specified index');
}
return $this->params[$index];
}
/**
* Set route parameter value
* @param string $index Name of URL parameter
* @param mixed $value The new parameter value
* @throws \InvalidArgumentException If route parameter does not exist at index
*/
public function setParam($index, $value)
{
if (!isset($this->params[$index])) {
throw new \InvalidArgumentException('Route parameter does not exist at specified index');
}
$this->params[$index] = $value;
}
/** /**
* Add supported HTTP method(s) * Add supported HTTP method(s)
* @return void
*/ */
public function setHttpMethods() public function setHttpMethods() {
{
$args = func_get_args(); $args = func_get_args();
$this->methods = $args; $this->methods = $args;
} }
@ -252,29 +202,26 @@ class Route
* Get supported HTTP methods * Get supported HTTP methods
* @return array * @return array
*/ */
public function getHttpMethods() public function getHttpMethods() {
{
return $this->methods; return $this->methods;
} }
/** /**
* Append supported HTTP methods * Append supported HTTP methods
* @return void
*/ */
public function appendHttpMethods() public function appendHttpMethods() {
{
$args = func_get_args(); $args = func_get_args();
$this->methods = array_merge($this->methods, $args); $this->methods = array_merge($this->methods, $args);
} }
/** /**
* Append supported HTTP methods (alias for Route::appendHttpMethods) * Append supported HTTP methods (alias for Route::appendHttpMethods)
* @return \Slim\Route * @return Slim_Route
*/ */
public function via() public function via() {
{
$args = func_get_args(); $args = func_get_args();
$this->methods = array_merge($this->methods, $args); $this->methods = array_merge($this->methods, $args);
return $this; return $this;
} }
@ -282,17 +229,32 @@ class Route
* Detect support for an HTTP method * Detect support for an HTTP method
* @return bool * @return bool
*/ */
public function supportsHttpMethod($method) public function supportsHttpMethod( $method ) {
{
return in_array($method, $this->methods); return in_array($method, $this->methods);
} }
/**
* Get router
* @return Slim_Router
*/
public function getRouter() {
return $this->router;
}
/**
* Set router
* @param Slim_Router $router
* @return void
*/
public function setRouter( Slim_Router $router ) {
$this->router = $router;
}
/** /**
* Get middleware * Get middleware
* @return array[Callable] * @return array[Callable]
*/ */
public function getMiddleware() public function getMiddleware() {
{
return $this->middleware; return $this->middleware;
} }
@ -305,22 +267,20 @@ class Route
* assume the argument is an array of callables and merge the array * assume the argument is an array of callables and merge the array
* with `$this->middleware`. Even if non-callables are included in the * with `$this->middleware`. Even if non-callables are included in the
* argument array, we still merge them; we lazily check each item * argument array, we still merge them; we lazily check each item
* against `is_callable` during Router::dispatch(). * against `is_callable` during Route::dispatch().
* *
* @param Callable|array[Callable] * @param Callable|array[Callable]
* @return \Slim\Route * @return Slim_Route
* @throws \InvalidArgumentException If argument is not callable or not an array * @throws InvalidArgumentException If argument is not callable or not an array
*/ */
public function setMiddleware($middleware) public function setMiddleware( $middleware ) {
{
if ( is_callable($middleware) ) { if ( is_callable($middleware) ) {
$this->middleware[] = $middleware; $this->middleware[] = $middleware;
} else if ( is_array($middleware) ) { } else if ( is_array($middleware) ) {
$this->middleware = array_merge($this->middleware, $middleware); $this->middleware = array_merge($this->middleware, $middleware);
} else { } else {
throw new \InvalidArgumentException('Route middleware must be callable or an array of callables'); throw new InvalidArgumentException('Route middleware must be callable or an array of callables');
} }
return $this; return $this;
} }
@ -335,73 +295,104 @@ class Route
* @param string $resourceUri A Request URI * @param string $resourceUri A Request URI
* @return bool * @return bool
*/ */
public function matches($resourceUri) public function matches( $resourceUri ) {
{ //Extract URL params
//Convert URL params into regex patterns, construct a regex for this route, init params preg_match_all('@:([\w]+)@', $this->pattern, $paramNames, PREG_PATTERN_ORDER);
$patternAsRegex = preg_replace_callback('#:([\w]+)\+?#', array($this, 'matchesCallback'), $paramNames = $paramNames[0];
str_replace(')', ')?', (string) $this->pattern));
//Convert URL params into regex patterns, construct a regex for this route
$patternAsRegex = preg_replace_callback('@:[\w]+@', array($this, 'convertPatternToRegex'), $this->pattern);
if ( substr($this->pattern, -1) === '/' ) { if ( substr($this->pattern, -1) === '/' ) {
$patternAsRegex .= '?'; $patternAsRegex = $patternAsRegex . '?';
} }
$patternAsRegex = '@^' . $patternAsRegex . '$@';
//Cache URL params' names and values if this route matches the current HTTP request //Cache URL params' names and values if this route matches the current HTTP request
if (!preg_match('#^' . $patternAsRegex . '$#', $resourceUri, $paramValues)) { if ( preg_match($patternAsRegex, $resourceUri, $paramValues) ) {
array_shift($paramValues);
foreach ( $paramNames as $index => $value ) {
$val = substr($value, 1);
if ( isset($paramValues[$val]) ) {
$this->params[$val] = urldecode($paramValues[$val]);
}
}
return true;
} else {
return false; return false;
} }
foreach ($this->paramNames as $name) {
if (isset($paramValues[$name])) {
if (isset($this->paramNamesPath[ $name ])) {
$this->params[$name] = explode('/', urldecode($paramValues[$name]));
} else {
$this->params[$name] = urldecode($paramValues[$name]);
}
}
}
return true;
} }
/** /**
* Convert a URL parameter (e.g. ":id", ":id+") into a regular expression * Convert a URL parameter (ie. ":id") into a regular expression
* @param array URL parameters * @param array URL parameters
* @return string Regular expression for URL parameter * @return string Regular expression for URL parameter
*/ */
protected function matchesCallback($m) protected function convertPatternToRegex( $matches ) {
{ $key = str_replace(':', '', $matches[0]);
$this->paramNames[] = $m[1]; if ( array_key_exists($key, $this->conditions) ) {
if (isset($this->conditions[ $m[1] ])) { return '(?P<' . $key . '>' . $this->conditions[$key] . ')';
return '(?P<' . $m[1] . '>' . $this->conditions[ $m[1] ] . ')'; } else {
return '(?P<' . $key . '>[a-zA-Z0-9_\-\.\!\~\*\\\'\(\)\:\@\&\=\$\+,%]+)';
} }
if (substr($m[0], -1) === '+') {
$this->paramNamesPath[ $m[1] ] = 1;
return '(?P<' . $m[1] . '>.+)';
}
return '(?P<' . $m[1] . '>[^/]+)';
} }
/** /**
* Set route name * Set route name
* @param string $name The name of the route * @param string $name The name of the route
* @return \Slim\Route * @return Slim_Route
*/ */
public function name($name) public function name( $name ) {
{
$this->setName($name); $this->setName($name);
return $this; return $this;
} }
/** /**
* Merge route conditions * Merge route conditions
* @param array $conditions Key-value array of URL parameter conditions * @param array $conditions Key-value array of URL parameter conditions
* @return \Slim\Route * @return Slim_Route
*/ */
public function conditions(array $conditions) public function conditions( array $conditions ) {
{
$this->conditions = array_merge($this->conditions, $conditions); $this->conditions = array_merge($this->conditions, $conditions);
return $this; return $this;
} }
/**
* Dispatch route
*
* This method invokes this route's callable. If middleware is
* registered for this route, each callable middleware is invoked in
* the order specified.
*
* This method is smart about trailing slashes on the route pattern.
* If this route's pattern is defined with a trailing slash, and if the
* current request URI does not have a trailing slash but otherwise
* matches this route's pattern, a Slim_Exception_RequestSlash
* will be thrown triggering an HTTP 301 Permanent Redirect to the same
* URI _with_ a trailing slash. This Exception is caught in the
* `Slim::run` loop. If this route's pattern is defined without a
* trailing slash, and if the current request URI does have a trailing
* slash, this route will not be matched and a 404 Not Found
* response will be sent if no subsequent matching routes are found.
*
* @return bool Was route callable invoked successfully?
* @throws Slim_Exception_RequestSlash
*/
public function dispatch() {
if ( substr($this->pattern, -1) === '/' && substr($this->router->getRequest()->getResourceUri(), -1) !== '/' ) {
throw new Slim_Exception_RequestSlash();
}
//Invoke middleware
foreach ( $this->middleware as $mw ) {
if ( is_callable($mw) ) {
call_user_func($mw);
}
}
//Invoke callable
if ( is_callable($this->getCallable()) ) {
call_user_func_array($this->callable, array_values($this->params));
return true;
}
return false;
}
} }

View File

@ -2,12 +2,11 @@
/** /**
* Slim - a micro PHP 5 framework * Slim - a micro PHP 5 framework
* *
* @author Josh Lockhart <info@slimframework.com> * @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart * @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com * @link http://www.slimframework.com
* @license http://www.slimframework.com/license * @license http://www.slimframework.com/license
* @version 2.0.0 * @version 1.5.0
* @package Slim
* *
* MIT LICENSE * MIT LICENSE
* *
@ -30,41 +29,43 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
namespace Slim;
/** /**
* Router * Router
* *
* This class organizes, iterates, and dispatches \Slim\Route objects. * Responsible for registering route paths with associated callables.
* When a Slim application is run, the Router finds a matching Route for
* the current HTTP request, and if a matching route is found, executes
* the Route's associated callable passing it parameters from the Request URI.
* *
* @package Slim * @package Slim
* @author Josh Lockhart * @author Josh Lockhart <info@joshlockhart.com>
* @since 1.0.0 * @since Version 1.0
*/ */
class Router implements \Iterator class Slim_Router implements IteratorAggregate {
{
/**
* @var string Request URI
*/
protected $resourceUri;
/** /**
* @var array Lookup hash of all route objects * @var Slim_Http_Request
*/
protected $request;
/**
* @var array Lookup hash of routes, keyed by Request method
*/ */
protected $routes; protected $routes;
/** /**
* @var array Lookup hash of named route objects, keyed by route name (lazy-loaded) * @var array Lookup hash of named routes, keyed by route name
*/ */
protected $namedRoutes; protected $namedRoutes;
/** /**
* @var array Array of route objects that match the request URI (lazy-loaded) * @var array Array of routes that match the Request method and URL
*/ */
protected $matchedRoutes; protected $matchedRoutes;
/** /**
* @var mixed Callable to be invoked if no matching route objects are found * @var mixed Callable to be invoked if no matching routes are found
*/ */
protected $notFound; protected $notFound;
@ -75,193 +76,104 @@ class Router implements \Iterator
/** /**
* Constructor * Constructor
* @param Slim_Http_Request $request The HTTP request object
*/ */
public function __construct() public function __construct( Slim_Http_Request $request ) {
{ $this->request = $request;
$this->routes = array(); $this->routes = array();
} }
/** /**
* Set Resource URI * Get Iterator
* * @return ArrayIterator
* This method injects the current request's resource URI. This method should be invoked
* only immediately before router iteration.
*
* @param string $uri The request URI
*/ */
public function setResourceUri($uri) public function getIterator() {
{ return new ArrayIterator($this->getMatchedRoutes());
$this->resourceUri = $uri;
} }
/** /**
* Get Current Route object * Get Request
* @return \Slim\Route|false * @return Slim_Http_Request
*/ */
public function getCurrentRoute() public function getRequest() {
{ return $this->request;
$this->getMatchedRoutes(); // <-- Parse if not already parsed
return $this->current();
} }
/** /**
* Return route objects that match the current request URI * Set Request
* @param bool $reload Should matching routes be re-parsed? * @param Slim_Http_Request $req
* @return array[\Slim\Route] * @return void
*/ */
public function getMatchedRoutes($reload = false) public function setRequest( Slim_Http_Request $req ) {
{ $this->request = $req;
}
/**
* Return routes that match the current request
* @return array[Slim_Route]
*/
public function getMatchedRoutes( $reload = false ) {
if ( $reload || is_null($this->matchedRoutes) ) { if ( $reload || is_null($this->matchedRoutes) ) {
$this->matchedRoutes = array(); $this->matchedRoutes = array();
foreach ( $this->routes as $route ) { foreach ( $this->routes as $route ) {
if ($route->matches($this->resourceUri)) { if ( $route->matches($this->request->getResourceUri()) ) {
$this->matchedRoutes[] = $route; $this->matchedRoutes[] = $route;
} }
} }
} }
return $this->matchedRoutes; return $this->matchedRoutes;
} }
/** /**
* Map a route object to a callback function * Map a route to a callback function
* @param string $pattern The URL pattern (ie. "/books/:id") * @param string $pattern The URL pattern (ie. "/books/:id")
* @param mixed $callable Anything that returns TRUE for is_callable() * @param mixed $callable Anything that returns TRUE for is_callable()
* @return \Slim\Route * @return Slim_Route
*/ */
public function map($pattern, $callable) public function map( $pattern, $callable ) {
{ $route = new Slim_Route($pattern, $callable);
$route = new \Slim\Route($pattern, $callable); $route->setRouter($this);
$this->routes[] = $route; $this->routes[] = $route;
return $route; return $route;
} }
/**
* Cache named route
* @param string $name The route name
* @param Slim_Route $route The route object
* @throws RuntimeException If a named route already exists with the same name
* @return void
*/
public function cacheNamedRoute( $name, Slim_Route $route ) {
if ( isset($this->namedRoutes[(string)$name]) ) {
throw new RuntimeException('Named route already exists with name: ' . $name);
}
$this->namedRoutes[$name] = $route;
}
/** /**
* Get URL for named route * Get URL for named route
* @param string $name The name of the route * @param string $name The name of the route
* @param array Associative array of URL parameter names and replacement values * @param array Associative array of URL parameter names and values
* @throws RuntimeException If named route not found * @throws RuntimeException If named route not found
* @return string The URL for the given route populated with provided replacement values * @return string The URL for the given route populated with the given parameters
*/ */
public function urlFor($name, $params = array()) public function urlFor( $name, $params = array() ) {
{ if ( !isset($this->namedRoutes[(string)$name]) ) {
if (!$this->hasNamedRoute($name)) { throw new RuntimeException('Named route not found for name: ' . $name);
throw new \RuntimeException('Named route not found for name: ' . $name);
} }
$search = array(); $pattern = $this->namedRoutes[(string)$name]->getPattern();
foreach (array_keys($params) as $key) { $search = $replace = array();
$search[] = '#:' . $key . '\+?(?!\w)#'; foreach ( $params as $key => $value ) {
$search[] = ':' . $key;
$replace[] = $value;
} }
$pattern = preg_replace($search, $params, $this->getNamedRoute($name)->getPattern()); $pattern = str_replace($search, $replace, $pattern);
//Remove remnants of unpopulated, trailing optional pattern segments //Remove remnants of unpopulated, trailing optional pattern segments
return preg_replace('#\(/?:.+\)|\(|\)#', '', $pattern); return preg_replace(array(
} '@\(\/?:.+\/??\)\??@',
'@\?|\(|\)@'
/** ), '', $this->request->getRootUri() . $pattern);
* Dispatch route
*
* This method invokes the route object's callable. If middleware is
* registered for the route, each callable middleware is invoked in
* the order specified.
*
* This method is smart about trailing slashes on the route pattern.
* If the route's pattern is defined with a trailing slash, and if the
* current request URI does not have a trailing slash but otherwise
* matches the route's pattern, a Slim_Exception_RequestSlash
* will be thrown triggering an HTTP 301 Permanent Redirect to the same
* URI _with_ a trailing slash. This Exception is caught in the
* `Slim::call` loop. If the route's pattern is defined without a
* trailing slash, and if the current request URI does have a trailing
* slash, the route will not be matched and a 404 Not Found
* response will be sent if no subsequent matching routes are found.
*
* @param \Slim\Route $route The route object
* @return bool Was route callable invoked successfully?
* @throws \Slim\Exception\RequestSlash
*/
public function dispatch(\Slim\Route $route)
{
if (substr($route->getPattern(), -1) === '/' && substr($this->resourceUri, -1) !== '/') {
throw new Exception\RequestSlash();
}
//Invoke middleware
foreach ($route->getMiddleware() as $mw) {
if (is_callable($mw)) {
call_user_func_array($mw, array($route));
}
}
//Invoke callable
if (is_callable($route->getCallable())) {
call_user_func_array($route->getCallable(), array_values($route->getParams()));
return true;
}
return false;
}
/**
* Add named route
* @param string $name The route name
* @param \Slim\Route $route The route object
* @throws \RuntimeException If a named route already exists with the same name
*/
public function addNamedRoute($name, \Slim\Route $route)
{
if ($this->hasNamedRoute($name)) {
throw new \RuntimeException('Named route already exists with name: ' . $name);
}
$this->namedRoutes[(string) $name] = $route;
}
/**
* Has named route
* @param string $name The route name
* @return bool
*/
public function hasNamedRoute($name)
{
$this->getNamedRoutes();
return isset($this->namedRoutes[(string) $name]);
}
/**
* Get named route
* @param string $name
* @return \Slim\Route|null
*/
public function getNamedRoute($name)
{
$this->getNamedRoutes();
if ($this->hasNamedRoute($name)) {
return $this->namedRoutes[(string) $name];
} else {
return null;
}
}
/**
* Get named routes
* @return \ArrayIterator
*/
public function getNamedRoutes()
{
if (is_null($this->namedRoutes)) {
$this->namedRoutes = array();
foreach ($this->routes as $route) {
if ($route->getName() !== null) {
$this->addNamedRoute($route->getName(), $route);
}
}
}
return new \ArrayIterator($this->namedRoutes);
} }
/** /**
@ -269,12 +181,10 @@ class Router implements \Iterator
* @param mixed $callable Anything that returns TRUE for is_callable() * @param mixed $callable Anything that returns TRUE for is_callable()
* @return mixed * @return mixed
*/ */
public function notFound($callable = null) public function notFound( $callable = null ) {
{
if ( is_callable($callable) ) { if ( is_callable($callable) ) {
$this->notFound = $callable; $this->notFound = $callable;
} }
return $this->notFound; return $this->notFound;
} }
@ -283,55 +193,11 @@ class Router implements \Iterator
* @param mixed $callable Anything that returns TRUE for is_callable() * @param mixed $callable Anything that returns TRUE for is_callable()
* @return mixed * @return mixed
*/ */
public function error($callable = null) public function error( $callable = null ) {
{
if ( is_callable($callable) ) { if ( is_callable($callable) ) {
$this->error = $callable; $this->error = $callable;
} }
return $this->error; return $this->error;
} }
/**
* Iterator Interface: Rewind
*/
public function rewind()
{
reset($this->matchedRoutes);
}
/**
* Iterator Interface: Current
* @return \Slim\Route|false
*/
public function current()
{
return current($this->matchedRoutes);
}
/**
* Iterator Interface: Key
* @return int|null
*/
public function key()
{
return key($this->matchedRoutes);
}
/**
* Iterator Interface: Next
*/
public function next()
{
next($this->matchedRoutes);
}
/**
* Iterator Interface: Valid
* @return boolean
*/
public function valid()
{
return $this->current();
}
} }

192
Slim/Session/Flash.php Normal file
View File

@ -0,0 +1,192 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.5.0
*
* MIT LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Flash Messaging
*
* This class enables Flash messaging. Messages are persisted in $_SESSION
* with a user-defined key.
*
* USAGE:
*
* 1. Set Flash message to be shown on the next request
*
* Slim::flash('error', 'The object could not be saved');
*
* 2. Set Flash message to be shown on the current request
*
* Slim::flashNow('error', 'The object could not be saved');
*
* 3. Keep old Flash messages for the next request
*
* Slim::flashKeep();
*
* @package Slim
* @author Josh Lockhart
* @since Version 1.3
*/
class Slim_Session_Flash implements ArrayAccess {
/**
* @var string Key used to identify flash information in $_SESSION array
*/
protected $sessionKey = 'flash';
/**
* @var array[array] Storage for flash messages
*/
protected $messages = array(
'prev' => array(), //flash messages from prev request
'next' => array(), //flash messages for next request
'now' => array() //flash messages for current request
);
/**
* Constructor
*
* Establishes Flash session key and loads existing
* Flash messages from the $_SESSION.
*
* @param string $sessionKey
* @return void
*/
public function __construct( $sessionKey = null ) {
if ( !is_null($sessionKey) ) {
$this->setSessionKey($sessionKey);
}
$this->load();
}
/**
* Set the $_SESSION key used to access Flash messages
* @param string $key
* @throws RuntimeException If session key is null
* @return Slim_Session_Flash
*/
public function setSessionKey( $key ) {
if ( is_null($key) ) {
throw new RuntimeException('Session key cannot be null');
}
$this->sessionKey = (string)$key;
return $this;
}
/**
* Get the $_SESSION key used to access Flash messages
* @return string
*/
public function getSessionKey() {
return $this->sessionKey;
}
/**
* Set a Flash message for the current request
* @param string $key
* @param string $value
* @return Slim_Session_Flash
*/
public function now( $key, $value ) {
$this->messages['now'][(string)$key] = $value;
return $this->save();
}
/**
* Set a Flash message for the next request
* @param string $key
* @param string $value
* @return Slim_Session_Flash
*/
public function set( $key, $value ) {
$this->messages['next'][(string)$key] = $value;
return $this->save();
}
/**
* Get Flash messages intended for the current request's View
* @return array[String]
*/
public function getMessages() {
return array_merge($this->messages['prev'], $this->messages['now']);
}
/**
* Load Flash messages from $_SESSION
* @return Slim_Session_Flash
*/
public function load() {
$this->messages['prev'] = isset($_SESSION[$this->sessionKey]) ? $_SESSION[$this->sessionKey] : array();
return $this;
}
/**
* Transfer Flash messages from the previous request
* so they are available to the next request.
* @return Slim_Session_Flash
*/
public function keep() {
foreach ( $this->messages['prev'] as $key => $val ) {
$this->messages['next'][$key] = $val;
}
return $this->save();
}
/**
* Save Flash messages to $_SESSION
* @return Slim_Session_Flash
*/
public function save() {
$_SESSION[$this->sessionKey] = $this->messages['next'];
return $this;
}
/***** ARRAY ACCESS INTERFACE *****/
public function offsetExists( $offset ) {
$messages = $this->getMessages();
return isset($messages[$offset]);
}
public function offsetGet( $offset ) {
$messages = $this->getMessages();
return isset($messages[$offset]) ? $messages[$offset] : null;
}
public function offsetSet( $offset, $value ) {
$this->now($offset, $value);
}
public function offsetUnset( $offset ) {
unset($this->messages['prev'][$offset]);
unset($this->messages['now'][$offset]);
}
}

125
Slim/Session/Handler.php Normal file
View File

@ -0,0 +1,125 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart
* @link http://www.slimframework.com
* @copyright 2011 Josh Lockhart
*
* MIT LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Abstract Session Handler
*
* This abstract class should be extended by each concrete
* session handler. This class defines the contractual class interface
* methods that must be implemented in concrete subclasses. This class
* also provides the final `register` method used by Slim itself to
* actually register the concrete session handler with PHP.
*
* @package Slim
* @author Josh Lockhart
* @since Version 1.3
*/
abstract class Slim_Session_Handler {
/**
* @var Slim
*/
protected $app;
/**
* Register session handler
*
* @return bool
*/
final public function register( Slim $app ) {
$this->app = $app;
return session_set_save_handler(
array($this, 'open'),
array($this, 'close'),
array($this, 'read'),
array($this, 'write'),
array($this, 'destroy'),
array($this, 'gc')
);
}
/**
* Open session
*
* @param string $savePath
* @param string $sessionName
* @return mixed
*/
abstract public function open( $savePath, $sessionName );
/**
* Close session
*
* @return mixed
*/
abstract public function close();
/**
* Read session data with ID
*
* @param string $id The session identifier
* @return string
*/
abstract public function read( $id );
/**
* Write session data with ID
*
* The "write" handler is not executed until after the output stream is
* closed. Thus, output from debugging statements in the "write" handler
* will never be seen in the browser. If debugging output is necessary, it
* is suggested that the debug output be written to a file instead.
*
* @param string $id The session identifier
* @param mixed $sessionData The session data
* @return mixed
*/
abstract public function write( $id, $sessionData );
/**
* Destroy session with ID
*
* @param string $id The session identifier
* @return mixed
*/
abstract public function destroy( $id );
/**
* Session garbage collection
*
* Executed when the PHP session garbage collector is invoked; should
* remove all session data older than the `$maxLifetime`.
*
* @param int $maxLifetime
* @return mixed
*/
abstract public function gc( $maxLifetime );
}

View File

@ -0,0 +1,71 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart
* @link http://www.slimframework.com
* @copyright 2011 Josh Lockhart
*
* MIT LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Session Cookie Handler
*
* This class is used as an adapter for PHP's $_SESSION handling.
* Session data will be written to and read from signed, encrypted
* cookies. If the current PHP installation does not have the `mcrypt`
* extension, session data will be written to signed but unencrypted
* cookies; however, the session cookies will still be secure and will
* become invalid if manually edited after set by PHP.
*
* @package Slim
* @author Josh Lockhart
* @since Version 1.3
*/
class Slim_Session_Handler_Cookies extends Slim_Session_Handler {
public function open( $savePath, $sessionName ) {
return true;
}
public function close() {
return true; //Not used
}
public function read( $id ) {
return $this->app->getEncryptedCookie($id);
}
public function write( $id, $sessionData ) {
$this->app->setEncryptedCookie($id, $sessionData, 0);
}
public function destroy( $id ) {
$this->app->deleteCookie($id);
}
public function gc( $maxLifetime ) {
return true; //Not used
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,12 +2,11 @@
/** /**
* Slim - a micro PHP 5 framework * Slim - a micro PHP 5 framework
* *
* @author Josh Lockhart <info@slimframework.com> * @author Josh Lockhart <info@joshlockhart.com>
* @copyright 2011 Josh Lockhart * @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com * @link http://www.slimframework.com
* @license http://www.slimframework.com/license * @license http://www.slimframework.com/license
* @version 2.0.0 * @version 1.5.0
* @package Slim
* *
* MIT LICENSE * MIT LICENSE
* *
@ -30,62 +29,47 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
namespace Slim;
/** /**
* View * Slim View
* *
* The view is responsible for rendering a template. The view * The View is responsible for rendering and/or displaying a template.
* should subclass \Slim\View and implement this interface: * It is recommended that you subclass View and re-implement the
* * `View::render` method to use a custom templating engine such as
* public render(string $template); * Smarty, Twig, Mustache, etc. It is important that `View::render`
* * `return` the final template output. Do not `echo` the output.
* This method should render the specified template and return
* the resultant string.
* *
* @package Slim * @package Slim
* @author Josh Lockhart * @author Josh Lockhart <info@joshlockhart.com>
* @since 1.0.0 * @since Version 1.0
*/ */
class View class Slim_View {
{
/**
* @var string Absolute or relative filesystem path to a specific template
*
* DEPRECATION WARNING!
* This variable will be removed in the near future
*/
protected $templatePath = '';
/** /**
* @var array Associative array of template variables * @var array Key-value array of data available to the template
*/ */
protected $data = array(); protected $data = array();
/** /**
* @var string Absolute or relative path to the application's templates directory * @var string Absolute or relative path to the templates directory
*/ */
protected $templatesDirectory; protected $templatesDirectory;
/** /**
* Constructor * Constructor
* *
* This is empty but may be implemented in a subclass * This is empty but may be overridden in a subclass
*/ */
public function __construct() public function __construct() {}
{
}
/** /**
* Get data * Get data
* @param string|null $key * @param string $key
* @return mixed If key is null, array of template data; * @return array|mixed|null All View data if no $key, value of datum
* If key exists, value of datum with key; * if $key, or NULL if $key but datum
* If key does not exist, null; * does not exist.
*/ */
public function getData($key = null) public function getData( $key = null ) {
{
if ( !is_null($key) ) { if ( !is_null($key) ) {
return isset($this->data[$key]) ? $this->data[$key] : null; return isset($this->data[$key]) ? $this->data[$key] : null;
} else { } else {
@ -96,121 +80,88 @@ class View
/** /**
* Set data * Set data
* *
* If two arguments: * This method is overloaded to accept two different method signatures.
* A single datum with key is assigned value; * You may use this to set a specific key with a specfic value,
* or you may use this to set all data to a specific array.
* *
* $view->setData('color', 'red'); * USAGE:
* *
* If one argument: * View::setData('color', 'red');
* Replace all data with provided array keys and values; * View::setData(array('color' => 'red', 'number' => 1));
* *
* $view->setData(array('color' => 'red', 'number' => 1)); * @param string|array
* * @param mixed Optional. Only use if first argument is a string.
* @param mixed * @return void
* @param mixed
* @throws InvalidArgumentException If incorrect method signature * @throws InvalidArgumentException If incorrect method signature
*/ */
public function setData() public function setData() {
{
$args = func_get_args(); $args = func_get_args();
if ( count($args) === 1 && is_array($args[0]) ) { if ( count($args) === 1 && is_array($args[0]) ) {
$this->data = $args[0]; $this->data = $args[0];
} else if ( count($args) === 2 ) { } else if ( count($args) === 2 ) {
$this->data[(string)$args[0]] = $args[1]; $this->data[(string)$args[0]] = $args[1];
} else { } else {
throw new \InvalidArgumentException('Cannot set View data with provided arguments. Usage: `View::setData( $key, $value );` or `View::setData([ key => value, ... ]);`'); throw new InvalidArgumentException('Cannot set View data with provided arguments. Usage: `View::setData( $key, $value );` or `View::setData([ key => value, ... ]);`');
} }
} }
/** /**
* Append new data to existing template data * Append data to existing View data
* @param array * @param array $data
* @throws InvalidArgumentException If not given an array argument * @return void
*/ */
public function appendData($data) public function appendData( array $data ) {
{
if (!is_array($data)) {
throw new \InvalidArgumentException('Cannot append view data. Expected array argument.');
}
$this->data = array_merge($this->data, $data); $this->data = array_merge($this->data, $data);
} }
/** /**
* Get templates directory * Get templates directory
* @return string|null Path to templates directory without trailing slash; * @return string|null Path to templates directory without trailing slash
* Returns null if templates directory not set;
*/ */
public function getTemplatesDirectory() public function getTemplatesDirectory() {
{
return $this->templatesDirectory; return $this->templatesDirectory;
} }
/** /**
* Set templates directory * Set templates directory
* @param string $dir * @param string $dir
* @return void
* @throws RuntimeException If directory is not a directory or does not exist
*/ */
public function setTemplatesDirectory($dir) public function setTemplatesDirectory( $dir ) {
{ if ( !is_dir($dir) ) {
throw new RuntimeException('Cannot set View templates directory to: ' . $dir . '. Directory does not exist.');
}
$this->templatesDirectory = rtrim($dir, '/'); $this->templatesDirectory = rtrim($dir, '/');
} }
/**
* Set template
* @param string $template
* @throws RuntimeException If template file does not exist
*
* DEPRECATION WARNING!
* This method will be removed in the near future.
*/
public function setTemplate($template)
{
$this->templatePath = $this->getTemplatesDirectory() . '/' . ltrim($template, '/');
if (!file_exists($this->templatePath)) {
throw new \RuntimeException('View cannot render template `' . $this->templatePath . '`. Template does not exist.');
}
}
/** /**
* Display template * Display template
* *
* This method echoes the rendered template to the current output buffer * This method echoes the rendered template to the current output buffer
* *
* @param string $template Pathname of template file relative to templates directoy * @param string $template Path to template file relative to templates directoy
* @return void
*/ */
public function display($template) public function display( $template ) {
{ echo $this->render($template);
echo $this->fetch($template);
}
/**
* Fetch rendered template
*
* This method returns the rendered template
*
* @param string $template Pathname of template file relative to templates directory
* @return string
*/
public function fetch($template)
{
return $this->render($template);
} }
/** /**
* Render template * Render template
* * @param string $template Path to template file relative to templates directory
* @param string $template Pathname of template file relative to templates directory * @return string Rendered template
* @return string * @throws RuntimeException If template does not exist
*
* DEPRECATION WARNING!
* Use `\Slim\View::fetch` to return a rendered template instead of `\Slim\View::render`.
*/ */
public function render($template) public function render( $template ) {
{
$this->setTemplate($template);
extract($this->data); extract($this->data);
$templatePath = $this->getTemplatesDirectory() . '/' . ltrim($template, '/');
if ( !file_exists($templatePath) ) {
throw new RuntimeException('View cannot render template `' . $templatePath . '`. Template does not exist.');
}
ob_start(); ob_start();
require $this->templatePath; require $templatePath;
return ob_get_clean(); return ob_get_clean();
} }
} }

24
aprs_func.php Normal file
View File

@ -0,0 +1,24 @@
<?php
function aprspass ($callsign) {
$stophere = strpos($callsign, '-');
if ($stophere) $callsign = substr($callsign, 0, $stophere);
$realcall = strtoupper(substr($callsign, 0, 10));
// initialize hash
$hash = 0x73e2;
$i = 0;
$len = strlen($realcall);
// hash callsign two bytes at a time
while ($i < $len) {
$hash ^= ord(substr($realcall, $i, 1))<<8;
$hash ^= ord(substr($realcall, $i + 1, 1));
$i += 2;
}
// mask off the high bit so number is always positive
return $hash & 0x7fff;
}
?>

24
index.php Normal file
View File

@ -0,0 +1,24 @@
<?php
// Load Slim
require 'Slim/Slim.php';
$app = new Slim();
// Functions
//GET route
$app->get('/', function () {
// Require HTML Template
require('templates/main.php');
});
$app->post('/passcode', function () {
require('aprs_func.php');
echo aprspass($_POST['callsign']);
});
// Run slim
$app->run();
?>

41
templates/main.php Normal file
View File

@ -0,0 +1,41 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>APRS Passcode Generator</title>
<style type="text/css" media="screen">
body {
font-family: Arial, "MS Trebuchet", sans-serif;
font-size: 14px;
background-color: whiteSmoke;
}
#container {
margin: 0 auto;
width: 400px;
padding: 10px;
border: 1px solid #E5E5E5;
background-color: #ffffff;
}
label {
width: 100%;
}
</style>
</head>
<body>
<div id="container">
<h1>APRS Passcode Generator</h1>
<form method="post" action="index.php/passcode">
<label for="callsign">Callsign</label>
<input type="text" name="callsign" value="" />
<input type="submit" name="Submit" value="Get Passcode" />
</form>
<p>Techical Example of Passcode Generation using PHP</p>
<p>Source code available on <a href="https://github.com/magicbug/PHP-APRS-Passcode" title="Github" target="_blank">Github</a></p>
</div>
</body>
</html>

0
templates/passcode.php Normal file
View File