diff --git a/application/config/application.php b/application/config/application.php index 3a1cf2b5..732c769c 100644 --- a/application/config/application.php +++ b/application/config/application.php @@ -135,7 +135,8 @@ 'Redis' => 'Laravel\\Redis', 'Request' => 'Laravel\\Request', 'Response' => 'Laravel\\Response', - 'Session' => 'Laravel\\Session', + 'Section' => 'Laravel\\Section', + 'Session' => 'Laravel\\Facades\\Session', 'Str' => 'Laravel\\Str', 'Validator' => 'Laravel\\Validation\\Validator', 'View' => 'Laravel\\View', diff --git a/laravel/autoloader.php b/laravel/autoloader.php index 6233df58..37e613c9 100644 --- a/laravel/autoloader.php +++ b/laravel/autoloader.php @@ -52,9 +52,19 @@ public static function load($class) */ protected static function find($class) { - $file = str_replace('\\', '/', $class); + // After PHP namespaces were introduced, most libaries ditched underscores for + // for namespaces to indicate the class directory hierarchy. We will chec for + // the present of namespace slashes to determine the directory separator. + if (strpos($class, '\\') !== false) + { + $library = substr($class, 0, strpos($class, '\\')); + } + else + { + $library = substr($class, 0, strpos($class, '_')); + } - $namespace = substr($class, 0, strpos($class, '\\')); + $file = str_replace('\\', '/', $class); // If the namespace has been registered as a PSR-0 compliant library, we will // load the library according to the PSR-0 naming standards, which state that @@ -63,7 +73,7 @@ protected static function find($class) // The PSR-0 standard is exactly like the typical Laravel standard, the only // difference being that Laravel files are all lowercase, while PSR-0 states // that the file name should match the class name. - if (isset(static::$libraries[$namespace])) + if (isset(static::$libraries[$library])) { return str_replace('_', '/', $file).EXT; } @@ -81,7 +91,7 @@ protected static function find($class) // directory for the class according to the PSR-0 naming standard. if (file_exists($path = LIBRARY_PATH.str_replace('_', '/', $file).EXT)) { - static::$libraries[] = $namespace; + static::$libraries[] = $library; return $path; } diff --git a/laravel/blade.php b/laravel/blade.php index 24304e10..b68f0d9d 100644 --- a/laravel/blade.php +++ b/laravel/blade.php @@ -2,6 +2,21 @@ class Blade { + /** + * All of the compiler functions used by Blade. + * + * @var array + */ + protected static $compilers = array( + 'echos', + 'structure_openings', + 'structure_closings', + 'else', + 'yields', + 'section_start', + 'section_end', + ); + /** * Compiles the specified file containing Blade pseudo-code into valid PHP. * @@ -10,18 +25,38 @@ class Blade { */ public static function compile($path) { - $value = file_get_contents($path); + return static::compile_string(file_get_contents($path)); + } - return static::closings(static::openings(static::echos($value))); + /** + * Compiles the given string containing Blade pseudo-code into valid PHP. + * + * @param string $value + * @return string + */ + public static function compile_string($value) + { + foreach (static::$compilers as $compiler) + { + $method = "compile_{$compiler}"; + + $value = static::$method($value); + } + + return $value; } /** * Rewrites Blade echo statements into PHP echo statements. * + * Blade echo statements are simply PHP statement enclosed within double curly + * braces. For example, {{$content}} will simply echo out the content variable + * to the output buffer. + * * @param string $value * @return string */ - protected static function echos($value) + protected static function compile_echos($value) { return preg_replace('/\{\{(.+?)\}\}/', '', $value); } @@ -29,12 +64,18 @@ protected static function echos($value) /** * Rewrites Blade structure openings into PHP structure openings. * + * By "structures", we mean the if, elseif, foreach, for, and while statements. + * All of these structures essentially have the same format, and can be lumped + * into a single regular expression. + * * @param string $value * @return string */ - protected static function openings($value) + protected static function compile_structure_openings($value) { - return preg_replace('/@(if|elseif|foreach|for|while)(\s*\(.*?\))\:/', '', $value); + $pattern = '/@(if|elseif|foreach|for|while)(\s*\(.*?\))/'; + + return preg_replace($pattern, '', $value); } /** @@ -43,12 +84,65 @@ protected static function openings($value) * @param string $value * @return string */ - protected static function closings($value) + protected static function compile_structure_closings($value) { - $value = preg_replace('/(\s*)@(else)(.*?)\:/', '$1', $value); - $value = preg_replace('/(\s*)@(endif|endforeach|endfor|endwhile)(\s*)/', '$1 $3', $value); + $pattern = '/@(endif|endforeach|endfor|endwhile)(\s*)/'; - return $value; + return preg_replace($pattern, '$2', $value); + } + + /** + * Rewrites Blade else statements into PHP else statements. + * + * @param string $value + * @return string + */ + protected static function compile_else($value) + { + return preg_replace('/(\s*)@(else)(\s*)/', '$1$3', $value); + } + + /** + * Rewrites Blade @yield statements into Section statements. + * + * The Blade @yield statement is a shortcut to the Section::yield method. + * + * @param string $value + * @return string + */ + protected static function compile_yields($value) + { + $pattern = '/(\s*)@yield(\s*\(.*?\))/'; + + return preg_replace($pattern, '$1', $value); + } + + /** + * Rewrites Blade @section statements into Section statements. + * + * The Blade @section statement is a shortcut to the Section::start method. + * + * @param string $value + * @return string + */ + protected static function compile_section_start($value) + { + $pattern = '/(\s*)@section(\s*\(.*?\))/'; + + return preg_replace($pattern, '$1', $value); + } + + /** + * Rewrites Blade @endsection statements into Section statements. + * + * The Blade @endsection statement is a shortcut to the Section::stop method. + * + * @param string $value + * @return string + */ + protected static function compile_section_end($value) + { + return preg_replace('/@endsection/', '', $value); } } \ No newline at end of file diff --git a/laravel/bootstrap/core.php b/laravel/bootstrap/core.php index a9d34ef6..debdf36c 100644 --- a/laravel/bootstrap/core.php +++ b/laravel/bootstrap/core.php @@ -51,6 +51,7 @@ */ require SYS_PATH.'arr'.EXT; require SYS_PATH.'config'.EXT; +require SYS_PATH.'facades'.EXT; require SYS_PATH.'container'.EXT; require SYS_PATH.'autoloader'.EXT; diff --git a/laravel/container.php b/laravel/container.php index 64bd9526..18af38cf 100644 --- a/laravel/container.php +++ b/laravel/container.php @@ -29,6 +29,29 @@ public static function container() return static::$container; } + /** + * Resolve a core Laravel class from the container. + * + * + * // Resolve the "laravel.router" class from the container + * $input = IoC::core('router'); + * + * // Equivalent resolution using the "resolve" method + * $input = IoC::resolve('laravel.router'); + * + * // Pass an array of parameters to the resolver + * $input = IoC::core('router', array('test')); + * + * + * @param string $name + * @param array $parameters + * @return mixed + */ + public static function core($name, $parameters = array()) + { + return static::$container->core($name, $parameters); + } + /** * Magic Method for calling methods on the active container instance. * diff --git a/laravel/facades.php b/laravel/facades.php index 5ccfa889..4f7d2cbb 100644 --- a/laravel/facades.php +++ b/laravel/facades.php @@ -57,4 +57,6 @@ public static function __callStatic($method, $parameters) } } -} \ No newline at end of file +} + +class Session extends Facade { public static $resolve = 'laravel.session'; } \ No newline at end of file diff --git a/laravel/form.php b/laravel/form.php index b67a95d2..af94d5f1 100644 --- a/laravel/form.php +++ b/laravel/form.php @@ -144,7 +144,9 @@ public static function close() */ public static function token() { - return static::input('hidden', 'csrf_token', Session::token()); + $token = IoC::container()->core('session')->token(); + + return static::input('hidden', 'csrf_token', $token); } /** diff --git a/laravel/input.php b/laravel/input.php index fd73bf6c..c50c242c 100644 --- a/laravel/input.php +++ b/laravel/input.php @@ -72,7 +72,7 @@ public static function flash() { if (Config::$items['session']['driver'] !== '') { - Session::flash(Input::old_input, static::get()); + IoC::container()->core('session')->flash(Input::old_input, static::get()); } } @@ -109,7 +109,9 @@ public static function old($key = null, $default = null) throw new \Exception('A session driver must be specified in order to access old input.'); } - return Arr::get(Session::get(Input::old_input, array()), $key, $default); + $old = IoC::container()->core('session')->get(Input::old_input, array()); + + return Arr::get($old, $key, $default); } /** diff --git a/laravel/laravel.php b/laravel/laravel.php index e514e72c..e237fb10 100644 --- a/laravel/laravel.php +++ b/laravel/laravel.php @@ -28,7 +28,9 @@ { $driver = IoC::container()->core('session.'.Config::$items['session']['driver']); - Session::start($driver); + $id = Cookie::get(Config::$items['session']['cookie']); + + IoC::container()->instance('laravel.session', new Session($driver, $id)); } /** @@ -117,7 +119,7 @@ */ if (Config::$items['session']['driver'] !== '') { - Session::save($driver); + IoC::container()->core('session')->save($driver); } /** diff --git a/laravel/redirect.php b/laravel/redirect.php index 4a49ae5c..92f76432 100644 --- a/laravel/redirect.php +++ b/laravel/redirect.php @@ -61,7 +61,7 @@ public function with($key, $value) throw new \Exception('A session driver must be set before setting flash data.'); } - Session::flash($key, $value); + IoC::container()->core('session')->flash($key, $value); return $this; } diff --git a/laravel/request.php b/laravel/request.php index 5dd5d109..6dd862db 100644 --- a/laravel/request.php +++ b/laravel/request.php @@ -154,7 +154,7 @@ public static function secure() */ public static function forged() { - return Input::get('csrf_token') !== Session::token(); + return Input::get('csrf_token') !== IoC::container()->core('session')->token(); } /** diff --git a/laravel/routing/controller.php b/laravel/routing/controller.php index 608fbd25..4ed8e337 100644 --- a/laravel/routing/controller.php +++ b/laravel/routing/controller.php @@ -1,11 +1,20 @@ execute($method, $parameters); + return $controller->_execute($method, $parameters); } /** @@ -52,14 +61,13 @@ public static function call($destination, $parameters = array()) * @param string $controller * @return Controller */ - public static function resolve($controller) + public static function _resolve($controller) { - if ( ! static::load($controller)) return; + if ( ! static::_load($controller)) return; - // If the controller is registered in the IoC container, we will - // resolve it out of the container. Using constructor injection - // on controllers via the container allows more flexible and - // testable development of applications. + // If the controller is registered in the IoC container, we will resolve + // it out of the container. Using constructor injection on controllers + // via the container allows more flexible and testable applications. if (IoC::container()->registered('controllers.'.$controller)) { return IoC::container()->resolve('controllers.'.$controller); @@ -67,7 +75,17 @@ public static function resolve($controller) $controller = str_replace(' ', '_', ucwords(str_replace('.', ' ', $controller))).'_Controller'; - return new $controller; + $controller = new $controller; + + // If the controller has specified a layout to be used when rendering + // views, we will instantiate the layout instance and set it to the + // layout property, replacing the string layout name. + if ( ! is_null($controller->layout)) + { + $controller->layout = View::make($controller->layout); + } + + return $controller; } /** @@ -76,7 +94,7 @@ public static function resolve($controller) * @param string $controller * @return bool */ - protected static function load($controller) + protected static function _load($controller) { $controller = strtolower(str_replace('.', '/', $controller)); @@ -97,9 +115,9 @@ protected static function load($controller) * @param array $parameters * @return Response */ - public function execute($method, $parameters = array()) + public function _execute($method, $parameters = array()) { - if (static::hidden($method)) + if (static::_hidden($method)) { return Response::error('404'); } @@ -108,19 +126,30 @@ public function execute($method, $parameters = array()) // "before" filters return a response, it will be considered the // response to the request and the controller method will not be // used to handle the request to the application. - $response = Filter::run($this->filters('before', $method), array(), true); + $response = Filter::run($this->gather_filters('before', $method), array(), true); if (is_null($response)) { $response = call_user_func_array(array($this, "action_{$method}"), $parameters); + + // If the controller has specified a layout view. The response + // returned by the controller method will be bound to that view + // and the layout will be considered the response. + if ( ! is_null($this->layout) and $this->_viewable($response)) + { + $response = $this->layout->with('content', $response); + } } // The after filter and the framework expects all responses to // be instances of the Response class. If the method did not // return an instsance of Response, we will make on now. - if ( ! $response instanceof Response) $response = new Response($response); + if ( ! $response instanceof Response) + { + $response = new Response($response); + } - Filter::run($this->filters('after', $method), array($response)); + Filter::run($this->gather_filters('after', $method), array($response)); return $response; } @@ -131,29 +160,91 @@ public function execute($method, $parameters = array()) * @param string $method * @return bool */ - protected static function hidden($method) + protected static function _hidden($method) { - return $method == 'before' or $method == 'after' or strncmp($method, '_', 1) == 0; + $hidden = array('before', 'after', 'register_filters', 'gather_filters'); + + return strncmp($method, '_', 1) == 0 or in_array($method, $hidden); } /** - * Set filters on the controller's methods. + * Deteremine if a given response is considered "viewable". + * + * This is primarily used to determine which types of responses should be + * bound to the controller's layout and which should not. We do not want + * to bind redirects and file downloads to the layout, as this obviously + * would not make any sense. + * + * @param mixed $response + * @return bool + */ + protected function _viewable($response) + { + if ($response instanceof Response) + { + if ($response instanceof Redirect) + { + return false; + } + elseif ($response->headers['Content-Description'] == 'File Transfer') + { + return false; + } + } + + return true; + } + + /** + * Register "before" filters on the controller's methods. * * Generally, this method will be used in the controller's constructor. * * * // Set a "foo" before filter on the controller - * $this->filter('before', 'foo'); + * $this->before_filter('foo'); * * // Set several filters on an explicit group of methods - * $this->filter('after', 'foo|bar')->only(array('user', 'profile')); + * $this->before_filter('foo|bar')->only(array('user', 'profile')); * * + * @param string|array $filters + * @return Filter_Collection + */ + public function before($filters) + { + return $this->register_filters('before', $filters); + } + + /** + * Register "after" filters on the controller's methods. + * + * Generally, this method will be used in the controller's constructor. + * + * + * // Set a "foo" after filter on the controller + * $this->after_filter('foo'); + * + * // Set several filters on an explicit group of methods + * $this->after_filter('foo|bar')->only(array('user', 'profile')); + * + * + * @param string|array $filters + * @return Filter_Collection + */ + public function after($filters) + { + return $this->register_filters('after', $filters); + } + + /** + * Set filters on the controller's methods. + * * @param string $name * @param string|array $filters * @return Filter_Collection */ - public function filter($name, $filters) + protected function register_filters($name, $filters) { $this->filters[] = new Filter_Collection($name, $filters); @@ -167,7 +258,7 @@ public function filter($name, $filters) * @param string $method * @return array */ - protected function filters($name, $method) + protected function gather_filters($name, $method) { $filters = array(); diff --git a/laravel/routing/route.php b/laravel/routing/route.php index 3c14dacc..fce00fe9 100644 --- a/laravel/routing/route.php +++ b/laravel/routing/route.php @@ -107,7 +107,7 @@ public function call() { if ($response instanceof Delegate) { - $response = Controller::call($response->destination, $this->parameters); + $response = Controller::_call($response->destination, $this->parameters); } } diff --git a/laravel/section.php b/laravel/section.php new file mode 100644 index 00000000..90ee5643 --- /dev/null +++ b/laravel/section.php @@ -0,0 +1,108 @@ + + * // Start injecting into the "header" section + * Section::start('header'); + * + * // Inject a raw string into the "header" section + * Section::start('header', 'Laravel'); + * + * + * @param string $section + * @param string $content + * @return void + */ + public static function start($section, $content = '') + { + if ($content !== '') + { + ob_start(); + + static::$last = $section; + } + + static::append($section, $content); + } + + /** + * Inject inline content into a section. + * + * This is helpful for injecting simple strings such as page titles. + * + * + * // Inject inline content into the "header" section + * Section::inject('header', 'Laravel'); + * + * + * @param string $section + * @param string $content + * @return void + */ + public static function inject($section, $content) + { + static::start($section, $content); + } + + /** + * Stop injecting content into a section. + * + * @return void + */ + public static function stop() + { + static::append(static::$last, ob_get_clean()); + } + + /** + * Append content to a given section. + * + * @param string $section + * @param string $content + * @return void + */ + protected static function append($section, $content) + { + if (isset(static::$sections[$section])) + { + $content = static::$sections[$section].PHP_EOL.$content; + } + + static::$sections[$section] = $content; + } + + /** + * Get the string contents of a section. + * + * @param string $section + * @return string + */ + public static function yield($section) + { + return (isset(static::$sections[$section])) ? static::$sections[$section] : ''; + } + +} \ No newline at end of file diff --git a/laravel/security/auth.php b/laravel/security/auth.php index 7ab50922..a8480aca 100644 --- a/laravel/security/auth.php +++ b/laravel/security/auth.php @@ -1,5 +1,6 @@ core('session')->get(Auth::user_key); + + static::$user = call_user_func(Config::get('auth.user'), $id); if (is_null(static::$user) and ! is_null($cookie = Cookie::get(Auth::remember_key))) { @@ -149,7 +152,7 @@ public static function login($user, $remember = false) if ($remember) static::remember($user->id); - Session::put(Auth::user_key, $user->id); + IoC::container()->core('session')->put(Auth::user_key, $user->id); } /** @@ -192,7 +195,7 @@ public static function logout() Cookie::forget(Auth::remember_key); - Session::forget(Auth::user_key); + IoC::container()->core('session')->forget(Auth::user_key); } } \ No newline at end of file diff --git a/laravel/session.php b/laravel/session.php index 67a53055..0d21d0ed 100644 --- a/laravel/session.php +++ b/laravel/session.php @@ -16,42 +16,43 @@ class Session { * * @var array */ - protected static $session; + protected $session; /** * Indicates if the session already exists in storage. * * @var bool */ - protected static $exists = true; + protected $exists = true; /** * Start the session handling for the current request. * * @param Driver $driver + * @param string $id * @return void */ - public static function start(Driver $driver) + public function __construct(Driver $driver, $id) { - if ( ! is_null($id = Cookie::get(Config::$items['session']['cookie']))) + if ( ! is_null($id)) { - static::$session = $driver->load($id); + $this->session = $driver->load($id); } - if (is_null(static::$session) or static::invalid()) + if (is_null($this->session) or $this->invalid()) { - static::$exists = false; + $this->exists = false; - static::$session = array('id' => Str::random(40), 'data' => array()); + $this->session = array('id' => Str::random(40), 'data' => array()); } - if ( ! static::has('csrf_token')) + if ( ! $this->has('csrf_token')) { // A CSRF token is stored in every session. The token is used by the // Form class and the "csrf" filter to protect the application from // cross-site request forgery attacks. The token is simply a long, // random string which should be posted with each request. - static::put('csrf_token', Str::random(40)); + $this->put('csrf_token', Str::random(40)); } } @@ -62,11 +63,11 @@ public static function start(Driver $driver) * * @return bool */ - protected static function invalid() + protected function invalid() { $lifetime = Config::$items['session']['lifetime']; - return (time() - static::$session['last_activity']) > ($lifetime * 60); + return (time() - $this->session['last_activity']) > ($lifetime * 60); } /** @@ -74,9 +75,9 @@ protected static function invalid() * * @return bool */ - public static function started() + public function started() { - return is_array(static::$session); + return is_array($this->session); } /** @@ -85,9 +86,9 @@ public static function started() * @param string $key * @return bool */ - public static function has($key) + public function has($key) { - return ( ! is_null(static::get($key))); + return ( ! is_null($this->get($key))); } /** @@ -107,13 +108,13 @@ public static function has($key) * @param mixed $default * @return mixed */ - public static function get($key, $default = null) + public function get($key, $default = null) { foreach (array($key, ':old:'.$key, ':new:'.$key) as $possibility) { - if (array_key_exists($possibility, static::$session['data'])) + if (array_key_exists($possibility, $this->session['data'])) { - return static::$session['data'][$possibility]; + return $this->session['data'][$possibility]; } } @@ -127,9 +128,9 @@ public static function get($key, $default = null) * @param mixed $value * @return void */ - public static function put($key, $value) + public function put($key, $value) { - static::$session['data'][$key] = $value; + $this->session['data'][$key] = $value; } /** @@ -141,9 +142,9 @@ public static function put($key, $value) * @param mixed $value * @return void */ - public static function flash($key, $value) + public function flash($key, $value) { - static::put(':new:'.$key, $value); + $this->put(':new:'.$key, $value); } /** @@ -151,11 +152,11 @@ public static function flash($key, $value) * * @return void */ - public static function reflash() + public function reflash() { $flash = array(); - foreach (static::$session['data'] as $key => $value) + foreach ($this->session['data'] as $key => $value) { if (strpos($key, ':old:') === 0) { @@ -163,7 +164,7 @@ public static function reflash() } } - static::keep($flash); + $this->keep($flash); } /** @@ -172,11 +173,11 @@ public static function reflash() * @param string|array $key * @return void */ - public static function keep($keys) + public function keep($keys) { foreach ((array) $keys as $key) { - static::flash($key, static::get($key)); + $this->flash($key, $this->get($key)); } } @@ -186,9 +187,9 @@ public static function keep($keys) * @param string $key * @return Driver */ - public static function forget($key) + public function forget($key) { - unset(static::$session['data'][$key]); + unset($this->session['data'][$key]); } /** @@ -196,9 +197,9 @@ public static function forget($key) * * @return void */ - public static function flush() + public function flush() { - static::$session['data'] = array(); + $this->session['data'] = array(); } /** @@ -206,11 +207,11 @@ public static function flush() * * @return void */ - public static function regenerate() + public function regenerate() { - static::$session['id'] = Str::random(40); + $this->session['id'] = Str::random(40); - static::$exists = false; + $this->exists = false; } /** @@ -218,9 +219,9 @@ public static function regenerate() * * @return string */ - public static function token() + public function token() { - return static::get('csrf_token'); + return $this->get('csrf_token'); } /** @@ -229,17 +230,17 @@ public static function token() * @param Driver $driver * @return void */ - public static function save(Driver $driver) + public function save(Driver $driver) { - static::$session['last_activity'] = time(); + $this->session['last_activity'] = time(); - static::age(); + $this->age(); $config = Config::$items['session']; - $driver->save(static::$session, $config, static::$exists); + $driver->save($this->session, $config, $this->exists); - static::cookie(); + $this->cookie(); // Some session drivers implement the Sweeper interface, meaning that they // must clean up expired sessions manually. If the driver is a sweeper, we @@ -261,20 +262,20 @@ public static function save(Driver $driver) * * @return void */ - protected static function age() + protected function age() { - foreach (static::$session['data'] as $key => $value) + foreach ($this->session['data'] as $key => $value) { - if (strpos($key, ':old:') === 0) static::forget($key); + if (strpos($key, ':old:') === 0) $this->forget($key); } // Now that all of the "old" keys have been removed from the session data, // we can re-address all of the newly flashed keys to have old addresses. // The array_combine method uses the first array for keys, and the second // array for values to construct a single array from both. - $keys = str_replace(':new:', ':old:', array_keys(static::$session['data'])); + $keys = str_replace(':new:', ':old:', array_keys($this->session['data'])); - static::$session['data'] = array_combine($keys, array_values(static::$session['data'])); + $this->session['data'] = array_combine($keys, array_values($this->session['data'])); } /** @@ -282,7 +283,7 @@ protected static function age() * * @return void */ - protected static function cookie() + protected function cookie() { $config = Config::$items['session']; @@ -290,7 +291,7 @@ protected static function cookie() $minutes = ( ! $expire_on_close) ? $lifetime : 0; - Cookie::put($cookie, static::$session['id'], $minutes, $path, $domain, $secure); + Cookie::put($cookie, $this->session['id'], $minutes, $path, $domain, $secure); } } \ No newline at end of file diff --git a/laravel/view.php b/laravel/view.php index 8c542628..7ffd54aa 100644 --- a/laravel/view.php +++ b/laravel/view.php @@ -52,9 +52,9 @@ public function __construct($view, $data = array()) // // This makes the implementation of the Post/Redirect/Get pattern very // convenient since each view can assume it has a message container. - if (Config::$items['session']['driver'] !== '' and Session::started()) + if (Config::$items['session']['driver'] !== '' and IoC::core('session')->started()) { - $this->data['errors'] = Session::get('errors', function() + $this->data['errors'] = IoC::core('session')->get('errors', function() { return new Messages; });