Skip to content

Commit

Permalink
Merge branch 'release-2.3.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
= committed Jul 20, 2013
2 parents 926865b + 8af44d9 commit 2e540cc
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 12 deletions.
20 changes: 20 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# How to Contribute

## Pull Requests

1. Fork the Slim Framework repository
2. Create a new branch for each feature or improvement
3. Send a pull request from each feature branch to the **develop** branch

It is very important to separate new features or improvements into separate feature branches, and to send a
pull request for each branch. This allows me to review and pull in new features or improvements individually.

## Style Guide

All pull requests must adhere to the [PSR-2 standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md).

## Unit Testing

All pull requests must be accompanied by passing unit tests and complete code coverage. The Slim Framework uses phpunit for testing.

[Learn about PHPUnit](https://github.com/sebastianbergmann/phpunit/)
3 changes: 2 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Thank you for choosing the Slim Framework for your next project. I think you're
* Route parameters with wildcards and conditions
* Route redirect, halt, and pass
* Route middleware
* Resource Locator and DI container
* Template rendering with custom views
* Flash messages
* Secure cookies with AES-256 encryption
Expand All @@ -29,7 +30,7 @@ Thank you for choosing the Slim Framework for your next project. I think you're

You may install the Slim Framework with Composer (recommended) or manually.

[Read how to install Slim](http://docs.slimframework.com/pages/getting-started-install)
[Read how to install Slim](http://docs.slimframework.com/#Installation)

### System Requirements

Expand Down
12 changes: 12 additions & 0 deletions Slim/Helper/Set.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,4 +207,16 @@ public function singleton($key, $value)
return $object;
});
}

/**
* Protect closure from being directly invoked
* @param Closure $callable A closure to keep from being invoked and evaluated
* @return Closure
*/
public function protect(\Closure $callable)
{
return function () use ($callable) {
return $callable;
};
}
}
8 changes: 7 additions & 1 deletion Slim/Http/Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,15 @@ public static function serializeCookies(\Slim\Http\Headers &$headers, \Slim\Http
{
if ($config['cookies.encrypt']) {
foreach ($cookies as $name => $settings) {
if (is_string($settings['expires'])) {
$expires = strtotime($settings['expires']);
} else {
$expires = (int) $settings['expires'];
}

$settings['value'] = static::encodeSecureCookie(
$settings['value'],
$settings['expires'],
$expires,
$config['cookies.secret_key'],
$config['cookies.cipher'],
$config['cookies.cipher_mode']
Expand Down
17 changes: 14 additions & 3 deletions Slim/Slim.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,14 @@ public function __set($name, $value)
{
$this->container[$name] = $value;
}

public function __isset($name){
return isset($this->container[$name]);
}

public function __unset($name){
unset($this->container[$name]);
}

/**
* Get application instance by name
Expand Down Expand Up @@ -754,7 +762,7 @@ public function render($template, $data = array(), $status = null)
public function lastModified($time)
{
if (is_integer($time)) {
$this->response->headers->set('Last-Modified', date(DATE_RFC1123, $time));
$this->response->headers->set('Last-Modified', gmdate('D, d M Y H:i:s T', $time));
if ($time === strtotime($this->request->headers->get('IF_MODIFIED_SINCE'))) {
$this->halt(304);
}
Expand Down Expand Up @@ -820,7 +828,7 @@ public function expires($time)
if (is_string($time)) {
$time = strtotime($time);
}
$this->response->headers->set('Expires', gmdate(DATE_RFC1123, $time));
$this->response->headers->set('Expires', gmdate('D, d M Y H:i:s T', $time));
}

/********************************************************************************
Expand Down Expand Up @@ -1234,7 +1242,10 @@ public function run()
set_error_handler(array('\Slim\Slim', 'handleErrors'));

//Apply final outer middleware layers
$this->add(new \Slim\Middleware\PrettyExceptions());
if($this->config('debug')){
//Apply pretty exceptions only in debug to avoid accidental information leakage in production
$this->add(new \Slim\Middleware\PrettyExceptions());
}

//Invoke middleware and application stack
$this->middleware[0]->call();
Expand Down
17 changes: 16 additions & 1 deletion Slim/View.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ public function set($key, $value)
$this->data->set($key, $value);
}

/**
* Set view data value as Closure with key
* @param string $key
* @param mixed $value
*/
public function keep($key, Closure $value)
{
$this->data->keep($key, $value);
}

/**
* Return view data
* @return array
Expand Down Expand Up @@ -158,7 +168,12 @@ public function setData()
if (count($args) === 1 && is_array($args[0])) {
$this->data->replace($args[0]);
} elseif (count($args) === 2) {
$this->data->set($args[0], $args[1]);
// Ensure original behavior is maintained. DO NOT invoke stored Closures.
if (is_object($args[1]) && method_exists($args[1], '__invoke')) {
$this->data->set($args[0], $this->data->protect($args[1]));
} else {
$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, ... ]);`');
}
Expand Down
11 changes: 11 additions & 0 deletions tests/Helper/SetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,15 @@ public function testGetIterator()
$this->property->setValue($this->bag, $data);
$this->assertInstanceOf('\ArrayIterator', $this->bag->getIterator());
}

public function testProtect()
{
$callable = function () {
return 'foo';
};
$result = $this->bag->protect($callable);

$this->assertInstanceOf('\Closure', $result);
$this->assertSame($callable, $result());
}
}
44 changes: 44 additions & 0 deletions tests/Http/UtilTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,50 @@ public function testSetCookieHeaderWithNameAndValueAndDomainAndPathAndExpiresAnd
$this->assertEquals('foo=bar; domain=foo.com; path=/foo; expires=' . $expiresFormat . '; secure; HttpOnly', $header['Set-Cookie']);
}

/**
* Test serializeCookies and decrypt with string expires
*
* In this test a cookie with a string typed value for 'expires' is set,
* which should be parsed by `strtotime` to a timestamp when it's added to
* the headers; this timestamp should then be correctly parsed, and the
* value correctly decrypted, by `decodeSecureCookie`.
*/
public function testSerializeCookiesAndDecryptWithStringExpires()
{
$value = 'bar';

$headers = new \Slim\Http\Headers();

$settings = array(
'cookies.encrypt' => true,
'cookies.secret_key' => 'secret',
'cookies.cipher' => MCRYPT_RIJNDAEL_256,
'cookies.cipher_mode' => MCRYPT_MODE_CBC
);

$cookies = new \Slim\Http\Cookies();
$cookies->set('foo', array(
'value' => $value,
'expires' => '1 hour'
));

\Slim\Http\Util::serializeCookies($headers, $cookies, $settings);

$encrypted = $headers->get('Set-Cookie');
$encrypted = strstr($encrypted, ';', true);
$encrypted = urldecode(substr(strstr($encrypted, '='), 1));

$decrypted = \Slim\Http\Util::decodeSecureCookie(
$encrypted,
$settings['cookies.secret_key'],
$settings['cookies.cipher'],
$settings['cookies.cipher_mode']
);

$this->assertEquals($value, $decrypted);
$this->assertTrue($value !== $encrypted);
}

public function testDeleteCookieHeaderWithSurvivingCookie()
{
$header = array('Set-Cookie' => "foo=bar\none=two");
Expand Down
31 changes: 25 additions & 6 deletions tests/SlimTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ public function testLastModifiedMatch()
'REQUEST_METHOD' => 'GET',
'SCRIPT_NAME' => '/foo', //<-- Physical
'PATH_INFO' => '/bar', //<-- Virtual
'HTTP_IF_MODIFIED_SINCE' => 'Sun, 03 Oct 2010 17:00:52 -0400',
'HTTP_IF_MODIFIED_SINCE' => 'Sun, 03 Oct 2010 21:00:52 GMT',
));
$s = new \Slim\Slim();
$s->get('/bar', function () use ($s) {
Expand All @@ -629,7 +629,7 @@ public function testLastModifiedDoesNotMatch()
\Slim\Environment::mock(array(
'SCRIPT_NAME' => '/foo', //<-- Physical
'PATH_INFO' => '/bar', //<-- Virtual
'IF_MODIFIED_SINCE' => 'Sun, 03 Oct 2010 17:00:52 -0400',
'IF_MODIFIED_SINCE' => 'Sun, 03 Oct 2010 21:00:52 GMT',
));
$s = new \Slim\Slim();
$s->get('/bar', function () use ($s) {
Expand All @@ -653,6 +653,25 @@ public function testLastModifiedOnlyAcceptsIntegers()
$s->call();
}

/**
* Test Last Modified header format
*/
public function testLastModifiedHeaderFormat()
{
\Slim\Environment::mock(array(
'SCRIPT_NAME' => '/foo', //<-- Physical
'PATH_INFO' => '/bar', //<-- Virtual
));
$s = new \Slim\Slim();
$s->get('/bar', function () use ($s) {
$s->lastModified(1286139652);
});
$s->call();
list($status, $header, $body) = $s->response()->finalize();
$this->assertTrue(isset($header['Last-Modified']));
$this->assertEquals('Sun, 03 Oct 2010 21:00:52 GMT', $header['Last-Modified']);
}

/**
* Test ETag matches
*/
Expand Down Expand Up @@ -716,15 +735,15 @@ public function testExpiresAsString()
'SCRIPT_NAME' => '/foo', //<-- Physical
'PATH_INFO' => '/bar', //<-- Virtual
));
$expectedDate = gmdate('D, d M Y', strtotime('5 days')); //Just the day, month, and year
$expectedDate = gmdate('D, d M Y H:i:s T', strtotime('5 days'));
$s = new \Slim\Slim();
$s->get('/bar', function () use ($s) {
$s->expires('5 days');
});
$s->call();
list($status, $header, $body) = $s->response()->finalize();
$this->assertTrue(isset($header['Expires']));
$this->assertEquals(0, strpos($header['Expires'], $expectedDate));
$this->assertEquals($header['Expires'], $expectedDate);
}

/**
Expand All @@ -737,15 +756,15 @@ public function testExpiresAsInteger()
'PATH_INFO' => '/bar', //<-- Virtual
));
$fiveDaysFromNow = time() + (60 * 60 * 24 * 5);
$expectedDate = gmdate('D, d M Y', $fiveDaysFromNow); //Just the day, month, and year
$expectedDate = gmdate('D, d M Y H:i:s T', $fiveDaysFromNow);
$s = new \Slim\Slim();
$s->get('/bar', function () use ($s, $fiveDaysFromNow) {
$s->expires($fiveDaysFromNow);
});
$s->call();
list($status, $header, $body) = $s->response()->finalize();
$this->assertTrue(isset($header['Expires']));
$this->assertEquals(0, strpos($header['Expires'], $expectedDate));
$this->assertEquals($header['Expires'], $expectedDate);
}

/************************************************
Expand Down
15 changes: 15 additions & 0 deletions tests/ViewTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ public function testSetDataKeyValue()
$this->assertEquals(array('foo' => 'bar'), $prop->getValue($view)->all());
}

public function testSetDataKeyValueAsClosure()
{
$view = new \Slim\View();
$prop = new \ReflectionProperty($view, 'data');
$prop->setAccessible(true);

$view->setData('fooClosure', function () {
return 'foo';
});

$value = $prop->getValue($view)->get('fooClosure');
$this->assertInstanceOf('Closure', $value);
$this->assertEquals('foo', $value());
}

public function testSetDataArray()
{
$view = new \Slim\View();
Expand Down

0 comments on commit 2e540cc

Please sign in to comment.