diff --git a/.travis.yml b/.travis.yml index e5f88b347..ac79ecea1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,5 +3,6 @@ language: php php: - 5.3 - 5.4 + - 5.5 script: phpunit --coverage-text diff --git a/Slim/Http/Request.php b/Slim/Http/Request.php index ff8703f06..49f1ffb6f 100644 --- a/Slim/Http/Request.php +++ b/Slim/Http/Request.php @@ -209,9 +209,10 @@ public function params($key = null) * the value of the array key if requested; if the array key does not exist, NULL is returned. * * @param string $key + * @param mixed $default Default return value when key does not exist * @return array|mixed|null */ - public function get($key = null) + public function get($key = null, $default = null) { if (!isset($this->env['slim.request.query_hash'])) { $output = array(); @@ -226,7 +227,7 @@ public function get($key = null) if (isset($this->env['slim.request.query_hash'][$key])) { return $this->env['slim.request.query_hash'][$key]; } else { - return null; + return $default; } } else { return $this->env['slim.request.query_hash']; @@ -240,10 +241,11 @@ public function get($key = null) * the value of a hash key if requested; if the array key does not exist, NULL is returned. * * @param string $key + * @param mixed $default Default return value when key does not exist * @return array|mixed|null * @throws \RuntimeException If environment input is not available */ - public function post($key = null) + public function post($key = null, $default = null) { if (!isset($this->env['slim.input'])) { throw new \RuntimeException('Missing slim.input in environment variables'); @@ -266,7 +268,7 @@ public function post($key = null) if (isset($this->env['slim.request.form_hash'][$key])) { return $this->env['slim.request.form_hash'][$key]; } else { - return null; + return $default; } } else { return $this->env['slim.request.form_hash']; @@ -276,31 +278,34 @@ public function post($key = null) /** * Fetch PUT data (alias for \Slim\Http\Request::post) * @param string $key + * @param mixed $default Default return value when key does not exist * @return array|mixed|null */ - public function put($key = null) + public function put($key = null, $default = null) { - return $this->post($key); + return $this->post($key, $default); } /** * Fetch PATCH data (alias for \Slim\Http\Request::post) * @param string $key + * @param mixed $default Default return value when key does not exist * @return array|mixed|null */ - public function patch($key = null) + public function patch($key = null, $default = null) { - return $this->post($key); + return $this->post($key, $default); } /** * Fetch DELETE data (alias for \Slim\Http\Request::post) * @param string $key + * @param mixed $default Default return value when key does not exist * @return array|mixed|null */ - public function delete($key = null) + public function delete($key = null, $default = null) { - return $this->post($key); + return $this->post($key, $default); } /** diff --git a/Slim/Http/Response.php b/Slim/Http/Response.php index c7a6499ed..0e5b8bb21 100644 --- a/Slim/Http/Response.php +++ b/Slim/Http/Response.php @@ -113,6 +113,7 @@ class Response implements \ArrayAccess, \Countable, \IteratorAggregate 415 => '415 Unsupported Media Type', 416 => '416 Requested Range Not Satisfiable', 417 => '417 Expectation Failed', + 418 => '418 I\'m a teapot', 422 => '422 Unprocessable Entity', 423 => '423 Locked', //Server Error 5xx diff --git a/Slim/Route.php b/Slim/Route.php index 113603554..ae87e27f6 100644 --- a/Slim/Route.php +++ b/Slim/Route.php @@ -92,8 +92,8 @@ class Route /** * Constructor - * @param string $pattern The URL pattern (e.g. "/books/:id") - * @param mixed $callable Anything that returns TRUE for is_callable() + * @param string $pattern The URL pattern (e.g. "/books/:id") + * @param mixed $callable Anything that returns TRUE for is_callable() */ public function __construct($pattern, $callable) { @@ -154,6 +154,11 @@ public function getCallable() */ public function setCallable($callable) { + $matches = array(); + if (is_string($callable) && preg_match('!^([^\:]+)\:([[:alnum:]]+)$!', $callable, $matches)) { + $callable = array(new $matches[1], $matches[2]); + } + if (!is_callable($callable)) { throw new \InvalidArgumentException('Route callable must be callable'); } @@ -194,7 +199,7 @@ public function getName() */ public function setName($name) { - $this->name = (string) $name; + $this->name = (string)$name; } /** @@ -217,7 +222,7 @@ public function setParams($params) /** * Get route parameter value - * @param string $index Name of URL parameter + * @param string $index Name of URL parameter * @return string * @throws \InvalidArgumentException If route parameter does not exist at index */ @@ -232,8 +237,8 @@ public function getParam($index) /** * Set route parameter value - * @param string $index Name of URL parameter - * @param mixed $value The new parameter value + * @param string $index Name of URL parameter + * @param mixed $value The new parameter value * @throws \InvalidArgumentException If route parameter does not exist at index */ public function setParam($index, $value) @@ -351,7 +356,7 @@ public function matches($resourceUri) $patternAsRegex = preg_replace_callback( '#:([\w]+)\+?#', array($this, 'matchesCallback'), - str_replace(')', ')?', (string) $this->pattern) + str_replace(')', ')?', (string)$this->pattern) ); if (substr($this->pattern, -1) === '/') { $patternAsRegex .= '?'; @@ -363,7 +368,7 @@ public function matches($resourceUri) } foreach ($this->paramNames as $name) { if (isset($paramValues[$name])) { - if (isset($this->paramNamesPath[ $name ])) { + if (isset($this->paramNamesPath[$name])) { $this->params[$name] = explode('/', urldecode($paramValues[$name])); } else { $this->params[$name] = urldecode($paramValues[$name]); @@ -376,17 +381,17 @@ public function matches($resourceUri) /** * Convert a URL parameter (e.g. ":id", ":id+") into a regular expression - * @param array $m URL parameters + * @param array $m URL parameters * @return string Regular expression for URL parameter */ protected function matchesCallback($m) { $this->paramNames[] = $m[1]; - if (isset($this->conditions[ $m[1] ])) { - return '(?P<' . $m[1] . '>' . $this->conditions[ $m[1] ] . ')'; + if (isset($this->conditions[$m[1]])) { + return '(?P<' . $m[1] . '>' . $this->conditions[$m[1]] . ')'; } if (substr($m[0], -1) === '+') { - $this->paramNamesPath[ $m[1] ] = 1; + $this->paramNamesPath[$m[1]] = 1; return '(?P<' . $m[1] . '>.+)'; } @@ -396,7 +401,7 @@ protected function matchesCallback($m) /** * Set route name - * @param string $name The name of the route + * @param string $name The name of the route * @return \Slim\Route */ public function name($name) @@ -408,7 +413,7 @@ public function name($name) /** * Merge route conditions - * @param array $conditions Key-value array of URL parameter conditions + * @param array $conditions Key-value array of URL parameter conditions * @return \Slim\Route */ public function conditions(array $conditions) @@ -434,6 +439,6 @@ public function dispatch() } $return = call_user_func_array($this->getCallable(), array_values($this->getParams())); - return ($return === false)? false : true; + return ($return === false) ? false : true; } } diff --git a/Slim/Slim.php b/Slim/Slim.php index 2a68b6c89..058ff30a7 100644 --- a/Slim/Slim.php +++ b/Slim/Slim.php @@ -40,9 +40,14 @@ /** * Slim - * @package Slim - * @author Josh Lockhart - * @since 1.0.0 + * @package Slim + * @author Josh Lockhart + * @since 1.0.0 + * + * @property \Slim\Environment $environment + * @property \Slim\Http\Response $response + * @property \Slim\Http\Request $request + * @property \Slim\Router $router */ class Slim { @@ -172,8 +177,11 @@ public function __construct(array $userSettings = array()) // Default view $this->container->singleton('view', function ($c) { $viewClass = $c['settings']['view']; + $templatesPath = $c['settings']['templates.path']; - return ($viewClass instanceOf \Slim\View) ? $viewClass : new $viewClass; + $view = ($viewClass instanceOf \Slim\View) ? $viewClass : new $viewClass; + $view->setTemplatesDirectory($templatesPath); + return $view; }); // Default log writer @@ -739,7 +747,6 @@ public function render($template, $data = array(), $status = null) if (!is_null($status)) { $this->response->status($status); } - $this->view->setTemplatesDirectory($this->config('templates.path')); $this->view->appendData($data); $this->view->display($template); } diff --git a/Slim/View.php b/Slim/View.php index c6c435a7f..816e886a4 100644 --- a/Slim/View.php +++ b/Slim/View.php @@ -236,20 +236,23 @@ public function getTemplatePathname($file) * This method echoes the rendered template to the current output buffer * * @param string $template Pathname of template file relative to templates directory + * @param array $data Any additonal data to be passed to the template. */ - public function display($template) + public function display($template, $data = null) { - echo $this->fetch($template); + echo $this->fetch($template, $data); } /** * Return the contents of a rendered template file - * @var string $template The template pathname, relative to the template base directory - * @return string The rendered template + * + * @param string $template The template pathname, relative to the template base directory + * @param array $data Any additonal data to be passed to the template. + * @return string The rendered template */ - public function fetch($template) + public function fetch($template, $data = null) { - return $this->render($template); + return $this->render($template, $data); } /** @@ -257,17 +260,20 @@ 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 + * @param string $template The template pathname, relative to the template base directory + * @param array $data Any additonal data to be passed to the template. * @return string The rendered template * @throws \RuntimeException If resolved template pathname is not a valid file */ - protected function render($template) + protected function render($template, $data = null) { $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()); + + $data = array_merge($this->data->all(), (array) $data); + extract($data); ob_start(); require $templatePathname; diff --git a/composer.json b/composer.json index 656de3297..0becfe687 100644 --- a/composer.json +++ b/composer.json @@ -13,8 +13,10 @@ } ], "require": { - "php": ">=5.3.0", - "ext-mcrypt": "*" + "php": ">=5.3.0" + }, + "suggest": { + "ext-mcrypt": "Required for HTTP cookie encryption" }, "autoload": { "psr-0": { "Slim": "." } diff --git a/tests/Http/RequestTest.php b/tests/Http/RequestTest.php index 35af9fc17..befcb2611 100644 --- a/tests/Http/RequestTest.php +++ b/tests/Http/RequestTest.php @@ -221,6 +221,7 @@ public function testGet() $this->assertEquals(3, count($req->get())); $this->assertEquals('1', $req->get('one')); $this->assertNull($req->get('foo')); + $this->assertFalse($req->get('foo', false)); } /** @@ -236,6 +237,7 @@ public function testGetWithoutMultibyte() $this->assertEquals(3, count($req->get())); $this->assertEquals('1', $req->get('one')); $this->assertNull($req->get('foo')); + $this->assertFalse($req->get('foo', false)); } /** @@ -253,6 +255,7 @@ public function testPost() $this->assertEquals(2, count($req->post())); $this->assertEquals('bar', $req->post('foo')); $this->assertNull($req->post('xyz')); + $this->assertFalse($req->post('xyz', false)); } /** @@ -271,6 +274,7 @@ public function testPostWithoutMultibyte() $this->assertEquals(2, count($req->post())); $this->assertEquals('bar', $req->post('foo')); $this->assertNull($req->post('xyz')); + $this->assertFalse($req->post('xyz', false)); } /** @@ -319,6 +323,7 @@ public function testPut() $this->assertEquals('bar', $req->put('foo')); $this->assertEquals('bar', $req->params('foo')); $this->assertNull($req->put('xyz')); + $this->assertFalse($req->put('xyz', false)); } /** @@ -337,6 +342,7 @@ public function testPatch() $this->assertEquals('bar', $req->patch('foo')); $this->assertEquals('bar', $req->params('foo')); $this->assertNull($req->patch('xyz')); + $this->assertFalse($req->patch('xyz', false)); } /** @@ -355,6 +361,7 @@ public function testDelete() $this->assertEquals('bar', $req->delete('foo')); $this->assertEquals('bar', $req->params('foo')); $this->assertNull($req->delete('xyz')); + $this->assertFalse($req->delete('xyz', false)); } /** diff --git a/tests/Middleware/SessionCookieTest.php b/tests/Middleware/SessionCookieTest.php index 36ae16e22..c1e725d91 100644 --- a/tests/Middleware/SessionCookieTest.php +++ b/tests/Middleware/SessionCookieTest.php @@ -135,6 +135,7 @@ public function testUnserializeErrorsAreCaughtAndLogged() $logWriter->expects($this->once()) ->method('write'); + $oldLevel = error_reporting(E_ALL); $app = new \Slim\Slim(array( 'log.writer' => $logWriter @@ -149,6 +150,8 @@ public function testUnserializeErrorsAreCaughtAndLogged() $mw->setNextMiddleware($app); $mw->call(); $this->assertEquals(array(), $_SESSION); + + error_reporting($oldLevel); } /** diff --git a/tests/RouteTest.php b/tests/RouteTest.php index 0e67d229e..61e60446d 100644 --- a/tests/RouteTest.php +++ b/tests/RouteTest.php @@ -67,6 +67,23 @@ public function testGetCallable() $this->assertSame($callable, $route->getCallable()); } + public function testGetCallableAsClass() + { + $route = new \Slim\Route('/foo', '\Slim\Router:getCurrentRoute'); + + $callable = $route->getCallable(); + $this->assertInstanceOf('\Slim\Router', $callable[0]); + $this->assertEquals('getCurrentRoute', $callable[1]); + } + + public function testGetCallableAsStaticMethod() + { + $route = new \Slim\Route('/bar', '\Slim\Slim::getInstance'); + + $callable = $route->getCallable(); + $this->assertEquals('\Slim\Slim::getInstance', $callable); + } + public function testSetCallable() { $callable = function () { diff --git a/tests/SlimTest.php b/tests/SlimTest.php index 26f2ec603..1c520dc2b 100644 --- a/tests/SlimTest.php +++ b/tests/SlimTest.php @@ -33,7 +33,7 @@ //Mock custom view class CustomView extends \Slim\View { - public function render($template) { echo "Custom view"; } + public function render($template, $data = null) { echo "Custom view"; } } //Echo Logger @@ -551,6 +551,16 @@ public function testViewDataTransfer() * RENDERING ************************************************/ + /** + * Test template path is passed to view + */ + public function testViewGetsTemplatesPath() + { + $path = dirname(__FILE__) . '/templates'; + $s = new \Slim\Slim(array('templates.path' => $path)); + $this->assertEquals($s->view->getTemplatesDirectory(), $path); + } + /** * Test render with template and data */ diff --git a/tests/ViewTest.php b/tests/ViewTest.php index 6f9f2c351..e04ce8644 100644 --- a/tests/ViewTest.php +++ b/tests/ViewTest.php @@ -115,6 +115,21 @@ public function testAppendData() $this->assertEquals(array('foo' => 'bar'), $prop->getValue($view)->all()); } + public function testLocalData() + { + $view = new \Slim\View(); + $prop1 = new \ReflectionProperty($view, 'data'); + $prop1->setAccessible(true); + $prop1->setValue($view, new \Slim\Helper\Set(array('foo' => 'bar'))); + + $prop2 = new \ReflectionProperty($view, 'templatesDirectory'); + $prop2->setAccessible(true); + $prop2->setValue($view, dirname(__FILE__) . '/templates'); + + $output = $view->fetch('test.php', array('foo' => 'baz')); + $this->assertEquals('test output baz', $output); + } + public function testAppendDataOverwrite() { $view = new \Slim\View();