Commit a1e82d21 by Taylor Otwell

added section class. refactored session for ioc usage. improved controller.…

added section class. refactored session for ioc usage. improved controller. added yielding and sections to blade. general blade improvements.
parent d05f4fa0
......@@ -135,7 +135,8 @@ return array(
'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',
......
......@@ -52,9 +52,19 @@ class Autoloader {
*/
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 @@ class Autoloader {
// 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 @@ class Autoloader {
// 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;
}
......
......@@ -3,6 +3,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.
*
* @param string $path
......@@ -10,18 +25,38 @@ class Blade {
*/
public static function compile($path)
{
$value = file_get_contents($path);
return static::compile_string(file_get_contents($path));
}
/**
* 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 static::closings(static::openings(static::echos($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('/\{\{(.+?)\}\}/', '<?php echo $1; ?>', $value);
}
......@@ -29,12 +64,18 @@ class Blade {
/**
* 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*\(.*?\))\:/', '<?php $1$2: ?>', $value);
$pattern = '/@(if|elseif|foreach|for|while)(\s*\(.*?\))/';
return preg_replace($pattern, '<?php $1$2: ?>', $value);
}
/**
......@@ -43,12 +84,65 @@ class Blade {
* @param string $value
* @return string
*/
protected static function closings($value)
protected static function compile_structure_closings($value)
{
$value = preg_replace('/(\s*)@(else)(.*?)\:/', '$1<?php $2$3: ?>', $value);
$value = preg_replace('/(\s*)@(endif|endforeach|endfor|endwhile)(\s*)/', '$1<?php $2; ?> $3', $value);
$pattern = '/@(endif|endforeach|endfor|endwhile)(\s*)/';
return $value;
return preg_replace($pattern, '<?php $1; ?>$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<?php $2: ?>$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<?php echo \\Laravel\\Section::yield$2; ?>', $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<?php \\Laravel\\Section::start$2; ?>', $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/', '<?php \\Laravel\\Section::stop(); ?>', $value);
}
}
\ No newline at end of file
......@@ -51,6 +51,7 @@ unset($application, $public, $laravel, $environment);
*/
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;
......
......@@ -30,6 +30,29 @@ class IoC {
}
/**
* Resolve a core Laravel class from the container.
*
* <code>
* // 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'));
* </code>
*
* @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.
*
* <code>
......
......@@ -57,4 +57,6 @@ abstract class Facade {
}
}
}
\ No newline at end of file
}
class Session extends Facade { public static $resolve = 'laravel.session'; }
\ No newline at end of file
......@@ -144,7 +144,9 @@ class Form {
*/
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);
}
/**
......
......@@ -72,7 +72,7 @@ class Input {
{
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 @@ class Input {
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);
}
/**
......
......@@ -28,7 +28,9 @@ if (Config::$items['session']['driver'] !== '')
{
$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 @@ $response->content = $response->render();
*/
if (Config::$items['session']['driver'] !== '')
{
Session::save($driver);
IoC::container()->core('session')->save($driver);
}
/**
......
......@@ -61,7 +61,7 @@ class Redirect extends Response {
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;
}
......
......@@ -154,7 +154,7 @@ class Request {
*/
public static function forged()
{
return Input::get('csrf_token') !== Session::token();
return Input::get('csrf_token') !== IoC::container()->core('session')->token();
}
/**
......
<?php namespace Laravel\Routing;
use Laravel\IoC;
use Laravel\View;
use Laravel\Request;
use Laravel\Redirect;
use Laravel\Response;
abstract class Controller {
/**
* The layout being used by the controller.
*
* @var string
*/
public $layout;
/**
* The filters assigned to the controller.
*
* @var array
......@@ -26,7 +35,7 @@ abstract class Controller {
* @param array $parameters
* @return Response
*/
public static function call($destination, $parameters = array())
public static function _call($destination, $parameters = array())
{
if (strpos($destination, '@') === false)
{
......@@ -35,14 +44,14 @@ abstract class Controller {
list($controller, $method) = explode('@', $destination);
$controller = static::resolve($controller);
$controller = static::_resolve($controller);
if (is_null($controller))
{
return Response::error('404');
}
return $controller->execute($method, $parameters);
return $controller->_execute($method, $parameters);
}
/**
......@@ -52,14 +61,13 @@ abstract class Controller {
* @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 @@ abstract class 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 @@ abstract class 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 @@ abstract class 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 @@ abstract class Controller {
// "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 @@ abstract class Controller {
* @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.
*
* <code>
* // 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'));
* </code>
*
* @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.
*
* <code>
* // 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'));
* </code>
*
* @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 @@ abstract class Controller {
* @param string $method
* @return array
*/
protected function filters($name, $method)
protected function gather_filters($name, $method)
{
$filters = array();
......
......@@ -107,7 +107,7 @@ class Route {
{
if ($response instanceof Delegate)
{
$response = Controller::call($response->destination, $this->parameters);
$response = Controller::_call($response->destination, $this->parameters);
}
}
......
<?php namespace Laravel;
class Section {
/**
* All of the captured sections.
*
* @var array
*/
public static $sections = array();
/**
* The last section on which injection was started.
*
* @var string
*/
protected static $last;
/**
* Start injecting content into a section.
*
* After calling this method, the "stop" method may be used to stop injecting
* content. A raw string may also be passed as the second argument, and will
* cause the given string to be injected into the section directly without
* using output buffering.
*
* <code>
* // Start injecting into the "header" section
* Section::start('header');
*
* // Inject a raw string into the "header" section
* Section::start('header', '<title>Laravel</title>');
* </code>
*
* @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.
*
* <code>
* // Inject inline content into the "header" section
* Section::inject('header', '<title>Laravel</title>');
* </code>
*
* @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
<?php namespace Laravel\Security;
use Laravel\IoC;
use Laravel\Str;
use Laravel\Config;
use Laravel\Cookie;
......@@ -73,7 +74,9 @@ class Auth {
{
if ( ! is_null(static::$user)) return static::$user;
static::$user = call_user_func(Config::get('auth.user'), Session::get(Auth::user_key));
$id = IoC::container()->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 @@ class Auth {
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 @@ class Auth {
Cookie::forget(Auth::remember_key);
Session::forget(Auth::user_key);
IoC::container()->core('session')->forget(Auth::user_key);
}
}
\ No newline at end of file
......@@ -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 @@ class Session {
*
* @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 @@ class Session {
*
* @return bool
*/
public static function started()
public function started()
{
return is_array(static::$session);
return is_array($this->session);
}
/**
......@@ -85,9 +86,9 @@ class Session {
* @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 @@ class Session {
* @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 @@ class Session {
* @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 @@ class Session {
* @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 @@ class Session {
*
* @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 @@ class Session {
}
}
static::keep($flash);
$this->keep($flash);
}
/**
......@@ -172,11 +173,11 @@ class Session {
* @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 @@ class Session {
* @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 @@ class Session {
*
* @return void
*/
public static function flush()
public function flush()
{
static::$session['data'] = array();
$this->session['data'] = array();
}
/**
......@@ -206,11 +207,11 @@ class Session {
*
* @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 @@ class Session {
*
* @return string
*/
public static function token()
public function token()
{
return static::get('csrf_token');
return $this->get('csrf_token');
}
/**
......@@ -229,17 +230,17 @@ class Session {
* @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 @@ class Session {
*
* @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 @@ class Session {
*
* @return void
*/
protected static function cookie()
protected function cookie()
{
$config = Config::$items['session'];
......@@ -290,7 +291,7 @@ class Session {
$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
......@@ -52,9 +52,9 @@ class View {
//
// 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;
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment