diff --git a/laravel/laravel.php b/laravel/laravel.php index a3252b99b..ba54492a0 100644 --- a/laravel/laravel.php +++ b/laravel/laravel.php @@ -51,7 +51,7 @@ if ($config->get('session.driver') !== '') { $session = $container->resolve('laravel.session.manager'); - $container->instance('laravel.session', $session->payload()); + $container->instance('laravel.session', $session->payload($config->get('session'))); } // -------------------------------------------------------------- @@ -78,7 +78,7 @@ $response->content = $response->render(); // -------------------------------------------------------------- if (isset($session)) { - $session->close($container->resolve('laravel.session')); + $session->close($container->resolve('laravel.session'), $config->get('session')); } // -------------------------------------------------------------- diff --git a/laravel/session/manager.php b/laravel/session/manager.php index ad6e63a73..3c1b2db06 100644 --- a/laravel/session/manager.php +++ b/laravel/session/manager.php @@ -21,13 +21,6 @@ class Manager { */ private $transporter; - /** - * The configuration manager instance. - * - * @var Config - */ - private $config; - /** * The session payload instance. * @@ -40,29 +33,28 @@ class Manager { * * @param Driver $driver * @param Transporter $transporter - * @param Config $config * @return void */ - public function __construct(Driver $driver, Transporter $transporter, Config $config) + public function __construct(Driver $driver, Transporter $transporter) { $this->driver = $driver; - $this->config = $config; $this->transporter = $transporter; } /** * Get the session payload for the request. * + * @param array $config * @return Payload */ - public function payload() + public function payload($config) { - $session = $this->driver->load($this->transporter->get()); + $session = $this->driver->load($this->transporter->get($config)); // If the session is expired, a new session will be generated and all of the data from // the previous session will be lost. The new session will be assigned a random, long // string ID to uniquely identify it among the application's current users. - if (is_null($session) or $this->expired($session)) + if (is_null($session) or $this->expired($session, $config)) { $session = array('id' => Str::random(40), 'data' => array()); } @@ -82,24 +74,24 @@ class Manager { * Deteremine if the session is expired based on the last activity timestamp * and the session lifetime set in the configuration file. * - * @param array $payload + * @param array $session + * @param array $config * @return bool */ - private function expired($payload) + private function expired($session, $config) { - return (time() - $payload['last_activity']) > ($this->config->get('session.lifetime') * 60); + return (time() - $session['last_activity']) > ($config['lifetime'] * 60); } /** * Close the session handling for the request. * * @param Payload $payload + * @param array $config * @return void */ - public function close(Payload $payload) + public function close(Payload $payload, $config) { - $config = $this->config->get('session'); - $this->driver->save($payload->age(), $config); $this->transporter->put($payload->session['id'], $config); @@ -107,23 +99,10 @@ class Manager { // Some session drivers implement the Sweeper interface, which specified that the driver // must do its garbage collection manually. Alternatively, some drivers such as APC and // Memcached are not required to manually clean up their sessions. - if (mt_rand(1, $config['sweepage'][1]) <= $config['sweepage'][0] and $this->driver instanceof Sweeper) + if (mt_rand(1, $config['sweepage'][1]) <= $config['sweepage'][0] and $this->driver instanceof Drivers\Sweeper) { $this->driver->sweep(time() - ($config['lifetime'] * 60)); } } - /** - * Magic Method for calling methods on the session payload instance. - */ - public function __call($method, $parameters) - { - if (method_exists($this->payload, $method)) - { - return call_user_func_array(array($this->payload, $method), $parameters); - } - - throw new \Exception("Attempting to call undefined method [$method] on session manager."); - } - } \ No newline at end of file diff --git a/laravel/session/transporters/cookie.php b/laravel/session/transporters/cookie.php index 61fba5311..98074d857 100644 --- a/laravel/session/transporters/cookie.php +++ b/laravel/session/transporters/cookie.php @@ -16,9 +16,10 @@ class Cookie implements Transporter { /** * Get the session identifier for the request. * + * @param array $config * @return string */ - public function get() + public function get($config) { return $this->cookie->get('laravel_session'); } diff --git a/laravel/session/transporters/transporter.php b/laravel/session/transporters/transporter.php index afc1fbc6d..b1c464abd 100644 --- a/laravel/session/transporters/transporter.php +++ b/laravel/session/transporters/transporter.php @@ -5,9 +5,10 @@ interface Transporter { /** * Get the session identifier for the request. * + * @param array $config * @return string */ - public function get(); + public function get($config); /** * Store the session identifier for the request. diff --git a/tests/Session/SessionDriverTest.php b/tests/Session/SessionDriverTest.php deleted file mode 100644 index db7a9c37c..000000000 --- a/tests/Session/SessionDriverTest.php +++ /dev/null @@ -1,346 +0,0 @@ -start(IoC::resolve('laravel.config'), null); - - $this->assertTrue(is_string($driver->session['id'])); - $this->assertEquals(strlen($driver->session['id']), 40); - $this->assertTrue(is_array($driver->session['data'])); - $this->assertEquals(strlen($driver->session['data']['csrf_token']), 16); - } - - - public function testStartMethodCallsLoadWhenIDIsGiven() - { - $mock = $this->getFileDriverMock(); - - $mock->expects($this->once()) - ->method('load') - ->with($this->equalTo('something')); - - $mock->start(IoC::resolve('laravel.config'), 'something'); - } - - - public function testSessionIsLoadedWhenIDIsValid() - { - $mock = $this->getFileDriverMock(); - - $time = time(); - - $session = array('id' => 'something', 'last_activity' => $time, 'data' => array('name' => 'Taylor', 'csrf_token' => 'token')); - - $this->setMockLoadExpectations($mock, $session); - - $mock->start(IoC::resolve('laravel.config'), 'something'); - - $this->assertEquals($mock->session['id'], 'something'); - $this->assertEquals($mock->session['last_activity'], $time); - $this->assertEquals($mock->session['data'], array('name' => 'Taylor', 'csrf_token' => 'token')); - } - - - public function testSessionIsRestartedWhenLoadedSessionIsExpired() - { - $mock = $this->getFileDriverMock(); - - $time = new DateTime('2009-01-01'); - $time = $time->getTimestamp(); - - $session = array('id' => 'something', 'last_activity' => $time, 'data' => array('name' => 'Taylor')); - - $this->setMockLoadExpectations($mock, $session); - - $mock->start(IoC::resolve('laravel.config'), 'something'); - - $this->assertEquals(strlen($mock->session['id']), 40); - $this->assertFalse(isset($mock->session['data']['name'])); - $this->assertTrue(isset($mock->session['data']['csrf_token'])); - } - - - public function testHasMethodIndicatesIfItemExistsInSession() - { - $mock = $this->getSessionDriverWithData(); - - $this->assertTrue($mock->has('name')); - $this->assertFalse($mock->has('test')); - } - - - public function testGetMethodGetsItemsFromTheSession() - { - $mock = $this->getSessionDriverWithData(); - - $this->assertNull($mock->get('test')); - $this->assertEquals($mock->get('name'), 'Taylor'); - $this->assertEquals($mock->name, 'Taylor'); - $this->assertEquals($mock->get('test', 'Taylor'), 'Taylor'); - $this->assertEquals($mock->get('test', function() {return 'Taylor';}), 'Taylor'); - - $mock->session['data'][':old:test1'] = 'test1'; - $mock->session['data'][':new:test2'] = 'test2'; - - $this->assertEquals($mock->get('test1'), 'test1'); - $this->assertEquals($mock->get('test2'), 'test2'); - } - - - public function testPutMethodPutsItemsInTheSession() - { - $mock = $this->getSessionDriverWithData(); - - $mock->put('name', 'Tony'); - $mock->age = 30; - - $this->assertEquals($mock->session['data']['name'], 'Tony'); - $this->assertEquals($mock->session['data']['age'], 30); - } - - - public function testFlashMethodPutsItemsInFlashData() - { - $mock = $this->getSessionDriverWithData(); - - $mock->flash('name', 'James'); - - $this->assertEquals($mock->session['data'][':new:name'], 'James'); - } - - - public function testKeepMethodRejuvenatesFlashData() - { - $mock = $this->getSessionDriverWithData(); - - $mock->session['data'][':old:test'] = 'test'; - $mock->keep('test'); - - $this->assertFalse(isset($mock->session['data'][':old:test'])); - $this->assertEquals($mock->session['data'][':new:test'], 'test'); - } - - - public function testKeepMethodRejuvenatesAllFlashDataInArray() - { - $mock = $this->getSessionDriverWithData(); - - $mock->session['data'][':old:test1'] = 'test1'; - $mock->session['data'][':old:test2'] = 'test2'; - - $mock->keep(array('test1', 'test2')); - - $this->assertFalse(isset($mock->session['data'][':old:test1'])); - $this->assertFalse(isset($mock->session['data'][':old:test2'])); - $this->assertEquals($mock->session['data'][':new:test1'], 'test1'); - $this->assertEquals($mock->session['data'][':new:test2'], 'test2'); - } - - - public function testReflashMethodRejuvenatesAllFlashData() - { - $mock = $this->getSessionDriverWithData(); - - $mock->session['data'][':old:test1'] = 'test1'; - $mock->session['data'][':old:test2'] = 'test2'; - - $mock->reflash(); - - $this->assertFalse(isset($mock->session['data'][':old:test1'])); - $this->assertFalse(isset($mock->session['data'][':old:test2'])); - $this->assertEquals($mock->session['data'][':new:test1'], 'test1'); - $this->assertEquals($mock->session['data'][':new:test2'], 'test2'); - } - - - public function testForgetMethodRemovesDataFromSession() - { - $mock = $this->getSessionDriverWithData(); - - $mock->forget('name'); - - $this->assertFalse(isset($mock->session['data']['name'])); - } - - - public function testFlushMethodsClearsEntireSessionData() - { - $mock = $this->getSessionDriverWithData(); - - $mock->flush(); - - $this->assertEquals(count($mock->session['data']), 0); - } - - - public function testRegenerateMethodDeletesSessionAndResetsID() - { - $mock = $this->getMock('Laravel\\Session\\Drivers\\File', array('load', 'delete'), $this->getFileDriverConstructor()); - - $this->setMockLoadExpectations($mock, $this->getDummySession()); - - $mock->expects($this->once()) - ->method('delete') - ->with($this->equalTo('something')); - - $mock->start(IoC::resolve('laravel.config'), 'something'); - - $mock->regenerate(); - - $this->assertEquals(strlen($mock->session['id']), 40); - } - - - public function testCloseMethodFlashesOldInputData() - { - $mock = $this->getMock('Laravel\\Session\\Drivers\\File', array('save'), $this->getFileDriverConstructor()); - - $this->setMockLoadExpectations($mock, $this->getDummySession()); - - $mock->start(IoC::resolve('laravel.config'), 'something'); - - $mock->close(new InputStub, time()); - - $this->assertEquals($mock->session['data'][':old:laravel_old_input'], array('name' => 'Taylor')); - } - - - public function testCloseMethodAgesFlashData() - { - $mock = $this->getSessionDriverWithData(); - - $mock->session['data'][':old:old'] = 'old'; - $mock->flash('flash', 'flash'); - - $mock->close(new InputStub, time()); - - $this->assertFalse(isset($mock->session['data'][':old:old'])); - $this->assertFalse(isset($mock->session['data'][':new:flash'])); - $this->assertEquals($mock->session['data'][':old:flash'], 'flash'); - } - - - public function testCloseMethodSavesSession() - { - $mock = $this->getMock('Laravel\\Session\\Drivers\\File', array('load', 'save', 'sweep'), $this->getFileDriverConstructor()); - - $session = $this->getDummySession(); - - $session['data']['csrf_token'] = 'token'; - - $this->setMockLoadExpectations($mock, $session); - - $expect = $session; - - Laravel\Arr::set($expect, 'data.:old:laravel_old_input', array('name' => 'Taylor')); - - $mock->expects($this->once()) - ->method('save') - ->with($this->equalTo($expect)); - - $mock->start(IoC::resolve('laravel.config'), 'something'); - - $mock->close(new InputStub, $mock->session['last_activity']); - } - - - /** - * @dataProvider cookieMethodProvider - */ - public function testCookieMethodWritesCookie($expire_on_close, $minutes) - { - $mock = $this->getSessionDriverWithData(); - - $config = IoC::resolve('laravel.config'); - - $config->set('session.expire_on_close', $expire_on_close); - - $mock->start($config, 'something'); - - $cookieMock = $this->getMock('Laravel\\Cookie', array('put'), array(array())); - - $cookieMock->expects($this->once()) - ->method('put') - ->with('laravel_session', 'something', $minutes, $config->get('session.path'), $config->get('session.domain')); - - $mock->cookie($cookieMock); - } - - // ----------------------------------------------------------------------------------- - // Utility Methods & Providers - // ----------------------------------------------------------------------------------- - - public function getSessionDriverWithData() - { - $mock = $this->getFileDriverMock(); - - $this->setMockLoadExpectations($mock, $this->getDummySession()); - - $mock->start(IoC::resolve('laravel.config'), 'something'); - - return $mock; - } - - - private function getFileDriverMock() - { - return $this->getMock('Laravel\\Session\\Drivers\\File', array('load', 'save'), $this->getFileDriverConstructor()); - } - - - private function getFileDriverConstructor() - { - return array(IoC::resolve('laravel.file'), null); - } - - - private function setMockLoadExpectations($mock, $session) - { - $mock->expects($this->any()) - ->method('load') - ->will($this->returnValue($session)); - } - - - private function getDummySession() - { - return array('id' => 'something', 'last_activity' => time(), 'data' => array('name' => 'Taylor')); - } - - - public function cookieMethodProvider() - { - return array( - array(false, 60), - array(true, 0), - ); - } - -} - -// ----------------------------------------------------------------------------------- -// Stub Classes -// ----------------------------------------------------------------------------------- - -class InputStub extends Laravel\Input { - - public function __construct() {} - - public function get($key = null, $default = null) - { - return array('name' => 'Taylor'); - } - -} - -class CookieStub extends Laravel\Cookie { - - public function put() {} - -} \ No newline at end of file diff --git a/tests/Session/SessionManagerTest.php b/tests/Session/SessionManagerTest.php index dcbd2fc8d..7c411ac6c 100644 --- a/tests/Session/SessionManagerTest.php +++ b/tests/Session/SessionManagerTest.php @@ -1,29 +1,218 @@ array('resolver' => function($container) - { - return new stdClass; - }) - ); + $transporter->expects($this->once())->method('get'); - $manager = new Laravel\Session\Manager(new Laravel\Container($dependencies)); + $manager = new Manager($driver, $transporter); - $this->assertInstanceOf('stdClass', $manager->driver('test')); + $manager->payload($this->getConfig()); } /** - * @expectedException Exception + * @dataProvider mockProvider */ - public function testDriverMethodThrowsExceptionForUndefinedDriver() + public function testSessionManagerCallsDriverLoadWithSessionID($driver, $transporter) { - $manager = new Laravel\Session\Manager(new Laravel\Container(array())); + $transporter->expects($this->any())->method('get')->will($this->returnValue('something')); - $manager->driver('test'); + $driver->expects($this->once())->method('load')->with($this->equalTo('something')); + + $manager = new Manager($driver, $transporter); + + $manager->payload($this->getConfig()); } + /** + * @dataProvider mockProvider + */ + public function testSessionManagerReturnsPayloadWhenFound($driver, $transporter) + { + $this->setDriverExpectation($driver, 'load', $this->getDummySession()); + + $manager = new Manager($driver, $transporter); + + $payload = $manager->payload($this->getConfig()); + + $this->assertInstanceOf('Laravel\\Session\\Payload', $payload); + $this->assertEquals($payload->session, $this->getDummySession()); + } + + /** + * @dataProvider mockProvider + */ + public function testSessionManagerCreatesNewSessionWhenSessionIsNull($driver, $transporter) + { + $this->setDriverExpectation($driver, 'load', null); + + $manager = new Manager($driver, $transporter); + + $payload = $manager->payload($this->getConfig()); + + $this->assertInstanceOf('Laravel\\Session\\Payload', $payload); + $this->assertEquals(strlen($payload->session['id']), 40); + $this->assertTrue(is_array($payload->session['data'])); + } + + /** + * @dataProvider mockProvider + */ + public function testSessionManagerCreatesNewSessionWhenSessionIsExpired($driver, $transporter) + { + $dateTime = new DateTime('1970-01-01'); + + $this->setDriverExpectation($driver, 'load', array('last_activity' => $dateTime->getTimestamp())); + + $manager = new Manager($driver, $transporter); + + $payload = $manager->payload($this->getConfig()); + + $this->assertInstanceOf('Laravel\\Session\\Payload', $payload); + $this->assertEquals(strlen($payload->session['id']), 40); + } + + /** + * @dataProvider mockProvider + */ + public function testSessionManagerSetsCSRFTokenIfOneIsNotPresent($driver, $transporter) + { + $session = $this->getDummySession(); + + unset($session['data']['csrf_token']); + + $this->setDriverExpectation($driver, 'load', $session); + + $manager = new Manager($driver, $transporter); + + $payload = $manager->payload($this->getConfig()); + + $this->assertTrue(isset($payload->session['data']['csrf_token'])); + $this->assertEquals(strlen($payload->session['data']['csrf_token']), 16); + } + + /** + * @dataProvider mockProvider + */ + public function testCloseMethodCallsDriverAndTransporter($driver, $transporter) + { + $driver->expects($this->any())->method('load')->will($this->returnValue($this->getDummySession())); + + $manager = new Manager($driver, $transporter); + + $payload = $this->getMock('Laravel\\Session\\Payload', array('age'), array(array('id' => 'something'))); + + $payload->expects($this->any())->method('age')->will($this->returnValue('something')); + + $driver->expects($this->once())->method('save')->with('something', $this->getConfig()); + + $transporter->expects($this->once())->method('put')->with('something', $this->getConfig()); + + $manager->close($payload, $this->getConfig()); + } + + /** + * @dataProvider mockProvider + */ + public function testCloseMethodCallsSweepWhenDriverIsSweeper($driver, $transporter) + { + $driver = $this->getMock('SweeperStub', array('sweep')); + + $driver->expects($this->once())->method('sweep'); + + $manager = new Manager($driver, $transporter); + + $config = $this->getConfig(); + + $config['sweepage'] = array(100, 100); + + $manager->close(new Laravel\Session\Payload($this->getDummySession()), $config); + } + + /** + * @dataProvider mockProvider + */ + public function testCloseMethodDoesntCallSweepWhenDriverIsNotSweeper($driver, $transporter) + { + $driver = $this->getMock('Laravel\\Session\\Drivers\\Driver', array('sweep', 'load', 'save', 'delete')); + + $driver->expects($this->never())->method('sweep'); + + $manager = new Manager($driver, $transporter); + + $manager = new Manager($driver, $transporter); + + $config = $this->getConfig(); + + $config['sweepage'] = array(100, 100); + + $manager->close(new Laravel\Session\Payload($this->getDummySession()), $config); + } + + // --------------------------------------------------------------------- + // Providers + // --------------------------------------------------------------------- + + public function mockProvider() + { + return array(array($this->getMockDriver(), $this->getMockTransporter())); + } + + // --------------------------------------------------------------------- + // Support Functions + // --------------------------------------------------------------------- + + private function setDriverExpectation($mock, $method, $session) + { + $mock->expects($this->any()) + ->method($method) + ->will($this->returnValue($session)); + } + + private function getMockDriver() + { + return $this->getMock('Laravel\\Session\\Drivers\\Driver'); + } + + private function getMockTransporter() + { + return $this->getMock('Laravel\\Session\\Transporters\\Transporter', array('get', 'put')); + } + + private function getDummySession() + { + return array( + 'id' => 'something', + 'last_activity' => time(), + 'data' => array( + 'name' => 'Taylor', + 'csrf_token' => 'token' + )); + } + + private function getConfig() + { + return IoC::resolve('laravel.config')->get('session'); + } + +} + +// --------------------------------------------------------------------- +// Stubs +// --------------------------------------------------------------------- + +class SweeperStub implements Laravel\Session\Drivers\Driver, Laravel\Session\Drivers\Sweeper { + + public function load($id) {} + public function save($session, $config) {} + public function delete($id) {} + public function sweep($expiration) {} + } \ No newline at end of file diff --git a/tests/Session/SessionResolverTest.php b/tests/Session/SessionResolverTest.php deleted file mode 100644 index 1440eef59..000000000 --- a/tests/Session/SessionResolverTest.php +++ /dev/null @@ -1,17 +0,0 @@ -set('application.key', 'something'); - - $this->assertInstanceOf('Laravel\\Session\\Manager', IoC::resolve('laravel.session.manager')); - $this->assertInstanceOf('Laravel\\Session\\Drivers\\APC', IoC::resolve('laravel.session.apc')); - $this->assertInstanceOf('Laravel\\Session\\Drivers\\Cookie', IoC::resolve('laravel.session.cookie')); - $this->assertInstanceOf('Laravel\\Session\\Drivers\\Database', IoC::resolve('laravel.session.database')); - $this->assertInstanceOf('Laravel\\Session\\Drivers\\File', IoC::resolve('laravel.session.file')); - $this->assertInstanceOf('Laravel\\Session\\Drivers\\Memcached', IoC::resolve('laravel.session.memcached')); - } - -} \ No newline at end of file