From a06b65da80887adb2c41ad477c4bc87181c7828e Mon Sep 17 00:00:00 2001 From: silentworks Date: Mon, 8 Oct 2012 14:56:46 +0100 Subject: [PATCH 01/66] Updated the default index.php file to use the use statement instead of writing the namespace over and over. --- index.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/index.php b/index.php index c3e67454c..d1720dadf 100644 --- a/index.php +++ b/index.php @@ -9,7 +9,9 @@ */ require 'Slim/Slim.php'; -\Slim\Slim::registerAutoloader(); +use \Slim\Slim; + +Slim::registerAutoloader(); /** * Step 2: Instantiate a Slim application @@ -19,7 +21,7 @@ * your Slim application now by passing an associative array * of setting names and values into the application constructor. */ -$app = new \Slim\Slim(); +$app = new Slim(); /** * Step 3: Define the Slim application routes From cdacca5487c3b6f969bceb477ac4089dfd9b1dc9 Mon Sep 17 00:00:00 2001 From: Marchenko Alexandr Date: Fri, 12 Oct 2012 10:40:51 +0300 Subject: [PATCH 02/66] IIS Web.config for routing added --- README.markdown | 22 ++++++++++++++++++++++ Web.config | 17 +++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 Web.config diff --git a/README.markdown b/README.markdown index 5c21b3d03..ba2f1ab15 100644 --- a/README.markdown +++ b/README.markdown @@ -79,6 +79,28 @@ lighttpd >= 1.4.24. 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: + + + + + + + + + + + + + + + + + + + ## Documentation diff --git a/Web.config b/Web.config new file mode 100644 index 000000000..e3ea45892 --- /dev/null +++ b/Web.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + From 121fc292b226ab81c58732d2b2d0d1dc214c486d Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Mon, 15 Oct 2012 01:33:13 +0000 Subject: [PATCH 03/66] removed assignation as there is no return from setter here --- tests/RouteTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/RouteTest.php b/tests/RouteTest.php index de873a577..e805d9b4a 100644 --- a/tests/RouteTest.php +++ b/tests/RouteTest.php @@ -212,7 +212,7 @@ public function testSetRouteParamWhenNotExists() // Get param try { - $param = $route->setParam('foo', 'bar'); + $route->setParam('foo', 'bar'); $this->fail('Did not catch expected InvalidArgumentException'); } catch ( \InvalidArgumentException $e ) {} } From 5bd86e307bfc8391bb852f1077647035e1fa5072 Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Mon, 15 Oct 2012 01:40:40 +0000 Subject: [PATCH 04/66] Fixed typos --- Slim/Environment.php | 2 +- Slim/Middleware/MethodOverride.php | 2 +- Slim/Middleware/SessionCookie.php | 2 +- Slim/Slim.php | 2 +- Slim/View.php | 2 +- tests/Http/RequestTest.php | 4 ++-- tests/Middleware/FlashTest.php | 2 +- tests/Middleware/MethodOverrideTest.php | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Slim/Environment.php b/Slim/Environment.php index 4fc33dfc8..213d47e8c 100644 --- a/Slim/Environment.php +++ b/Slim/Environment.php @@ -175,7 +175,7 @@ private function __construct($settings = null) //Is the application running under HTTPS or HTTP protocol? $env['slim.url_scheme'] = empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off' ? 'http' : 'https'; - //Input stream (readable one time only; not available for mutipart/form-data requests) + //Input stream (readable one time only; not available for multipart/form-data requests) $rawInput = @file_get_contents('php://input'); if (!$rawInput) { $rawInput = ''; diff --git a/Slim/Middleware/MethodOverride.php b/Slim/Middleware/MethodOverride.php index ea873c358..16aa7e355 100644 --- a/Slim/Middleware/MethodOverride.php +++ b/Slim/Middleware/MethodOverride.php @@ -36,7 +36,7 @@ * HTTP Method Override * * This is middleware for a Slim application that allows traditional - * desktop browsers to submit psuedo PUT and DELETE requests by relying + * desktop browsers to submit pseudo PUT and DELETE requests by relying * on a pre-determined request parameter. Without this middleware, * desktop browsers are only able to submit GET and POST requests. * diff --git a/Slim/Middleware/SessionCookie.php b/Slim/Middleware/SessionCookie.php index 905ff8a3d..8957b49b9 100644 --- a/Slim/Middleware/SessionCookie.php +++ b/Slim/Middleware/SessionCookie.php @@ -51,7 +51,7 @@ * session data in a database or alternative server-side cache. * * Because this class stores serialized session data in an HTTP cookie, - * you are inherently limtied to 4 Kb. If you attempt to store + * you are inherently limited to 4 Kb. If you attempt to store * more than this amount, serialization will fail. * * @package Slim diff --git a/Slim/Slim.php b/Slim/Slim.php index 6a36723b4..4f7de0232 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -1309,6 +1309,6 @@ protected function defaultNotFound() */ protected function defaultError() { - echo self::generateTemplateMarkup('Error', '

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

'); + echo self::generateTemplateMarkup('Error', '

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

'); } } diff --git a/Slim/View.php b/Slim/View.php index 39b1a5ea3..ae71540ad 100644 --- a/Slim/View.php +++ b/Slim/View.php @@ -175,7 +175,7 @@ public function setTemplate($template) * * This method echoes the rendered template to the current output buffer * - * @param string $template Pathname of template file relative to templates directoy + * @param string $template Pathname of template file relative to templates directory */ public function display($template) { diff --git a/tests/Http/RequestTest.php b/tests/Http/RequestTest.php index e7fdd729d..03e6dc2af 100644 --- a/tests/Http/RequestTest.php +++ b/tests/Http/RequestTest.php @@ -173,7 +173,7 @@ public function testIsAjaxWithQueryParameter() } /** - * Test AJAX method detection wihtout header or query paramter + * Test AJAX method detection without header or query parameter */ public function testIsAjaxWithoutHeaderOrQueryParameter() { @@ -681,7 +681,7 @@ public function testGetHostWithPort() } /** - * Test get host with port doesn't dulplicate port numbers + * Test get host with port doesn't duplicate port numbers */ public function testGetHostDoesntDulplicatePort() { diff --git a/tests/Middleware/FlashTest.php b/tests/Middleware/FlashTest.php index 80970cf19..3b829b3dc 100644 --- a/tests/Middleware/FlashTest.php +++ b/tests/Middleware/FlashTest.php @@ -86,7 +86,7 @@ public function testKeepFlashFromPreviousRequest() } /** - * Test flash messages from preivous request do not persist to next request + * Test flash messages from previous request do not persist to next request */ public function testFlashMessagesFromPreviousRequestDoNotPersist() { diff --git a/tests/Middleware/MethodOverrideTest.php b/tests/Middleware/MethodOverrideTest.php index 9185e396a..7db54f0f9 100644 --- a/tests/Middleware/MethodOverrideTest.php +++ b/tests/Middleware/MethodOverrideTest.php @@ -98,7 +98,7 @@ public function testDoesNotOverrideMethodIfNotPost() } /** - * Test does not override method if no method ovveride parameter + * Test does not override method if no method override parameter */ public function testDoesNotOverrideMethodAsPostWithoutParameter() { From 1f1824364097d5424c0796c72029faa1c7a989cc Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Mon, 15 Oct 2012 01:49:27 +0000 Subject: [PATCH 05/66] Fixed PHPDocs --- Slim/Http/Response.php | 1 + Slim/Http/Util.php | 10 +++++----- Slim/Middleware/Flash.php | 1 - Slim/Middleware/MethodOverride.php | 2 -- Slim/Middleware/SessionCookie.php | 1 - Slim/Route.php | 1 + Slim/Slim.php | 5 +++-- 7 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Slim/Http/Response.php b/Slim/Http/Response.php index 55b2beadf..976340812 100644 --- a/Slim/Http/Response.php +++ b/Slim/Http/Response.php @@ -446,6 +446,7 @@ public function getIterator() /** * Get message for HTTP status code + * @param int $status * @return string|null */ public static function getMessageForCode($status) diff --git a/Slim/Http/Util.php b/Slim/Http/Util.php index 41016f609..f0d914c92 100644 --- a/Slim/Http/Util.php +++ b/Slim/Http/Util.php @@ -51,7 +51,8 @@ class 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 + * @param array|string $rawData + * @param bool $overrideStripSlashes * @return array|string */ public static function stripSlashesIfMagicQuotes($rawData, $overrideStripSlashes = null) @@ -185,7 +186,7 @@ public static function decrypt($data, $key, $iv, $settings = array()) * @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 + * @return string */ public static function encodeSecureCookie($value, $expires, $secret, $algorithm, $mode) { @@ -208,11 +209,10 @@ public static function encodeSecureCookie($value, $expires, $secret, $algorithm, * 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 + * @return false|string */ public static function decodeSecureCookie($value, $secret, $algorithm, $mode) { @@ -310,7 +310,7 @@ public static function setCookieHeader(&$header, $name, $value) * * @param array $header * @param string $name - * @param string $value + * @param array $value */ public static function deleteCookieHeader(&$header, $name, $value = array()) { diff --git a/Slim/Middleware/Flash.php b/Slim/Middleware/Flash.php index c592d886d..234c48040 100644 --- a/Slim/Middleware/Flash.php +++ b/Slim/Middleware/Flash.php @@ -59,7 +59,6 @@ class Flash extends \Slim\Middleware implements \ArrayAccess, \IteratorAggregate /** * Constructor - * @param \Slim $app * @param array $settings */ public function __construct($settings = array()) diff --git a/Slim/Middleware/MethodOverride.php b/Slim/Middleware/MethodOverride.php index 16aa7e355..69eda300a 100644 --- a/Slim/Middleware/MethodOverride.php +++ b/Slim/Middleware/MethodOverride.php @@ -55,7 +55,6 @@ class MethodOverride extends \Slim\Middleware /** * Constructor - * @param \Slim $app * @param array $settings */ public function __construct($settings = array()) @@ -72,7 +71,6 @@ public function __construct($settings = array()) * 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] */ public function call() diff --git a/Slim/Middleware/SessionCookie.php b/Slim/Middleware/SessionCookie.php index 8957b49b9..643f81617 100644 --- a/Slim/Middleware/SessionCookie.php +++ b/Slim/Middleware/SessionCookie.php @@ -119,7 +119,6 @@ public function call() /** * Load session - * @param array $env */ protected function loadSession() { diff --git a/Slim/Route.php b/Slim/Route.php index 1a055940c..4d6308189 100644 --- a/Slim/Route.php +++ b/Slim/Route.php @@ -280,6 +280,7 @@ public function via() /** * Detect support for an HTTP method + * @param string $method * @return bool */ public function supportsHttpMethod($method) diff --git a/Slim/Slim.php b/Slim/Slim.php index 4f7de0232..ab7232c04 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -835,6 +835,7 @@ public function setEncryptedCookie($name, $value, $expires = null, $path = null, * the current request will not be available until the next request. * * @param string $name + * @param bool $deleteIfInvalid * @return string|false */ public function getEncryptedCookie($name, $deleteIfInvalid = true) @@ -966,7 +967,7 @@ public function contentType($type) /** * Set the HTTP response status code - * @param int $status The HTTP response status code + * @param int $code The HTTP response status code */ public function status($code) { @@ -1064,7 +1065,7 @@ public function hook($name, $callable, $priority = 10) /** * Invoke hook * @param string $name The hook name - * @param mixed $hookArgs (Optional) Argument for hooked functions + * @param mixed $hookArg (Optional) Argument for hooked functions */ public function applyHook($name, $hookArg = null) { From 023ac6e24f86f2bcb5dea6a904181309f520cd61 Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Mon, 15 Oct 2012 02:04:00 +0000 Subject: [PATCH 06/66] Fixed minor whitespaces --- Slim/Http/Response.php | 4 ++-- Slim/Slim.php | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Slim/Http/Response.php b/Slim/Http/Response.php index 976340812..58b514a63 100644 --- a/Slim/Http/Response.php +++ b/Slim/Http/Response.php @@ -390,7 +390,7 @@ public function isServerError() /** * Array Access: Offset Exists */ - public function offsetExists( $offset ) + public function offsetExists($offset) { return isset($this->header[$offset]); } @@ -398,7 +398,7 @@ public function offsetExists( $offset ) /** * Array Access: Offset Get */ - public function offsetGet( $offset ) + public function offsetGet($offset) { if (isset($this->header[$offset])) { return $this->header[$offset]; diff --git a/Slim/Slim.php b/Slim/Slim.php index ab7232c04..ad39268d2 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -127,7 +127,7 @@ public static function autoload($className) $thisClass = str_replace(__NAMESPACE__.'\\', '', __CLASS__); $baseDir = __DIR__; - + if (substr($baseDir, -strlen($thisClass)) === $thisClass) { $baseDir = substr($baseDir, 0, -strlen($thisClass)); } @@ -719,7 +719,9 @@ public function etag($value, $type = 'strong') //Set etag value $value = '"' . $value . '"'; - if ($type === 'weak') $value = 'W/'.$value; + if ($type === 'weak') { + $value = 'W/'.$value; + } $this->response['ETag'] = $value; //Check conditional GET @@ -1233,7 +1235,7 @@ public function call() $this->response['Allow'] = implode(' ', $httpMethodsAllowed); $this->halt(405, 'HTTP method not allowed for the requested resource. Use one of these instead: ' . implode(', ', $httpMethodsAllowed)); } else { - $this->notFound(); + $this->notFound(); } } $this->applyHook('slim.after.router'); From b3e05b49b7097f2241d31789a53cf7b0bc6e6df4 Mon Sep 17 00:00:00 2001 From: Marchenko Alexandr Date: Mon, 29 Oct 2012 12:48:35 +0200 Subject: [PATCH 07/66] removed Web.config --- Web.config | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 Web.config diff --git a/Web.config b/Web.config deleted file mode 100644 index e3ea45892..000000000 --- a/Web.config +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - From 28f4b7e93704e106d2a01547a8050f8af62e1628 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 16 Dec 2012 22:34:57 -0500 Subject: [PATCH 08/66] Refactor Route unit tests --- tests/RouteTest.php | 764 +++++++++++++++++++++----------------------- 1 file changed, 364 insertions(+), 400 deletions(-) diff --git a/tests/RouteTest.php b/tests/RouteTest.php index decec7d4f..d5f1c97c6 100644 --- a/tests/RouteTest.php +++ b/tests/RouteTest.php @@ -29,292 +29,197 @@ * 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() + public function testGetPattern() { - $route = new \Slim\Route('/foo/bar', function () {}); - $route->name('foo'); - $this->assertEquals('foo', $route->getName()); - } + $route = new \Slim\Route('/foo', function () {}); - /** - * Route should set pattern - */ - public function testRouteSetsPattern() - { - $route1 = new \Slim\Route('/foo/bar', function () {}); - $this->assertEquals('/foo/bar', $route1->getPattern()); + $this->assertEquals('/foo', $route->getPattern()); } - /** - * Route sets pattern with params - */ - public function testRouteSetsPatternWithParams() + public function testGetName() { - $route = new \Slim\Route('/hello/:first/:last', function () {}); - $this->assertEquals('/hello/:first/:last', $route->getPattern()); + $route = new \Slim\Route('/foo', function () {}); + + $property = new \ReflectionProperty($route, 'name'); + $property->setAccessible(true); + $property->setValue($route, 'foo'); + + $this->assertEquals('foo', $route->getName()); } - /** - * Route sets custom pattern that overrides pattern - */ - public function testRouteSetsCustomTemplate() + public function testSetName() { - $route = new \Slim\Route('/hello/*', function () {}); - $route->setPattern('/hello/:name'); - $this->assertEquals('/hello/:name', $route->getPattern()); + $route = new \Slim\Route('/foo', function () {}); + $route->name('foo'); // <-- Alias for `setName()` + + $property = new \ReflectionProperty($route, 'name'); + $property->setAccessible(true); + + $this->assertEquals('foo', $property->getValue($route)); } - /** - * Route should store a reference to the callable - * anonymous function. - */ - public function testRouteSetsCallableAsFunction() + public function testGetCallable() { - $callable = function () { echo "Foo!"; }; - $route = new \Slim\Route('/foo/bar', $callable); + $callable = function () { + echo 'Foo'; + }; + $route = new \Slim\Route('/foo', $callable); + $this->assertSame($callable, $route->getCallable()); } - /** - * Route should store a reference to the callable - * regular function (for PHP 5 < 5.3) - */ - public function testRouteSetsCallableAsString() + public function testSetCallable() { - $route = new \Slim\Route('/foo/bar', 'testCallable'); - $this->assertEquals('testCallable', $route->getCallable()); - } + $callable = function () { + echo 'Foo'; + }; + $route = new \Slim\Route('/foo', $callable); // <-- Called inside __construct() - /** - * Route should throw exception when creating with an invalid callable - */ - public function testRouteThrowsExecptionWithInvalidCallable() - { - $this->setExpectedException('InvalidArgumentException'); - $route = new \Slim\Route('/foo/bar', 'fnDoesNotExist'); + $property = new \ReflectionProperty($route, 'callable'); + $property->setAccessible(true); + + $this->assertSame($callable, $property->getValue($route)); } - /** - * Route should throw exception when setting an invalid callable - */ - public function testRouteThrowsExecptionWhenSettingInvalidCallable() + public function testSetCallableWithInvalidArgument() { - $route = new \Slim\Route('/foo/bar', function () {}); - try - { - $route->setCallable('fnDoesNotExist'); - $this->fail('Did not catch InvalidArgumentException when setting invalid callable'); - } catch(\InvalidArgumentException $e) {} + $this->setExpectedException('\InvalidArgumentException'); + $route = new \Slim\Route('/foo', 'doesNotExist'); // <-- Called inside __construct() } - /** - * Test gets all params - */ - public function testGetRouteParams() + public function testGetParams() { - // Prepare route - $requestUri = '/hello/mr/anderson'; $route = new \Slim\Route('/hello/:first/:last', function () {}); + $route->matches('/hello/mr/anderson'); // <-- Parses params from argument - // 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']); + $this->assertEquals(array( + 'first' => 'mr', + 'last' => 'anderson' + ), $route->getParams()); } - /** - * Test sets all params - */ - public function testSetRouteParams() + public function testSetParams() { - // 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->matches('/hello/mr/anderson'); // <-- Parses params from argument $route->setParams(array( - 'first' => 'john', + 'first' => 'agent', 'last' => 'smith' )); - // Get new params - $params = $route->getParams(); - $this->assertEquals(2, count($params)); - $this->assertEquals('john', $params['first']); - $this->assertEquals('smith', $params['last']); + $property = new \ReflectionProperty($route, 'params'); + $property->setAccessible(true); + + $this->assertEquals(array( + 'first' => 'agent', + 'last' => 'smith' + ), $property->getValue($route)); } - /** - * Test gets param when exists - */ - public function testGetRouteParamWhenExists() + public function testGetParam() { - // Prepare route - $requestUri = '/hello/mr/anderson'; $route = new \Slim\Route('/hello/:first/:last', function () {}); - // Parse route params - $this->assertTrue($route->matches($requestUri)); + $property = new \ReflectionProperty($route, 'params'); + $property->setAccessible(true); + $property->setValue($route, array( + 'first' => 'foo', + 'last' => 'bar' + )); - // Get param - $this->assertEquals('anderson', $route->getParam('last')); + $this->assertEquals('foo', $route->getParam('first')); } - /** - * Test gets param when not exists - */ - public function testGetRouteParamWhenNotExists() + public function testGetParamThatDoesNotExist() { - // Prepare route - $requestUri = '/hello/mr/anderson'; + $this->setExpectedException('InvalidArgumentException'); + $route = new \Slim\Route('/hello/:first/:last', function () {}); - // Parse route params - $this->assertTrue($route->matches($requestUri)); + $property = new \ReflectionProperty($route, 'params'); + $property->setAccessible(true); + $property->setValue($route, array( + 'first' => 'foo', + 'last' => 'bar' + )); - // Get param - try { - $param = $route->getParam('foo'); - $this->fail('Did not catch expected InvalidArgumentException'); - } catch ( \InvalidArgumentException $e ) {} + $route->getParam('middle'); } - /** - * Test sets param when exists - */ - public function testSetRouteParamWhenExists() + public function testSetParam() { - // 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->matches('/hello/mr/anderson'); // <-- Parses params from argument $route->setParam('last', 'smith'); - // Get new param - $this->assertEquals('smith', $route->getParam('last')); + $property = new \ReflectionProperty($route, 'params'); + $property->setAccessible(true); + + $this->assertEquals(array( + 'first' => 'mr', + 'last' => 'smith' + ), $property->getValue($route)); } - /** - * Test sets param when not exists - */ - public function testSetRouteParamWhenNotExists() + public function testSetParamThatDoesNotExist() { - // Prepare route - $requestUri = '/hello/mr/anderson'; - $route = new \Slim\Route('/hello/:first/:last', function () {}); - - // Parse route params - $this->assertTrue($route->matches($requestUri)); + $this->setExpectedException('InvalidArgumentException'); - // Get param - try { - $param = $route->setParam('foo', 'bar'); - $this->fail('Did not catch expected InvalidArgumentException'); - } catch ( \InvalidArgumentException $e ) {} + $route = new \Slim\Route('/hello/:first/:last', function () {}); + $route->matches('/hello/mr/anderson'); // <-- Parses params from argument + $route->setParam('middle', 'smith'); // <-- Should trigger InvalidArgumentException } - /** - * If route matches a resource URI, param should be extracted. - */ - public function testRouteMatchesAndParamExtracted() + public function testMatches() { - $resource = '/hello/Josh'; $route = new \Slim\Route('/hello/:name', function () {}); - $result = $route->matches($resource); - $this->assertTrue($result); - $this->assertEquals(array('name' => 'Josh'), $route->getParams()); + + $this->assertTrue($route->matches('/hello/josh')); } - /** - * If route matches a resource URI, multiple params should be extracted. - */ - public function testRouteMatchesAndMultipleParamsExtracted() + public function testMatchesIsFalse() { - $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()); + $route = new \Slim\Route('/foo', function () {}); + + $this->assertFalse($route->matches('/bar')); } - /** - * If route does not match a resource URI, params remain an empty array - */ - public function testRouteDoesNotMatchAndParamsNotExtracted() + public function testMatchesPatternWithTrailingSlash() { - $resource = '/foo/bar'; - $route = new \Slim\Route('/hello/:name', function () {}); - $result = $route->matches($resource); - $this->assertFalse($result); - $this->assertEquals(array(), $route->getParams()); + $route = new \Slim\Route('/foo/', function () {}); + + $this->assertTrue($route->matches('/foo/')); + $this->assertTrue($route->matches('/foo')); } - /** - * Route matches URI with trailing slash - * - */ - public function testRouteMatchesWithTrailingSlash() + public function testMatchesPatternWithoutTrailingSlash() { - $resource1 = '/foo/bar/'; - $resource2 = '/foo/bar'; - $route = new \Slim\Route('/foo/:one/', function () {}); - $this->assertTrue($route->matches($resource1)); - $this->assertTrue($route->matches($resource2)); + $route = new \Slim\Route('/foo', function () {}); + + $this->assertFalse($route->matches('/foo/')); + $this->assertTrue($route->matches('/foo')); } - /** - * Route matches URI with conditions - */ - public function testRouteMatchesResourceWithConditions() + public function testMatchesWithConditions() { - $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->conditions(array( + 'first' => '[a-zA-Z]{3,}' + )); + + $this->assertTrue($route->matches('/hello/Josh/and/John')); } - /** - * Route does not match URI with conditions - */ - public function testRouteDoesNotMatchResourceWithConditions() + public function testMatchesWithConditionsIsFalse() { - $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->conditions(array( + 'first' => '[a-z]{3,}' + )); + + $this->assertFalse($route->matches('/hello/Josh/and/John')); } /* @@ -324,14 +229,12 @@ public function testRouteDoesNotMatchResourceWithConditions() * * Excludes "+" which is valid but decodes into a space character */ - public function testRouteMatchesResourceWithValidRfc2396PathComponent() + public function testMatchesWithValidRfc2396PathComponent() { $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()); + + $this->assertTrue($route->matches('/rfc2386/' . $symbols)); } /* @@ -339,221 +242,282 @@ public function testRouteMatchesResourceWithValidRfc2396PathComponent() * * "Uniform Resource Identifiers (URI): Generic Syntax" http://www.ietf.org/rfc/rfc2396.txt */ - public function testRouteMatchesResourceWithUnreservedMarks() + public function testMatchesWithUnreservedMarks() { $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()); + + $this->assertTrue($route->matches('/marks/' . $marks)); } - /** - * 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() + public function testMatchesOptionalParameters() { $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() + $route1 = new \Slim\Route($pattern, function () {}); + $this->assertTrue($route1->matches('/archive/2010')); + $this->assertEquals(array('year' => '2010'), $route1->getParams()); + + $route2 = new \Slim\Route($pattern, function () {}); + $this->assertTrue($route2->matches('/archive/2010/05')); + $this->assertEquals(array('year' => '2010', 'month' => '05'), $route2->getParams()); + + $route3 = new \Slim\Route($pattern, function () {}); + $this->assertTrue($route3->matches('/archive/2010/05/13')); + $this->assertEquals(array('year' => '2010', 'month' => '05', 'day' => '13'), $route3->getParams()); + } + + public function testGetConditions() { - \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 = new \Slim\Route('/foo', function () {}); + + $property = new \ReflectionProperty($route, 'conditions'); + $property->setAccessible(true); + $property->setValue($route, array('foo' => '\d{3}')); + + $this->assertEquals(array('foo' => '\d{3}'), $route->getConditions()); } - /** - * Route matches URI with wildcard - */ - public function testRouteMatchesResourceWithWildcard() + public function testSetDefaultConditions() + { + \Slim\Route::setDefaultConditions(array( + 'id' => '\d+' + )); + + $property = new \ReflectionProperty('\Slim\Route', 'defaultConditions'); + $property->setAccessible(true); + + $this->assertEquals(array( + 'id' => '\d+' + ), $property->getValue()); + } + + public function testGetDefaultConditions() + { + $property = new \ReflectionProperty('\Slim\Route', 'defaultConditions'); + $property->setAccessible(true); + $property->setValue(array( + 'id' => '\d+' + )); + + $this->assertEquals(array( + 'id' => '\d+' + ), \Slim\Route::getDefaultConditions()); + } + + public function testDefaultConditionsAssignedToInstance() + { + $staticProperty = new \ReflectionProperty('\Slim\Route', 'defaultConditions'); + $staticProperty->setAccessible(true); + $staticProperty->setValue(array( + 'id' => '\d+' + )); + + $route = new \Slim\Route('/foo', function () {}); + + $instanceProperty = new \ReflectionProperty($route, 'conditions'); + $instanceProperty->setAccessible(true); + + $this->assertArrayHasKey('id', $instanceProperty->getValue($route)); + } + + public function testMatchesWildcard() { - $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()); + + $property = new \ReflectionProperty($route, 'params'); + $property->setAccessible(true); + + $this->assertTrue($route->matches('/hello/foo/bar/world')); + $this->assertEquals(array( + 'path' => array('foo', 'bar') + ), $property->getValue($route)); } - /** - * Route matches URI with more than one wildcard - */ - public function testRouteMatchesResourceWithMultipleWildcards() + public function testMatchesMultipleWildcards() { - $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()); + + $property = new \ReflectionProperty($route, 'params'); + $property->setAccessible(true); + + $this->assertTrue($route->matches('/hello/foo/bar/world/2012/03/10')); + $this->assertEquals(array( + 'path' => array('foo', 'bar'), + 'date' => array('2012', '03', '10') + ), $property->getValue($route)); } - /** - * Route matches URI with wildcards and parameters - */ - public function testRouteMatchesResourceWithWildcardsAndParams() + public function testMatchesParamsAndWildcards() { - $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()); + + $property = new \ReflectionProperty($route, 'params'); + $property->setAccessible(true); + + $this->assertTrue($route->matches('/hello/foo/bar/world/2012/03/10/first/second')); + $this->assertEquals(array( + 'path' => array('foo', 'bar'), + 'year' => '2012', + 'month' => '03', + 'day' => '10', + 'path2' => array('first', 'second') + ), $property->getValue($route)); } - /** - * Route matches URI with optional wildcard and parameter - */ - public function testRouteMatchesResourceWithOptionalWildcardsAndParams() + public function testMatchesParamsWithOptionalWildcard() { - $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()); + $route = new \Slim\Route('/hello(/:foo(/:bar+))', function () {}); - $resourceB = '/hello/world'; - $routeB = new \Slim\Route('/hello(/:world(/:path))', function () {}); - $this->assertTrue($routeB->matches($resourceB)); - $this->assertEquals(array('world'=>'world'), $routeB->getParams()); + $this->assertTrue($route->matches('/hello')); + $this->assertTrue($route->matches('/hello/world')); + $this->assertTrue($route->matches('/hello/world/foo')); + $this->assertTrue($route->matches('/hello/world/foo/bar')); } - /** - * Route does not match URI with wildcard - */ - public function testRouteDoesNotMatchResourceWithWildcard() + public function testSetMiddleware() { - $resource = '/hello'; - $route = new \Slim\Route('/hello/:path+', function () {}); - $result = $route->matches($resource); - $this->assertFalse($result); - $this->assertEquals(array(), $route->getParams()); + $route = new \Slim\Route('/foo', function () {}); + $mw = function () { + echo 'Foo'; + }; + + $property = new \ReflectionProperty($route, 'middleware'); + $property->setAccessible(true); + + $route->setMiddleware($mw); + + $this->assertSame($mw, $property->getValue($route)[0]); } - /** - * 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')); + public function testSetMiddlewareMultipleTimes() + { + $route = new \Slim\Route('/foo', function () {}); + $mw1 = function () { + echo 'Foo'; + }; + $mw2 = function () { + echo 'Bar'; + }; + + $property = new \ReflectionProperty($route, 'middleware'); + $property->setAccessible(true); + + $route->setMiddleware($mw1); + $route->setMiddleware($mw2); + + $this->assertSame($mw1, $property->getValue($route)[0]); + $this->assertSame($mw2, $property->getValue($route)[1]); + } + + public function testSetMiddlewareWithArray() + { + $route = new \Slim\Route('/foo', function () {}); + $mw1 = function () { + echo 'Foo'; + }; + $mw2 = function () { + echo 'Bar'; + }; + + $property = new \ReflectionProperty($route, 'middleware'); + $property->setAccessible(true); + + $route->setMiddleware(array($mw1, $mw2)); + + $this->assertSame($mw1, $property->getValue($route)[0]); + $this->assertSame($mw2, $property->getValue($route)[1]); + } + + public function testSetMiddlewareWithInvalidArgument() + { + $this->setExpectedException('InvalidArgumentException'); + + $route = new \Slim\Route('/foo', function () {}); + $route->setMiddleware('doesNotExist'); // <-- Should throw InvalidArgumentException + } + + public function testSetMiddlewareWithArrayWithInvalidArgument() + { + $this->setExpectedException('InvalidArgumentException'); + + $route = new \Slim\Route('/foo', function () {}); + + $property = new \ReflectionProperty($route, 'middleware'); + $property->setAccessible(true); + + $route->setMiddleware(array('doesNotExist')); + } + + public function testGetMiddleware() + { + $route = new \Slim\Route('/foo', function () {}); + + $property = new \ReflectionProperty($route, 'middleware'); + $property->setAccessible(true); + $property->setValue($route, array('foo' => 'bar')); + + $this->assertEquals(array('foo' => 'bar'), $route->getMiddleware()); + } + + public function testSetHttpMethods() + { + $route = new \Slim\Route('/foo', function () {}); + + $property = new \ReflectionProperty($route, 'methods'); + $property->setAccessible(true); + + $route->setHttpMethods('GET', 'POST'); + + $this->assertEquals(array('GET', 'POST'), $property->getValue($route)); + } + + public function testGetHttpMethods() + { + $route = new \Slim\Route('/foo', function () {}); + + $property = new \ReflectionProperty($route, 'methods'); + $property->setAccessible(true); + $property->setValue($route, array('GET', 'POST')); + + $this->assertEquals(array('GET', 'POST'), $route->getHttpMethods()); + } + + public function testAppendHttpMethods() + { + $route = new \Slim\Route('/foo', function () {}); + + $property = new \ReflectionProperty($route, 'methods'); + $property->setAccessible(true); + $property->setValue($route, array('GET', 'POST')); + + $route->appendHttpMethods('PUT'); + + $this->assertEquals(array('GET', 'POST', 'PUT'), $property->getValue($route)); + } + + public function testAppendHttpMethodsWithVia() + { + $route = new \Slim\Route('/foo', function () {}); + + $property = new \ReflectionProperty($route, 'methods'); + $property->setAccessible(true); + $property->setValue($route, array('GET', 'POST')); + + $route->via('PUT'); + + $this->assertEquals(array('GET', 'POST', 'PUT'), $property->getValue($route)); + } + + public function testSupportsHttpMethod() + { + $route = new \Slim\Route('/foo', function () {}); + + $property = new \ReflectionProperty($route, 'methods'); + $property->setAccessible(true); + $property->setValue($route, array('POST')); + + $this->assertTrue($route->supportsHttpMethod('POST')); + $this->assertFalse($route->supportsHttpMethod('PUT')); } } From d0bc6cee24a8e86fc7876a8fc625e59c5f81a5a0 Mon Sep 17 00:00:00 2001 From: Christopher Thomas Date: Mon, 17 Dec 2012 19:17:51 -0800 Subject: [PATCH 09/66] Adding Support for PATCH HTTP Verb --- Slim/Http/Request.php | 20 ++++++++++++++++++ Slim/Slim.php | 16 +++++++++++++-- index.php | 7 ++++++- tests/Http/RequestTest.php | 42 ++++++++++++++++++++++++++++++++++++++ tests/RouterTest.php | 3 ++- tests/SlimTest.php | 21 +++++++++++++++++++ 6 files changed, 105 insertions(+), 4 deletions(-) diff --git a/Slim/Http/Request.php b/Slim/Http/Request.php index 2af8a0750..ed66eaa20 100644 --- a/Slim/Http/Request.php +++ b/Slim/Http/Request.php @@ -48,6 +48,7 @@ class Request const METHOD_GET = 'GET'; const METHOD_POST = 'POST'; const METHOD_PUT = 'PUT'; + const METHOD_PATCH = 'PATCH'; const METHOD_DELETE = 'DELETE'; const METHOD_OPTIONS = 'OPTIONS'; const METHOD_OVERRIDE = '_METHOD'; @@ -108,6 +109,15 @@ public function isPut() return $this->getMethod() === self::METHOD_PUT; } + /** + * Is this a PATCH request? + * @return bool + */ + public function isPatch() + { + return $this->getMethod() === self::METHOD_PATCH; + } + /** * Is this a DELETE request? * @return bool @@ -263,6 +273,16 @@ public function put($key = null) return $this->post($key); } + /** + * Fetch PATCH data (alias for \Slim\Http\Request::post) + * @param string $key + * @return array|mixed|null + */ + public function patch($key = null) + { + return $this->post($key); + } + /** * Fetch DELETE data (alias for \Slim\Http\Request::post) * @param string $key diff --git a/Slim/Slim.php b/Slim/Slim.php index 9f940b4a5..9c1a56fcd 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -369,7 +369,7 @@ public function getLog() *******************************************************************************/ /** - * Add GET|POST|PUT|DELETE route + * Add GET|POST|PUT|PATCH|DELETE route * * Adds a new route to the router with associated callable. This * route will only be invoked when the HTTP request's method matches @@ -458,6 +458,18 @@ public function put() return $this->mapRoute($args)->via(\Slim\Http\Request::METHOD_PUT); } + /** + * Add PATCH route + * @see mapRoute() + * @return \Slim\Route + */ + public function patch() + { + $args = func_get_args(); + + return $this->mapRoute($args)->via(\Slim\Http\Request::METHOD_PATCH); + } + /** * Add DELETE route * @see mapRoute() @@ -653,7 +665,7 @@ public function view($viewClass = null) /** * Render a template * - * Call this method within a GET, POST, PUT, DELETE, NOT FOUND, or ERROR + * Call this method within a GET, POST, PUT, PATCH, DELETE, NOT FOUND, or ERROR * callable to render a template whose output is appended to the * current HTTP response body. How the template is rendered is * delegated to the current View. diff --git a/index.php b/index.php index c3e67454c..f1cf98297 100644 --- a/index.php +++ b/index.php @@ -26,7 +26,7 @@ * * Here we define several Slim application routes that respond * to appropriate HTTP request methods. In this example, the second - * argument for `Slim::get`, `Slim::post`, `Slim::put`, and `Slim::delete` + * argument for `Slim::get`, `Slim::post`, `Slim::put`, `Slim::patch`, and `Slim::delete` * is an anonymous function. */ @@ -138,6 +138,11 @@ echo 'This is a PUT route'; }); +// PATCH route +$app->patch('/patch', function () { + echo 'This is a PATCH route'; +}); + // DELETE route $app->delete('/delete', function () { echo 'This is a DELETE route'; diff --git a/tests/Http/RequestTest.php b/tests/Http/RequestTest.php index 467e37bcd..1229a06ec 100644 --- a/tests/Http/RequestTest.php +++ b/tests/Http/RequestTest.php @@ -56,6 +56,7 @@ public function testIsGet() $this->assertTrue($req->isGet()); $this->assertFalse($req->isPost()); $this->assertFalse($req->isPut()); + $this->assertFalse($req->isPatch()); $this->assertFalse($req->isDelete()); $this->assertFalse($req->isOptions()); $this->assertFalse($req->isHead()); @@ -73,6 +74,7 @@ public function testIsPost() $this->assertFalse($req->isGet()); $this->assertTrue($req->isPost()); $this->assertFalse($req->isPut()); + $this->assertFalse($req->isPatch()); $this->assertFalse($req->isDelete()); $this->assertFalse($req->isOptions()); $this->assertFalse($req->isHead()); @@ -90,6 +92,25 @@ public function testIsPut() $this->assertFalse($req->isGet()); $this->assertFalse($req->isPost()); $this->assertTrue($req->isPut()); + $this->assertFalse($req->isPatch()); + $this->assertFalse($req->isDelete()); + $this->assertFalse($req->isOptions()); + $this->assertFalse($req->isHead()); + } + + /** + * Test HTTP PATCH method detection + */ + public function testIsPatch() + { + $env = \Slim\Environment::mock(array( + 'REQUEST_METHOD' => 'PATCH', + )); + $req = new \Slim\Http\Request($env); + $this->assertFalse($req->isGet()); + $this->assertFalse($req->isPost()); + $this->assertFalse($req->isPut()); + $this->assertTrue($req->isPatch()); $this->assertFalse($req->isDelete()); $this->assertFalse($req->isOptions()); $this->assertFalse($req->isHead()); @@ -107,6 +128,7 @@ public function testIsDelete() $this->assertFalse($req->isGet()); $this->assertFalse($req->isPost()); $this->assertFalse($req->isPut()); + $this->assertFalse($req->isPatch()); $this->assertTrue($req->isDelete()); $this->assertFalse($req->isOptions()); $this->assertFalse($req->isHead()); @@ -124,6 +146,7 @@ public function testIsOptions() $this->assertFalse($req->isGet()); $this->assertFalse($req->isPost()); $this->assertFalse($req->isPut()); + $this->assertFalse($req->isPatch()); $this->assertFalse($req->isDelete()); $this->assertTrue($req->isOptions()); $this->assertFalse($req->isHead()); @@ -141,6 +164,7 @@ public function testIsHead() $this->assertFalse($req->isGet()); $this->assertFalse($req->isPost()); $this->assertFalse($req->isPut()); + $this->assertFalse($req->isPatch()); $this->assertFalse($req->isDelete()); $this->assertFalse($req->isOptions()); $this->assertTrue($req->isHead()); @@ -339,6 +363,24 @@ public function testPut() $this->assertNull($req->put('xyz')); } + /** + * Test fetch PATCH params + */ + public function testPatch() + { + $env = \Slim\Environment::mock(array( + 'REQUEST_METHOD' => 'PATCH', + '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->patch())); + $this->assertEquals('bar', $req->patch('foo')); + $this->assertEquals('bar', $req->params('foo')); + $this->assertNull($req->patch('xyz')); + } + /** * Test fetch DELETE params */ diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 1772a46ab..4bfad7512 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -287,8 +287,9 @@ public function testRouterMatchesRoutesByUriOnly() $router->map('/foo', function () {})->via('GET'); $router->map('/foo', function () {})->via('POST'); $router->map('/foo', function () {})->via('PUT'); + $router->map('/foo', function () {})->via('PATCH'); $router->map('/foo/bar/xyz', function () {})->via('DELETE'); - $this->assertEquals(3, count($router->getMatchedRoutes())); + $this->assertEquals(4, count($router->getMatchedRoutes())); } /** diff --git a/tests/SlimTest.php b/tests/SlimTest.php index 47b9954dd..b009604ee 100644 --- a/tests/SlimTest.php +++ b/tests/SlimTest.php @@ -343,6 +343,27 @@ public function testPutRoute() $this->assertSame($callable, $route->getCallable()); } + /** + * Test PATCH route + */ + public function testPatchRoute() + { + \Slim\Environment::mock(array( + 'REQUEST_METHOD' => 'PATCH', + 'SCRIPT_NAME' => '/foo', //<-- Physical + 'PATH_INFO' => '/bar', //<-- Virtual + )); + $s = new \Slim\Slim(); + $mw1 = function () { echo "foo"; }; + $mw2 = function () { echo "bar"; }; + $callable = function () { echo "xyz"; }; + $route = $s->patch('/bar', $mw1, $mw2, $callable); + $s->call(); + $this->assertEquals('foobarxyz', $s->response()->body()); + $this->assertEquals('/bar', $route->getPattern()); + $this->assertSame($callable, $route->getCallable()); + } + /** * Test DELETE route */ From e837ae569e72596ff0cf7ef67943f3c7dc6ebdbe Mon Sep 17 00:00:00 2001 From: = Date: Sat, 9 Mar 2013 22:21:54 -0500 Subject: [PATCH 10/66] Refactor Route unit tests --- Slim/Route.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Slim/Route.php b/Slim/Route.php index 445fbe9d5..7f527a156 100644 --- a/Slim/Route.php +++ b/Slim/Route.php @@ -413,4 +413,17 @@ public function conditions(array $conditions) return $this; } + + /** + * Dispatch + * @return mixed The return value of the route callable, or FALSE on error + */ + public function dispatch() + { + foreach ($this->middleware as $routeMiddleware) { + call_user_func_array($routeMiddleware, array($this)); + } + + return call_user_func_array($this->callable, array_values($this->params)); + } } From 3915e5374b47d1845c7f477744205c20518372ae Mon Sep 17 00:00:00 2001 From: = Date: Sat, 9 Mar 2013 22:22:05 -0500 Subject: [PATCH 11/66] Refactor Router unit tests --- Slim/Router.php | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/Slim/Router.php b/Slim/Router.php index 44979ea0a..aa0f48e76 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -151,27 +151,14 @@ public function urlFor($name, $params = array()) /** * 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? + * @param \Slim\Route $route The route to dispatch + * @return bool True if route dispatched successfully, else false */ 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; + return ($route->dispatch() !== false); } /** From a142f36b7726d4c6db48946ef8e6ef6e3e1ed2c9 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 9 Mar 2013 22:22:31 -0500 Subject: [PATCH 12/66] Refactor Middleware unit tests --- tests/MiddlewareTest.php | 65 ++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/tests/MiddlewareTest.php b/tests/MiddlewareTest.php index db78918c3..47ef8bb46 100644 --- a/tests/MiddlewareTest.php +++ b/tests/MiddlewareTest.php @@ -30,57 +30,50 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -class My_Middleware extends \Slim\Middleware +class MyMiddleware extends \Slim\Middleware { - public function call() - { - echo "Before"; - $this->next->call(); - echo "After"; - } + public function call() {} } -class My_Application +class MiddlewareTest extends PHPUnit_Framework_TestCase { - public function call() + public function testSetApplication() { - echo "Application"; + $app = new MyApplication(); + $mw = new MyMiddleware(); + $mw->setApplication($app); + + $this->assertAttributeSame($app, 'app', $mw); } -} -class MiddlewareTest extends PHPUnit_Framework_TestCase -{ - /** - * Get and set application - */ - public function testGetAndSetApplication() + public function testGetApplication() { - $app = new My_Application(); - $mw = new My_Middleware(); - $mw->setApplication($app); + $app = new MyApplication(); + $mw = new MyMiddleware(); + $property = new \ReflectionProperty($mw, 'app'); + $property->setAccessible(true); + $property->setValue($mw, $app); + $this->assertSame($app, $mw->getApplication()); } - /** - * Get and set next middleware - */ - public function testGetAndSetNextMiddleware() + public function testSetNextMiddleware() { - $mw1 = new My_Middleware(); - $mw2 = new My_Middleware(); + $mw1 = new MyMiddleware(); + $mw2 = new MyMiddleware(); $mw1->setNextMiddleware($mw2); - $this->assertSame($mw2, $mw1->getNextMiddleware()); + + $this->assertAttributeSame($mw2, 'next', $mw1); } - /** - * Test call - */ - public function testCall() + public function testGetNextMiddleware() { - $this->expectOutputString('BeforeApplicationAfter'); - $app = new My_Application(); - $mw = new My_Middleware(); - $mw->setNextMiddleware($app); - $mw->call(); + $mw1 = new MyMiddleware(); + $mw2 = new MyMiddleware(); + $property = new \ReflectionProperty($mw1, 'next'); + $property->setAccessible(true); + $property->setValue($mw1, $mw2); + + $this->assertSame($mw2, $mw1->getNextMiddleware()); } } From 577fdfd18d90aaa61d1060123670f868fdc5fa55 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 9 Mar 2013 22:23:00 -0500 Subject: [PATCH 13/66] Refactor Route unit tests --- tests/RouteTest.php | 99 +++++++++++++-------------------------------- 1 file changed, 28 insertions(+), 71 deletions(-) diff --git a/tests/RouteTest.php b/tests/RouteTest.php index d5f1c97c6..26ff2facf 100644 --- a/tests/RouteTest.php +++ b/tests/RouteTest.php @@ -54,10 +54,7 @@ public function testSetName() $route = new \Slim\Route('/foo', function () {}); $route->name('foo'); // <-- Alias for `setName()` - $property = new \ReflectionProperty($route, 'name'); - $property->setAccessible(true); - - $this->assertEquals('foo', $property->getValue($route)); + $this->assertAttributeEquals('foo', 'name', $route); } public function testGetCallable() @@ -77,10 +74,7 @@ public function testSetCallable() }; $route = new \Slim\Route('/foo', $callable); // <-- Called inside __construct() - $property = new \ReflectionProperty($route, 'callable'); - $property->setAccessible(true); - - $this->assertSame($callable, $property->getValue($route)); + $this->assertAttributeSame($callable, 'callable', $route); } public function testSetCallableWithInvalidArgument() @@ -109,13 +103,10 @@ public function testSetParams() 'last' => 'smith' )); - $property = new \ReflectionProperty($route, 'params'); - $property->setAccessible(true); - - $this->assertEquals(array( + $this->assertAttributeEquals(array( 'first' => 'agent', 'last' => 'smith' - ), $property->getValue($route)); + ), 'params', $route); } public function testGetParam() @@ -154,13 +145,10 @@ public function testSetParam() $route->matches('/hello/mr/anderson'); // <-- Parses params from argument $route->setParam('last', 'smith'); - $property = new \ReflectionProperty($route, 'params'); - $property->setAccessible(true); - - $this->assertEquals(array( + $this->assertAttributeEquals(array( 'first' => 'mr', 'last' => 'smith' - ), $property->getValue($route)); + ), 'params', $route); } public function testSetParamThatDoesNotExist() @@ -312,57 +300,46 @@ public function testDefaultConditionsAssignedToInstance() $staticProperty->setValue(array( 'id' => '\d+' )); - $route = new \Slim\Route('/foo', function () {}); - $instanceProperty = new \ReflectionProperty($route, 'conditions'); - $instanceProperty->setAccessible(true); - - $this->assertArrayHasKey('id', $instanceProperty->getValue($route)); + $this->assertAttributeEquals(array( + 'id' => '\d+' + ), 'conditions', $route); } public function testMatchesWildcard() { $route = new \Slim\Route('/hello/:path+/world', function () {}); - $property = new \ReflectionProperty($route, 'params'); - $property->setAccessible(true); - $this->assertTrue($route->matches('/hello/foo/bar/world')); - $this->assertEquals(array( + $this->assertAttributeEquals(array( 'path' => array('foo', 'bar') - ), $property->getValue($route)); + ), 'params', $route); } public function testMatchesMultipleWildcards() { $route = new \Slim\Route('/hello/:path+/world/:date+', function () {}); - $property = new \ReflectionProperty($route, 'params'); - $property->setAccessible(true); - $this->assertTrue($route->matches('/hello/foo/bar/world/2012/03/10')); - $this->assertEquals(array( + $this->assertAttributeEquals(array( 'path' => array('foo', 'bar'), 'date' => array('2012', '03', '10') - ), $property->getValue($route)); + ), 'params', $route); } public function testMatchesParamsAndWildcards() { $route = new \Slim\Route('/hello/:path+/world/:year/:month/:day/:path2+', function () {}); - $property = new \ReflectionProperty($route, 'params'); - $property->setAccessible(true); - $this->assertTrue($route->matches('/hello/foo/bar/world/2012/03/10/first/second')); - $this->assertEquals(array( + $this->assertAttributeEquals(array( 'path' => array('foo', 'bar'), 'year' => '2012', 'month' => '03', 'day' => '10', 'path2' => array('first', 'second') - ), $property->getValue($route)); + ), 'params', $route); } public function testMatchesParamsWithOptionalWildcard() @@ -381,13 +358,9 @@ public function testSetMiddleware() $mw = function () { echo 'Foo'; }; - - $property = new \ReflectionProperty($route, 'middleware'); - $property->setAccessible(true); - $route->setMiddleware($mw); - $this->assertSame($mw, $property->getValue($route)[0]); + $this->assertAttributeContains($mw, 'middleware', $route); } public function testSetMiddlewareMultipleTimes() @@ -399,15 +372,11 @@ public function testSetMiddlewareMultipleTimes() $mw2 = function () { echo 'Bar'; }; - - $property = new \ReflectionProperty($route, 'middleware'); - $property->setAccessible(true); - $route->setMiddleware($mw1); $route->setMiddleware($mw2); - $this->assertSame($mw1, $property->getValue($route)[0]); - $this->assertSame($mw2, $property->getValue($route)[1]); + $this->assertAttributeContains($mw1, 'middleware', $route); + $this->assertAttributeContains($mw2, 'middleware', $route); } public function testSetMiddlewareWithArray() @@ -419,14 +388,10 @@ public function testSetMiddlewareWithArray() $mw2 = function () { echo 'Bar'; }; - - $property = new \ReflectionProperty($route, 'middleware'); - $property->setAccessible(true); - $route->setMiddleware(array($mw1, $mw2)); - $this->assertSame($mw1, $property->getValue($route)[0]); - $this->assertSame($mw2, $property->getValue($route)[1]); + $this->assertAttributeContains($mw1, 'middleware', $route); + $this->assertAttributeContains($mw2, 'middleware', $route); } public function testSetMiddlewareWithInvalidArgument() @@ -442,10 +407,6 @@ public function testSetMiddlewareWithArrayWithInvalidArgument() $this->setExpectedException('InvalidArgumentException'); $route = new \Slim\Route('/foo', function () {}); - - $property = new \ReflectionProperty($route, 'middleware'); - $property->setAccessible(true); - $route->setMiddleware(array('doesNotExist')); } @@ -463,13 +424,9 @@ public function testGetMiddleware() public function testSetHttpMethods() { $route = new \Slim\Route('/foo', function () {}); - - $property = new \ReflectionProperty($route, 'methods'); - $property->setAccessible(true); - $route->setHttpMethods('GET', 'POST'); - $this->assertEquals(array('GET', 'POST'), $property->getValue($route)); + $this->assertAttributeEquals(array('GET', 'POST'), 'methods', $route); } public function testGetHttpMethods() @@ -493,20 +450,15 @@ public function testAppendHttpMethods() $route->appendHttpMethods('PUT'); - $this->assertEquals(array('GET', 'POST', 'PUT'), $property->getValue($route)); + $this->assertAttributeEquals(array('GET', 'POST', 'PUT'), 'methods', $route); } public function testAppendHttpMethodsWithVia() { $route = new \Slim\Route('/foo', function () {}); - - $property = new \ReflectionProperty($route, 'methods'); - $property->setAccessible(true); - $property->setValue($route, array('GET', 'POST')); - $route->via('PUT'); - $this->assertEquals(array('GET', 'POST', 'PUT'), $property->getValue($route)); + $this->assertAttributeContains('PUT', 'methods', $route); } public function testSupportsHttpMethod() @@ -520,4 +472,9 @@ public function testSupportsHttpMethod() $this->assertTrue($route->supportsHttpMethod('POST')); $this->assertFalse($route->supportsHttpMethod('PUT')); } + + public function testDispatch() + { + + } } From ffbc914ef24601e4a2642ad53229b313c9eb9fde Mon Sep 17 00:00:00 2001 From: = Date: Sat, 9 Mar 2013 22:23:09 -0500 Subject: [PATCH 14/66] Refactor Router unit tests --- tests/RouterTest.php | 600 +++++++++---------------------------------- 1 file changed, 122 insertions(+), 478 deletions(-) diff --git a/tests/RouterTest.php b/tests/RouterTest.php index cb5cc33f1..378289c7c 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -32,571 +32,215 @@ 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. + * Constructor should initialize routes as empty array */ - public function testUrlForNamedRouteWithoutParams() + public function testConstruct() { $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'))); + $this->assertAttributeEquals(array(), 'routes', $router); } /** - * Router::urlFor should throw an exception if Route with name - * does not exist. - * @expectedException \RuntimeException + * Map should set and return instance of \Slim\Route */ - public function testUrlForNamedRouteThatDoesNotExist() + public function testMap() { $router = new \Slim\Router(); - $route = $router->map('/foo/bar', function () {})->via('GET'); - $router->addNamedRoute('bar', $route); - $router->urlFor('foo'); + $route = $router->map('/foo', function() {}); + + $this->assertInstanceOf('\Slim\Route', $route); + $this->assertAttributeContains($route, 'routes', $router); } /** - * Router::addNamedRoute should throw an exception if named Route - * with same name already exists. + * Named route should be added and indexed by name */ - public function testNamedRouteWithExistingName() + public function testAddNamedRoute() { - $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); + $route = new \Slim\Route('/foo', function () {}); + $router->addNamedRoute('foo', $route); + + $property = new \ReflectionProperty($router, 'namedRoutes'); + $property->setAccessible(true); + + $this->assertSame($route, $property->getValue($router)['foo']); } /** - * 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; + * Named route should have unique name */ - public function testHasNamedRoute() + public function testAddNamedRouteWithDuplicateKey() { + $this->setExpectedException('RuntimeException'); + $router = new \Slim\Router(); - $route = $router->map('/foo', function () {})->via('GET'); + $route = new \Slim\Route('/foo', function () {}); + $router->addNamedRoute('foo', $route); $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; + * Router should return named route by name, or null if not found */ public function testGetNamedRoute() { $router = new \Slim\Router(); - $route1 = $router->map('/foo', function () {})->via('GET'); - $router->addNamedRoute('foo', $route1); - $this->assertSame($route1, $router->getNamedRoute('foo')); + $route = new \Slim\Route('/foo', function () {}); + + $property = new \ReflectionProperty($router, 'namedRoutes'); + $property->setAccessible(true); + $property->setValue($router, array('foo' => $route)); + + $this->assertSame($route, $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; + * Router should determine named routes and cache results */ 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()); - } + $route1 = new \Slim\Route('/foo', function () {}); + $route2 = new \Slim\Route('/bar', function () {}); - /** - * 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); - } + // Init router routes to array + $propertyRouterRoutes = new \ReflectionProperty($router, 'routes'); + $propertyRouterRoutes->setAccessible(true); + $propertyRouterRoutes->setValue($router, array($route1, $route2)); - /** - * 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'))); + // Init router named routes to null + $propertyRouterNamedRoutes = new \ReflectionProperty($router, 'namedRoutes'); + $propertyRouterNamedRoutes->setAccessible(true); + $propertyRouterNamedRoutes->setValue($router, null); + + // Init route name + $propertyRouteName = new \ReflectionProperty($route2, 'name'); + $propertyRouteName->setAccessible(true); + $propertyRouteName->setValue($route2, 'bar'); + + $namedRoutes = $router->getNamedRoutes(); + $this->assertCount(1, $namedRoutes); + $this->assertSame($route2, $namedRoutes['bar']); } /** - * Test that router returns no matches when neither HTTP method nor URI match. + * Router should detect presence of a named route by name */ - public function testRouterMatchesRoutesNone() + public function testHasNamedRoute() { - \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()))); + $route = new \Slim\Route('/foo', function () {}); + + $property = new \ReflectionProperty($router, 'namedRoutes'); + $property->setAccessible(true); + $property->setValue($router, array('foo' => $route)); + + $this->assertTrue($router->hasNamedRoute('foo')); + $this->assertFalse($router->hasNamedRoute('bar')); } /** - * Test that router returns no matches when HTTP method matches but URI does not. + * Router should set current route and dispatch returns non-false value */ - public function testRouterMatchesRoutesNoneWhenMethodMatchesUriDoesNot() + public function testDispatch() { - \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()))); + $route = new \Slim\Route('/foo', function () {}); + + $this->assertTrue($router->dispatch($route) !== false); + $this->assertAttributeSame($route, 'currentRoute', $router); } /** - * Test that router returns no matches when HTTP method does not match but URI does. + * Router should return current route if set during iteration */ - public function testRouterMatchesRoutesNoneWhenMethodDoesNotMatchUriMatches() + public function testGetCurrentRoute() { - \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()))); + $route = new \Slim\Route('/foo', function () {}); + + $property = new \ReflectionProperty($router, 'currentRoute'); + $property->setAccessible(true); + $property->setValue($router, $route); + + $this->assertSame($route, $router->getCurrentRoute()); } /** - * Test that router returns matched routes based on HTTP method and URI. + * Router should return first matching route if current route not set yet by iteration */ - public function testRouterMatchesRoutes() + public function testGetCurrentRouteIfMatchedRoutes() { - \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()))); - } + $route = new \Slim\Route('/foo', function () {}); - /** - * 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()); - } + $propertyMatchedRoutes = new \ReflectionProperty($router, 'matchedRoutes'); + $propertyMatchedRoutes->setAccessible(true); + $propertyMatchedRoutes->setValue($router, array($route)); - /** - * 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(); - } + $propertyCurrentRoute = new \ReflectionProperty($router, 'currentRoute'); + $propertyCurrentRoute->setAccessible(true); + $propertyCurrentRoute->setValue($router, null); - /** - * 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()); + $this->assertSame($route, $router->getCurrentRoute()); } /** - * Test get current route during slim.before.dispatch hook + * Router should return `null` if current route not set yet and there are no matching routes */ - public function testGetCurrentRouteDuringBeforeDispatchHook() + public function testGetCurrentRouteIfNoMatchedRoutes() { - $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); - } + $router = new \Slim\Router(); - /** - * 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); - } + $propertyMatchedRoutes = new \ReflectionProperty($router, 'matchedRoutes'); + $propertyMatchedRoutes->setAccessible(true); + $propertyMatchedRoutes->setValue($router, array()); - /** - * 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()); - } + $propertyCurrentRoute = new \ReflectionProperty($router, 'currentRoute'); + $propertyCurrentRoute->setAccessible(true); + $propertyCurrentRoute->setValue($router, null); - 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); + $this->assertNull($router->getCurrentRoute()); } - public function testDispatchWithMiddlware() + public function testGetMatchedRoutes() { - $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); + $route1 = (new \Slim\Route('/foo', function () {}))->via('GET'); + $route2 = (new \Slim\Route('/foo', function () {}))->via('POST'); + $route3 = (new \Slim\Route('/bar', function () {}))->via('PUT'); + + $routes = new \ReflectionProperty($router, 'routes'); + $routes->setAccessible(true); + $routes->setValue($router, array($route1, $route2, $route3)); + + $matchedRoutes = $router->getMatchedRoutes('GET', '/foo'); + $this->assertSame($route1, $matchedRoutes[0]); } - public function testRouteMiddlwareArguments() + // Test url for named route + + public function testUrlFor() { - $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); + $route1 = (new \Slim\Route('/hello/:first/:last', function () {}))->via('GET')->name('hello'); + + $routes = new \ReflectionProperty($router, 'namedRoutes'); + $routes->setAccessible(true); + $routes->setValue($router, array('hello' => $route1)); + + $this->assertEquals('/hello/Josh/Lockhart', $router->urlFor('hello', array('first' => 'Josh', 'last' => 'Lockhart'))); } - public function testDispatchWithoutCallable() + public function testUrlForIfNoSuchRoute() { - $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); + $this->setExpectedException('RuntimeException'); + $router = new \Slim\Router(); - $route = new \Slim\Route('/hello/:name', 'foo'); // <-- Fail fast + $router->urlFor('foo', array('abc' => '123')); } } From 9360394e13b6c86e4f687a8c961ae03cbfb4037f Mon Sep 17 00:00:00 2001 From: = Date: Sat, 9 Mar 2013 22:23:18 -0500 Subject: [PATCH 15/66] Refactor View unit tests --- tests/ViewTest.php | 245 +++++++++++++++++++++------------------------ 1 file changed, 114 insertions(+), 131 deletions(-) diff --git a/tests/ViewTest.php b/tests/ViewTest.php index d7f501cef..f8dfc98d7 100644 --- a/tests/ViewTest.php +++ b/tests/ViewTest.php @@ -32,164 +32,147 @@ class ViewTest extends PHPUnit_Framework_TestCase { - public function setUp() + public function testViewIsConstructedWithDataArray() { - $this->view = new \Slim\View(); + $view = new \Slim\View(); + $this->assertAttributeEquals(array(), 'data', $view); } - public function generateTestData() + public function testGetDataAll() { - return array('a' => 1, 'b' => 2, 'c' => 3); + $view = new \Slim\View(); + $property = new \ReflectionProperty($view, 'data'); + $property->setAccessible(true); + $property->setValue($view, array('foo' => 'bar')); + + $this->assertSame(array('foo' => 'bar'), $view->getData()); } - /** - * 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() + public function testGetDataKeyExists() { - $this->assertEquals(array(), $this->view->getData()); + $view = new \Slim\View(); + $property = new \ReflectionProperty($view, 'data'); + $property->setAccessible(true); + $property->setValue($view, array('foo' => 'bar')); + + $this->assertEquals('bar', $view->getData('foo')); } - /** - * 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() + public function testGetDataKeyNotExists() { - //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 ) {} + $view = new \Slim\View(); + $property = new \ReflectionProperty($view, 'data'); + $property->setAccessible(true); + $property->setValue($view, array('foo' => 'bar')); + + $this->assertNull($view->getData('abc')); + } + + public function testSetDataKeyValue() + { + $view = new \Slim\View(); + $view->setData('foo', 'bar'); + + $this->assertAttributeEquals(array('foo' => 'bar'), 'data', $view); } - /** - * 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() + public function testSetDataArray() { - //Case A - $this->view->appendData(array('a' => 'A')); - $this->view->appendData(array('b' => 'B')); - $this->assertEquals(array('a' => 'A', 'b' => 'B'), $this->view->getData()); + $view = new \Slim\View(); + $view->setData(array('foo' => 'bar')); - //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 ) {} + $this->assertAttributeEquals(array('foo' => 'bar'), 'data', $view); + } + + public function testSetDataInvalidArgument() + { + $this->setExpectedException('InvalidArgumentException'); + $view = new \Slim\View(); + $view->setData('foo'); } - /** - * 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() + public function testAppendData() { - $templatesDirectory = dirname(__FILE__) . '/templates'; - $this->view->setTemplatesDirectory($templatesDirectory); - $this->assertEquals($templatesDirectory, $this->view->getTemplatesDirectory()); + $view = new \Slim\View(); + $view->appendData(array('foo' => 'bar')); + + $this->assertAttributeEquals(array('foo' => 'bar'), 'data', $view); } - /** - * 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() + public function testAppendDataOverwrite() { - $this->view->setTemplatesDirectory(dirname(__FILE__) . '/templates/'); - $this->assertEquals(dirname(__FILE__) . '/templates', $this->view->getTemplatesDirectory()); + $view = new \Slim\View(); + $property = new \ReflectionProperty($view, 'data'); + $property->setAccessible(true); + $property->setValue($view, array('foo' => 'bar')); + $view->appendData(array('foo' => '123')); + + $this->assertAttributeEquals(array('foo' => '123'), 'data', $view); } - /** - * 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() + public function testAppendDataInvalidArgument() { - $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 ) {} + $this->setExpectedException('InvalidArgumentException'); + + $view = new \Slim\View(); + $view->appendData('foo'); } - /** - * 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() + public function testGetTemplatesDirectory() { - $this->expectOutputString('test output bar'); - $this->view->setTemplatesDirectory(dirname(__FILE__) . '/templates'); - $this->view->setData(array('foo' => 'bar')); - $this->view->display('test.php'); + $view = new \Slim\View(); + $property = new \ReflectionProperty($view, 'templatesDirectory'); + $property->setAccessible(true); + $property->setValue($view, 'templates'); + + $this->assertEquals('templates', $view->getTemplatesDirectory()); + } + + public function testSetTemplatesDirectory() + { + $view = new \Slim\View(); + $view->setTemplatesDirectory('templates/'); // <-- Should strip trailing slash + + $this->assertAttributeEquals('templates', 'templatesDirectory', $view); + } + + public function testSetTemplateExists() + { + $view = new \Slim\View(); + $property = new \ReflectionProperty($view, 'templatesDirectory'); + $property->setAccessible(true); + $property->setValue($view, dirname(__FILE__) . '/templates'); + $view->setTemplate('test.php'); + + $this->assertAttributeEquals(dirname(__FILE__) . '/templates/test.php', 'templatePath', $view); + } + + public function testSetTemplateNotExists() + { + $this->setExpectedException('RuntimeException'); + + $view = new \Slim\View(); + $property = new \ReflectionProperty($view, 'templatesDirectory'); + $property->setAccessible(true); + $property->setValue($view, dirname(__FILE__) . '/templates'); + $view->setTemplate('foo.php'); } + public function testDisplay() + { + $this->expectOutputString('test output bar'); + + $view = new \Slim\View(); + + $property1 = new \ReflectionProperty($view, 'data'); + $property1->setAccessible(true); + $property1->setValue($view, array('foo' => 'bar')); + + $property2 = new \ReflectionProperty($view, 'templatesDirectory'); + $property2->setAccessible(true); + $property2->setValue($view, dirname(__FILE__) . '/templates'); + + $view->display('test.php'); + } } From e8b708e01a523f58ebc67b258eef3fe004c7c44e Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Tue, 12 Mar 2013 18:56:09 +0200 Subject: [PATCH 16/66] Added 'ANY' Route Method --- Slim/Router.php | 2 +- Slim/Slim.php | 12 ++++++++++++ tests/SlimTest.php | 24 ++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Slim/Router.php b/Slim/Router.php index 44979ea0a..ac1da8bd6 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -100,7 +100,7 @@ public function getMatchedRoutes($httpMethod, $resourceUri, $reload = false) if ($reload || is_null($this->matchedRoutes)) { $this->matchedRoutes = array(); foreach ($this->routes as $route) { - if (!$route->supportsHttpMethod($httpMethod)) { + if (!$route->supportsHttpMethod($httpMethod) && !$route->supportsHttpMethod("ANY")) { continue; } diff --git a/Slim/Slim.php b/Slim/Slim.php index 5e10c6073..50714abb5 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -482,6 +482,18 @@ public function options() return $this->mapRoute($args)->via(\Slim\Http\Request::METHOD_OPTIONS); } + /** + * Add route for any HTTP method + * @see mapRoute() + * @return \Slim\Route + */ + public function any() + { + $args = func_get_args(); + + return $this->mapRoute($args)->via("ANY"); + } + /** * Not Found Handler * diff --git a/tests/SlimTest.php b/tests/SlimTest.php index a1aa3ad76..034656e56 100644 --- a/tests/SlimTest.php +++ b/tests/SlimTest.php @@ -413,6 +413,30 @@ public function testOptionsRoute() $this->assertSame($callable, $route->getCallable()); } + /** + * Test ANY route + */ + public function testAnyRoute() + { + $mw1 = function () { echo "foo"; }; + $mw2 = function () { echo "bar"; }; + $callable = function () { echo "xyz"; }; + $methods = array('GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'); + foreach ($methods as $i => $method) { + \Slim\Environment::mock(array( + 'REQUEST_METHOD' => $method, + 'SCRIPT_NAME' => '/foo', //<-- Physical + 'PATH_INFO' => '/bar', //<-- Virtual + )); + $s = new \Slim\Slim(); + $route = $s->any('/bar', $mw1, $mw2, $callable); + $s->call(); + $this->assertEquals('foobarxyz', $s->response()->body()); + $this->assertEquals('/bar', $route->getPattern()); + $this->assertSame($callable, $route->getCallable()); + } + } + /** * Test if route does NOT expect trailing slash and URL has one */ From 88f196432dbf36846b1dc3da1b80fb2db151f5f6 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 22 Mar 2013 20:56:30 -0400 Subject: [PATCH 17/66] Add Set helper to be extended for collections like cookies and headers --- Slim/Helper/Set.php | 150 +++++++++++++++++++++++++++++++++++ tests/Helper/SetTest.php | 166 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 316 insertions(+) create mode 100644 Slim/Helper/Set.php create mode 100644 tests/Helper/SetTest.php diff --git a/Slim/Helper/Set.php b/Slim/Helper/Set.php new file mode 100644 index 000000000..ff52de9e7 --- /dev/null +++ b/Slim/Helper/Set.php @@ -0,0 +1,150 @@ +add($items); + } + + /** + * Normalize data key + * + * Used to transform data key into the necessary + * key format for this set. Used in subclasses + * like \Slim\Http\Headers. + * + * @param string $key The data key + * @return mixed The transformed/normalized data key + */ + protected function normalizeKey($key) + { + return $key; + } + + /** + * Set data key to value + * @param string $key The data key + * @param mixed $value The data value + */ + public function set($key, $value) + { + $this->data[$this->normalizeKey($key)] = $value; + } + + /** + * Get data value with key + * @param string $key The data key + * @param mixed $default The value to return if data key does not exist + * @return mixed The data value, or the default value + */ + public function get($key, $default = null) + { + if ($this->has($key)) { + return $this->data[$this->normalizeKey($key)]; + } + + return $default; + } + + /** + * Add data to set + * @param array $items Key-value array of data to append to this set + */ + public function add($items) + { + foreach ($items as $key => $value) { + $this->set($key, $value); // Ensure keys are normalized + } + } + + /** + * Fetch set data + * @return array This set's key-value data array + */ + public function all() + { + return $this->data; + } + + /** + * Fetch set data keys + * @return array This set's key-value data array keys + */ + public function keys() + { + return array_keys($this->data); + } + + /** + * Does this set contain a key? + * @param string $key The data key + * @return boolean + */ + public function has($key) + { + return array_key_exists($this->normalizeKey($key), $this->data); + } + + /** + * Remove value with key from this set + * @param string $key The data key + */ + public function remove($key) + { + unset($this->data[$this->normalizeKey($key)]); + } + + /** + * Array Access + */ + + public function offsetExists($offset) + { + return $this->has($offset); + } + + public function offsetGet($offset) + { + return $this->get($offset); + } + + public function offsetSet($offset, $value) + { + $this->set($offset, $value); + } + + public function offsetUnset($offset) + { + $this->remove($offset); + } + + /** + * Countable + */ + + public function count() + { + return count($this->data); + } + + /** + * IteratorAggregate + */ + + public function getIterator() + { + return new \ArrayIterator($this->data); + } +} diff --git a/tests/Helper/SetTest.php b/tests/Helper/SetTest.php new file mode 100644 index 000000000..f454941a2 --- /dev/null +++ b/tests/Helper/SetTest.php @@ -0,0 +1,166 @@ + + * @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 SetTest extends PHPUnit_Framework_TestCase +{ + public function setUp() + { + $this->bag = new \Slim\Helper\Set(); + $this->property = new \ReflectionProperty($this->bag, 'data'); + $this->property->setAccessible(true); + } + + public function testSet() + { + $this->bag->set('foo', 'bar'); + $this->assertArrayHasKey('foo', $this->property->getValue($this->bag)); + $this->assertEquals('bar', $this->property->getValue($this->bag)['foo']); + } + + public function testGet() + { + $this->property->setValue($this->bag, array('foo' => 'bar')); + $this->assertEquals('bar', $this->bag->get('foo')); + } + + public function testGetNotExists() + { + $this->property->setValue($this->bag, array('foo' => 'bar')); + $this->assertEquals('default', $this->bag->get('abc', 'default')); + } + + public function testAdd() + { + $this->bag->add(array( + 'abc' => '123', + 'foo' => 'bar' + )); + $this->assertArrayHasKey('abc', $this->property->getValue($this->bag)); + $this->assertArrayHasKey('foo', $this->property->getValue($this->bag)); + $this->assertEquals('123', $this->property->getValue($this->bag)['abc']); + $this->assertEquals('bar', $this->property->getValue($this->bag)['foo']); + } + + public function testAll() + { + $data = array( + 'abc' => '123', + 'foo' => 'bar' + ); + $this->property->setValue($this->bag, $data); + $this->assertEquals($data, $this->bag->all()); + } + + public function testKeys() + { + $data = array( + 'abc' => '123', + 'foo' => 'bar' + ); + $this->property->setValue($this->bag, $data); + $this->assertEquals(array('abc', 'foo'), $this->bag->keys()); + } + + public function testRemove() + { + $data = array( + 'abc' => '123', + 'foo' => 'bar' + ); + $this->property->setValue($this->bag, $data); + $this->bag->remove('foo'); + $this->assertEquals(array('abc' => '123'), $this->property->getValue($this->bag)); + } + + public function testArrayAccessGet() + { + $data = array( + 'abc' => '123', + 'foo' => 'bar' + ); + $this->property->setValue($this->bag, $data); + $this->assertEquals('bar', $this->bag['foo']); + } + + public function testArrayAccessSet() + { + $data = array( + 'abc' => '123', + 'foo' => 'bar' + ); + $this->property->setValue($this->bag, $data); + $this->bag['foo'] = 'changed'; + $this->assertEquals('changed', $this->property->getValue($this->bag)['foo']); + } + + public function testArrayAccessExists() + { + $data = array( + 'abc' => '123', + 'foo' => 'bar' + ); + $this->property->setValue($this->bag, $data); + $this->assertTrue(isset($this->bag['foo'])); + $this->assertFalse(isset($this->bag['bar'])); + } + + public function testArrayAccessUnset() + { + $data = array( + 'abc' => '123', + 'foo' => 'bar' + ); + $this->property->setValue($this->bag, $data); + unset($this->bag['foo']); + $this->assertEquals(array('abc' => '123'), $this->property->getValue($this->bag)); + } + + public function testCount() + { + $data = array( + 'abc' => '123', + 'foo' => 'bar' + ); + $this->property->setValue($this->bag, $data); + $this->assertEquals(2, count($this->bag)); + } + + public function testGetIterator() + { + $data = array( + 'abc' => '123', + 'foo' => 'bar' + ); + $this->property->setValue($this->bag, $data); + $this->assertInstanceOf('\ArrayIterator', $this->bag->getIterator()); + } +} From bf3c493363a92e4fb9cd6cd8be1679b3140c709b Mon Sep 17 00:00:00 2001 From: = Date: Fri, 22 Mar 2013 20:56:51 -0400 Subject: [PATCH 18/66] Let Headers extend Set --- Slim/Http/Headers.php | 134 +--------------------------- tests/Http/HeadersTest.php | 176 +++++++++++++++++++++---------------- 2 files changed, 103 insertions(+), 207 deletions(-) diff --git a/Slim/Http/Headers.php b/Slim/Http/Headers.php index 0185f086b..2691f4426 100644 --- a/Slim/Http/Headers.php +++ b/Slim/Http/Headers.php @@ -35,147 +35,19 @@ /** * HTTP Headers * - * This class is an abstraction of the HTTP response headers and - * provides array access to the header list while automatically - * stores and retrieves headers with lowercase canonical keys regardless - * of the input format. - * - * This class also implements the `Iterator` and `Countable` - * interfaces for even more convenient usage. - * * @package Slim * @author Josh Lockhart * @since 1.6.0 */ -class Headers implements \ArrayAccess, \Iterator, \Countable +class Headers extends \Slim\Helper\Set { - /** - * @var array HTTP headers - */ - protected $headers; - - /** - * @var array Map canonical header name to original header name - */ - protected $map; - - /** - * Constructor - * @param array $headers - */ - public function __construct($headers = array()) - { - $this->merge($headers); - } - - /** - * Merge Headers - * @param array $headers - */ - public function merge($headers) - { - foreach ($headers as $name => $value) { - $this[$name] = $value; - } - } - /** * Transform header name into canonical form * @param string $name * @return string */ - protected function canonical($name) - { - return strtolower(trim($name)); - } - - /** - * Array Access: Offset Exists - */ - public function offsetExists($offset) - { - return isset($this->headers[$this->canonical($offset)]); - } - - /** - * Array Access: Offset Get - */ - public function offsetGet($offset) - { - $canonical = $this->canonical($offset); - if (isset($this->headers[$canonical])) { - return $this->headers[$canonical]; - } else { - return null; - } - } - - /** - * Array Access: Offset Set - */ - public function offsetSet($offset, $value) - { - $canonical = $this->canonical($offset); - $this->headers[$canonical] = $value; - $this->map[$canonical] = $offset; - } - - /** - * Array Access: Offset Unset - */ - public function offsetUnset($offset) - { - $canonical = $this->canonical($offset); - unset($this->headers[$canonical], $this->map[$canonical]); - } - - /** - * Countable: Count - */ - public function count() - { - return count($this->headers); - } - - /** - * Iterator: Rewind - */ - public function rewind() - { - reset($this->headers); - } - - /** - * Iterator: Current - */ - public function current() - { - return current($this->headers); - } - - /** - * Iterator: Key - */ - public function key() - { - $key = key($this->headers); - - return $this->map[$key]; - } - - /** - * Iterator: Next - */ - public function next() - { - return next($this->headers); - } - - /** - * Iterator: Valid - */ - public function valid() + protected function normalizeKey($key) { - return current($this->headers) !== false; + return strtolower(trim($key)); } } diff --git a/tests/Http/HeadersTest.php b/tests/Http/HeadersTest.php index 0b06d3ab1..e11e0ffe2 100644 --- a/tests/Http/HeadersTest.php +++ b/tests/Http/HeadersTest.php @@ -32,112 +32,136 @@ class HeadersTest extends PHPUnit_Framework_TestCase { - /** - * Test constructor without args - */ - public function testConstructorWithoutArgs() + public function testConstructWithoutArg() { - $h = new \Slim\Http\Headers(); - $this->assertEquals(0, count($h)); + $headers = new \Slim\Http\Headers(); + + $this->assertAttributeEquals(array(), 'headers', $headers); } - /** - * Test constructor with args - */ - public function testConstructorWithArgs() + public function testConstructWithArg() { - $h = new \Slim\Http\Headers(array('Content-Type' => 'text/html')); - $this->assertEquals(1, count($h)); + $headers = new \Slim\Http\Headers(array('Content-Type' => 'text/html')); + + $this->assertAttributeEquals(array('content-type' => 'text/html'), 'headers', $headers); } - /** - * Test get and set header - */ - public function testSetAndGetHeader() + public function testMerge() { - $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']); + $headers = new \Slim\Http\Headers(); + + $property = new \ReflectionProperty($headers, 'headers'); + $property->setAccessible(true); + $property->setValue($headers, array('content-length' => 100)); + + $headers->merge(array('Content-Type' => 'text/html')); + + $this->assertAttributeEquals( + array( + 'content-type' => 'text/html', + 'content-length' => 100 + ), + 'headers', + $headers + ); } - /** - * Test get non-existent header - */ - public function testGetNonExistentHeader() + public function testIterable() { - $h = new \Slim\Http\Headers(); - $this->assertNull($h['foo']); + $headers = new \Slim\Http\Headers(array( + 'Content-Type' => 'text/html', + 'Content-Length' => 100 + )); + $iteratorResults = array(); + + foreach ($headers as $name => $value) { + $iteratorResults[$name] = $value; + } + + $this->assertEquals( + array( + 'Content-Type' => 'text/html', + 'Content-Length' => 100 + ), + $iteratorResults + ); } - /** - * Test isset header - */ - public function testHeaderIsSet() + public function testCountable() { - $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'])); + $headers = new \Slim\Http\Headers(array( + 'Content-Type' => 'text/html', + 'Content-Length' => 100 + )); + + $this->assertEquals(2, count($headers)); } /** - * Test unset header + * @covers Slim\Http\Response::setHeader */ - public function testUnsetHeader() + public function testArrayAccessSetter() { - $h = new \Slim\Http\Headers(); - $h['Content-Type'] = 'text/html'; - $this->assertEquals(1, count($h)); - unset($h['Content-Type']); - $this->assertEquals(0, count($h)); + $headers = new \Slim\Http\Headers(); + $headers['Content-Length'] = 100; + + $this->assertAttributeEquals( + array('content-length' => 100), + 'headers', + $headers + ); } /** - * Test merge headers + * @covers Slim\Http\Headers::offsetGet + * @covers Slim\Http\Response::getHeader */ - public function testMergeHeaders() + public function testArrayAccessGetterExists() { - $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']); + $headers = new \Slim\Http\Headers(array( + 'Content-Type' => 'text/html', + 'Content-Length' => 100 + )); + + $this->assertEquals('text/html', $headers['Content-Type']); } /** - * Test iteration + * @covers Slim\Http\Headers::offsetGet + * @covers Slim\Http\Response::getHeader */ - public function testIteration() + public function testArrayAccessGetterNotExists() { - $h = new \Slim\Http\Headers(); - $h['One'] = 'Foo'; - $h['Two'] = 'Bar'; - $output = ''; - foreach ($h as $key => $value) { - $output .= $key . $value; - } - $this->assertEquals('OneFooTwoBar', $output); + $headers = new \Slim\Http\Headers(array( + 'Content-Type' => 'text/html', + 'Content-Length' => 100 + )); + + $this->assertNull($headers['foo']); } - /** - * Test outputs header name in original form, not canonical form - */ - public function testOutputsOriginalNotCanonicalName() + public function testArrayAccessExists() { - $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); + $headers = new \Slim\Http\Headers(array( + 'Content-Type' => 'text/html', + 'Content-Length' => 100 + )); + + $this->assertTrue(isset($headers['Content-Type'])); + } + + public function testArrayAccessUnset() + { + $headers = new \Slim\Http\Headers(array( + 'Content-Type' => 'text/html', + 'Content-Length' => 100 + )); + unset($headers['Content-Type']); + + $this->assertAttributeEquals( + array('content-length' => 100), + 'headers', + $headers + ); } } From 24f97e6c92197d13c80925e4901946f0fe4e2baa Mon Sep 17 00:00:00 2001 From: = Date: Fri, 22 Mar 2013 20:58:17 -0400 Subject: [PATCH 19/66] Let Cookies extend Set --- Slim/Http/Cookies.php | 31 ++++++++++++ tests/Http/CookiesTest.php | 96 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 Slim/Http/Cookies.php create mode 100644 tests/Http/CookiesTest.php diff --git a/Slim/Http/Cookies.php b/Slim/Http/Cookies.php new file mode 100644 index 000000000..4e6fe8294 --- /dev/null +++ b/Slim/Http/Cookies.php @@ -0,0 +1,31 @@ + '', + 'domain' => null, + 'path' => null, + 'expires' => null, + 'secure' => false, + 'httponly' => false + ); + + public function set($key, $value) + { + if (is_array($value)) { + $cookieSettings = array_replace($this->defaults, $value); + } else { + $cookieSettings = array_replace($this->defaults, array('value' => $value)); + } + parent::set($key, $cookieSettings); + } + + public function remove($key, $settings = array()) + { + $settings['value'] = ''; + $settings['expires'] = time() - 86400; + $this->set($key, array_replace($this->defaults, $settings)); + } +} diff --git a/tests/Http/CookiesTest.php b/tests/Http/CookiesTest.php new file mode 100644 index 000000000..4d014e0d8 --- /dev/null +++ b/tests/Http/CookiesTest.php @@ -0,0 +1,96 @@ + + * @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 CookiesTest extends PHPUnit_Framework_TestCase +{ + public function testSetWithStringValue() + { + $c = new \Slim\Http\Cookies(); + $c->set('foo', 'bar'); + + $this->assertAttributeEquals( + array( + 'foo' => array( + 'value' => 'bar', + 'expires' => null, + 'domain' => null, + 'path' => null, + 'secure' => false, + 'httponly' => false + ) + ), + 'data', + $c + ); + } + + public function testSetWithArrayValue() + { + $now = time(); + $c = new \Slim\Http\Cookies(); + $c->set('foo', array( + 'value' => 'bar', + 'expires' => $now + 86400, + 'domain' => '.example.com', + 'path' => '/', + 'secure' => true, + 'httponly' => true + )); + + $this->assertAttributeEquals( + array( + 'foo' => array( + 'value' => 'bar', + 'expires' => $now + 86400, + 'domain' => '.example.com', + 'path' => '/', + 'secure' => true, + 'httponly' => true + ) + ), + 'data', + $c + ); + } + + public function testRemove() + { + $c = new \Slim\Http\Cookies(); + $c->remove('foo'); + + $prop = new \ReflectionProperty($c, 'data'); + $prop->setAccessible(true); + $cValue = $prop->getValue($c); + + $this->assertEquals('', $cValue['foo']['value']); + $this->assertLessThan(time(), $cValue['foo']['expires']); + } +} From 0efd38f620694f1149a502255a4dbd8b437efed1 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 22 Mar 2013 20:58:46 -0400 Subject: [PATCH 20/66] Let Response use new Cookies implementation --- Slim/Http/Response.php | 152 ++++++++---- tests/Http/ResponseTest.php | 477 +++++++----------------------------- 2 files changed, 191 insertions(+), 438 deletions(-) diff --git a/Slim/Http/Response.php b/Slim/Http/Response.php index 8e95b376f..823eaa575 100644 --- a/Slim/Http/Response.php +++ b/Slim/Http/Response.php @@ -51,9 +51,14 @@ class Response implements \ArrayAccess, \Countable, \IteratorAggregate protected $status; /** - * @var \Slim\Http\Headers List of HTTP response headers + * @var \Slim\Http\Headers */ - protected $header; + public $headers; + + /** + * @var \Slim\Http\Cookies + */ + public $cookies; /** * @var string HTTP response body @@ -125,19 +130,28 @@ class Response implements \ArrayAccess, \Countable, \IteratorAggregate * @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()) + public function __construct($body = '', $status = 200, $headers = array()) { - $this->status = (int) $status; - $headers = array(); - foreach ($header as $key => $value) { - $headers[$key] = $value; - } - $this->header = new Headers(array_merge(array('Content-Type' => 'text/html'), $headers)); - $this->body = ''; + $this->setStatus($status); + $this->headers = new \Slim\Http\Headers(array('Content-Type' => 'text/html')); + $this->headers->add($headers); + $this->cookies = new \Slim\Http\Cookies(); $this->write($body); } + public function getStatus() + { + return $this->status; + } + + public function setStatus($status) + { + $this->status = (int)$status; + } + /** + * DEPRECATION WARNING! Use `getStatus` or `setStatus` instead. + * * Get and set status * @param int|null $status * @return int @@ -152,6 +166,8 @@ public function status($status = null) } /** + * DEPRECATION WARNING! Access `headers` property directly. + * * Get and set header * @param string $name Header name * @param string|null $value Header value @@ -160,22 +176,36 @@ public function status($status = null) public function header($name, $value = null) { if (!is_null($value)) { - $this[$name] = $value; + $this->headers->set($name, $value); } - return $this[$name]; + return $this->headers->get($name); } /** + * DEPRECATION WARNING! Access `headers` property directly. + * * Get headers * @return \Slim\Http\Headers */ public function headers() { - return $this->header; + return $this->headers; + } + + public function getBody() + { + return $this->body; + } + + public function setBody($content) + { + $this->write($content, true); } /** + * DEPRECATION WARNING! use `getBody` or `setBody` instead. + * * Get and set body * @param string|null $body Content of HTTP response body * @return string @@ -189,20 +219,6 @@ public function body($body = null) return $this->body; } - /** - * Get and set length - * @param int|null $length - * @return int - */ - public function length($length = null) - { - if (!is_null($length)) { - $this->length = (int) $length; - } - - return $this->length; - } - /** * Append HTTP response body * @param string $body Content to append to the current HTTP response body @@ -214,13 +230,34 @@ 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; } + public function getLength() + { + return $this->length; + } + + /** + * DEPRECATION WARNING! Use `getLength` or `write` or `body` instead. + * + * Get and set length + * @param int|null $length + * @return int + */ + public function length($length = null) + { + if (!is_null($length)) { + $this->length = (int) $length; + } + + return $this->length; + } + /** * Finalize * @@ -232,16 +269,24 @@ public function write($body, $replace = false) */ public function finalize() { - if (in_array($this->status, array(204, 304))) { - unset($this['Content-Type'], $this['Content-Length']); + // Serialize cookies into raw HTTP headers + foreach ($this->cookies as $name => $value) { + Util::setCookieHeader($this->headers, $name, $value); + } - return array($this->status, $this->header, ''); - } else { - return array($this->status, $this->header, $this->body); + // Prepare response + if (in_array($this->status, array(204, 304))) { + $this->headers->remove('Content-Type'); + $this->headers->remove('Content-Length'); + $this->setBody(''); } + + return array($this->status, $this->headers, $this->body); } /** + * DEPRECATION WARNING! Access `cookies` property directly. + * * Set cookie * * Instead of using PHP's `setcookie()` function, Slim manually constructs the HTTP `Set-Cookie` @@ -256,10 +301,13 @@ public function finalize() */ public function setCookie($name, $value) { - Util::setCookieHeader($this->header, $name, $value); + // Util::setCookieHeader($this->header, $name, $value); + $this->cookies->set($name, $value); } /** + * DEPRECATION WARNING! Access `cookies` property directly. + * * Delete cookie * * Instead of using PHP's `setcookie()` function, Slim manually constructs the HTTP `Set-Cookie` @@ -277,9 +325,10 @@ public function setCookie($name, $value) * @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()) + public function deleteCookie($name, $settings = array()) { - Util::deleteCookieHeader($this->header, $name, $value); + $this->cookies->remove($name, $settings); + // Util::deleteCookieHeader($this->header, $name, $value); } /** @@ -293,8 +342,8 @@ public function deleteCookie($name, $value = array()) */ public function redirect ($url, $status = 302) { - $this->status = $status; - $this['Location'] = $url; + $this->setStatus($status); + $this->headers->set('Location', $url); } /** @@ -387,12 +436,17 @@ public function isServerError() return $this->status >= 500 && $this->status < 600; } + /** + * DEPRECATION WARNING! ArrayAccess interface will be removed from \Slim\Http\Response. + * Iterate `headers` or `cookies` properties directly. + */ + /** * Array Access: Offset Exists */ public function offsetExists( $offset ) { - return isset($this->header[$offset]); + return isset($this->headers[$offset]); } /** @@ -400,11 +454,7 @@ public function offsetExists( $offset ) */ public function offsetGet( $offset ) { - if (isset($this->header[$offset])) { - return $this->header[$offset]; - } else { - return null; - } + return $this->headers[$offset]; } /** @@ -412,7 +462,7 @@ public function offsetGet( $offset ) */ public function offsetSet($offset, $value) { - $this->header[$offset] = $value; + $this->headers[$offset] = $value; } /** @@ -420,18 +470,24 @@ public function offsetSet($offset, $value) */ public function offsetUnset($offset) { - unset($this->header[$offset]); + unset($this->headers[$offset]); } /** + * DEPRECATION WARNING! Countable interface will be removed from \Slim\Http\Response. + * Call `count` on `headers` or `cookies` properties directly. + * * Countable: Count */ public function count() { - return count($this->header); + return count($this->headers); } /** + * DEPRECATION WARNING! IteratorAggreate interface will be removed from \Slim\Http\Response. + * Iterate `headers` or `cookies` properties directly. + * * Get Iterator * * This returns the contained `\Slim\Http\Headers` instance which @@ -441,7 +497,7 @@ public function count() */ public function getIterator() { - return $this->header; + return $this->headers->getIterator(); } /** diff --git a/tests/Http/ResponseTest.php b/tests/Http/ResponseTest.php index 2321e613c..f8e52c224 100644 --- a/tests/Http/ResponseTest.php +++ b/tests/Http/ResponseTest.php @@ -32,541 +32,238 @@ 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() + public function testConstructWithoutArgs() { - $r = new \Slim\Http\Response(); - $this->assertEquals(200, $r->status()); - } + $res = new \Slim\Http\Response(); - /** - * Test set status - */ - public function testSetStatus() - { - $r = new \Slim\Http\Response(); - $r->status(500); - $this->assertEquals(500, $r->status()); + $this->assertAttributeEquals(200, 'status', $res); + $this->assertAttributeEquals('', 'body', $res); } - /** - * Test get headers - */ - public function testGetHeaders() + public function testConstructWithArgs() { - $r = new \Slim\Http\Response(); - $headers = $r->headers(); - $this->assertEquals(1, count($headers)); - $this->assertEquals('text/html', $headers['Content-Type']); - } + $res = new \Slim\Http\Response('Foo', 201); - /** - * 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')); + $this->assertAttributeEquals(201, 'status', $res); + $this->assertAttributeEquals('Foo', 'body', $res); } - /** - * Test get body - */ - public function testGetBody() + public function testGetStatus() { - $r = new \Slim\Http\Response('Foo'); - $this->assertEquals('Foo', $r->body()); - } + $res = new \Slim\Http\Response(); - /** - * Test set body - */ - public function testSetBody() - { - $r = new \Slim\Http\Response(); - $r->body('Foo'); - $this->assertEquals('Foo', $r->body()); + $this->assertEquals(200, $res->getStatus()); } - /** - * Test get length - */ - public function testGetLength() + public function testSetStatus() { - $r = new \Slim\Http\Response('Foo'); - $this->assertEquals(3, $r->length()); - } + $res = new \Slim\Http\Response(); + $res->setStatus(301); - /** - * Test set length - */ - public function testSetLength() - { - $r = new \Slim\Http\Response(); - $r->length(3); - $this->assertEquals(3, $r->length()); + $this->assertAttributeEquals(301, 'status', $res); } /** - * Test write for appending + * DEPRECATION WARNING! */ - public function testWriteAppend() + public function testStatusGetOld() { - $r = new \Slim\Http\Response('Foo'); - $r->write('Bar'); - $this->assertEquals('FooBar', $r->body()); + $res = new \Slim\Http\Response('', 201); + $this->assertEquals(201, $res->status()); } /** - * Test write for replacing + * DEPRECATION WARNING! */ - public function testWriteReplace() + public function testStatusSetOld() { - $r = new \Slim\Http\Response('Foo'); - $r->write('Bar', true); - $this->assertEquals('Bar', $r->body()); - } + $res = new \Slim\Http\Response(); + $prop = new \ReflectionProperty($res, 'status'); + $prop->setAccessible(true); + $res->status(301); - /** - * 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'])); + $this->assertEquals(301, $prop->getValue($res)); } - /** - * Test finalize - */ - public function testFinalizeWithoutBody() + public function testGetBody() { - $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]); - } + $res = new \Slim\Http\Response(); + $property = new \ReflectionProperty($res, 'body'); + $property->setAccessible(true); + $property->setValue($res, 'foo'); - /** - * 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']); + $this->assertEquals('foo', $res->getBody()); } - /** - * Test set multiple cookies with only name and value - */ - public function testSetMultipleCookiesWithNameAndValue() + public function testSetBody() { - $r = new \Slim\Http\Response(); - $r->setCookie('foo', 'bar'); - $r->setCookie('abc', '123'); - $this->assertEquals("foo=bar\nabc=123", $r['Set-Cookie']); - } + $res = new \Slim\Http\Response('bar'); + $res->setBody('foo'); // <-- Should replace body - /** - * 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']); + $this->assertAttributeEquals('foo', 'body', $res); } - /** - * Test set cookie with only name and value and expires (as string) - */ - public function testSetMultipleCookiesWithNameAndValueAndExpiresAsString() + public function testWrite() { - $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']); - } + $res = new \Slim\Http\Response(); + $property = new \ReflectionProperty($res, 'body'); + $property->setAccessible(true); + $property->setValue($res, 'foo'); + $res->write('bar'); // <-- Should append to body - /** - * 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']); + $this->assertAttributeEquals('foobar', 'body', $res); } - /** - * Test set cookie with name, value, domain, path - */ - public function testSetCookieWithNameAndValueAndDomainAndPath() + public function testLength() { - $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'); - } + $res = new \Slim\Http\Response('foo'); // <-- Sets body and length - /** - * 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']); + $this->assertEquals(3, $res->getLength()); } - /** - * 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() + public function testFinalize() { - $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'])); - } + $res = new \Slim\Http\Response(); + $resFinal = $res->finalize(); - /* - * 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'])); + $this->assertTrue(is_array($resFinal)); + $this->assertEquals(3, count($resFinal)); + $this->assertEquals(200, $resFinal[0]); + $this->assertInstanceOf('\Slim\Http\Headers', $resFinal[1]); + $this->assertEquals('', $resFinal[2]); } - /* - * Test delete cookie by name and domain - */ - public function testDeleteCookieByNameAndDomain2() + public function testFinalizeWithEmptyBody() { - $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'])); - } + $res = new \Slim\Http\Response('Foo', 304); + $resFinal = $res->finalize(); - /** - * 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'])); + $this->assertEquals('', $resFinal[2]); } - /** - * Test redirect - */ public function testRedirect() { - $r = new \Slim\Http\Response(); - $r->redirect('/foo'); - $this->assertEquals(302, $r->status()); - $this->assertEquals('/foo', $r['Location']); - } + $res = new \Slim\Http\Response(); + $res->redirect('/foo'); - /** - * 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']); + $pStatus = new \ReflectionProperty($res, 'status'); + $pStatus->setAccessible(true); + + $this->assertEquals(302, $pStatus->getValue($res)); + $this->assertEquals('/foo', $res->headers['Location']); } - /** - * Test isEmpty - */ public function testIsEmpty() { $r1 = new \Slim\Http\Response(); $r2 = new \Slim\Http\Response(); - $r1->status(404); - $r2->status(201); + $r1->setStatus(404); + $r2->setStatus(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); + $r1->setStatus(404); + $r2->setStatus(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); + $r1->setStatus(403); + $r2->setStatus(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); + $r1->setStatus(100); + $r2->setStatus(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); + $r1->setStatus(404); + $r2->setStatus(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); + $r1->setStatus(200); + $r2->setStatus(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); + $r1->setStatus(200); + $r2->setStatus(201); + $r3->setStatus(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); + $r1->setStatus(307); + $r2->setStatus(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); + $r1->setStatus(307); + $r2->setStatus(304); + $r3->setStatus(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); + $r1->setStatus(500); + $r2->setStatus(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)); From 988928db3096121eb430f3bfeeccf7d4c1cfd75e Mon Sep 17 00:00:00 2001 From: = Date: Fri, 22 Mar 2013 20:59:26 -0400 Subject: [PATCH 21/66] Expose request, response, view, and router as public properties on app object --- Slim/Slim.php | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Slim/Slim.php b/Slim/Slim.php index 5e10c6073..6ca9ef491 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -69,22 +69,22 @@ class Slim /** * @var \Slim\Http\Request */ - protected $request; + public $request; /** * @var \Slim\Http\Response */ - protected $response; + public $response; /** * @var \Slim\Router */ - protected $router; + public $router; /** * @var \Slim\View */ - protected $view; + public $view; /** * @var array @@ -692,8 +692,8 @@ public function render($template, $data = array(), $status = null) public function lastModified($time) { if (is_integer($time)) { - $this->response['Last-Modified'] = date(DATE_RFC1123, $time); - if ($time === strtotime($this->request->headers('IF_MODIFIED_SINCE'))) { + $this->response->headers->set('Last-Modified', date(DATE_RFC1123, $time)); + if ($time === strtotime($this->request->headers->get('IF_MODIFIED_SINCE'))) { $this->halt(304); } } else { @@ -730,7 +730,7 @@ public function etag($value, $type = 'strong') $this->response['ETag'] = $value; //Check conditional GET - if ($etagsHeader = $this->request->headers('IF_NONE_MATCH')) { + if ($etagsHeader = $this->request->headers->get('IF_NONE_MATCH')) { $etags = preg_split('@\s*,\s*@', $etagsHeader); if (in_array($value, $etags) || in_array('*', $etags)) { $this->halt(304); @@ -756,7 +756,7 @@ public function expires($time) if (is_string($time)) { $time = strtotime($time); } - $this->response['Expires'] = gmdate(DATE_RFC1123, $time); + $this->response->headers->set('Expires', gmdate(DATE_RFC1123, $time)); } /******************************************************************************** @@ -779,7 +779,7 @@ public function expires($time) */ public function setCookie($name, $value, $time = null, $path = null, $domain = null, $secure = null, $httponly = null) { - $this->response->setCookie($name, array( + $this->response->cookies->set($name, array( 'value' => $value, 'expires' => is_null($time) ? $this->config('cookies.lifetime') : $time, 'path' => is_null($path) ? $this->config('cookies.path') : $path, @@ -801,7 +801,7 @@ public function setCookie($name, $value, $time = null, $path = null, $domain = n */ public function getCookie($name) { - return $this->request->cookies($name); + return $this->request->cookies->get($name); } /** @@ -847,7 +847,7 @@ public function setEncryptedCookie($name, $value, $expires = null, $path = null, public function getEncryptedCookie($name, $deleteIfInvalid = true) { $value = \Slim\Http\Util::decodeSecureCookie( - $this->request->cookies($name), + $this->request->cookies->get($name), $this->config('cookies.secret_key'), $this->config('cookies.cipher'), $this->config('cookies.cipher_mode') @@ -877,7 +877,7 @@ public function getEncryptedCookie($name, $deleteIfInvalid = true) */ public function deleteCookie($name, $path = null, $domain = null, $secure = null, $httponly = null) { - $this->response->deleteCookie($name, array( + $this->response->cookies->remove($name, array( 'domain' => is_null($domain) ? $this->config('cookies.domain') : $domain, 'path' => is_null($path) ? $this->config('cookies.path') : $path, 'secure' => is_null($secure) ? $this->config('cookies.secure') : $secure, @@ -968,7 +968,7 @@ public function pass() */ public function contentType($type) { - $this->response['Content-Type'] = $type; + $this->response->headers->set('Content-Type', $type); } /** @@ -977,7 +977,7 @@ public function contentType($type) */ public function status($code) { - $this->response->status($code); + $this->response->setStatus($code); } /** From 9ac8fbeae786893c357352e20e66e37c55743065 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 22 Mar 2013 21:14:03 -0400 Subject: [PATCH 22/66] Remove obsolete Headers tests --- tests/Http/HeadersTest.php | 136 ++----------------------------------- 1 file changed, 6 insertions(+), 130 deletions(-) diff --git a/tests/Http/HeadersTest.php b/tests/Http/HeadersTest.php index e11e0ffe2..ce29bb9d1 100644 --- a/tests/Http/HeadersTest.php +++ b/tests/Http/HeadersTest.php @@ -32,136 +32,12 @@ class HeadersTest extends PHPUnit_Framework_TestCase { - public function testConstructWithoutArg() + public function testNormalizesKey() { - $headers = new \Slim\Http\Headers(); - - $this->assertAttributeEquals(array(), 'headers', $headers); - } - - public function testConstructWithArg() - { - $headers = new \Slim\Http\Headers(array('Content-Type' => 'text/html')); - - $this->assertAttributeEquals(array('content-type' => 'text/html'), 'headers', $headers); - } - - public function testMerge() - { - $headers = new \Slim\Http\Headers(); - - $property = new \ReflectionProperty($headers, 'headers'); - $property->setAccessible(true); - $property->setValue($headers, array('content-length' => 100)); - - $headers->merge(array('Content-Type' => 'text/html')); - - $this->assertAttributeEquals( - array( - 'content-type' => 'text/html', - 'content-length' => 100 - ), - 'headers', - $headers - ); - } - - public function testIterable() - { - $headers = new \Slim\Http\Headers(array( - 'Content-Type' => 'text/html', - 'Content-Length' => 100 - )); - $iteratorResults = array(); - - foreach ($headers as $name => $value) { - $iteratorResults[$name] = $value; - } - - $this->assertEquals( - array( - 'Content-Type' => 'text/html', - 'Content-Length' => 100 - ), - $iteratorResults - ); - } - - public function testCountable() - { - $headers = new \Slim\Http\Headers(array( - 'Content-Type' => 'text/html', - 'Content-Length' => 100 - )); - - $this->assertEquals(2, count($headers)); - } - - /** - * @covers Slim\Http\Response::setHeader - */ - public function testArrayAccessSetter() - { - $headers = new \Slim\Http\Headers(); - $headers['Content-Length'] = 100; - - $this->assertAttributeEquals( - array('content-length' => 100), - 'headers', - $headers - ); - } - - /** - * @covers Slim\Http\Headers::offsetGet - * @covers Slim\Http\Response::getHeader - */ - public function testArrayAccessGetterExists() - { - $headers = new \Slim\Http\Headers(array( - 'Content-Type' => 'text/html', - 'Content-Length' => 100 - )); - - $this->assertEquals('text/html', $headers['Content-Type']); - } - - /** - * @covers Slim\Http\Headers::offsetGet - * @covers Slim\Http\Response::getHeader - */ - public function testArrayAccessGetterNotExists() - { - $headers = new \Slim\Http\Headers(array( - 'Content-Type' => 'text/html', - 'Content-Length' => 100 - )); - - $this->assertNull($headers['foo']); - } - - public function testArrayAccessExists() - { - $headers = new \Slim\Http\Headers(array( - 'Content-Type' => 'text/html', - 'Content-Length' => 100 - )); - - $this->assertTrue(isset($headers['Content-Type'])); - } - - public function testArrayAccessUnset() - { - $headers = new \Slim\Http\Headers(array( - 'Content-Type' => 'text/html', - 'Content-Length' => 100 - )); - unset($headers['Content-Type']); - - $this->assertAttributeEquals( - array('content-length' => 100), - 'headers', - $headers - ); + $h = new \Slim\Http\Headers(); + $h->set('Content-Type', 'text/html'); + $prop = new \ReflectionProperty($h, 'data'); + $prop->setAccessible(true); + $this->assertArrayHasKey('content-type', $prop->getValue($h)); } } From 573ae1af2cff2e9676dc0736409a69677ca018af Mon Sep 17 00:00:00 2001 From: = Date: Fri, 22 Mar 2013 21:14:40 -0400 Subject: [PATCH 23/66] Fix errors in Middleware tests --- tests/MiddlewareTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/MiddlewareTest.php b/tests/MiddlewareTest.php index 47ef8bb46..ac6a19d44 100644 --- a/tests/MiddlewareTest.php +++ b/tests/MiddlewareTest.php @@ -39,7 +39,7 @@ class MiddlewareTest extends PHPUnit_Framework_TestCase { public function testSetApplication() { - $app = new MyApplication(); + $app = new stdClass(); $mw = new MyMiddleware(); $mw->setApplication($app); @@ -48,7 +48,7 @@ public function testSetApplication() public function testGetApplication() { - $app = new MyApplication(); + $app = new stdClass(); $mw = new MyMiddleware(); $property = new \ReflectionProperty($mw, 'app'); $property->setAccessible(true); From 327615207bd6851dca79699136ffbef07cf956fa Mon Sep 17 00:00:00 2001 From: = Date: Fri, 22 Mar 2013 21:16:08 -0400 Subject: [PATCH 24/66] Add doc headers to Cookies and Set classes --- Slim/Helper/Set.php | 31 +++++++++++++++++++++++++++++++ Slim/Http/Cookies.php | 31 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/Slim/Helper/Set.php b/Slim/Helper/Set.php index ff52de9e7..ddddb0cfb 100644 --- a/Slim/Helper/Set.php +++ b/Slim/Helper/Set.php @@ -1,4 +1,35 @@ + * @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\Helper; class Set implements \ArrayAccess, \Countable, \IteratorAggregate diff --git a/Slim/Http/Cookies.php b/Slim/Http/Cookies.php index 4e6fe8294..2b7b4f377 100644 --- a/Slim/Http/Cookies.php +++ b/Slim/Http/Cookies.php @@ -1,4 +1,35 @@ + * @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\Http; class Cookies extends \Slim\Helper\Set From 6b83ccdd62352943527235193aaed4b03e146862 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 23 Mar 2013 13:13:23 -0400 Subject: [PATCH 25/66] Fixes to various classes and tests --- Slim/Environment.php | 23 ++-- Slim/Http/Cookies.php | 29 +++++ Slim/Http/Headers.php | 52 ++++++++- Slim/Http/Request.php | 146 +++++++++++++------------ tests/EnvironmentTest.php | 2 +- tests/Http/CookiesTest.php | 4 - tests/Http/HeadersTest.php | 20 +++- tests/Http/RequestTest.php | 63 +++-------- tests/Middleware/SessionCookieTest.php | 2 +- tests/SlimTest.php | 42 +++---- 10 files changed, 227 insertions(+), 156 deletions(-) diff --git a/Slim/Environment.php b/Slim/Environment.php index 147d00f20..7078e935e 100644 --- a/Slim/Environment.php +++ b/Slim/Environment.php @@ -161,17 +161,22 @@ private function __construct($settings = null) //Number of server port that is running the script $env['SERVER_PORT'] = $_SERVER['SERVER_PORT']; - //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) { - $value = is_string($value) ? trim($value) : $value; - if (strpos($key, 'HTTP_') === 0) { - $env[substr($key, 5)] = $value; - } elseif (strpos($key, 'X_') === 0 || in_array($key, $specialHeaders)) { - $env[$key] = $value; - } + //HTTP request headers (retains HTTP_ prefix to match $_SERVER) + $headers = \Slim\Http\Headers::extract($_SERVER); + foreach ($headers as $key => $value) { + $env[$key] = $value; } + // $specialHeaders = array('CONTENT_TYPE', 'CONTENT_LENGTH', 'PHP_AUTH_USER', 'PHP_AUTH_PW', 'PHP_AUTH_DIGEST', 'AUTH_TYPE'); + // foreach ($_SERVER as $key => $value) { + // $value = is_string($value) ? trim($value) : $value; + // if (strpos($key, 'HTTP_') === 0) { + // $env[substr($key, 5)] = $value; + // } elseif (strpos($key, 'X_') === 0 || in_array($key, $specialHeaders)) { + // $env[$key] = $value; + // } + // } + //Is the application running under HTTPS or HTTP protocol? $env['slim.url_scheme'] = empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off' ? 'http' : 'https'; diff --git a/Slim/Http/Cookies.php b/Slim/Http/Cookies.php index 2b7b4f377..61e0f6d29 100644 --- a/Slim/Http/Cookies.php +++ b/Slim/Http/Cookies.php @@ -34,6 +34,10 @@ class Cookies extends \Slim\Helper\Set { + /** + * Default cookie settings + * @var array + */ protected $defaults = array( 'value' => '', 'domain' => null, @@ -43,6 +47,20 @@ class Cookies extends \Slim\Helper\Set 'httponly' => false ); + /** + * Set cookie + * + * The second argument may be a single scalar value, in which case + * it will be merged with the default settings and considered the `value` + * of the merged result. + * + * The second argument may also be an array containing any or all of + * the keys shown in the default settings above. This array will be + * merged with the defaults shown above. + * + * @param string $key Cookie name + * @param mixed $value Cookie settings + */ public function set($key, $value) { if (is_array($value)) { @@ -53,6 +71,17 @@ public function set($key, $value) parent::set($key, $cookieSettings); } + /** + * Remove cookie + * + * Unlike \Slim\Helper\Set, this will actually *set* a cookie with + * an expiration date in the past. This expiration date will force + * the client-side cache to remove its cookie with the given name + * and settings. + * + * @param string $key Cookie name + * @param array $settings Optional cookie settings + */ public function remove($key, $settings = array()) { $settings['value'] = ''; diff --git a/Slim/Http/Headers.php b/Slim/Http/Headers.php index 2691f4426..ce6c8f437 100644 --- a/Slim/Http/Headers.php +++ b/Slim/Http/Headers.php @@ -41,6 +41,51 @@ */ class Headers extends \Slim\Helper\Set { + /******************************************************************************** + * Static interface + *******************************************************************************/ + + /** + * Special-case HTTP headers that are otherwise unidentifiable as HTTP headers. + * Typically, HTTP headers in the $_SERVER array will be prefixed with + * `HTTP_` or `X_`. These are not so we list them here for later reference. + * + * @var array + */ + protected static $special = array( + 'CONTENT_TYPE', + 'CONTENT_LENGTH', + 'PHP_AUTH_USER', + 'PHP_AUTH_PW', + 'PHP_AUTH_DIGEST', + 'AUTH_TYPE' + ); + + /** + * Extract HTTP headers from an array of data (e.g. $_SERVER) + * @param array $data + * @return array + */ + public static function extract($data) + { + $results = array(); + foreach ($data as $key => $value) { + $key = strtoupper($key); + if (strpos($key, 'X_') === 0 || strpos($key, 'HTTP_') === 0 || in_array($key, static::$special)) { + if ($key === 'HTTP_CONTENT_TYPE' || $key === 'HTTP_CONTENT_LENGTH') { + continue; + } + $results[$key] = $value; + } + } + + return $results; + } + + /******************************************************************************** + * Instance interface + *******************************************************************************/ + /** * Transform header name into canonical form * @param string $name @@ -48,6 +93,11 @@ class Headers extends \Slim\Helper\Set */ protected function normalizeKey($key) { - return strtolower(trim($key)); + $key = trim($key); + $key = strtoupper($key); + $key = str_replace('-', '_', $key); + $key = preg_replace('#^HTTP_#', '', $key); + + return $key; } } diff --git a/Slim/Http/Request.php b/Slim/Http/Request.php index e508394ad..2c413f083 100644 --- a/Slim/Http/Request.php +++ b/Slim/Http/Request.php @@ -58,18 +58,34 @@ class Request protected static $formDataMediaTypes = array('application/x-www-form-urlencoded'); /** - * @var array + * Application Environment + * @var \Slim\Environment */ protected $env; + /** + * HTTP Headers + * @var \Slim\Http\Headers + */ + public $headers; + + /** + * HTTP Cookies + * @var \Slim\Helper\Set + */ + public $cookies; + /** * Constructor * @param array $env * @see \Slim\Environment */ - public function __construct($env) + + public function __construct(\Slim\Environment $env) { $this->env = $env; + $this->headers = new \Slim\Http\Headers(\Slim\Http\Headers::extract($env)); + $this->cookies = new \Slim\Helper\Set(\Slim\Http\Util::parseCookieHeader($env['HTTP_COOKIE'])); } /** @@ -143,7 +159,7 @@ public function isAjax() { if ($this->params('isajax')) { return true; - } elseif (isset($this->env['X_REQUESTED_WITH']) && $this->env['X_REQUESTED_WITH'] === 'XMLHttpRequest') { + } elseif (isset($this->headers['X_REQUESTED_WITH']) && $this->headers['X_REQUESTED_WITH'] === 'XMLHttpRequest') { return true; } else { return false; @@ -172,14 +188,10 @@ public function params($key = null) { $union = array_merge($this->get(), $this->post()); if ($key) { - if (isset($union[$key])) { - return $union[$key]; - } else { - return null; - } - } else { - return $union; + return isset($union[$key]) ? $union[$key] : null; } + + return $union; } /** @@ -284,19 +296,24 @@ public function delete($key = null) */ public function cookies($key = null) { - if (!isset($this->env['slim.request.cookie_hash'])) { - $cookieHeader = isset($this->env['COOKIE']) ? $this->env['COOKIE'] : ''; - $this->env['slim.request.cookie_hash'] = Util::parseCookieHeader($cookieHeader); - } if ($key) { - if (isset($this->env['slim.request.cookie_hash'][$key])) { - return $this->env['slim.request.cookie_hash'][$key]; - } else { - return null; - } - } else { - return $this->env['slim.request.cookie_hash']; + return $this->cookies->get($key); } + + return $this->cookies; + // if (!isset($this->env['slim.request.cookie_hash'])) { + // $cookieHeader = isset($this->env['COOKIE']) ? $this->env['COOKIE'] : ''; + // $this->env['slim.request.cookie_hash'] = Util::parseCookieHeader($cookieHeader); + // } + // if ($key) { + // if (isset($this->env['slim.request.cookie_hash'][$key])) { + // return $this->env['slim.request.cookie_hash'][$key]; + // } else { + // return null; + // } + // } else { + // return $this->env['slim.request.cookie_hash']; + // } } /** @@ -323,24 +340,29 @@ public function isFormData() public function headers($key = null, $default = null) { if ($key) { - $key = strtoupper($key); - $key = str_replace('-', '_', $key); - $key = preg_replace('@^HTTP_@', '', $key); - if (isset($this->env[$key])) { - return $this->env[$key]; - } else { - return $default; - } - } else { - $headers = array(); - foreach ($this->env as $key => $value) { - if (strpos($key, 'slim.') !== 0) { - $headers[$key] = $value; - } - } - - return $headers; + return $this->headers->get($key, $default); } + + return $this->headers; + // if ($key) { + // $key = strtoupper($key); + // $key = str_replace('-', '_', $key); + // $key = preg_replace('@^HTTP_@', '', $key); + // if (isset($this->env[$key])) { + // return $this->env[$key]; + // } else { + // return $default; + // } + // } else { + // $headers = array(); + // foreach ($this->env as $key => $value) { + // if (strpos($key, 'slim.') !== 0) { + // $headers[$key] = $value; + // } + // } + // + // return $headers; + // } } /** @@ -354,15 +376,11 @@ public function getBody() /** * Get Content Type - * @return string + * @return string|null */ public function getContentType() { - if (isset($this->env['CONTENT_TYPE'])) { - return $this->env['CONTENT_TYPE']; - } else { - return null; - } + return $this->headers->get('CONTENT_TYPE'); } /** @@ -376,9 +394,9 @@ public function getMediaType() $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType); return strtolower($contentTypeParts[0]); - } else { - return null; } + + return null; } /** @@ -410,9 +428,9 @@ public function getContentCharset() $mediaTypeParams = $this->getMediaTypeParams(); if (isset($mediaTypeParams['charset'])) { return $mediaTypeParams['charset']; - } else { - return null; } + + return null; } /** @@ -421,11 +439,7 @@ public function getContentCharset() */ public function getContentLength() { - if (isset($this->env['CONTENT_LENGTH'])) { - return (int) $this->env['CONTENT_LENGTH']; - } else { - return 0; - } + return $this->headers->get('CONTENT_LENGTH', 0); } /** @@ -434,17 +448,17 @@ public function getContentLength() */ public function getHost() { - if (isset($this->env['HOST'])) { - if (strpos($this->env['HOST'], ':') !== false) { - $hostParts = explode(':', $this->env['HOST']); + if (isset($this->env['HTTP_HOST'])) { + if (strpos($this->env['HTTP_HOST'], ':') !== false) { + $hostParts = explode(':', $this->env['HTTP_HOST']); return $hostParts[0]; } - return $this->env['HOST']; - } else { - return $this->env['SERVER_NAME']; + return $this->env['HTTP_HOST']; } + + return $this->env['SERVER_NAME']; } /** @@ -462,7 +476,7 @@ public function getHostWithPort() */ public function getPort() { - return (int) $this->env['SERVER_PORT']; + return (int)$this->env['SERVER_PORT']; } /** @@ -554,11 +568,7 @@ public function getIp() */ public function getReferrer() { - if (isset($this->env['REFERER'])) { - return $this->env['REFERER']; - } else { - return null; - } + return $this->headers->get('HTTP_REFERER'); } /** @@ -576,10 +586,6 @@ public function getReferer() */ public function getUserAgent() { - if (isset($this->env['USER_AGENT'])) { - return $this->env['USER_AGENT']; - } else { - return null; - } + return $this->headers->get('HTTP_USER_AGENT'); } } diff --git a/tests/EnvironmentTest.php b/tests/EnvironmentTest.php index 91505b8ce..d528ae8ac 100644 --- a/tests/EnvironmentTest.php +++ b/tests/EnvironmentTest.php @@ -292,7 +292,7 @@ public function testSetsSpecialHeaders() $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']); + $this->assertEquals('XmlHttpRequest', $env['HTTP_X_REQUESTED_WITH']); } /** diff --git a/tests/Http/CookiesTest.php b/tests/Http/CookiesTest.php index 4d014e0d8..da061a749 100644 --- a/tests/Http/CookiesTest.php +++ b/tests/Http/CookiesTest.php @@ -35,7 +35,6 @@ public function testSetWithStringValue() { $c = new \Slim\Http\Cookies(); $c->set('foo', 'bar'); - $this->assertAttributeEquals( array( 'foo' => array( @@ -64,7 +63,6 @@ public function testSetWithArrayValue() 'secure' => true, 'httponly' => true )); - $this->assertAttributeEquals( array( 'foo' => array( @@ -85,11 +83,9 @@ public function testRemove() { $c = new \Slim\Http\Cookies(); $c->remove('foo'); - $prop = new \ReflectionProperty($c, 'data'); $prop->setAccessible(true); $cValue = $prop->getValue($c); - $this->assertEquals('', $cValue['foo']['value']); $this->assertLessThan(time(), $cValue['foo']['expires']); } diff --git a/tests/Http/HeadersTest.php b/tests/Http/HeadersTest.php index ce29bb9d1..c7bd7fac5 100644 --- a/tests/Http/HeadersTest.php +++ b/tests/Http/HeadersTest.php @@ -35,9 +35,25 @@ class HeadersTest extends PHPUnit_Framework_TestCase public function testNormalizesKey() { $h = new \Slim\Http\Headers(); - $h->set('Content-Type', 'text/html'); + $h->set('Http-Content-Type', 'text/html'); $prop = new \ReflectionProperty($h, 'data'); $prop->setAccessible(true); - $this->assertArrayHasKey('content-type', $prop->getValue($h)); + $this->assertArrayHasKey('CONTENT_TYPE', $prop->getValue($h)); + } + + public function testExtractHeaders() + { + $test = array( + 'HTTP_HOST' => 'foo.com', + 'SERVER_NAME' => 'foo', + 'CONTENT_TYPE' => 'text/html', + 'X_FORWARDED_FOR' => '127.0.0.1' + ); + $results = \Slim\Http\Headers::extract($test); + $this->assertEquals(array( + 'HTTP_HOST' => 'foo.com', + 'CONTENT_TYPE' => 'text/html', + 'X_FORWARDED_FOR' => '127.0.0.1' + ), $results); } } diff --git a/tests/Http/RequestTest.php b/tests/Http/RequestTest.php index b2d3def74..ed2f1b973 100644 --- a/tests/Http/RequestTest.php +++ b/tests/Http/RequestTest.php @@ -54,11 +54,6 @@ public function testIsGet() )); $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()); } /** @@ -70,12 +65,7 @@ public function testIsPost() '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()); } /** @@ -87,12 +77,7 @@ public function testIsPut() '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()); } /** @@ -104,12 +89,7 @@ public function testIsDelete() '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()); } /** @@ -121,12 +101,7 @@ public function testIsOptions() '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()); } /** @@ -138,11 +113,6 @@ public function testIsHead() '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()); } @@ -165,7 +135,7 @@ public function testIsAjaxWithHeader() public function testIsAjaxWithQueryParameter() { $env = \Slim\Environment::mock(array( - 'QUERY_STRING' => 'one=1&two=2&three=3&isajax=1', + 'QUERY_STRING' => 'isajax=1', )); $req = new \Slim\Http\Request($env); $this->assertTrue($req->isAjax()); @@ -363,7 +333,7 @@ public function testDelete() public function testCookies() { $env = \Slim\Environment::mock(array( - 'COOKIE' => 'foo=bar; abc=123' + 'HTTP_COOKIE' => 'foo=bar; abc=123' )); $req = new \Slim\Http\Request($env); $this->assertEquals(2, count($req->cookies())); @@ -431,12 +401,11 @@ public function testIsNotFormData() public function testHeaders() { $env = \Slim\Environment::mock(array( - 'ACCEPT_ENCODING' => 'gzip' + 'HTTP_ACCEPT_ENCODING' => 'gzip' )); $req = new \Slim\Http\Request($env); $headers = $req->headers(); - $this->assertTrue(is_array($headers)); - $this->assertArrayHasKey('ACCEPT_ENCODING', $headers); + $this->assertInstanceOf('\Slim\Http\Headers', $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')); @@ -632,7 +601,7 @@ public function testGetHost() { $env = \Slim\Environment::mock(array( 'SERVER_NAME' => 'slim', - 'HOST' => 'slimframework.com' + 'HTTP_HOST' => 'slimframework.com' )); $req = new \Slim\Http\Request($env); $this->assertEquals('slimframework.com', $req->getHost()); //Uses HTTP_HOST if available @@ -645,7 +614,7 @@ public function testGetHostAndStripPort() { $env = \Slim\Environment::mock(array( 'SERVER_NAME' => 'slim', - 'HOST' => 'slimframework.com:80' + 'HTTP_HOST' => 'slimframework.com:80' )); $req = new \Slim\Http\Request($env); $this->assertEquals('slimframework.com', $req->getHost()); //Uses HTTP_HOST if available @@ -658,9 +627,9 @@ public function testGetHostWhenNotExists() { $env = \Slim\Environment::mock(array( 'SERVER_NAME' => 'slim', - 'HOST' => 'slimframework.com' + 'HTTP_HOST' => 'slimframework.com' )); - unset($env['HOST']); + unset($env['HTTP_HOST']); $req = new \Slim\Http\Request($env); $this->assertEquals('slim', $req->getHost()); //Uses SERVER_NAME as backup } @@ -671,7 +640,7 @@ public function testGetHostWhenNotExists() public function testGetHostWithPort() { $env = \Slim\Environment::mock(array( - 'HOST' => 'slimframework.com', + 'HTTP_HOST' => 'slimframework.com', 'SERVER_NAME' => 'slim', 'SERVER_PORT' => 80, 'slim.url_scheme' => 'http' @@ -686,7 +655,7 @@ public function testGetHostWithPort() public function testGetHostDoesntDulplicatePort() { $env = \Slim\Environment::mock(array( - 'HOST' => 'slimframework.com:80', + 'HTTP_HOST' => 'slimframework.com:80', 'SERVER_NAME' => 'slim', 'SERVER_PORT' => 80, 'slim.url_scheme' => 'http' @@ -806,7 +775,7 @@ public function testAppPathsInRootDirectoryWithHtaccess() public function testGetUrl() { $env = \Slim\Environment::mock(array( - 'HOST' => 'slimframework.com', + 'HTTP_HOST' => 'slimframework.com', 'SERVER_NAME' => 'slim', 'SERVER_PORT' => 80, 'slim.url_scheme' => 'http' @@ -821,7 +790,7 @@ public function testGetUrl() public function testGetUrlWithCustomPort() { $env = \Slim\Environment::mock(array( - 'HOST' => 'slimframework.com', + 'HTTP_HOST' => 'slimframework.com', 'SERVER_NAME' => 'slim', 'SERVER_PORT' => 8080, 'slim.url_scheme' => 'http' @@ -836,7 +805,7 @@ public function testGetUrlWithCustomPort() public function testGetUrlWithHttps() { $env = \Slim\Environment::mock(array( - 'HOST' => 'slimframework.com', + 'HTTP_HOST' => 'slimframework.com', 'SERVER_NAME' => 'slim', 'SERVER_PORT' => 443, 'slim.url_scheme' => 'https' @@ -890,7 +859,7 @@ public function testGetIpWithForwardedFor() public function testGetReferrer() { $env = \Slim\Environment::mock(array( - 'REFERER' => 'http://foo.com' + 'HTTP_REFERER' => 'http://foo.com' )); $req = new \Slim\Http\Request($env); $this->assertEquals('http://foo.com', $req->getReferrer()); @@ -914,7 +883,7 @@ public function testGetReferrerWhenNotExists() public function testGetUserAgent() { $env = \Slim\Environment::mock(array( - 'USER_AGENT' => 'user-agent-string' + 'HTTP_USER_AGENT' => 'user-agent-string' )); $req = new \Slim\Http\Request($env); $this->assertEquals('user-agent-string', $req->getUserAgent()); @@ -926,7 +895,7 @@ public function testGetUserAgent() public function testGetUserAgentWhenNotExists() { $env = \Slim\Environment::mock(); - unset($env['USER_AGENT']); + unset($env['HTTP_USER_AGENT']); $req = new \Slim\Http\Request($env); $this->assertNull($req->getUserAgent()); } diff --git a/tests/Middleware/SessionCookieTest.php b/tests/Middleware/SessionCookieTest.php index 8ef12ef0c..47b90d968 100644 --- a/tests/Middleware/SessionCookieTest.php +++ b/tests/Middleware/SessionCookieTest.php @@ -80,7 +80,7 @@ public function testSessionIsPopulatedFromCookie() \Slim\Environment::mock(array( 'SCRIPT_NAME' => '/index.php', 'PATH_INFO' => '/foo', - 'COOKIE' => 'slim_session=1644004961%7CLKkYPwqKIMvBK7MWl6D%2BxeuhLuMaW4quN%2F512ZAaVIY%3D%7Ce0f007fa852c7101e8224bb529e26be4d0dfbd63', + 'HTTP_COOKIE' => 'slim_session=1644004961%7CLKkYPwqKIMvBK7MWl6D%2BxeuhLuMaW4quN%2F512ZAaVIY%3D%7Ce0f007fa852c7101e8224bb529e26be4d0dfbd63', )); $app = new \Slim\Slim(); $app->get('/foo', function () { diff --git a/tests/SlimTest.php b/tests/SlimTest.php index a1aa3ad76..0c791524e 100644 --- a/tests/SlimTest.php +++ b/tests/SlimTest.php @@ -545,7 +545,7 @@ public function testLastModifiedMatch() 'REQUEST_METHOD' => 'GET', 'SCRIPT_NAME' => '/foo', //<-- Physical 'PATH_INFO' => '/bar', //<-- Virtual - 'IF_MODIFIED_SINCE' => 'Sun, 03 Oct 2010 17:00:52 -0400', + 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 03 Oct 2010 17:00:52 -0400', )); $s = new \Slim\Slim(); $s->get('/bar', function () use ($s) { @@ -595,7 +595,7 @@ public function testEtagMatches() \Slim\Environment::mock(array( 'SCRIPT_NAME' => '/foo', //<-- Physical 'PATH_INFO' => '/bar', //<-- Virtual - 'IF_NONE_MATCH' => '"abc123"', + 'HTTP_IF_NONE_MATCH' => '"abc123"', )); $s = new \Slim\Slim(); $s->get('/bar', function () use ($s) { @@ -730,7 +730,7 @@ public function testGetCookie() 'QUERY_STRING' => 'one=foo&two=bar', 'SERVER_NAME' => 'slimframework.com', 'SERVER_PORT' => 80, - 'COOKIE' => 'foo=bar; foo2=bar2', + 'HTTP_COOKIE' => 'foo=bar; foo2=bar2', 'slim.url_scheme' => 'http', 'slim.input' => '', 'slim.errors' => @fopen('php://stderr', 'w') @@ -785,13 +785,13 @@ public function testDeleteCookie() * This method ensures that the `Set-Cookie:` HTTP request * header is set. The implementation is tested in a separate file. */ - public function testSetEncryptedCookie() - { - $s = new \Slim\Slim(); - $s->setEncryptedCookie('foo', 'bar'); - $r = $s->response(); - $this->assertEquals(1, preg_match("@^foo=.+%7C.+%7C.+@", $r['Set-Cookie'])); //<-- %7C is a url-encoded pipe - } + // public function testSetEncryptedCookie() + // { + // $s = new \Slim\Slim(); + // $s->setEncryptedCookie('foo', 'bar'); + // $r = $s->response(); + // $this->assertEquals(1, preg_match("@^foo=.+%7C.+%7C.+@", $r['Set-Cookie'])); //<-- %7C is a url-encoded pipe + // } /** * Test get encrypted cookie @@ -799,17 +799,17 @@ public function testSetEncryptedCookie() * This only tests that this method runs without error. The implementation of * fetching the encrypted cookie is tested separately. */ - public function testGetEncryptedCookieAndDeletingIt() - { - \Slim\Environment::mock(array( - 'SCRIPT_NAME' => '/foo', //<-- Physical - 'PATH_INFO' => '/bar', //<-- Virtual - )); - $s = new \Slim\Slim(); - $r = $s->response(); - $this->assertFalse($s->getEncryptedCookie('foo')); - $this->assertEquals(1, preg_match("@foo=;.*@", $r['Set-Cookie'])); - } + // public function testGetEncryptedCookieAndDeletingIt() + // { + // \Slim\Environment::mock(array( + // 'SCRIPT_NAME' => '/foo', //<-- Physical + // 'PATH_INFO' => '/bar', //<-- Virtual + // )); + // $s = new \Slim\Slim(); + // $r = $s->response(); + // $this->assertFalse($s->getEncryptedCookie('foo')); + // $this->assertEquals(1, preg_match("@foo=;.*@", $r['Set-Cookie'])); + // } /** * Test get encrypted cookie WITHOUT deleting it From 698db8edf16f1a211449c5fc74d6a882ecd35f75 Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Sat, 23 Mar 2013 21:09:20 +0200 Subject: [PATCH 26/66] Fixed Tests for PHP 5.3 --- tests/Helper/SetTest.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/Helper/SetTest.php b/tests/Helper/SetTest.php index f454941a2..2392064a1 100644 --- a/tests/Helper/SetTest.php +++ b/tests/Helper/SetTest.php @@ -43,7 +43,8 @@ public function testSet() { $this->bag->set('foo', 'bar'); $this->assertArrayHasKey('foo', $this->property->getValue($this->bag)); - $this->assertEquals('bar', $this->property->getValue($this->bag)['foo']); + $bag = $this->property->getValue($this->bag); + $this->assertEquals('bar', $bag['foo']); } public function testGet() @@ -66,8 +67,9 @@ public function testAdd() )); $this->assertArrayHasKey('abc', $this->property->getValue($this->bag)); $this->assertArrayHasKey('foo', $this->property->getValue($this->bag)); - $this->assertEquals('123', $this->property->getValue($this->bag)['abc']); - $this->assertEquals('bar', $this->property->getValue($this->bag)['foo']); + $bag = $this->property->getValue($this->bag); + $this->assertEquals('123', $bag['abc']); + $this->assertEquals('bar', $bag['foo']); } public function testAll() @@ -119,7 +121,8 @@ public function testArrayAccessSet() ); $this->property->setValue($this->bag, $data); $this->bag['foo'] = 'changed'; - $this->assertEquals('changed', $this->property->getValue($this->bag)['foo']); + $bag = $this->property->getValue($this->bag); + $this->assertEquals('changed', $bag['foo']); } public function testArrayAccessExists() From f5e3c5f25f840a7eb5a9286b2de70a9f69e20449 Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Sat, 23 Mar 2013 21:12:31 +0200 Subject: [PATCH 27/66] Fixed Tests for PHP 5.3 --- tests/RouterTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 378289c7c..0c01dbb19 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -66,7 +66,8 @@ public function testAddNamedRoute() $property = new \ReflectionProperty($router, 'namedRoutes'); $property->setAccessible(true); - $this->assertSame($route, $property->getValue($router)['foo']); + $rV = $property->getValue($router); + $this->assertSame($route, $rV['foo']); } /** From bcaabdda83ab94a37f8d2c63a6b500123641c881 Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Sat, 23 Mar 2013 21:18:01 +0200 Subject: [PATCH 28/66] Fixed Tests for PHP 5.3 --- tests/RouterTest.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 0c01dbb19..c645d5f3f 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -211,9 +211,15 @@ public function testGetCurrentRouteIfNoMatchedRoutes() public function testGetMatchedRoutes() { $router = new \Slim\Router(); - $route1 = (new \Slim\Route('/foo', function () {}))->via('GET'); - $route2 = (new \Slim\Route('/foo', function () {}))->via('POST'); - $route3 = (new \Slim\Route('/bar', function () {}))->via('PUT'); + + $route1 = new \Slim\Route('/foo', function () {}); + $route1 = $route1->via('GET'); + + $route2 = new \Slim\Route('/foo', function () {}); + $route2 = $route2->via('POST'); + + $route3 = new \Slim\Route('/bar', function () {}); + $route3 = $route3->via('PUT'); $routes = new \ReflectionProperty($router, 'routes'); $routes->setAccessible(true); @@ -228,7 +234,8 @@ public function testGetMatchedRoutes() public function testUrlFor() { $router = new \Slim\Router(); - $route1 = (new \Slim\Route('/hello/:first/:last', function () {}))->via('GET')->name('hello'); + $route1 = new \Slim\Route('/hello/:first/:last', function () {}); + $route1 = $route1->via('GET')->name('hello'); $routes = new \ReflectionProperty($router, 'namedRoutes'); $routes->setAccessible(true); From 11e142270bd266996d1d1ecc43410ff71f472e8b Mon Sep 17 00:00:00 2001 From: = Date: Sat, 23 Mar 2013 21:44:03 -0400 Subject: [PATCH 29/66] Ensure header names are normalized to camelcase with dashes --- Slim/Http/Headers.php | 9 +++++---- tests/Http/HeadersTest.php | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Slim/Http/Headers.php b/Slim/Http/Headers.php index ce6c8f437..d48cdcc03 100644 --- a/Slim/Http/Headers.php +++ b/Slim/Http/Headers.php @@ -93,10 +93,11 @@ public static function extract($data) */ protected function normalizeKey($key) { - $key = trim($key); - $key = strtoupper($key); - $key = str_replace('-', '_', $key); - $key = preg_replace('#^HTTP_#', '', $key); + $key = strtolower($key); + $key = str_replace(array('-', '_'), ' ', $key); + $key = preg_replace('#^http #', '', $key); + $key = ucwords($key); + $key = str_replace(' ', '-', $key); return $key; } diff --git a/tests/Http/HeadersTest.php b/tests/Http/HeadersTest.php index c7bd7fac5..9221db981 100644 --- a/tests/Http/HeadersTest.php +++ b/tests/Http/HeadersTest.php @@ -35,10 +35,10 @@ class HeadersTest extends PHPUnit_Framework_TestCase public function testNormalizesKey() { $h = new \Slim\Http\Headers(); - $h->set('Http-Content-Type', 'text/html'); + $h->set('Http_Content_Type', 'text/html'); $prop = new \ReflectionProperty($h, 'data'); $prop->setAccessible(true); - $this->assertArrayHasKey('CONTENT_TYPE', $prop->getValue($h)); + $this->assertArrayHasKey('Content-Type', $prop->getValue($h)); } public function testExtractHeaders() From 2485b5ccef29ca206da833ef7f0028418061051e Mon Sep 17 00:00:00 2001 From: = Date: Sun, 24 Mar 2013 12:39:42 -0400 Subject: [PATCH 30/66] Refactor setCookie and getCookie methods --- Slim/Slim.php | 63 ++++++++++++++++++++++-------------------- tests/SlimTest.php | 68 ++++++---------------------------------------- 2 files changed, 42 insertions(+), 89 deletions(-) diff --git a/Slim/Slim.php b/Slim/Slim.php index 6ca9ef491..498ce38bf 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -255,6 +255,7 @@ public static function getDefaultSettings() 'templates.path' => './templates', 'view' => '\Slim\View', // Cookies + 'cookies.encrypt' => false, 'cookies.lifetime' => '20 minutes', 'cookies.path' => '/', 'cookies.domain' => null, @@ -764,7 +765,7 @@ public function expires($time) *******************************************************************************/ /** - * Set unencrypted HTTP cookie + * Set HTTP cookie to be sent with the HTTP response * * @param string $name The cookie name * @param string $value The cookie value @@ -790,7 +791,7 @@ public function setCookie($name, $value, $time = null, $path = null, $domain = n } /** - * Get value of unencrypted HTTP cookie + * Get value of HTTP cookie from the current HTTP request * * Return the value of a cookie from the current HTTP request, * or return NULL if cookie does not exist. Cookies created during @@ -799,12 +800,30 @@ public function setCookie($name, $value, $time = null, $path = null, $domain = n * @param string $name * @return string|null */ - public function getCookie($name) + public function getCookie($name, $deleteIfInvalid = true) { - return $this->request->cookies->get($name); + // Get cookie value + $value = $this->request->cookies->get($name); + + // Decode if encrypted + if ($this->config('cookies.encrypt')) { + $value = \Slim\Http\Util::decodeSecureCookie( + $value, + $this->config('cookies.secret_key'), + $this->config('cookies.cipher'), + $this->config('cookies.cipher_mode') + ); + if ($value === false && $deleteIfInvalid) { + $this->deleteCookie($name); + } + } + + return $value; } /** + * DEPRECATION WARNING! Use `setCookie` with the `cookies.encrypt` app setting set to `true`. + * * Set encrypted HTTP cookie * * @param string $name The cookie name @@ -818,23 +837,14 @@ public function getCookie($name) * HTTPS connection from the client * @param bool $httponly When TRUE the cookie will be made accessible only through the HTTP protocol */ - public function setEncryptedCookie($name, $value, $expires = null, $path = null, $domain = null, $secure = null, $httponly = null) + public function setEncryptedCookie($name, $value, $expires = null, $path = null, $domain = null, $secure = false, $httponly = false) { - $expires = is_null($expires) ? $this->config('cookies.lifetime') : $expires; - if (is_string($expires)) { - $expires = strtotime($expires); - } - $secureValue = \Slim\Http\Util::encodeSecureCookie( - $value, - $expires, - $this->config('cookies.secret_key'), - $this->config('cookies.cipher'), - $this->config('cookies.cipher_mode') - ); - $this->setCookie($name, $secureValue, $expires, $path, $domain, $secure, $httponly); + $this->setCookie($name, $value, $expires, $path, $domain, $secure, $httponly); } /** + * DEPRECATION WARNING! Use `getCookie` with the `cookies.encrypt` app setting set to `true`. + * * Get value of encrypted HTTP cookie * * Return the value of an encrypted cookie from the current HTTP request, @@ -846,17 +856,7 @@ public function setEncryptedCookie($name, $value, $expires = null, $path = null, */ public function getEncryptedCookie($name, $deleteIfInvalid = true) { - $value = \Slim\Http\Util::decodeSecureCookie( - $this->request->cookies->get($name), - $this->config('cookies.secret_key'), - $this->config('cookies.cipher'), - $this->config('cookies.cipher_mode') - ); - if ($value === false && $deleteIfInvalid) { - $this->deleteCookie($name); - } - - return $value; + return $this->getCookie($name, $deleteIfInvalid); } /** @@ -1174,7 +1174,10 @@ public function run() $this->middleware[0]->call(); //Fetch status, header, and body - list($status, $header, $body) = $this->response->finalize(); + list($status, $headers, $body) = $this->response->finalize(); + + // Serialize cookies (with optional encryption) + \Slim\Http\Util::serializeCookies($headers, $this->response->cookies, $this->settings); //Send headers if (headers_sent() === false) { @@ -1186,7 +1189,7 @@ public function run() } //Send headers - foreach ($header as $name => $value) { + foreach ($headers as $name => $value) { $hValues = explode("\n", $value); foreach ($hValues as $hVal) { header("$name: $hVal", false); diff --git a/tests/SlimTest.php b/tests/SlimTest.php index 0c791524e..83f77b7c8 100644 --- a/tests/SlimTest.php +++ b/tests/SlimTest.php @@ -706,11 +706,11 @@ public function testSetCookie() $s->setCookie('foo1', 'bar1', '2 days'); }); $s->call(); - list($status, $header, $body) = $s->response()->finalize(); - $cookies = explode("\n", $header['Set-Cookie']); - $this->assertEquals(2, count($cookies)); - $this->assertEquals(1, preg_match('@foo=bar@', $cookies[0])); - $this->assertEquals(1, preg_match('@foo1=bar1@', $cookies[1])); + $cookie1 = $s->response->cookies->get('foo'); + $cookie2 = $s->response->cookies->get('foo1'); + $this->assertEquals(2, count($s->response->cookies)); + $this->assertEquals('bar', $cookie1['value']); + $this->assertEquals('bar1', $cookie2['value']); } /** @@ -773,60 +773,10 @@ public function testDeleteCookie() $s->deleteCookie('foo'); }); $s->call(); - list($status, $header, $body) = $s->response()->finalize(); - $cookies = explode("\n", $header['Set-Cookie']); - $this->assertEquals(1, count($cookies)); - $this->assertEquals(1, preg_match('@^foo=;@', $cookies[0])); - } - - /** - * Test set encrypted cookie - * - * This method ensures that the `Set-Cookie:` HTTP request - * header is set. The implementation is tested in a separate file. - */ - // public function testSetEncryptedCookie() - // { - // $s = new \Slim\Slim(); - // $s->setEncryptedCookie('foo', 'bar'); - // $r = $s->response(); - // $this->assertEquals(1, preg_match("@^foo=.+%7C.+%7C.+@", $r['Set-Cookie'])); //<-- %7C is a url-encoded pipe - // } - - /** - * Test get encrypted cookie - * - * This only tests that this method runs without error. The implementation of - * fetching the encrypted cookie is tested separately. - */ - // public function testGetEncryptedCookieAndDeletingIt() - // { - // \Slim\Environment::mock(array( - // 'SCRIPT_NAME' => '/foo', //<-- Physical - // 'PATH_INFO' => '/bar', //<-- Virtual - // )); - // $s = new \Slim\Slim(); - // $r = $s->response(); - // $this->assertFalse($s->getEncryptedCookie('foo')); - // $this->assertEquals(1, preg_match("@foo=;.*@", $r['Set-Cookie'])); - // } - - /** - * Test get encrypted cookie WITHOUT deleting it - * - * This only tests that this method runs without error. The implementation of - * fetching the encrypted cookie is tested separately. - */ - public function testGetEncryptedCookieWithoutDeletingIt() - { - \Slim\Environment::mock(array( - 'SCRIPT_NAME' => '/foo', //<-- Physical - 'PATH_INFO' => '/bar', //<-- Virtual - )); - $s = new \Slim\Slim(); - $r = $s->response(); - $this->assertFalse($s->getEncryptedCookie('foo', false)); - $this->assertEquals(0, preg_match("@foo=;.*@", $r['Set-Cookie'])); + $cookie = $s->response->cookies->get('foo'); + $this->assertEquals(1, count($s->response->cookies)); + $this->assertEquals('', $cookie['value']); + $this->assertLessThan(time(), $cookie['expires']); } /************************************************ From 6336c0d575977c43c38d6f2317dfd803e656a764 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 24 Mar 2013 12:40:05 -0400 Subject: [PATCH 31/66] Do not serialize cookies in Response finalize method --- Slim/Http/Response.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Slim/Http/Response.php b/Slim/Http/Response.php index 823eaa575..254558348 100644 --- a/Slim/Http/Response.php +++ b/Slim/Http/Response.php @@ -269,11 +269,6 @@ public function length($length = null) */ public function finalize() { - // Serialize cookies into raw HTTP headers - foreach ($this->cookies as $name => $value) { - Util::setCookieHeader($this->headers, $name, $value); - } - // Prepare response if (in_array($this->status, array(204, 304))) { $this->headers->remove('Content-Type'); From 5b17a198d97b6bbc361e6c8305ce01bd2c7abef0 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 24 Mar 2013 12:40:23 -0400 Subject: [PATCH 32/66] Add method to Slim\Http\Util to serialize cookies into headers --- Slim/Http/Util.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Slim/Http/Util.php b/Slim/Http/Util.php index f7e99a962..f5c585622 100644 --- a/Slim/Http/Util.php +++ b/Slim/Http/Util.php @@ -173,6 +173,32 @@ public static function decrypt($data, $key, $iv, $settings = array()) return $res; } + /** + * Serialize Response cookies into raw HTTP header + * @param \Slim\Http\Headers $headers The Response headers + * @param \Slim\Http\Cookies $cookies The Response cookies + * @param array $config The Slim app settings + */ + public static function serializeCookies(\Slim\Http\Headers &$headers, \Slim\Http\Cookies $cookies, array $config) + { + if ($config['cookies.encrypt']) { + foreach ($cookies as $name => $settings) { + $settings['value'] = static::encodeSecureCookie( + $settings['value'], + $settings['expires'], + $config['cookies.secret_key'], + $config['cookies.cipher'], + $config['cookies.cipher_mode'] + ); + static::setCookieHeader($headers, $name, $settings); + } + } else { + foreach ($cookies as $name => $settings) { + static::setCookieHeader($headers, $name, $settings); + } + } + } + /** * Encode secure cookie value * From 6902ed3316064668d511b71f994e0b90e22712c4 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 24 Mar 2013 12:49:55 -0400 Subject: [PATCH 33/66] Fix Session Middleware tests --- tests/Middleware/SessionCookieTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/Middleware/SessionCookieTest.php b/tests/Middleware/SessionCookieTest.php index 47b90d968..2364cad63 100644 --- a/tests/Middleware/SessionCookieTest.php +++ b/tests/Middleware/SessionCookieTest.php @@ -48,7 +48,7 @@ public function setUp() * 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() + public function testSessionCookieIsCreated() { \Slim\Environment::mock(array( 'SCRIPT_NAME' => '/index.php', @@ -63,9 +63,7 @@ public function testSessionCookieIsCreatedAndEncrypted() $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)); + $this->assertTrue($app->response->cookies->has('slim_session')); } /** From 73bfdc1819dd921465c913c0e5a0246ecc55fde3 Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Tue, 2 Apr 2013 11:24:14 +0300 Subject: [PATCH 34/66] New Feature: Route Groups --- Slim/Router.php | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ Slim/Slim.php | 23 ++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/Slim/Router.php b/Slim/Router.php index aa0f48e76..0432c6020 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -63,12 +63,18 @@ class Router */ protected $matchedRoutes; + /** + * @var array Array containing all route groups + */ + protected $routeGroups; + /** * Constructor */ public function __construct() { $this->routes = array(); + $this->routeGroups = array(); } /** @@ -121,12 +127,63 @@ public function getMatchedRoutes($httpMethod, $resourceUri, $reload = false) */ public function map($pattern, $callable) { + list($groupPattern, $groupMiddleware) = $this->processGroups(); + if (count($this->routeGroups) > 0) { + $pattern = $groupPattern . ltrim($pattern, '/'); + } $route = new \Slim\Route($pattern, $callable); $this->routes[] = $route; + if (count($this->routeGroups) > 0) { + foreach ($groupMiddleware as $middlewareArr) { + if (is_array($middlewareArr)) { + foreach ($middlewareArr as $middleware) { + $route->setMiddleware($middleware); + } + } + } + } + return $route; } + /** + * A helper function for proccesing the group's pattern and middleware + * @return array Returns an array with the elements: pattern, middlewareArr + */ + protected function processGroups() + { + $pattern = "/"; + $middleware = array(); + foreach ($this->routeGroups as $group) { + $k = key($group); + $pattern .= $k . "/"; + array_push($middleware, $group[$k]); + } + return array($pattern, $middleware); + } + + /** + * Add a route group to the array + * @param string $group The group pattern (ie. "/books/:id") + * @param array|null $middleware Optional parameter array of middleware + * @return int The index of the new group + */ + public function pushGroup($group, $middleware = null) + { + $group = trim($group, '/'); + return array_push($this->routeGroups, array($group => $middleware)); + } + + /** + * Removes the last route group from the array + * @return bool True if successful, else False + */ + public function popGroup() + { + return (array_pop($this->routeGroups) !== null); + } + /** * Get URL for named route * @param string $name The name of the route diff --git a/Slim/Slim.php b/Slim/Slim.php index 6ca9ef491..aad63eafc 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -482,6 +482,29 @@ public function options() return $this->mapRoute($args)->via(\Slim\Http\Request::METHOD_OPTIONS); } + /** + * Route Groups + * + * This method accepts a route pattern and a callback all Route + * declarations in the callback will be prepended by the group(s) + * that it is in + * + * Accepts the same paramters as a standard route so: + * (pattern, middleware1, middleware2, ..., $callback) + */ + + public function group() + { + $args = func_get_args(); + $pattern = array_shift($args); + $callable = array_pop($args); + $this->router->pushGroup($pattern, $args); + if (is_callable($callable)) { + call_user_func($callable); + } + $this->router->popGroup(); + } + /** * Not Found Handler * From 80c32ef50d49dc5e40fee969c9e9a5e462496643 Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Tue, 2 Apr 2013 11:37:38 +0300 Subject: [PATCH 35/66] Added Test for group Routes --- tests/SlimTest.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/SlimTest.php b/tests/SlimTest.php index 0c791524e..9084903ec 100644 --- a/tests/SlimTest.php +++ b/tests/SlimTest.php @@ -413,6 +413,27 @@ public function testOptionsRoute() $this->assertSame($callable, $route->getCallable()); } + /** + * Test route groups + */ + public function testRouteGroups() + { + \Slim\Environment::mock(array( + 'REQUEST_METHOD' => 'GET', + 'SCRIPT_NAME' => '/foo', //<-- Physical + 'PATH_INFO' => '/bar/baz', //<-- Virtual' + )); + $s = new \Slim\Slim(); + $mw1 = function () { echo "foo"; }; + $mw2 = function () { echo "bar"; }; + $callable = function () { echo "xyz"; }; + $s->group('/bar', $mw1, function () use ($s, $mw2, $callable) { + $s->get('/baz', $mw2, $callable); + }); + $s->call(); + $this->assertEquals('foobarxyz', $s->response()->body()); + } + /** * Test if route does NOT expect trailing slash and URL has one */ From 4d85e993e8cdeac6f50c11d60d81dfb1aa8ad44a Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Tue, 2 Apr 2013 12:37:51 +0300 Subject: [PATCH 36/66] Made PSR2 Compliant --- Slim/Environment.php | 7 +++-- Slim/Exception/Pass.php | 1 - Slim/Exception/Stop.php | 1 - Slim/Http/Response.php | 4 +-- Slim/Http/Util.php | 50 ++++++++++++++++++++----------- Slim/Middleware/ContentTypes.php | 5 ++-- Slim/Middleware/SessionCookie.php | 24 ++++++++------- Slim/Route.php | 9 ++++-- Slim/Slim.php | 25 +++++++++------- index.php | 38 +++++++++++++++-------- 10 files changed, 101 insertions(+), 63 deletions(-) diff --git a/Slim/Environment.php b/Slim/Environment.php index 7078e935e..d11f08118 100644 --- a/Slim/Environment.php +++ b/Slim/Environment.php @@ -87,7 +87,7 @@ public static function getInstance($refresh = false) */ public static function mock($userSettings = array()) { - self::$environment = new self(array_merge(array( + $defaults = array( 'REQUEST_METHOD' => 'GET', 'SCRIPT_NAME' => '', 'PATH_INFO' => '', @@ -102,7 +102,8 @@ public static function mock($userSettings = array()) 'slim.url_scheme' => 'http', 'slim.input' => '', 'slim.errors' => @fopen('php://stderr', 'w') - ), $userSettings)); + ); + self::$environment = new self(array_merge($defaults, $userSettings)); return self::$environment; } @@ -143,7 +144,7 @@ private function __construct($settings = null) if (strpos($_SERVER['REQUEST_URI'], $_SERVER['SCRIPT_NAME']) === 0) { $env['SCRIPT_NAME'] = $_SERVER['SCRIPT_NAME']; //Without URL rewrite } else { - $env['SCRIPT_NAME'] = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']) ); //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) { diff --git a/Slim/Exception/Pass.php b/Slim/Exception/Pass.php index f4f25fac5..67daa8b42 100644 --- a/Slim/Exception/Pass.php +++ b/Slim/Exception/Pass.php @@ -46,5 +46,4 @@ */ class Pass extends \Exception { - } diff --git a/Slim/Exception/Stop.php b/Slim/Exception/Stop.php index f008959a4..38652edf5 100644 --- a/Slim/Exception/Stop.php +++ b/Slim/Exception/Stop.php @@ -44,5 +44,4 @@ */ class Stop extends \Exception { - } diff --git a/Slim/Http/Response.php b/Slim/Http/Response.php index 823eaa575..5736b01f4 100644 --- a/Slim/Http/Response.php +++ b/Slim/Http/Response.php @@ -444,7 +444,7 @@ public function isServerError() /** * Array Access: Offset Exists */ - public function offsetExists( $offset ) + public function offsetExists($offset) { return isset($this->headers[$offset]); } @@ -452,7 +452,7 @@ public function offsetExists( $offset ) /** * Array Access: Offset Get */ - public function offsetGet( $offset ) + public function offsetGet($offset) { return $this->headers[$offset]; } diff --git a/Slim/Http/Util.php b/Slim/Http/Util.php index f7e99a962..ca003cec5 100644 --- a/Slim/Http/Util.php +++ b/Slim/Http/Util.php @@ -58,7 +58,7 @@ public static function stripSlashesIfMagicQuotes($rawData, $overrideStripSlashes { $strip = is_null($overrideStripSlashes) ? get_magic_quotes_gpc() : $overrideStripSlashes; if ($strip) { - return self::_stripSlashes($rawData); + return self::stripSlashes($rawData); } else { return $rawData; } @@ -69,9 +69,9 @@ public static function stripSlashesIfMagicQuotes($rawData, $overrideStripSlashes * @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); + return is_array($rawData) ? array_map(array('self', 'stripSlashes'), $rawData) : stripslashes($rawData); } /** @@ -95,10 +95,11 @@ public static function encrypt($data, $key, $iv, $settings = array()) } //Merge settings with defaults - $settings = array_merge(array( + $defaults = array( 'algorithm' => MCRYPT_RIJNDAEL_256, 'mode' => MCRYPT_MODE_CBC - ), $settings); + ); + $settings = array_merge($defaults, $settings); //Get module $module = mcrypt_module_open($settings['algorithm'], '', $settings['mode'], ''); @@ -144,10 +145,11 @@ public static function decrypt($data, $key, $iv, $settings = array()) } //Merge settings with defaults - $settings = array_merge(array( + $defaults = array( 'algorithm' => MCRYPT_RIJNDAEL_256, 'mode' => MCRYPT_MODE_CBC - ), $settings); + ); + $settings = array_merge($defaults, $settings); //Get module $module = mcrypt_module_open($settings['algorithm'], '', $settings['mode'], ''); @@ -190,11 +192,18 @@ public static function decrypt($data, $key, $iv, $settings = array()) public static function encodeSecureCookie($value, $expires, $secret, $algorithm, $mode) { $key = hash_hmac('sha1', $expires, $secret); - $iv = self::get_iv($expires, $secret); - $secureString = base64_encode(self::encrypt($value, $key, $iv, array( - 'algorithm' => $algorithm, - 'mode' => $mode - ))); + $iv = self::getIv($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)); @@ -220,11 +229,16 @@ public static function decodeSecureCookie($value, $secret, $algorithm, $mode) $value = explode('|', $value); if (count($value) === 3 && ((int) $value[0] === 0 || (int) $value[0] > time())) { $key = hash_hmac('sha1', $value[0], $secret); - $iv = self::get_iv($value[0], $secret); - $data = self::decrypt(base64_decode($value[1]), $key, $iv, array( - 'algorithm' => $algorithm, - 'mode' => $mode - )); + $iv = self::getIv($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]) { return $data; @@ -378,7 +392,7 @@ public static function parseCookieHeader($header) * @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) + private static function getIv($expires, $secret) { $data1 = hash_hmac('sha1', 'a'.$expires.'b', $secret); $data2 = hash_hmac('sha1', 'z'.$expires.'y', $secret); diff --git a/Slim/Middleware/ContentTypes.php b/Slim/Middleware/ContentTypes.php index b11e46052..b6e13e348 100644 --- a/Slim/Middleware/ContentTypes.php +++ b/Slim/Middleware/ContentTypes.php @@ -58,12 +58,13 @@ class ContentTypes extends \Slim\Middleware */ public function __construct($settings = array()) { - $this->contentTypes = array_merge(array( + $defaults = array( 'application/json' => array($this, 'parseJson'), 'application/xml' => array($this, 'parseXml'), 'text/xml' => array($this, 'parseXml'), 'text/csv' => array($this, 'parseCsv') - ), $settings); + ); + $this->contentTypes = array_merge($defaults, $settings); } /** diff --git a/Slim/Middleware/SessionCookie.php b/Slim/Middleware/SessionCookie.php index 1747a8677..04e909420 100644 --- a/Slim/Middleware/SessionCookie.php +++ b/Slim/Middleware/SessionCookie.php @@ -72,7 +72,7 @@ class SessionCookie extends \Slim\Middleware */ public function __construct($settings = array()) { - $this->settings = array_merge(array( + $defaults = array( 'expires' => '20 minutes', 'path' => '/', 'domain' => null, @@ -82,7 +82,8 @@ public function __construct($settings = array()) 'secret' => 'CHANGE_ME', 'cipher' => MCRYPT_RIJNDAEL_256, 'cipher_mode' => MCRYPT_MODE_CBC - ), $settings); + ); + $this->settings = array_merge($defaults, $settings); if (is_string($this->settings['expires'])) { $this->settings['expires'] = strtotime($this->settings['expires']); } @@ -155,14 +156,17 @@ protected function saveSession() 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, - 'domain' => $this->settings['domain'], - 'path' => $this->settings['path'], - 'expires' => $this->settings['expires'], - 'secure' => $this->settings['secure'], - 'httponly' => $this->settings['httponly'] - )); + $this->app->response()->setCookie( + $this->settings['name'], + array( + 'value' => $value, + 'domain' => $this->settings['domain'], + 'path' => $this->settings['path'], + 'expires' => $this->settings['expires'], + 'secure' => $this->settings['secure'], + 'httponly' => $this->settings['httponly'] + ) + ); } session_destroy(); } diff --git a/Slim/Route.php b/Slim/Route.php index 7f527a156..919e836ba 100644 --- a/Slim/Route.php +++ b/Slim/Route.php @@ -320,7 +320,7 @@ public function setMiddleware($middleware) if (is_callable($middleware)) { $this->middleware[] = $middleware; } elseif (is_array($middleware)) { - foreach($middleware as $callable) { + foreach ($middleware as $callable) { if (!is_callable($callable)) { throw new \InvalidArgumentException('All Route middleware must be callable'); } @@ -347,8 +347,11 @@ public function setMiddleware($middleware) 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)); + $patternAsRegex = preg_replace_callback( + '#:([\w]+)\+?#', + array($this, 'matchesCallback'), + str_replace(')', ')?', (string) $this->pattern) + ); if (substr($this->pattern, -1) === '/') { $patternAsRegex .= '?'; } diff --git a/Slim/Slim.php b/Slim/Slim.php index 6ca9ef491..fd1a3b432 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -503,12 +503,13 @@ public function options() * * @param mixed $callable Anything that returns true for is_callable() */ - public function notFound( $callable = null ) { - if ( is_callable($callable) ) { + public function notFound ($callable = null) + { + if (is_callable($callable)) { $this->notFound = $callable; } else { ob_start(); - if ( is_callable($this->notFound) ) { + if (is_callable($this->notFound)) { call_user_func($this->notFound); } else { call_user_func(array($this, 'defaultNotFound')); @@ -566,7 +567,7 @@ public function error($argument = null) protected function callErrorHandler($argument = null) { ob_start(); - if ( is_callable($this->error) ) { + if (is_callable($this->error)) { call_user_func_array($this->error, array($argument)); } else { call_user_func_array(array($this, 'defaultError'), array($argument)); @@ -726,7 +727,9 @@ public function etag($value, $type = 'strong') //Set etag value $value = '"' . $value . '"'; - if ($type === 'weak') $value = 'W/'.$value; + if ($type === 'weak') { + $value = 'W/'.$value; + } $this->response['ETag'] = $value; //Check conditional GET @@ -779,14 +782,15 @@ public function expires($time) */ public function setCookie($name, $value, $time = null, $path = null, $domain = null, $secure = null, $httponly = null) { - $this->response->cookies->set($name, array( + $settings = array( 'value' => $value, 'expires' => is_null($time) ? $this->config('cookies.lifetime') : $time, 'path' => is_null($path) ? $this->config('cookies.path') : $path, 'domain' => is_null($domain) ? $this->config('cookies.domain') : $domain, 'secure' => is_null($secure) ? $this->config('cookies.secure') : $secure, 'httponly' => is_null($httponly) ? $this->config('cookies.httponly') : $httponly - )); + ); + $this->response->cookies->set($name, $settings); } /** @@ -877,12 +881,13 @@ public function getEncryptedCookie($name, $deleteIfInvalid = true) */ public function deleteCookie($name, $path = null, $domain = null, $secure = null, $httponly = null) { - $this->response->cookies->remove($name, array( + $settings = array( 'domain' => is_null($domain) ? $this->config('cookies.domain') : $domain, 'path' => is_null($path) ? $this->config('cookies.path') : $path, 'secure' => is_null($secure) ? $this->config('cookies.secure') : $secure, 'httponly' => is_null($httponly) ? $this->config('cookies.httponly') : $httponly - )); + ); + $this->response->cookies->remove($name, $settings); } /******************************************************************************** @@ -1229,7 +1234,7 @@ public function call() } } if (!$dispatched) { - $this->notFound(); + $this->notFound(); } $this->applyHook('slim.after.router'); $this->stop(); diff --git a/index.php b/index.php index c3e67454c..a802b2b69 100644 --- a/index.php +++ b/index.php @@ -31,8 +31,10 @@ */ // GET route -$app->get('/', function () { - $template = <<get( + '/', + function () { + $template = << @@ -125,23 +127,33 @@ EOT; - echo $template; -}); + echo $template; + } +); // POST route -$app->post('/post', function () { - echo 'This is a POST route'; -}); +$app->post( + '/post', + function () { + echo 'This is a POST route'; + } +); // PUT route -$app->put('/put', function () { - echo 'This is a PUT route'; -}); +$app->put( + '/put', + function () { + echo 'This is a PUT route'; + } +); // DELETE route -$app->delete('/delete', function () { - echo 'This is a DELETE route'; -}); +$app->delete( + '/delete', + function () { + echo 'This is a DELETE route'; + } +); /** * Step 4: Run the Slim application From 877cac9ec54f190284074020612c679776d9cdca Mon Sep 17 00:00:00 2001 From: mnlg Date: Thu, 18 Oct 2012 20:54:22 +0200 Subject: [PATCH 37/66] move dispatch method to \Slim\Route --- Slim/Route.php | 16 +++++++++++----- Slim/Router.php | 12 ------------ Slim/Slim.php | 2 +- tests/RouteTest.php | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/Slim/Route.php b/Slim/Route.php index 7f527a156..13259a7d2 100644 --- a/Slim/Route.php +++ b/Slim/Route.php @@ -415,15 +415,21 @@ public function conditions(array $conditions) } /** - * Dispatch - * @return mixed The return value of the route callable, or FALSE on error + * 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. + * + * @return bool */ public function dispatch() { - foreach ($this->middleware as $routeMiddleware) { - call_user_func_array($routeMiddleware, array($this)); + foreach ($this->getMiddleware() as $mw) { + call_user_func_array($mw, array($this)); } - return call_user_func_array($this->callable, array_values($this->params)); + $return = call_user_func_array($this->getCallable(), array_values($this->getParams())); + return ($return === false)? false : true; } } diff --git a/Slim/Router.php b/Slim/Router.php index aa0f48e76..2e0a4e5c9 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -149,18 +149,6 @@ public function urlFor($name, $params = array()) return preg_replace('#\(/?:.+\)|\(|\)#', '', $pattern); } - /** - * Dispatch route - * @param \Slim\Route $route The route to dispatch - * @return bool True if route dispatched successfully, else false - */ - public function dispatch(\Slim\Route $route) - { - $this->currentRoute = $route; - - return ($route->dispatch() !== false); - } - /** * Add named route * @param string $name The route name diff --git a/Slim/Slim.php b/Slim/Slim.php index 498ce38bf..6861907eb 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -1222,7 +1222,7 @@ public function call() foreach ($matchedRoutes as $route) { try { $this->applyHook('slim.before.dispatch'); - $dispatched = $this->router->dispatch($route); + $dispatched = $route->dispatch(); $this->applyHook('slim.after.dispatch'); if ($dispatched) { break; diff --git a/tests/RouteTest.php b/tests/RouteTest.php index 26ff2facf..3f7fb8dbe 100644 --- a/tests/RouteTest.php +++ b/tests/RouteTest.php @@ -473,8 +473,46 @@ public function testSupportsHttpMethod() $this->assertFalse($route->supportsHttpMethod('PUT')); } + /** + * Test dispatch with params + */ public function testDispatch() { + $this->expectOutputString('Hello josh'); + $route = new \Slim\Route('/hello/:name', function ($name) { echo "Hello $name"; }); + $route->matches('/hello/josh'); //<-- Extracts params from resource URI + $route->dispatch(); + } + /** + * Test dispatch with middleware + */ + public function testDispatchWithMiddlware() + { + $this->expectOutputString('First! Second! Hello josh'); + $route = new \Slim\Route('/hello/:name', function ($name) { echo "Hello $name"; }); + $route->setMiddleware(function () { + echo "First! "; + }); + $route->setMiddleware(function () { + echo "Second! "; + }); + $route->matches('/hello/josh'); //<-- Extracts params from resource URI + $route->dispatch(); + } + + /** + * Test middleware with arguments + */ + public function testRouteMiddlwareArguments() + { + $this->expectOutputString('foobar'); + $route = new \Slim\Route('/foo', function () { echo "bar"; }); + $route->setName('foo'); + $route->setMiddleware(function ($route) { + echo $route->getName(); + }); + $route->matches('/foo'); //<-- Extracts params from resource URI + $route->dispatch(); } } From f71dbdd4d0c1408a2805bb0e6aebbc001db41db1 Mon Sep 17 00:00:00 2001 From: mnlg Date: Wed, 3 Apr 2013 20:35:11 +0200 Subject: [PATCH 38/66] remove Router dispatch test --- tests/RouterTest.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tests/RouterTest.php b/tests/RouterTest.php index c645d5f3f..a8969ba28 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -144,18 +144,6 @@ public function testHasNamedRoute() $this->assertFalse($router->hasNamedRoute('bar')); } - /** - * Router should set current route and dispatch returns non-false value - */ - public function testDispatch() - { - $router = new \Slim\Router(); - $route = new \Slim\Route('/foo', function () {}); - - $this->assertTrue($router->dispatch($route) !== false); - $this->assertAttributeSame($route, 'currentRoute', $router); - } - /** * Router should return current route if set during iteration */ From 2639450b960f21d1960e76d392a08b43c669299c Mon Sep 17 00:00:00 2001 From: mnlg Date: Wed, 3 Apr 2013 20:42:07 +0200 Subject: [PATCH 39/66] get the middleware from the class property instead of using the getMiddleware method --- Slim/Route.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Slim/Route.php b/Slim/Route.php index 13259a7d2..032080049 100644 --- a/Slim/Route.php +++ b/Slim/Route.php @@ -425,7 +425,7 @@ public function conditions(array $conditions) */ public function dispatch() { - foreach ($this->getMiddleware() as $mw) { + foreach ($this->middleware as $mw) { call_user_func_array($mw, array($this)); } From 45a247f10f23f9440fa4b0ed2cf96bab79e8a419 Mon Sep 17 00:00:00 2001 From: mnlg Date: Fri, 19 Oct 2012 11:00:16 +0200 Subject: [PATCH 40/66] create Routes in Slim instead of Router --- Slim/Router.php | 11 +++-------- Slim/Slim.php | 3 ++- tests/RouterTest.php | 4 ++-- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Slim/Router.php b/Slim/Router.php index aa0f48e76..1dd59c265 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -114,17 +114,12 @@ public function getMatchedRoutes($httpMethod, $resourceUri, $reload = false) } /** - * 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 + * Add a route object to the router + * @param \Slim\Route $route The Slim Route */ - public function map($pattern, $callable) + public function map(\Slim\Route $route) { - $route = new \Slim\Route($pattern, $callable); $this->routes[] = $route; - - return $route; } /** diff --git a/Slim/Slim.php b/Slim/Slim.php index 498ce38bf..e63edc159 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -403,7 +403,8 @@ protected function mapRoute($args) { $pattern = array_shift($args); $callable = array_pop($args); - $route = $this->router->map($pattern, $callable); + $route = new \Slim\Route($pattern, $callable); + $this->router->map($route); if (count($args) > 0) { $route->setMiddleware($args); } diff --git a/tests/RouterTest.php b/tests/RouterTest.php index c645d5f3f..a51aa6d6f 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -48,9 +48,9 @@ public function testConstruct() public function testMap() { $router = new \Slim\Router(); - $route = $router->map('/foo', function() {}); + $route = new \Slim\Route('/foo', function() {}); + $router->map($route); - $this->assertInstanceOf('\Slim\Route', $route); $this->assertAttributeContains($route, 'routes', $router); } From 8d08c4824997e1fceb1080e46e6515c35af0f1f8 Mon Sep 17 00:00:00 2001 From: "Herman J. Radtke III" Date: Sat, 6 Apr 2013 14:50:14 -0700 Subject: [PATCH 41/66] Added query params support to nginx The documentation for the nginx configuration did not send query parameters in the URL to the calling code. This means something like /test?hello=world would fail to set the $_GET superglobal with the 'hello' key. --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index dae9ba108..a772db216 100644 --- a/README.markdown +++ b/README.markdown @@ -66,7 +66,7 @@ should contain this code: 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; + try_files $uri $uri/ /index.php?$args; This assumes that Slim's `index.php` is in the root folder of your project (www root). From 41f1c0b039124f451ef47c1846773a0f271c3022 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 6 Apr 2013 18:51:57 -0400 Subject: [PATCH 42/66] Tweak Set interface --- Slim/Helper/Set.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Slim/Helper/Set.php b/Slim/Helper/Set.php index ddddb0cfb..a0957f1dd 100644 --- a/Slim/Helper/Set.php +++ b/Slim/Helper/Set.php @@ -93,7 +93,7 @@ public function get($key, $default = null) * Add data to set * @param array $items Key-value array of data to append to this set */ - public function add($items) + public function replace($items) { foreach ($items as $key => $value) { $this->set($key, $value); // Ensure keys are normalized @@ -137,6 +137,15 @@ public function remove($key) unset($this->data[$this->normalizeKey($key)]); } + /** + * Clear all values + */ + public function clear() + { + unset($this->data); + $this->data = array(); + } + /** * Array Access */ From 5ec3f4b169876c2a6807f8a88172e463cf56a35d Mon Sep 17 00:00:00 2001 From: = Date: Sat, 6 Apr 2013 19:34:40 -0400 Subject: [PATCH 43/66] Update view interface and associated unit tests --- Slim/Helper/Set.php | 2 +- Slim/View.php | 200 +++++++++++++++++++++++++++----------------- tests/ViewTest.php | 86 +++++++++---------- 3 files changed, 160 insertions(+), 128 deletions(-) diff --git a/Slim/Helper/Set.php b/Slim/Helper/Set.php index a0957f1dd..8f79faf28 100644 --- a/Slim/Helper/Set.php +++ b/Slim/Helper/Set.php @@ -46,7 +46,7 @@ class Set implements \ArrayAccess, \Countable, \IteratorAggregate */ public function __construct($items = array()) { - $this->add($items); + $this->replace($items); } /** diff --git a/Slim/View.php b/Slim/View.php index 0ce956899..0a84f60e6 100644 --- a/Slim/View.php +++ b/Slim/View.php @@ -50,132 +50,174 @@ class View { /** - * @var string Absolute or relative filesystem path to a specific template - * - * DEPRECATION WARNING! - * This variable will be removed in the near future - */ - protected $templatePath = ''; - - /** - * @var array Associative array of template variables + * Data available to the view templates + * @var \Slim\Helper\Set */ - protected $data = array(); + protected $data; /** - * @var string Absolute or relative path to the application's templates directory + * Path to templates base directory (without trailing slash) + * @var string */ protected $templatesDirectory; /** * Constructor - * - * This is empty but may be implemented in a subclass */ public function __construct() { + $this->data = new \Slim\Helper\Set(); + } + + /******************************************************************************** + * Data methods + *******************************************************************************/ + + /** + * Does view data have value with key? + * @param string $key + * @return boolean + */ + public function has($key) + { + return $this->data->has($key); + } + + /** + * Return view data value with key + * @param string $key + * @return mixed + */ + public function get($key) + { + return $this->data->get($key); + } + + /** + * Set view data value with key + * @param string $key + * @param mixed $value + */ + public function set($key, $value) + { + $this->data->set($key, $value); + } + /** + * Return view data + * @return array + */ + public function all() + { + return $this->data->all(); + } + + /** + * Replace view data + * @param array $data + */ + public function replace(array $data) + { + $this->data->replace($data); } /** - * Get data - * @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; + * Clear view data + */ + public function clear() + { + $this->data->clear(); + } + + /******************************************************************************** + * Legacy data methods + *******************************************************************************/ + + /** + * DEPRECATION WARNING! This method will be removed in the next major point release + * + * Get data from view */ public function getData($key = null) { if (!is_null($key)) { return isset($this->data[$key]) ? $this->data[$key] : null; } else { - return $this->data; + return $this->data->all(); } } /** - * Set data - * - * If two arguments: - * A single datum with key is assigned value; + * DEPRECATION WARNING! This method will be removed in the next major point release * - * $view->setData('color', 'red'); - * - * If one argument: - * Replace all data with provided array keys and values; - * - * $view->setData(array('color' => 'red', 'number' => 1)); - * - * @param mixed - * @param mixed - * @throws InvalidArgumentException If incorrect method signature + * Set data for view */ public function setData() { $args = func_get_args(); if (count($args) === 1 && is_array($args[0])) { - $this->data = $args[0]; + $this->data->replace($args[0]); } elseif (count($args) === 2) { - $this->data[(string) $args[0]] = $args[1]; + $this->data->set($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, ... ]);`'); } } /** - * Append new data to existing template data - * @param array - * @throws InvalidArgumentException If not given an array argument + * DEPRECATION WARNING! This method will be removed in the next major point release + * + * Append data to view + * @param 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); + $this->data->replace($data); } + /******************************************************************************** + * Resolve template paths + *******************************************************************************/ + /** - * Get templates directory - * @return string|null Path to templates directory without trailing slash; - * Returns null if templates directory not set; + * Set the base directory that contains view templates + * @param string $directory + * @throws \InvalidArgumentException If directory is not a directory */ - public function getTemplatesDirectory() + public function setTemplatesDirectory($directory) { - return $this->templatesDirectory; + $this->templatesDirectory = rtrim($directory, DIRECTORY_SEPARATOR); } /** - * Set templates directory - * @param string $dir + * Get templates base directory + * @return string */ - public function setTemplatesDirectory($dir) + public function getTemplatesDirectory() { - $this->templatesDirectory = rtrim($dir, '/'); + return $this->templatesDirectory; } /** - * Set template - * @param string $template - * @throws RuntimeException If template file does not exist - * - * DEPRECATION WARNING! - * This method will be removed in the near future. + * Get fully qualified path to template file using templates base directory + * @param string $file The template file pathname relative to templates base directory + * @return string */ - public function setTemplate($template) + public function getTemplatePathname($file) { - $this->templatePath = $this->getTemplatesDirectory() . '/' . ltrim($template, '/'); - if (!file_exists($this->templatePath)) { - throw new \RuntimeException('View cannot render template `' . $this->templatePath . '`. Template does not exist.'); - } + return $this->templatesDirectory . DIRECTORY_SEPARATOR . ltrim($file, DIRECTORY_SEPARATOR); } + /******************************************************************************** + * Rendering + *******************************************************************************/ + /** - * Display template - * - * This method echoes the rendered template to the current output buffer - * - * @param string $template Pathname of template file relative to templates directoy + * Echo the contents of a rendered template file + * @var string $template The template pathname, relative to the template base directory */ public function display($template) { @@ -183,12 +225,9 @@ public function display($template) } /** - * Fetch rendered template - * - * This method returns the rendered template - * - * @param string $template Pathname of template file relative to templates directory - * @return string + * Return the contents of a rendered template file + * @var string $template The template pathname, relative to the template base directory + * @return The rendered template */ public function fetch($template) { @@ -196,20 +235,23 @@ public function fetch($template) } /** - * Render template + * Render a template file * - * @param string $template Pathname of template file relative to templates directory - * @return string + * NOTE: This method should be overridden by custom view subclasses * - * DEPRECATION WARNING! - * Use `\Slim\View::fetch` to return a rendered template instead of `\Slim\View::render`. + * @var string $template The template pathname, relative to the template base directory + * @return The rendered template + * @throws \RuntimeException If resolved template pathname is not a valid file */ - public function render($template) + protected function render($template) { - $this->setTemplate($template); - extract($this->data); + $templatePathname = $this->getTemplatePathname($template); + if (!is_file($templatePathname)) { + throw new \RuntimeException("View cannot render `$template` because the template does not exist"); + } + extract($this->data->all()); ob_start(); - require $this->templatePath; + require $templatePathname; return ob_get_clean(); } diff --git a/tests/ViewTest.php b/tests/ViewTest.php index f8dfc98d7..8749c500c 100644 --- a/tests/ViewTest.php +++ b/tests/ViewTest.php @@ -32,18 +32,12 @@ class ViewTest extends PHPUnit_Framework_TestCase { - public function testViewIsConstructedWithDataArray() - { - $view = new \Slim\View(); - $this->assertAttributeEquals(array(), 'data', $view); - } - public function testGetDataAll() { $view = new \Slim\View(); - $property = new \ReflectionProperty($view, 'data'); - $property->setAccessible(true); - $property->setValue($view, array('foo' => 'bar')); + $prop = new \ReflectionProperty($view, 'data'); + $prop->setAccessible(true); + $prop->setValue($view, new \Slim\Helper\Set(array('foo' => 'bar'))); $this->assertSame(array('foo' => 'bar'), $view->getData()); } @@ -51,9 +45,9 @@ public function testGetDataAll() public function testGetDataKeyExists() { $view = new \Slim\View(); - $property = new \ReflectionProperty($view, 'data'); - $property->setAccessible(true); - $property->setValue($view, array('foo' => 'bar')); + $prop = new \ReflectionProperty($view, 'data'); + $prop->setAccessible(true); + $prop->setValue($view, new \Slim\Helper\Set(array('foo' => 'bar'))); $this->assertEquals('bar', $view->getData('foo')); } @@ -61,9 +55,9 @@ public function testGetDataKeyExists() public function testGetDataKeyNotExists() { $view = new \Slim\View(); - $property = new \ReflectionProperty($view, 'data'); - $property->setAccessible(true); - $property->setValue($view, array('foo' => 'bar')); + $prop = new \ReflectionProperty($view, 'data'); + $prop->setAccessible(true); + $prop->setValue($view, new \Slim\Helper\Set(array('foo' => 'bar'))); $this->assertNull($view->getData('abc')); } @@ -71,17 +65,21 @@ public function testGetDataKeyNotExists() public function testSetDataKeyValue() { $view = new \Slim\View(); + $prop = new \ReflectionProperty($view, 'data'); + $prop->setAccessible(true); $view->setData('foo', 'bar'); - $this->assertAttributeEquals(array('foo' => 'bar'), 'data', $view); + $this->assertEquals(array('foo' => 'bar'), $prop->getValue($view)->all()); } public function testSetDataArray() { $view = new \Slim\View(); + $prop = new \ReflectionProperty($view, 'data'); + $prop->setAccessible(true); $view->setData(array('foo' => 'bar')); - $this->assertAttributeEquals(array('foo' => 'bar'), 'data', $view); + $this->assertEquals(array('foo' => 'bar'), $prop->getValue($view)->all()); } public function testSetDataInvalidArgument() @@ -95,20 +93,22 @@ public function testSetDataInvalidArgument() public function testAppendData() { $view = new \Slim\View(); + $prop = new \ReflectionProperty($view, 'data'); + $prop->setAccessible(true); $view->appendData(array('foo' => 'bar')); - $this->assertAttributeEquals(array('foo' => 'bar'), 'data', $view); + $this->assertEquals(array('foo' => 'bar'), $prop->getValue($view)->all()); } public function testAppendDataOverwrite() { $view = new \Slim\View(); - $property = new \ReflectionProperty($view, 'data'); - $property->setAccessible(true); - $property->setValue($view, array('foo' => 'bar')); + $prop = new \ReflectionProperty($view, 'data'); + $prop->setAccessible(true); + $prop->setValue($view, new \Slim\Helper\Set(array('foo' => 'bar'))); $view->appendData(array('foo' => '123')); - $this->assertAttributeEquals(array('foo' => '123'), 'data', $view); + $this->assertEquals(array('foo' => '123'), $prop->getValue($view)->all()); } public function testAppendDataInvalidArgument() @@ -137,42 +137,32 @@ public function testSetTemplatesDirectory() $this->assertAttributeEquals('templates', 'templatesDirectory', $view); } - public function testSetTemplateExists() + public function testDisplay() { - $view = new \Slim\View(); - $property = new \ReflectionProperty($view, 'templatesDirectory'); - $property->setAccessible(true); - $property->setValue($view, dirname(__FILE__) . '/templates'); - $view->setTemplate('test.php'); + $this->expectOutputString('test output bar'); - $this->assertAttributeEquals(dirname(__FILE__) . '/templates/test.php', 'templatePath', $view); - } + $view = new \Slim\View(); + $prop1 = new \ReflectionProperty($view, 'data'); + $prop1->setAccessible(true); + $prop1->setValue($view, new \Slim\Helper\Set(array('foo' => 'bar'))); - public function testSetTemplateNotExists() - { - $this->setExpectedException('RuntimeException'); + $prop2 = new \ReflectionProperty($view, 'templatesDirectory'); + $prop2->setAccessible(true); + $prop2->setValue($view, dirname(__FILE__) . '/templates'); - $view = new \Slim\View(); - $property = new \ReflectionProperty($view, 'templatesDirectory'); - $property->setAccessible(true); - $property->setValue($view, dirname(__FILE__) . '/templates'); - $view->setTemplate('foo.php'); + $view->display('test.php'); } - public function testDisplay() + public function testDisplayTemplateThatDoesNotExist() { - $this->expectOutputString('test output bar'); + $this->setExpectedException('\RuntimeException'); $view = new \Slim\View(); - $property1 = new \ReflectionProperty($view, 'data'); - $property1->setAccessible(true); - $property1->setValue($view, array('foo' => 'bar')); - - $property2 = new \ReflectionProperty($view, 'templatesDirectory'); - $property2->setAccessible(true); - $property2->setValue($view, dirname(__FILE__) . '/templates'); + $prop2 = new \ReflectionProperty($view, 'templatesDirectory'); + $prop2->setAccessible(true); + $prop2->setValue($view, dirname(__FILE__) . '/templates'); - $view->display('test.php'); + $view->display('foo.php'); } } From a215ff9688e59d41ca898e833ede33020a5b0938 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 6 Apr 2013 19:41:04 -0400 Subject: [PATCH 44/66] Fix loose ends caused by change from Set::add to Set::replace --- Slim/Http/Response.php | 2 +- tests/Helper/SetTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Slim/Http/Response.php b/Slim/Http/Response.php index 254558348..97f5185d5 100644 --- a/Slim/Http/Response.php +++ b/Slim/Http/Response.php @@ -134,7 +134,7 @@ public function __construct($body = '', $status = 200, $headers = array()) { $this->setStatus($status); $this->headers = new \Slim\Http\Headers(array('Content-Type' => 'text/html')); - $this->headers->add($headers); + $this->headers->replace($headers); $this->cookies = new \Slim\Http\Cookies(); $this->write($body); } diff --git a/tests/Helper/SetTest.php b/tests/Helper/SetTest.php index 2392064a1..0d33c0a44 100644 --- a/tests/Helper/SetTest.php +++ b/tests/Helper/SetTest.php @@ -61,7 +61,7 @@ public function testGetNotExists() public function testAdd() { - $this->bag->add(array( + $this->bag->replace(array( 'abc' => '123', 'foo' => 'bar' )); From 61518e81196d07bea661e9644ed8390982487301 Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Mon, 8 Apr 2013 23:45:10 +0300 Subject: [PATCH 45/66] Modified Slim/Log to be PSR-3 Compliant --- Slim/Log.php | 171 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 141 insertions(+), 30 deletions(-) diff --git a/Slim/Log.php b/Slim/Log.php index 7706b2f24..ddf1dce42 100644 --- a/Slim/Log.php +++ b/Slim/Log.php @@ -39,11 +39,15 @@ * a Log Writer in conjunction with this Log to write to various output * destinations (e.g. a file). This class provides this interface: * - * debug( mixed $object ) - * info( mixed $object ) - * warn( mixed $object ) - * error( mixed $object ) - * fatal( mixed $object ) + * debug( mixed $object, array $context ) + * info( mixed $object, array $context ) + * notice( mixed $object, array $context ) + * warning( mixed $object, array $context ) + * error( mixed $object, array $context ) + * critical( mixed $object, array $context ) + * alert( mixed $object, array $context ) + * emergency( mixed $object, array $context ) + * log( mixed $level, mixed $object, array $context ) * * This class assumes only that your Log Writer has a public `write()` method * that accepts any object as its one and only argument. The Log Writer @@ -56,21 +60,28 @@ */ class Log { - const FATAL = 0; - const ERROR = 1; - const WARN = 2; - const INFO = 3; - const DEBUG = 4; + const EMERGENCY = 1; + const ALERT = 2; + const CRITICAL = 3; + const FATAL = 3; //Depracated replace with CRITICAL + const ERROR = 4; + const WARN = 5; + const NOTICE = 6; + const INFO = 7; + const DEBUG = 8; /** * @var array */ protected static $levels = array( - self::FATAL => 'FATAL', - self::ERROR => 'ERROR', - self::WARN => 'WARN', - self::INFO => 'INFO', - self::DEBUG => 'DEBUG' + self::EMERGENCY => 'EMERGENCY', + self::ALERT => 'ALERT', + self::CRITICAL => 'CRITICAL', + self::ERROR => 'ERROR', + self::WARN => 'WARNING', + self::NOTICE => 'NOTICE', + self::INFO => 'INFO', + self::DEBUG => 'DEBUG' ); /** @@ -173,54 +184,143 @@ public function isEnabled() /** * Log debug message * @param mixed $object + * @param array $context * @return mixed|false What the Logger returns, or false if Logger not set or not enabled */ - public function debug($object) + public function debug($object, $context = array()) { - return $this->write($object, self::DEBUG); + return $this->log(self::DEBUG, $object, $context); } /** * Log info message * @param mixed $object + * @param array $context * @return mixed|false What the Logger returns, or false if Logger not set or not enabled */ - public function info($object) + public function info($object, $context = array()) { - return $this->write($object, self::INFO); + return $this->log(self::INFO, $object, $context); } /** - * Log warn message + * Log notice message * @param mixed $object + * @param array $context * @return mixed|false What the Logger returns, or false if Logger not set or not enabled */ - public function warn($object) + public function notice($object, $context = array()) { - return $this->write($object, self::WARN); + return $this->log(self::NOTICE, $object, $context); + } + + /** + * Log warning message + * @param mixed $object + * @param array $context + * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + */ + public function warning($object, $context = array()) + { + return $this->log(self::WARN, $object, $context); + } + + /** + * DEPRACATED for function warning + * Log warning message + * @param mixed $object + * @param array $context + * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + */ + public function warn($object, $context = array()) + { + return $this->log(self::WARN, $object, $context); } /** * Log error message * @param mixed $object + * @param array $context * @return mixed|false What the Logger returns, or false if Logger not set or not enabled */ - public function error($object) + public function error($object, $context = array()) { - return $this->write($object, self::ERROR); + return $this->log(self::ERROR, $object, $context); } /** + * Log critical message + * @param mixed $object + * @param array $context + * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + */ + public function critical($object, $context = array()) + { + return $this->log(self::CRITICAL, $object, $context); + } + + /** + * DEPRACATED for function critical * Log fatal message * @param mixed $object + * @param array $context + * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + */ + public function fatal($object, $context = array()) + { + return $this->log(self::CRITICAL, $object, $context); + } + + /** + * Log alert message + * @param mixed $object + * @param array $context + * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + */ + public function alert($object, $context = array()) + { + return $this->log(self::ALERT, $object, $context); + } + + /** + * Log emergency message + * @param mixed $object + * @param array $context * @return mixed|false What the Logger returns, or false if Logger not set or not enabled */ - public function fatal($object) + public function emergency($object, $context = array()) { - return $this->write($object, self::FATAL); + return $this->log(self::EMERGENCY, $object, $context); } /** + * Log message + * @param mixed $level + * @param mixed $object + * @param array $context + * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + */ + public function log($level, $object, $context = array()) + { + if (!isset(self::$levels[$level])) { + throw new \InvalidArgumentException('Invalid log level supplied to function'); + } else if ($this->enabled && $this->writer && $level <= $this->level) { + $message = (string)$object; + if (count($context) > 0) { + if (isset($context['exception']) && $context['exception'] instanceof \Exception) { + $message .= ' - ' . $context['exception']; + unset($context['exception']); + } + $message = $this->interpolate($message, $context); + } + return $this->writer->write($message, $level); + } else { + return false; + } + } + + /** + * DEPRACATED for function log * Log message * @param mixed The object to log * @param int The message level @@ -228,10 +328,21 @@ public function fatal($object) */ protected function write($object, $level) { - if ($this->enabled && $this->writer && $level <= $this->level) { - return $this->writer->write($object, $level); - } else { - return false; + return $this->log($level, $object); + } + + /** + * Interpolate log message + * @param mixed The log message + * @param array An array of placeholder values + * @return string The processed string + */ + protected function interpolate($message, $context = array()) + { + $replace = array(); + foreach ($context as $key => $value) { + $replace['{' . $key . '}'] = $value; } + return strstr($message, $replace); } } From dfefbd6a00012068008e92479a4667493ae51b80 Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Tue, 9 Apr 2013 00:10:01 +0300 Subject: [PATCH 46/66] Fixed wrong function call in method interpolate --- Slim/Log.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Slim/Log.php b/Slim/Log.php index ddf1dce42..c18add516 100644 --- a/Slim/Log.php +++ b/Slim/Log.php @@ -343,6 +343,6 @@ protected function interpolate($message, $context = array()) foreach ($context as $key => $value) { $replace['{' . $key . '}'] = $value; } - return strstr($message, $replace); + return strtr($message, $replace); } } From c62b41a8ee690068aca39dd99efe6e27b976101d Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Tue, 9 Apr 2013 00:22:31 +0300 Subject: [PATCH 47/66] Added/Modified Tests for new additions to Logger --- tests/LogTest.php | 68 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/tests/LogTest.php b/tests/LogTest.php index d390e45c4..8e7362f51 100644 --- a/tests/LogTest.php +++ b/tests/LogTest.php @@ -96,6 +96,21 @@ public function testLogInfo() } public function testLogInfoExcludedByLevel() + { + $log = new \Slim\Log(new MyWriter()); + $log->setLevel(\Slim\Log::NOTICE); + $this->assertFalse($log->info('Info')); + } + + public function testLogNotice() + { + $this->expectOutputString('Notice'); + $log = new \Slim\Log(new MyWriter()); + $result = $log->notice('Notice'); + $this->assertTrue($result); + } + + public function testLogNoticeExcludedByLevel() { $log = new \Slim\Log(new MyWriter()); $log->setLevel(\Slim\Log::WARN); @@ -106,7 +121,7 @@ public function testLogWarn() { $this->expectOutputString('Warn'); $log = new \Slim\Log(new MyWriter()); - $result = $log->warn('Warn'); + $result = $log->warning('Warn'); $this->assertTrue($result); } @@ -114,7 +129,7 @@ public function testLogWarnExcludedByLevel() { $log = new \Slim\Log(new MyWriter()); $log->setLevel(\Slim\Log::ERROR); - $this->assertFalse($log->warn('Warn')); + $this->assertFalse($log->warning('Warn')); } public function testLogError() @@ -128,15 +143,56 @@ public function testLogError() public function testLogErrorExcludedByLevel() { $log = new \Slim\Log(new MyWriter()); - $log->setLevel(\Slim\Log::FATAL); + $log->setLevel(\Slim\Log::CRITICAL); $this->assertFalse($log->error('Error')); } - public function testLogFatal() + public function testLogCritical() + { + $this->expectOutputString('Critical'); + $log = new \Slim\Log(new MyWriter()); + $result = $log->critical('Critical'); + $this->assertTrue($result); + } + + public function testLogCriticalExcludedByLevel() + { + $log = new \Slim\Log(new MyWriter()); + $log->setLevel(\Slim\Log::ALERT); + $this->assertFalse($log->critical('Critical')); + } + + public function testLogAlert() + { + $this->expectOutputString('Alert'); + $log = new \Slim\Log(new MyWriter()); + $result = $log->alert('Alert'); + $this->assertTrue($result); + } + + public function testLogAlertExcludedByLevel() + { + $log = new \Slim\Log(new MyWriter()); + $log->setLevel(\Slim\Log::EMERGENCY); + $this->assertFalse($log->alert('Alert')); + } + + public function testLogEmergency() + { + $this->expectOutputString('Emergency'); + $log = new \Slim\Log(new MyWriter()); + $result = $log->emergency('Emergency'); + $this->assertTrue($result); + } + + public function testInterpolateMessage() { - $this->expectOutputString('Fatal'); + $this->expectOutputString('Hello Slim !'); $log = new \Slim\Log(new MyWriter()); - $result = $log->fatal('Fatal'); + $result = $log->debug( + 'Hello {framework} !', + array('framework' => "Slim") + ); $this->assertTrue($result); } From f91841f5706e72be8f4931001f9a0de3ccd2c19a Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Tue, 9 Apr 2013 00:30:46 +0300 Subject: [PATCH 48/66] Fixed spelling for the word 'deprecated' --- Slim/Log.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Slim/Log.php b/Slim/Log.php index c18add516..d43a010a5 100644 --- a/Slim/Log.php +++ b/Slim/Log.php @@ -63,7 +63,7 @@ class Log const EMERGENCY = 1; const ALERT = 2; const CRITICAL = 3; - const FATAL = 3; //Depracated replace with CRITICAL + const FATAL = 3; //DEPRECATED replace with CRITICAL const ERROR = 4; const WARN = 5; const NOTICE = 6; @@ -226,7 +226,7 @@ public function warning($object, $context = array()) } /** - * DEPRACATED for function warning + * DEPRECATED for function warning * Log warning message * @param mixed $object * @param array $context @@ -260,7 +260,7 @@ public function critical($object, $context = array()) } /** - * DEPRACATED for function critical + * DEPRECATED for function critical * Log fatal message * @param mixed $object * @param array $context @@ -320,7 +320,7 @@ public function log($level, $object, $context = array()) } /** - * DEPRACATED for function log + * DEPRECATED for function log * Log message * @param mixed The object to log * @param int The message level From f968979c9124386bba209bef0dcb588ff62ac9d0 Mon Sep 17 00:00:00 2001 From: Dmitry Lomakin Date: Sun, 14 Apr 2013 22:11:45 +0400 Subject: [PATCH 49/66] On some environments fopen('php://stderr', 'w') can produce a wPHP Warning. We'd like to get rid of that --- Slim/Environment.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Slim/Environment.php b/Slim/Environment.php index 147d00f20..9684e826c 100644 --- a/Slim/Environment.php +++ b/Slim/Environment.php @@ -183,7 +183,7 @@ private function __construct($settings = null) $env['slim.input'] = $rawInput; //Error stream - $env['slim.errors'] = fopen('php://stderr', 'w'); + $env['slim.errors'] = @fopen('php://stderr', 'w'); $this->properties = $env; } From f4ceddee1a39fb2dde10dd6b88d7c42f47a5b3ca Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Thu, 2 May 2013 17:04:12 +0100 Subject: [PATCH 50/66] Changed route group to not assume the user will want a trailing slash --- Slim/Router.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Slim/Router.php b/Slim/Router.php index 3888354a5..7ff7e2f92 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -129,7 +129,7 @@ public function map($pattern, $callable) { list($groupPattern, $groupMiddleware) = $this->processGroups(); if (count($this->routeGroups) > 0) { - $pattern = $groupPattern . ltrim($pattern, '/'); + $pattern = $groupPattern . $pattern; } $route = new \Slim\Route($pattern, $callable); $this->routes[] = $route; @@ -153,11 +153,11 @@ public function map($pattern, $callable) */ protected function processGroups() { - $pattern = "/"; + $pattern = ""; $middleware = array(); foreach ($this->routeGroups as $group) { $k = key($group); - $pattern .= $k . "/"; + $pattern .= $k; array_push($middleware, $group[$k]); } return array($pattern, $middleware); @@ -171,7 +171,6 @@ protected function processGroups() */ public function pushGroup($group, $middleware = null) { - $group = trim($group, '/'); return array_push($this->routeGroups, array($group => $middleware)); } From 6843cbc9aaa73c23c42c50e252c1e52125887f1a Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Thu, 2 May 2013 17:07:40 +0100 Subject: [PATCH 51/66] Reversed to original setup of index.php --- index.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/index.php b/index.php index 8839a0426..a802b2b69 100644 --- a/index.php +++ b/index.php @@ -9,9 +9,7 @@ */ require 'Slim/Slim.php'; -use \Slim\Slim; - -Slim::registerAutoloader(); +\Slim\Slim::registerAutoloader(); /** * Step 2: Instantiate a Slim application @@ -21,7 +19,7 @@ * your Slim application now by passing an associative array * of setting names and values into the application constructor. */ -$app = new Slim(); +$app = new \Slim\Slim(); /** * Step 3: Define the Slim application routes From ddeb7526b7ee7c4d53f01b09961e342d7bd7e0a2 Mon Sep 17 00:00:00 2001 From: Christoph Kempen Date: Sat, 18 May 2013 10:36:27 +0200 Subject: [PATCH 52/66] Since mcrypt is needed to for the encrypt/decrypt methods and the unit test will fail if mcrypt is not present, a check in closer would be handy. Problem is that if the mcrypt library is not present NO encryption is being performed without warning, thus returning the string like it was passed. --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a49c05ec2..656de3297 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,8 @@ } ], "require": { - "php": ">=5.3.0" + "php": ">=5.3.0", + "ext-mcrypt": "*" }, "autoload": { "psr-0": { "Slim": "." } From ae7c4815929af0baa0abdc6f81e0dc73f1ff785e Mon Sep 17 00:00:00 2001 From: = Date: Mon, 3 Jun 2013 16:28:45 -0400 Subject: [PATCH 53/66] Prevent code injection within Router::urlFor with preg_quote --- Slim/Router.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Slim/Router.php b/Slim/Router.php index 7ff7e2f92..7ee314520 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -166,7 +166,7 @@ protected function processGroups() /** * Add a route group to the array * @param string $group The group pattern (ie. "/books/:id") - * @param array|null $middleware Optional parameter array of middleware + * @param array|null $middleware Optional parameter array of middleware * @return int The index of the new group */ public function pushGroup($group, $middleware = null) @@ -196,10 +196,12 @@ public function urlFor($name, $params = array()) throw new \RuntimeException('Named route not found for name: ' . $name); } $search = array(); - foreach (array_keys($params) as $key) { - $search[] = '#:' . $key . '\+?(?!\w)#'; + $replace = array(); + foreach ($params as $key => $value) { + $search[] = '#:' . preg_quote($key) . '\+?(?!\w)#'; + $replace[] = preg_quote($value); } - $pattern = preg_replace($search, $params, $this->getNamedRoute($name)->getPattern()); + $pattern = preg_replace($search, $replace, $this->getNamedRoute($name)->getPattern()); //Remove remnants of unpopulated, trailing optional pattern segments return preg_replace('#\(/?:.+\)|\(|\)#', '', $pattern); From 86fb42f9363aaa227553dae4a85b5fcd3170128d Mon Sep 17 00:00:00 2001 From: = Date: Mon, 3 Jun 2013 16:43:31 -0400 Subject: [PATCH 54/66] Refine Router::urlFor parameter escaping --- Slim/Router.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Slim/Router.php b/Slim/Router.php index 7ee314520..0cd60bd78 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -196,12 +196,10 @@ public function urlFor($name, $params = array()) throw new \RuntimeException('Named route not found for name: ' . $name); } $search = array(); - $replace = array(); foreach ($params as $key => $value) { - $search[] = '#:' . preg_quote($key) . '\+?(?!\w)#'; - $replace[] = preg_quote($value); + $search[] = '#:' . preg_quote($key, '#') . '\+?(?!\w)#'; } - $pattern = preg_replace($search, $replace, $this->getNamedRoute($name)->getPattern()); + $pattern = preg_replace($search, $params, $this->getNamedRoute($name)->getPattern()); //Remove remnants of unpopulated, trailing optional pattern segments return preg_replace('#\(/?:.+\)|\(|\)#', '', $pattern); From c62d671b193d874555b492b059d2c6dfbfc661fe Mon Sep 17 00:00:00 2001 From: = Date: Thu, 6 Jun 2013 12:00:10 -0400 Subject: [PATCH 55/66] Implement IoC container --- Slim/Container.php | 129 ++++++++++++++++++++++++++++++++++++++ Slim/Slim.php | 152 ++++++++++++++++++++++++--------------------- 2 files changed, 210 insertions(+), 71 deletions(-) create mode 100644 Slim/Container.php diff --git a/Slim/Container.php b/Slim/Container.php new file mode 100644 index 000000000..96219bdac --- /dev/null +++ b/Slim/Container.php @@ -0,0 +1,129 @@ + + * @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; + +/** + * Container + * + * This is an IoC (Inversion of Control) container for the Slim Framework. + * It allows simple and configurable dependency injection for most of the + * Slim application's component objects (e.g. Request, Response, Router). + * + * This class is largely inspired by Fabien Potencier's Pimple component + * which you can find here: https://github.com/fabpot/Pimple + * + * @package Slim + * @author Josh Lockhart + * @since 2.3.0 + */ +class Container implements \ArrayAccess +{ + /** + * @var array + */ + protected $registry; + + /** + * Constructor + * @param array $values The default registry objects or values + */ + public function __construct(array $values = array()) + { + $this->registry = $values; + } + + /** + * Sets a value or object + * @param string $key The name of the value or object + * @param mixed $value The value or Closure to define the object + */ + public function offsetSet($key, $value) + { + $this->registry[$key] = $value; + } + + /** + * Gets a value or object + * @param string $key The name of the value or object + * @return mixed The value or object + * @throws \InvalidArgumentException If the container key is not defined + */ + public function offsetGet($key) + { + if (!isset($this->registry[$key])) { + throw new \InvalidArgumentException(sprintf('Container key "%s" is not defined', $key)); + } + + $isInvokable = is_object($this->registry[$key]) && method_exists($this->registry[$key], '__invoke'); + + return $isInvokable ? $this->registry[$key]($this) : $this->registry[$key]; + } + + /** + * Does a value or object exist for key? + * @param string $key The name of the value or object + * @return bool + */ + public function offsetExists($key) + { + return array_key_exists($key, $this->registry); + } + + /** + * Unsets a value or object + * @param string $key The name of the value or object + */ + public function offsetUnset($key) + { + unset($this->registry[$key]); + } + + /** + * Ensure a value or object will remain globally unique + * @param string $key The value or object name + * @param Closure The closure that defines the object + * @return mixed + */ + public function singleton($key, $value) + { + $this->registry[$key] = function ($c) use ($value) { + static $object; + + if (null === $object) { + $object = $value($c); + } + + return $object; + }; + } +} diff --git a/Slim/Slim.php b/Slim/Slim.php index 051ae62c6..dbc5cf862 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -52,49 +52,19 @@ class Slim const VERSION = '2.2.0'; /** - * @var array[\Slim] - */ - protected static $apps = array(); - - /** - * @var string - */ - protected $name; - - /** - * @var array - */ - protected $environment; - - /** - * @var \Slim\Http\Request - */ - public $request; - - /** - * @var \Slim\Http\Response - */ - public $response; - - /** - * @var \Slim\Router + * @var \Slim\Container */ - public $router; + public $container; /** - * @var \Slim\View - */ - public $view; - - /** - * @var array + * @var array[\Slim] */ - protected $settings; + protected static $apps = array(); /** * @var string */ - protected $mode; + protected $name; /** * @var array @@ -173,38 +143,91 @@ public static function registerAutoloader() * Constructor * @param array $userSettings Associative array of application settings */ - public function __construct($userSettings = array()) + public function __construct(array $userSettings = array()) { - // Setup Slim application - $this->settings = array_merge(static::getDefaultSettings(), $userSettings); - $this->environment = \Slim\Environment::getInstance(); - $this->request = new \Slim\Http\Request($this->environment); - $this->response = new \Slim\Http\Response(); - $this->router = new \Slim\Router(); + // Setup IoC container + $this->container = new \Slim\Container(); + $this->container['settings'] = array_merge(static::getDefaultSettings(), $userSettings); + + // Default environment + $this->container->singleton('environment', function ($c) { + return \Slim\Environment::getInstance(); + }); + + // Default request + $this->container->singleton('request', function ($c) { + return new \Slim\Http\Request($c['environment']); + }); + + // Default response + $this->container->singleton('response', function ($c) { + return new \Slim\Http\Response(); + }); + + // Default router + $this->container->singleton('router', function ($c) { + return new \Slim\Router(); + }); + + // Default view + $this->container->singleton('view', function ($c) { + $viewClass = $c['settings']['view']; + + return ($viewClass instanceOf \Slim\View) ? $viewClass : new $viewClass; + }); + + // Default log writer + $this->container->singleton('logWriter', function ($c) { + $logWriter = $c['settings']['log.writer']; + + return is_object($logWriter) ? $logWriter : new \Slim\LogWriter($c['environment']['slim.errors']); + }); + + // Default log + $this->container->singleton('log', function ($c) { + $log = new \Slim\Log($c['logWriter']); + $log->setEnabled($c['settings']['log.enabled']); + $log->setLevel($c['settings']['log.level']); + $c['environment']['slim.log'] = $log; + + return $log; + }); + + // Default mode + $this->container['mode'] = function ($c) { + $mode = $c['settings']['mode']; + + if (isset($_ENV['SLIM_MODE'])) { + $mode = $_ENV['SLIM_MODE']; + } else { + $envMode = getenv('SLIM_MODE'); + if ($envMode !== false) { + $mode = $envMode; + } + } + + return $mode; + }; + + // Define default middleware stack $this->middleware = array($this); $this->add(new \Slim\Middleware\Flash()); $this->add(new \Slim\Middleware\MethodOverride()); - // Determine application mode - $this->getMode(); - - // Setup view - $this->view($this->config('view')); - // Make default if first instance if (is_null(static::getInstance())) { $this->setName('default'); } + } - // Set default logger that writes to stderr (may be overridden with middleware) - $logWriter = $this->config('log.writer'); - if (!$logWriter) { - $logWriter = new \Slim\LogWriter($this->environment['slim.errors']); - } - $log = new \Slim\Log($logWriter); - $log->setEnabled($this->config('log.enabled')); - $log->setLevel($this->config('log.level')); - $this->environment['slim.log'] = $log; + public function __get($name) + { + return $this->container[$name]; + } + + public function __set($name, $value) + { + $this->container[$name] = $value; } /** @@ -317,19 +340,6 @@ public function config($name, $value = null) */ public function getMode() { - if (!isset($this->mode)) { - if (isset($_ENV['SLIM_MODE'])) { - $this->mode = $_ENV['SLIM_MODE']; - } else { - $envMode = getenv('SLIM_MODE'); - if ($envMode !== false) { - $this->mode = $envMode; - } else { - $this->mode = $this->config('mode'); - } - } - } - return $this->mode; } @@ -362,7 +372,7 @@ public function configureMode($mode, $callable) */ public function getLog() { - return $this->environment['slim.log']; + return $this->log; } /******************************************************************************** From 8ea6d58ee2fa5b144ee24daaf8e0ce93eb5b8690 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 6 Jun 2013 12:50:13 -0400 Subject: [PATCH 56/66] Fix a few broken unit tests --- Slim/Middleware/PrettyExceptions.php | 1 + Slim/Slim.php | 7 +++++-- tests/SlimTest.php | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Slim/Middleware/PrettyExceptions.php b/Slim/Middleware/PrettyExceptions.php index e079e30f5..f89e04ea7 100644 --- a/Slim/Middleware/PrettyExceptions.php +++ b/Slim/Middleware/PrettyExceptions.php @@ -66,6 +66,7 @@ public function call() try { $this->next->call(); } catch (\Exception $e) { + $log = $this->app->getLog(); // Force Slim to append log to env if not already $env = $this->app->environment(); $env['slim.log']->error($e); $this->app->contentType('text/html'); diff --git a/Slim/Slim.php b/Slim/Slim.php index dbc5cf862..c58215e32 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -188,7 +188,8 @@ public function __construct(array $userSettings = array()) $log = new \Slim\Log($c['logWriter']); $log->setEnabled($c['settings']['log.enabled']); $log->setLevel($c['settings']['log.level']); - $c['environment']['slim.log'] = $log; + $env = $c['environment']; + $env['slim.log'] = $log; return $log; }); @@ -321,7 +322,9 @@ public function config($name, $value = null) return isset($this->settings[$name]) ? $this->settings[$name] : null; } } else { - $this->settings[$name] = $value; + $settings = $this->settings; + $settings[$name] = $value; + $this->settings = $settings; } } diff --git a/tests/SlimTest.php b/tests/SlimTest.php index a7a907d00..7e996fc84 100644 --- a/tests/SlimTest.php +++ b/tests/SlimTest.php @@ -1231,13 +1231,13 @@ public function testSlimError() public function testDefaultHandlerLogsTheErrorWhenDebugIsFalse() { $s = new \Slim\Slim(array('debug' => false)); + $s->container->singleton('log', function ($c) { + return new EchoErrorLogger(); + }); $s->get('/bar', function () use ($s) { throw new \InvalidArgumentException('my specific error message'); }); - $env = $s->environment(); - $env['slim.log'] = new EchoErrorLogger(); // <-- inject the fake logger - ob_start(); $s->run(); $output = ob_get_clean(); From 985dc9243988d058f09189c1175a24fbd37256e8 Mon Sep 17 00:00:00 2001 From: mnlg Date: Thu, 6 Jun 2013 21:39:49 +0200 Subject: [PATCH 57/66] replace Container with Helper\Set --- Slim/Container.php | 129 -------------------------------------------- Slim/Helper/Set.php | 23 +++++++- Slim/Slim.php | 2 +- 3 files changed, 23 insertions(+), 131 deletions(-) delete mode 100644 Slim/Container.php diff --git a/Slim/Container.php b/Slim/Container.php deleted file mode 100644 index 96219bdac..000000000 --- a/Slim/Container.php +++ /dev/null @@ -1,129 +0,0 @@ - - * @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; - -/** - * Container - * - * This is an IoC (Inversion of Control) container for the Slim Framework. - * It allows simple and configurable dependency injection for most of the - * Slim application's component objects (e.g. Request, Response, Router). - * - * This class is largely inspired by Fabien Potencier's Pimple component - * which you can find here: https://github.com/fabpot/Pimple - * - * @package Slim - * @author Josh Lockhart - * @since 2.3.0 - */ -class Container implements \ArrayAccess -{ - /** - * @var array - */ - protected $registry; - - /** - * Constructor - * @param array $values The default registry objects or values - */ - public function __construct(array $values = array()) - { - $this->registry = $values; - } - - /** - * Sets a value or object - * @param string $key The name of the value or object - * @param mixed $value The value or Closure to define the object - */ - public function offsetSet($key, $value) - { - $this->registry[$key] = $value; - } - - /** - * Gets a value or object - * @param string $key The name of the value or object - * @return mixed The value or object - * @throws \InvalidArgumentException If the container key is not defined - */ - public function offsetGet($key) - { - if (!isset($this->registry[$key])) { - throw new \InvalidArgumentException(sprintf('Container key "%s" is not defined', $key)); - } - - $isInvokable = is_object($this->registry[$key]) && method_exists($this->registry[$key], '__invoke'); - - return $isInvokable ? $this->registry[$key]($this) : $this->registry[$key]; - } - - /** - * Does a value or object exist for key? - * @param string $key The name of the value or object - * @return bool - */ - public function offsetExists($key) - { - return array_key_exists($key, $this->registry); - } - - /** - * Unsets a value or object - * @param string $key The name of the value or object - */ - public function offsetUnset($key) - { - unset($this->registry[$key]); - } - - /** - * Ensure a value or object will remain globally unique - * @param string $key The value or object name - * @param Closure The closure that defines the object - * @return mixed - */ - public function singleton($key, $value) - { - $this->registry[$key] = function ($c) use ($value) { - static $object; - - if (null === $object) { - $object = $value($c); - } - - return $object; - }; - } -} diff --git a/Slim/Helper/Set.php b/Slim/Helper/Set.php index 8f79faf28..167fca059 100644 --- a/Slim/Helper/Set.php +++ b/Slim/Helper/Set.php @@ -83,7 +83,9 @@ public function set($key, $value) public function get($key, $default = null) { if ($this->has($key)) { - return $this->data[$this->normalizeKey($key)]; + $isInvokable = is_object($this->data[$this->normalizeKey($key)]) && method_exists($this->data[$this->normalizeKey($key)], '__invoke'); + + return $isInvokable ? $this->data[$this->normalizeKey($key)]($this) : $this->data[$this->normalizeKey($key)]; } return $default; @@ -187,4 +189,23 @@ public function getIterator() { return new \ArrayIterator($this->data); } + + /** + * Ensure a value or object will remain globally unique + * @param string $key The value or object name + * @param Closure The closure that defines the object + * @return mixed + */ + public function singleton($key, $value) + { + $this->set($key, function ($c) use ($value) { + static $object; + + if (null === $object) { + $object = $value($c); + } + + return $object; + }); + } } diff --git a/Slim/Slim.php b/Slim/Slim.php index c58215e32..93a8dbe6c 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -146,7 +146,7 @@ public static function registerAutoloader() public function __construct(array $userSettings = array()) { // Setup IoC container - $this->container = new \Slim\Container(); + $this->container = new \Slim\Helper\Set(); $this->container['settings'] = array_merge(static::getDefaultSettings(), $userSettings); // Default environment From dbd2f9cf9541c1decad0b7953b1f986e9597cc0e Mon Sep 17 00:00:00 2001 From: = Date: Thu, 6 Jun 2013 17:08:27 -0400 Subject: [PATCH 58/66] Update inline docs for Slim::$container variable --- Slim/Slim.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Slim/Slim.php b/Slim/Slim.php index 93a8dbe6c..e9f790e98 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -52,7 +52,7 @@ class Slim const VERSION = '2.2.0'; /** - * @var \Slim\Container + * @var \Slim\Helper\Set */ public $container; From c360a18f8ccc167c1bf44556dee5151ffc34b9f8 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 5 Jul 2013 17:48:59 -0400 Subject: [PATCH 59/66] Bump version --- Slim/Environment.php | 2 +- Slim/Exception/Pass.php | 2 +- Slim/Exception/Stop.php | 2 +- Slim/Helper/Set.php | 2 +- Slim/Http/Cookies.php | 2 +- Slim/Http/Headers.php | 2 +- Slim/Http/Request.php | 2 +- Slim/Http/Response.php | 2 +- Slim/Http/Util.php | 2 +- Slim/Log.php | 4 ++-- Slim/LogWriter.php | 2 +- Slim/Middleware.php | 2 +- Slim/Middleware/ContentTypes.php | 2 +- Slim/Middleware/MethodOverride.php | 2 +- Slim/Middleware/PrettyExceptions.php | 2 +- Slim/Middleware/SessionCookie.php | 2 +- Slim/Router.php | 2 +- Slim/Slim.php | 4 ++-- Slim/View.php | 2 +- tests/EnvironmentTest.php | 2 +- tests/Helper/SetTest.php | 2 +- tests/Http/CookiesTest.php | 2 +- tests/Http/HeadersTest.php | 2 +- tests/Http/RequestTest.php | 2 +- tests/Http/ResponseTest.php | 2 +- tests/Http/UtilTest.php | 2 +- tests/LogTest.php | 2 +- tests/LogWriterTest.php | 2 +- tests/Middleware/ContentTypesTest.php | 2 +- tests/Middleware/FlashTest.php | 2 +- tests/Middleware/MethodOverrideTest.php | 2 +- tests/Middleware/PrettyExceptionsTest.php | 2 +- tests/Middleware/SessionCookieTest.php | 2 +- tests/MiddlewareTest.php | 2 +- tests/RouteTest.php | 2 +- tests/RouterTest.php | 2 +- tests/SlimTest.php | 2 +- tests/ViewTest.php | 2 +- 38 files changed, 40 insertions(+), 40 deletions(-) diff --git a/Slim/Environment.php b/Slim/Environment.php index 073891cf6..707b2dc16 100644 --- a/Slim/Environment.php +++ b/Slim/Environment.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Exception/Pass.php b/Slim/Exception/Pass.php index 67daa8b42..2833dd0ec 100644 --- a/Slim/Exception/Pass.php +++ b/Slim/Exception/Pass.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Exception/Stop.php b/Slim/Exception/Stop.php index 38652edf5..8667eaeaa 100644 --- a/Slim/Exception/Stop.php +++ b/Slim/Exception/Stop.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Helper/Set.php b/Slim/Helper/Set.php index 167fca059..e1034b0db 100644 --- a/Slim/Helper/Set.php +++ b/Slim/Helper/Set.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Http/Cookies.php b/Slim/Http/Cookies.php index 61e0f6d29..61c70e742 100644 --- a/Slim/Http/Cookies.php +++ b/Slim/Http/Cookies.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Http/Headers.php b/Slim/Http/Headers.php index d48cdcc03..1ec605d0b 100644 --- a/Slim/Http/Headers.php +++ b/Slim/Http/Headers.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Http/Request.php b/Slim/Http/Request.php index 43d2c5b79..51c3ccf04 100644 --- a/Slim/Http/Request.php +++ b/Slim/Http/Request.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Http/Response.php b/Slim/Http/Response.php index 5f5e56bdc..2308465ad 100644 --- a/Slim/Http/Response.php +++ b/Slim/Http/Response.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Http/Util.php b/Slim/Http/Util.php index fe816b671..992e7cb3b 100644 --- a/Slim/Http/Util.php +++ b/Slim/Http/Util.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Log.php b/Slim/Log.php index d43a010a5..aeb852164 100644 --- a/Slim/Log.php +++ b/Slim/Log.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE @@ -44,7 +44,7 @@ * notice( mixed $object, array $context ) * warning( mixed $object, array $context ) * error( mixed $object, array $context ) - * critical( mixed $object, array $context ) + * critical( mixed $object, array $context ) * alert( mixed $object, array $context ) * emergency( mixed $object, array $context ) * log( mixed $level, mixed $object, array $context ) diff --git a/Slim/LogWriter.php b/Slim/LogWriter.php index f26eb0fcb..77b255fe7 100644 --- a/Slim/LogWriter.php +++ b/Slim/LogWriter.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Middleware.php b/Slim/Middleware.php index 641226a68..39bcb4bee 100644 --- a/Slim/Middleware.php +++ b/Slim/Middleware.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Middleware/ContentTypes.php b/Slim/Middleware/ContentTypes.php index b6e13e348..809b500c0 100644 --- a/Slim/Middleware/ContentTypes.php +++ b/Slim/Middleware/ContentTypes.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Middleware/MethodOverride.php b/Slim/Middleware/MethodOverride.php index 917fac188..f43b41f0b 100644 --- a/Slim/Middleware/MethodOverride.php +++ b/Slim/Middleware/MethodOverride.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Middleware/PrettyExceptions.php b/Slim/Middleware/PrettyExceptions.php index f89e04ea7..b67545e19 100644 --- a/Slim/Middleware/PrettyExceptions.php +++ b/Slim/Middleware/PrettyExceptions.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Middleware/SessionCookie.php b/Slim/Middleware/SessionCookie.php index f6a4117a7..d13dd94cd 100644 --- a/Slim/Middleware/SessionCookie.php +++ b/Slim/Middleware/SessionCookie.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Router.php b/Slim/Router.php index 6e26dee54..3b7d15e52 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/Slim/Slim.php b/Slim/Slim.php index e9f790e98..2510de88b 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE @@ -49,7 +49,7 @@ class Slim /** * @const string */ - const VERSION = '2.2.0'; + const VERSION = '2.3.0'; /** * @var \Slim\Helper\Set diff --git a/Slim/View.php b/Slim/View.php index 667bebbc8..dc0ff8730 100644 --- a/Slim/View.php +++ b/Slim/View.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * @package Slim * * MIT LICENSE diff --git a/tests/EnvironmentTest.php b/tests/EnvironmentTest.php index d528ae8ac..e86b24ac5 100644 --- a/tests/EnvironmentTest.php +++ b/tests/EnvironmentTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/Helper/SetTest.php b/tests/Helper/SetTest.php index 0d33c0a44..a2994b09d 100644 --- a/tests/Helper/SetTest.php +++ b/tests/Helper/SetTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/Http/CookiesTest.php b/tests/Http/CookiesTest.php index da061a749..33868cef0 100644 --- a/tests/Http/CookiesTest.php +++ b/tests/Http/CookiesTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/Http/HeadersTest.php b/tests/Http/HeadersTest.php index 9221db981..e5fe17fe1 100644 --- a/tests/Http/HeadersTest.php +++ b/tests/Http/HeadersTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/Http/RequestTest.php b/tests/Http/RequestTest.php index 9cd192356..7a837a7d0 100644 --- a/tests/Http/RequestTest.php +++ b/tests/Http/RequestTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/Http/ResponseTest.php b/tests/Http/ResponseTest.php index f8e52c224..85b857f35 100644 --- a/tests/Http/ResponseTest.php +++ b/tests/Http/ResponseTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/Http/UtilTest.php b/tests/Http/UtilTest.php index 4eb0b4d30..9d665da0e 100644 --- a/tests/Http/UtilTest.php +++ b/tests/Http/UtilTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/LogTest.php b/tests/LogTest.php index 8e7362f51..d56299f1f 100644 --- a/tests/LogTest.php +++ b/tests/LogTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/LogWriterTest.php b/tests/LogWriterTest.php index 3c30d8039..839f09f6e 100644 --- a/tests/LogWriterTest.php +++ b/tests/LogWriterTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/Middleware/ContentTypesTest.php b/tests/Middleware/ContentTypesTest.php index 3857b3428..6f9fcfe87 100644 --- a/tests/Middleware/ContentTypesTest.php +++ b/tests/Middleware/ContentTypesTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/Middleware/FlashTest.php b/tests/Middleware/FlashTest.php index d0c85aedc..7b8f7961d 100644 --- a/tests/Middleware/FlashTest.php +++ b/tests/Middleware/FlashTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/Middleware/MethodOverrideTest.php b/tests/Middleware/MethodOverrideTest.php index 9187bd799..c2c5857f3 100644 --- a/tests/Middleware/MethodOverrideTest.php +++ b/tests/Middleware/MethodOverrideTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/Middleware/PrettyExceptionsTest.php b/tests/Middleware/PrettyExceptionsTest.php index 4f41b2d52..28dabcc2f 100644 --- a/tests/Middleware/PrettyExceptionsTest.php +++ b/tests/Middleware/PrettyExceptionsTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/Middleware/SessionCookieTest.php b/tests/Middleware/SessionCookieTest.php index 2364cad63..472c01c01 100644 --- a/tests/Middleware/SessionCookieTest.php +++ b/tests/Middleware/SessionCookieTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/MiddlewareTest.php b/tests/MiddlewareTest.php index ac6a19d44..a95036a12 100644 --- a/tests/MiddlewareTest.php +++ b/tests/MiddlewareTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/RouteTest.php b/tests/RouteTest.php index 3f7fb8dbe..dec3f9efa 100644 --- a/tests/RouteTest.php +++ b/tests/RouteTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/RouterTest.php b/tests/RouterTest.php index 318830c5e..4f61c4b33 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/SlimTest.php b/tests/SlimTest.php index 7e996fc84..296d78cce 100644 --- a/tests/SlimTest.php +++ b/tests/SlimTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * diff --git a/tests/ViewTest.php b/tests/ViewTest.php index 8749c500c..2f1b1e9e3 100644 --- a/tests/ViewTest.php +++ b/tests/ViewTest.php @@ -6,7 +6,7 @@ * @copyright 2011 Josh Lockhart * @link http://www.slimframework.com * @license http://www.slimframework.com/license - * @version 2.2.0 + * @version 2.3.0 * * MIT LICENSE * From 35a623fc2d3768c7eff6ad6f286436ff9ee550b7 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 5 Jul 2013 18:04:35 -0400 Subject: [PATCH 60/66] Unit test Set::clear --- tests/Helper/SetTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/Helper/SetTest.php b/tests/Helper/SetTest.php index a2994b09d..3fc905d13 100644 --- a/tests/Helper/SetTest.php +++ b/tests/Helper/SetTest.php @@ -103,6 +103,17 @@ public function testRemove() $this->assertEquals(array('abc' => '123'), $this->property->getValue($this->bag)); } + public function testClear() + { + $data = array( + 'abc' => '123', + 'foo' => 'bar' + ); + $this->property->setValue($this->bag, $data); + $this->bag->clear(); + $this->assertEquals(array(), $this->property->getValue($this->bag)); + } + public function testArrayAccessGet() { $data = array( From cdcf492173be914051275f9fd756c12a870c6d87 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 7 Jul 2013 20:38:06 -0400 Subject: [PATCH 61/66] Update inline documentation --- Slim/Helper/Set.php | 2 +- Slim/Http/Headers.php | 2 +- Slim/Http/Request.php | 6 ++---- Slim/Http/Response.php | 8 ++++---- Slim/Http/Util.php | 6 +++--- Slim/Log.php | 33 +++++++++++++++++---------------- Slim/LogWriter.php | 2 +- Slim/Middleware.php | 8 ++++---- Slim/Route.php | 4 ++-- Slim/Router.php | 10 +++++----- Slim/Slim.php | 6 +++--- Slim/View.php | 8 ++++---- 12 files changed, 47 insertions(+), 48 deletions(-) diff --git a/Slim/Helper/Set.php b/Slim/Helper/Set.php index e1034b0db..280bffd65 100644 --- a/Slim/Helper/Set.php +++ b/Slim/Helper/Set.php @@ -42,7 +42,7 @@ class Set implements \ArrayAccess, \Countable, \IteratorAggregate /** * Constructor - * @param array $items Prepopulate set with this key-value array + * @param array $items Pre-populate set with this key-value array */ public function __construct($items = array()) { diff --git a/Slim/Http/Headers.php b/Slim/Http/Headers.php index 1ec605d0b..5e0283e6e 100644 --- a/Slim/Http/Headers.php +++ b/Slim/Http/Headers.php @@ -88,7 +88,7 @@ public static function extract($data) /** * Transform header name into canonical form - * @param string $name + * @param string $key * @return string */ protected function normalizeKey($key) diff --git a/Slim/Http/Request.php b/Slim/Http/Request.php index 51c3ccf04..b7d32f985 100644 --- a/Slim/Http/Request.php +++ b/Slim/Http/Request.php @@ -78,10 +78,8 @@ class Request /** * Constructor - * @param array $env - * @see \Slim\Environment + * @param \Slim\Environment $env */ - public function __construct(\Slim\Environment $env) { $this->env = $env; @@ -337,7 +335,7 @@ public function cookies($key = null) } /** - * Does the Request body contain parseable form data? + * Does the Request body contain parsed form data? * @return bool */ public function isFormData() diff --git a/Slim/Http/Response.php b/Slim/Http/Response.php index 2308465ad..1d3b729d5 100644 --- a/Slim/Http/Response.php +++ b/Slim/Http/Response.php @@ -128,7 +128,7 @@ class 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 \Slim\Http\Headers|array $headers The HTTP response headers */ public function __construct($body = '', $status = 200, $headers = array()) { @@ -223,7 +223,7 @@ public function body($body = null) * 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 + * @return string The updated HTTP response body */ public function write($body, $replace = false) { @@ -317,8 +317,8 @@ public function setCookie($name, $value) * 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 $settings Properties for cookie including: value, expire, path, domain, secure, httponly */ public function deleteCookie($name, $settings = array()) { diff --git a/Slim/Http/Util.php b/Slim/Http/Util.php index 992e7cb3b..b3a1f12d5 100644 --- a/Slim/Http/Util.php +++ b/Slim/Http/Util.php @@ -209,7 +209,7 @@ public static function serializeCookies(\Slim\Http\Headers &$headers, \Slim\Http * 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 string $value The insecure 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 @@ -247,7 +247,7 @@ public static function encodeSecureCookie($value, $expires, $secret, $algorithm, * @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 - * @return false|string + * @return bool|string */ public static function decodeSecureCookie($value, $secret, $algorithm, $mode) { @@ -416,7 +416,7 @@ public static function parseCookieHeader($header) * * @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 + * @return string Hash */ private static function getIv($expires, $secret) { diff --git a/Slim/Log.php b/Slim/Log.php index aeb852164..1c679d07b 100644 --- a/Slim/Log.php +++ b/Slim/Log.php @@ -185,7 +185,7 @@ public function isEnabled() * Log debug message * @param mixed $object * @param array $context - * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + * @return mixed|bool What the Logger returns, or false if Logger not set or not enabled */ public function debug($object, $context = array()) { @@ -196,7 +196,7 @@ public function debug($object, $context = array()) * Log info message * @param mixed $object * @param array $context - * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + * @return mixed|bool What the Logger returns, or false if Logger not set or not enabled */ public function info($object, $context = array()) { @@ -207,7 +207,7 @@ public function info($object, $context = array()) * Log notice message * @param mixed $object * @param array $context - * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + * @return mixed|bool What the Logger returns, or false if Logger not set or not enabled */ public function notice($object, $context = array()) { @@ -218,7 +218,7 @@ public function notice($object, $context = array()) * Log warning message * @param mixed $object * @param array $context - * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + * @return mixed|bool What the Logger returns, or false if Logger not set or not enabled */ public function warning($object, $context = array()) { @@ -230,7 +230,7 @@ public function warning($object, $context = array()) * Log warning message * @param mixed $object * @param array $context - * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + * @return mixed|bool What the Logger returns, or false if Logger not set or not enabled */ public function warn($object, $context = array()) { @@ -241,7 +241,7 @@ public function warn($object, $context = array()) * Log error message * @param mixed $object * @param array $context - * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + * @return mixed|bool What the Logger returns, or false if Logger not set or not enabled */ public function error($object, $context = array()) { @@ -252,7 +252,7 @@ public function error($object, $context = array()) * Log critical message * @param mixed $object * @param array $context - * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + * @return mixed|bool What the Logger returns, or false if Logger not set or not enabled */ public function critical($object, $context = array()) { @@ -264,7 +264,7 @@ public function critical($object, $context = array()) * Log fatal message * @param mixed $object * @param array $context - * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + * @return mixed|bool What the Logger returns, or false if Logger not set or not enabled */ public function fatal($object, $context = array()) { @@ -275,7 +275,7 @@ public function fatal($object, $context = array()) * Log alert message * @param mixed $object * @param array $context - * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + * @return mixed|bool What the Logger returns, or false if Logger not set or not enabled */ public function alert($object, $context = array()) { @@ -286,7 +286,7 @@ public function alert($object, $context = array()) * Log emergency message * @param mixed $object * @param array $context - * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + * @return mixed|bool What the Logger returns, or false if Logger not set or not enabled */ public function emergency($object, $context = array()) { @@ -298,7 +298,8 @@ public function emergency($object, $context = array()) * @param mixed $level * @param mixed $object * @param array $context - * @return mixed|false What the Logger returns, or false if Logger not set or not enabled + * @return mixed|bool What the Logger returns, or false if Logger not set or not enabled + * @throws \InvalidArgumentException If invalid log level */ public function log($level, $object, $context = array()) { @@ -322,9 +323,9 @@ public function log($level, $object, $context = array()) /** * DEPRECATED for function log * Log message - * @param mixed The object to log - * @param int The message level - * @return int|false + * @param mixed $object The object to log + * @param int $level The message level + * @return int|bool */ protected function write($object, $level) { @@ -333,8 +334,8 @@ protected function write($object, $level) /** * Interpolate log message - * @param mixed The log message - * @param array An array of placeholder values + * @param mixed $message The log message + * @param array $context An array of placeholder values * @return string The processed string */ protected function interpolate($message, $context = array()) diff --git a/Slim/LogWriter.php b/Slim/LogWriter.php index 77b255fe7..47c1f4e40 100644 --- a/Slim/LogWriter.php +++ b/Slim/LogWriter.php @@ -66,7 +66,7 @@ public function __construct($resource) * Write message * @param mixed $message * @param int $level - * @return int|false + * @return int|bool */ public function write($message, $level = null) { diff --git a/Slim/Middleware.php b/Slim/Middleware.php index 39bcb4bee..09ff17767 100644 --- a/Slim/Middleware.php +++ b/Slim/Middleware.php @@ -42,7 +42,7 @@ abstract class Middleware { /** - * @var \Slim Reference to the primary application instance + * @var \Slim\Slim Reference to the primary application instance */ protected $app; @@ -57,7 +57,7 @@ abstract class Middleware * This method injects the primary Slim application instance into * this middleware. * - * @param \Slim $application + * @param \Slim\Slim $application */ final public function setApplication($application) { @@ -70,7 +70,7 @@ final public function setApplication($application) * This method retrieves the application previously injected * into this middleware. * - * @return \Slim + * @return \Slim\Slim */ final public function getApplication() { @@ -97,7 +97,7 @@ final public function setNextMiddleware($nextMiddleware) * This method retrieves the next downstream middleware * previously injected into this middleware. * - * @return \Slim|\Slim\Middleware + * @return \Slim\Slim|\Slim\Middleware */ final public function getNextMiddleware() { diff --git a/Slim/Route.php b/Slim/Route.php index b416511c7..d1f7fbf77 100644 --- a/Slim/Route.php +++ b/Slim/Route.php @@ -376,8 +376,8 @@ public function matches($resourceUri) /** * Convert a URL parameter (e.g. ":id", ":id+") into a regular expression - * @param array URL parameters - * @return string Regular expression for URL parameter + * @param array $m URL parameters + * @return string Regular expression for URL parameter */ protected function matchesCallback($m) { diff --git a/Slim/Router.php b/Slim/Router.php index 3b7d15e52..7928a15d9 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -143,7 +143,7 @@ public function map(\Slim\Route $route) } /** - * A helper function for proccesing the group's pattern and middleware + * A helper function for processing the group's pattern and middleware * @return array Returns an array with the elements: pattern, middlewareArr */ protected function processGroups() @@ -181,9 +181,9 @@ public function popGroup() /** * Get URL for named route * @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 + * @param array $params 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()) { @@ -204,7 +204,7 @@ public function urlFor($name, $params = array()) * 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 + * @throws \RuntimeException If a named route already exists with the same name */ public function addNamedRoute($name, \Slim\Route $route) { diff --git a/Slim/Slim.php b/Slim/Slim.php index 2510de88b..9e1b72bfa 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -234,7 +234,7 @@ public function __set($name, $value) /** * Get application instance by name * @param string $name The name of the Slim application - * @return \Slim|null + * @return \Slim\Slim|null */ public static function getInstance($name = 'default') { @@ -917,7 +917,7 @@ public function setEncryptedCookie($name, $value, $expires = null, $path = null, * * @param string $name * @param bool $deleteIfInvalid - * @return string|false + * @return string|bool */ public function getEncryptedCookie($name, $deleteIfInvalid = true) { @@ -1333,7 +1333,7 @@ public function call() * @param string $errstr The error message * @param string $errfile The absolute path to the affected file * @param int $errline The line number of the error in the affected file - * @return true + * @return bool * @throws \ErrorException */ public static function handleErrors($errno, $errstr = '', $errfile = '', $errline = '') diff --git a/Slim/View.php b/Slim/View.php index dc0ff8730..48f2dcb2d 100644 --- a/Slim/View.php +++ b/Slim/View.php @@ -230,7 +230,7 @@ public function display($template) /** * Return the contents of a rendered template file * @var string $template The template pathname, relative to the template base directory - * @return The rendered template + * @return string The rendered template */ public function fetch($template) { @@ -242,9 +242,9 @@ public function fetch($template) * * NOTE: This method should be overridden by custom view subclasses * - * @var string $template The template pathname, relative to the template base directory - * @return The rendered template - * @throws \RuntimeException If resolved template pathname is not a valid file + * @var string $template The template pathname, relative to the template base directory + * @return string The rendered template + * @throws \RuntimeException If resolved template pathname is not a valid file */ protected function render($template) { From 9212ea5d13909d8169c600eb526e9857466e37d1 Mon Sep 17 00:00:00 2001 From: = Date: Sun, 7 Jul 2013 21:04:49 -0400 Subject: [PATCH 62/66] Tweak Set::clear unit test --- Slim/Helper/Set.php | 1 - tests/Helper/SetTest.php | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Slim/Helper/Set.php b/Slim/Helper/Set.php index 280bffd65..8a8a56a03 100644 --- a/Slim/Helper/Set.php +++ b/Slim/Helper/Set.php @@ -144,7 +144,6 @@ public function remove($key) */ public function clear() { - unset($this->data); $this->data = array(); } diff --git a/tests/Helper/SetTest.php b/tests/Helper/SetTest.php index 3fc905d13..674e2b167 100644 --- a/tests/Helper/SetTest.php +++ b/tests/Helper/SetTest.php @@ -32,6 +32,9 @@ class SetTest extends PHPUnit_Framework_TestCase { + protected $bag; + protected $property; + public function setUp() { $this->bag = new \Slim\Helper\Set(); From 12cc604a11e85b9b489be3ea0e4befea2e2089d2 Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Mon, 8 Jul 2013 14:53:06 +0300 Subject: [PATCH 63/66] Cleaned up Route code --- Slim/Router.php | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/Slim/Router.php b/Slim/Router.php index 6e26dee54..000fded76 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -126,19 +126,13 @@ public function getMatchedRoutes($httpMethod, $resourceUri, $reload = false) public function map(\Slim\Route $route) { list($groupPattern, $groupMiddleware) = $this->processGroups(); - if (count($this->routeGroups) > 0) { - $route->setPattern($groupPattern . $route->getPattern()); - } + + $route->setPattern($groupPattern . $route->getPattern()); $this->routes[] = $route; - if (count($this->routeGroups) > 0) { - foreach ($groupMiddleware as $middlewareArr) { - if (is_array($middlewareArr)) { - foreach ($middlewareArr as $middleware) { - $route->setMiddleware($middleware); - } - } - } + + foreach ($groupMiddleware as $middleware) { + $route->setMiddleware($middleware); } } @@ -153,7 +147,9 @@ protected function processGroups() foreach ($this->routeGroups as $group) { $k = key($group); $pattern .= $k; - array_push($middleware, $group[$k]); + if (is_array($group[$k])) { + $middleware = array_merge($middleware, $group[$k]); + } } return array($pattern, $middleware); } @@ -164,9 +160,9 @@ protected function processGroups() * @param array|null $middleware Optional parameter array of middleware * @return int The index of the new group */ - public function pushGroup($group, $middleware = null) + public function pushGroup($group, $middleware = array()) { - return array_push($this->routeGroups, array($group => $middleware)); + return array_push($this->routeGroups, array($pattern . $group => $m)); } /** From 7a7d3a1ad4fa999852570f305cb937d8b4392353 Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Mon, 8 Jul 2013 14:56:59 +0300 Subject: [PATCH 64/66] Small Fix --- Slim/Router.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Slim/Router.php b/Slim/Router.php index 000fded76..360bb3f08 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -162,7 +162,7 @@ protected function processGroups() */ public function pushGroup($group, $middleware = array()) { - return array_push($this->routeGroups, array($pattern . $group => $m)); + return array_push($this->routeGroups, array($group => $m)); } /** From 5d183743f6689edc62fc31bd26aa605dfea356ab Mon Sep 17 00:00:00 2001 From: Gabriel Manricks Date: Mon, 8 Jul 2013 14:58:15 +0300 Subject: [PATCH 65/66] Small Fix --- Slim/Router.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Slim/Router.php b/Slim/Router.php index 360bb3f08..0aa842a45 100644 --- a/Slim/Router.php +++ b/Slim/Router.php @@ -162,7 +162,7 @@ protected function processGroups() */ public function pushGroup($group, $middleware = array()) { - return array_push($this->routeGroups, array($group => $m)); + return array_push($this->routeGroups, array($group => $middleware)); } /** From b7f45dfebb89d5fac6bb35db028cbd4dff234522 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 8 Jul 2013 13:35:46 -0400 Subject: [PATCH 66/66] Update Slim's custom error handler --- Slim/Slim.php | 6 +++--- tests/SlimTest.php | 10 ++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Slim/Slim.php b/Slim/Slim.php index 9e1b72bfa..86f7537cd 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -1338,11 +1338,11 @@ public function call() */ public static function handleErrors($errno, $errstr = '', $errfile = '', $errline = '') { - if (error_reporting() & $errno) { - throw new \ErrorException($errstr, $errno, 0, $errfile, $errline); + if (!($errno & error_reporting())) { + return; } - return true; + throw new \ErrorException($errstr, $errno, 0, $errfile, $errline); } /** diff --git a/tests/SlimTest.php b/tests/SlimTest.php index 296d78cce..638da7c84 100644 --- a/tests/SlimTest.php +++ b/tests/SlimTest.php @@ -1339,22 +1339,20 @@ public function testHandleErrors() { $defaultErrorReporting = error_reporting(); - // Assert Slim ignores E_NOTICE errors + // Test 1 error_reporting(E_ALL ^ E_NOTICE); // <-- Report all errors EXCEPT notices try { - $this->assertTrue(\Slim\Slim::handleErrors(E_NOTICE, 'test error', 'Slim.php', 119)); + \Slim\Slim::handleErrors(E_NOTICE, 'test error', 'Slim.php', 119); } catch (\ErrorException $e) { $this->fail('Slim::handleErrors reported a disabled error level.'); } - // Assert Slim reports E_STRICT errors + // Test 2 error_reporting(E_ALL | E_STRICT); // <-- Report all errors, including E_STRICT try { \Slim\Slim::handleErrors(E_STRICT, 'test error', 'Slim.php', 119); $this->fail('Slim::handleErrors didn\'t report a enabled error level'); - } catch (\ErrorException $e) { - $this->assertEquals('test error', $e->getMessage()); - } + } catch (\ErrorException $e) {} error_reporting($defaultErrorReporting); }