bump library versions

This commit is contained in:
Colin Mitchell 2013-02-15 20:29:21 -05:00
parent 3d03cff797
commit 16b22e3263
47 changed files with 8366 additions and 1365 deletions

View File

@ -12,6 +12,6 @@ RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
<FilesMatch "\.(jpg|jpeg|png|gif|swf)$">
Header set Cache-Control "max-age=604800, public"
</FilesMatch>
#<FilesMatch "\.(jpg|jpeg|png|gif|swf)$">
#Header set Cache-Control "max-age=604800, public"
#</FilesMatch>

26
LICENSE
View File

@ -1,13 +1,19 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (c) 2012 Josh Lockhart
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
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:
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
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.

View File

@ -1,64 +1,152 @@
# Gophper: A Gopher Proxy for Modern Times #
# Slim Framework
The [Gopher](http://en.wikipedia.org/wiki/Gopher_(protocol))
protocol was published in 1991. It was popular for some
time, but died off in the late 1990s, partially because of some
poor decisions by the University of Minnesota (which owned
the licensing rights), and also because HTTP and HTML was
undoubtedly a better system for what became the World Wide Web.
[![Build Status](https://secure.travis-ci.org/codeguy/Slim.png)](http://travis-ci.org/codeguy/Slim)
There are still gopher servers out there today, but most
browsers can't visit them anymore, because support for the
Gopher protocol has been removed. There are a couple of
proxy servers out there, but they all suck, and none of them
are open-source, so I wrote Gophper, a very simple proxy
server with a small PHP backend, and a parser written in
Javascript.
Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs.
Slim is easy to use for both beginners and professionals. Slim favors cleanliness over terseness and common cases
over edge cases. Its interface is simple, intuitive, and extensively documented — both online and in the code itself.
Thank you for choosing the Slim Framework for your next project. I think you're going to love it.
## About Gophper ##
## Features
Gophper has a bunch of cool features:
* Powerful router
* Standard and custom HTTP methods
* Route parameters with wildcards and conditions
* Route redirect, halt, and pass
* Route middleware
* Template rendering with custom views
* Flash messages
* Secure cookies with AES-256 encryption
* HTTP caching
* Logging with custom log writers
* Error handling and debugging
* Middleware and hook architecture
* Simple configuration
* The backend is written in PHP using the Slim framework. It is very
simple and easy to modify.
* The bulk of the work is done in Javascript, so you can very easily
re-implement with a different backend if desired.
* Uses the Twitter Bootstrap framework for output -- it's visually
appealing, generates nice code, and should be easy to tweak if
needed.
* It caches gopher pages for fast performance.
* Displays images in modal windows
* Detects when a user has requested a binary file (Image, Document,
etc) and returns that file to the browser, rather than returning junk
and crashing your browser like most gopher proxies.
* Report traffic to GA, and also track stats locally to present reports to users.
* Restrict the proxy to a single host or port if desired. That way you
can use it as the web frontend to your nifty gopher project.
## Getting Started
## Using Gophper ##
### Install
Using Gophper is easy:
* drop the code wherever you want it
* copy config.php.example to config.php, and update any values inside
if needed
* If you want to track traffic in the database, run stats.sql, and
make sure that you've set the right login credentials in the config
file.
* Update templates/intro.html if you want, to point to your Gopher
server, etc. Right now, it has a description of gophper-proxy and
links to some popular gopher sites.
You may install the Slim Framework with Composer (recommended) or manually.
## Bugs/Troubleshooting ##
[Read how to install Slim](http://docs.slimframework.com/pages/getting-started-install)
If you have a problem, feel free to submit a support request on Github.
### System Requirements
## TODO ##
You need **PHP >= 5.3.0**. If you use encrypted cookies, you'll also need the `mcrypt` extension.
* Make it possible to run in a subdirectory
### Hello World Tutorial
## Copyright/License ##
Instantiate a Slim application:
Copyright (c) 2012 Colin Mitchell. Licensed under WTFPL
licence. Please see LICENSE.txt for further details.
$app = new \Slim\Slim();
http://muffinlabs.com
Define a HTTP GET route:
$app->get('/hello/:name', function ($name) {
echo "Hello, $name";
});
Run the Slim application:
$app->run();
### Setup your web server
#### Apache
Ensure the `.htaccess` and `index.php` files are in the same public-accessible directory. The `.htaccess` file
should contain this code:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
#### Nginx
Your nginx configuration file should contain this code (along with other settings you may need) in your `location` block:
try_files $uri $uri/ /index.php;
This assumes that Slim's `index.php` is in the root folder of your project (www root).
#### lighttpd ####
Your lighttpd configuration file should contain this code (along with other settings you may need). This code requires
lighttpd >= 1.4.24.
url.rewrite-if-not-file = ("(.*)" => "/index.php/$0")
This assumes that Slim's `index.php` is in the root folder of your project (www root).
#### IIS
Ensure the `Web.config` and `index.php` files are in the same public-accessible directory. The `Web.config` file should contain this code:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="slim" patternSyntax="Wildcard">
<match url="*" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.php" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
## Documentation
<http://docs.slimframework.com/>
## How to Contribute
### Pull Requests
1. Fork the Slim Framework repository
2. Create a new branch for each feature or improvement
3. Send a pull request from each feature branch to the **develop** branch
It is very important to separate new features or improvements into separate feature branches, and to send a pull
request for each branch. This allows me to review and pull in new features or improvements individually.
### Style Guide
All pull requests must adhere to the [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) standard.
### Unit Testing
All pull requests must be accompanied by passing unit tests and complete code coverage. The Slim Framework uses
`phpunit` for testing.
[Learn about PHPUnit](https://github.com/sebastianbergmann/phpunit/)
## Community
### Forum and Knowledgebase
Visit Slim's official forum and knowledge base at <http://help.slimframework.com> where you can find announcements,
chat with fellow Slim users, ask questions, help others, or show off your cool Slim Framework apps.
### Twitter
Follow [@slimphp](http://www.twitter.com/slimphp) on Twitter to receive news and updates about the framework.
## Author
The Slim Framework is created and maintained by [Josh Lockhart](https://www.joshlockhart.com). Josh is a senior
web developer at [New Media Campaigns](http://www.newmediacampaigns.com/). Josh also created and maintains
[PHP: The Right Way](http://www.phptherightway.com/), a popular movement in the PHP community to introduce new
PHP programmers to best practices and good information.
## License
The Slim Framework is released under the MIT public license.
<http://www.slimframework.com/license>

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim;
/**
* Environment
@ -47,41 +48,45 @@
* @author Josh Lockhart
* @since 1.6.0
*/
class Slim_Environment implements ArrayAccess, IteratorAggregate {
class Environment implements \ArrayAccess, \IteratorAggregate
{
/**
* @var array
*/
protected $properties;
/**
* @var Slim_Environment
* @var \Slim\Environment
*/
protected static $environment;
/**
* Get environment instance (singleton)
*
* This creates and/or returns an Environment instance (singleton)
* derived from $_SERVER variables. You may override the global server
* variables by using `Environment::mock()` instead.
* This creates and/or returns an environment instance (singleton)
* derived from $_SERVER variables. You may override the global server
* variables by using `\Slim\Environment::mock()` instead.
*
* @param bool $refresh Refresh properties using global server variables?
* @return Slim_Environment
* @param bool $refresh Refresh properties using global server variables?
* @return \Slim\Environment
*/
public static function getInstance( $refresh = false ) {
if ( is_null(self::$environment) || $refresh ) {
public static function getInstance($refresh = false)
{
if (is_null(self::$environment) || $refresh) {
self::$environment = new self();
}
return self::$environment;
}
/**
* Get mock environment instance
*
* @param array $userSettings
* @return Environment
* @param array $userSettings
* @return \Slim\Environment
*/
public static function mock( $userSettings = array() ) {
public static function mock($userSettings = array())
{
self::$environment = new self(array_merge(array(
'REQUEST_METHOD' => 'GET',
'SCRIPT_NAME' => '',
@ -98,17 +103,18 @@ class Slim_Environment implements ArrayAccess, IteratorAggregate {
'slim.input' => '',
'slim.errors' => @fopen('php://stderr', 'w')
), $userSettings));
return self::$environment;
}
/**
* Constructor (private access)
*
* @param array|null $settings If present, these are used instead of global server variables
* @return void
* @param array|null $settings If present, these are used instead of global server variables
*/
private function __construct( $settings = null ) {
if ( $settings ) {
private function __construct($settings = null)
{
if ($settings) {
$this->properties = $settings;
} else {
$env = array();
@ -134,13 +140,13 @@ class Slim_Environment implements ArrayAccess, IteratorAggregate {
* The PATH_INFO will be an absolute path with a leading slash; this will be
* used for application routing.
*/
if ( strpos($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']) === 0 ) {
if (strpos($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']) === 0) {
$env['SCRIPT_NAME'] = $_SERVER['SCRIPT_NAME']; //Without URL rewrite
} else {
$env['SCRIPT_NAME'] = pathinfo($_SERVER['SCRIPT_NAME'], PATHINFO_DIRNAME); //With URL rewrite
$env['SCRIPT_NAME'] = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']) ); //With URL rewrite
}
$env['PATH_INFO'] = substr_replace($_SERVER['REQUEST_URI'], '', 0, strlen($env['SCRIPT_NAME']));
if ( strpos($env['PATH_INFO'], '?') !== false ) {
if (strpos($env['PATH_INFO'], '?') !== false) {
$env['PATH_INFO'] = substr_replace($env['PATH_INFO'], '', strpos($env['PATH_INFO'], '?')); //query string is not removed automatically
}
$env['SCRIPT_NAME'] = rtrim($env['SCRIPT_NAME'], '/');
@ -157,10 +163,11 @@ class Slim_Environment implements ArrayAccess, IteratorAggregate {
//HTTP request headers
$specialHeaders = array('CONTENT_TYPE', 'CONTENT_LENGTH', 'PHP_AUTH_USER', 'PHP_AUTH_PW', 'PHP_AUTH_DIGEST', 'AUTH_TYPE');
foreach ( $_SERVER as $key => $value ) {
if ( strpos($key, 'HTTP_') === 0 ) {
foreach ($_SERVER as $key => $value) {
$value = is_string($value) ? trim($value) : $value;
if (strpos($key, 'HTTP_') === 0) {
$env[substr($key, 5)] = $value;
} else if ( strpos($key, 'X_') === 0 || in_array($key, $specialHeaders) ) {
} elseif (strpos($key, 'X_') === 0 || in_array($key, $specialHeaders)) {
$env[$key] = $value;
}
}
@ -170,7 +177,7 @@ class Slim_Environment implements ArrayAccess, IteratorAggregate {
//Input stream (readable one time only; not available for mutipart/form-data requests)
$rawInput = @file_get_contents('php://input');
if ( !$rawInput ) {
if (!$rawInput) {
$rawInput = '';
}
$env['slim.input'] = $rawInput;
@ -185,15 +192,17 @@ class Slim_Environment implements ArrayAccess, IteratorAggregate {
/**
* Array Access: Offset Exists
*/
public function offsetExists( $offset ) {
public function offsetExists($offset)
{
return isset($this->properties[$offset]);
}
/**
* Array Access: Offset Get
*/
public function offsetGet( $offset ) {
if ( isset($this->properties[$offset]) ) {
public function offsetGet($offset)
{
if (isset($this->properties[$offset])) {
return $this->properties[$offset];
} else {
return null;
@ -203,23 +212,26 @@ class Slim_Environment implements ArrayAccess, IteratorAggregate {
/**
* Array Access: Offset Set
*/
public function offsetSet( $offset, $value ) {
public function offsetSet($offset, $value)
{
$this->properties[$offset] = $value;
}
/**
* Array Access: Offset Unset
*/
public function offsetUnset( $offset ) {
public function offsetUnset($offset)
{
unset($this->properties[$offset]);
}
/**
* IteratorAggregate
*
* @return ArrayIterator
* @return \ArrayIterator
*/
public function getIterator() {
return new ArrayIterator($this->properties);
public function getIterator()
{
return new \ArrayIterator($this->properties);
}
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim\Exception;
/**
* Pass Exception
@ -43,4 +44,7 @@
* @author Josh Lockhart
* @since 1.0.0
*/
class Slim_Exception_Pass extends Exception {}
class Pass extends \Exception
{
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim\Exception;
/**
* Stop Exception
@ -41,4 +42,7 @@
* @author Josh Lockhart
* @since 1.0.0
*/
class Slim_Exception_Stop extends Exception {}
class Stop extends \Exception
{
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim\Http;
/**
* HTTP Headers
@ -46,7 +47,8 @@
* @author Josh Lockhart
* @since 1.6.0
*/
class Slim_Http_Headers implements ArrayAccess, Iterator, Countable {
class Headers implements \ArrayAccess, \Iterator, \Countable
{
/**
* @var array HTTP headers
*/
@ -59,46 +61,49 @@ class Slim_Http_Headers implements ArrayAccess, Iterator, Countable {
/**
* Constructor
* @param array $headers
* @return void
* @param array $headers
*/
public function __construct( $headers = array() ) {
public function __construct($headers = array())
{
$this->merge($headers);
}
/**
* Merge Headers
* @param array $headers
* @return void
* @param array $headers
*/
public function merge( $headers ) {
foreach ( $headers as $name => $value ) {
public function merge($headers)
{
foreach ($headers as $name => $value) {
$this[$name] = $value;
}
}
/**
* Transform header name into canonical form
* @param string $name
* @return string
* @param string $name
* @return string
*/
protected function canonical( $name ) {
protected function canonical($name)
{
return strtolower(trim($name));
}
/**
* Array Access: Offset Exists
*/
public function offsetExists( $offset ) {
public function offsetExists($offset)
{
return isset($this->headers[$this->canonical($offset)]);
}
/**
* Array Access: Offset Get
*/
public function offsetGet( $offset ) {
public function offsetGet($offset)
{
$canonical = $this->canonical($offset);
if ( isset($this->headers[$canonical]) ) {
if (isset($this->headers[$canonical])) {
return $this->headers[$canonical];
} else {
return null;
@ -108,7 +113,8 @@ class Slim_Http_Headers implements ArrayAccess, Iterator, Countable {
/**
* Array Access: Offset Set
*/
public function offsetSet( $offset, $value ) {
public function offsetSet($offset, $value)
{
$canonical = $this->canonical($offset);
$this->headers[$canonical] = $value;
$this->map[$canonical] = $offset;
@ -117,7 +123,8 @@ class Slim_Http_Headers implements ArrayAccess, Iterator, Countable {
/**
* Array Access: Offset Unset
*/
public function offsetUnset( $offset ) {
public function offsetUnset($offset)
{
$canonical = $this->canonical($offset);
unset($this->headers[$canonical], $this->map[$canonical]);
}
@ -125,43 +132,50 @@ class Slim_Http_Headers implements ArrayAccess, Iterator, Countable {
/**
* Countable: Count
*/
public function count() {
public function count()
{
return count($this->headers);
}
/**
* Iterator: Rewind
*/
public function rewind() {
public function rewind()
{
reset($this->headers);
}
/**
* Iterator: Current
*/
public function current() {
public function current()
{
return current($this->headers);
}
/**
* Iterator: Key
*/
public function key() {
public function key()
{
$key = key($this->headers);
return $this->map[$key];
}
/**
* Iterator: Next
*/
public function next() {
public function next()
{
return next($this->headers);
}
/**
* Iterator: Valid
*/
public function valid() {
public function valid()
{
return current($this->headers) !== false;
}
}
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim\Http;
/**
* Slim HTTP Request
@ -41,7 +42,8 @@
* @author Josh Lockhart
* @since 1.0.0
*/
class Slim_Http_Request {
class Request
{
const METHOD_HEAD = 'HEAD';
const METHOD_GET = 'GET';
const METHOD_POST = 'POST';
@ -62,10 +64,11 @@ class Slim_Http_Request {
/**
* Constructor
* @param array $env
* @see Slim_Environment
* @param array $env
* @see \Slim\Environment
*/
public function __construct( $env ) {
public function __construct($env)
{
$this->env = $env;
}
@ -73,7 +76,8 @@ class Slim_Http_Request {
* Get HTTP method
* @return string
*/
public function getMethod() {
public function getMethod()
{
return $this->env['REQUEST_METHOD'];
}
@ -81,7 +85,8 @@ class Slim_Http_Request {
* Is this a GET request?
* @return bool
*/
public function isGet() {
public function isGet()
{
return $this->getMethod() === self::METHOD_GET;
}
@ -89,7 +94,8 @@ class Slim_Http_Request {
* Is this a POST request?
* @return bool
*/
public function isPost() {
public function isPost()
{
return $this->getMethod() === self::METHOD_POST;
}
@ -97,7 +103,8 @@ class Slim_Http_Request {
* Is this a PUT request?
* @return bool
*/
public function isPut() {
public function isPut()
{
return $this->getMethod() === self::METHOD_PUT;
}
@ -105,7 +112,8 @@ class Slim_Http_Request {
* Is this a DELETE request?
* @return bool
*/
public function isDelete() {
public function isDelete()
{
return $this->getMethod() === self::METHOD_DELETE;
}
@ -113,7 +121,8 @@ class Slim_Http_Request {
* Is this a HEAD request?
* @return bool
*/
public function isHead() {
public function isHead()
{
return $this->getMethod() === self::METHOD_HEAD;
}
@ -121,7 +130,8 @@ class Slim_Http_Request {
* Is this a OPTIONS request?
* @return bool
*/
public function isOptions() {
public function isOptions()
{
return $this->getMethod() === self::METHOD_OPTIONS;
}
@ -129,10 +139,11 @@ class Slim_Http_Request {
* Is this an AJAX request?
* @return bool
*/
public function isAjax() {
if ( $this->params('isajax') ) {
public function isAjax()
{
if ($this->params('isajax')) {
return true;
} else if ( isset($this->env['X_REQUESTED_WITH']) && $this->env['X_REQUESTED_WITH'] === 'XMLHttpRequest' ) {
} elseif (isset($this->env['X_REQUESTED_WITH']) && $this->env['X_REQUESTED_WITH'] === 'XMLHttpRequest') {
return true;
} else {
return false;
@ -143,7 +154,8 @@ class Slim_Http_Request {
* Is this an XHR request? (alias of Slim_Http_Request::isAjax)
* @return bool
*/
public function isXhr() {
public function isXhr()
{
return $this->isAjax();
}
@ -153,13 +165,14 @@ class Slim_Http_Request {
* 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
* @param string $key
* @return array|mixed|null
*/
public function params( $key = null ) {
public function params($key = null)
{
$union = array_merge($this->get(), $this->post());
if ( $key ) {
if ( isset($union[$key]) ) {
if ($key) {
if (isset($union[$key])) {
return $union[$key];
} else {
return null;
@ -175,21 +188,22 @@ class Slim_Http_Request {
* This method returns a key-value array of data sent in the HTTP request query string, 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
* @param string $key
* @return array|mixed|null
*/
public function get( $key = null ) {
if ( !isset($this->env['slim.request.query_hash']) ) {
public function get($key = null)
{
if (!isset($this->env['slim.request.query_hash'])) {
$output = array();
if ( function_exists('mb_parse_str') && !isset($this->env['slim.tests.ignore_multibyte']) ) {
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'] = Slim_Http_Util::stripSlashesIfMagicQuotes($output);
$this->env['slim.request.query_hash'] = Util::stripSlashesIfMagicQuotes($output);
}
if ( $key ) {
if ( isset($this->env['slim.request.query_hash'][$key]) ) {
if ($key) {
if (isset($this->env['slim.request.query_hash'][$key])) {
return $this->env['slim.request.query_hash'][$key];
} else {
return null;
@ -205,28 +219,31 @@ class Slim_Http_Request {
* 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
* @param string $key
* @return array|mixed|null
* @throws \RuntimeException If environment input is not available
*/
public function post( $key = null ) {
if ( !isset($this->env['slim.input']) ) {
throw new RuntimeException('Missing slim.input in environment variables');
public function post($key = null)
{
if (!isset($this->env['slim.input'])) {
throw new \RuntimeException('Missing slim.input in environment variables');
}
if ( !isset($this->env['slim.request.form_hash']) ) {
if (!isset($this->env['slim.request.form_hash'])) {
$this->env['slim.request.form_hash'] = array();
if ( $this->isFormData() ) {
if ($this->isFormData() && is_string($this->env['slim.input'])) {
$output = array();
if ( function_exists('mb_parse_str') && !isset($this->env['slim.tests.ignore_multibyte']) ) {
if (function_exists('mb_parse_str') && !isset($this->env['slim.tests.ignore_multibyte'])) {
mb_parse_str($this->env['slim.input'], $output);
} else {
parse_str($this->env['slim.input'], $output);
}
$this->env['slim.request.form_hash'] = Slim_Http_Util::stripSlashesIfMagicQuotes($output);
$this->env['slim.request.form_hash'] = Util::stripSlashesIfMagicQuotes($output);
} else {
$this->env['slim.request.form_hash'] = Util::stripSlashesIfMagicQuotes($_POST);
}
}
if ( $key ) {
if ( isset($this->env['slim.request.form_hash'][$key]) ) {
if ($key) {
if (isset($this->env['slim.request.form_hash'][$key])) {
return $this->env['slim.request.form_hash'][$key];
} else {
return null;
@ -237,11 +254,22 @@ class Slim_Http_Request {
}
/**
* Fetch PUT data (alias for Slim_Http_Request::post)
* @param string $key
* @return array|mixed|null
* Fetch PUT data (alias for \Slim\Http\Request::post)
* @param string $key
* @return array|mixed|null
*/
public function put( $key = null ) {
public function put($key = null)
{
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);
}
@ -251,16 +279,17 @@ class Slim_Http_Request {
* 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
* @param string $key
* @return array|string|null
*/
public function cookies( $key = null ) {
if ( !isset($this->env['slim.request.cookie_hash']) ) {
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'] = Slim_Http_Util::parseCookieHeader($cookieHeader);
$this->env['slim.request.cookie_hash'] = Util::parseCookieHeader($cookieHeader);
}
if ( $key ) {
if ( isset($this->env['slim.request.cookie_hash'][$key]) ) {
if ($key) {
if (isset($this->env['slim.request.cookie_hash'][$key])) {
return $this->env['slim.request.cookie_hash'][$key];
} else {
return null;
@ -274,9 +303,11 @@ class Slim_Http_Request {
* Does the Request body contain parseable form data?
* @return bool
*/
public function isFormData() {
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);
return ($method === self::METHOD_POST && is_null($this->getContentType())) || in_array($this->getMediaType(), self::$formDataMediaTypes);
}
/**
@ -285,27 +316,29 @@ class Slim_Http_Request {
* 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
* @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 ) {
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]) ) {
if (isset($this->env[$key])) {
return $this->env[$key];
} else {
return $default;
}
} else {
$headers = array();
foreach ( $this->env as $key => $value ) {
if ( strpos($key, 'slim.') !== 0 ) {
foreach ($this->env as $key => $value) {
if (strpos($key, 'slim.') !== 0) {
$headers[$key] = $value;
}
}
return $headers;
}
}
@ -314,7 +347,8 @@ class Slim_Http_Request {
* Get Body
* @return string
*/
public function getBody() {
public function getBody()
{
return $this->env['slim.input'];
}
@ -322,8 +356,9 @@ class Slim_Http_Request {
* Get Content Type
* @return string
*/
public function getContentType() {
if ( isset($this->env['CONTENT_TYPE']) ) {
public function getContentType()
{
if (isset($this->env['CONTENT_TYPE'])) {
return $this->env['CONTENT_TYPE'];
} else {
return null;
@ -334,10 +369,12 @@ class Slim_Http_Request {
* Get Media Type (type/subtype within Content Type header)
* @return string|null
*/
public function getMediaType() {
public function getMediaType()
{
$contentType = $this->getContentType();
if ( $contentType ) {
if ($contentType) {
$contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType);
return strtolower($contentTypeParts[0]);
} else {
return null;
@ -348,17 +385,19 @@ class Slim_Http_Request {
* Get Media Type Params
* @return array
*/
public function getMediaTypeParams() {
public function getMediaTypeParams()
{
$contentType = $this->getContentType();
$contentTypeParams = array();
if ( $contentType ) {
if ($contentType) {
$contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType);
$contentTypePartsLength = count($contentTypeParts);
for ( $i = 1; $i < $contentTypePartsLength; $i++ ) {
for ($i = 1; $i < $contentTypePartsLength; $i++) {
$paramParts = explode('=', $contentTypeParts[$i]);
$contentTypeParams[strtolower($paramParts[0])] = $paramParts[1];
}
}
return $contentTypeParams;
}
@ -366,9 +405,10 @@ class Slim_Http_Request {
* Get Content Charset
* @return string|null
*/
public function getContentCharset() {
public function getContentCharset()
{
$mediaTypeParams = $this->getMediaTypeParams();
if ( isset($mediaTypeParams['charset']) ) {
if (isset($mediaTypeParams['charset'])) {
return $mediaTypeParams['charset'];
} else {
return null;
@ -379,9 +419,10 @@ class Slim_Http_Request {
* Get Content-Length
* @return int
*/
public function getContentLength() {
if ( isset($this->env['CONTENT_LENGTH']) ) {
return (int)$this->env['CONTENT_LENGTH'];
public function getContentLength()
{
if (isset($this->env['CONTENT_LENGTH'])) {
return (int) $this->env['CONTENT_LENGTH'];
} else {
return 0;
}
@ -391,8 +432,15 @@ class Slim_Http_Request {
* Get Host
* @return string
*/
public function getHost() {
if ( isset($this->env['HOST']) ) {
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'];
@ -403,7 +451,8 @@ class Slim_Http_Request {
* Get Host with Port
* @return string
*/
public function getHostWithPort() {
public function getHostWithPort()
{
return sprintf('%s:%s', $this->getHost(), $this->getPort());
}
@ -411,15 +460,17 @@ class Slim_Http_Request {
* Get Port
* @return int
*/
public function getPort() {
return (int)$this->env['SERVER_PORT'];
public function getPort()
{
return (int) $this->env['SERVER_PORT'];
}
/**
* Get Scheme (https or http)
* @return string
*/
public function getScheme() {
public function getScheme()
{
return $this->env['slim.url_scheme'];
}
@ -427,7 +478,8 @@ class Slim_Http_Request {
* Get Script Name (physical path)
* @return string
*/
public function getScriptName() {
public function getScriptName()
{
return $this->env['SCRIPT_NAME'];
}
@ -435,7 +487,8 @@ class Slim_Http_Request {
* LEGACY: Get Root URI (alias for Slim_Http_Request::getScriptName)
* @return string
*/
public function getRootUri() {
public function getRootUri()
{
return $this->getScriptName();
}
@ -443,7 +496,8 @@ class Slim_Http_Request {
* Get Path (physical path + virtual path)
* @return string
*/
public function getPath() {
public function getPath()
{
return $this->getScriptName() . $this->getPathInfo();
}
@ -451,7 +505,8 @@ class Slim_Http_Request {
* Get Path Info (virtual path)
* @return string
*/
public function getPathInfo() {
public function getPathInfo()
{
return $this->env['PATH_INFO'];
}
@ -459,7 +514,8 @@ class Slim_Http_Request {
* LEGACY: Get Resource URI (alias for Slim_Http_Request::getPathInfo)
* @return string
*/
public function getResourceUri() {
public function getResourceUri()
{
return $this->getPathInfo();
}
@ -467,11 +523,13 @@ class Slim_Http_Request {
* Get URL (scheme + host [ + port if non-standard ])
* @return string
*/
public function getUrl() {
public function getUrl()
{
$url = $this->getScheme() . '://' . $this->getHost();
if ( ( $this->getScheme() === 'https' && $this->getPort() !== 443 ) || ( $this->getScheme() === 'http' && $this->getPort() !== 80 ) ) {
if (($this->getScheme() === 'https' && $this->getPort() !== 443) || ($this->getScheme() === 'http' && $this->getPort() !== 80)) {
$url .= sprintf(':%s', $this->getPort());
}
return $url;
}
@ -479,7 +537,14 @@ class Slim_Http_Request {
* Get IP
* @return string
*/
public function getIp() {
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'];
}
@ -487,8 +552,9 @@ class Slim_Http_Request {
* Get Referrer
* @return string|null
*/
public function getReferrer() {
if ( isset($this->env['REFERER']) ) {
public function getReferrer()
{
if (isset($this->env['REFERER'])) {
return $this->env['REFERER'];
} else {
return null;
@ -499,7 +565,8 @@ class Slim_Http_Request {
* Get Referer (for those who can't spell)
* @return string|null
*/
public function getReferer() {
public function getReferer()
{
return $this->getReferrer();
}
@ -507,8 +574,9 @@ class Slim_Http_Request {
* Get User Agent
* @return string|null
*/
public function getUserAgent() {
if ( isset($this->env['USER_AGENT']) ) {
public function getUserAgent()
{
if (isset($this->env['USER_AGENT'])) {
return $this->env['USER_AGENT'];
} else {
return null;

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim\Http;
/**
* Response
@ -42,15 +43,15 @@
* @author Josh Lockhart
* @since 1.0.0
*/
class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
class Response implements \ArrayAccess, \Countable, \IteratorAggregate
{
/**
* @var int HTTP status code
*/
protected $status;
/**
* @var Slim_Http_Headers List of HTTP response headers
* @see Slim_Http_Headers
* @var \Slim\Http\Headers List of HTTP response headers
*/
protected $header;
@ -120,91 +121,103 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
/**
* Constructor
* @param string $body The HTTP response body
* @param int $status The HTTP response status
* @param Slim_Http_Headers|array $header The HTTP response headers
* @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() ) {
$this->status = (int)$status;
public function __construct($body = '', $status = 200, $header = array())
{
$this->status = (int) $status;
$headers = array();
foreach ( $header as $key => $value ) {
foreach ($header as $key => $value) {
$headers[$key] = $value;
}
$this->header = new Slim_Http_Headers(array_merge(array('Content-Type' => 'text/html'), $headers));
$this->header = new Headers(array_merge(array('Content-Type' => 'text/html'), $headers));
$this->body = '';
$this->write($body);
}
/**
* Get and set status
* @param int|null $status
* @return int
* @param int|null $status
* @return int
*/
public function status( $status = null ) {
if ( !is_null($status) ) {
$this->status = (int)$status;
public function status($status = null)
{
if (!is_null($status)) {
$this->status = (int) $status;
}
return $this->status;
}
/**
* Get and set header
* @param string $name Header name
* @param string|null $value Header value
* @return string Header value
* @param string $name Header name
* @param string|null $value Header value
* @return string Header value
*/
public function header( $name, $value = null ) {
if ( !is_null($value) ) {
public function header($name, $value = null)
{
if (!is_null($value)) {
$this[$name] = $value;
}
return $this[$name];
}
/**
* Get headers
* @return Slim_Http_Headers
* @return \Slim\Http\Headers
*/
public function headers() {
public function headers()
{
return $this->header;
}
/**
* Get and set body
* @param string|null $body Content of HTTP response body
* @return string
* @param string|null $body Content of HTTP response body
* @return string
*/
public function body( $body = null ) {
if ( !is_null($body) ) {
public function body($body = null)
{
if (!is_null($body)) {
$this->write($body, true);
}
return $this->body;
}
/**
* Get and set length
* @param int|null $length
* @return int
* @param int|null $length
* @return int
*/
public function length( $length = null ) {
if ( !is_null($length) ) {
$this->length = (int)$length;
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 bool $replace Overwrite existing response body?
* @return string The updated 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
*/
public function write( $body, $replace = false ) {
if ( $replace ) {
public function write($body, $replace = false)
{
if ($replace) {
$this->body = $body;
} else {
$this->body .= (string)$body;
$this->body .= (string) $body;
}
$this->length = strlen($this->body);
return $this->body;
}
@ -217,9 +230,11 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
*
* @return array[int status, array headers, string body]
*/
public function finalize() {
if ( in_array($this->status, array(204, 304)) ) {
public function finalize()
{
if (in_array($this->status, array(204, 304))) {
unset($this['Content-Type'], $this['Content-Length']);
return array($this->status, $this->header, '');
} else {
return array($this->status, $this->header, $this->body);
@ -235,12 +250,13 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* 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
* @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 ) {
Slim_Http_Util::setCookieHeader($this->header, $name, $value);
public function setCookie($name, $value)
{
Util::setCookieHeader($this->header, $name, $value);
}
/**
@ -258,11 +274,12 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* 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
* @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() ) {
Slim_Http_Util::deleteCookieHeader($this->header, $name, $value);
public function deleteCookie($name, $value = array())
{
Util::deleteCookieHeader($this->header, $name, $value);
}
/**
@ -271,10 +288,11 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* 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
* @param string $url The redirect destination
* @param int $status The redirect HTTP status code
*/
public function redirect ( $url, $status = 302 ) {
public function redirect ($url, $status = 302)
{
$this->status = $status;
$this['Location'] = $url;
}
@ -283,7 +301,8 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* Helpers: Empty?
* @return bool
*/
public function isEmpty() {
public function isEmpty()
{
return in_array($this->status, array(201, 204, 304));
}
@ -291,7 +310,8 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* Helpers: Informational?
* @return bool
*/
public function isInformational() {
public function isInformational()
{
return $this->status >= 100 && $this->status < 200;
}
@ -299,7 +319,8 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* Helpers: OK?
* @return bool
*/
public function isOk() {
public function isOk()
{
return $this->status === 200;
}
@ -307,7 +328,8 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* Helpers: Successful?
* @return bool
*/
public function isSuccessful() {
public function isSuccessful()
{
return $this->status >= 200 && $this->status < 300;
}
@ -315,7 +337,8 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* Helpers: Redirect?
* @return bool
*/
public function isRedirect() {
public function isRedirect()
{
return in_array($this->status, array(301, 302, 303, 307));
}
@ -323,7 +346,8 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* Helpers: Redirection?
* @return bool
*/
public function isRedirection() {
public function isRedirection()
{
return $this->status >= 300 && $this->status < 400;
}
@ -331,7 +355,8 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* Helpers: Forbidden?
* @return bool
*/
public function isForbidden() {
public function isForbidden()
{
return $this->status === 403;
}
@ -339,7 +364,8 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* Helpers: Not Found?
* @return bool
*/
public function isNotFound() {
public function isNotFound()
{
return $this->status === 404;
}
@ -347,7 +373,8 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* Helpers: Client error?
* @return bool
*/
public function isClientError() {
public function isClientError()
{
return $this->status >= 400 && $this->status < 500;
}
@ -355,22 +382,25 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* Helpers: Server Error?
* @return bool
*/
public function isServerError() {
public function isServerError()
{
return $this->status >= 500 && $this->status < 600;
}
/**
* Array Access: Offset Exists
*/
public function offsetExists( $offset ) {
public function offsetExists( $offset )
{
return isset($this->header[$offset]);
}
/**
* Array Access: Offset Get
*/
public function offsetGet( $offset ) {
if ( isset($this->header[$offset]) ) {
public function offsetGet( $offset )
{
if (isset($this->header[$offset])) {
return $this->header[$offset];
} else {
return null;
@ -380,33 +410,37 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
/**
* Array Access: Offset Set
*/
public function offsetSet( $offset, $value ) {
public function offsetSet($offset, $value)
{
$this->header[$offset] = $value;
}
/**
* Array Access: Offset Unset
*/
public function offsetUnset( $offset ) {
public function offsetUnset($offset)
{
unset($this->header[$offset]);
}
/**
* Countable: Count
*/
public function count() {
public function count()
{
return count($this->header);
}
/**
* Get Iterator
*
* This returns the contained `Slim_Http_Headers` instance which
* This returns the contained `\Slim\Http\Headers` instance which
* is itself iterable.
*
* @return Slim_Http_Headers
* @return \Slim\Http\Headers
*/
public function getIterator() {
public function getIterator()
{
return $this->header;
}
@ -414,11 +448,12 @@ class Slim_Http_Response implements ArrayAccess, Countable, IteratorAggregate {
* Get message for HTTP status code
* @return string|null
*/
public static function getMessageForCode( $status ) {
if ( isset(self::$messages[$status]) ) {
public static function getMessageForCode($status)
{
if (isset(self::$messages[$status])) {
return self::$messages[$status];
} else {
return null;
}
}
}
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim\Http;
/**
* Slim HTTP Utilities
@ -40,7 +41,8 @@
* @author Josh Lockhart
* @since 1.0.0
*/
class Slim_Http_Util {
class Util
{
/**
* Strip slashes from string or array
*
@ -49,12 +51,13 @@ class Slim_Http_Util {
* override the magic quotes setting with either TRUE or FALSE as the send argument
* to force this method to strip or not strip slashes from its input.
*
* @var array|string $rawData
* @return array|string
* @var array|string $rawData
* @return array|string
*/
public static function stripSlashesIfMagicQuotes( $rawData, $overrideStripSlashes = null ) {
public static function stripSlashesIfMagicQuotes($rawData, $overrideStripSlashes = null)
{
$strip = is_null($overrideStripSlashes) ? get_magic_quotes_gpc() : $overrideStripSlashes;
if ( $strip ) {
if ($strip) {
return self::_stripSlashes($rawData);
} else {
return $rawData;
@ -63,10 +66,11 @@ class Slim_Http_Util {
/**
* Strip slashes from string or array
* @param array|string $rawData
* @return array|string
* @param array|string $rawData
* @return array|string
*/
protected static function _stripSlashes( $rawData ) {
protected static function _stripSlashes($rawData)
{
return is_array($rawData) ? array_map(array('self', '_stripSlashes'), $rawData) : stripslashes($rawData);
}
@ -78,14 +82,15 @@ class Slim_Http_Util {
* may override the default cipher and cipher mode by passing your own desired
* cipher and cipher mode as the final key-value array argument.
*
* @param string $data The unencrypted data
* @param string $key The encryption key
* @param string $iv The encryption initialization vector
* @param array $settings Optional key-value array with custom algorithm and mode
* @return string
* @param string $data The unencrypted data
* @param string $key The encryption key
* @param string $iv The encryption initialization vector
* @param array $settings Optional key-value array with custom algorithm and mode
* @return string
*/
public static function encrypt( $data, $key, $iv, $settings = array() ) {
if ( $data === '' || !extension_loaded('mcrypt') ) {
public static function encrypt($data, $key, $iv, $settings = array())
{
if ($data === '' || !extension_loaded('mcrypt')) {
return $data;
}
@ -100,13 +105,13 @@ class Slim_Http_Util {
//Validate IV
$ivSize = mcrypt_enc_get_iv_size($module);
if ( strlen($iv) > $ivSize ) {
if (strlen($iv) > $ivSize) {
$iv = substr($iv, 0, $ivSize);
}
//Validate key
$keySize = mcrypt_enc_get_key_size($module);
if ( strlen($key) > $keySize ) {
if (strlen($key) > $keySize) {
$key = substr($key, 0, $keySize);
}
@ -126,14 +131,15 @@ class Slim_Http_Util {
* may override the default cipher and cipher mode by passing your own desired
* cipher and cipher mode as the final key-value array argument.
*
* @param string $data The encrypted data
* @param string $key The encryption key
* @param string $iv The encryption initialization vector
* @param array $settings Optional key-value array with custom algorithm and mode
* @return string
* @param string $data The encrypted data
* @param string $key The encryption key
* @param string $iv The encryption initialization vector
* @param array $settings Optional key-value array with custom algorithm and mode
* @return string
*/
public static function decrypt( $data, $key, $iv, $settings = array() ) {
if ( $data === '' || !extension_loaded('mcrypt') ) {
public static function decrypt($data, $key, $iv, $settings = array())
{
if ($data === '' || !extension_loaded('mcrypt')) {
return $data;
}
@ -148,13 +154,13 @@ class Slim_Http_Util {
//Validate IV
$ivSize = mcrypt_enc_get_iv_size($module);
if ( strlen($iv) > $ivSize ) {
if (strlen($iv) > $ivSize) {
$iv = substr($iv, 0, $ivSize);
}
//Validate key
$keySize = mcrypt_enc_get_key_size($module);
if ( strlen($key) > $keySize ) {
if (strlen($key) > $keySize) {
$key = substr($key, 0, $keySize);
}
@ -174,21 +180,23 @@ class Slim_Http_Util {
* cookie value is encrypted and hashed so that its value is
* secure and checked for integrity when read in subsequent requests.
*
* @param string $value The unsecure HTTP cookie value
* @param int $expires The UNIX timestamp at which this cookie will expire
* @param string $secret The secret key used to hash the cookie value
* @param int $algorithm The algorithm to use for encryption
* @param int $mode The algorithm mode to use for encryption
* @param string
* @param string $value The unsecure HTTP cookie value
* @param int $expires The UNIX timestamp at which this cookie will expire
* @param string $secret The secret key used to hash the cookie value
* @param int $algorithm The algorithm to use for encryption
* @param int $mode The algorithm mode to use for encryption
* @param string
*/
public static function encodeSecureCookie( $value, $expires, $secret, $algorithm, $mode ) {
public static function encodeSecureCookie($value, $expires, $secret, $algorithm, $mode)
{
$key = hash_hmac('sha1', $expires, $secret);
$iv = md5($expires);
$iv = self::get_iv($expires, $secret);
$secureString = base64_encode(self::encrypt($value, $key, $iv, array(
'algorithm' => $algorithm,
'mode' => $mode
)));
$verificationString = hash_hmac('sha1', $expires . $value, $key);
return implode('|', array($expires, $secureString, $verificationString));
}
@ -199,29 +207,31 @@ class Slim_Http_Util {
* cookie value is encrypted and hashed so that its value is
* secure and checked for integrity when read in subsequent requests.
*
* @param string $value The secure HTTP cookie value
* @param int $expires The UNIX timestamp at which this cookie will expire
* @param string $secret The secret key used to hash the cookie value
* @param int $algorithm The algorithm to use for encryption
* @param int $mode The algorithm mode to use for encryption
* @param string
* @param string $value The secure HTTP cookie value
* @param int $expires The UNIX timestamp at which this cookie will expire
* @param string $secret The secret key used to hash the cookie value
* @param int $algorithm The algorithm to use for encryption
* @param int $mode The algorithm mode to use for encryption
* @param string
*/
public static function decodeSecureCookie( $value, $secret, $algorithm, $mode ) {
if ( $value ) {
public static function decodeSecureCookie($value, $secret, $algorithm, $mode)
{
if ($value) {
$value = explode('|', $value);
if ( count($value) === 3 && ( (int)$value[0] === 0 || (int)$value[0] > time() ) ) {
if (count($value) === 3 && ((int) $value[0] === 0 || (int) $value[0] > time())) {
$key = hash_hmac('sha1', $value[0], $secret);
$iv = md5($value[0]);
$iv = self::get_iv($value[0], $secret);
$data = self::decrypt(base64_decode($value[1]), $key, $iv, array(
'algorithm' => $algorithm,
'mode' => $mode
));
$verificationString = hash_hmac('sha1', $value[0] . $data, $key);
if ( $verificationString === $value[2] ) {
if ($verificationString === $value[2]) {
return $data;
}
}
}
return false;
}
@ -237,48 +247,48 @@ class Slim_Http_Util {
* first argument; this method directly modifies this object instead of
* returning a value.
*
* @param array $header
* @param string $name
* @param string $value
* @return void
* @param array $header
* @param string $name
* @param string $value
*/
public static function setCookieHeader( &$header, $name, $value ) {
public static function setCookieHeader(&$header, $name, $value)
{
//Build cookie header
if ( is_array($value) ) {
if (is_array($value)) {
$domain = '';
$path = '';
$expires = '';
$secure = '';
$httponly = '';
if ( isset($value['domain']) && $value['domain'] ) {
if (isset($value['domain']) && $value['domain']) {
$domain = '; domain=' . $value['domain'];
}
if ( isset($value['path']) && $value['path'] ) {
if (isset($value['path']) && $value['path']) {
$path = '; path=' . $value['path'];
}
if ( isset($value['expires']) ) {
if ( is_string($value['expires']) ) {
if (isset($value['expires'])) {
if (is_string($value['expires'])) {
$timestamp = strtotime($value['expires']);
} else {
$timestamp = (int)$value['expires'];
$timestamp = (int) $value['expires'];
}
if ( $timestamp !== 0 ) {
if ($timestamp !== 0) {
$expires = '; expires=' . gmdate('D, d-M-Y H:i:s e', $timestamp);
}
}
if ( isset($value['secure']) && $value['secure'] ) {
if (isset($value['secure']) && $value['secure']) {
$secure = '; secure';
}
if ( isset($value['httponly']) && $value['httponly'] ) {
if (isset($value['httponly']) && $value['httponly']) {
$httponly = '; HttpOnly';
}
$cookie = sprintf('%s=%s%s', urlencode($name), urlencode((string)$value['value']), $domain . $path . $expires . $secure . $httponly);
$cookie = sprintf('%s=%s%s', urlencode($name), urlencode((string) $value['value']), $domain . $path . $expires . $secure . $httponly);
} else {
$cookie = sprintf('%s=%s', urlencode($name), urlencode((string)$value));
$cookie = sprintf('%s=%s', urlencode($name), urlencode((string) $value));
}
//Set cookie header
if ( !isset($header['Set-Cookie']) || $header['Set-Cookie'] === '' ) {
if (!isset($header['Set-Cookie']) || $header['Set-Cookie'] === '') {
$header['Set-Cookie'] = $cookie;
} else {
$header['Set-Cookie'] = implode("\n", array($header['Set-Cookie'], $cookie));
@ -298,29 +308,29 @@ class Slim_Http_Util {
* first argument; this method directly modifies this object instead of
* returning a value.
*
* @param array $header
* @param string $name
* @param string $value
* @return void
* @param array $header
* @param string $name
* @param string $value
*/
public static function deleteCookieHeader( &$header, $name, $value = array() ) {
public static function deleteCookieHeader(&$header, $name, $value = array())
{
//Remove affected cookies from current response header
$cookiesOld = array();
$cookiesNew = array();
if ( isset($header['Set-Cookie']) ) {
if (isset($header['Set-Cookie'])) {
$cookiesOld = explode("\n", $header['Set-Cookie']);
}
foreach ( $cookiesOld as $c ) {
if ( isset($value['domain']) && $value['domain'] ) {
foreach ($cookiesOld as $c) {
if (isset($value['domain']) && $value['domain']) {
$regex = sprintf('@%s=.*domain=%s@', urlencode($name), preg_quote($value['domain']));
} else {
$regex = sprintf('@%s=@', urlencode($name));
}
if ( preg_match($regex, $c) === 0 ) {
if (preg_match($regex, $c) === 0) {
$cookiesNew[] = $c;
}
}
if ( $cookiesNew ) {
if ($cookiesNew) {
$header['Set-Cookie'] = implode("\n", $cookiesNew);
} else {
unset($header['Set-Cookie']);
@ -336,29 +346,44 @@ class Slim_Http_Util {
* This method will parse the HTTP requst's `Cookie` header
* and extract cookies into an associative array.
*
* @param string
* @return array
* @param string
* @return array
*/
public static function parseCookieHeader( $header ) {
public static function parseCookieHeader($header)
{
$cookies = array();
$header = rtrim($header, "\r\n");
$headerPieces = preg_split('@\s*;\s*@', $header);
foreach ( $headerPieces as $c ) {
$headerPieces = preg_split('@\s*[;,]\s*@', $header);
foreach ($headerPieces as $c) {
$cParts = explode('=', $c);
if ( count($cParts) === 2 ) {
if (count($cParts) === 2) {
$key = urldecode($cParts[0]);
$value = urldecode($cParts[1]);
if ( isset($cookies[$key]) ) {
if ( is_array($cookies[$key]) ) {
$cookies[$key][] = $value;
} else {
$cookies[$key] = array($cookies[$key], $value);
}
} else {
if (!isset($cookies[$key])) {
$cookies[$key] = $value;
}
}
}
return $cookies;
}
}
/**
* Generate a random IV
*
* This method will generate a non-predictable IV for use with
* the cookie encryption
*
* @param int $expires The UNIX timestamp at which this cookie will expire
* @param string $secret The secret key used to hash the cookie value
* @return binary string with length 40
*/
private static function get_iv($expires, $secret)
{
$data1 = hash_hmac('sha1', 'a'.$expires.'b', $secret);
$data2 = hash_hmac('sha1', 'z'.$expires.'y', $secret);
return pack("h*", $data1.$data2);
}
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim;
/**
* Log
@ -53,16 +54,23 @@
* @author Josh Lockhart
* @since 1.0.0
*/
class Slim_Log {
class Log
{
const FATAL = 0;
const ERROR = 1;
const WARN = 2;
const INFO = 3;
const DEBUG = 4;
/**
* @var array
*/
static protected $levels = array(
0 => 'FATAL',
1 => 'ERROR',
2 => 'WARN',
3 => 'INFO',
4 => 'DEBUG'
protected static $levels = array(
self::FATAL => 'FATAL',
self::ERROR => 'ERROR',
self::WARN => 'WARN',
self::INFO => 'INFO',
self::DEBUG => 'DEBUG'
);
/**
@ -82,30 +90,31 @@ class Slim_Log {
/**
* Constructor
* @param mixed $writer
* @return void
* @param mixed $writer
*/
public function __construct( $writer ) {
public function __construct($writer)
{
$this->writer = $writer;
$this->enabled = true;
$this->level = 4;
$this->level = self::DEBUG;
}
/**
* Is logging enabled?
* @return bool
*/
public function getEnabled() {
public function getEnabled()
{
return $this->enabled;
}
/**
* Enable or disable logging
* @param bool $enabled
* @return void
* @param bool $enabled
*/
public function setEnabled( $enabled ) {
if ( $enabled ) {
public function setEnabled($enabled)
{
if ($enabled) {
$this->enabled = true;
} else {
$this->enabled = false;
@ -114,13 +123,13 @@ class Slim_Log {
/**
* Set level
* @param int $level
* @return void
* @throws InvalidArgumentException
* @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');
public function setLevel($level)
{
if (!isset(self::$levels[$level])) {
throw new \InvalidArgumentException('Invalid log level');
}
$this->level = $level;
}
@ -129,16 +138,17 @@ class Slim_Log {
* Get level
* @return int
*/
public function getLevel() {
public function getLevel()
{
return $this->level;
}
/**
* Set writer
* @param mixed $writer
* @return void
* @param mixed $writer
*/
public function setWriter( $writer ) {
public function setWriter($writer)
{
$this->writer = $writer;
}
@ -146,7 +156,8 @@ class Slim_Log {
* Get writer
* @return mixed
*/
public function getWriter() {
public function getWriter()
{
return $this->writer;
}
@ -154,66 +165,73 @@ class Slim_Log {
* Is logging enabled?
* @return bool
*/
public function isEnabled() {
public function isEnabled()
{
return $this->enabled;
}
/**
* Log debug message
* @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled
* @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled
*/
public function debug( $object ) {
return $this->log($object, 4);
public function debug($object)
{
return $this->write($object, self::DEBUG);
}
/**
* Log info message
* @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled
* @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled
*/
public function info( $object ) {
return $this->log($object, 3);
public function info($object)
{
return $this->write($object, self::INFO);
}
/**
* Log warn message
* @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled
* @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled
*/
public function warn( $object ) {
return $this->log($object, 2);
public function warn($object)
{
return $this->write($object, self::WARN);
}
/**
* Log error message
* @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled
* @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled
*/
public function error( $object ) {
return $this->log($object, 1);
public function error($object)
{
return $this->write($object, self::ERROR);
}
/**
* Log fatal message
* @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled
* @param mixed $object
* @return mixed|false What the Logger returns, or false if Logger not set or not enabled
*/
public function fatal( $object ) {
return $this->log($object, 0);
public function fatal($object)
{
return $this->write($object, self::FATAL);
}
/**
* Log message
* @param mixed The object to log
* @param int The message level
* @return int|false
* @return int|false
*/
protected function log( $object, $level ) {
if ( $this->enabled && $this->writer && $level <= $this->level ) {
return $this->writer->write($object);
protected function write($object, $level)
{
if ($this->enabled && $this->writer && $level <= $this->level) {
return $this->writer->write($object, $level);
} else {
return false;
}
}
}
}

75
Slim/LogWriter.php Normal file
View File

@ -0,0 +1,75 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.0
* @package Slim
*
* 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.
*/
namespace Slim;
/**
* Log Writer
*
* This class is used by Slim_Log to write log messages to a valid, writable
* resource handle (e.g. a file or STDERR).
*
* @package Slim
* @author Josh Lockhart
* @since 1.6.0
*/
class LogWriter
{
/**
* @var resource
*/
protected $resource;
/**
* Constructor
* @param resource $resource
* @throws \InvalidArgumentException If invalid resource
*/
public function __construct($resource)
{
if (!is_resource($resource)) {
throw new \InvalidArgumentException('Cannot create LogWriter. Invalid resource handle.');
}
$this->resource = $resource;
}
/**
* Write message
* @param mixed $message
* @param int $level
* @return int|false
*/
public function write($message, $level = null)
{
return fwrite($this->resource, (string) $message . PHP_EOL);
}
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim;
/**
* Middleware
@ -38,9 +39,10 @@
* @author Josh Lockhart
* @since 1.6.0
*/
abstract class Slim_Middleware {
abstract class Middleware
{
/**
* @var Slim Reference to the primary Slim application instance
* @var \Slim Reference to the primary application instance
*/
protected $app;
@ -55,10 +57,10 @@ abstract class Slim_Middleware {
* This method injects the primary Slim application instance into
* this middleware.
*
* @param Slim $application
* @return void
* @param \Slim $application
*/
final public function setApplication( $application ) {
final public function setApplication($application)
{
$this->app = $application;
}
@ -68,9 +70,10 @@ abstract class Slim_Middleware {
* This method retrieves the application previously injected
* into this middleware.
*
* @return Slim
* @return \Slim
*/
final public function getApplication() {
final public function getApplication()
{
return $this->app;
}
@ -81,10 +84,10 @@ abstract class Slim_Middleware {
* this middleware so that it may optionally be called
* when appropriate.
*
* @param Slim|Slim_Middleware
* @return void
* @param \Slim|\Slim\Middleware
*/
final public function setNextMiddleware( $nextMiddleware ) {
final public function setNextMiddleware($nextMiddleware)
{
$this->next = $nextMiddleware;
}
@ -94,9 +97,10 @@ abstract class Slim_Middleware {
* This method retrieves the next downstream middleware
* previously injected into this middleware.
*
* @return Slim|Slim_Middleware
* @return \Slim|\Slim\Middleware
*/
final public function getNextMiddleware() {
final public function getNextMiddleware()
{
return $this->next;
}
@ -105,8 +109,6 @@ abstract class Slim_Middleware {
*
* Perform actions specific to this middleware and optionally
* call the next downstream middleware.
*
* @return void
*/
abstract public function call();
}
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim\Middleware;
/**
* Content Types
@ -44,7 +45,8 @@
* @author Josh Lockhart
* @since 1.6.0
*/
class Slim_Middleware_ContentTypes extends Slim_Middleware {
class ContentTypes extends \Slim\Middleware
{
/**
* @var array
*/
@ -54,7 +56,8 @@ class Slim_Middleware_ContentTypes extends Slim_Middleware {
* Constructor
* @param array $settings
*/
public function __construct( $settings = array() ) {
public function __construct($settings = array())
{
$this->contentTypes = array_merge(array(
'application/json' => array($this, 'parseJson'),
'application/xml' => array($this, 'parseXml'),
@ -65,13 +68,14 @@ class Slim_Middleware_ContentTypes extends Slim_Middleware {
/**
* Call
* @return void
*/
public function call() {
$env = $this->app->environment();
if ( isset($env['CONTENT_TYPE']) ) {
public function call()
{
$mediaType = $this->app->request()->getMediaType();
if ($mediaType) {
$env = $this->app->environment();
$env['slim.input_original'] = $env['slim.input'];
$env['slim.input'] = $this->parse($env['slim.input'], $env['CONTENT_TYPE']);
$env['slim.input'] = $this->parse($env['slim.input'], $mediaType);
}
$this->next->call();
}
@ -82,17 +86,19 @@ class Slim_Middleware_ContentTypes extends Slim_Middleware {
* This method will attempt to parse the request body
* based on its content type if available.
*
* @param string $input
* @param string $contentType
* @return mixed
* @param string $input
* @param string $contentType
* @return mixed
*/
protected function parse ( $input, $contentType ) {
if ( isset($this->contentTypes[$contentType]) && is_callable($this->contentTypes[$contentType]) ) {
protected function parse ($input, $contentType)
{
if (isset($this->contentTypes[$contentType]) && is_callable($this->contentTypes[$contentType])) {
$result = call_user_func($this->contentTypes[$contentType], $input);
if ( $result ) {
if ($result) {
return $result;
}
}
return $input;
}
@ -102,13 +108,14 @@ class Slim_Middleware_ContentTypes extends Slim_Middleware {
* This method converts the raw JSON input
* into an associative array.
*
* @param string $input
* @return array|string
* @param string $input
* @return array|string
*/
protected function parseJson( $input ) {
if ( function_exists('json_decode') ) {
protected function parseJson($input)
{
if (function_exists('json_decode')) {
$result = json_decode($input, true);
if ( $result ) {
if ($result) {
return $result;
}
}
@ -122,15 +129,19 @@ class Slim_Middleware_ContentTypes extends Slim_Middleware {
* extension is not available, the raw input
* will be returned unchanged.
*
* @param string $input
* @return SimpleXMLElement|string
* @param string $input
* @return \SimpleXMLElement|string
*/
protected function parseXml( $input ) {
if ( class_exists('SimpleXMLElement') ) {
protected function parseXml($input)
{
if (class_exists('SimpleXMLElement')) {
try {
return new SimpleXMLElement($input);
} catch ( Exception $e ) {}
return new \SimpleXMLElement($input);
} catch (\Exception $e) {
// Do nothing
}
}
return $input;
}
@ -140,18 +151,20 @@ class Slim_Middleware_ContentTypes extends Slim_Middleware {
* This method parses CSV content into a numeric array
* containing an array of data for each CSV line.
*
* @param string $input
* @return array
* @param string $input
* @return array
*/
protected function parseCsv( $input ) {
protected function parseCsv($input)
{
$temp = fopen('php://memory', 'rw');
fwrite($temp, $input);
fseek($temp, 0);
$res = array();
while ( ($data = fgetcsv($temp)) !== false ) {
while (($data = fgetcsv($temp)) !== false) {
$res[] = $data;
}
fclose($temp);
return $res;
}
}
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim\Middleware;
/**
* Flash
@ -44,7 +45,8 @@
* @author Josh Lockhart
* @since 1.6.0
*/
class Slim_Middleware_Flash extends Slim_Middleware implements ArrayAccess {
class Flash extends \Slim\Middleware implements \ArrayAccess, \IteratorAggregate
{
/**
* @var array
*/
@ -57,14 +59,14 @@ class Slim_Middleware_Flash extends Slim_Middleware implements ArrayAccess {
/**
* Constructor
* @param Slim $app
* @param array $settings
* @return void
* @param \Slim $app
* @param array $settings
*/
public function __construct( $settings = array() ) {
public function __construct($settings = array())
{
$this->settings = array_merge(array('key' => 'slim.flash'), $settings);
$this->messages = array(
'prev' => isset($_SESSION[$this->settings['key']]) ? $_SESSION[$this->settings['key']] : array(), //flash messages from prev request
'prev' => array(), //flash messages from prev request (loaded when middleware called)
'next' => array(), //flash messages for next request
'now' => array() //flash messages for current request
);
@ -72,9 +74,13 @@ class Slim_Middleware_Flash extends Slim_Middleware implements ArrayAccess {
/**
* Call
* @return void
*/
public function call() {
public function call()
{
//Read flash messaging from previous request if available
$this->loadMessages();
//Prepare flash messaging for current request
$env = $this->app->environment();
$env['slim.flash'] = $this;
$this->next->call();
@ -86,12 +92,12 @@ class Slim_Middleware_Flash extends Slim_Middleware implements ArrayAccess {
*
* Specify a flash message for a given key to be shown for the current request
*
* @param string $key
* @param string $value
* @return void
* @param string $key
* @param string $value
*/
public function now( $key, $value ) {
$this->messages['now'][(string)$key] = $value;
public function now($key, $value)
{
$this->messages['now'][(string) $key] = $value;
}
/**
@ -99,23 +105,22 @@ class Slim_Middleware_Flash extends Slim_Middleware implements ArrayAccess {
*
* Specify a flash message for a given key to be shown for the next request
*
* @param string $key
* @param string $value
* @return void
* @param string $key
* @param string $value
*/
public function set( $key, $value ) {
$this->messages['next'][(string)$key] = $value;
public function set($key, $value)
{
$this->messages['next'][(string) $key] = $value;
}
/**
* Keep
*
* Retain flash messages from the previous request for the next request
*
* @return void
*/
public function keep() {
foreach ( $this->messages['prev'] as $key => $val ) {
public function keep()
{
foreach ($this->messages['prev'] as $key => $val) {
$this->messages['next'][$key] = $val;
}
}
@ -123,48 +128,75 @@ class Slim_Middleware_Flash extends Slim_Middleware implements ArrayAccess {
/**
* Save
*/
public function save() {
public function save()
{
$_SESSION[$this->settings['key']] = $this->messages['next'];
}
/**
* Get messages
*
* Load messages from previous request if available
*/
public function loadMessages()
{
if (isset($_SESSION[$this->settings['key']])) {
$this->messages['prev'] = $_SESSION[$this->settings['key']];
}
}
/**
* Return array of flash messages to be shown for the current request
*
* @return array
*/
public function getMessages() {
public function getMessages()
{
return array_merge($this->messages['prev'], $this->messages['now']);
}
/**
* Array Access: Offset Exists
*/
public function offsetExists( $offset ) {
public function offsetExists($offset)
{
$messages = $this->getMessages();
return isset($messages[$offset]);
}
/**
* Array Access: Offset Get
*/
public function offsetGet( $offset ) {
public function offsetGet($offset)
{
$messages = $this->getMessages();
return isset($messages[$offset]) ? $messages[$offset] : null;
}
/**
* Array Access: Offset Set
*/
public function offsetSet( $offset, $value ) {
public function offsetSet($offset, $value)
{
$this->now($offset, $value);
}
/**
* Array Access: Offset Unset
*/
public function offsetUnset( $offset ) {
public function offsetUnset($offset)
{
unset($this->messages['prev'][$offset], $this->messages['now'][$offset]);
}
}
/**
* Iterator Aggregate: Get Iterator
* @return \ArrayIterator
*/
public function getIterator()
{
$messages = $this->getMessages();
return new \ArrayIterator($messages);
}
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim\Middleware;
/**
* HTTP Method Override
@ -45,7 +46,8 @@
* @author Josh Lockhart
* @since 1.6.0
*/
class Slim_Middleware_MethodOverride extends Slim_Middleware {
class MethodOverride extends \Slim\Middleware
{
/**
* @var array
*/
@ -53,11 +55,11 @@ class Slim_Middleware_MethodOverride extends Slim_Middleware {
/**
* Constructor
* @param Slim $app
* @param array $settings
* @return void
* @param \Slim $app
* @param array $settings
*/
public function __construct( $settings = array() ) {
public function __construct($settings = array())
{
$this->settings = array_merge(array('key' => '_METHOD'), $settings);
}
@ -65,29 +67,30 @@ class Slim_Middleware_MethodOverride extends Slim_Middleware {
* Call
*
* Implements Slim middleware interface. This method is invoked and passed
* an array of environemnt variables. This middleware inspects the environment
* an array of environment variables. This middleware inspects the environment
* variables for the HTTP method override parameter; if found, this middleware
* modifies the environment settings so downstream middleware and/or the Slim
* application will treat the request with the desired HTTP method.
*
* @param array $env
* @return array[status, header, body]
* @param array $env
* @return array[status, header, body]
*/
public function call() {
public function call()
{
$env = $this->app->environment();
if ( isset($env['X_HTTP_METHOD_OVERRIDE']) ) {
if (isset($env['X_HTTP_METHOD_OVERRIDE'])) {
// Header commonly used by Backbone.js and others
$env['slim.method_override.original_method'] = $env['REQUEST_METHOD'];
$env['REQUEST_METHOD'] = strtoupper($env['X_HTTP_METHOD_OVERRIDE']);
} else if ( isset($env['REQUEST_METHOD']) && $env['REQUEST_METHOD'] === 'POST' ) {
} elseif (isset($env['REQUEST_METHOD']) && $env['REQUEST_METHOD'] === 'POST') {
// HTML Form Override
$req = new Slim_Http_Request($env);
$req = new \Slim\Http\Request($env);
$method = $req->post($this->settings['key']);
if ( $method ) {
if ($method) {
$env['slim.method_override.original_method'] = $env['REQUEST_METHOD'];
$env['REQUEST_METHOD'] = strtoupper($method);
}
}
$this->next->call();
}
}
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim\Middleware;
/**
* Pretty Exceptions
@ -41,7 +42,8 @@
* @author Josh Lockhart
* @since 1.0.0
*/
class Slim_Middleware_PrettyExceptions extends Slim_Middleware {
class PrettyExceptions extends \Slim\Middleware
{
/**
* @var array
*/
@ -49,23 +51,24 @@ class Slim_Middleware_PrettyExceptions extends Slim_Middleware {
/**
* Constructor
* @param Slim|middleware $app
* @param array $settings
*/
public function __construct( $settings = array() ) {
public function __construct($settings = array())
{
$this->settings = $settings;
}
/**
* Call
* @return void
*/
public function call() {
public function call()
{
try {
$this->next->call();
} catch ( Exception $e ) {
} catch (\Exception $e) {
$env = $this->app->environment();
$env['slim.log']->error($e);
$this->app->contentType('text/html');
$this->app->response()->status(500);
$this->app->response()->body($this->renderBody($env, $e));
}
@ -73,11 +76,12 @@ class Slim_Middleware_PrettyExceptions extends Slim_Middleware {
/**
* Render response body
* @param array $env
* @param Exception $exception
* @param array $env
* @param \Exception $exception
* @return string
*/
protected function renderBody( &$env, $exception ) {
protected function renderBody(&$env, $exception)
{
$title = 'Slim Application Error';
$code = $exception->getCode();
$message = $exception->getMessage();
@ -87,22 +91,24 @@ class Slim_Middleware_PrettyExceptions extends Slim_Middleware {
$html = sprintf('<h1>%s</h1>', $title);
$html .= '<p>The application could not run because of the following error:</p>';
$html .= '<h2>Details</h2>';
if ( $code ) {
$html .= sprintf('<div><strong>Type:</strong> %s</div>', get_class($exception));
if ($code) {
$html .= sprintf('<div><strong>Code:</strong> %s</div>', $code);
}
if ( $message ) {
if ($message) {
$html .= sprintf('<div><strong>Message:</strong> %s</div>', $message);
}
if ( $file ) {
if ($file) {
$html .= sprintf('<div><strong>File:</strong> %s</div>', $file);
}
if ( $line ) {
if ($line) {
$html .= sprintf('<div><strong>Line:</strong> %s</div>', $line);
}
if ( $trace ) {
if ($trace) {
$html .= '<h2>Trace</h2>';
$html .= sprintf('<pre>%s</pre>', $trace);
}
return sprintf("<html><head><title>%s</title><style>body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana,sans-serif;}h1{margin:0;font-size:48px;font-weight:normal;line-height:48px;}strong{display:inline-block;width:65px;}</style></head><body>%s</body></html>", $title, $html);
}
}
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,6 +30,7 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim\Middleware;
/**
* Session Cookie
@ -57,7 +58,8 @@
* @author Josh Lockhart
* @since 1.6.0
*/
class Slim_Middleware_SessionCookie extends Slim_Middleware {
class SessionCookie extends \Slim\Middleware
{
/**
* @var array
*/
@ -66,10 +68,10 @@ class Slim_Middleware_SessionCookie extends Slim_Middleware {
/**
* Constructor
*
* @param array $settings
* @return void
* @param array $settings
*/
public function __construct( $settings = array() ) {
public function __construct($settings = array())
{
$this->settings = array_merge(array(
'expires' => '20 minutes',
'path' => '/',
@ -81,16 +83,35 @@ class Slim_Middleware_SessionCookie extends Slim_Middleware {
'cipher' => MCRYPT_RIJNDAEL_256,
'cipher_mode' => MCRYPT_MODE_CBC
), $settings);
if ( is_string($this->settings['expires']) ) {
if (is_string($this->settings['expires'])) {
$this->settings['expires'] = strtotime($this->settings['expires']);
}
/**
* Session
*
* We must start a native PHP session to initialize the $_SESSION superglobal.
* However, we won't be using the native session store for persistence, so we
* disable the session cookie and cache limiter. We also set the session
* handler to this class instance to avoid PHP's native session file locking.
*/
ini_set('session.use_cookies', 0);
session_cache_limiter(false);
session_set_save_handler(
array($this, 'open'),
array($this, 'close'),
array($this, 'read'),
array($this, 'write'),
array($this, 'destroy'),
array($this, 'gc')
);
}
/**
* Call
* @return void
*/
public function call() {
public function call()
{
$this->loadSession();
$this->next->call();
$this->saveSession();
@ -98,17 +119,21 @@ class Slim_Middleware_SessionCookie extends Slim_Middleware {
/**
* Load session
* @param array $env
* @return void
* @param array $env
*/
protected function loadSession() {
$value = Slim_Http_Util::decodeSecureCookie(
protected function loadSession()
{
if (session_id() === '') {
session_start();
}
$value = \Slim\Http\Util::decodeSecureCookie(
$this->app->request()->cookies($this->settings['name']),
$this->settings['secret'],
$this->settings['cipher'],
$this->settings['cipher_mode']
);
if ( $value ) {
if ($value) {
$_SESSION = unserialize($value);
} else {
$_SESSION = array();
@ -117,18 +142,18 @@ class Slim_Middleware_SessionCookie extends Slim_Middleware {
/**
* Save session
* @return void
*/
protected function saveSession() {
$value = Slim_Http_Util::encodeSecureCookie(
protected function saveSession()
{
$value = \Slim\Http\Util::encodeSecureCookie(
serialize($_SESSION),
$this->settings['expires'],
$this->settings['secret'],
$this->settings['cipher'],
$this->settings['cipher_mode']
);
if ( strlen($value) > 4096 ) {
$this->app->getLog()->error('WARNING! Slim_Middleware_SessionCookie data size is larger than 4KB. Content save failed.');
if (strlen($value) > 4096) {
$this->app->getLog()->error('WARNING! Slim\Middleware\SessionCookie data size is larger than 4KB. Content save failed.');
} else {
$this->app->response()->setCookie($this->settings['name'], array(
'value' => $value,
@ -139,5 +164,40 @@ class Slim_Middleware_SessionCookie extends Slim_Middleware {
'httponly' => $this->settings['httponly']
));
}
session_destroy();
}
}
/********************************************************************************
* Session Handler
*******************************************************************************/
public function open($savePath, $sessionName)
{
return true;
}
public function close()
{
return true;
}
public function read($id)
{
return '';
}
public function write($id, $data)
{
return true;
}
public function destroy($id)
{
return true;
}
public function gc($maxlifetime)
{
return true;
}
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.0.0
* @package Slim
*
* MIT LICENSE
@ -30,21 +30,23 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim;
/**
* Route
* @package Slim
* @author Josh Lockhart
* @author Josh Lockhart, Thomas Bley
* @since 1.0.0
*/
class Slim_Route {
class Route
{
/**
* @var string The route pattern (ie. "/books/:id")
* @var string The route pattern (e.g. "/books/:id")
*/
protected $pattern;
/**
* @var mixed The callable associated with this route
* @var mixed The route callable
*/
protected $callable;
@ -54,7 +56,7 @@ class Slim_Route {
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();
@ -68,27 +70,33 @@ class Slim_Route {
*/
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
*/
protected $methods = array();
/**
* @var Slim_Router The Router to which this Route belongs
*/
protected $router;
/**
* @var array[Callable] Middleware
* @var array[Callable] Middleware to be run before only this route instance
*/
protected $middleware = array();
/**
* Constructor
* @param string $pattern The URL pattern (ie. "/books/:id")
* @param mixed $callable Anything that returns TRUE for is_callable()
* @param string $pattern The URL pattern (e.g. "/books/:id")
* @param mixed $callable Anything that returns TRUE for is_callable()
*/
public function __construct( $pattern, $callable ) {
public function __construct($pattern, $callable)
{
$this->setPattern($pattern);
$this->setCallable($callable);
$this->setConditions(self::getDefaultConditions());
@ -96,10 +104,10 @@ class Slim_Route {
/**
* Set default route conditions for all instances
* @param array $defaultConditions
* @return void
* @param array $defaultConditions
*/
public static function setDefaultConditions( array $defaultConditions ) {
public static function setDefaultConditions(array $defaultConditions)
{
self::$defaultConditions = $defaultConditions;
}
@ -107,7 +115,8 @@ class Slim_Route {
* Get default route conditions for all instances
* @return array
*/
public static function getDefaultConditions() {
public static function getDefaultConditions()
{
return self::$defaultConditions;
}
@ -115,33 +124,40 @@ class Slim_Route {
* Get route pattern
* @return string
*/
public function getPattern() {
public function getPattern()
{
return $this->pattern;
}
/**
* Set route pattern
* @param string $pattern
* @return void
* @param string $pattern
*/
public function setPattern( $pattern ) {
$this->pattern = str_replace(')', ')?', (string)$pattern);
public function setPattern($pattern)
{
$this->pattern = $pattern;
}
/**
* Get route callable
* @return mixed
*/
public function getCallable() {
public function getCallable()
{
return $this->callable;
}
/**
* Set route callable
* @param mixed $callable
* @return void
* @param mixed $callable
* @throws \InvalidArgumentException If argument is not callable
*/
public function setCallable($callable) {
public function setCallable($callable)
{
if (!is_callable($callable)) {
throw new \InvalidArgumentException('Route callable must be callable');
}
$this->callable = $callable;
}
@ -149,16 +165,17 @@ class Slim_Route {
* Get route conditions
* @return array
*/
public function getConditions() {
public function getConditions()
{
return $this->conditions;
}
/**
* Set route conditions
* @param array $conditions
* @return void
* @param array $conditions
*/
public function setConditions( array $conditions ) {
public function setConditions(array $conditions)
{
$this->conditions = $conditions;
}
@ -166,33 +183,72 @@ class Slim_Route {
* Get route name
* @return string|null
*/
public function getName() {
public function getName()
{
return $this->name;
}
/**
* Set route name
* @param string $name
* @return void
* @param string $name
*/
public function setName( $name ) {
$this->name = (string)$name;
$this->router->addNamedRoute($this->name, $this);
public function setName($name)
{
$this->name = (string) $name;
}
/**
* Get route parameters
* @return array
*/
public function getParams() {
public function getParams()
{
return $this->params;
}
/**
* Add supported HTTP method(s)
* @return void
* Set route parameters
* @param array $params
*/
public function setHttpMethods() {
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)
*/
public function setHttpMethods()
{
$args = func_get_args();
$this->methods = $args;
}
@ -201,26 +257,29 @@ class Slim_Route {
* Get supported HTTP methods
* @return array
*/
public function getHttpMethods() {
public function getHttpMethods()
{
return $this->methods;
}
/**
* Append supported HTTP methods
* @return void
*/
public function appendHttpMethods() {
public function appendHttpMethods()
{
$args = func_get_args();
$this->methods = array_merge($this->methods, $args);
}
/**
* Append supported HTTP methods (alias for Route::appendHttpMethods)
* @return Slim_Route
* @return \Slim\Route
*/
public function via() {
public function via()
{
$args = func_get_args();
$this->methods = array_merge($this->methods, $args);
return $this;
}
@ -228,32 +287,17 @@ class Slim_Route {
* Detect support for an HTTP method
* @return bool
*/
public function supportsHttpMethod( $method ) {
public function supportsHttpMethod($method)
{
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
* @return array[Callable]
*/
public function getMiddleware() {
public function getMiddleware()
{
return $this->middleware;
}
@ -264,22 +308,28 @@ class Slim_Route {
* If the method argument `is_callable` (including callable arrays!),
* we directly append the argument to `$this->middleware`. Else, we
* assume the argument is an array of callables and merge the array
* with `$this->middleware`. Even if non-callables are included in the
* argument array, we still merge them; we lazily check each item
* against `is_callable` during Route::dispatch().
* with `$this->middleware`. Each middleware is checked for is_callable()
* and an InvalidArgumentException is thrown immediately if it isn't.
*
* @param Callable|array[Callable]
* @return Slim_Route
* @throws InvalidArgumentException If argument is not callable or not an array
* @param Callable|array[Callable]
* @return \Slim\Route
* @throws \InvalidArgumentException If argument is not callable or not an array of callables.
*/
public function setMiddleware( $middleware ) {
if ( is_callable($middleware) ) {
public function setMiddleware($middleware)
{
if (is_callable($middleware)) {
$this->middleware[] = $middleware;
} else if ( is_array($middleware) ) {
} elseif (is_array($middleware)) {
foreach($middleware as $callable) {
if (!is_callable($callable)) {
throw new \InvalidArgumentException('All Route middleware must be callable');
}
}
$this->middleware = array_merge($this->middleware, $middleware);
} 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;
}
@ -291,110 +341,76 @@ class Slim_Route {
*
* http://blog.sosedoff.com/2009/09/20/rails-like-php-url-router/
*
* @param string $resourceUri A Request URI
* @return bool
* @param string $resourceUri A Request URI
* @return bool
*/
public function matches( $resourceUri ) {
//Extract URL params
preg_match_all('@:([\w]+)@', $this->pattern, $paramNames, PREG_PATTERN_ORDER);
$paramNames = $paramNames[0];
//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) === '/' ) {
$patternAsRegex = $patternAsRegex . '?';
public function matches($resourceUri)
{
//Convert URL params into regex patterns, construct a regex for this route, init params
$patternAsRegex = preg_replace_callback('#:([\w]+)\+?#', array($this, 'matchesCallback'),
str_replace(')', ')?', (string) $this->pattern));
if (substr($this->pattern, -1) === '/') {
$patternAsRegex .= '?';
}
$patternAsRegex = '@^' . $patternAsRegex . '$@';
//Cache URL params' names and values if this route matches the current HTTP request
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 {
if (!preg_match('#^' . $patternAsRegex . '$#', $resourceUri, $paramValues)) {
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 (ie. ":id") into a regular expression
* @param array URL parameters
* @return string Regular expression for URL parameter
* Convert a URL parameter (e.g. ":id", ":id+") into a regular expression
* @param array URL parameters
* @return string Regular expression for URL parameter
*/
protected function convertPatternToRegex( $matches ) {
$key = str_replace(':', '', $matches[0]);
if ( array_key_exists($key, $this->conditions) ) {
return '(?P<' . $key . '>' . $this->conditions[$key] . ')';
} else {
return '(?P<' . $key . '>[a-zA-Z0-9_\-\.\!\~\*\\\'\(\)\:\@\&\=\$\+,%]+)';
protected function matchesCallback($m)
{
$this->paramNames[] = $m[1];
if (isset($this->conditions[ $m[1] ])) {
return '(?P<' . $m[1] . '>' . $this->conditions[ $m[1] ] . ')';
}
if (substr($m[0], -1) === '+') {
$this->paramNamesPath[ $m[1] ] = 1;
return '(?P<' . $m[1] . '>.+)';
}
return '(?P<' . $m[1] . '>[^/]+)';
}
/**
* Set route name
* @param string $name The name of the route
* @return Slim_Route
* @param string $name The name of the route
* @return \Slim\Route
*/
public function name( $name ) {
public function name($name)
{
$this->setName($name);
return $this;
}
/**
* Merge route conditions
* @param array $conditions Key-value array of URL parameter conditions
* @return Slim_Route
* @param array $conditions Key-value array of URL parameter conditions
* @return \Slim\Route
*/
public function conditions( array $conditions ) {
public function conditions(array $conditions)
{
$this->conditions = array_merge($this->conditions, $conditions);
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
$req = $this->router->getRequest();
$res = $this->router->getResponse();
foreach ( $this->middleware as $mw ) {
if ( is_callable($mw) ) {
call_user_func_array($mw, array($req, $res, $this));
}
}
//Invoke callable
if ( is_callable($this->getCallable()) ) {
call_user_func_array($this->callable, array_values($this->params));
return true;
}
return false;
}
}
}

View File

@ -6,7 +6,7 @@
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 1.6.0
* @version 2.2.0
* @package Slim
*
* MIT LICENSE
@ -30,174 +30,186 @@
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Slim;
/**
* Router
*
* This class organizes Route objects and, upon request, will
* return an iterator for routes that match the HTTP request URI.
* This class organizes, iterates, and dispatches \Slim\Route objects.
*
* @package Slim
* @author Josh Lockhart
* @since 1.0.0
*/
class Slim_Router implements IteratorAggregate {
class Router
{
/**
* @var Slim_Http_Request
* @var Route The current route (most recently dispatched)
*/
protected $request;
protected $currentRoute;
/**
* @var Slim_Http_Response
*/
protected $response;
/**
* @var array Lookup hash of all routes
* @var array Lookup hash of all route objects
*/
protected $routes;
/**
* @var array Lookup hash of named routes, keyed by route name
* @var array Lookup hash of named route objects, keyed by route name (lazy-loaded)
*/
protected $namedRoutes;
/**
* @var array Array of routes that match the Request URI (lazy-loaded)
* @var array Array of route objects that match the request URI (lazy-loaded)
*/
protected $matchedRoutes;
/**
* @var mixed Callable to be invoked if no matching routes are found
*/
protected $notFound;
/**
* @var mixed Callable to be invoked if application error
*/
protected $error;
/**
* Constructor
* @param Slim_Http_Request $request The HTTP request object
* @param Slim_Http_Response $response The HTTP response object
*/
public function __construct( Slim_Http_Request $request, Slim_Http_Response $response ) {
$this->request = $request;
$this->response = $response;
public function __construct()
{
$this->routes = array();
$this->namedRoutes = array();
}
/**
* Get Iterator
* @return ArrayIterator
* Get Current Route object or the first matched one if matching has been performed
* @return \Slim\Route|null
*/
public function getIterator() {
return new ArrayIterator($this->getMatchedRoutes());
public function getCurrentRoute()
{
if ($this->currentRoute !== null) {
return $this->currentRoute;
}
if (is_array($this->matchedRoutes) && count($this->matchedRoutes) > 0) {
return $this->matchedRoutes[0];
}
return null;
}
/**
* Get Request
* @return Slim_Http_Request
* Return route objects that match the given HTTP method and URI
* @param string $httpMethod The HTTP method to match against
* @param string $resourceUri The resource URI to match against
* @param bool $reload Should matching routes be re-parsed?
* @return array[\Slim\Route]
*/
public function getRequest() {
return $this->request;
}
/**
* Get Response
* @return Slim_Http_Response
*/
public function getResponse() {
return $this->response;
}
/**
* Return routes that match the current request
* @return array[Slim_Route]
*/
public function getMatchedRoutes( $reload = false ) {
if ( $reload || is_null($this->matchedRoutes) ) {
public function getMatchedRoutes($httpMethod, $resourceUri, $reload = false)
{
if ($reload || is_null($this->matchedRoutes)) {
$this->matchedRoutes = array();
foreach ( $this->routes as $route ) {
if ( $route->matches($this->request->getResourceUri()) ) {
foreach ($this->routes as $route) {
if (!$route->supportsHttpMethod($httpMethod)) {
continue;
}
if ($route->matches($resourceUri)) {
$this->matchedRoutes[] = $route;
}
}
}
return $this->matchedRoutes;
}
/**
* Map a route to a callback function
* @param string $pattern The URL pattern (ie. "/books/:id")
* @param mixed $callable Anything that returns TRUE for is_callable()
* @return Slim_Route
* Map a route object to a callback function
* @param string $pattern The URL pattern (ie. "/books/:id")
* @param mixed $callable Anything that returns TRUE for is_callable()
* @return \Slim\Route
*/
public function map( $pattern, $callable ) {
$route = new Slim_Route($pattern, $callable);
$route->setRouter($this);
public function map($pattern, $callable)
{
$route = new \Slim\Route($pattern, $callable);
$this->routes[] = $route;
return $route;
}
/**
* Get URL for named route
* @param string $name The name of the route
* @param array Associative array of URL parameter names and values
* @throws RuntimeException If named route not found
* @return string The URL for the given route populated with the given parameters
* @param string $name The name of the route
* @param array Associative array of URL parameter names and replacement values
* @throws RuntimeException If named route not found
* @return string The URL for the given route populated with provided replacement values
*/
public function urlFor( $name, $params = array() ) {
if ( !$this->hasNamedRoute($name) ) {
throw new RuntimeException('Named route not found for name: ' . $name);
public function urlFor($name, $params = array())
{
if (!$this->hasNamedRoute($name)) {
throw new \RuntimeException('Named route not found for name: ' . $name);
}
$pattern = $this->getNamedRoute($name)->getPattern();
$search = $replace = array();
foreach ( $params as $key => $value ) {
$search[] = ':' . $key;
$replace[] = $value;
$search = array();
foreach (array_keys($params) as $key) {
$search[] = '#:' . $key . '\+?(?!\w)#';
}
$pattern = str_replace($search, $replace, $pattern);
$pattern = preg_replace($search, $params, $this->getNamedRoute($name)->getPattern());
//Remove remnants of unpopulated, trailing optional pattern segments
return preg_replace(array(
'@\(\/?:.+\/??\)\??@',
'@\?|\(|\)@'
), '', $this->request->getRootUri() . $pattern);
return preg_replace('#\(/?:.+\)|\(|\)#', '', $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.
*
* @param \Slim\Route $route The route object
* @return bool Was route callable invoked successfully?
*/
public function dispatch(\Slim\Route $route)
{
$this->currentRoute = $route;
//Invoke middleware
foreach ($route->getMiddleware() as $mw) {
call_user_func_array($mw, array($route));
}
//Invoke callable
call_user_func_array($route->getCallable(), array_values($route->getParams()));
return true;
}
/**
* 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
* @return void
* @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);
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;
$this->namedRoutes[(string) $name] = $route;
}
/**
* Has named route
* @param string $name The route name
* @return bool
* @param string $name The route name
* @return bool
*/
public function hasNamedRoute( $name ) {
return isset($this->namedRoutes[(string)$name]);
public function hasNamedRoute($name)
{
$this->getNamedRoutes();
return isset($this->namedRoutes[(string) $name]);
}
/**
* Get named route
* @param string $name
* @return Slim_Route|null
* @param string $name
* @return \Slim\Route|null
*/
public function getNamedRoute( $name ) {
if ( $this->hasNamedRoute($name) ) {
return $this->namedRoutes[(string)$name];
public function getNamedRoute($name)
{
$this->getNamedRoutes();
if ($this->hasNamedRoute($name)) {
return $this->namedRoutes[(string) $name];
} else {
return null;
}
@ -205,33 +217,19 @@ class Slim_Router implements IteratorAggregate {
/**
* Get named routes
* @return ArrayIterator
* @return \ArrayIterator
*/
public function getNamedRoutes() {
return new ArrayIterator($this->namedRoutes);
}
/**
* Register a 404 Not Found callback
* @param mixed $callable Anything that returns TRUE for is_callable()
* @return mixed
*/
public function notFound( $callable = null ) {
if ( is_callable($callable) ) {
$this->notFound = $callable;
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 $this->notFound;
}
/**
* Register a 500 Error callback
* @param mixed $callable Anything that returns TRUE for is_callable()
* @return mixed
*/
public function error( $callable = null ) {
if ( is_callable($callable) ) {
$this->error = $callable;
}
return $this->error;
return new \ArrayIterator($this->namedRoutes);
}
}
}

File diff suppressed because it is too large Load Diff

View File

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

21
composer.json Normal file
View File

@ -0,0 +1,21 @@
{
"name": "slim/slim",
"type": "library",
"description": "Slim Framework, a PHP micro framework",
"keywords": ["microframework","rest","router"],
"homepage": "http://github.com/codeguy/Slim",
"license": "MIT",
"authors": [
{
"name": "Josh Lockhart",
"email": "info@joshlockhart.com",
"homepage": "http://www.joshlockhart.com/"
}
],
"require": {
"php": ">=5.3.0"
},
"autoload": {
"psr-0": { "Slim": "." }
}
}

View File

@ -1,13 +1,30 @@
<?php
/**
* Step 1: Require the Slim Framework
*
* If you are not using Composer, you need to require the
* Slim Framework and register its PSR-0 autoloader.
*
* If you are using Composer, you can skip this step.
*/
require 'Slim/Slim.php';
require 'Slim/View.php';
\Slim\Slim::registerAutoloader();
require 'lib/GopherGetter.php';
require_once 'lib/meekrodb.2.0.class.php';
require_once 'lib/meekrodb.2.1.class.php';
require_once 'config.php';
$app = new Slim();
/**
* Step 2: Instantiate a Slim application
*
* This example instantiates a Slim application using
* its default settings. However, you will usually configure
* your Slim application now by passing an associative array
* of setting names and values into the application constructor.
*/
$app = new \Slim\Slim();
//
// default route
@ -45,8 +62,6 @@ $app->notFound(function () use ($app) {
$app->render('home.html', array("class" => "hide"));
});
/**
* handle requests for a gopher page
*/

View File

@ -1,6 +1,6 @@
<?php
/*
Copyright (C) 2008-2011 Sergey Tsalkov (stsalkov@gmail.com)
Copyright (C) 2008-2012 Sergey Tsalkov (stsalkov@gmail.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@ -29,11 +29,13 @@ class DB {
// configure workings
public static $queryMode = 'queryAllRows';
public static $param_char = '%';
public static $named_param_seperator = '_';
public static $success_handler = false;
public static $error_handler = true;
public static $throw_exception_on_error = false;
public static $nonsql_error_handler = null;
public static $throw_exception_on_nonsql_error = false;
public static $nested_transactions = false;
// internal
protected static $mdb = null;
@ -47,15 +49,18 @@ class DB {
if ($mdb->queryMode !== DB::$queryMode) $mdb->queryMode = DB::$queryMode;
if ($mdb->param_char !== DB::$param_char) $mdb->param_char = DB::$param_char;
if ($mdb->named_param_seperator !== DB::$named_param_seperator) $mdb->named_param_seperator = DB::$named_param_seperator;
if ($mdb->success_handler !== DB::$success_handler) $mdb->success_handler = DB::$success_handler;
if ($mdb->error_handler !== DB::$error_handler) $mdb->error_handler = DB::$error_handler;
if ($mdb->throw_exception_on_error !== DB::$throw_exception_on_error) $mdb->throw_exception_on_error = DB::$throw_exception_on_error;
if ($mdb->nonsql_error_handler !== DB::$nonsql_error_handler) $mdb->nonsql_error_handler = DB::$nonsql_error_handler;
if ($mdb->throw_exception_on_nonsql_error !== DB::$throw_exception_on_nonsql_error) $mdb->throw_exception_on_nonsql_error = DB::$throw_exception_on_nonsql_error;
if ($mdb->nested_transactions !== DB::$nested_transactions) $mdb->nested_transactions = DB::$nested_transactions;
return $mdb;
}
public static function get() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'get'), $args); }
public static function query() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'query'), $args); }
public static function quickPrepare() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'quickPrepare'), $args); }
public static function queryFirstRow() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryFirstRow'), $args); }
@ -92,6 +97,10 @@ class DB {
public static function sqlEval() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'sqlEval'), $args); }
public static function nonSQLError() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'nonSQLError'), $args); }
public static function serverVersion() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'serverVersion'), $args); }
public static function transactionDepth() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'transactionDepth'), $args); }
public static function debugMode($handler = true) {
DB::$success_handler = $handler;
}
@ -111,14 +120,17 @@ class MeekroDB {
// configure workings
public $queryMode = 'queryAllRows';
public $param_char = '%';
public $named_param_seperator = '_';
public $success_handler = false;
public $error_handler = true;
public $throw_exception_on_error = false;
public $nonsql_error_handler = null;
public $throw_exception_on_nonsql_error = false;
public $nested_transactions = false;
// internal
public $internal_mysql = null;
public $server_info = null;
public $insert_id = 0;
public $num_rows = 0;
public $affected_rows = 0;
@ -126,6 +138,7 @@ class MeekroDB {
public $queryResultType = null;
public $old_db = null;
public $current_db = null;
public $nested_transactions_count = 0;
public function __construct($host=null, $user=null, $password=null, $dbName=null, $port=null, $encoding=null) {
@ -158,6 +171,7 @@ class MeekroDB {
$mysql->set_charset($this->encoding);
$this->internal_mysql = $mysql;
$this->server_info = $mysql->server_info;
}
return $mysql;
@ -181,6 +195,8 @@ class MeekroDB {
$this->success_handler = $handler;
}
public function serverVersion() { return $this->server_info; }
public function transactionDepth() { return $this->nested_transactions_count; }
public function insertId() { return $this->insert_id; }
public function affectedRows() { return $this->affected_rows; }
public function count() { $args = func_get_args(); return call_user_func_array(array($this, 'numRows'), $args); }
@ -196,15 +212,55 @@ class MeekroDB {
public function startTransaction() {
$this->queryNull('START TRANSACTION');
if ($this->nested_transactions && $this->serverVersion() < '5.5') {
return $this->nonSQLError("Nested transactions are only available on MySQL 5.5 and greater. You are using MySQL " . $this->serverVersion());
}
if (!$this->nested_transactions || $this->nested_transactions_count == 0) {
$this->queryNull('START TRANSACTION');
$this->nested_transactions_count = 1;
} else {
$this->queryNull("SAVEPOINT LEVEL{$this->nested_transactions_count}");
$this->nested_transactions_count++;
}
return $this->nested_transactions_count;
}
public function commit() {
$this->queryNull('COMMIT');
public function commit($all=false) {
if ($this->nested_transactions && $this->serverVersion() < '5.5') {
return $this->nonSQLError("Nested transactions are only available on MySQL 5.5 and greater. You are using MySQL " . $this->serverVersion());
}
if ($this->nested_transactions && $this->nested_transactions_count > 0)
$this->nested_transactions_count--;
if (!$this->nested_transactions || $all || $this->nested_transactions_count == 0) {
$this->nested_transactions_count = 0;
$this->queryNull('COMMIT');
} else {
$this->queryNull("RELEASE SAVEPOINT LEVEL{$this->nested_transactions_count}");
}
return $this->nested_transactions_count;
}
public function rollback() {
$this->queryNull('ROLLBACK');
public function rollback($all=false) {
if ($this->nested_transactions && $this->serverVersion() < '5.5') {
return $this->nonSQLError("Nested transactions are only available on MySQL 5.5 and greater. You are using MySQL " . $this->serverVersion());
}
if ($this->nested_transactions && $this->nested_transactions_count > 0)
$this->nested_transactions_count--;
if (!$this->nested_transactions || $all || $this->nested_transactions_count == 0) {
$this->nested_transactions_count = 0;
$this->queryNull('ROLLBACK');
} else {
$this->queryNull("ROLLBACK TO SAVEPOINT LEVEL{$this->nested_transactions_count}");
}
return $this->nested_transactions_count;
}
public function escape($str) {
@ -299,7 +355,7 @@ class MeekroDB {
$insert_values = array();
foreach ($keys as $key) {
if ($many && !isset($data[$key])) $this->nonSQLError('insert/replace many: each assoc array must have the same keys!');
if ($many && !array_key_exists($key, $data)) $this->nonSQLError('insert/replace many: each assoc array must have the same keys!');
$datum = $data[$key];
$datum = $this->sanitize($datum);
$insert_values[] = $datum;
@ -409,6 +465,8 @@ class MeekroDB {
$posList = array();
$pos_adj = 0;
$param_char_length = strlen($this->param_char);
$named_seperator_length = strlen($this->named_param_seperator);
$types = array(
$this->param_char . 'll', // list of literals
$this->param_char . 'ls', // list of strings
@ -438,12 +496,25 @@ class MeekroDB {
$type = substr($type, $param_char_length);
$length_type = strlen($type) + $param_char_length;
if ($arg_number_length = strspn($sql, '0123456789', $pos + $pos_adj + $length_type)) {
$arg_number = substr($sql, $pos + $pos_adj + $length_type, $arg_number_length);
$new_pos = $pos + $pos_adj;
$new_pos_back = $new_pos + $length_type;
if ($arg_number_length = strspn($sql, '0123456789', $new_pos_back)) {
$arg_number = substr($sql, $new_pos_back, $arg_number_length);
if (! isset($args_all[$arg_number])) $this->nonSQLError("Non existent argument reference (arg $arg_number): $sql");
$arg = $args_all[$arg_number];
} else if (substr($sql, $new_pos_back, $named_seperator_length) == $this->named_param_seperator) {
$arg_number_length = strspn($sql, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_',
$new_pos_back + $named_seperator_length) + $named_seperator_length;
$arg_number = substr($sql, $new_pos_back + $named_seperator_length, $arg_number_length - $named_seperator_length);
if (count($args_all) != 1) $this->nonSQLError("If you use named parameters, the second argument must be an array of parameters");
if (! isset($args_all[0][$arg_number])) $this->nonSQLError("Non existent argument reference (arg $arg_number): $sql");
$arg = $args_all[0][$arg_number];
} else {
$arg_number = 0;
$arg = array_shift($args);
@ -463,7 +534,7 @@ class MeekroDB {
if ($type == 'ls') $result = $this->wrapStr($arg, "'", true);
else if ($type == 'li') $result = array_map('intval', $arg);
else if ($type == 'ld') $result = array_map('floatval', $arg);
else if ($type == 'lb') $result = array_map('$this->formatTableName', $arg);
else if ($type == 'lb') $result = array_map(array($this, 'formatTableName'), $arg);
else if ($type == 'll') $result = $arg;
else if (! $result) $this->nonSQLError("Badly formatted SQL query: $sql");
@ -472,7 +543,7 @@ class MeekroDB {
else $result = '(' . implode(',', $result) . ')';
}
$sql = substr_replace($sql, $result, $pos + $pos_adj, $length_type + $arg_number_length);
$sql = substr_replace($sql, $result, $new_pos, $length_type + $arg_number_length);
$pos_adj += strlen($result) - ($length_type + $arg_number_length);
}
return $sql;
@ -542,7 +613,8 @@ class MeekroDB {
call_user_func($success_handler, array(
'query' => $sql,
'runtime' => $runtime
'runtime' => $runtime,
'affected' => $db->affected_rows
));
}
@ -770,6 +842,8 @@ class WhereClause {
if ($this->negate) $A = '(NOT ' . $A . ')';
return $A;
}
function __toString() { return $this->text(); }
}
class DBTransaction {
@ -800,6 +874,56 @@ class MeekroDBException extends Exception {
public function getQuery() { return $this->query; }
}
class DBHelper {
/*
verticalSlice
1. For an array of assoc rays, return an array of values for a particular key
2. if $keyfield is given, same as above but use that hash key as the key in new array
*/
public static function verticalSlice($array, $field, $keyfield = null) {
$array = (array) $array;
$R = array();
foreach ($array as $obj) {
if (! array_key_exists($field, $obj)) die("verticalSlice: array doesn't have requested field\n");
if ($keyfield) {
if (! array_key_exists($keyfield, $obj)) die("verticalSlice: array doesn't have requested field\n");
$R[$obj[$keyfield]] = $obj[$field];
} else {
$R[] = $obj[$field];
}
}
return $R;
}
/*
reIndex
For an array of assoc rays, return a new array of assoc rays using a certain field for keys
*/
public static function reIndex() {
$fields = func_get_args();
$array = array_shift($fields);
$array = (array) $array;
$R = array();
foreach ($array as $obj) {
$target =& $R;
foreach ($fields as $field) {
if (! array_key_exists($field, $obj)) die("reIndex: array doesn't have requested field\n");
$nextkey = $obj[$field];
$target =& $target[$nextkey];
}
$target = $obj;
}
return $R;
}
}
function meekrodb_error_handler($params) {
if (isset($params['query'])) $out[] = "QUERY: " . $params['query'];
if (isset($params['error'])) $out[] = "ERROR: " . $params['error'];
@ -811,8 +935,6 @@ function meekrodb_error_handler($params) {
echo implode("<br>\n", $out);
}
debug_print_backtrace();
die;
}

25
phpunit.xml.dist Normal file
View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="tests/bootstrap.php"
>
<testsuites>
<testsuite name="Slim Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./Slim/</directory>
</whitelist>
</filter>
</phpunit>

358
tests/EnvironmentTest.php Normal file
View File

@ -0,0 +1,358 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class EnvironmentTest extends PHPUnit_Framework_TestCase
{
/**
* Default server settings assume the Slim app is installed
* in a subdirectory `foo/` directly beneath the public document
* root directory; URL rewrite is disabled; requested app
* resource is GET `/bar/xyz` with three query params.
*
* These only provide a common baseline for the following
* tests; tests are free to override these values.
*/
public function setUp()
{
$_SERVER['SERVER_NAME'] = 'slim';
$_SERVER['SERVER_PORT'] = '80';
$_SERVER['SCRIPT_NAME'] = '/foo/index.php';
$_SERVER['REQUEST_URI'] = '/foo/index.php/bar/xyz';
$_SERVER['PATH_INFO'] = '/bar/xyz';
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['QUERY_STRING'] = 'one=1&two=2&three=3';
$_SERVER['HTTPS'] = '';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
unset($_SERVER['CONTENT_TYPE'], $_SERVER['CONTENT_LENGTH']);
}
/**
* Test mock environment
*
* This should return the custom values where specified
* and the default values otherwise.
*/
public function testMockEnvironment()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'PUT'
));
$env2 = \Slim\Environment::getInstance();
$this->assertSame($env, $env2);
$this->assertInstanceOf('\Slim\Environment', $env);
$this->assertEquals('PUT', $env['REQUEST_METHOD']);
$this->assertEquals(80, $env['SERVER_PORT']);
$this->assertNull($env['foo']);
}
/**
* Test sets HTTP method
*/
public function testSetsHttpMethod()
{
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('GET', $env['REQUEST_METHOD']);
}
/**
* Test parses script name and path info
*
* Pre-conditions:
* URL Rewrite is disabled;
* App installed in subdirectory;
*/
public function testParsesPathsWithoutUrlRewriteInSubdirectory()
{
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('/bar/xyz', $env['PATH_INFO']);
$this->assertEquals('/foo/index.php', $env['SCRIPT_NAME']);
}
/**
* Test parses script name and path info
*
* Pre-conditions:
* URL Rewrite is disabled;
* App installed in root directory;
*/
public function testParsesPathsWithoutUrlRewriteInRootDirectory()
{
$_SERVER['REQUEST_URI'] = '/index.php/bar/xyz';
$_SERVER['SCRIPT_NAME'] = '/index.php';
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('/bar/xyz', $env['PATH_INFO']);
$this->assertEquals('/index.php', $env['SCRIPT_NAME']);
}
/**
* Test parses script name and path info
*
* Pre-conditions:
* URL Rewrite disabled;
* App installed in root directory;
* Requested resource is "/";
*/
public function testParsesPathsWithoutUrlRewriteInRootDirectoryForAppRootUri()
{
$_SERVER['REQUEST_URI'] = '/index.php';
$_SERVER['SCRIPT_NAME'] = '/index.php';
unset($_SERVER['PATH_INFO']);
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('/', $env['PATH_INFO']);
$this->assertEquals('/index.php', $env['SCRIPT_NAME']);
}
/**
* Test parses script name and path info
*
* Pre-conditions:
* URL Rewrite enabled;
* App installed in subdirectory;
*/
public function testParsesPathsWithUrlRewriteInSubdirectory()
{
$_SERVER['SCRIPT_NAME'] = '/foo/index.php';
$_SERVER['REQUEST_URI'] = '/foo/bar/xyz';
unset($_SERVER['PATH_INFO']);
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('/bar/xyz', $env['PATH_INFO']);
$this->assertEquals('/foo', $env['SCRIPT_NAME']);
}
/**
* Test parses script name and path info
*
* Pre-conditions:
* URL Rewrite enabled;
* App installed in root directory;
*/
public function testParsesPathsWithUrlRewriteInRootDirectory()
{
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['REQUEST_URI'] = '/bar/xyz';
unset($_SERVER['PATH_INFO']);
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('/bar/xyz', $env['PATH_INFO']);
$this->assertEquals('', $env['SCRIPT_NAME']);
}
/**
* Test parses script name and path info
*
* Pre-conditions:
* URL Rewrite enabled;
* App installed in root directory;
* Requested resource is "/"
*/
public function testParsesPathsWithUrlRewriteInRootDirectoryForAppRootUri()
{
$_SERVER['REQUEST_URI'] = '/';
$_SERVER['SCRIPT_NAME'] = '/index.php';
unset($_SERVER['PATH_INFO']);
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('/', $env['PATH_INFO']);
$this->assertEquals('', $env['SCRIPT_NAME']);
}
/**
* Test parses query string
*
* Pre-conditions:
* $_SERVER['QUERY_STRING'] exists and is not empty;
*/
public function testParsesQueryString()
{
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('one=1&two=2&three=3', $env['QUERY_STRING']);
}
/**
* Test removes query string from PATH_INFO when using URL Rewrite
*
* Pre-conditions:
* $_SERVER['QUERY_STRING'] exists and is not empty;
* URL Rewrite enabled;
*/
public function testRemovesQueryStringFromPathInfo()
{
$_SERVER['SCRIPT_NAME'] = '/foo/index.php';
$_SERVER['REQUEST_URI'] = '/foo/bar/xyz?one=1&two=2&three=3';
unset($_SERVER['PATH_INFO']);
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('/bar/xyz', $env['PATH_INFO']);
}
/**
* Test environment's PATH_INFO retains URL encoded characters (e.g. #)
*
* In earlier version, \Slim\Environment would use PATH_INFO instead
* of REQUEST_URI to determine the root URI and resource URI.
* Unfortunately, the server would URL decode the PATH_INFO string
* before it was handed to PHP. This prevented certain URL-encoded
* characters like the octothorpe from being delivered correctly to
* the Slim application environment. This test ensures the
* REQUEST_URI is used instead and parsed as expected.
*/
public function testPathInfoRetainsUrlEncodedCharacters()
{
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['REQUEST_URI'] = '/foo/%23bar'; //<-- URL-encoded "#bar"
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('/foo/%23bar', $env['PATH_INFO']);
}
/**
* Test parses query string
*
* Pre-conditions:
* $_SERVER['QUERY_STRING'] does not exist;
*/
public function testParsesQueryStringThatDoesNotExist()
{
unset($_SERVER['QUERY_STRING']);
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('', $env['QUERY_STRING']);
}
/**
* Test SERVER_NAME is not empty
*/
public function testServerNameIsNotEmpty()
{
$env = \Slim\Environment::getInstance(true);
$this->assertFalse(empty($env['SERVER_NAME']));
}
/**
* Test SERVER_PORT is not empty
*/
public function testServerPortIsNotEmpty()
{
$env = \Slim\Environment::getInstance(true);
$this->assertFalse(empty($env['SERVER_PORT']));
}
/**
* Test unsets HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH
*
* Pre-conditions:
* HTTP_CONTENT_TYPE is sent with HTTP request;
* HTTP_CONTENT_LENGTH is sent with HTTP request;
*/
public function testUnsetsContentTypeAndContentLength()
{
$_SERVER['HTTP_CONTENT_TYPE'] = 'text/csv';
$_SERVER['HTTP_CONTENT_LENGTH'] = 150;
$env = \Slim\Environment::getInstance(true);
$this->assertFalse(isset($env['HTTP_CONTENT_TYPE']));
$this->assertFalse(isset($env['HTTP_CONTENT_LENGTH']));
}
/**
* Test sets special request headers if not empty
*
* Pre-conditions:
* CONTENT_TYPE, CONTENT_LENGTH, X_REQUESTED_WITH are sent in client HTTP request;
* CONTENT_TYPE, CONTENT_LENGTH, X_REQUESTED_WITH are not empty;
*/
public function testSetsSpecialHeaders()
{
$_SERVER['CONTENT_TYPE'] = 'text/csv';
$_SERVER['CONTENT_LENGTH'] = '100';
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XmlHttpRequest';
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('text/csv', $env['CONTENT_TYPE']);
$this->assertEquals('100', $env['CONTENT_LENGTH']);
$this->assertEquals('XmlHttpRequest', $env['X_REQUESTED_WITH']);
}
/**
* Test detects HTTPS
*
* Pre-conditions:
* $_SERVER['HTTPS'] is set to a non-empty value;
*/
public function testHttps()
{
$_SERVER['HTTPS'] = 1;
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('https', $env['slim.url_scheme']);
}
/**
* Test detects not HTTPS
*
* Pre-conditions:
* $_SERVER['HTTPS'] is set to an empty value;
*/
public function testNotHttps()
{
$_SERVER['HTTPS'] = '';
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('http', $env['slim.url_scheme']);
}
/**
* Test detects not HTTPS on IIS
*
* Pre-conditions:
* $_SERVER['HTTPS'] is set to "off";
*/
public function testNotHttpsIIS()
{
$_SERVER['HTTPS'] = 'off';
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('http', $env['slim.url_scheme']);
}
/**
* Test input is an empty string (and not false)
*
* Pre-conditions:
* Input at php://input may only be read once; subsequent attempts
* will return `false`; in which case, use an empty string.
*/
public function testInputIsEmptyString()
{
$env = \Slim\Environment::getInstance(true);
$this->assertEquals('', $env['slim.input']);
}
/**
* Test valid resource handle to php://stdErr
*/
public function testErrorResource()
{
$env = \Slim\Environment::getInstance(true);
$this->assertTrue(is_resource($env['slim.errors']));
}
}

7
tests/Foo.php Normal file
View File

@ -0,0 +1,7 @@
<?php
class Foo
{
public function __construct()
{
}
}

143
tests/Http/HeadersTest.php Normal file
View File

@ -0,0 +1,143 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class HeadersTest extends PHPUnit_Framework_TestCase
{
/**
* Test constructor without args
*/
public function testConstructorWithoutArgs()
{
$h = new \Slim\Http\Headers();
$this->assertEquals(0, count($h));
}
/**
* Test constructor with args
*/
public function testConstructorWithArgs()
{
$h = new \Slim\Http\Headers(array('Content-Type' => 'text/html'));
$this->assertEquals(1, count($h));
}
/**
* Test get and set header
*/
public function testSetAndGetHeader()
{
$h = new \Slim\Http\Headers();
$h['Content-Type'] = 'text/html';
$this->assertEquals('text/html', $h['Content-Type']);
$this->assertEquals('text/html', $h['Content-type']);
$this->assertEquals('text/html', $h['content-type']);
}
/**
* Test get non-existent header
*/
public function testGetNonExistentHeader()
{
$h = new \Slim\Http\Headers();
$this->assertNull($h['foo']);
}
/**
* Test isset header
*/
public function testHeaderIsSet()
{
$h = new \Slim\Http\Headers();
$h['Content-Type'] = 'text/html';
$this->assertTrue(isset($h['Content-Type']));
$this->assertTrue(isset($h['Content-type']));
$this->assertTrue(isset($h['content-type']));
$this->assertFalse(isset($h['foo']));
}
/**
* Test unset header
*/
public function testUnsetHeader()
{
$h = new \Slim\Http\Headers();
$h['Content-Type'] = 'text/html';
$this->assertEquals(1, count($h));
unset($h['Content-Type']);
$this->assertEquals(0, count($h));
}
/**
* Test merge headers
*/
public function testMergeHeaders()
{
$h = new \Slim\Http\Headers();
$h['Content-Type'] = 'text/html';
$this->assertEquals(1, count($h));
$h->merge(array('Content-type' => 'text/csv', 'content-length' => 10));
$this->assertEquals(2, count($h));
$this->assertEquals('text/csv', $h['content-type']);
$this->assertEquals(10, $h['Content-length']);
}
/**
* Test iteration
*/
public function testIteration()
{
$h = new \Slim\Http\Headers();
$h['One'] = 'Foo';
$h['Two'] = 'Bar';
$output = '';
foreach ($h as $key => $value) {
$output .= $key . $value;
}
$this->assertEquals('OneFooTwoBar', $output);
}
/**
* Test outputs header name in original form, not canonical form
*/
public function testOutputsOriginalNotCanonicalName()
{
$h = new \Slim\Http\Headers();
$h['X-Powered-By'] = 'Slim';
$h['Content-Type'] = 'text/csv';
$keys = array();
foreach ($h as $name => $value) {
$keys[] = $name;
}
$this->assertContains('X-Powered-By', $keys);
$this->assertContains('Content-Type', $keys);
}
}

933
tests/Http/RequestTest.php Normal file
View File

@ -0,0 +1,933 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class RequestTest extends PHPUnit_Framework_TestCase
{
/**
* Test sets HTTP method
*/
public function testGetMethod()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('GET', $req->getMethod());
}
/**
* Test HTTP GET method detection
*/
public function testIsGet()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET'
));
$req = new \Slim\Http\Request($env);
$this->assertTrue($req->isGet());
$this->assertFalse($req->isPost());
$this->assertFalse($req->isPut());
$this->assertFalse($req->isDelete());
$this->assertFalse($req->isOptions());
$this->assertFalse($req->isHead());
}
/**
* Test HTTP POST method detection
*/
public function testIsPost()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
));
$req = new \Slim\Http\Request($env);
$this->assertFalse($req->isGet());
$this->assertTrue($req->isPost());
$this->assertFalse($req->isPut());
$this->assertFalse($req->isDelete());
$this->assertFalse($req->isOptions());
$this->assertFalse($req->isHead());
}
/**
* Test HTTP PUT method detection
*/
public function testIsPut()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'PUT',
));
$req = new \Slim\Http\Request($env);
$this->assertFalse($req->isGet());
$this->assertFalse($req->isPost());
$this->assertTrue($req->isPut());
$this->assertFalse($req->isDelete());
$this->assertFalse($req->isOptions());
$this->assertFalse($req->isHead());
}
/**
* Test HTTP DELETE method detection
*/
public function testIsDelete()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'DELETE',
));
$req = new \Slim\Http\Request($env);
$this->assertFalse($req->isGet());
$this->assertFalse($req->isPost());
$this->assertFalse($req->isPut());
$this->assertTrue($req->isDelete());
$this->assertFalse($req->isOptions());
$this->assertFalse($req->isHead());
}
/**
* Test HTTP OPTIONS method detection
*/
public function testIsOptions()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'OPTIONS',
));
$req = new \Slim\Http\Request($env);
$this->assertFalse($req->isGet());
$this->assertFalse($req->isPost());
$this->assertFalse($req->isPut());
$this->assertFalse($req->isDelete());
$this->assertTrue($req->isOptions());
$this->assertFalse($req->isHead());
}
/**
* Test HTTP HEAD method detection
*/
public function testIsHead()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'HEAD',
));
$req = new \Slim\Http\Request($env);
$this->assertFalse($req->isGet());
$this->assertFalse($req->isPost());
$this->assertFalse($req->isPut());
$this->assertFalse($req->isDelete());
$this->assertFalse($req->isOptions());
$this->assertTrue($req->isHead());
}
/**
* Test AJAX method detection w/ header
*/
public function testIsAjaxWithHeader()
{
$env = \Slim\Environment::mock(array(
'X_REQUESTED_WITH' => 'XMLHttpRequest'
));
$req = new \Slim\Http\Request($env);
$this->assertTrue($req->isAjax());
$this->assertTrue($req->isXhr());
}
/**
* Test AJAX method detection w/ query parameter
*/
public function testIsAjaxWithQueryParameter()
{
$env = \Slim\Environment::mock(array(
'QUERY_STRING' => 'one=1&two=2&three=3&isajax=1',
));
$req = new \Slim\Http\Request($env);
$this->assertTrue($req->isAjax());
$this->assertTrue($req->isXhr());
}
/**
* Test AJAX method detection wihtout header or query paramter
*/
public function testIsAjaxWithoutHeaderOrQueryParameter()
{
$env = \Slim\Environment::mock();
$req = new \Slim\Http\Request($env);
$this->assertFalse($req->isAjax());
$this->assertFalse($req->isXhr());
}
/**
* Test AJAX method detection with misspelled header
*/
public function testIsAjaxWithMisspelledHeader()
{
$env = \Slim\Environment::mock(array(
'X_REQUESTED_WITH' => 'foo'
));
$req = new \Slim\Http\Request($env);
$this->assertFalse($req->isAjax());
$this->assertFalse($req->isXhr());
}
/**
* Test params from query string
*/
public function testParamsFromQueryString()
{
$env = \Slim\Environment::mock(array(
'QUERY_STRING' => 'one=1&two=2&three=3'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals(3, count($req->params()));
$this->assertEquals('1', $req->params('one'));
$this->assertNull($req->params('foo'));
}
/**
* Test params from request body
*/
public function testParamsFromRequestBody()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'QUERY_STRING' => 'one=1&two=2&three=3',
'slim.input' => 'foo=bar&abc=123',
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
'CONTENT_LENGTH' => 15
));
$req = new \Slim\Http\Request($env);
$this->assertEquals(5, count($req->params())); //Union of GET and POST
$this->assertEquals('bar', $req->params('foo'));
}
/**
* Test fetch GET params
*/
public function testGet()
{
$env = \Slim\Environment::mock(array(
'QUERY_STRING' => 'one=1&two=2&three=3'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals(3, count($req->get()));
$this->assertEquals('1', $req->get('one'));
$this->assertNull($req->get('foo'));
}
/**
* Test fetch GET params without multibyte
*/
public function testGetWithoutMultibyte()
{
$env = \Slim\Environment::mock(array(
'QUERY_STRING' => 'one=1&two=2&three=3',
'slim.tests.ignore_multibyte' => true
));
$req = new \Slim\Http\Request($env);
$this->assertEquals(3, count($req->get()));
$this->assertEquals('1', $req->get('one'));
$this->assertNull($req->get('foo'));
}
/**
* Test fetch POST params
*/
public function testPost()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'slim.input' => 'foo=bar&abc=123',
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
'CONTENT_LENGTH' => 15
));
$req = new \Slim\Http\Request($env);
$this->assertEquals(2, count($req->post()));
$this->assertEquals('bar', $req->post('foo'));
$this->assertNull($req->post('xyz'));
}
/**
* Test fetch POST params without multibyte
*/
public function testPostWithoutMultibyte()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'slim.input' => 'foo=bar&abc=123',
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
'CONTENT_LENGTH' => 15,
'slim.tests.ignore_multibyte' => true
));
$req = new \Slim\Http\Request($env);
$this->assertEquals(2, count($req->post()));
$this->assertEquals('bar', $req->post('foo'));
$this->assertNull($req->post('xyz'));
}
/**
* Test fetch POST without slim.input
*/
public function testPostWithoutInput()
{
$this->setExpectedException('RuntimeException');
$env = \Slim\Environment::mock();
unset($env['slim.input']);
$req = new \Slim\Http\Request($env);
$req->post('foo');
}
/**
* Test fetch POST params even if multipart/form-data request
*/
public function testPostWithMultipartRequest()
{
$_POST = array('foo' => 'bar'); //<-- Set by PHP
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'slim.input' => '', //<-- "php://input" is empty for multipart/form-data requests
'CONTENT_TYPE' => 'multipart/form-data',
'CONTENT_LENGTH' => 0
));
$req = new \Slim\Http\Request($env);
$this->assertEquals(1, count($req->post()));
$this->assertEquals('bar', $req->post('foo'));
$this->assertNull($req->post('xyz'));
}
/**
* Test fetch PUT params
*/
public function testPut()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'PUT',
'slim.input' => 'foo=bar&abc=123',
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
'CONTENT_LENGTH' => 15
));
$req = new \Slim\Http\Request($env);
$this->assertEquals(2, count($req->put()));
$this->assertEquals('bar', $req->put('foo'));
$this->assertEquals('bar', $req->params('foo'));
$this->assertNull($req->put('xyz'));
}
/**
* Test fetch DELETE params
*/
public function testDelete()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'DELETE',
'slim.input' => 'foo=bar&abc=123',
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
'CONTENT_LENGTH' => 15
));
$req = new \Slim\Http\Request($env);
$this->assertEquals(2, count($req->delete()));
$this->assertEquals('bar', $req->delete('foo'));
$this->assertEquals('bar', $req->params('foo'));
$this->assertNull($req->delete('xyz'));
}
/**
* Test fetch COOKIE params
*/
public function testCookies()
{
$env = \Slim\Environment::mock(array(
'COOKIE' => 'foo=bar; abc=123'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals(2, count($req->cookies()));
$this->assertEquals('bar', $req->cookies('foo'));
$this->assertNull($req->cookies('xyz'));
}
/**
* Test is form data
*/
public function testIsFormDataContentFormUrlencoded()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'PUT',
'slim.input' => '',
'CONTENT_TYPE' => 'application/x-www-form-urlencoded'
));
$req = new \Slim\Http\Request($env);
$this->assertTrue($req->isFormData());
}
/**
* Test is form data
*/
public function testIsFormDataPostContentUnknown()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'slim.input' => '',
));
$req = new \Slim\Http\Request($env);
$this->assertTrue($req->isFormData());
}
/**
* Test is form data
*/
public function testIsFormDataPostContentUnknownWithMethodOverride()
{
$env = \Slim\Environment::mock(array(
'REQUEST_METHOD' => 'PUT',
));
$env['slim.method_override.original_method'] = 'POST';
$req = new \Slim\Http\Request($env);
$this->assertTrue($req->isPut());
$this->assertTrue($req->isFormData());
}
/**
* Test is not form data
*/
public function testIsNotFormData()
{
$env = \Slim\Environment::mock(array(
'slim.input' => '',
'CONTENT_TYPE' => 'application/json'
));
$req = new \Slim\Http\Request($env);
$this->assertFalse($req->isFormData());
}
/**
* Test headers
*/
public function testHeaders()
{
$env = \Slim\Environment::mock(array(
'ACCEPT_ENCODING' => 'gzip'
));
$req = new \Slim\Http\Request($env);
$headers = $req->headers();
$this->assertTrue(is_array($headers));
$this->assertArrayHasKey('ACCEPT_ENCODING', $headers);
$this->assertEquals('gzip', $req->headers('HTTP_ACCEPT_ENCODING'));
$this->assertEquals('gzip', $req->headers('HTTP-ACCEPT-ENCODING'));
$this->assertEquals('gzip', $req->headers('http_accept_encoding'));
$this->assertEquals('gzip', $req->headers('http-accept-encoding'));
$this->assertEquals('gzip', $req->headers('ACCEPT_ENCODING'));
$this->assertEquals('gzip', $req->headers('ACCEPT-ENCODING'));
$this->assertEquals('gzip', $req->headers('accept_encoding'));
$this->assertEquals('gzip', $req->headers('accept-encoding'));
$this->assertNull($req->headers('foo'));
}
/**
* Test accurately removes HTTP_ prefix from input header name
*/
public function testHeaderRemovesHttpPrefix()
{
$env = \Slim\Environment::mock(array(
'X_HTTP_METHOD_OVERRIDE' => 'PUT',
'CONTENT_TYPE' => 'application/json'
));
//fwrite(fopen('php://stdout', 'w'), print_r($env, true));
$req = new \Slim\Http\Request($env);
$this->assertEquals('PUT', $req->headers('X_HTTP_METHOD_OVERRIDE'));
$this->assertNull($req->headers('X_METHOD_OVERRIDE')); //<-- Ensures `HTTP_` is not removed if not prefix
$this->assertEquals('application/json', $req->headers('HTTP_CONTENT_TYPE')); //<-- Ensures `HTTP_` is removed if prefix
}
/**
* Test get body
*/
public function testGetBodyWhenExists()
{
$env = \Slim\Environment::mock(array(
'slim.input' => 'foo=bar&abc=123',
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
'CONTENT_LENGTH' => 15
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('foo=bar&abc=123', $req->getBody());
}
/**
* Test get body
*/
public function testGetBodyWhenNotExists()
{
$env = \Slim\Environment::mock();
$req = new \Slim\Http\Request($env);
$this->assertEquals('', $req->getBody());
}
/**
* Test get content type
*/
public function testGetContentTypeWhenExists()
{
$env = \Slim\Environment::mock(array(
'slim.input' => '',
'CONTENT_TYPE' => 'application/json; charset=ISO-8859-4'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('application/json; charset=ISO-8859-4', $req->getContentType());
}
/**
* Test get content type
*/
public function testGetContentTypeWhenNotExists()
{
$env = \Slim\Environment::mock();
$req = new \Slim\Http\Request($env);
$this->assertNull($req->getContentType());
}
/**
* Test get media type
*/
public function testGetMediaTypeWhenExists()
{
$env = \Slim\Environment::mock(array(
'CONTENT_TYPE' => 'application/json;charset=utf-8'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('application/json', $req->getMediaType());
}
/**
* Test get media type
*/
public function testGetMediaTypeWhenNotExists()
{
$env = \Slim\Environment::mock();
$req = new \Slim\Http\Request($env);
$this->assertNull($req->getMediaType());
}
/**
* Test get media type
*/
public function testGetMediaTypeWhenNoParamsExist()
{
$env = \Slim\Environment::mock(array(
'slim.input' => '',
'CONTENT_TYPE' => 'application/json'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('application/json', $req->getMediaType());
}
/**
* Test get media type params
*/
public function testGetMediaTypeParams()
{
$env = \Slim\Environment::mock(array(
'CONTENT_TYPE' => 'application/json; charset=ISO-8859-4'
));
$req = new \Slim\Http\Request($env);
$params = $req->getMediaTypeParams();
$this->assertEquals(1, count($params));
$this->assertArrayHasKey('charset', $params);
$this->assertEquals('ISO-8859-4', $params['charset']);
}
/**
* Test get media type params
*/
public function testGetMediaTypeParamsWhenNotExists()
{
$env = \Slim\Environment::mock();
$req = new \Slim\Http\Request($env);
$params = $req->getMediaTypeParams();
$this->assertTrue(is_array($params));
$this->assertEquals(0, count($params));
}
/**
* Test get content charset
*/
public function testGetContentCharset()
{
$env = \Slim\Environment::mock(array(
'slim.input' => '',
'CONTENT_TYPE' => 'application/json; charset=ISO-8859-4'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('ISO-8859-4', $req->getContentCharset());
}
/**
* Test get content charset
*/
public function testGetContentCharsetWhenNotExists()
{
$env = \Slim\Environment::mock(array(
'slim.input' => '',
'CONTENT_TYPE' => 'application/json'
));
$req = new \Slim\Http\Request($env);
$this->assertNull($req->getContentCharset());
}
/**
* Test get content length
*/
public function testGetContentLength()
{
$env = \Slim\Environment::mock(array(
'slim.input' => 'foo=bar&abc=123',
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
'CONTENT_LENGTH' => 15
));
$req = new \Slim\Http\Request($env);
$this->assertEquals(15, $req->getContentLength());
}
/**
* Test get content length
*/
public function testGetContentLengthWhenNotExists()
{
$env = \Slim\Environment::mock(array(
'slim.input' => '',
));
$req = new \Slim\Http\Request($env);
$this->assertEquals(0, $req->getContentLength());
}
/**
* Test get host
*/
public function testGetHost()
{
$env = \Slim\Environment::mock(array(
'SERVER_NAME' => 'slim',
'HOST' => 'slimframework.com'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('slimframework.com', $req->getHost()); //Uses HTTP_HOST if available
}
/**
* Test get host when it has a port number
*/
public function testGetHostAndStripPort()
{
$env = \Slim\Environment::mock(array(
'SERVER_NAME' => 'slim',
'HOST' => 'slimframework.com:80'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('slimframework.com', $req->getHost()); //Uses HTTP_HOST if available
}
/**
* Test get host
*/
public function testGetHostWhenNotExists()
{
$env = \Slim\Environment::mock(array(
'SERVER_NAME' => 'slim',
'HOST' => 'slimframework.com'
));
unset($env['HOST']);
$req = new \Slim\Http\Request($env);
$this->assertEquals('slim', $req->getHost()); //Uses SERVER_NAME as backup
}
/**
* Test get host with port
*/
public function testGetHostWithPort()
{
$env = \Slim\Environment::mock(array(
'HOST' => 'slimframework.com',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('slimframework.com:80', $req->getHostWithPort());
}
/**
* Test get host with port doesn't dulplicate port numbers
*/
public function testGetHostDoesntDulplicatePort()
{
$env = \Slim\Environment::mock(array(
'HOST' => 'slimframework.com:80',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('slimframework.com:80', $req->getHostWithPort());
}
/**
* Test get port
*/
public function testGetPort()
{
$env = \Slim\Environment::mock(array(
'SERVER_PORT' => 80
));
$req = new \Slim\Http\Request($env);
$this->assertTrue(is_integer($req->getPort()));
$this->assertEquals(80, $req->getPort());
}
/**
* Test get scheme
*/
public function testGetSchemeIfHttp()
{
$env = \Slim\Environment::mock(array(
'slim.url_scheme' => 'http'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('http', $req->getScheme());
}
/**
* Test get scheme
*/
public function testGetSchemeIfHttps()
{
$env = \Slim\Environment::mock(array(
'slim.url_scheme' => 'https',
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('https', $req->getScheme());
}
/**
* Test get [script name, root uri, path, path info, resource uri] in subdirectory without htaccess
*/
public function testAppPathsInSubdirectoryWithoutHtaccess()
{
$env = \Slim\Environment::mock(array(
'SCRIPT_NAME' => '/foo/index.php', //<-- Physical
'PATH_INFO' => '/bar/xyz', //<-- Virtual
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('/foo/index.php', $req->getScriptName());
$this->assertEquals('/foo/index.php', $req->getRootUri());
$this->assertEquals('/foo/index.php/bar/xyz', $req->getPath());
$this->assertEquals('/bar/xyz', $req->getPathInfo());
$this->assertEquals('/bar/xyz', $req->getResourceUri());
}
/**
* Test get [script name, root uri, path, path info, resource uri] in subdirectory with htaccess
*/
public function testAppPathsInSubdirectoryWithHtaccess()
{
$env = \Slim\Environment::mock(array(
'SCRIPT_NAME' => '/foo', //<-- Physical
'PATH_INFO' => '/bar/xyz', //<-- Virtual
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('/foo', $req->getScriptName());
$this->assertEquals('/foo', $req->getRootUri());
$this->assertEquals('/foo/bar/xyz', $req->getPath());
$this->assertEquals('/bar/xyz', $req->getPathInfo());
$this->assertEquals('/bar/xyz', $req->getResourceUri());
}
/**
* Test get [script name, root uri, path, path info, resource uri] in root directory without htaccess
*/
public function testAppPathsInRootDirectoryWithoutHtaccess()
{
$env = \Slim\Environment::mock(array(
'SCRIPT_NAME' => '/index.php', //<-- Physical
'PATH_INFO' => '/bar/xyz', //<-- Virtual
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('/index.php', $req->getScriptName());
$this->assertEquals('/index.php', $req->getRootUri());
$this->assertEquals('/index.php/bar/xyz', $req->getPath());
$this->assertEquals('/bar/xyz', $req->getPathInfo());
$this->assertEquals('/bar/xyz', $req->getResourceUri());
}
/**
* Test get [script name, root uri, path, path info, resource uri] in root directory with htaccess
*/
public function testAppPathsInRootDirectoryWithHtaccess()
{
$env = \Slim\Environment::mock(array(
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/bar/xyz', //<-- Virtual
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('', $req->getScriptName());
$this->assertEquals('', $req->getRootUri());
$this->assertEquals('/bar/xyz', $req->getPath());
$this->assertEquals('/bar/xyz', $req->getPathInfo());
$this->assertEquals('/bar/xyz', $req->getResourceUri());
}
/**
* Test get URL
*/
public function testGetUrl()
{
$env = \Slim\Environment::mock(array(
'HOST' => 'slimframework.com',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('http://slimframework.com', $req->getUrl());
}
/**
* Test get URL
*/
public function testGetUrlWithCustomPort()
{
$env = \Slim\Environment::mock(array(
'HOST' => 'slimframework.com',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 8080,
'slim.url_scheme' => 'http'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('http://slimframework.com:8080', $req->getUrl());
}
/**
* Test get URL
*/
public function testGetUrlWithHttps()
{
$env = \Slim\Environment::mock(array(
'HOST' => 'slimframework.com',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 443,
'slim.url_scheme' => 'https'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('https://slimframework.com', $req->getUrl());
}
/**
* Test get IP
*/
public function testGetIp()
{
$env = \Slim\Environment::mock(array(
'REMOTE_ADDR' => '127.0.0.1'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('127.0.0.1', $req->getIp());
}
/**
* Test get IP with proxy server and Client-Ip header
*/
public function testGetIpWithClientIp()
{
$env = \Slim\Environment::mock(array(
'REMOTE_ADDR' => '127.0.0.1',
'CLIENT_IP' => '127.0.0.2'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('127.0.0.2', $req->getIp());
}
/**
* Test get IP with proxy server and X-Forwarded-For header
*/
public function testGetIpWithForwardedFor()
{
$env = \Slim\Environment::mock(array(
'REMOTE_ADDR' => '127.0.0.1',
'CLIENT_IP' => '127.0.0.2',
'X_FORWARDED_FOR' => '127.0.0.3'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('127.0.0.3', $req->getIp());
}
/**
* Test get refererer
*/
public function testGetReferrer()
{
$env = \Slim\Environment::mock(array(
'REFERER' => 'http://foo.com'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('http://foo.com', $req->getReferrer());
$this->assertEquals('http://foo.com', $req->getReferer());
}
/**
* Test get refererer
*/
public function testGetReferrerWhenNotExists()
{
$env = \Slim\Environment::mock();
$req = new \Slim\Http\Request($env);
$this->assertNull($req->getReferrer());
$this->assertNull($req->getReferer());
}
/**
* Test get user agent string
*/
public function testGetUserAgent()
{
$env = \Slim\Environment::mock(array(
'USER_AGENT' => 'user-agent-string'
));
$req = new \Slim\Http\Request($env);
$this->assertEquals('user-agent-string', $req->getUserAgent());
}
/**
* Test get user agent string when not set
*/
public function testGetUserAgentWhenNotExists()
{
$env = \Slim\Environment::mock();
unset($env['USER_AGENT']);
$req = new \Slim\Http\Request($env);
$this->assertNull($req->getUserAgent());
}
}

574
tests/Http/ResponseTest.php Normal file
View File

@ -0,0 +1,574 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class ResponseTest extends PHPUnit_Framework_TestCase
{
/**
* Test constructor without args
*/
public function testConstructorWithoutArgs()
{
$r = new \Slim\Http\Response();
$this->assertEquals('', $r->body());
$this->assertEquals(200, $r->status());
$this->assertEquals(0, $r->length());
$this->assertEquals('text/html', $r['Content-Type']);
}
/**
* Test constructor with args
*/
public function testConstructorWithArgs()
{
$r = new \Slim\Http\Response('Page Not Found', 404, array('Content-Type' => 'application/json', 'X-Created-By' => 'Slim'));
$this->assertEquals('Page Not Found', $r->body());
$this->assertEquals(404, $r->status());
$this->assertEquals(14, $r->length());
$this->assertEquals('application/json', $r['Content-Type']);
$this->assertEquals('Slim', $r['X-Created-By']);
}
/**
* Test get status
*/
public function testGetStatus()
{
$r = new \Slim\Http\Response();
$this->assertEquals(200, $r->status());
}
/**
* Test set status
*/
public function testSetStatus()
{
$r = new \Slim\Http\Response();
$r->status(500);
$this->assertEquals(500, $r->status());
}
/**
* Test get headers
*/
public function testGetHeaders()
{
$r = new \Slim\Http\Response();
$headers = $r->headers();
$this->assertEquals(1, count($headers));
$this->assertEquals('text/html', $headers['Content-Type']);
}
/**
* Test get and set header (without Array Access)
*/
public function testGetAndSetHeader()
{
$r = new \Slim\Http\Response();
$r->header('X-Foo', 'Bar');
$this->assertEquals('Bar', $r->header('X-Foo'));
}
/**
* Test get body
*/
public function testGetBody()
{
$r = new \Slim\Http\Response('Foo');
$this->assertEquals('Foo', $r->body());
}
/**
* Test set body
*/
public function testSetBody()
{
$r = new \Slim\Http\Response();
$r->body('Foo');
$this->assertEquals('Foo', $r->body());
}
/**
* Test get length
*/
public function testGetLength()
{
$r = new \Slim\Http\Response('Foo');
$this->assertEquals(3, $r->length());
}
/**
* Test set length
*/
public function testSetLength()
{
$r = new \Slim\Http\Response();
$r->length(3);
$this->assertEquals(3, $r->length());
}
/**
* Test write for appending
*/
public function testWriteAppend()
{
$r = new \Slim\Http\Response('Foo');
$r->write('Bar');
$this->assertEquals('FooBar', $r->body());
}
/**
* Test write for replacing
*/
public function testWriteReplace()
{
$r = new \Slim\Http\Response('Foo');
$r->write('Bar', true);
$this->assertEquals('Bar', $r->body());
}
/**
* Test finalize
*/
public function testFinalize()
{
$r = new \Slim\Http\Response();
$r->status(404);
$r['Content-Type'] = 'application/json';
$r->write('Foo');
$result = $r->finalize();
$this->assertEquals(3, count($result));
$this->assertEquals(404, $result[0]);
$this->assertFalse(is_null($result[1]['Content-Type']));
}
/**
* Test finalize
*/
public function testFinalizeWithoutBody()
{
$r = new \Slim\Http\Response();
$r->status(204);
$r['Content-Type'] = 'application/json';
$r->write('Foo');
$result = $r->finalize();
$this->assertEquals(3, count($result));
$this->assertEquals('', $result[2]);
}
/**
* Test set cookie with only name and value
*/
public function testSetCookieWithNameAndValue()
{
$r = new \Slim\Http\Response();
$r->setCookie('foo', 'bar');
$this->assertEquals('foo=bar', $r['Set-Cookie']);
}
/**
* Test set multiple cookies with only name and value
*/
public function testSetMultipleCookiesWithNameAndValue()
{
$r = new \Slim\Http\Response();
$r->setCookie('foo', 'bar');
$r->setCookie('abc', '123');
$this->assertEquals("foo=bar\nabc=123", $r['Set-Cookie']);
}
/**
* Test set cookie only name and value and expires (as int)
*/
public function testSetMultipleCookiesWithNameAndValueAndExpiresAsInt()
{
$now = time();
$r = new \Slim\Http\Response();
$r->setCookie('foo', array(
'value' => 'bar',
'expires' => $now
));
$this->assertEquals("foo=bar; expires=" . gmdate('D, d-M-Y H:i:s e', $now), $r['Set-Cookie']);
}
/**
* Test set cookie with only name and value and expires (as string)
*/
public function testSetMultipleCookiesWithNameAndValueAndExpiresAsString()
{
$expires = 'next Tuesday';
$r = new \Slim\Http\Response();
$r->setCookie('foo', array(
'value' => 'bar',
'expires' => $expires
));
$this->assertEquals("foo=bar; expires=" . gmdate('D, d-M-Y H:i:s e', strtotime($expires)), $r['Set-Cookie']);
}
/**
* Test set cookie with name, value, domain
*/
public function testSetCookieWithNameAndValueAndDomain()
{
$r = new \Slim\Http\Response();
$r->setCookie('foo', array(
'value' => 'bar',
'domain' => '.slimframework.com'
));
$this->assertEquals('foo=bar; domain=.slimframework.com', $r['Set-Cookie']);
}
/**
* Test set cookie with name, value, domain, path
*/
public function testSetCookieWithNameAndValueAndDomainAndPath()
{
$r = new \Slim\Http\Response();
$r->setCookie('foo', array(
'value' => 'bar',
'domain' => '.slimframework.com',
'path' => '/foo'
));
$this->assertEquals($r['Set-Cookie'], 'foo=bar; domain=.slimframework.com; path=/foo');
}
/**
* Test set cookie with only name and value and secure flag
*/
public function testSetCookieWithNameAndValueAndSecureFlag()
{
$r = new \Slim\Http\Response();
$r->setCookie('foo', array(
'value' => 'bar',
'secure' => true
));
$this->assertEquals('foo=bar; secure', $r['Set-Cookie']);
}
/**
* Test set cookie with only name and value and secure flag (as non-truthy)
*/
public function testSetCookieWithNameAndValueAndSecureFlagAsNonTruthy()
{
$r = new \Slim\Http\Response();
$r->setCookie('foo', array(
'value' => 'bar',
'secure' => 0
));
$this->assertEquals('foo=bar', $r['Set-Cookie']);
}
/**
* Test set cookie with only name and value and httponly flag
*/
public function testSetCookieWithNameAndValueAndHttpOnlyFlag()
{
$r = new \Slim\Http\Response();
$r->setCookie('foo', array(
'value' => 'bar',
'httponly' => true
));
$this->assertEquals('foo=bar; HttpOnly', $r['Set-Cookie']);
}
/**
* Test set cookie with only name and value and httponly flag (as non-truthy)
*/
public function testSetCookieWithNameAndValueAndHttpOnlyFlagAsNonTruthy()
{
$r = new \Slim\Http\Response();
$r->setCookie('foo', array(
'value' => 'bar',
'httponly' => 0
));
$this->assertEquals('foo=bar', $r['Set-Cookie']);
}
/*
* Test delete cookie by name
*/
public function testDeleteCookieByName()
{
$r = new \Slim\Http\Response();
$r->setCookie('foo', 'bar');
$r->setCookie('abc', '123');
$r->deleteCookie('foo');
$this->assertEquals(1, preg_match("@abc=123\nfoo=; expires=@", $r['Set-Cookie']));
}
/*
* Test delete cookie by name and domain
*/
public function testDeleteCookieByNameAndDomain1()
{
$r = new \Slim\Http\Response();
$r->setCookie('foo', 'bar'); //Note: This does not have domain associated with it
$r->setCookie('abc', '123');
$r->deleteCookie('foo', array('domain' => '.slimframework.com')); //This SHOULD NOT remove the `foo` cookie
$this->assertEquals(1, preg_match("@foo=bar\nabc=123\nfoo=; domain=.slimframework.com; expires=@", $r['Set-Cookie']));
}
/*
* Test delete cookie by name and domain
*/
public function testDeleteCookieByNameAndDomain2()
{
$r = new \Slim\Http\Response();
$r->setCookie('foo', array(
'value' => 'bar',
'domain' => '.slimframework.com' //Note: This does have domain associated with it
));
$r->setCookie('abc', '123');
$r->deleteCookie('foo', array('domain' => '.slimframework.com')); //This SHOULD remove the `foo` cookie
$this->assertEquals(1, preg_match("@abc=123\nfoo=; domain=.slimframework.com; expires=@", $r['Set-Cookie']));
}
/**
* Test delete cookie by name and custom props
*/
public function testDeleteCookieByNameAndCustomProps()
{
$r = new \Slim\Http\Response();
$r->setCookie('foo', 'bar');
$r->setCookie('abc', '123');
$r->deleteCookie('foo', array(
'secure' => true,
'httponly' => true
));
$this->assertEquals(1, preg_match("@abc=123\nfoo=; expires=.*; secure; HttpOnly@", $r['Set-Cookie']));
}
/**
* Test redirect
*/
public function testRedirect()
{
$r = new \Slim\Http\Response();
$r->redirect('/foo');
$this->assertEquals(302, $r->status());
$this->assertEquals('/foo', $r['Location']);
}
/**
* Test redirect with custom status
*/
public function testRedirectWithCustomStatus()
{
$r = new \Slim\Http\Response();
$r->redirect('/foo', 307);
$this->assertEquals(307, $r->status());
$this->assertEquals('/foo', $r['Location']);
}
/**
* Test isEmpty
*/
public function testIsEmpty()
{
$r1 = new \Slim\Http\Response();
$r2 = new \Slim\Http\Response();
$r1->status(404);
$r2->status(201);
$this->assertFalse($r1->isEmpty());
$this->assertTrue($r2->isEmpty());
}
/**
* Test isClientError
*/
public function testIsClientError()
{
$r1 = new \Slim\Http\Response();
$r2 = new \Slim\Http\Response();
$r1->status(404);
$r2->status(500);
$this->assertTrue($r1->isClientError());
$this->assertFalse($r2->isClientError());
}
/**
* Test isForbidden
*/
public function testIsForbidden()
{
$r1 = new \Slim\Http\Response();
$r2 = new \Slim\Http\Response();
$r1->status(403);
$r2->status(500);
$this->assertTrue($r1->isForbidden());
$this->assertFalse($r2->isForbidden());
}
/**
* Test isInformational
*/
public function testIsInformational()
{
$r1 = new \Slim\Http\Response();
$r2 = new \Slim\Http\Response();
$r1->status(100);
$r2->status(200);
$this->assertTrue($r1->isInformational());
$this->assertFalse($r2->isInformational());
}
/**
* Test isInformational
*/
public function testIsNotFound()
{
$r1 = new \Slim\Http\Response();
$r2 = new \Slim\Http\Response();
$r1->status(404);
$r2->status(200);
$this->assertTrue($r1->isNotFound());
$this->assertFalse($r2->isNotFound());
}
/**
* Test isOk
*/
public function testIsOk()
{
$r1 = new \Slim\Http\Response();
$r2 = new \Slim\Http\Response();
$r1->status(200);
$r2->status(201);
$this->assertTrue($r1->isOk());
$this->assertFalse($r2->isOk());
}
/**
* Test isSuccessful
*/
public function testIsSuccessful()
{
$r1 = new \Slim\Http\Response();
$r2 = new \Slim\Http\Response();
$r3 = new \Slim\Http\Response();
$r1->status(200);
$r2->status(201);
$r3->status(302);
$this->assertTrue($r1->isSuccessful());
$this->assertTrue($r2->isSuccessful());
$this->assertFalse($r3->isSuccessful());
}
/**
* Test isRedirect
*/
public function testIsRedirect()
{
$r1 = new \Slim\Http\Response();
$r2 = new \Slim\Http\Response();
$r1->status(307);
$r2->status(304);
$this->assertTrue($r1->isRedirect());
$this->assertFalse($r2->isRedirect());
}
/**
* Test isRedirection
*/
public function testIsRedirection()
{
$r1 = new \Slim\Http\Response();
$r2 = new \Slim\Http\Response();
$r3 = new \Slim\Http\Response();
$r1->status(307);
$r2->status(304);
$r3->status(200);
$this->assertTrue($r1->isRedirection());
$this->assertTrue($r2->isRedirection());
$this->assertFalse($r3->isRedirection());
}
/**
* Test isServerError
*/
public function testIsServerError()
{
$r1 = new \Slim\Http\Response();
$r2 = new \Slim\Http\Response();
$r1->status(500);
$r2->status(400);
$this->assertTrue($r1->isServerError());
$this->assertFalse($r2->isServerError());
}
/**
* Test offset exists and offset get
*/
public function testOffsetExistsAndGet()
{
$r = new \Slim\Http\Response();
$this->assertFalse(empty($r['Content-Type']));
$this->assertNull($r['foo']);
}
/**
* Test iteration
*/
public function testIteration()
{
$h = new \Slim\Http\Response();
$output = '';
foreach ($h as $key => $value) {
$output .= $key . $value;
}
$this->assertEquals('Content-Typetext/html', $output);
}
/**
* Test countable
*/
public function testCountable()
{
$r1 = new \Slim\Http\Response();
$this->assertEquals(1, count($r1)); //Content-Type
}
/**
* Test message for code when message exists
*/
public function testMessageForCode()
{
$this->assertEquals('200 OK', \Slim\Http\Response::getMessageForCode(200));
}
/**
* Test message for code when message exists
*/
public function testMessageForCodeWithInvalidCode()
{
$this->assertNull(\Slim\Http\Response::getMessageForCode(600));
}
}

388
tests/Http/UtilTest.php Normal file
View File

@ -0,0 +1,388 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class SlimHttpUtilTest extends PHPUnit_Framework_TestCase
{
/**
* Test strip slashes when magic quotes disabled
*/
public function testStripSlashesWithoutMagicQuotes()
{
$data = "This should have \"quotes\" in it";
$stripped = \Slim\Http\Util::stripSlashesIfMagicQuotes($data, false);
$this->assertEquals($data, $stripped);
}
/**
* Test strip slashes from array when magic quotes disabled
*/
public function testStripSlashesFromArrayWithoutMagicQuotes()
{
$data = array("This should have \"quotes\" in it", "And this \"too\" has quotes");
$stripped = \Slim\Http\Util::stripSlashesIfMagicQuotes($data, false);
$this->assertEquals($data, $stripped);
}
/**
* Test strip slashes when magic quotes enabled
*/
public function testStripSlashesWithMagicQuotes()
{
$data = "This should have \"quotes\" in it";
$stripped = \Slim\Http\Util::stripSlashesIfMagicQuotes($data, true);
$this->assertEquals('This should have "quotes" in it', $stripped);
}
/**
* Test strip slashes from array when magic quotes enabled
*/
public function testStripSlashesFromArrayWithMagicQuotes()
{
$data = array("This should have \"quotes\" in it", "And this \"too\" has quotes");
$stripped = \Slim\Http\Util::stripSlashesIfMagicQuotes($data, true);
$this->assertEquals($data = array('This should have "quotes" in it', 'And this "too" has quotes'), $stripped);
}
/**
* Test encrypt and decrypt with valid data
*/
public function testEncryptAndDecryptWithValidData()
{
$data = 'foo';
$key = 'secret';
$iv = md5('initializationVector');
$encrypted = \Slim\Http\Util::encrypt($data, $key, $iv);
$decrypted = \Slim\Http\Util::decrypt($encrypted, $key, $iv);
$this->assertEquals($data, $decrypted);
$this->assertTrue($data !== $encrypted);
}
/**
* Test encrypt when data is empty string
*/
public function testEncryptWhenDataIsEmptyString()
{
$data = '';
$key = 'secret';
$iv = md5('initializationVector');
$encrypted = \Slim\Http\Util::encrypt($data, $key, $iv);
$this->assertEquals('', $encrypted);
}
/**
* Test decrypt when data is empty string
*/
public function testDecryptWhenDataIsEmptyString()
{
$data = '';
$key = 'secret';
$iv = md5('initializationVector');
$decrypted = \Slim\Http\Util::decrypt($data, $key, $iv);
$this->assertEquals('', $decrypted);
}
/**
* Test encrypt when IV and key sizes are too long
*/
public function testEncryptAndDecryptWhenKeyAndIvAreTooLong()
{
$data = 'foo';
$key = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz';
$iv = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz';
$encrypted = \Slim\Http\Util::encrypt($data, $key, $iv);
$decrypted = \Slim\Http\Util::decrypt($encrypted, $key, $iv);
$this->assertEquals($data, $decrypted);
$this->assertTrue($data !== $encrypted);
}
public function testEncodeAndDecodeSecureCookieWithValidData()
{
//Prepare cookie value
$value = 'foo';
$expires = time() + 86400;
$secret = 'password';
$algorithm = MCRYPT_RIJNDAEL_256;
$mode = MCRYPT_MODE_CBC;
$encodedValue = \Slim\Http\Util::encodeSecureCookie($value, $expires, $secret, $algorithm, $mode);
$decodedValue = \Slim\Http\Util::decodeSecureCookie($encodedValue, $secret, $algorithm, $mode);
//Test secure cookie value
$parts = explode('|', $encodedValue);
$this->assertEquals(3, count($parts));
$this->assertEquals($expires, $parts[0]);
$this->assertEquals($value, $decodedValue);
}
/**
* Test encode/decode secure cookie with old expiration
*
* In this test, the expiration date is purposefully set to a time before now.
* When decoding the encoded cookie value, FALSE is returned since the cookie
* will have expired before it is decoded.
*/
public function testEncodeAndDecodeSecureCookieWithOldExpiration()
{
$value = 'foo';
$expires = time() - 100;
$secret = 'password';
$algorithm = MCRYPT_RIJNDAEL_256;
$mode = MCRYPT_MODE_CBC;
$encodedValue = \Slim\Http\Util::encodeSecureCookie($value, $expires, $secret, $algorithm, $mode);
$decodedValue = \Slim\Http\Util::decodeSecureCookie($encodedValue, $secret, $algorithm, $mode);
$this->assertFalse($decodedValue);
}
/**
* Test encode/decode secure cookie with tampered data
*
* In this test, the encoded data is purposefully changed to simulate someone
* tampering with the client-side cookie data. When decoding the encoded cookie value,
* FALSE is returned since the verification key will not match.
*/
public function testEncodeAndDecodeSecureCookieWithTamperedData()
{
$value = 'foo';
$expires = time() + 86400;
$secret = 'password';
$algorithm = MCRYPT_RIJNDAEL_256;
$mode = MCRYPT_MODE_CBC;
$encodedValue = \Slim\Http\Util::encodeSecureCookie($value, $expires, $secret, $algorithm, $mode);
$encodedValueParts = explode('|', $encodedValue);
$encodedValueParts[1] = $encodedValueParts[1] . 'changed';
$encodedValue = implode('|', $encodedValueParts);
$decodedValue = \Slim\Http\Util::decodeSecureCookie($encodedValue, $secret, $algorithm, $mode);
$this->assertFalse($decodedValue);
}
public function testSetCookieHeaderWithNameAndValue()
{
$name = 'foo';
$value = 'bar';
$header = array();
\Slim\Http\Util::setCookieHeader($header, $name, $value);
$this->assertEquals('foo=bar', $header['Set-Cookie']);
}
public function testSetCookieHeaderWithNameAndValueWhenCookieAlreadySet()
{
$name = 'foo';
$value = 'bar';
$header = array('Set-Cookie' => 'one=two');
\Slim\Http\Util::setCookieHeader($header, $name, $value);
$this->assertEquals("one=two\nfoo=bar", $header['Set-Cookie']);
}
public function testSetCookieHeaderWithNameAndValueAndDomain()
{
$name = 'foo';
$value = 'bar';
$domain = 'foo.com';
$header = array();
\Slim\Http\Util::setCookieHeader($header, $name, array(
'value' => $value,
'domain' => $domain
));
$this->assertEquals('foo=bar; domain=foo.com', $header['Set-Cookie']);
}
public function testSetCookieHeaderWithNameAndValueAndDomainAndPath()
{
$name = 'foo';
$value = 'bar';
$domain = 'foo.com';
$path = '/foo';
$header = array();
\Slim\Http\Util::setCookieHeader($header, $name, array(
'value' => $value,
'domain' => $domain,
'path' => $path
));
$this->assertEquals('foo=bar; domain=foo.com; path=/foo', $header['Set-Cookie']);
}
public function testSetCookieHeaderWithNameAndValueAndDomainAndPathAndExpiresAsString()
{
$name = 'foo';
$value = 'bar';
$domain = 'foo.com';
$path = '/foo';
$expires = '2 days';
$expiresFormat = gmdate('D, d-M-Y H:i:s e', strtotime($expires));
$header = array();
\Slim\Http\Util::setCookieHeader($header, $name, array(
'value' => $value,
'domain' => $domain,
'path' => '/foo',
'expires' => $expires
));
$this->assertEquals('foo=bar; domain=foo.com; path=/foo; expires=' . $expiresFormat, $header['Set-Cookie']);
}
public function testSetCookieHeaderWithNameAndValueAndDomainAndPathAndExpiresAsInteger()
{
$name = 'foo';
$value = 'bar';
$domain = 'foo.com';
$path = '/foo';
$expires = strtotime('2 days');
$expiresFormat = gmdate('D, d-M-Y H:i:s e', $expires);
$header = array();
\Slim\Http\Util::setCookieHeader($header, $name, array(
'value' => $value,
'domain' => $domain,
'path' => '/foo',
'expires' => $expires
));
$this->assertEquals('foo=bar; domain=foo.com; path=/foo; expires=' . $expiresFormat, $header['Set-Cookie']);
}
public function testSetCookieHeaderWithNameAndValueAndDomainAndPathAndExpiresAsZero()
{
$name = 'foo';
$value = 'bar';
$domain = 'foo.com';
$path = '/foo';
$expires = 0;
$header = array();
\Slim\Http\Util::setCookieHeader($header, $name, array(
'value' => $value,
'domain' => $domain,
'path' => '/foo',
'expires' => $expires
));
$this->assertEquals('foo=bar; domain=foo.com; path=/foo', $header['Set-Cookie']);
}
public function testSetCookieHeaderWithNameAndValueAndDomainAndPathAndExpiresAndSecure()
{
$name = 'foo';
$value = 'bar';
$domain = 'foo.com';
$path = '/foo';
$expires = strtotime('2 days');
$expiresFormat = gmdate('D, d-M-Y H:i:s e', $expires);
$secure = true;
$header = array();
\Slim\Http\Util::setCookieHeader($header, $name, array(
'value' => $value,
'domain' => $domain,
'path' => '/foo',
'expires' => $expires,
'secure' => $secure
));
$this->assertEquals('foo=bar; domain=foo.com; path=/foo; expires=' . $expiresFormat . '; secure', $header['Set-Cookie']);
}
public function testSetCookieHeaderWithNameAndValueAndDomainAndPathAndExpiresAndSecureAndHttpOnly()
{
$name = 'foo';
$value = 'bar';
$domain = 'foo.com';
$path = '/foo';
$expires = strtotime('2 days');
$expiresFormat = gmdate('D, d-M-Y H:i:s e', $expires);
$secure = true;
$httpOnly = true;
$header = array();
\Slim\Http\Util::setCookieHeader($header, $name, array(
'value' => $value,
'domain' => $domain,
'path' => '/foo',
'expires' => $expires,
'secure' => $secure,
'httponly' => $httpOnly
));
$this->assertEquals('foo=bar; domain=foo.com; path=/foo; expires=' . $expiresFormat . '; secure; HttpOnly', $header['Set-Cookie']);
}
public function testDeleteCookieHeaderWithSurvivingCookie()
{
$header = array('Set-Cookie' => "foo=bar\none=two");
\Slim\Http\Util::deleteCookieHeader($header, 'foo');
$this->assertEquals(1, preg_match("@^one=two\nfoo=; expires=@", $header['Set-Cookie']));
}
public function testDeleteCookieHeaderWithoutSurvivingCookie()
{
$header = array('Set-Cookie' => "foo=bar");
\Slim\Http\Util::deleteCookieHeader($header, 'foo');
$this->assertEquals(1, preg_match("@foo=; expires=@", $header['Set-Cookie']));
}
public function testDeleteCookieHeaderWithMatchingDomain()
{
$header = array('Set-Cookie' => "foo=bar; domain=foo.com");
\Slim\Http\Util::deleteCookieHeader($header, 'foo', array(
'domain' => 'foo.com'
));
$this->assertEquals(1, preg_match("@foo=; domain=foo.com; expires=@", $header['Set-Cookie']));
}
public function testDeleteCookieHeaderWithoutMatchingDomain()
{
$header = array('Set-Cookie' => "foo=bar; domain=foo.com");
\Slim\Http\Util::deleteCookieHeader($header, 'foo', array(
'domain' => 'bar.com'
));
$this->assertEquals(1, preg_match("@foo=bar; domain=foo\.com\nfoo=; domain=bar\.com@", $header['Set-Cookie']));
}
/**
* Test parses Cookie: HTTP header
*/
public function testParsesCookieHeader()
{
$header = 'foo=bar; one=two; colors=blue';
$result = \Slim\Http\Util::parseCookieHeader($header);
$this->assertEquals(3, count($result));
$this->assertEquals('bar', $result['foo']);
$this->assertEquals('two', $result['one']);
$this->assertEquals('blue', $result['colors']);
}
public function testParsesCookieHeaderWithCommaSeparator()
{
$header = 'foo=bar, one=two, colors=blue';
$result = \Slim\Http\Util::parseCookieHeader($header);
$this->assertEquals(3, count($result));
$this->assertEquals('bar', $result['foo']);
$this->assertEquals('two', $result['one']);
$this->assertEquals('blue', $result['colors']);
}
public function testPrefersLeftmostCookieWhenManyCookiesWithSameName()
{
$header = 'foo=bar; foo=beer';
$result = \Slim\Http\Util::parseCookieHeader($header);
$this->assertEquals('bar', $result['foo']);
}
}

152
tests/LogTest.php Normal file
View File

@ -0,0 +1,152 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class MyWriter
{
public function write( $object, $level )
{
echo (string) $object;
return true;
}
}
class LogTest extends PHPUnit_Framework_TestCase
{
public function testEnabled()
{
$log = new \Slim\Log(new MyWriter());
$this->assertTrue($log->isEnabled()); //<-- Default case
$log->setEnabled(true);
$this->assertTrue($log->isEnabled());
$log->setEnabled(false);
$this->assertFalse($log->isEnabled());
}
public function testGetLevel()
{
$log = new \Slim\Log(new MyWriter());
$this->assertEquals(\Slim\Log::DEBUG, $log->getLevel());
}
public function testSetLevel()
{
$log = new \Slim\Log(new MyWriter());
$log->setLevel(\Slim\Log::WARN);
$this->assertEquals(\Slim\Log::WARN, $log->getLevel());
}
public function testSetInvalidLevel()
{
$this->setExpectedException('InvalidArgumentException');
$log = new \Slim\Log(new MyWriter());
$log->setLevel(\Slim\Log::DEBUG + 1);
}
public function testLogDebug()
{
$this->expectOutputString('Debug');
$log = new \Slim\Log(new MyWriter());
$result = $log->debug('Debug');
$this->assertTrue($result);
}
public function testLogDebugExcludedByLevel()
{
$log = new \Slim\Log(new MyWriter());
$log->setLevel(\Slim\Log::INFO);
$this->assertFalse($log->debug('Debug'));
}
public function testLogInfo()
{
$this->expectOutputString('Info');
$log = new \Slim\Log(new MyWriter());
$result = $log->info('Info');
$this->assertTrue($result);
}
public function testLogInfoExcludedByLevel()
{
$log = new \Slim\Log(new MyWriter());
$log->setLevel(\Slim\Log::WARN);
$this->assertFalse($log->info('Info'));
}
public function testLogWarn()
{
$this->expectOutputString('Warn');
$log = new \Slim\Log(new MyWriter());
$result = $log->warn('Warn');
$this->assertTrue($result);
}
public function testLogWarnExcludedByLevel()
{
$log = new \Slim\Log(new MyWriter());
$log->setLevel(\Slim\Log::ERROR);
$this->assertFalse($log->warn('Warn'));
}
public function testLogError()
{
$this->expectOutputString('Error');
$log = new \Slim\Log(new MyWriter());
$result = $log->error('Error');
$this->assertTrue($result);
}
public function testLogErrorExcludedByLevel()
{
$log = new \Slim\Log(new MyWriter());
$log->setLevel(\Slim\Log::FATAL);
$this->assertFalse($log->error('Error'));
}
public function testLogFatal()
{
$this->expectOutputString('Fatal');
$log = new \Slim\Log(new MyWriter());
$result = $log->fatal('Fatal');
$this->assertTrue($result);
}
public function testGetAndSetWriter()
{
$writer1 = new MyWriter();
$writer2 = new MyWriter();
$log = new \Slim\Log($writer1);
$this->assertSame($writer1, $log->getWriter());
$log->setWriter($writer2);
$this->assertSame($writer2, $log->getWriter());
}
}

48
tests/LogWriterTest.php Normal file
View File

@ -0,0 +1,48 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class LogWriterTest extends PHPUnit_Framework_TestCase
{
public function testInstantiation()
{
$this->expectOutputString('Hello!' . PHP_EOL);
$handle = fopen('php://output', 'w');
$fw = new \Slim\LogWriter($handle);
$this->assertTrue($fw->write('Hello!') > 0); //<-- Returns number of bytes written if successful
}
public function testInstantiationWithNonResource()
{
$this->setExpectedException('InvalidArgumentException');
$fw = new \Slim\LogWriter(@fopen('/foo/bar.txt', 'w'));
}
}

View File

@ -0,0 +1,161 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class ContentTypesTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
ob_start();
}
public function tearDown()
{
ob_end_clean();
}
/**
* Test parses JSON
*/
public function testParsesJson()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'CONTENT_TYPE' => 'application/json',
'CONENT_LENGTH' => 13,
'slim.input' => '{"foo":"bar"}'
));
$s = new \Slim\Slim();
$s->add(new \Slim\Middleware\ContentTypes());
$s->run();
$body = $s->request()->getBody();
$this->assertTrue(is_array($body));
$this->assertEquals('bar', $body['foo']);
}
/**
* Test ignores JSON with errors
*/
public function testParsesJsonWithError()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'CONTENT_TYPE' => 'application/json',
'CONENT_LENGTH' => 12,
'slim.input' => '{"foo":"bar"' //<-- This should be incorrect!
));
$s = new \Slim\Slim();
$s->add(new \Slim\Middleware\ContentTypes());
$s->run();
$body = $s->request()->getBody();
$this->assertTrue(is_string($body));
$this->assertEquals('{"foo":"bar"', $body);
}
/**
* Test parses XML
*/
public function testParsesXml()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'CONTENT_TYPE' => 'application/xml',
'CONENT_LENGTH' => 68,
'slim.input' => '<books><book><id>1</id><author>Clive Cussler</author></book></books>'
));
$s = new \Slim\Slim();
$s->add(new \Slim\Middleware\ContentTypes());
$s->run();
$body = $s->request()->getBody();
$this->assertInstanceOf('SimpleXMLElement', $body);
$this->assertEquals('Clive Cussler', (string) $body->book->author);
}
/**
* Test ignores XML with errors
*/
public function testParsesXmlWithError()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'CONTENT_TYPE' => 'application/xml',
'CONENT_LENGTH' => 68,
'slim.input' => '<books><book><id>1</id><author>Clive Cussler</book></books>' //<-- This should be incorrect!
));
$s = new \Slim\Slim();
$s->add(new \Slim\Middleware\ContentTypes());
$s->run();
$body = $s->request()->getBody();
$this->assertTrue(is_string($body));
$this->assertEquals('<books><book><id>1</id><author>Clive Cussler</book></books>', $body);
}
/**
* Test parses CSV
*/
public function testParsesCsv()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'CONTENT_TYPE' => 'text/csv',
'CONENT_LENGTH' => 44,
'slim.input' => "John,Doe,000-111-2222\nJane,Doe,111-222-3333"
));
$s = new \Slim\Slim();
$s->add(new \Slim\Middleware\ContentTypes());
$s->run();
$body = $s->request()->getBody();
$this->assertTrue(is_array($body));
$this->assertEquals(2, count($body));
$this->assertEquals('000-111-2222', $body[0][2]);
$this->assertEquals('Doe', $body[1][1]);
}
/**
* Test parses request body based on media-type only, disregarding
* any extra content-type header parameters
*/
public function testParsesRequestBodyWithMediaType()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'CONTENT_TYPE' => 'application/json; charset=ISO-8859-4',
'CONENT_LENGTH' => 13,
'slim.input' => '{"foo":"bar"}'
));
$s = new \Slim\Slim();
$s->add(new \Slim\Middleware\ContentTypes());
$s->run();
$body = $s->request()->getBody();
$this->assertTrue(is_array($body));
$this->assertEquals('bar', $body['foo']);
}
}

View File

@ -0,0 +1,128 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class SlimFlashTest extends PHPUnit_Framework_TestCase
{
/**
* Setup
*/
public function setUp()
{
$_SESSION = array();
}
/**
* Test set flash message for next request
*/
public function testSetFlashForNextRequest()
{
$f = new \Slim\Middleware\Flash();
$f->set('foo', 'bar');
$f->save();
$this->assertEquals('bar', $_SESSION['slim.flash']['foo']);
}
/**
* Test set flash message for current request
*/
public function testSetFlashForCurrentRequest()
{
$f = new \Slim\Middleware\Flash();
$f->now('foo', 'bar');
$this->assertEquals('bar', $f['foo']);
}
/**
* Test loads flash from previous request
*/
public function testLoadsFlashFromPreviousRequest()
{
$_SESSION['slim.flash'] = array('info' => 'foo');
$f = new \Slim\Middleware\Flash();
$f->loadMessages();
$this->assertEquals('foo', $f['info']);
}
/**
* Test keep flash message for next request
*/
public function testKeepFlashFromPreviousRequest()
{
$_SESSION['slim.flash'] = array('info' => 'foo');
$f = new \Slim\Middleware\Flash();
$f->loadMessages();
$f->keep();
$f->save();
$this->assertEquals('foo', $_SESSION['slim.flash']['info']);
}
/**
* Test flash messages from preivous request do not persist to next request
*/
public function testFlashMessagesFromPreviousRequestDoNotPersist()
{
$_SESSION['slim.flash'] = array('info' => 'foo');
$f = new \Slim\Middleware\Flash();
$f->save();
$this->assertEmpty($_SESSION['slim.flash']);
}
/**
* Test set Flash using array access
*/
public function testFlashArrayAccess()
{
$_SESSION['slim.flash'] = array('info' => 'foo');
$f = new \Slim\Middleware\Flash();
$f['info'] = 'bar';
$f->save();
$this->assertTrue(isset($f['info']));
$this->assertEquals('bar', $f['info']);
unset($f['info']);
$this->assertFalse(isset($f['info']));
}
/**
* Test iteration
*/
public function testIteration()
{
$_SESSION['slim.flash'] = array('info' => 'foo', 'error' => 'bar');
$f = new \Slim\Middleware\Flash();
$f->loadMessages();
$output = '';
foreach ($f as $key => $value) {
$output .= $key . $value;
}
$this->assertEquals('infofooerrorbar', $output);
}
}

View File

@ -0,0 +1,149 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
/**
* We use a mock application, instead of a Slim application.
* so that we may easily test the Method Override middleware
* in isolation.
*/
class CustomAppMethod
{
protected $environment;
public function __construct()
{
$this->environment = \Slim\Environment::getInstance();
}
public function &environment() {
return $this->environment;
}
public function call()
{
//Do nothing
}
}
class MethodOverrideTest extends PHPUnit_Framework_TestCase
{
/**
* Test overrides method as POST
*/
public function testOverrideMethodAsPost()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
'CONENT_LENGTH' => 11,
'slim.input' => '_METHOD=PUT'
));
$app = new CustomAppMethod();
$mw = new \Slim\Middleware\MethodOverride();
$mw->setApplication($app);
$mw->setNextMiddleware($app);
$mw->call();
$env =& $app->environment();
$this->assertEquals('PUT', $env['REQUEST_METHOD']);
$this->assertTrue(isset($env['slim.method_override.original_method']));
$this->assertEquals('POST', $env['slim.method_override.original_method']);
}
/**
* Test does not override method if not POST
*/
public function testDoesNotOverrideMethodIfNotPost()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'slim.input' => ''
));
$app = new CustomAppMethod();
$mw = new \Slim\Middleware\MethodOverride();
$mw->setApplication($app);
$mw->setNextMiddleware($app);
$mw->call();
$env =& $app->environment();
$this->assertEquals('GET', $env['REQUEST_METHOD']);
$this->assertFalse(isset($env['slim.method_override.original_method']));
}
/**
* Test does not override method if no method ovveride parameter
*/
public function testDoesNotOverrideMethodAsPostWithoutParameter()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'REMOTE_ADDR' => '127.0.0.1',
'SCRIPT_NAME' => '/foo/index.php', //<-- Physical
'PATH_INFO' => '/bar', //<-- Virtual
'QUERY_STRING' => 'foo=bar',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http',
'slim.input' => '',
'slim.errors' => fopen('php://stderr', 'w')
));
$app = new CustomAppMethod();
$mw = new \Slim\Middleware\MethodOverride();
$mw->setApplication($app);
$mw->setNextMiddleware($app);
$mw->call();
$env =& $app->environment();
$this->assertEquals('POST', $env['REQUEST_METHOD']);
$this->assertFalse(isset($env['slim.method_override.original_method']));
}
/**
* Test overrides method with X-Http-Method-Override header
*/
public function testOverrideMethodAsHeader()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'POST',
'CONTENT_TYPE' => 'application/json',
'CONENT_LENGTH' => 0,
'slim.input' => '',
'X_HTTP_METHOD_OVERRIDE' => 'DELETE'
));
$app = new CustomAppMethod();
$mw = new \Slim\Middleware\MethodOverride();
$mw->setApplication($app);
$mw->setNextMiddleware($app);
$mw->call();
$env =& $app->environment();
$this->assertEquals('DELETE', $env['REQUEST_METHOD']);
$this->assertTrue(isset($env['slim.method_override.original_method']));
$this->assertEquals('POST', $env['slim.method_override.original_method']);
}
}

View File

@ -0,0 +1,125 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class PrettyExceptionsTest extends PHPUnit_Framework_TestCase
{
/**
* Test middleware returns successful response unchanged
*/
public function testReturnsUnchangedSuccessResponse()
{
\Slim\Environment::mock(array(
'SCRIPT_NAME' => '/index.php',
'PATH_INFO' => '/foo'
));
$app = new \Slim\Slim();
$app->get('/foo', function () {
echo "Success";
});
$mw = new \Slim\Middleware\PrettyExceptions();
$mw->setApplication($app);
$mw->setNextMiddleware($app);
$mw->call();
$this->assertEquals(200, $app->response()->status());
$this->assertEquals('Success', $app->response()->body());
}
/**
* Test middleware returns diagnostic screen for error response
*/
public function testReturnsDiagnosticsForErrorResponse()
{
\Slim\Environment::mock(array(
'SCRIPT_NAME' => '/index.php',
'PATH_INFO' => '/foo'
));
$app = new \Slim\Slim(array(
'log.enabled' => false
));
$app->get('/foo', function () {
throw new \Exception('Test Message', 100);
});
$mw = new \Slim\Middleware\PrettyExceptions();
$mw->setApplication($app);
$mw->setNextMiddleware($app);
$mw->call();
$this->assertEquals(1, preg_match('@Slim Application Error@', $app->response()->body()));
$this->assertEquals(500, $app->response()->status());
}
/**
* Test middleware overrides response content type to html
*/
public function testResponseContentTypeIsOverriddenToHtml()
{
\Slim\Environment::mock(array(
'SCRIPT_NAME' => '/index.php',
'PATH_INFO' => '/foo'
));
$app = new \Slim\Slim(array(
'log.enabled' => false
));
$app->get('/foo', function () use ($app) {
$app->contentType('application/json;charset=utf-8'); //<-- set content type to something else
throw new \Exception('Test Message', 100);
});
$mw = new \Slim\Middleware\PrettyExceptions();
$mw->setApplication($app);
$mw->setNextMiddleware($app);
$mw->call();
$response = $app->response();
$this->assertEquals('text/html', $response['Content-Type']);
}
/**
* Test exception type is in response body
*/
public function testExceptionTypeIsInResponseBody()
{
\Slim\Environment::mock(array(
'SCRIPT_NAME' => '/index.php',
'PATH_INFO' => '/foo'
));
$app = new \Slim\Slim(array(
'log.enabled' => false
));
$app->get('/foo', function () use ($app) {
throw new \LogicException('Test Message', 100);
});
$mw = new \Slim\Middleware\PrettyExceptions();
$mw->setApplication($app);
$mw->setNextMiddleware($app);
$mw->call();
$this->assertContains('LogicException', $app->response()->body());
}
}

View File

@ -0,0 +1,115 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class SessionCookieTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
if ( session_id() !== '' ) {
session_unset();
session_destroy();
}
$_SESSION = array();
}
/**
* Test session cookie is set and constructed correctly
*
* We test for two things:
* 1) That the HTTP cookie is added to the `Set-Cookie:` response header;
* 2) That the HTTP cookie is constructed in the expected format;
*/
public function testSessionCookieIsCreatedAndEncrypted()
{
\Slim\Environment::mock(array(
'SCRIPT_NAME' => '/index.php',
'PATH_INFO' => '/foo'
));
$app = new \Slim\Slim();
$app->get('/foo', function () {
echo "Success";
});
$mw = new \Slim\Middleware\SessionCookie(array('expires' => '10 years'));
$mw->setApplication($app);
$mw->setNextMiddleware($app);
$mw->call();
list($status, $header, $body) = $app->response()->finalize();
$matches = array();
preg_match_all('@^slim_session=.+|.+|.+; expires=@', $header['Set-Cookie'], $matches, PREG_SET_ORDER);
$this->assertEquals(1, count($matches));
}
/**
* Test $_SESSION is populated from HTTP cookie
*
* The HTTP cookie in this test was created using the previous test; the encrypted cookie contains
* the serialized array ['foo' => 'bar']. The middleware secret, cipher, and cipher mode are assumed
* to be the default values.
*/
public function testSessionIsPopulatedFromCookie()
{
\Slim\Environment::mock(array(
'SCRIPT_NAME' => '/index.php',
'PATH_INFO' => '/foo',
'COOKIE' => 'slim_session=1644004961%7CLKkYPwqKIMvBK7MWl6D%2BxeuhLuMaW4quN%2F512ZAaVIY%3D%7Ce0f007fa852c7101e8224bb529e26be4d0dfbd63',
));
$app = new \Slim\Slim();
$app->get('/foo', function () {
echo "Success";
});
$mw = new \Slim\Middleware\SessionCookie(array('expires' => '10 years'));
$mw->setApplication($app);
$mw->setNextMiddleware($app);
$mw->call();
$this->assertEquals(array('foo' => 'bar'), $_SESSION);
}
/**
* Test $_SESSION is populated as empty array if no HTTP cookie
*/
public function testSessionIsPopulatedAsEmptyIfNoCookie()
{
\Slim\Environment::mock(array(
'SCRIPT_NAME' => '/index.php',
'PATH_INFO' => '/foo'
));
$app = new \Slim\Slim();
$app->get('/foo', function () {
echo "Success";
});
$mw = new \Slim\Middleware\SessionCookie(array('expires' => '10 years'));
$mw->setApplication($app);
$mw->setNextMiddleware($app);
$mw->call();
$this->assertEquals(array(), $_SESSION);
}
}

86
tests/MiddlewareTest.php Normal file
View File

@ -0,0 +1,86 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class My_Middleware extends \Slim\Middleware
{
public function call()
{
echo "Before";
$this->next->call();
echo "After";
}
}
class My_Application
{
public function call()
{
echo "Application";
}
}
class MiddlewareTest extends PHPUnit_Framework_TestCase
{
/**
* Get and set application
*/
public function testGetAndSetApplication()
{
$app = new My_Application();
$mw = new My_Middleware();
$mw->setApplication($app);
$this->assertSame($app, $mw->getApplication());
}
/**
* Get and set next middleware
*/
public function testGetAndSetNextMiddleware()
{
$mw1 = new My_Middleware();
$mw2 = new My_Middleware();
$mw1->setNextMiddleware($mw2);
$this->assertSame($mw2, $mw1->getNextMiddleware());
}
/**
* Test call
*/
public function testCall()
{
$this->expectOutputString('BeforeApplicationAfter');
$app = new My_Application();
$mw = new My_Middleware();
$mw->setNextMiddleware($app);
$mw->call();
}
}

18
tests/README Normal file
View File

@ -0,0 +1,18 @@
Slim Framework Unit Tests
Follow the directions below to run the Slim Framework unit tests. You'll need the latest version of PHPUnit. To save development time, these unit tests require PHP >= 5.3. However, the Slim Framework itself requires only PHP >= 5.2.
1. Install the latest version of PHPUnit
Visit http://www.phpunit.de/ for installation instructions.
2. Run PHPUnit
From the filesystem directory that contains the `tests` directory, you may run all unit tests or specific unit tests. Here are several examples. The '$>' in the examples below is your command prompt.
To run all tests:
$> phpunit tests
To run all HTTP-related tests:
$> phpunit tests/Http
To run only the HTTP Request tests:
$> phpunit tests/Http/RequestTest

559
tests/RouteTest.php Normal file
View File

@ -0,0 +1,559 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
// Used for passing callable via string
function testCallable() {}
class RouteTest extends PHPUnit_Framework_TestCase
{
/**
* Route should set name
*/
public function testRouteSetsName()
{
$route = new \Slim\Route('/foo/bar', function () {});
$route->name('foo');
$this->assertEquals('foo', $route->getName());
}
/**
* Route should set pattern
*/
public function testRouteSetsPattern()
{
$route1 = new \Slim\Route('/foo/bar', function () {});
$this->assertEquals('/foo/bar', $route1->getPattern());
}
/**
* Route sets pattern with params
*/
public function testRouteSetsPatternWithParams()
{
$route = new \Slim\Route('/hello/:first/:last', function () {});
$this->assertEquals('/hello/:first/:last', $route->getPattern());
}
/**
* Route sets custom pattern that overrides pattern
*/
public function testRouteSetsCustomTemplate()
{
$route = new \Slim\Route('/hello/*', function () {});
$route->setPattern('/hello/:name');
$this->assertEquals('/hello/:name', $route->getPattern());
}
/**
* Route should store a reference to the callable
* anonymous function.
*/
public function testRouteSetsCallableAsFunction()
{
$callable = function () { echo "Foo!"; };
$route = new \Slim\Route('/foo/bar', $callable);
$this->assertSame($callable, $route->getCallable());
}
/**
* Route should store a reference to the callable
* regular function (for PHP 5 < 5.3)
*/
public function testRouteSetsCallableAsString()
{
$route = new \Slim\Route('/foo/bar', 'testCallable');
$this->assertEquals('testCallable', $route->getCallable());
}
/**
* Route should throw exception when creating with an invalid callable
*/
public function testRouteThrowsExecptionWithInvalidCallable()
{
$this->setExpectedException('InvalidArgumentException');
$route = new \Slim\Route('/foo/bar', 'fnDoesNotExist');
}
/**
* Route should throw exception when setting an invalid callable
*/
public function testRouteThrowsExecptionWhenSettingInvalidCallable()
{
$route = new \Slim\Route('/foo/bar', function () {});
try
{
$route->setCallable('fnDoesNotExist');
$this->fail('Did not catch InvalidArgumentException when setting invalid callable');
} catch(\InvalidArgumentException $e) {}
}
/**
* Test gets all params
*/
public function testGetRouteParams()
{
// Prepare route
$requestUri = '/hello/mr/anderson';
$route = new \Slim\Route('/hello/:first/:last', function () {});
// Parse route params
$this->assertTrue($route->matches($requestUri));
// Get params
$params = $route->getParams();
$this->assertEquals(2, count($params));
$this->assertEquals('mr', $params['first']);
$this->assertEquals('anderson', $params['last']);
}
/**
* Test sets all params
*/
public function testSetRouteParams()
{
// Prepare route
$requestUri = '/hello/mr/anderson';
$route = new \Slim\Route('/hello/:first/:last', function () {});
// Parse route params
$this->assertTrue($route->matches($requestUri));
// Get params
$params = $route->getParams();
$this->assertEquals(2, count($params));
$this->assertEquals('mr', $params['first']);
$this->assertEquals('anderson', $params['last']);
// Replace params
$route->setParams(array(
'first' => 'john',
'last' => 'smith'
));
// Get new params
$params = $route->getParams();
$this->assertEquals(2, count($params));
$this->assertEquals('john', $params['first']);
$this->assertEquals('smith', $params['last']);
}
/**
* Test gets param when exists
*/
public function testGetRouteParamWhenExists()
{
// Prepare route
$requestUri = '/hello/mr/anderson';
$route = new \Slim\Route('/hello/:first/:last', function () {});
// Parse route params
$this->assertTrue($route->matches($requestUri));
// Get param
$this->assertEquals('anderson', $route->getParam('last'));
}
/**
* Test gets param when not exists
*/
public function testGetRouteParamWhenNotExists()
{
// Prepare route
$requestUri = '/hello/mr/anderson';
$route = new \Slim\Route('/hello/:first/:last', function () {});
// Parse route params
$this->assertTrue($route->matches($requestUri));
// Get param
try {
$param = $route->getParam('foo');
$this->fail('Did not catch expected InvalidArgumentException');
} catch ( \InvalidArgumentException $e ) {}
}
/**
* Test sets param when exists
*/
public function testSetRouteParamWhenExists()
{
// Prepare route
$requestUri = '/hello/mr/anderson';
$route = new \Slim\Route('/hello/:first/:last', function () {});
// Parse route params
$this->assertTrue($route->matches($requestUri));
// Get param
$this->assertEquals('anderson', $route->getParam('last'));
// Set param
$route->setParam('last', 'smith');
// Get new param
$this->assertEquals('smith', $route->getParam('last'));
}
/**
* Test sets param when not exists
*/
public function testSetRouteParamWhenNotExists()
{
// Prepare route
$requestUri = '/hello/mr/anderson';
$route = new \Slim\Route('/hello/:first/:last', function () {});
// Parse route params
$this->assertTrue($route->matches($requestUri));
// Get param
try {
$param = $route->setParam('foo', 'bar');
$this->fail('Did not catch expected InvalidArgumentException');
} catch ( \InvalidArgumentException $e ) {}
}
/**
* If route matches a resource URI, param should be extracted.
*/
public function testRouteMatchesAndParamExtracted()
{
$resource = '/hello/Josh';
$route = new \Slim\Route('/hello/:name', function () {});
$result = $route->matches($resource);
$this->assertTrue($result);
$this->assertEquals(array('name' => 'Josh'), $route->getParams());
}
/**
* If route matches a resource URI, multiple params should be extracted.
*/
public function testRouteMatchesAndMultipleParamsExtracted()
{
$resource = '/hello/Josh/and/John';
$route = new \Slim\Route('/hello/:first/and/:second', function () {});
$result = $route->matches($resource);
$this->assertTrue($result);
$this->assertEquals(array('first' => 'Josh', 'second' => 'John'), $route->getParams());
}
/**
* If route does not match a resource URI, params remain an empty array
*/
public function testRouteDoesNotMatchAndParamsNotExtracted()
{
$resource = '/foo/bar';
$route = new \Slim\Route('/hello/:name', function () {});
$result = $route->matches($resource);
$this->assertFalse($result);
$this->assertEquals(array(), $route->getParams());
}
/**
* Route matches URI with trailing slash
*
*/
public function testRouteMatchesWithTrailingSlash()
{
$resource1 = '/foo/bar/';
$resource2 = '/foo/bar';
$route = new \Slim\Route('/foo/:one/', function () {});
$this->assertTrue($route->matches($resource1));
$this->assertTrue($route->matches($resource2));
}
/**
* Route matches URI with conditions
*/
public function testRouteMatchesResourceWithConditions()
{
$resource = '/hello/Josh/and/John';
$route = new \Slim\Route('/hello/:first/and/:second', function () {});
$route->conditions(array('first' => '[a-zA-Z]{3,}'));
$result = $route->matches($resource);
$this->assertTrue($result);
$this->assertEquals(array('first' => 'Josh', 'second' => 'John'), $route->getParams());
}
/**
* Route does not match URI with conditions
*/
public function testRouteDoesNotMatchResourceWithConditions()
{
$resource = '/hello/Josh/and/John';
$route = new \Slim\Route('/hello/:first/and/:second', function () {});
$route->conditions(array('first' => '[a-z]{3,}'));
$result = $route->matches($resource);
$this->assertFalse($result);
$this->assertEquals(array(), $route->getParams());
}
/*
* Route should match URI with valid path component according to rfc2396
*
* "Uniform Resource Identifiers (URI): Generic Syntax" http://www.ietf.org/rfc/rfc2396.txt
*
* Excludes "+" which is valid but decodes into a space character
*/
public function testRouteMatchesResourceWithValidRfc2396PathComponent()
{
$symbols = ':@&=$,';
$resource = '/rfc2386/' . $symbols;
$route = new \Slim\Route('/rfc2386/:symbols', function () {});
$result = $route->matches($resource);
$this->assertTrue($result);
$this->assertEquals(array('symbols' => $symbols), $route->getParams());
}
/*
* Route should match URI including unreserved punctuation marks from rfc2396
*
* "Uniform Resource Identifiers (URI): Generic Syntax" http://www.ietf.org/rfc/rfc2396.txt
*/
public function testRouteMatchesResourceWithUnreservedMarks()
{
$marks = "-_.!~*'()";
$resource = '/marks/' . $marks;
$route = new \Slim\Route('/marks/:marks', function () {});
$result = $route->matches($resource);
$this->assertTrue($result);
$this->assertEquals(array('marks' => $marks), $route->getParams());
}
/**
* Route optional parameters
*
* Pre-conditions:
* Route pattern requires :year, optionally accepts :month and :day
*
* Post-conditions:
* All: Year is 2010
* Case A: Month and day default values are used
* Case B: Month is "05" and day default value is used
* Case C: Month is "05" and day is "13"
*/
public function testRouteOptionalParameters()
{
$pattern = '/archive/:year(/:month(/:day))';
//Case A
$routeA = new \Slim\Route($pattern, function () {});
$resourceA = '/archive/2010';
$resultA = $routeA->matches($resourceA);
$this->assertTrue($resultA);
$this->assertEquals(array('year' => '2010'), $routeA->getParams());
//Case B
$routeB = new \Slim\Route($pattern, function () {});
$resourceB = '/archive/2010/05';
$resultB = $routeB->matches($resourceB);
$this->assertTrue($resultB);
$this->assertEquals(array('year' => '2010', 'month' => '05'), $routeB->getParams());
//Case C
$routeC = new \Slim\Route($pattern, function () {});
$resourceC = '/archive/2010/05/13';
$resultC = $routeC->matches($resourceC);
$this->assertTrue($resultC);
$this->assertEquals(array('year' => '2010', 'month' => '05', 'day' => '13'), $routeC->getParams());
}
/**
* Test route default conditions
*
* Pre-conditions:
* Route class has default conditions;
*
* Post-conditions:
* Case A: Route instance has default conditions;
* Case B: Route instance has newly merged conditions;
*/
public function testRouteDefaultConditions()
{
\Slim\Route::setDefaultConditions(array('id' => '\d+'));
$r = new \Slim\Route('/foo', function () {});
//Case A
$this->assertEquals(\Slim\Route::getDefaultConditions(), $r->getConditions());
//Case B
$r->conditions(array('name' => '[a-z]{2,5}'));
$c = $r->getConditions();
$this->assertArrayHasKey('id', $c);
$this->assertArrayHasKey('name', $c);
}
/**
* Route matches URI with wildcard
*/
public function testRouteMatchesResourceWithWildcard()
{
$resource = '/hello/foo/bar/world';
$route = new \Slim\Route('/hello/:path+/world', function () {});
$result = $route->matches($resource);
$this->assertTrue($result);
$this->assertEquals(array('path'=>array('foo', 'bar')), $route->getParams());
}
/**
* Route matches URI with more than one wildcard
*/
public function testRouteMatchesResourceWithMultipleWildcards()
{
$resource = '/hello/foo/bar/world/2012/03/10';
$route = new \Slim\Route('/hello/:path+/world/:date+', function () {});
$result = $route->matches($resource);
$this->assertTrue($result);
$this->assertEquals(array('path'=>array('foo', 'bar'), 'date'=>array('2012', '03', '10')), $route->getParams());
}
/**
* Route matches URI with wildcards and parameters
*/
public function testRouteMatchesResourceWithWildcardsAndParams()
{
$resource = '/hello/foo/bar/world/2012/03/10/first/second';
$route = new \Slim\Route('/hello/:path+/world/:year/:month/:day/:path2+', function () {});
$result = $route->matches($resource);
$this->assertTrue($result);
$this->assertEquals(array('path'=>array('foo', 'bar'), 'year'=>'2012', 'month'=>'03', 'day'=>'10', 'path2'=>array('first', 'second')), $route->getParams());
}
/**
* Route matches URI with optional wildcard and parameter
*/
public function testRouteMatchesResourceWithOptionalWildcardsAndParams()
{
$resourceA = '/hello/world/foo/bar';
$routeA = new \Slim\Route('/hello(/:world(/:path+))', function () {});
$this->assertTrue($routeA->matches($resourceA));
$this->assertEquals(array('world'=>'world', 'path'=>array('foo', 'bar')), $routeA->getParams());
$resourceB = '/hello/world';
$routeB = new \Slim\Route('/hello(/:world(/:path))', function () {});
$this->assertTrue($routeB->matches($resourceB));
$this->assertEquals(array('world'=>'world'), $routeB->getParams());
}
/**
* Route does not match URI with wildcard
*/
public function testRouteDoesNotMatchResourceWithWildcard()
{
$resource = '/hello';
$route = new \Slim\Route('/hello/:path+', function () {});
$result = $route->matches($resource);
$this->assertFalse($result);
$this->assertEquals(array(), $route->getParams());
}
/**
* Test route sets and gets middleware
*
* Pre-conditions:
* Route instantiated
*
* Post-conditions:
* Case A: Middleware set as callable, not array
* Case B: Middleware set after other middleware already set
* Case C: Middleware set as array of callables
* Case D: Middleware set as a callable array
* Case E: Middleware is invalid; throws InvalidArgumentException
* Case F: Middleware is an array with one invalid callable; throws InvalidArgumentException
*/
public function testRouteMiddleware()
{
$callable1 = function () {};
$callable2 = function () {};
//Case A
$r1 = new \Slim\Route('/foo', function () {});
$r1->setMiddleware($callable1);
$mw = $r1->getMiddleware();
$this->assertInternalType('array', $mw);
$this->assertEquals(1, count($mw));
//Case B
$r1->setMiddleware($callable2);
$mw = $r1->getMiddleware();
$this->assertEquals(2, count($mw));
//Case C
$r2 = new \Slim\Route('/foo', function () {});
$r2->setMiddleware(array($callable1, $callable2));
$mw = $r2->getMiddleware();
$this->assertInternalType('array', $mw);
$this->assertEquals(2, count($mw));
//Case D
$r3 = new \Slim\Route('/foo', function () {});
$r3->setMiddleware(array($this, 'callableTestFunction'));
$mw = $r3->getMiddleware();
$this->assertInternalType('array', $mw);
$this->assertEquals(1, count($mw));
//Case E
try {
$r3->setMiddleware('sdjfsoi788');
$this->fail('Did not catch InvalidArgumentException when setting invalid route middleware');
} catch ( \InvalidArgumentException $e ) {}
//Case F
try {
$r3->setMiddleware(array($callable1, $callable2, 'sdjfsoi788'));
$this->fail('Did not catch InvalidArgumentException when setting an array with one invalid route middleware');
} catch ( \InvalidArgumentException $e ) {}
}
public function callableTestFunction() {}
/**
* Test that a Route manages the HTTP methods that it supports
*
* Case A: Route initially supports no HTTP methods
* Case B: Route can set its supported HTTP methods
* Case C: Route can append supported HTTP methods
* Case D: Route can test if it supports an HTTP method
* Case E: Route can lazily declare supported HTTP methods with `via`
*/
public function testHttpMethods()
{
//Case A
$r = new \Slim\Route('/foo', function () {});
$this->assertEmpty($r->getHttpMethods());
//Case B
$r->setHttpMethods('GET');
$this->assertEquals(array('GET'), $r->getHttpMethods());
//Case C
$r->appendHttpMethods('POST', 'PUT');
$this->assertEquals(array('GET', 'POST', 'PUT'), $r->getHttpMethods());
//Case D
$this->assertTrue($r->supportsHttpMethod('GET'));
$this->assertFalse($r->supportsHttpMethod('DELETE'));
//Case E
$viaResult = $r->via('DELETE');
$this->assertTrue($viaResult instanceof \Slim\Route);
$this->assertTrue($r->supportsHttpMethod('DELETE'));
}
}

602
tests/RouterTest.php Normal file
View File

@ -0,0 +1,602 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class RouterTest extends PHPUnit_Framework_TestCase
{
protected $env;
protected $req;
protected $res;
public function setUp()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'REMOTE_ADDR' => '127.0.0.1',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/bar', //<-- Virtual
'QUERY_STRING' => 'one=1&two=2&three=3',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http',
'slim.input' => '',
'slim.errors' => fopen('php://stderr', 'w'),
'HTTP_HOST' => 'slim'
));
$this->env = \Slim\Environment::getInstance();
$this->req = new \Slim\Http\Request($this->env);
$this->res = new \Slim\Http\Response();
}
/**
* Router::urlFor should return a full route pattern
* even if no params data is provided.
*/
public function testUrlForNamedRouteWithoutParams()
{
$router = new \Slim\Router();
$route = $router->map('/foo/bar', function () {})->via('GET');
$router->addNamedRoute('foo', $route);
$this->assertEquals('/foo/bar', $router->urlFor('foo'));
}
/**
* Router::urlFor should return a full route pattern if
* param data is provided.
*/
public function testUrlForNamedRouteWithParams()
{
$router = new \Slim\Router();
$route = $router->map('/foo/:one/and/:two', function ($one, $two) {})->via('GET');
$router->addNamedRoute('foo', $route);
$this->assertEquals('/foo/Josh/and/John', $router->urlFor('foo', array('one' => 'Josh', 'two' => 'John')));
}
/**
* Router::urlFor should throw an exception if Route with name
* does not exist.
* @expectedException \RuntimeException
*/
public function testUrlForNamedRouteThatDoesNotExist()
{
$router = new \Slim\Router();
$route = $router->map('/foo/bar', function () {})->via('GET');
$router->addNamedRoute('bar', $route);
$router->urlFor('foo');
}
/**
* Router::addNamedRoute should throw an exception if named Route
* with same name already exists.
*/
public function testNamedRouteWithExistingName()
{
$this->setExpectedException('\RuntimeException');
$router = new \Slim\Router();
$route1 = $router->map('/foo/bar', function () {})->via('GET');
$route2 = $router->map('/foo/bar/2', function () {})->via('GET');
$router->addNamedRoute('bar', $route1);
$router->addNamedRoute('bar', $route2);
}
/**
* Test if named route exists
*
* Pre-conditions:
* Slim app instantiated;
* Named route created;
*
* Post-conditions:
* Named route found to exist;
* Non-existant route found not to exist;
*/
public function testHasNamedRoute()
{
$router = new \Slim\Router();
$route = $router->map('/foo', function () {})->via('GET');
$router->addNamedRoute('foo', $route);
$this->assertTrue($router->hasNamedRoute('foo'));
$this->assertFalse($router->hasNamedRoute('bar'));
}
/**
* Test Router gets named route
*
* Pre-conditions;
* Slim app instantiated;
* Named route created;
*
* Post-conditions:
* Named route fetched by named;
* NULL is returned if named route does not exist;
*/
public function testGetNamedRoute()
{
$router = new \Slim\Router();
$route1 = $router->map('/foo', function () {})->via('GET');
$router->addNamedRoute('foo', $route1);
$this->assertSame($route1, $router->getNamedRoute('foo'));
$this->assertNull($router->getNamedRoute('bar'));
}
/**
* Test external iterator for Router's named routes
*
* Pre-conditions:
* Slim app instantiated;
* Named routes created;
*
* Post-conditions:
* Array iterator returned for named routes;
*/
public function testGetNamedRoutes()
{
$router = new \Slim\Router();
$route1 = $router->map('/foo', function () {})->via('GET');
$route2 = $router->map('/bar', function () {})->via('POST');
$router->addNamedRoute('foo', $route1);
$router->addNamedRoute('bar', $route2);
$namedRoutesIterator = $router->getNamedRoutes();
$this->assertInstanceOf('ArrayIterator', $namedRoutesIterator);
$this->assertEquals(2, $namedRoutesIterator->count());
}
/**
* Router considers HEAD requests as GET requests
*/
public function testRouterConsidersHeadAsGet()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'HEAD',
'REMOTE_ADDR' => '127.0.0.1',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/bar', //<-- Virtual
'QUERY_STRING' => 'one=1&two=2&three=3',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http',
'slim.input' => '',
'slim.errors' => fopen('php://stderr', 'w'),
'HTTP_HOST' => 'slim'
));
$this->env = \Slim\Environment::getInstance();
$this->req = new \Slim\Http\Request($this->env);
$this->res = new \Slim\Http\Response();
$router = new \Slim\Router();
$route = $router->map('/bar', function () {})->via('GET', 'HEAD');
$numberOfMatchingRoutes = count($router->getMatchedRoutes($this->req->getMethod(), $this->req->getResourceUri()));
$this->assertEquals(1, $numberOfMatchingRoutes);
}
/**
* Router::urlFor
*/
public function testRouterUrlFor()
{
$router = new \Slim\Router();
$route1 = $router->map('/foo/bar', function () {})->via('GET');
$route2 = $router->map('/foo/:one/:two', function () {})->via('GET');
$route3 = $router->map('/foo/:one(/:two)', function () {})->via('GET');
$route4 = $router->map('/foo/:one/(:two/)', function () {})->via('GET');
$route5 = $router->map('/foo/:one/(:two/(:three/))', function () {})->via('GET');
$route6 = $router->map('/foo/:path+/bar', function (){})->via('GET');
$route7 = $router->map('/foo/:path+/:path2+/bar', function (){})->via('GET');
$route8 = $router->map('/foo/:path+', function (){})->via('GET');
$route9 = $router->map('/foo/:var/:var2', function (){})->via('GET');
$route1->setName('route1');
$route2->setName('route2');
$route3->setName('route3');
$route4->setName('route4');
$route5->setName('route5');
$route6->setName('route6');
$route7->setName('route7');
$route8->setName('route8');
$route9->setName('route9');
//Route
$this->assertEquals('/foo/bar', $router->urlFor('route1'));
//Route with params
$this->assertEquals('/foo/foo/bar', $router->urlFor('route2', array('one' => 'foo', 'two' => 'bar')));
$this->assertEquals('/foo/foo/:two', $router->urlFor('route2', array('one' => 'foo')));
$this->assertEquals('/foo/:one/bar', $router->urlFor('route2', array('two' => 'bar')));
//Route with params and optional segments
$this->assertEquals('/foo/foo/bar', $router->urlFor('route3', array('one' => 'foo', 'two' => 'bar')));
$this->assertEquals('/foo/foo', $router->urlFor('route3', array('one' => 'foo')));
$this->assertEquals('/foo/:one/bar', $router->urlFor('route3', array('two' => 'bar')));
$this->assertEquals('/foo/:one', $router->urlFor('route3'));
//Route with params and optional segments
$this->assertEquals('/foo/foo/bar/', $router->urlFor('route4', array('one' => 'foo', 'two' => 'bar')));
$this->assertEquals('/foo/foo/', $router->urlFor('route4', array('one' => 'foo')));
$this->assertEquals('/foo/:one/bar/', $router->urlFor('route4', array('two' => 'bar')));
$this->assertEquals('/foo/:one/', $router->urlFor('route4'));
//Route with params and optional segments
$this->assertEquals('/foo/foo/bar/what/', $router->urlFor('route5', array('one' => 'foo', 'two' => 'bar', 'three' => 'what')));
$this->assertEquals('/foo/foo/', $router->urlFor('route5', array('one' => 'foo')));
$this->assertEquals('/foo/:one/bar/', $router->urlFor('route5', array('two' => 'bar')));
$this->assertEquals('/foo/:one/bar/what/', $router->urlFor('route5', array('two' => 'bar', 'three' => 'what')));
$this->assertEquals('/foo/:one/', $router->urlFor('route5'));
//Route with wildcard params
$this->assertEquals('/foo/bar/bar', $router->urlFor('route6', array('path'=>'bar')));
$this->assertEquals('/foo/foo/bar/bar', $router->urlFor('route7', array('path'=>'foo', 'path2'=>'bar')));
$this->assertEquals('/foo/bar', $router->urlFor('route8', array('path'=>'bar')));
//Route with similar param names, test greedy matching
$this->assertEquals('/foo/1/2', $router->urlFor('route9', array('var2'=>'2', 'var'=>'1')));
$this->assertEquals('/foo/1/2', $router->urlFor('route9', array('var'=>'1', 'var2'=>'2')));
}
/**
* Test that router returns no matches when neither HTTP method nor URI match.
*/
public function testRouterMatchesRoutesNone()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'REMOTE_ADDR' => '127.0.0.1',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/foo', //<-- Virtual
'QUERY_STRING' => 'one=1&two=2&three=3',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http',
'slim.input' => '',
'slim.errors' => fopen('php://stderr', 'w'),
'HTTP_HOST' => 'slim'
));
$this->env = \Slim\Environment::getInstance();
$this->req = new \Slim\Http\Request($this->env);
$this->res = new \Slim\Http\Response();
$router = new \Slim\Router();
$router->map('/bar', function () {})->via('POST');
$router->map('/foo', function () {})->via('POST');
$router->map('/foo', function () {})->via('PUT');
$router->map('/foo/bar/xyz', function () {})->via('DELETE');
$this->assertEquals(0, count($router->getMatchedRoutes($this->req->getMethod(), $this->req->getResourceUri())));
}
/**
* Test that router returns no matches when HTTP method matches but URI does not.
*/
public function testRouterMatchesRoutesNoneWhenMethodMatchesUriDoesNot()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'REMOTE_ADDR' => '127.0.0.1',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/foo', //<-- Virtual
'QUERY_STRING' => 'one=1&two=2&three=3',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http',
'slim.input' => '',
'slim.errors' => fopen('php://stderr', 'w'),
'HTTP_HOST' => 'slim'
));
$this->env = \Slim\Environment::getInstance();
$this->req = new \Slim\Http\Request($this->env);
$this->res = new \Slim\Http\Response();
$router = new \Slim\Router();
$router->map('/fooNOMATCH', function () {})->via('GET');
$router->map('/foo', function () {})->via('POST');
$router->map('/foo', function () {})->via('PUT');
$router->map('/foo/bar/xyz', function () {})->via('DELETE');
$this->assertEquals(0, count($router->getMatchedRoutes($this->req->getMethod(), $this->req->getResourceUri())));
}
/**
* Test that router returns no matches when HTTP method does not match but URI does.
*/
public function testRouterMatchesRoutesNoneWhenMethodDoesNotMatchUriMatches()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'REMOTE_ADDR' => '127.0.0.1',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/foo', //<-- Virtual
'QUERY_STRING' => 'one=1&two=2&three=3',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http',
'slim.input' => '',
'slim.errors' => fopen('php://stderr', 'w'),
'HTTP_HOST' => 'slim'
));
$this->env = \Slim\Environment::getInstance();
$this->req = new \Slim\Http\Request($this->env);
$this->res = new \Slim\Http\Response();
$router = new \Slim\Router();
$router->map('/foo', function () {})->via('OPTIONS');
$router->map('/foo', function () {})->via('POST');
$router->map('/foo', function () {})->via('PUT');
$router->map('/foo/bar/xyz', function () {})->via('DELETE');
$this->assertEquals(0, count($router->getMatchedRoutes($this->req->getMethod(), $this->req->getResourceUri())));
}
/**
* Test that router returns matched routes based on HTTP method and URI.
*/
public function testRouterMatchesRoutes()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'REMOTE_ADDR' => '127.0.0.1',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/foo', //<-- Virtual
'QUERY_STRING' => 'one=1&two=2&three=3',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http',
'slim.input' => '',
'slim.errors' => fopen('php://stderr', 'w'),
'HTTP_HOST' => 'slim'
));
$this->env = \Slim\Environment::getInstance();
$this->req = new \Slim\Http\Request($this->env);
$this->res = new \Slim\Http\Response();
$router = new \Slim\Router();
$router->map('/foo', function () {})->via('GET');
$router->map('/foo', function () {})->via('POST');
$router->map('/foo', function () {})->via('PUT');
$router->map('/foo/bar/xyz', function () {})->via('DELETE');
$this->assertEquals(1, count($router->getMatchedRoutes($this->req->getMethod(), $this->req->getResourceUri())));
}
/**
* Test get current route
*/
public function testGetCurrentRoute()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/foo' //<-- Virtual
));
$app = new \Slim\Slim();
$route1 = $app->get('/bar', function () {
echo "Bar";
});
$route2 = $app->get('/foo', function () {
echo "Foo";
});
$app->call();
$this->assertSame($route2, $app->router()->getCurrentRoute());
}
/**
* Test calling get current route before routing doesn't cause errors
*/
public function testGetCurrentRouteBeforeRoutingDoesntError()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/foo' //<-- Virtual
));
$app = new \Slim\Slim();
$route1 = $app->get('/bar', function () {
echo "Bar";
});
$route2 = $app->get('/foo', function () {
echo "Foo";
});
$app->router()->getCurrentRoute();
$app->call();
}
/**
* Test get current route before routing returns null
*/
public function testGetCurrentRouteBeforeRoutingReturnsNull()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/foo' //<-- Virtual
));
$app = new \Slim\Slim();
$route1 = $app->get('/bar', function () {
echo "Bar";
});
$route2 = $app->get('/foo', function () {
echo "Foo";
});
$this->assertSame(null, $app->router()->getCurrentRoute());
}
/**
* Test get current route during slim.before.dispatch hook
*/
public function testGetCurrentRouteDuringBeforeDispatchHook()
{
$route = null;
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/foo' //<-- Virtual
));
$app = new \Slim\Slim();
$app->hook('slim.before.dispatch', function() use(&$route, $app) {
$route = $app->router()->getCurrentRoute();
});
$route1 = $app->get('/bar', function () {
echo "Bar";
});
$route2 = $app->get('/foo', function () {
echo "Foo";
});
$app->call();
$this->assertSame($route2, $route);
}
/**
* Test get current route during routing
*/
public function testGetCurrentRouteDuringRouting()
{
$route = null;
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/foo' //<-- Virtual
));
$app = new \Slim\Slim();
$route1 = $app->get('/bar', function () {
echo "Bar";
});
$route2 = $app->get('/foo', function () use (&$route, $app) {
echo "Foo";
$route = $app->router()->getCurrentRoute();
});
$app->call();
$this->assertSame($route2, $route);
}
/**
* Test get current route after routing
*/
public function testGetCurrentRouteAfterRouting()
{
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/foo' //<-- Virtual
));
$app = new \Slim\Slim();
$route1 = $app->get('/bar', function () {
echo "Bar";
});
$route2 = $app->get('/foo', function () {
echo "Foo";
});
$app->call();
$this->assertSame($route2, $app->router()->getCurrentRoute());
}
public function testDispatch()
{
$this->expectOutputString('Hello josh');
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'REMOTE_ADDR' => '127.0.0.1',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/hello/josh', //<-- Virtual
'QUERY_STRING' => 'one=1&two=2&three=3',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http',
'slim.input' => '',
'slim.errors' => fopen('php://stderr', 'w'),
'HTTP_HOST' => 'slim'
));
$env = \Slim\Environment::getInstance();
$req = new \Slim\Http\Request($env);
$router = new \Slim\Router();
$route = new \Slim\Route('/hello/:name', function ($name) { echo "Hello $name"; });
$route->matches($req->getResourceUri()); //<-- Extracts params from resource URI
$router->dispatch($route);
}
public function testDispatchWithMiddlware()
{
$this->expectOutputString('First! Second! Hello josh');
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'REMOTE_ADDR' => '127.0.0.1',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/hello/josh', //<-- Virtual
'QUERY_STRING' => 'one=1&two=2&three=3',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http',
'slim.input' => '',
'slim.errors' => fopen('php://stderr', 'w'),
'HTTP_HOST' => 'slim'
));
$env = \Slim\Environment::getInstance();
$req = new \Slim\Http\Request($env);
$router = new \Slim\Router();
$route = new \Slim\Route('/hello/:name', function ($name) { echo "Hello $name"; });
$route->setMiddleware(function () {
echo "First! ";
});
$route->setMiddleware(function () {
echo "Second! ";
});
$route->matches($req->getResourceUri()); //<-- Extracts params from resource URI
$router->dispatch($route);
}
public function testRouteMiddlwareArguments()
{
$this->expectOutputString('foobar');
\Slim\Environment::mock(array(
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/foo' //<-- Virtual
));
$env = \Slim\Environment::getInstance();
$req = new \Slim\Http\Request($env);
$router = new \Slim\Router();
$route = new \Slim\Route('/foo', function () { echo "bar"; });
$route->setName('foo');
$route->setMiddleware(function ($route) {
echo $route->getName();
});
$route->matches($req->getResourceUri()); //<-- Extracts params from resource URI
$router->dispatch($route);
}
public function testDispatchWithoutCallable()
{
$this->setExpectedException('InvalidArgumentException');
\Slim\Environment::mock(array(
'REQUEST_METHOD' => 'GET',
'REMOTE_ADDR' => '127.0.0.1',
'SCRIPT_NAME' => '', //<-- Physical
'PATH_INFO' => '/hello/josh', //<-- Virtual
'QUERY_STRING' => 'one=1&two=2&three=3',
'SERVER_NAME' => 'slim',
'SERVER_PORT' => 80,
'slim.url_scheme' => 'http',
'slim.input' => '',
'slim.errors' => fopen('php://stderr', 'w'),
'HTTP_HOST' => 'slim'
));
$env = \Slim\Environment::getInstance();
$req = new \Slim\Http\Request($env);
$router = new \Slim\Router();
$route = new \Slim\Route('/hello/:name', 'foo'); // <-- Fail fast
}
}

1492
tests/SlimTest.php Normal file

File diff suppressed because it is too large Load Diff

195
tests/ViewTest.php Normal file
View File

@ -0,0 +1,195 @@
<?php
/**
* Slim - a micro PHP 5 framework
*
* @author Josh Lockhart <info@slimframework.com>
* @copyright 2011 Josh Lockhart
* @link http://www.slimframework.com
* @license http://www.slimframework.com/license
* @version 2.2.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.
*/
class ViewTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->view = new \Slim\View();
}
public function generateTestData()
{
return array('a' => 1, 'b' => 2, 'c' => 3);
}
/**
* Test initial View data is an empty array
*
* Pre-conditions:
* None
*
* Post-conditions:
* The View object's data attribute is an empty array
*/
public function testViewIsConstructedWithDataArray()
{
$this->assertEquals(array(), $this->view->getData());
}
/**
* Test View sets and gets data
*
* Pre-conditions:
* Case A: Set view data key/value
* Case B: Set view data as array
* Case C: Set view data with one argument that is not an array
*
* Post-conditions:
* Case A: Data key/value are set
* Case B: Data is set to array
* Case C: An InvalidArgumentException is thrown
*/
public function testViewSetAndGetData()
{
//Case A
$this->view->setData('one', 1);
$this->assertEquals(1, $this->view->getData('one'));
//Case B
$data = array('foo' => 'bar', 'a' => 'A');
$this->view->setData($data);
$this->assertSame($data, $this->view->getData());
//Case C
try {
$this->view->setData('foo');
$this->fail('Setting View data with non-array single argument did not throw exception');
} catch ( \InvalidArgumentException $e ) {}
}
/**
* Test View appends data
*
* Pre-conditions:
* Case A: Append data to View several times
* Case B: Append view data which is not an array
*
* Post-conditions:
* Case A: The View data contains all appended data
* Case B: An InvalidArgumentException is thrown
*/
public function testViewAppendsData()
{
//Case A
$this->view->appendData(array('a' => 'A'));
$this->view->appendData(array('b' => 'B'));
$this->assertEquals(array('a' => 'A', 'b' => 'B'), $this->view->getData());
//Case B
try {
$this->view->appendData('not an array');
$this->fail('Appending View data with non-array argument did not throw exception');
} catch ( \InvalidArgumentException $e ) {}
}
/**
* Test View templates directory
*
* Pre-conditions:
* View templates directory is set to an existing directory
*
* Post-conditions:
* The templates directory is set correctly.
*/
public function testSetsTemplatesDirectory()
{
$templatesDirectory = dirname(__FILE__) . '/templates';
$this->view->setTemplatesDirectory($templatesDirectory);
$this->assertEquals($templatesDirectory, $this->view->getTemplatesDirectory());
}
/**
* Test View templates directory may have a trailing slash when set
*
* Pre-conditions:
* View templates directory is set to an existing directory with a trailing slash
*
* Post-conditions:
* The View templates directory is set correctly without a trailing slash
*/
public function testTemplatesDirectoryWithTrailingSlash()
{
$this->view->setTemplatesDirectory(dirname(__FILE__) . '/templates/');
$this->assertEquals(dirname(__FILE__) . '/templates', $this->view->getTemplatesDirectory());
}
/**
* Test View renders template
*
* Pre-conditions:
* View templates directory is set to an existing directory.
* View data is set without errors
* Case A: View renders an existing template
* Case B: View renders a non-existing template
*
* Post-conditions:
* Case A: The rendered template is returned as a string
* Case B: A RuntimeException is thrown
*/
public function testRendersTemplateWithData()
{
$this->view->setTemplatesDirectory(dirname(__FILE__) . '/templates');
$this->view->setData(array('foo' => 'bar'));
//Case A
$output = $this->view->render('test.php');
$this->assertEquals('test output bar', $output);
//Case B
try {
$output = $this->view->render('foo.php');
$this->fail('Rendering non-existent template did not throw exception');
} catch ( \RuntimeException $e ) {}
}
/**
* Test View displays template
*
* Pre-conditions:
* View templates directory is set to an existing directory.
* View data is set without errors
* View is displayed
*
* Post-conditions:
* The output buffer contains the rendered template
*/
public function testDisplaysTemplateWithData()
{
$this->expectOutputString('test output bar');
$this->view->setTemplatesDirectory(dirname(__FILE__) . '/templates');
$this->view->setData(array('foo' => 'bar'));
$this->view->display('test.php');
}
}

19
tests/bootstrap.php Normal file
View File

@ -0,0 +1,19 @@
<?php
set_include_path(dirname(__FILE__) . '/../' . PATH_SEPARATOR . get_include_path());
require_once 'Slim/Slim.php';
// Register Slim's autoloader
\Slim\Slim::registerAutoloader();
//Register non-Slim autoloader
function customAutoLoader( $class )
{
$file = rtrim(dirname(__FILE__), '/') . '/' . $class . '.php';
if ( file_exists($file) ) {
require $file;
} else {
return;
}
}
spl_autoload_register('customAutoLoader');

1
tests/templates/test.php Normal file
View File

@ -0,0 +1 @@
test output <?php echo $foo; ?>