From 26a66027f8334144eb3fda3bf05586d63086b9ca Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 14 Sep 2011 20:16:13 -0500 Subject: [PATCH] more refactoring on the framework. --- application/config/session.php | 28 +--- laravel/cache/{ => drivers}/apc.php | 4 +- laravel/cache/{ => drivers}/driver.php | 23 +-- laravel/cache/{ => drivers}/file.php | 2 +- laravel/cache/{ => drivers}/memcached.php | 7 +- laravel/config/container.php | 22 ++- laravel/laravel.php | 4 +- laravel/session/{ => drivers}/apc.php | 21 +-- laravel/session/{ => drivers}/cookie.php | 23 +-- laravel/session/{ => drivers}/database.php | 15 +- laravel/session/{ => drivers}/driver.php | 169 +++++++++++--------- laravel/session/{ => drivers}/file.php | 2 +- laravel/session/{ => drivers}/memcached.php | 14 +- laravel/session/{ => drivers}/sweeper.php | 2 +- 14 files changed, 143 insertions(+), 193 deletions(-) rename laravel/cache/{ => drivers}/apc.php (90%) rename laravel/cache/{ => drivers}/driver.php (70%) rename laravel/cache/{ => drivers}/file.php (97%) rename laravel/cache/{ => drivers}/memcached.php (86%) rename laravel/session/{ => drivers}/apc.php (61%) rename laravel/session/{ => drivers}/cookie.php (78%) rename laravel/session/{ => drivers}/database.php (82%) rename laravel/session/{ => drivers}/driver.php (50%) rename laravel/session/{ => drivers}/file.php (97%) rename laravel/session/{ => drivers}/memcached.php (77%) rename laravel/session/{ => drivers}/sweeper.php (81%) diff --git a/application/config/session.php b/application/config/session.php index 6969dadb..d997ba5c 100644 --- a/application/config/session.php +++ b/application/config/session.php @@ -16,7 +16,7 @@ return array( | */ - 'driver' => '', + 'driver' => 'file', /* |-------------------------------------------------------------------------- @@ -75,30 +75,4 @@ return array( 'domain' => null, - /* - |-------------------------------------------------------------------------- - | Session Cookie HTTPS - |-------------------------------------------------------------------------- - | - | Determines if the session cookie should only be transported over HTTPS. - | - */ - - 'https' => false, - - /* - |-------------------------------------------------------------------------- - | HTTP Only Session Cookie - |-------------------------------------------------------------------------- - | - | Determines if the session cookie should only be accessible over HTTP. - | - | Note: The intention of the "HTTP Only" option is to keep cookies from - | being accessed by client-side scripting languages. However, this - | setting should not be viewed as providing total XSS protection. - | - */ - - 'http_only' => false, - ); \ No newline at end of file diff --git a/laravel/cache/apc.php b/laravel/cache/drivers/apc.php similarity index 90% rename from laravel/cache/apc.php rename to laravel/cache/drivers/apc.php index 154bde11..d0bf2321 100644 --- a/laravel/cache/apc.php +++ b/laravel/cache/drivers/apc.php @@ -1,4 +1,4 @@ -proxy->apc_fetch($this->key.$key))) ? $cache : null; + if ( ! is_null($cache = $this->proxy->apc_fetch($this->key.$key))) return $cache; } /** diff --git a/laravel/cache/driver.php b/laravel/cache/drivers/driver.php similarity index 70% rename from laravel/cache/driver.php rename to laravel/cache/drivers/driver.php index 658629ec..3a622e61 100644 --- a/laravel/cache/driver.php +++ b/laravel/cache/drivers/driver.php @@ -1,4 +1,4 @@ - - * // Retrieve an item from the cache - * $name = Cache::get('name'); - * - * // Retrieve an item from the cache and return a default value if it doesn't exist - * $name = Cache::get('name', 'Fred'); - * - * * @param string $key * @param mixed $default * @param string $driver @@ -49,11 +41,6 @@ abstract class Driver { /** * Write an item to the cache for a given number of minutes. * - * - * // Store an item in the cache for 5 minutes - * Cache::put('name', 'Fred', 5); - * - * * @param string $key * @param mixed $value * @param int $minutes @@ -65,14 +52,6 @@ abstract class Driver { * Get an item from the cache. If the item doesn't exist in the cache, store * the default value in the cache and return it. * - * - * // Get an item from the cache and store the default value if it doesn't exist - * Cache::remember('name', 'Fred', 5); - * - * // Closures may also be used to defer retrieval of the default value - * Cache::remember('users', function() {return DB::table('users')->get();}, 5); - * - * * @param string $key * @param mixed $default * @param int $minutes diff --git a/laravel/cache/file.php b/laravel/cache/drivers/file.php similarity index 97% rename from laravel/cache/file.php rename to laravel/cache/drivers/file.php index 8c7c98ed..47ace3e6 100644 --- a/laravel/cache/file.php +++ b/laravel/cache/drivers/file.php @@ -1,4 +1,4 @@ -key = $key; $this->memcache = $memcache; @@ -49,7 +50,7 @@ class Memcached extends Driver { */ protected function retrieve($key) { - return (($cache = $this->memcache->get($this->key.$key)) !== false) ? $cache : null; + if (($cache = $this->memcache->get($this->key.$key)) !== false) return $cache; } /** diff --git a/laravel/config/container.php b/laravel/config/container.php index c3c6f46f..8e0b3184 100644 --- a/laravel/config/container.php +++ b/laravel/config/container.php @@ -225,7 +225,7 @@ return array( $config = $container->resolve('laravel.config')->get('session'); - return new Session\Cookie(Security\Crypter::make(), $cookies, $config); + return new Session\Drivers\Cookie(Security\Crypter::make(), $cookies); }), /* @@ -238,7 +238,7 @@ return array( { $table = $container->resolve('laravel.config')->get('session.table'); - return new Session\Database($container->resolve('laravel.database.manager')->connection(), $table); + return new Session\Drivers\Database($container->resolve('laravel.database.manager')->connection()); }), /* @@ -260,13 +260,13 @@ return array( 'laravel.cache.file' => array('resolver' => function($container) { - return new Cache\File($container->resolve('laravel.file'), CACHE_PATH); + return new Cache\Drivers\File($container->resolve('laravel.file'), CACHE_PATH); }), 'laravel.session.file' => array('resolver' => function($container) { - return new Session\File($container->resolve('laravel.file'), SESSION_PATH); + return new Session\Drivers\File($container->resolve('laravel.file'), SESSION_PATH); }), /* @@ -277,7 +277,13 @@ return array( 'laravel.cache.apc' => array('resolver' => function($container) { - return new Cache\APC(new Proxy, $container->resolve('laravel.config')->get('cache.key')); + return new Cache\Drivers\APC(new Proxy, $container->resolve('laravel.config')->get('cache.key')); + }), + + + 'laravel.session.id' => array('singleton' => true, 'resolver' => function($container) + { + return $container->resolve('laravel.cookie')->get('laravel_session'); }), @@ -285,7 +291,7 @@ return array( { $lifetime = $container->resolve('laravel.config')->get('session.lifetime'); - return new Session\APC($container->resolve('laravel.cache.apc'), $lifetime); + return new Session\Drivers\APC($container->resolve('laravel.cache.apc')); }), /* @@ -300,7 +306,7 @@ return array( $key = $container->resolve('laravel.config')->get('cache.key'); - return new Cache\Memcached($connection, $key); + return new Cache\Drivers\Memcached($connection, $key); }), @@ -308,7 +314,7 @@ return array( { $lifetime = $container->resolve('laravel.config')->get('session.lifetime'); - return new Session\Memcached($container->resolve('laravel.cache.memcached'), $lifetime); + return new Session\Drivers\Memcached($container->resolve('laravel.cache.memcached')); }), diff --git a/laravel/laravel.php b/laravel/laravel.php index 45f87517..20f83cf7 100644 --- a/laravel/laravel.php +++ b/laravel/laravel.php @@ -49,9 +49,9 @@ date_default_timezone_set($config->get('application.timezone')); // -------------------------------------------------------------- if ($config->get('session.driver') !== '') { - $cookie = $container->resolve('laravel.input')->cookies->get('laravel_session'); + $id = $container->resolve('laravel.session.id'); - $container->resolve('laravel.session')->start($cookie, $config->get('session')); + $container->resolve('laravel.session')->start($container->resolve('laravel.config'), $id); } // -------------------------------------------------------------- diff --git a/laravel/session/apc.php b/laravel/session/drivers/apc.php similarity index 61% rename from laravel/session/apc.php rename to laravel/session/drivers/apc.php index 055551f6..b2c2594d 100644 --- a/laravel/session/apc.php +++ b/laravel/session/drivers/apc.php @@ -1,32 +1,23 @@ -apc = $apc; - $this->lifetime = $lifetime; } /** @@ -47,7 +38,7 @@ class APC extends Driver { */ protected function save() { - $this->apc->put($this->session['id'], $this->session, $this->lifetime); + $this->apc->put($this->session['id'], $this->session, $this->config->get('session.lifetime')); } /** diff --git a/laravel/session/cookie.php b/laravel/session/drivers/cookie.php similarity index 78% rename from laravel/session/cookie.php rename to laravel/session/drivers/cookie.php index 203f054a..79a155b9 100644 --- a/laravel/session/cookie.php +++ b/laravel/session/drivers/cookie.php @@ -1,42 +1,33 @@ -cookie = $cookie; - $this->config = $config; $this->crypter = $crypter; } @@ -63,11 +54,13 @@ class Cookie extends Driver { { if ( ! headers_sent()) { - extract($this->config); + $config = $this->config->get('session'); + + extract($config); $payload = $this->crypter->encrypt(serialize($this->session)); - $this->cookie->put('session_payload', $payload, $lifetime, $path, $domain, $https, $http_only); + $this->cookie->put('session_payload', $payload, $lifetime, $path, $domain); } } diff --git a/laravel/session/database.php b/laravel/session/drivers/database.php similarity index 82% rename from laravel/session/database.php rename to laravel/session/drivers/database.php index 9e17268d..e5fdbbf8 100644 --- a/laravel/session/database.php +++ b/laravel/session/drivers/database.php @@ -1,4 +1,4 @@ -table = $table; $this->connection = $connection; } @@ -95,7 +86,7 @@ class Database extends Driver implements Sweeper { */ protected function table() { - return $this->connection->table($this->table); + return $this->connection->table($this->config->get('session.table')); } } \ No newline at end of file diff --git a/laravel/session/driver.php b/laravel/session/drivers/driver.php similarity index 50% rename from laravel/session/driver.php rename to laravel/session/drivers/driver.php index d3a4c2d1..61500336 100644 --- a/laravel/session/driver.php +++ b/laravel/session/drivers/driver.php @@ -1,7 +1,9 @@ -config = $config; $this->session = ( ! is_null($id)) ? $this->load($id) : null; - if (is_null($this->session) or (time() - $this->session['last_activity']) > ($this->config['lifetime'] * 60)) + // 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($this->session) or $this->expired()) { $this->session = array('id' => Str::random(40), 'data' => array()); } - if ( ! $this->has('csrf_token')) $this->put('csrf_token', Str::random(16)); + // If a CSRF token is not present in the session, we will generate one. These tokens + // are generated per session to protect against Cross-Site Request Forgery attacks on + // the application. It is up to the developer to take advantage of them using the token + // methods on the Form class and the "csrf" route filter. + if ( ! $this->has('csrf_token')) + { + $this->put('csrf_token', Str::random(16)); + } + } - $this->session['last_activity'] = time(); + /** + * Deteremine if the session is expired based on the last activity timestamp + * and the session lifetime set in the configuration file. + * + * @return bool + */ + private function expired() + { + return (time() - $this->session['last_activity']) > ($this->config->get('session.lifetime') * 60); } /** @@ -86,41 +104,28 @@ abstract class Driver { * * A default value may also be specified, and will be returned in the item doesn't exist. * - * - * // Get an item from the session - * $name = Session::get('name'); - * - * // Get an item from the session and return a default value if it doesn't exist - * $name = Session::get('name', 'Fred'); - * - * * @param string $key * @param mixed $default * @return mixed */ - public function get($key, $default = null) + public final function get($key, $default = null) { foreach (array($key, ':old:'.$key, ':new:'.$key) as $possibility) { if (array_key_exists($possibility, $this->session['data'])) return $this->session['data'][$possibility]; } - return ($default instanceof \Closure) ? call_user_func($default) : $default; + return ($default instanceof Closure) ? call_user_func($default) : $default; } /** * Write an item to the session. * - * - * // Store an item in the session - * Session::put('name', 'Fred'); - * - * * @param string $key * @param mixed $value * @return Driver */ - public function put($key, $value) + public final function put($key, $value) { $this->session['data'][$key] = $value; @@ -133,29 +138,52 @@ abstract class Driver { * Flash data only exists for the next request. After that, it will be removed from * the session. Flash data is useful for temporary status or welcome messages. * - * - * // Store an item in the session flash data - * Session::flash('name', 'Fred'); - * - * * @param string $key * @param mixed $value * @return Driver */ - public function flash($key, $value) + public final function flash($key, $value) { $this->put(':new:'.$key, $value); return $this; } + /** + * Keep all of the session flash data from expiring at the end of the request. + * + * @return void + */ + public final function reflash() + { + $this->readdress(':old:', ':new:', array_keys($this->session['data'])); + } + + /** + * Keep a session flash item from expiring at the end of the request. + * + * If a string is passed to the method, only that item will be kept. An array may also + * be passed to the method, in which case all items in the array will be kept. + * + * @param string|array $key + * @return void + */ + public final function keep($key) + { + if (is_array($key)) return array_map(array($this, 'keep'), $key); + + $this->flash($key, $this->get($key)); + + $this->forget(':old:'.$key); + } + /** * Remove an item from the session. * * @param string $key * @return Driver */ - public function forget($key) + public final function forget($key) { unset($this->session['data'][$key]); } @@ -165,7 +193,7 @@ abstract class Driver { * * @return void */ - public function flush() + public final function flush() { $this->session['data'] = array(); } @@ -175,7 +203,7 @@ abstract class Driver { * * @return void */ - public function regenerate() + public final function regenerate() { $this->delete(); @@ -183,25 +211,28 @@ abstract class Driver { } /** - * Close the session. - * - * The session will be stored in persistant storage and the session cookie will be - * session cookie will be sent to the browser. - * - * The input of the current request will also be flashed to the session so it is - * available for the next request via the "old" method on the input class. + * Close the session and store the session payload in persistant storage. * * @param Laravel\Input $input * @return void */ public function close(Input $input) { + // The input for the current request will be flashed to the session for + // convenient access through the "old" method of the input class. This + // allows the easy repopulation of forms. $this->flash('laravel_old_input', $input->get())->age(); + $this->session['last_activity'] = time(); + $this->save(); - $this->write_cookie($input->cookies, $this->config); + $this->cookie($input->cookies); + // Some session drivers implement the "Sweeper" interface, which specifies + // that the driver needs to manually clean up its expired sessions. If the + // driver does in fact implement this interface, we will randomly call the + // sweep method on the driver. if ($this instanceof Sweeper and mt_rand(1, 100) <= 2) { $this->sweep(time() - ($this->config['lifetime'] * 60)); @@ -211,56 +242,56 @@ abstract class Driver { /** * Age the session flash data. * - * To age the data, we will forget all of the old keys and then rewrite the newly - * flashed items to have old keys, which will be available for the next request. - * * @return void */ - protected function age() + private function age() { + // To age the data, we will forget all of the old keys and then rewrite the newly + // flashed items to have old keys, which will be available for the next request. foreach ($this->session['data'] as $key => $value) { if (strpos($key, ':old:') === 0) $this->forget($key); } - $session = $this->session['data']; + $this->readdress(':new:', ':old:', array_keys($this->session['data'])); + } - $this->session['data'] = array_combine(str_replace(':new:', ':old:', array_keys($session)), array_values($session)); + /** + * Readdress the session data by performing a string replacement on the keys. + * + * @param string $search + * @param string $replace + * @param array $keys + * @return void + */ + private function readdress($search, $replace, $keys) + { + $this->session['data'] = array_combine(str_replace($search, $replace, $keys), array_values($this->session['data'])); } /** * Write the session cookie. * - * All of the session cookie configuration options are stored in the session - * configuration file. The cookie will only be written if the headers have not - * already been sent to the browser. - * * @param Laravel\Cookie $cookie * @param array $config * @return void */ - protected function write_cookie(Cookie $cookies, $config) + private function cookie(Cookie $cookies) { if ( ! headers_sent()) { + $config = $this->config->get('session'); + extract($config); $minutes = ($expire_on_close) ? 0 : $lifetime; - $cookies->put('laravel_session', $this->session['id'], $minutes, $path, $domain, $https, $http_only); + $cookies->put('laravel_session', $this->session['id'], $minutes, $path, $domain); } } /** * Magic Method for retrieving items from the session. - * - * This method is particularly helpful in controllers where access to the IoC container - * is provided through the controller's magic __get method. - * - * - * // Retrieve an item from the session from a controller method - * $name = $this->session->name; - * */ public function __get($key) { @@ -269,14 +300,6 @@ abstract class Driver { /** * Magic Method for writings items to the session. - * - * This method is particularly helpful in controllers where access to the IoC container - * is provided through the controller's magic __get method. - * - * - * // Set an item in the session from a controller method - * $this->session->name = 'Fred'; - * */ public function __set($key, $value) { diff --git a/laravel/session/file.php b/laravel/session/drivers/file.php similarity index 97% rename from laravel/session/file.php rename to laravel/session/drivers/file.php index 9088585a..309c6f27 100644 --- a/laravel/session/file.php +++ b/laravel/session/drivers/file.php @@ -1,4 +1,4 @@ -lifetime = $lifetime; $this->memcached = $memcached; } @@ -46,7 +38,7 @@ class Memcached extends Driver { */ protected function save() { - $this->memcached->put($this->session['id'], $this->session, $this->lifetime); + $this->memcached->put($this->session['id'], $this->session, $this->config->get('session.lifetime')); } /** diff --git a/laravel/session/sweeper.php b/laravel/session/drivers/sweeper.php similarity index 81% rename from laravel/session/sweeper.php rename to laravel/session/drivers/sweeper.php index 8fa57eb2..a62ff622 100644 --- a/laravel/session/sweeper.php +++ b/laravel/session/drivers/sweeper.php @@ -1,4 +1,4 @@ -