first commit of 2.0

This commit is contained in:
Taylor Otwell
2011-08-18 19:56:29 -05:00
parent 119b356bde
commit 1e90e42404
79 changed files with 796 additions and 688 deletions

81
laravel/arr.php Normal file
View File

@@ -0,0 +1,81 @@
<?php namespace Laravel;
class Arr {
/**
* Get an item from an array.
*
* If the specified key is null, the entire array will be returned. The array may
* also be accessed using JavaScript "dot" style notation. Retrieving items nested
* in multiple arrays is also supported.
*
* <code>
* // Returns "taylor"
* Arr::get(array('name' => array('is' => 'Taylor')), 'name.is');
* </code>
*
* @param array $array
* @param string $key
* @param mixed $default
* @return mixed
*/
public static function get($array, $key, $default = null)
{
if (is_null($key)) return $array;
foreach (explode('.', $key) as $segment)
{
if ( ! is_array($array) or ! array_key_exists($segment, $array))
{
return is_callable($default) ? call_user_func($default) : $default;
}
$array = $array[$segment];
}
return $array;
}
/**
* Set an array item to a given value.
*
* This method is primarly helpful for setting the value in an array with
* a variable depth, such as configuration arrays.
*
* If the specified item doesn't exist, it will be created. If the item's
* parents do no exist, they will also be created as arrays.
*
* Like the Arr::get method, JavaScript "dot" syntax is supported.
*
* <code>
* // Set "name.is" to "taylor"
* Arr::set(array('name' => array('is' => 'something')), 'name.is', 'taylor');
* </code>
*
* @param array $array
* @param string $key
* @param mixed $value
* @return void
*/
public static function set(&$array, $key, $value)
{
if (is_null($key)) return $array = $value;
$keys = explode('.', $key);
while (count($keys) > 1)
{
$key = array_shift($keys);
if ( ! isset($array[$key]) or ! is_array($array[$key]))
{
$array[$key] = array();
}
$array =& $array[$key];
}
$array[array_shift($keys)] = $value;
}
}

360
laravel/asset.php Normal file
View File

@@ -0,0 +1,360 @@
<?php namespace Laravel;
use Laravel\File;
use Laravel\HTML;
class Asset {
/**
* All of the instantiated asset containers.
*
* Asset containers are created through the container method, and are singletons.
*
* @var array
*/
public static $containers = array();
/**
* Get an asset container instance.
*
* If no container name is specified, the default container will be returned.
* Containers provide a convenient method of grouping assets while maintaining
* expressive code and a clean API.
*
* <code>
* // Get the default asset container
* $container = Asset::container();
*
* // Get the "footer" asset container
* $container = Asset::container('footer');
* </code>
*
* @param string $container
* @return Asset_Container
*/
public static function container($container = 'default')
{
if ( ! isset(static::$containers[$container]))
{
static::$containers[$container] = new Asset_Container($container);
}
return static::$containers[$container];
}
/**
* Magic Method for calling methods on the default Asset container.
*
* This provides a convenient API, allowing the develop to skip the "container"
* method when using the default container.
*
* <code>
* // Add an asset to the default container
* Asset::add('jquery', 'js/jquery.js');
*
* // Equivalent statement using the container method
* Asset::container()->add('jquery', 'js/jquery.js');
* </code>
*/
public static function __callStatic($method, $parameters)
{
return call_user_func_array(array(static::container(), $method), $parameters);
}
}
class Asset_Container {
/**
* The asset container name.
*
* This name may be used to access the container instance via the Asset::container method.
*
* @var string
*/
public $name;
/**
* All of the registered assets.
*
* @var array
*/
public $assets = array();
/**
* Create a new asset container instance.
*
* @param string $name
* @return void
*/
public function __construct($name)
{
$this->name = $name;
}
/**
* Add an asset to the container.
*
* The extension of the asset source will be used to determine the type of
* asset being registered (CSS or JavaScript). If you are using a non-standard
* extension, you may use the style or script methods to register assets.
*
* You may also specify asset dependencies. This will instruct the class to
* only link to the registered asset after its dependencies have been linked.
* For example, you may wish to make jQuery UI dependent on jQuery.
*
* <code>
* // Add an asset to the container
* Asset::container()->add('jquery', 'js/jquery.js');
*
* // Add an asset that is dependent on another asset
* Asset::container()->add('jquery-ui', 'js/jquery-ui.js', array('jquery'));
* </code>
*
* @param string $name
* @param string $source
* @param array $dependencies
* @param array $attributes
* @return void
*/
public function add($name, $source, $dependencies = array(), $attributes = array())
{
$type = (File::extension($source) == 'css') ? 'style' : 'script';
return call_user_func(array($this, $type), $name, $source, $dependencies, $attributes);
}
/**
* Add CSS to the registered assets.
*
* @param string $name
* @param string $source
* @param array $dependencies
* @param array $attributes
* @return void
*/
public function style($name, $source, $dependencies = array(), $attributes = array())
{
if ( ! array_key_exists('media', $attributes))
{
$attributes['media'] = 'all';
}
$this->register('style', $name, $source, $dependencies, $attributes);
}
/**
* Add JavaScript to the registered assets.
*
* @param string $name
* @param string $source
* @param array $dependencies
* @param array $attributes
* @return void
*/
public function script($name, $source, $dependencies = array(), $attributes = array())
{
$this->register('script', $name, $source, $dependencies, $attributes);
}
/**
* Add an asset to the array of registered assets.
*
* Assets are organized in the array by type (CSS or JavaScript).
*
* @param string $type
* @param string $name
* @param string $source
* @param array $dependencies
* @param array $attributes
* @return void
*/
private function register($type, $name, $source, $dependencies, $attributes)
{
$dependencies = (array) $dependencies;
$this->assets[$type][$name] = compact('source', 'dependencies', 'attributes');
}
/**
* Get the links to all of the registered CSS assets.
*
* <code>
* echo Asset::container()->styles();
* </code>
*
* @return string
*/
public function styles()
{
return $this->get_group('style');
}
/**
* Get the links to all of the registered JavaScript assets.
*
* <code>
* echo Asset::container()->scripts();
* </code>
*
* @return string
*/
public function scripts()
{
return $this->get_group('script');
}
/**
* Get all of the registered assets for a given type / group.
*
* @param string $group
* @return string
*/
private function get_group($group)
{
if ( ! isset($this->assets[$group]) or count($this->assets[$group]) == 0) return '';
$assets = '';
foreach ($this->arrange($this->assets[$group]) as $name => $data)
{
$assets .= $this->get_asset($group, $name);
}
return $assets;
}
/**
* Get the link to a single registered CSS asset.
*
* <code>
* echo Asset::container()->get_style('common');
* </code>
*
* @param string $name
* @return string
*/
public function get_style($name)
{
return $this->get_asset('style', $name);
}
/**
* Get the link to a single registered JavaScript asset.
*
* <code>
* echo Asset::container()->get_script('jquery');
* </code>
*
* @param string $name
* @return string
*/
public function get_script($name)
{
return $this->get_asset('script', $name);
}
/**
* Get the HTML link to a registered asset.
*
* @param string $group
* @param string $name
* @return string
*/
private function get_asset($group, $name)
{
if ( ! isset($this->assets[$group][$name])) return '';
$asset = $this->assets[$group][$name];
return HTML::$group($asset['source'], $asset['attributes']);
}
/**
* Sort and retrieve assets based on their dependencies
*
* @param array $assets
* @return array
*/
private function arrange($assets)
{
list($original, $sorted) = array($assets, array());
while (count($assets) > 0)
{
foreach ($assets as $asset => $value)
{
$this->evaluate_asset($asset, $value, $original, $sorted, $assets);
}
}
return $sorted;
}
/**
* Evaluate an asset and its dependencies.
*
* @param string $asset
* @param string $value
* @param array $original
* @param array $sorted
* @param array $assets
* @return void
*/
private function evaluate_asset($asset, $value, $original, &$sorted, &$assets)
{
// If the asset has no more dependencies, we can add it to the sorted list
// and remove it from the array of assets. Otherwise, we will not verify
// the asset's dependencies and determine if they have already been sorted.
if (count($assets[$asset]['dependencies']) == 0)
{
$sorted[$asset] = $value;
unset($assets[$asset]);
}
else
{
foreach ($assets[$asset]['dependencies'] as $key => $dependency)
{
if ( ! $this->dependency_is_valid($asset, $dependency, $original, $assets))
{
unset($assets[$asset]['dependencies'][$key]);
continue;
}
// If the dependency has not yet been added to the sorted list, we can not
// remove it from this asset's array of dependencies. We'll try again on
// the next trip through the loop.
if ( ! isset($sorted[$dependency])) continue;
unset($assets[$asset]['dependencies'][$key]);
}
}
}
/**
* Verify that an asset's dependency is valid.
*
* A dependency is considered valid if it exists, is not a circular reference, and is
* not a reference to the owning asset itself.
*
* @param string $asset
* @param string $dependency
* @param array $original
* @param array $assets
* @return bool
*/
private function dependency_is_valid($asset, $dependency, $original, $assets)
{
if ( ! isset($original[$dependency])) return false;
if ($dependency === $asset)
{
throw new \Exception("Asset [$asset] is dependent on itself.");
}
elseif (isset($assets[$dependency]) and in_array($asset, $assets[$dependency]['dependencies']))
{
throw new \Exception("Assets [$asset] and [$dependency] have a circular dependency.");
}
}
}

132
laravel/auth.php Normal file
View File

@@ -0,0 +1,132 @@
<?php namespace Laravel;
if (Config::get('session.driver') == '')
{
throw new \Exception("You must specify a session driver before using the Auth class.");
}
class Auth {
/**
* The current user of the application.
*
* If no user is logged in, this will be NULL. Otherwise, it will contain the result
* of the "by_id" closure in the authentication configuration file.
*
* Typically, the user should be accessed via the "user" method.
*
* @var object
*/
public static $user;
/**
* The key used to store the user ID in the session.
*
* @var string
*/
protected static $key = 'laravel_user_id';
/**
* Determine if the current user of the application is authenticated.
*
* @see login()
* @return bool
*/
public static function check()
{
return ! is_null(static::user());
}
/**
* Get the current user of the application.
*
* To retrieve the user, the user ID stored in the session will be passed to
* the "by_id" closure in the authentication configuration file. The result
* of the closure will be cached and returned.
*
* <code>
* $email = Auth::user()->email;
* </code>
*
* @return object
*/
public static function user()
{
if (is_null(static::$user) and Session::has(static::$key))
{
static::$user = call_user_func(Config::get('auth.by_id'), Session::get(static::$key));
}
return static::$user;
}
/**
* Attempt to log a user into your application.
*
* If the user credentials are valid. The user's ID will be stored in the session and the
* user will be considered "logged in" on subsequent requests to the application.
*
* The password passed to the method should be plain text, as it will be hashed
* by the Hash class when authenticating.
*
* <code>
* if (Auth::login('email@example.com', 'password'))
* {
* // The credentials are valid and the user is now logged in.
* }
* </code>
*
* @param string $username
* @param string $password
* @return bool
*/
public static function login($username, $password)
{
if ( ! is_null($user = call_user_func(Config::get('auth.by_username'), $username)))
{
if (Hash::check($password, $user->password))
{
static::remember($user);
return true;
}
}
return false;
}
/**
* Log a user into your application.
*
* The user's ID will be stored in the session and the user will be considered
* "logged in" on subsequent requests to your application. This method is called
* by the login method after determining a user's credentials are valid.
*
* Note: The user given to this method should be an object having an "id" property.
*
* @param object $user
* @return void
*/
public static function remember($user)
{
static::$user = $user;
Session::put(static::$key, $user->id);
}
/**
* Log the user out of your application.
*
* The user ID will be removed from the session and the user will no longer
* be considered logged in on subsequent requests to your application.
*
* @return void
*/
public static function logout()
{
static::$user = null;
Session::forget(static::$key);
}
}

52
laravel/benchmark.php Normal file
View File

@@ -0,0 +1,52 @@
<?php namespace Laravel;
class Benchmark {
/**
* All of the benchmark starting times.
*
* @var array
*/
public static $marks = array();
/**
* Start a benchmark.
*
* After starting a benchmark, the elapsed time in milliseconds can be
* retrieved using the "check" method.
*
* @param string $name
* @return void
*/
public static function start($name)
{
static::$marks[$name] = microtime(true);
}
/**
* Get the elapsed time in milliseconds since starting a benchmark.
*
* @param string $name
* @return float
*/
public static function check($name)
{
if (array_key_exists($name, static::$marks))
{
return number_format((microtime(true) - static::$marks[$name]) * 1000, 2);
}
return 0.0;
}
/**
* Get the total memory usage in megabytes.
*
* @return float
*/
public static function memory()
{
return number_format(memory_get_usage() / 1024 / 1024, 2);
}
}

70
laravel/cache.php Normal file
View File

@@ -0,0 +1,70 @@
<?php namespace Laravel;
class Cache {
/**
* All of the active cache drivers.
*
* @var Cache\Driver
*/
public static $drivers = array();
/**
* Get a cache driver instance.
*
* If no driver name is specified, the default cache driver will be returned
* as defined in the cache configuration file.
*
* <code>
* // Get the default cache driver
* $driver = Cache::driver();
*
* // Get the APC cache driver
* $apc = Cache::driver('apc');
* </code>
*
* @param string $driver
* @return Cache\Driver
*/
public static function driver($driver = null)
{
if (is_null($driver)) $driver = Config::get('cache.driver');
if ( ! array_key_exists($driver, static::$drivers))
{
switch ($driver)
{
case 'file':
return static::$drivers[$driver] = new Cache\File;
case 'memcached':
return static::$drivers[$driver] = new Cache\Memcached;
case 'apc':
return static::$drivers[$driver] = new Cache\APC;
default:
throw new \Exception("Cache driver [$driver] is not supported.");
}
}
return static::$drivers[$driver];
}
/**
* Pass all other methods to the default driver.
*
* Passing method calls to the driver instance provides a convenient API for the developer
* when always using the default cache driver.
*
* <code>
* // Get an item from the default cache driver
* $name = Cache::get('name');
* </code>
*/
public static function __callStatic($method, $parameters)
{
return call_user_func_array(array(static::driver(), $method), $parameters);
}
}

29
laravel/cache/apc.php vendored Normal file
View File

@@ -0,0 +1,29 @@
<?php namespace Laravel\Cache;
use Laravel\Config;
class APC extends Driver {
public function has($key)
{
return ( ! is_null($this->get($key)));
}
public function get($key, $default = null)
{
$item = ( ! is_null($cache = apc_fetch(Config::get('cache.key').$key))) ? $cache : null;
return $this->prepare($item, $default);
}
public function put($key, $value, $minutes)
{
apc_store(Config::get('cache.key').$key, $value, $minutes * 60);
}
public function forget($key)
{
apc_delete(Config::get('cache.key').$key);
}
}

93
laravel/cache/driver.php vendored Normal file
View File

@@ -0,0 +1,93 @@
<?php namespace Laravel\Cache;
abstract class Driver {
/**
* Determine if an item exists in the cache.
*
* @param string $key
* @return bool
*/
abstract public function has($key);
/**
* Get an item from the cache.
*
* A default value may also be specified, and will be returned in the requested
* item does not exist in the cache.
*
* <code>
* // Get the "name" item from the cache
* $name = Cache::driver()->get('name');
*
* // Get the "name" item from the cache or return "Fred"
* $name = Cache::driver()->get('name', 'Fred');
* </code>
*
* @param string $key
* @param mixed $default
* @param string $driver
* @return mixed
*/
abstract public function get($key, $default = null);
/**
* Prepare the cache item for returning to the requestor.
*
* If the item is NULL, the default will be returned.
*
* @param mixed $item
* @param mixed $default
* @return mixed
*/
protected function prepare($item, $default)
{
if ( ! is_null($item)) return $item;
return (is_callable($default)) ? call_user_func($default) : $default;
}
/**
* Write an item to the cache.
*
* @param string $key
* @param mixed $value
* @param int $minutes
* @return void
*/
abstract public function put($key, $value, $minutes);
/**
* 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.
*
* <code>
* // Get the "name" item from the cache or store "Fred" for 30 minutes
* $name = Cache::driver()->remember('name', 'Fred', 30);
* </code>
*
* @param string $key
* @param mixed $default
* @param int $minutes
* @return mixed
*/
public function remember($key, $value, $minutes)
{
if ( ! is_null($item = $this->get($key, null, $driver))) return $item;
$default = is_callable($default) ? call_user_func($default) : $default;
$this->put($key, $default, $minutes);
return $default;
}
/**
* Delete an item from the cache.
*
* @param string $key
* @return void
*/
abstract public function forget($key);
}

41
laravel/cache/file.php vendored Normal file
View File

@@ -0,0 +1,41 @@
<?php namespace Laravel\Cache;
class File extends Driver {
public function has($key)
{
return ( ! is_null($this->get($key)));
}
public function get($key, $default = null)
{
if ( ! file_exists(CACHE_PATH.$key))
{
return $this->prepare(null, $default);
}
$cache = file_get_contents(CACHE_PATH.$key);
// The cache expiration date is stored as a UNIX timestamp at the beginning
// of the cache file. We'll extract it out and check it here.
if (time() >= substr($cache, 0, 10))
{
$this->forget($key);
return $this->prepare(null, $default);
}
return $this->prepare(unserialize(substr($cache, 10)), $default);
}
public function put($key, $value, $minutes)
{
file_put_contents(CACHE_PATH.$key, (time() + ($minutes * 60)).serialize($value), LOCK_EX);
}
public function forget($key)
{
@unlink(CACHE_PATH.$key);
}
}

30
laravel/cache/memcached.php vendored Normal file
View File

@@ -0,0 +1,30 @@
<?php namespace Laravel\Cache;
use Laravel\Config;
use Laravel\Memcached as Mem;
class Memcached extends Driver {
public function has($key)
{
return ( ! is_null($this->get($key)));
}
public function get($key, $default = null)
{
$item = (($cache = Mem::instance()->get(Config::get('cache.key').$key)) !== false) ? $cache : null;
return $this->prepare($item, $default);
}
public function put($key, $value, $minutes)
{
Mem::instance()->set(Config::get('cache.key').$key, $value, 0, $minutes * 60);
}
public function forget($key)
{
Mem::instance()->delete(Config::get('cache.key').$key);
}
}

170
laravel/config.php Normal file
View File

@@ -0,0 +1,170 @@
<?php namespace Laravel;
class Config {
/**
* All of the loaded configuration items.
*
* The configuration arrays are keyed by module and file names.
*
* @var array
*/
public static $items = array();
/**
* Determine if a configuration item or file exists.
*
* <code>
* // Determine if the "session" configuration file exists
* Config::has('session');
*
* // Determine if the application timezone option exists
* Config::has('application.timezone');
* </code>
*
* @param string $key
* @return bool
*/
public static function has($key)
{
return ! is_null(static::get($key));
}
/**
* Get a configuration item.
*
* Configuration items are retrieved using "dot" notation. So, asking for the
* "application.timezone" configuration item would return the "timezone" option
* from the "application" configuration file.
*
* If the name of a configuration file is passed without specifying an item, the
* entire configuration array will be returned.
*
* <code>
* // Get the timezone option from the application configuration file
* $timezone = Config::get('application.timezone');
*
* // Get the SQLite database connection configuration
* $sqlite = Config::get('db.connections.sqlite');
* </code>
*
* @param string $key
* @param string $default
* @return array
*/
public static function get($key, $default = null)
{
list($module, $file, $key) = static::parse($key);
if ( ! static::load($module, $file))
{
return is_callable($default) ? call_user_func($default) : $default;
}
if (is_null($key)) return static::$items[$module][$file];
return Arr::get(static::$items[$module][$file], $key, $default);
}
/**
* Set a configuration item.
*
* Like the get method, "dot" notation is used to set items, and setting items
* at any depth in the configuration array is supported.
*
* If a specific configuration item is not specified, the entire configuration
* array will be replaced with the given value.
*
* <code>
* // Set the timezone option in the application configuration file
* Config::set('application.timezone', 'America/Chicago');
*
* // Set the session configuration array
* Config::set('session', array());
* </code>
*
* @param string $key
* @param mixed $value
* @return void
*/
public static function set($key, $value)
{
list($module, $file, $key) = static::parse($key);
if ( ! static::load($module, $file))
{
throw new \Exception("Error setting configuration option. Configuration file [$file] is not defined.");
}
Arr::set(static::$items[$module][$file], $key, $value);
}
/**
* Parse a configuration key and return its module, file, and key segments.
*
* Modular configuration keys follow a {module}::{file}.{key} convention.
*
* @param string $key
* @return array
*/
private static function parse($key)
{
list($module, $key) = Module::parse($key);
$segments = explode('.', $key);
return array($module, $segments[0], (count($segments) > 1) ? implode('.', array_slice($segments, 1)) : null);
}
/**
* Load all of the configuration items from a module configuration file.
*
* If the configuration file has already been loaded, it will not be loaded again.
*
* @param string $file
* @param string $module
* @return bool
*/
private static function load($module, $file)
{
if (isset(static::$items[$module]) and array_key_exists($file, static::$items[$module])) return true;
$config = array();
foreach (static::paths($module, $file) as $directory)
{
$config = (file_exists($path = $directory.$file.EXT)) ? array_merge($config, require $path) : $config;
}
if (count($config) > 0) static::$items[$module][$file] = $config;
return isset(static::$items[$module][$file]);
}
/**
* Get the path hierarchy for a given configuration file and module.
*
* The paths returned by this method paths will be searched by the load method when merging
* configuration files, meaning the configuration files will cascade in this order.
*
* By default, the base configuration directory will be searched first, followed by the configuration
* directory for the active module. Next, any environment specific configuration directories
* will be searched.
*
* @param string $module
* @param string $file
* @return array
*/
private static function paths($module, $file)
{
$paths = array(CONFIG_PATH, Module::path($module).'config/');
if (isset($_SERVER['LARAVEL_ENV']))
{
$paths[] = Module::path($module).'/config/'.$_SERVER['LARAVEL_ENV'].'/';
}
return $paths;
}
}

75
laravel/cookie.php Normal file
View File

@@ -0,0 +1,75 @@
<?php namespace Laravel;
class Cookie {
/**
* Determine if a cookie exists.
*
* @param string $name
* @return bool
*/
public static function has($name)
{
return ! is_null(static::get($name));
}
/**
* Get the value of a cookie.
*
* @param string $name
* @param mixed $default
* @return string
*/
public static function get($name, $default = null)
{
return Arr::get($_COOKIE, $name, $default);
}
/**
* Set a "permanent" cookie. The cookie will last 5 years.
*
* @param string $name
* @param string $value
* @param string $path
* @param string $domain
* @param bool $secure
* @param bool $http_only
* @return bool
*/
public static function forever($name, $value, $path = '/', $domain = null, $secure = false, $http_only = false)
{
return static::put($name, $value, 2628000, $path, $domain, $secure, $http_only);
}
/**
* Set the value of a cookie. If a negative number of minutes is
* specified, the cookie will be deleted.
*
* @param string $name
* @param string $value
* @param int $minutes
* @param string $path
* @param string $domain
* @param bool $secure
* @param bool $http_only
* @return bool
*/
public static function put($name, $value, $minutes = 0, $path = '/', $domain = null, $secure = false, $http_only = false)
{
if ($minutes < 0) unset($_COOKIE[$name]);
return setcookie($name, $value, ($minutes != 0) ? time() + ($minutes * 60) : 0, $path, $domain, $secure, $http_only);
}
/**
* Delete a cookie.
*
* @param string $name
* @return bool
*/
public static function forget($name)
{
return static::put($name, null, -60);
}
}

118
laravel/crypter.php Normal file
View File

@@ -0,0 +1,118 @@
<?php namespace Laravel;
class Crypter {
/**
* The encryption cipher.
*
* @var string
*/
public $cipher;
/**
* The encryption mode.
*
* @var string
*/
public $mode;
/**
* Create a new Crypter instance.
*
* @param string $cipher
* @param string $mode
* @return void
*/
public function __construct($cipher = 'rijndael-256', $mode = 'cbc')
{
$this->cipher = $cipher;
$this->mode = $mode;
}
/**
* Create a new Crypter instance.
*
* @param string $cipher
* @param string $mode
* @return Crypt
*/
public static function make($cipher = 'rijndael-256', $mode = 'cbc')
{
return new static($cipher, $mode);
}
/**
* Encrypt a value using the MCrypt library.
*
* @param string $value
* @return string
*/
public function encrypt($value)
{
$iv = mcrypt_create_iv($this->iv_size(), $this->randomizer());
return base64_encode($iv.mcrypt_encrypt($this->cipher, $this->key(), $value, $this->mode, $iv));
}
/**
* Get the random number source available to the OS.
*
* @return int
*/
protected function randomizer()
{
if (defined('MCRYPT_DEV_URANDOM'))
{
return MCRYPT_DEV_URANDOM;
}
elseif (defined('MCRYPT_DEV_RANDOM'))
{
return MCRYPT_DEV_RANDOM;
}
return MCRYPT_RAND;
}
/**
* Decrypt a value using the MCrypt library.
*
* @param string $value
* @return string
*/
public function decrypt($value)
{
if ( ! is_string($value = base64_decode($value, true)))
{
throw new \Exception('Decryption error. Input value is not valid base64 data.');
}
list($iv, $value) = array(substr($value, 0, $this->iv_size()), substr($value, $this->iv_size()));
return rtrim(mcrypt_decrypt($this->cipher, $this->key(), $value, $this->mode, $iv), "\0");
}
/**
* Get the application key from the application configuration file.
*
* @return string
*/
private function key()
{
if ( ! is_null($key = Config::get('application.key')) and $key !== '') return $key;
throw new \Exception("The encryption class can not be used without an encryption key.");
}
/**
* Get the input vector size for the cipher and mode.
*
* Different ciphers and modes use varying lengths of input vectors.
*
* @return int
*/
private function iv_size()
{
return mcrypt_get_iv_size($this->cipher, $this->mode);
}
}

58
laravel/db.php Normal file
View File

@@ -0,0 +1,58 @@
<?php namespace Laravel;
class DB {
/**
* The established database connections.
*
* @var array
*/
public static $connections = array();
/**
* Get a database connection. If no database name is specified, the default
* connection will be returned as defined in the db configuration file.
*
* Note: Database connections are managed as singletons.
*
* @param string $connection
* @return DB\Connection
*/
public static function connection($connection = null)
{
if (is_null($connection)) $connection = Config::get('db.default');
if ( ! array_key_exists($connection, static::$connections))
{
if (is_null($config = Config::get('db.connections.'.$connection)))
{
throw new \Exception("Database connection [$connection] is not defined.");
}
static::$connections[$connection] = new DB\Connection($connection, (object) $config, new DB\Connector);
}
return static::$connections[$connection];
}
/**
* Begin a fluent query against a table.
*
* @param string $table
* @param string $connection
* @return DB\Query
*/
public static function table($table, $connection = null)
{
return static::connection($connection)->table($table);
}
/**
* Magic Method for calling methods on the default database connection.
*/
public static function __callStatic($method, $parameters)
{
return call_user_func_array(array(static::connection(), $method), $parameters);
}
}

137
laravel/db/connection.php Normal file
View File

@@ -0,0 +1,137 @@
<?php namespace Laravel\DB;
class Connection {
/**
* The connection name.
*
* @var string
*/
public $name;
/**
* The connection configuration.
*
* @var array
*/
public $config;
/**
* The PDO connection.
*
* @var PDO
*/
public $pdo;
/**
* All of the queries that have been executed on the connection.
*
* @var array
*/
public $queries = array();
/**
* Create a new Connection instance.
*
* @param string $name
* @param object $config
* @param Connector $connector
* @return void
*/
public function __construct($name, $config, $connector)
{
$this->name = $name;
$this->config = $config;
$this->pdo = $connector->connect($this->config);
}
/**
* Execute a SQL query against the connection and return the first result.
*
* @param string $sql
* @param array $bindings
* @return object
*/
public function first($sql, $bindings = array())
{
return (count($results = $this->query($sql, $bindings)) > 0) ? $results[0] : null;
}
/**
* Execute a SQL query against the connection.
*
* The method returns the following based on query type:
*
* SELECT -> Array of stdClasses
* UPDATE -> Number of rows affected.
* DELETE -> Number of Rows affected.
* ELSE -> Boolean true / false depending on success.
*
* @param string $sql
* @param array $bindings
* @return array
*/
public function query($sql, $bindings = array())
{
$this->queries[] = $sql;
$query = $this->pdo->prepare($sql);
$result = $query->execute($bindings);
if (strpos(strtoupper($sql), 'SELECT') === 0)
{
return $query->fetchAll(\PDO::FETCH_CLASS, 'stdClass');
}
elseif (strpos(strtoupper($sql), 'UPDATE') === 0 or strpos(strtoupper($sql), 'DELETE') === 0)
{
return $query->rowCount();
}
return $result;
}
/**
* Begin a fluent query against a table.
*
* @param string $table
* @return Query
*/
public function table($table)
{
return new Query($table, $this);
}
/**
* Get the keyword identifier wrapper for the connection.
*
* @return string
*/
public function wrapper()
{
if (array_key_exists('wrap', $this->config) and $this->config['wrap'] === false) return '';
return ($this->driver() == 'mysql') ? '`' : '"';
}
/**
* Get the driver name for the database connection.
*
* @return string
*/
public function driver()
{
return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
}
/**
* Get the table prefix for the database connection.
*
* @return string
*/
public function prefix()
{
return (array_key_exists('prefix', $this->config)) ? $this->config['prefix'] : '';
}
}

108
laravel/db/connector.php Normal file
View File

@@ -0,0 +1,108 @@
<?php namespace Laravel\DB;
use Laravel\Config;
class Connector {
/**
* The PDO connection options.
*
* @var array
*/
public $options = array(
\PDO::ATTR_CASE => \PDO::CASE_LOWER,
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_ORACLE_NULLS => \PDO::NULL_NATURAL,
\PDO::ATTR_STRINGIFY_FETCHES => false,
\PDO::ATTR_EMULATE_PREPARES => false,
);
/**
* Establish a PDO database connection.
*
* @param object $connection
* @return PDO
*/
public function connect($config)
{
switch ($config->driver)
{
case 'sqlite':
return $this->connect_to_sqlite($config);
case 'mysql':
case 'pgsql':
return $this->connect_to_server($config);
default:
return $this->connect_to_generic($config);
}
throw new \Exception('Database driver '.$config->driver.' is not supported.');
}
/**
* Establish a PDO connection to a SQLite database.
*
* SQLite database paths can be specified either relative to the application/db
* directory, or as an absolute path to any location on the file system. In-memory
* databases are also supported.
*
* @param object $config
* @return PDO
*/
private function connect_to_sqlite($config)
{
if ($config->database == ':memory:')
{
return new \PDO('sqlite::memory:', null, null, $this->options);
}
elseif (file_exists($path = DATABASE_PATH.$config->database.'.sqlite'))
{
return new \PDO('sqlite:'.$path, null, null, $this->options);
}
elseif (file_exists($config->database))
{
return new \PDO('sqlite:'.$config->database, null, null, $this->options);
}
throw new \Exception("SQLite database [".$config->database."] could not be found.");
}
/**
* Connect to a MySQL or PostgreSQL database server.
*
* @param object $config
* @return PDO
*/
private function connect_to_server($config)
{
$dsn = $config->driver.':host='.$config->host.';dbname='.$config->database;
if (isset($config->port))
{
$dsn .= ';port='.$config->port;
}
$connection = new \PDO($dsn, $config->username, $config->password, $this->options);
if (isset($config->charset))
{
$connection->prepare("SET NAMES '".$config->charset."'")->execute();
}
return $connection;
}
/**
* Connect to a generic data source.
*
* @param object $config
* @return PDO
*/
private function connect_to_generic($config)
{
return new \PDO($config->driver.':'.$config->dsn, $config->username, $config->password, $this->options);
}
}

View File

@@ -0,0 +1,203 @@
<?php namespace System\DB\Eloquent;
class Hydrator {
/**
* Load the array of hydrated models and their eager relationships.
*
* @param Model $eloquent
* @return array
*/
public static function hydrate($eloquent)
{
$results = static::base(get_class($eloquent), $eloquent->query->get());
if (count($results) > 0)
{
foreach ($eloquent->includes as $include)
{
if ( ! method_exists($eloquent, $include))
{
throw new \Exception("Attempting to eager load [$include], but the relationship is not defined.");
}
static::eagerly($eloquent, $results, $include);
}
}
return $results;
}
/**
* Hydrate the base models for a query.
*
* The resulting model array is keyed by the primary keys of the models.
* This allows the models to easily be matched to their children.
*
* @param string $class
* @param array $results
* @return array
*/
private static function base($class, $results)
{
$models = array();
foreach ($results as $result)
{
$model = new $class;
$model->attributes = (array) $result;
$model->exists = true;
$models[$model->id] = $model;
}
return $models;
}
/**
* Eagerly load a relationship.
*
* @param object $eloquent
* @param array $parents
* @param string $include
* @return void
*/
private static function eagerly($eloquent, &$parents, $include)
{
// We temporarily spoof the belongs_to key to allow the query to be fetched without
// any problems, since the belongs_to method actually gets the attribute.
$eloquent->attributes[$spoof = $include.'_id'] = 0;
$relationship = $eloquent->$include();
unset($eloquent->attributes[$spoof]);
// Reset the WHERE clause and bindings on the query. We'll add our own WHERE clause soon.
// This will allow us to load a range of related models instead of only one.
$relationship->query->reset_where();
// Initialize the relationship attribute on the parents. As expected, "many" relationships
// are initialized to an array and "one" relationships are initialized to null.
foreach ($parents as &$parent)
{
$parent->ignore[$include] = (in_array($eloquent->relating, array('has_many', 'has_and_belongs_to_many'))) ? array() : null;
}
if (in_array($relating = $eloquent->relating, array('has_one', 'has_many', 'belongs_to')))
{
return static::$relating($relationship, $parents, $eloquent->relating_key, $include);
}
else
{
static::has_and_belongs_to_many($relationship, $parents, $eloquent->relating_key, $eloquent->relating_table, $include);
}
}
/**
* Eagerly load a 1:1 relationship.
*
* @param object $relationship
* @param array $parents
* @param string $relating_key
* @param string $relating
* @param string $include
* @return void
*/
private static function has_one($relationship, &$parents, $relating_key, $include)
{
foreach ($relationship->where_in($relating_key, array_keys($parents))->get() as $key => $child)
{
$parents[$child->$relating_key]->ignore[$include] = $child;
}
}
/**
* Eagerly load a 1:* relationship.
*
* @param object $relationship
* @param array $parents
* @param string $relating_key
* @param string $relating
* @param string $include
* @return void
*/
private static function has_many($relationship, &$parents, $relating_key, $include)
{
foreach ($relationship->where_in($relating_key, array_keys($parents))->get() as $key => $child)
{
$parents[$child->$relating_key]->ignore[$include][$child->id] = $child;
}
}
/**
* Eagerly load a 1:1 belonging relationship.
*
* @param object $relationship
* @param array $parents
* @param string $relating_key
* @param string $include
* @return void
*/
private static function belongs_to($relationship, &$parents, $relating_key, $include)
{
$keys = array();
foreach ($parents as &$parent)
{
$keys[] = $parent->$relating_key;
}
$children = $relationship->where_in('id', array_unique($keys))->get();
foreach ($parents as &$parent)
{
if (array_key_exists($parent->$relating_key, $children))
{
$parent->ignore[$include] = $children[$parent->$relating_key];
}
}
}
/**
* Eagerly load a many-to-many relationship.
*
* @param object $relationship
* @param array $parents
* @param string $relating_key
* @param string $relating_table
* @param string $include
*
* @return void
*/
private static function has_and_belongs_to_many($relationship, &$parents, $relating_key, $relating_table, $include)
{
// The model "has and belongs to many" method sets the SELECT clause; however, we need
// to clear it here since we will be adding the foreign key to the select.
$relationship->query->select = null;
$relationship->query->where_in($relating_table.'.'.$relating_key, array_keys($parents));
// The foreign key is added to the select to allow us to easily match the models back to their parents.
// Otherwise, there would be no apparent connection between the models to allow us to match them.
$children = $relationship->query->get(array(Model::table(get_class($relationship)).'.*', $relating_table.'.'.$relating_key));
$class = get_class($relationship);
foreach ($children as $child)
{
$related = new $class;
$related->attributes = (array) $child;
$related->exists = true;
// Remove the foreign key since it was only added to the query to help match the models.
unset($related->attributes[$relating_key]);
$parents[$child->$relating_key]->ignore[$include][$child->id] = $related;
}
}
}

View File

@@ -0,0 +1,520 @@
<?php namespace System\DB\Eloquent;
use System\DB;
use System\Str;
use System\Config;
use System\Inflector;
use System\Paginator;
abstract class Model {
/**
* The connection that should be used for the model.
*
* @var string
*/
public static $connection;
/**
* The model query instance.
*
* @var Query
*/
public $query;
/**
* Indicates if the model exists in the database.
*
* @var bool
*/
public $exists = false;
/**
* The model's attributes.
*
* Typically, a model has an attribute for each column on the table.
*
* @var array
*/
public $attributes = array();
/**
* The model's dirty attributes.
*
* @var array
*/
public $dirty = array();
/**
* The model's ignored attributes.
*
* Ignored attributes will not be saved to the database, and are
* primarily used to hold relationships.
*
* @var array
*/
public $ignore = array();
/**
* The relationships that should be eagerly loaded.
*
* @var array
*/
public $includes = array();
/**
* The relationship type the model is currently resolving.
*
* @var string
*/
public $relating;
/**
* The foreign key of the "relating" relationship.
*
* @var string
*/
public $relating_key;
/**
* The table name of the model being resolved.
*
* This is used during many-to-many eager loading.
*
* @var string
*/
public $relating_table;
/**
* Create a new Eloquent model instance.
*
* @param array $attributes
* @return void
*/
public function __construct($attributes = array())
{
$this->fill($attributes);
}
/**
* Set the attributes of the model using an array.
*
* @param array $attributes
* @return Model
*/
public function fill($attributes)
{
foreach ($attributes as $key => $value)
{
$this->$key = $value;
}
return $this;
}
/**
* Set the eagerly loaded models on the queryable model.
*
* @return Model
*/
private function _with()
{
$this->includes = func_get_args();
return $this;
}
/**
* Factory for creating queryable Eloquent model instances.
*
* @param string $class
* @return object
*/
public static function query($class)
{
$model = new $class;
// Since this method is only used for instantiating models for querying
// purposes, we will go ahead and set the Query instance on the model.
$model->query = DB::connection(static::$connection)->table(static::table($class));
return $model;
}
/**
* Get the table name for a model.
*
* @param string $class
* @return string
*/
public static function table($class)
{
if (property_exists($class, 'table')) return $class::$table;
return strtolower(Inflector::plural(static::model_name($class)));
}
/**
* Get an Eloquent model name without any namespaces.
*
* @param string|Model $model
* @return string
*/
public static function model_name($model)
{
$class = (is_object($model)) ? get_class($model) : $model;
$segments = array_reverse(explode('\\', $class));
return $segments[0];
}
/**
* Get all of the models from the database.
*
* @return array
*/
public static function all()
{
return Hydrator::hydrate(static::query(get_called_class()));
}
/**
* Get a model by the primary key.
*
* @param int $id
* @return mixed
*/
public static function find($id)
{
return static::query(get_called_class())->where('id', '=', $id)->first();
}
/**
* Get an array of models from the database.
*
* @return array
*/
private function _get($columns = array('*'))
{
$this->query->select($columns);
return Hydrator::hydrate($this);
}
/**
* Get the first model result
*
* @return mixed
*/
private function _first($columns = array('*'))
{
return (count($results = $this->take(1)->_get($columns)) > 0) ? reset($results) : null;
}
/**
* Get paginated model results.
*
* @param int $per_page
* @return Paginator
*/
private function _paginate($per_page = null, $columns = array('*'))
{
$total = $this->query->count();
if (is_null($per_page))
{
$per_page = (property_exists(get_class($this), 'per_page')) ? static::$per_page : 20;
}
return Paginator::make($this->select($columns)->for_page(Paginator::page($total, $per_page), $per_page)->get(), $total, $per_page);
}
/**
* Retrieve the query for a 1:1 relationship.
*
* @param string $model
* @param string $foreign_key
* @return mixed
*/
public function has_one($model, $foreign_key = null)
{
$this->relating = __FUNCTION__;
return $this->has_one_or_many($model, $foreign_key);
}
/**
* Retrieve the query for a 1:* relationship.
*
* @param string $model
* @param string $foreign_key
* @return mixed
*/
public function has_many($model, $foreign_key = null)
{
$this->relating = __FUNCTION__;
return $this->has_one_or_many($model, $foreign_key);
}
/**
* Retrieve the query for a 1:1 or 1:* relationship.
*
* The default foreign key for has one and has many relationships is the name
* of the model with an appended _id. For example, the foreign key for a
* User model would be user_id. Photo would be photo_id, etc.
*
* @param string $model
* @param string $foreign_key
* @return mixed
*/
private function has_one_or_many($model, $foreign_key)
{
$this->relating_key = (is_null($foreign_key)) ? strtolower(static::model_name($this)).'_id' : $foreign_key;
return static::query($model)->where($this->relating_key, '=', $this->id);
}
/**
* Retrieve the query for a 1:1 belonging relationship.
*
* The default foreign key for belonging relationships is the name of the
* relationship method name with _id. So, if a model has a "manager" method
* returning a belongs_to relationship, the key would be manager_id.
*
* @param string $model
* @param string $foreign_key
* @return mixed
*/
public function belongs_to($model, $foreign_key = null)
{
$this->relating = __FUNCTION__;
if ( ! is_null($foreign_key))
{
$this->relating_key = $foreign_key;
}
else
{
list(, $caller) = debug_backtrace(false);
$this->relating_key = $caller['function'].'_id';
}
return static::query($model)->where('id', '=', $this->attributes[$this->relating_key]);
}
/**
* Retrieve the query for a *:* relationship.
*
* The default foreign key for many-to-many relations is the name of the model
* with an appended _id. This is the same convention as has_one and has_many.
*
* @param string $model
* @param string $table
* @param string $foreign_key
* @param string $associated_key
* @return mixed
*/
public function has_and_belongs_to_many($model, $table = null, $foreign_key = null, $associated_key = null)
{
$this->relating = __FUNCTION__;
$this->relating_table = (is_null($table)) ? $this->intermediate_table($model) : $table;
// Allowing the overriding of the foreign and associated keys provides the flexibility for
// self-referential many-to-many relationships, such as a "buddy list".
$this->relating_key = (is_null($foreign_key)) ? strtolower(static::model_name($this)).'_id' : $foreign_key;
// The associated key is the foreign key name of the related model. So, if the related model
// is "Role", the associated key on the intermediate table would be "role_id".
$associated_key = (is_null($associated_key)) ? strtolower(static::model_name($model)).'_id' : $associated_key;
return static::query($model)
->select(array(static::table($model).'.*'))
->join($this->relating_table, static::table($model).'.id', '=', $this->relating_table.'.'.$associated_key)
->where($this->relating_table.'.'.$this->relating_key, '=', $this->id);
}
/**
* Determine the intermediate table name for a given model.
*
* By default, the intermediate table name is the plural names of the models
* arranged alphabetically and concatenated with an underscore.
*
* @param string $model
* @return string
*/
private function intermediate_table($model)
{
$models = array(Inflector::plural(static::model_name($model)), Inflector::plural(static::model_name($this)));
sort($models);
return strtolower($models[0].'_'.$models[1]);
}
/**
* Save the model to the database.
*
* @return bool
*/
public function save()
{
// If the model does not have any dirty attributes, there is no reason
// to save it to the database.
if ($this->exists and count($this->dirty) == 0) return true;
$model = get_class($this);
// Since the model was instantiated using "new", a query instance has not been set.
// Only models being used for querying have their query instances set by default.
$this->query = DB::connection(static::$connection)->table(static::table($model));
if (property_exists($model, 'timestamps') and $model::$timestamps)
{
$this->timestamp();
}
// If the model already exists in the database, we will just update it.
// Otherwise, we will insert the model and set the ID attribute.
if ($this->exists)
{
$success = ($this->query->where_id($this->attributes['id'])->update($this->dirty) === 1);
}
else
{
$success = is_numeric($this->attributes['id'] = $this->query->insert_get_id($this->attributes));
}
($this->exists = true) and $this->dirty = array();
return $success;
}
/**
* Set the creation and update timestamps on the model.
*
* @return void
*/
private function timestamp()
{
$this->updated_at = date('Y-m-d H:i:s');
if ( ! $this->exists) $this->created_at = $this->updated_at;
}
/**
* Delete a model from the database.
*
* @param int $id
* @return int
*/
public function delete($id = null)
{
// If the delete method is being called on an existing model, we only want to delete
// that model. If it is being called from an Eloquent query model, it is probably
// the developer's intention to delete more than one model, so we will pass the
// delete statement to the query instance.
if ( ! $this->exists) return $this->query->delete();
return DB::connection(static::$connection)->table(static::table(get_class($this)))->delete($this->id);
}
/**
* Magic method for retrieving model attributes.
*/
public function __get($key)
{
if (array_key_exists($key, $this->attributes))
{
return $this->attributes[$key];
}
// Is the requested item a model relationship that has already been loaded?
// All of the loaded relationships are stored in the "ignore" array.
elseif (array_key_exists($key, $this->ignore))
{
return $this->ignore[$key];
}
// Is the requested item a model relationship? If it is, we will dynamically
// load it and return the results of the relationship query.
elseif (method_exists($this, $key))
{
$query = $this->$key();
return $this->ignore[$key] = (in_array($this->relating, array('has_one', 'belongs_to'))) ? $query->first() : $query->get();
}
}
/**
* Magic Method for setting model attributes.
*/
public function __set($key, $value)
{
// If the key is a relationship, add it to the ignored attributes.
// Ignored attributes are not stored in the database.
if (method_exists($this, $key))
{
$this->ignore[$key] = $value;
}
else
{
$this->attributes[$key] = $value;
$this->dirty[$key] = $value;
}
}
/**
* Magic Method for determining if a model attribute is set.
*/
public function __isset($key)
{
return (array_key_exists($key, $this->attributes) or array_key_exists($key, $this->ignore));
}
/**
* Magic Method for unsetting model attributes.
*/
public function __unset($key)
{
unset($this->attributes[$key], $this->ignore[$key], $this->dirty[$key]);
}
/**
* Magic Method for handling dynamic method calls.
*/
public function __call($method, $parameters)
{
// To allow the "with", "get", "first", and "paginate" methods to be called both
// staticly and on an instance, we need to have private, underscored versions
// of the methods and handle them dynamically.
if (in_array($method, array('with', 'get', 'first', 'paginate')))
{
return call_user_func_array(array($this, '_'.$method), $parameters);
}
// All of the aggregate and persistance functions can be passed directly to the query
// instance. For these functions, we can simply return the response of the query.
if (in_array($method, array('insert', 'update', 'count', 'sum', 'min', 'max', 'avg')))
{
return call_user_func_array(array($this->query, $method), $parameters);
}
// Pass the method to the query instance. This allows the chaining of methods
// from the query builder, providing the same convenient query API as the
// query builder itself.
call_user_func_array(array($this->query, $method), $parameters);
return $this;
}
/**
* Magic Method for handling dynamic static method calls.
*/
public static function __callStatic($method, $parameters)
{
// Just pass the method to a model instance and let the __call method take care of it.
return call_user_func_array(array(static::query(get_called_class()), $method), $parameters);
}
}

709
laravel/db/query.php Normal file
View File

@@ -0,0 +1,709 @@
<?php namespace Laravel\DB;
use Laravel\Str;
use Laravel\Config;
use Laravel\Paginator;
class Query {
/**
* The database connection.
*
* @var Connection
*/
public $connection;
/**
* The SELECT clause.
*
* @var string
*/
public $select;
/**
* Indicates if the query should return distinct results.
*
* @var bool
*/
public $distinct = false;
/**
* The FROM clause.
*
* @var string
*/
public $from;
/**
* The table name.
*
* @var string
*/
public $table;
/**
* The WHERE clause.
*
* @var string
*/
public $where = 'WHERE 1 = 1';
/**
* The ORDER BY columns.
*
* @var array
*/
public $orderings = array();
/**
* The LIMIT value.
*
* @var int
*/
public $limit;
/**
* The OFFSET value.
*
* @var int
*/
public $offset;
/**
* The query value bindings.
*
* @var array
*/
public $bindings = array();
/**
* Create a new query instance.
*
* @param string $table
* @param Connection $connection
* @return void
*/
public function __construct($table, $connection)
{
$this->table = $table;
$this->connection = $connection;
$this->from = 'FROM '.$this->wrap($table);
}
/**
* Create a new query instance.
*
* @param string $table
* @param Connection $connection
* @return Query
*/
public static function table($table, $connection)
{
return new static($table, $connection);
}
/**
* Force the query to return distinct results.
*
* @return Query
*/
public function distinct()
{
$this->distinct = true;
return $this;
}
/**
* Add columns to the SELECT clause.
*
* @param array $columns
* @return Query
*/
public function select($columns = array('*'))
{
$this->select = ($this->distinct) ? 'SELECT DISTINCT ' : 'SELECT ';
$this->select .= implode(', ', array_map(array($this, 'wrap'), $columns));
return $this;
}
/**
* Set the FROM clause.
*
* @param string $from
* @return Query
*/
public function from($from)
{
$this->from = $from;
return $this;
}
/**
* Add a join to the query.
*
* @param string $table
* @param string $column1
* @param string $operator
* @param string $column2
* @param string $type
* @return Query
*/
public function join($table, $column1, $operator, $column2, $type = 'INNER')
{
$this->from .= ' '.$type.' JOIN '.$this->wrap($table).' ON '.$this->wrap($column1).' '.$operator.' '.$this->wrap($column2);
return $this;
}
/**
* Add a left join to the query.
*
* @param string $table
* @param string $column1
* @param string $operator
* @param string $column2
* @return Query
*/
public function left_join($table, $column1, $operator, $column2)
{
return $this->join($table, $column1, $operator, $column2, 'LEFT');
}
/**
* Reset the where clause to its initial state. All bindings will be cleared.
*
* @return void
*/
public function reset_where()
{
$this->where = 'WHERE 1 = 1';
$this->bindings = array();
}
/**
* Add a raw where condition to the query.
*
* @param string $where
* @param array $bindings
* @param string $connector
* @return Query
*/
public function raw_where($where, $bindings = array(), $connector = 'AND')
{
$this->where .= ' '.$connector.' '.$where;
$this->bindings = array_merge($this->bindings, $bindings);
return $this;
}
/**
* Add a raw or where condition to the query.
*
* @param string $where
* @param array $bindings
* @return Query
*/
public function raw_or_where($where, $bindings = array())
{
return $this->raw_where($where, $bindings, 'OR');
}
/**
* Add a where condition to the query.
*
* @param string $column
* @param string $operator
* @param mixed $value
* @param string $connector
* @return Query
*/
public function where($column, $operator, $value, $connector = 'AND')
{
$this->where .= ' '.$connector.' '.$this->wrap($column).' '.$operator.' ?';
$this->bindings[] = $value;
return $this;
}
/**
* Add an or where condition to the query.
*
* @param string $column
* @param string $operator
* @param mixed $value
* @return Query
*/
public function or_where($column, $operator, $value)
{
return $this->where($column, $operator, $value, 'OR');
}
/**
* Add a where condition for the primary key to the query.
* This is simply a short-cut method for convenience.
*
* @param mixed $value
* @return Query
*/
public function where_id($value)
{
return $this->where('id', '=', $value);
}
/**
* Add an or where condition for the primary key to the query.
* This is simply a short-cut method for convenience.
*
* @param mixed $value
* @return Query
*/
public function or_where_id($value)
{
return $this->or_where('id', '=', $value);
}
/**
* Add a where in condition to the query.
*
* @param string $column
* @param array $values
* @param string $connector
* @return Query
*/
public function where_in($column, $values, $connector = 'AND')
{
$this->where .= ' '.$connector.' '.$this->wrap($column).' IN ('.$this->parameterize($values).')';
$this->bindings = array_merge($this->bindings, $values);
return $this;
}
/**
* Add an or where in condition to the query.
*
* @param string $column
* @param array $values
* @return Query
*/
public function or_where_in($column, $values)
{
return $this->where_in($column, $values, 'OR');
}
/**
* Add a where not in condition to the query.
*
* @param string $column
* @param array $values
* @param string $connector
* @return Query
*/
public function where_not_in($column, $values, $connector = 'AND')
{
$this->where .= ' '.$connector.' '.$this->wrap($column).' NOT IN ('.$this->parameterize($values).')';
$this->bindings = array_merge($this->bindings, $values);
return $this;
}
/**
* Add an or where not in condition to the query.
*
* @param string $column
* @param array $values
* @return Query
*/
public function or_where_not_in($column, $values)
{
return $this->where_not_in($column, $values, 'OR');
}
/**
* Add a where null condition to the query.
*
* @param string $column
* @param string $connector
* @return Query
*/
public function where_null($column, $connector = 'AND')
{
$this->where .= ' '.$connector.' '.$this->wrap($column).' IS NULL';
return $this;
}
/**
* Add an or where null condition to the query.
*
* @param string $column
* @return Query
*/
public function or_where_null($column)
{
return $this->where_null($column, 'OR');
}
/**
* Add a where not null condition to the query.
*
* @param string $column
* @param string $connector
* @return Query
*/
public function where_not_null($column, $connector = 'AND')
{
$this->where .= ' '.$connector.' '.$this->wrap($column).' IS NOT NULL';
return $this;
}
/**
* Add an or where not null condition to the query.
*
* @param string $column
* @return Query
*/
public function or_where_not_null($column)
{
return $this->where_not_null($column, 'OR');
}
/**
* Add dynamic where conditions to the query.
*
* Dynamic queries are caught by the __call magic method and are parsed here.
* They provide a convenient, expressive API for building simple conditions.
*
* @param string $method
* @param array $parameters
* @return Query
*/
private function dynamic_where($method, $parameters)
{
// Strip the "where_" off of the method.
$finder = substr($method, 6);
// Split the column names from the connectors.
$segments = preg_split('/(_and_|_or_)/i', $finder, -1, PREG_SPLIT_DELIM_CAPTURE);
// The connector variable will determine which connector will be used for the condition.
// We'll change it as we come across new connectors in the dynamic method string.
//
// The index variable helps us get the correct parameter value for the where condition.
// We increment it each time we add a condition.
$connector = 'AND';
$index = 0;
foreach ($segments as $segment)
{
if ($segment != '_and_' and $segment != '_or_')
{
$this->where($segment, '=', $parameters[$index], $connector);
$index++;
}
else
{
$connector = trim(strtoupper($segment), '_');
}
}
return $this;
}
/**
* Add an ordering to the query.
*
* @param string $column
* @param string $direction
* @return Query
*/
public function order_by($column, $direction = 'asc')
{
$this->orderings[] = $this->wrap($column).' '.strtoupper($direction);
return $this;
}
/**
* Set the query offset.
*
* @param int $value
* @return Query
*/
public function skip($value)
{
$this->offset = $value;
return $this;
}
/**
* Set the query limit.
*
* @param int $value
* @return Query
*/
public function take($value)
{
$this->limit = $value;
return $this;
}
/**
* Set the limit and offset values for a given page.
*
* @param int $page
* @param int $per_page
* @return Query
*/
public function for_page($page, $per_page)
{
return $this->skip(($page - 1) * $per_page)->take($per_page);
}
/**
* Find a record by the primary key.
*
* @param int $id
* @param array $columns
* @return object
*/
public function find($id, $columns = array('*'))
{
return $this->where('id', '=', $id)->first($columns);
}
/**
* Get an aggregate value.
*
* @param string $aggregate
* @param string $column
* @return mixed
*/
private function aggregate($aggregator, $column)
{
$this->select = 'SELECT '.$aggregator.'('.$this->wrap($column).') AS '.$this->wrap('aggregate');
return $this->first()->aggregate;
}
/**
* Get paginated query results.
*
* @param int $per_page
* @param array $columns
* @return Paginator
*/
public function paginate($per_page, $columns = array('*'))
{
$total = $this->count();
return Paginator::make($this->for_page(Paginator::page($total, $per_page), $per_page)->get($columns), $total, $per_page);
}
/**
* Execute the query as a SELECT statement and return the first result.
*
* @param array $columns
* @return object
*/
public function first($columns = array('*'))
{
return (count($results = $this->take(1)->get($columns)) > 0) ? $results[0] : null;
}
/**
* Execute the query as a SELECT statement.
*
* @param array $columns
* @return array
*/
public function get($columns = array('*'))
{
if (is_null($this->select))
{
$this->select($columns);
}
$results = $this->connection->query($this->compile_select(), $this->bindings);
// Reset the SELECT clause so more queries can be performed using the same instance.
// This is helpful for getting aggregates and then getting actual results.
$this->select = null;
return $results;
}
/**
* Compile the query into a SQL SELECT statement.
*
* @return string
*/
private function compile_select()
{
$sql = $this->select.' '.$this->from.' '.$this->where;
if (count($this->orderings) > 0)
{
$sql .= ' ORDER BY '.implode(', ', $this->orderings);
}
if ( ! is_null($this->limit))
{
$sql .= ' LIMIT '.$this->limit;
}
if ( ! is_null($this->offset))
{
$sql .= ' OFFSET '.$this->offset;
}
return $sql;
}
/**
* Execute an INSERT statement.
*
* @param array $values
* @return bool
*/
public function insert($values)
{
return $this->connection->query($this->compile_insert($values), array_values($values));
}
/**
* Execute an INSERT statement and get the insert ID.
*
* @param array $values
* @return int
*/
public function insert_get_id($values)
{
$sql = $this->compile_insert($values);
if ($this->connection->driver() == 'pgsql')
{
$query = $this->connection->pdo->prepare($sql.' RETURNING '.$this->wrap('id'));
$query->execute(array_values($values));
return $query->fetch(\PDO::FETCH_CLASS, 'stdClass')->id;
}
$this->connection->query($sql, array_values($values));
return $this->connection->pdo->lastInsertId();
}
/**
* Compile the query into a SQL INSERT statement.
*
* @param array $values
* @return string
*/
private function compile_insert($values)
{
$sql = 'INSERT INTO '.$this->wrap($this->table);
$columns = array_map(array($this, 'wrap'), array_keys($values));
return $sql .= ' ('.implode(', ', $columns).') VALUES ('.$this->parameterize($values).')';
}
/**
* Execute the query as an UPDATE statement.
*
* @param array $values
* @return bool
*/
public function update($values)
{
$sql = 'UPDATE '.$this->wrap($this->table).' SET ';
foreach (array_keys($values) as $column)
{
$sets[] = $this->wrap($column).' = ?';
}
return $this->connection->query($sql.implode(', ', $sets).' '.$this->where, array_merge(array_values($values), $this->bindings));
}
/**
* Execute the query as a DELETE statement.
*
* @param int $id
* @return bool
*/
public function delete($id = null)
{
if ( ! is_null($id)) $this->where('id', '=', $id);
return $this->connection->query('DELETE FROM '.$this->wrap($this->table).' '.$this->where, $this->bindings);
}
/**
* Wrap a value in keyword identifiers.
*
* @param string $value
* @return string
*/
private function wrap($value)
{
if (strpos(strtolower($value), ' as ') !== false)
{
return $this->wrap_alias($value);
}
$wrap = $this->connection->wrapper();
foreach (explode('.', $value) as $segment)
{
$wrapped[] = ($segment != '*') ? $wrap.$segment.$wrap : $segment;
}
return implode('.', $wrapped);
}
/**
* Wrap an alias in keyword identifiers.
*
* @param string $value
* @return string
*/
private function wrap_alias($value)
{
$segments = explode(' ', $value);
return $this->wrap($segments[0]).' AS '.$this->wrap($segments[2]);
}
/**
* Create query parameters from an array of values.
*
* @param array $values
* @return string
*/
private function parameterize($values)
{
return implode(', ', array_fill(0, count($values), '?'));
}
/**
* Magic Method for handling dynamic functions.
*/
public function __call($method, $parameters)
{
if (strpos($method, 'where_') === 0)
{
return $this->dynamic_where($method, $parameters, $this);
}
if (in_array($method, array('count', 'min', 'max', 'avg', 'sum')))
{
return ($method == 'count') ? $this->aggregate(strtoupper($method), '*') : $this->aggregate(strtoupper($method), $parameters[0]);
}
throw new \Exception("Method [$method] is not defined on the Query class.");
}
}

View File

@@ -0,0 +1,94 @@
<?php namespace Laravel\Exception;
use Laravel\File;
class Examiner {
/**
* The exception being examined.
*
* @var Exception
*/
public $exception;
/**
* Human-readable error levels and descriptions.
*
* @var array
*/
private $levels = array(
0 => 'Error',
E_ERROR => 'Error',
E_WARNING => 'Warning',
E_PARSE => 'Parsing Error',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Core Error',
E_CORE_WARNING => 'Core Warning',
E_COMPILE_ERROR => 'Compile Error',
E_COMPILE_WARNING => 'Compile Warning',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Runtime Notice'
);
/**
* Create a new exception examiner instance.
*
* @param Exception $e
* @return void
*/
public function __construct($e)
{
$this->exception = $e;
}
/**
* Get a human-readable version of the exception error code.
*
* @return string
*/
public function severity()
{
if (array_key_exists($this->exception->getCode(), $this->levels))
{
return $this->levels[$this->exception->getCode()];
}
return $this->exception->getCode();
}
/**
* Get the exception error message formatted for use by Laravel.
*
* The exception file paths will be shortened, and the file name and line number
* will be added to the exception message.
*
* @return string
*/
public function message()
{
$file = str_replace(array(ACTIVE_MODULE_PATH, SYS_PATH), array('MODULE_PATH/', 'SYS_PATH/'), $this->exception->getFile());
return rtrim($this->exception->getMessage(), '.').' in '.$file.' on line '.$this->exception->getLine().'.';
}
/**
* Get the code surrounding the line where the exception occurred.
*
* @return array
*/
public function context()
{
return File::snapshot($this->exception->getFile(), $this->exception->getLine());
}
/**
* Magic Method to pass function calls to the exception.
*/
public function __call($method, $parameters)
{
return call_user_func_array(array($this->exception, $method), $parameters);
}
}

View File

@@ -0,0 +1,107 @@
<?php namespace Laravel\Exception;
use Laravel\View;
use Laravel\Config;
use Laravel\Response;
class Handler {
/**
* The exception examiner for the exception being handled.
*
* @var Examiner
*/
public $exception;
/**
* Create a new exception handler instance.
*
* @param Exception $e
* @return void
*/
public function __construct($e)
{
$this->exception = new Examiner($e);
}
/**
* Create a new exception handler instance.
*
* @param Exception $e
* @return Handler
*/
public static function make($e)
{
return new static($e);
}
/**
* Handle the exception and display the error report.
*
* The exception will be logged if error logging is enabled.
*
* The output buffer will be cleaned so nothing is sent to the browser except the
* error message. This prevents any views that have already been rendered from
* being shown in an incomplete or erroneous state.
*
* After the exception is displayed, the request will be halted.
*
* @return void
*/
public function handle()
{
if (ob_get_level() > 0) ob_clean();
if (Config::get('error.log')) $this->log();
$this->get_response(Config::get('error.detail'))->send();
exit(1);
}
/**
* Log the exception using the logger closure specified in the error configuration.
*
* @return void
*/
private function log()
{
$parameters = array(
$this->exception->severity(),
$this->exception->message(),
$this->exception->getTraceAsString(),
);
call_user_func_array(Config::get('error.logger'), $parameters);
}
/**
* Get the error report response for the exception.
*
* @param bool $detailed
* @return Resposne
*/
private function get_response($detailed)
{
return ($detailed) ? $this->detailed_response() : Response::error('500');
}
/**
* Get the detailed error report for the exception.
*
* @return Response
*/
private function detailed_response()
{
$data = array(
'severity' => $this->exception->severity(),
'message' => $this->exception->message(),
'line' => $this->exception->getLine(),
'trace' => $this->exception->getTraceAsString(),
'contexts' => $this->exception->context(),
);
return Response::make(View::make('error.exception', $data), 500);
}
}

153
laravel/file.php Normal file
View File

@@ -0,0 +1,153 @@
<?php namespace Laravel;
class File {
/**
* Get the contents of a file.
*
* @param string $path
* @return string
*/
public static function get($path)
{
return file_get_contents($path);
}
/**
* Write to a file.
*
* @param string $path
* @param string $data
* @return int
*/
public static function put($path, $data)
{
return file_put_contents($path, $data, LOCK_EX);
}
/**
* Append to a file.
*
* @param string $path
* @param string $data
* @return int
*/
public static function append($path, $data)
{
return file_put_contents($path, $data, LOCK_EX | FILE_APPEND);
}
/**
* Extract the extension from a file path.
*
* @param string $path
* @return string
*/
public static function extension($path)
{
return pathinfo($path, PATHINFO_EXTENSION);
}
/**
* Get the lines surrounding a given line in a file.
*
* @param string $path
* @param int $line
* @param int $padding
* @return array
*/
public static function snapshot($path, $line, $padding = 5)
{
if ( ! file_exists($path)) return array();
$file = file($path, FILE_IGNORE_NEW_LINES);
array_unshift($file, '');
if (($start = $line - $padding) < 0) $start = 0;
if (($length = ($line - $start) + $padding + 1) < 0) $length = 0;
return array_slice($file, $start, $length, true);
}
/**
* Get a file MIME type by extension.
*
* @param string $extension
* @param string $default
* @return string
*/
public static function mime($extension, $default = 'application/octet-stream')
{
$mimes = Config::get('mimes');
if (array_key_exists($extension, $mimes))
{
return (is_array($mimes[$extension])) ? $mimes[$extension][0] : $mimes[$extension];
}
return $default;
}
/**
* Determine if a file is a given type.
*
* The Fileinfo PHP extension will be used to determine the MIME type
* of the file. Any extension in the mimes configuration array may
* be passed as a type.
*/
public static function is($extension, $path)
{
$mimes = Config::get('mimes');
if ( ! array_key_exists($extension, $mimes))
{
throw new \Exception("File extension [$extension] is unknown. Cannot determine file type.");
}
$mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path);
return (is_array($mimes[$extension])) ? in_array($mime, $mimes[$extension]) : $mime === $mimes[$extension];
}
/**
* Create a response that will force a file to be downloaded.
*
* @param string $path
* @param string $name
* @return Response
*/
public static function download($path, $name = null)
{
if (is_null($name))
{
$name = basename($path);
}
$response = Response::make(static::get($path));
$response->header('Content-Description', 'File Transfer');
$response->header('Content-Type', static::mime(static::extension($path)));
$response->header('Content-Disposition', 'attachment; filename="'.$name.'"');
$response->header('Content-Transfer-Encoding', 'binary');
$response->header('Expires', 0);
$response->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0');
$response->header('Pragma', 'public');
$response->header('Content-Length', filesize($path));
return $response;
}
/**
* Move an uploaded file to storage.
*
* @param string $key
* @param string $path
* @return bool
*/
public static function upload($key, $path)
{
return array_key_exists($key, $_FILES) ? move_uploaded_file($_FILES[$key]['tmp_name'], $path) : false;
}
}

423
laravel/form.php Normal file
View File

@@ -0,0 +1,423 @@
<?php namespace Laravel;
class Form {
/**
* Stores labels names.
*
* @var array
*/
private static $labels = array();
/**
* Open a HTML form.
*
* @param string $action
* @param string $method
* @param array $attributes
* @param bool $https
* @return string
*/
public static function open($action = null, $method = 'POST', $attributes = array(), $https = false)
{
$attributes['action'] = HTML::entities(URL::to(((is_null($action)) ? Request::uri() : $action), $https));
// PUT and DELETE methods are spoofed using a hidden field containing the request method.
// Since, HTML does not support PUT and DELETE on forms, we will use POST.
$attributes['method'] = ($method == 'PUT' or $method == 'DELETE') ? 'POST' : $method;
if ( ! array_key_exists('accept-charset', $attributes))
{
$attributes['accept-charset'] = Config::get('application.encoding');
}
$html = '<form'.HTML::attributes($attributes).'>';
if ($method == 'PUT' or $method == 'DELETE')
{
$html .= PHP_EOL.static::input('hidden', 'REQUEST_METHOD', $method);
}
return $html.PHP_EOL;
}
/**
* Open a HTML form with a HTTPS action.
*
* @param string $action
* @param string $method
* @param array $attributes
* @return string
*/
public static function open_secure($action = null, $method = 'POST', $attributes = array())
{
return static::open($action, $method, $attributes, true);
}
/**
* Open a HTML form that accepts file uploads.
*
* @param string $action
* @param string $method
* @param array $attributes
* @param bool $https
* @return string
*/
public static function open_for_files($action = null, $method = 'POST', $attributes = array(), $https = false)
{
$attributes['enctype'] = 'multipart/form-data';
return static::open($action, $method, $attributes, $https);
}
/**
* Open a HTML form that accepts file uploads with a HTTPS action.
*
* @param string $action
* @param string $method
* @param array $attributes
* @return string
*/
public static function open_secure_for_files($action = null, $method = 'POST', $attributes = array())
{
return static::open_for_files($action, $method, $attributes, true);
}
/**
* Close a HTML form.
*
* @return string
*/
public static function close()
{
return '</form>';
}
/**
* Generate a hidden field containing the current CSRF token.
*
* @return string
*/
public static function token()
{
return static::input('hidden', 'csrf_token', static::raw_token());
}
/**
* Retrieve the current CSRF token.
*
* @return string
*/
public static function raw_token()
{
if (Config::get('session.driver') == '')
{
throw new \Exception('Sessions must be enabled to retrieve a CSRF token.');
}
return Session::get('csrf_token');
}
/**
* Create a HTML label element.
*
* @param string $name
* @param string $value
* @param array $attributes
* @return string
*/
public static function label($name, $value, $attributes = array())
{
static::$labels[] = $name;
return '<label for="'.$name.'"'.HTML::attributes($attributes).'>'.HTML::entities($value).'</label>'.PHP_EOL;
}
/**
* Create a HTML input element.
*
* @param string $name
* @param mixed $value
* @param array $attributes
* @return string
*/
public static function input($type, $name, $value = null, $attributes = array())
{
$id = static::id($name, $attributes);
return '<input'.HTML::attributes(array_merge($attributes, compact('type', 'name', 'value', 'id'))).'>'.PHP_EOL;
}
/**
* Create a HTML text input element.
*
* @param string $name
* @param string $value
* @param array $attributes
* @return string
*/
public static function text($name, $value = null, $attributes = array())
{
return static::input('text', $name, $value, $attributes);
}
/**
* Create a HTML password input element.
*
* @param string $name
* @param array $attributes
* @return string
*/
public static function password($name, $attributes = array())
{
return static::input('password', $name, null, $attributes);
}
/**
* Create a HTML hidden input element.
*
* @param string $name
* @param string $value
* @param array $attributes
* @return string
*/
public static function hidden($name, $value = null, $attributes = array())
{
return static::input('hidden', $name, $value, $attributes);
}
/**
* Create a HTML search input element.
*
* @param string $name
* @param string $value
* @param array $attributes
* @return string
*/
public static function search($name, $value = null, $attributes = array())
{
return static::input('search', $name, $value, $attributes);
}
/**
* Create a HTML email input element.
*
* @param string $name
* @param string $value
* @param array $attributes
* @return string
*/
public static function email($name, $value = null, $attributes = array())
{
return static::input('email', $name, $value, $attributes);
}
/**
* Create a HTML telephone input element.
*
* @param string $name
* @param string $value
* @param array $attributes
* @return string
*/
public static function telephone($name, $value = null, $attributes = array())
{
return static::input('tel', $name, $value, $attributes);
}
/**
* Create a HTML URL input element.
*
* @param string $name
* @param string $value
* @param array $attributes
* @return string
*/
public static function url($name, $value = null, $attributes = array())
{
return static::input('url', $name, $value, $attributes);
}
/**
* Create a HTML number input element.
*
* @param string $name
* @param string $value
* @param array $attributes
* @return string
*/
public static function number($name, $value = null, $attributes = array())
{
return static::input('number', $name, $value, $attributes);
}
/**
* Create a HTML file input element.
*
* @param string $name
* @param array $attributes
* @return string
*/
public static function file($name, $attributes = array())
{
return static::input('file', $name, null, $attributes);
}
/**
* Create a HTML textarea element.
*
* @param string $name
* @param string $value
* @param array $attributes
* @return string
*/
public static function textarea($name, $value = '', $attributes = array())
{
$attributes = array_merge($attributes, array('id' => static::id($name, $attributes), 'name' => $name));
if ( ! isset($attributes['rows'])) $attributes['rows'] = 10;
if ( ! isset($attributes['cols'])) $attributes['cols'] = 50;
return '<textarea'.HTML::attributes($attributes).'>'.HTML::entities($value).'</textarea>'.PHP_EOL;
}
/**
* Create a HTML select element.
*
* @param string $name
* @param array $options
* @param string $selected
* @param array $attributes
* @return string
*/
public static function select($name, $options = array(), $selected = null, $attributes = array())
{
$attributes = array_merge($attributes, array('id' => static::id($name, $attributes), 'name' => $name));
$html = array();
foreach ($options as $value => $display)
{
$option_attributes = array('value' => HTML::entities($value), 'selected' => ($value == $selected) ? 'selected' : null);
$html[] = '<option'.HTML::attributes($option_attributes).'>'.HTML::entities($display).'</option>';
}
return '<select'.HTML::attributes($attributes).'>'.implode('', $html).'</select>'.PHP_EOL;
}
/**
* Create a HTML checkbox input element.
*
* @param string $name
* @param string $value
* @param bool $checked
* @param array $attributes
* @return string
*/
public static function checkbox($name, $value = null, $checked = false, $attributes = array())
{
return static::checkable('checkbox', $name, $value, $checked, $attributes);
}
/**
* Create a HTML radio button input element.
*
* @param string $name
* @param string $value
* @param bool $checked
* @param array $attributes
* @return string
*/
public static function radio($name, $value = null, $checked = false, $attributes = array())
{
return static::checkable('radio', $name, $value, $checked, $attributes);
}
/**
* Create a checkable input element.
*
* @param string $type
* @param string $name
* @param string $value
* @param bool $checked
* @param array $attributes
* @return string
*/
private static function checkable($type, $name, $value, $checked, $attributes)
{
$attributes = array_merge($attributes, array('id' => static::id($name, $attributes), 'checked' => ($checked) ? 'checked' : null));
return static::input($type, $name, $value, $attributes);
}
/**
* Create a HTML submit input element.
*
* @param string $value
* @param array $attributes
* @return string
*/
public static function submit($value, $attributes = array())
{
return static::input('submit', null, $value, $attributes);
}
/**
* Create a HTML reset input element.
*
* @param string $value
* @param array $attributes
* @return string
*/
public static function reset($value, $attributes = array())
{
return static::input('reset', null, $value, $attributes);
}
/**
* Create a HTML image input element.
*
* @param string $url
* @param array $attributes
* @return string
*/
public static function image($url, $name = null, $attributes = array())
{
$attributes['src'] = URL::to_asset($url);
return static::input('image', $name, null, $attributes);
}
/**
* Create a HTML button element.
*
* @param string $name
* @param string $value
* @param array $attributes
* @return string
*/
public static function button($value, $attributes = array())
{
return '<button'.HTML::attributes($attributes).'>'.HTML::entities($value).'</button>'.PHP_EOL;
}
/**
* Determine the ID attribute for a form element.
*
* An explicitly specified ID in the attributes takes first precedence, then
* the label names will be checked for a label matching the element name.
*
* @param string $name
* @param array $attributes
* @return mixed
*/
private static function id($name, $attributes)
{
if (array_key_exists('id', $attributes)) return $attributes['id'];
if (in_array($name, static::$labels)) return $name;
}
}

45
laravel/hash.php Normal file
View File

@@ -0,0 +1,45 @@
<?php namespace Laravel;
class Hash {
/**
* Hash a string using PHPass.
*
* PHPass provides reliable bcrypt hashing, and is used by many popular PHP
* applications such as Wordpress and Joomla.
*
* @access public
* @param string $value
* @return string
*/
public static function make($value, $rounds = 10)
{
return static::hasher($rounds)->HashPassword($value);
}
/**
* Determine if an unhashed value matches a given hash.
*
* @param string $value
* @param string $hash
* @return bool
*/
public static function check($value, $hash)
{
return static::hasher()->CheckPassword($value, $hash);
}
/**
* Create a new PHPass instance.
*
* @param int $rounds
* @return PasswordHash
*/
private static function hasher($rounds = 10)
{
require_once SYS_PATH.'vendor/phpass'.EXT;
return new \PasswordHash($rounds, false);
}
}

305
laravel/html.php Normal file
View File

@@ -0,0 +1,305 @@
<?php namespace Laravel;
class HTML {
/**
* Convert HTML characters to entities.
*
* @param string $value
* @return string
*/
public static function entities($value)
{
return htmlentities($value, ENT_QUOTES, Config::get('application.encoding'), false);
}
/**
* Generate a JavaScript reference.
*
* @param string $url
* @param array $attributes
* @return string
*/
public static function script($url, $attributes = array())
{
return '<script type="text/javascript" src="'.static::entities(URL::to_asset($url)).'"'.static::attributes($attributes).'></script>'.PHP_EOL;
}
/**
* Generate a CSS reference.
*
* @param string $url
* @param array $attributes
* @return string
*/
public static function style($url, $attributes = array())
{
if ( ! array_key_exists('media', $attributes)) $attributes['media'] = 'all';
$attributes = $attributes + array('rel' => 'stylesheet', 'type' => 'text/css');
return '<link href="'.static::entities(URL::to_asset($url)).'"'.static::attributes($attributes).'>'.PHP_EOL;
}
/**
* Generate a HTML span.
*
* @param string $value
* @param array $attributes
* @return string
*/
public static function span($value, $attributes = array())
{
return '<span'.static::attributes($attributes).'>'.static::entities($value).'</span>';
}
/**
* Generate a HTML link.
*
* @param string $url
* @param string $title
* @param array $attributes
* @param bool $https
* @param bool $asset
* @return string
*/
public static function link($url, $title, $attributes = array(), $https = false, $asset = false)
{
return '<a href="'.static::entities(URL::to($url, $https, $asset)).'"'.static::attributes($attributes).'>'.static::entities($title).'</a>';
}
/**
* Generate a HTTPS HTML link.
*
* @param string $url
* @param string $title
* @param array $attributes
* @return string
*/
public static function link_to_secure($url, $title, $attributes = array())
{
return static::link($url, $title, $attributes, true);
}
/**
* Generate an HTML link to an asset.
*
* @param string $url
* @param string $title
* @param array $attributes
* @return string
*/
public static function link_to_asset($url, $title, $attributes = array(), $https = false)
{
return static::link($url, $title, $attributes, $https, true);
}
/**
* Generate an HTTPS HTML link to an asset.
*
* @param string $url
* @param string $title
* @param array $attributes
* @return string
*/
public static function link_to_secure_asset($url, $title, $attributes = array())
{
return static::link_to_asset($url, $title, $attributes, true);
}
/**
* Generate an HTML link to a route.
*
* @param string $name
* @param string $title
* @param array $parameters
* @param array $attributes
* @return string
*/
public static function link_to_route($name, $title, $parameters = array(), $attributes = array(), $https = false)
{
return static::link(URL::to_route($name, $parameters, $https), $title, $attributes);
}
/**
* Generate an HTTPS HTML link to a route.
*
* @param string $name
* @param string $title
* @param array $parameters
* @param array $attributes
* @return string
*/
public static function link_to_secure_route($name, $title, $parameters = array(), $attributes = array())
{
return static::link_to_route($name, $title, $parameters, $attributes, true);
}
/**
* Generate an HTML mailto link.
*
* @param string $email
* @param string $title
* @param array $attributes
* @return string
*/
public static function mailto($email, $title = null, $attributes = array())
{
$email = static::email($email);
if (is_null($title)) $title = $email;
return '<a href="&#109;&#097;&#105;&#108;&#116;&#111;&#058;'.$email.'"'.static::attributes($attributes).'>'.static::entities($title).'</a>';
}
/**
* Obfuscate an e-mail address to prevent spam-bots from sniffing it.
*
* @param string $email
* @return string
*/
public static function email($email)
{
return str_replace('@', '&#64;', static::obfuscate($email));
}
/**
* Generate an HTML image.
*
* @param string $url
* @param string $alt
* @param array $attributes
* @return string
*/
public static function image($url, $alt = '', $attributes = array())
{
$attributes['alt'] = static::entities($alt);
return '<img src="'.static::entities(URL::to_asset($url)).'"'.static::attributes($attributes).'>';
}
/**
* Generate an ordered list.
*
* @param array $list
* @param array $attributes
* @return string
*/
public static function ol($list, $attributes = array())
{
return static::list_elements('ol', $list, $attributes);
}
/**
* Generate an un-ordered list.
*
* @param array $list
* @param array $attributes
* @return string
*/
public static function ul($list, $attributes = array())
{
return static::list_elements('ul', $list, $attributes);
}
/**
* Generate an ordered or un-ordered list.
*
* @param string $type
* @param array $list
* @param array $attributes
* @return string
*/
private static function list_elements($type, $list, $attributes = array())
{
$html = '';
foreach ($list as $key => $value)
{
$html .= (is_array($value)) ? static::list_elements($type, $value) : '<li>'.static::entities($value).'</li>';
}
return '<'.$type.static::attributes($attributes).'>'.$html.'</'.$type.'>';
}
/**
* Build a list of HTML attributes.
*
* @param array $attributes
* @return string
*/
public static function attributes($attributes)
{
$html = array();
foreach ($attributes as $key => $value)
{
// Assume numeric-keyed attributes to have the same key and value.
// Example: required="required", autofocus="autofocus", etc.
if (is_numeric($key)) $key = $value;
if ( ! is_null($value))
{
$html[] = $key.'="'.static::entities($value).'"';
}
}
return (count($html) > 0) ? ' '.implode(' ', $html) : '';
}
/**
* Obfuscate a string to prevent spam-bots from sniffing it.
*
* @param string $value
* @return string
*/
public static function obfuscate($value)
{
$safe = '';
foreach (str_split($value) as $letter)
{
switch (rand(1, 3))
{
// Convert the letter to its entity representation.
case 1:
$safe .= '&#'.ord($letter).';';
break;
// Convert the letter to a Hex character code.
case 2:
$safe .= '&#x'.dechex(ord($letter)).';';
break;
// No encoding.
case 3:
$safe .= $letter;
}
}
return $safe;
}
/**
* Magic Method for handling dynamic static methods.
*/
public static function __callStatic($method, $parameters)
{
if (strpos($method, 'link_to_secure_') === 0)
{
array_unshift($parameters, substr($method, 15));
return forward_static_call_array('HTML::link_to_secure_route', $parameters);
}
if (strpos($method, 'link_to_') === 0)
{
array_unshift($parameters, substr($method, 8));
return forward_static_call_array('HTML::link_to_route', $parameters);
}
throw new \Exception("Static method [$method] is not defined on the HTML class.");
}
}

206
laravel/inflector.php Normal file
View File

@@ -0,0 +1,206 @@
<?php namespace Laravel;
class Inflector {
/**
* The words that have been converted to singular.
*
* @var array
*/
private static $singular_cache = array();
/**
* The words that have been converted to plural.
*
* @var array
*/
private static $plural_cache = array();
/**
* Plural word forms.
*
* @var array
*/
private static $plural = array(
'/(quiz)$/i' => "$1zes",
'/^(ox)$/i' => "$1en",
'/([m|l])ouse$/i' => "$1ice",
'/(matr|vert|ind)ix|ex$/i' => "$1ices",
'/(x|ch|ss|sh)$/i' => "$1es",
'/([^aeiouy]|qu)y$/i' => "$1ies",
'/(hive)$/i' => "$1s",
'/(?:([^f])fe|([lr])f)$/i' => "$1$2ves",
'/(shea|lea|loa|thie)f$/i' => "$1ves",
'/sis$/i' => "ses",
'/([ti])um$/i' => "$1a",
'/(tomat|potat|ech|her|vet)o$/i' => "$1oes",
'/(bu)s$/i' => "$1ses",
'/(alias)$/i' => "$1es",
'/(octop)us$/i' => "$1i",
'/(ax|test)is$/i' => "$1es",
'/(us)$/i' => "$1es",
'/s$/i' => "s",
'/$/' => "s"
);
/**
* Singular word forms.
*
* @var array
*/
private static $singular = array(
'/(quiz)zes$/i' => "$1",
'/(matr)ices$/i' => "$1ix",
'/(vert|ind)ices$/i' => "$1ex",
'/^(ox)en$/i' => "$1",
'/(alias)es$/i' => "$1",
'/(octop|vir)i$/i' => "$1us",
'/(cris|ax|test)es$/i' => "$1is",
'/(shoe)s$/i' => "$1",
'/(o)es$/i' => "$1",
'/(bus)es$/i' => "$1",
'/([m|l])ice$/i' => "$1ouse",
'/(x|ch|ss|sh)es$/i' => "$1",
'/(m)ovies$/i' => "$1ovie",
'/(s)eries$/i' => "$1eries",
'/([^aeiouy]|qu)ies$/i' => "$1y",
'/([lr])ves$/i' => "$1f",
'/(tive)s$/i' => "$1",
'/(hive)s$/i' => "$1",
'/(li|wi|kni)ves$/i' => "$1fe",
'/(shea|loa|lea|thie)ves$/i' => "$1f",
'/(^analy)ses$/i' => "$1sis",
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => "$1$2sis",
'/([ti])a$/i' => "$1um",
'/(n)ews$/i' => "$1ews",
'/(h|bl)ouses$/i' => "$1ouse",
'/(corpse)s$/i' => "$1",
'/(us)es$/i' => "$1",
'/(us|ss)$/i' => "$1",
'/s$/i' => "",
);
/**
* Irregular word forms.
*
* @var array
*/
private static $irregular = array(
'move' => 'moves',
'foot' => 'feet',
'goose' => 'geese',
'sex' => 'sexes',
'child' => 'children',
'man' => 'men',
'tooth' => 'teeth',
'person' => 'people',
);
/**
* Uncountable word forms.
*
* @var array
*/
private static $uncountable = array(
'sheep',
'fish',
'deer',
'series',
'species',
'money',
'rice',
'information',
'equipment',
);
/**
* Convert a word to its plural form.
*
* @param string $value
* @return string
*/
public static function plural($value)
{
if (array_key_exists($value, static::$plural_cache))
{
return static::$plural_cache[$value];
}
if (in_array(strtolower($value), static::$uncountable))
{
return static::$plural_cache[$value] = $value;
}
foreach (static::$irregular as $pattern => $irregular)
{
$pattern = '/'.$pattern.'$/i';
if (preg_match($pattern, $value))
{
return static::$plural_cache[$value] = preg_replace($pattern, $irregular, $value);
}
}
foreach (static::$plural as $pattern => $plural)
{
if (preg_match($pattern, $value))
{
return static::$plural_cache[$value] = preg_replace($pattern, $plural, $value);
}
}
return static::$plural_cache[$value] = $value;
}
/**
* Convert a word to its singular form.
*
* @param string $value
* @return string
*/
public static function singular($value)
{
if (array_key_exists($value, static::$singular_cache))
{
return static::$singular_cache[$value];
}
if (in_array(strtolower($value), static::$uncountable))
{
return static::$singular_cache[$value] = $value;
}
foreach (static::$irregular as $irregular => $pattern)
{
$pattern = '/'.$pattern.'$/i';
if (preg_match($pattern, $value))
{
return static::$singular_cache[$value] = preg_replace($pattern, $irregular, $value);
}
}
foreach (static::$singular as $pattern => $singular)
{
if (preg_match($pattern, $value))
{
return static::$singular_cache[$value] = preg_replace($pattern, $singular, $value);
}
}
return static::$singular_cache[$value] = $value;
}
/**
* Get the plural form of a word if the count is greater than zero.
*
* @param string $value
* @param int $count
* @return string
*/
public static function plural_if($value, $count)
{
return ($count > 1) ? static::plural($value) : $value;
}
}

119
laravel/input.php Normal file
View File

@@ -0,0 +1,119 @@
<?php namespace Laravel;
class Input {
/**
* The input data for the request.
*
* @var array
*/
public static $input;
/**
* Get all of the input data for the request.
*
* This method returns a merged array containing Input::get and Input::file.
*
* @return array
*/
public static function all()
{
return array_merge(static::get(), static::file());
}
/**
* Determine if the input data contains an item.
*
* @param string $key
* @return bool
*/
public static function has($key)
{
return ( ! is_null(static::get($key)) and trim((string) static::get($key)) !== '');
}
/**
* Get an item from the input data.
*
* @param string $key
* @param mixed $default
* @return string
*/
public static function get($key = null, $default = null)
{
if (is_null(static::$input)) static::hydrate();
return Arr::get(static::$input, $key, $default);
}
/**
* Determine if the old input data contains an item.
*
* @param string $key
* @return bool
*/
public static function had($key)
{
return ( ! is_null(static::old($key)) and trim((string) static::old($key)) !== '');
}
/**
* Get input data from the previous request.
*
* @param string $key
* @param mixed $default
* @return string
*/
public static function old($key = null, $default = null)
{
if (Config::get('session.driver') == '')
{
throw new \Exception("Sessions must be enabled to retrieve old input data.");
}
return Arr::get(Session::get('laravel_old_input', array()), $key, $default);
}
/**
* Get an item from the uploaded file data.
*
* @param string $key
* @param mixed $default
* @return array
*/
public static function file($key = null, $default = null)
{
return Arr::get($_FILES, $key, $default);
}
/**
* Hydrate the input data for the request.
*
* @return void
*/
public static function hydrate()
{
switch (Request::method())
{
case 'GET':
static::$input =& $_GET;
break;
case 'POST':
static::$input =& $_POST;
break;
case 'PUT':
case 'DELETE':
if (Request::spoofed())
{
static::$input =& $_POST;
}
else
{
parse_str(file_get_contents('php://input'), static::$input);
}
}
}
}

132
laravel/lang.php Normal file
View File

@@ -0,0 +1,132 @@
<?php namespace Laravel;
class Lang {
/**
* All of the loaded language lines.
*
* The array is keyed by [$language.$file].
*
* @var array
*/
public static $lines = array();
/**
* The key of the line that is being requested.
*
* @var string
*/
public $key;
/**
* The place-holder replacements.
*
* @var array
*/
public $replacements = array();
/**
* Create a new Lang instance.
*
* Language lines are retrieved using "dot" notation. So, asking for the
* "messages.required" language line would return the "required" line
* from the "messages" language file.
*
* @param string $key
* @param array $replacements
* @return void
*/
public function __construct($key, $replacements = array())
{
$this->key = $key;
$this->replacements = $replacements;
}
/**
* Create a Lang instance for a language line.
*
* @param string $key
* @param array $replacements
* @return Lang
*/
public static function line($key, $replacements = array())
{
return new static($key, $replacements);
}
/**
* Get the language line.
*
* @param string $language
* @param mixed $default
* @return string
*/
public function get($language = null, $default = null)
{
if (is_null($language)) $language = Config::get('application.language');
list($module, $file, $line) = $this->parse($this->key, $language);
$this->load($module, $file, $language);
if ( ! isset(static::$lines[$module][$language.$file][$line]))
{
return is_callable($default) ? call_user_func($default) : $default;
}
$line = static::$lines[$module][$language.$file][$line];
foreach ($this->replacements as $key => $value)
{
$line = str_replace(':'.$key, $value, $line);
}
return $line;
}
/**
* Parse a language key.
*
* @param string $key
* @param string $language
* @return array
*/
private function parse($key, $language)
{
list($module, $key) = Module::parse($key);
if (count($segments = explode('.', $key)) > 1)
{
return array($module, $segments[0], implode('.', array_slice($segments, 1)));
}
throw new \Exception("Invalid language line [$key]. A specific line must be specified.");
}
/**
* Load a language file.
*
* @param string $module
* @param string $file
* @param string $language
* @return void
*/
private function load($module, $file, $language)
{
if (isset(static::$lines[$module][$language.$file])) return;
if (file_exists($path = Module::path($module).'lang/'.$language.'/'.$file.EXT))
{
static::$lines[$module][$language.$file] = require $path;
}
}
/**
* Get the string content of the language line.
*/
public function __toString()
{
return $this->get();
}
}

198
laravel/laravel.php Normal file
View File

@@ -0,0 +1,198 @@
<?php namespace Laravel;
// --------------------------------------------------------------
// Define the PHP file extension.
// --------------------------------------------------------------
define('EXT', '.php');
// --------------------------------------------------------------
// Define the core framework paths.
// --------------------------------------------------------------
define('BASE_PATH', realpath(str_replace('laravel', '', $system)).'/');
define('CONFIG_PATH', realpath($config).'/');
define('MODULE_PATH', realpath($modules).'/');
define('PACKAGE_PATH', realpath($packages).'/');
define('PUBLIC_PATH', realpath($public).'/');
define('STORAGE_PATH', realpath($storage).'/');
define('SYS_PATH', realpath($system).'/');
unset($system, $config, $modules, $packages, $public, $storage);
// --------------------------------------------------------------
// Define various other framework paths.
// --------------------------------------------------------------
define('CACHE_PATH', STORAGE_PATH.'cache/');
define('DATABASE_PATH', STORAGE_PATH.'db/');
define('SCRIPT_PATH', PUBLIC_PATH.'js/');
define('SESSION_PATH', STORAGE_PATH.'sessions/');
define('STYLE_PATH', PUBLIC_PATH.'css/');
// --------------------------------------------------------------
// Define the default module.
// --------------------------------------------------------------
define('DEFAULT_MODULE', 'application');
// --------------------------------------------------------------
// Load the classes used by the auto-loader.
// --------------------------------------------------------------
require SYS_PATH.'loader'.EXT;
require SYS_PATH.'config'.EXT;
require SYS_PATH.'module'.EXT;
require SYS_PATH.'arr'.EXT;
// --------------------------------------------------------------
// Register the active modules.
// --------------------------------------------------------------
Module::$modules = $active;
unset($active);
// --------------------------------------------------------------
// Register the auto-loader.
// --------------------------------------------------------------
Loader::bootstrap(array(
Module::path(DEFAULT_MODULE).'libraries/',
Module::path(DEFAULT_MODULE).'models/',
));
spl_autoload_register(array('Laravel\\Loader', 'load'));
// --------------------------------------------------------------
// Set the error reporting and display levels.
// --------------------------------------------------------------
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 'Off');
// --------------------------------------------------------------
// Register the error / exception handlers.
// --------------------------------------------------------------
$error_dependencies = function()
{
require_once SYS_PATH.'exception/handler'.EXT;
require_once SYS_PATH.'exception/examiner'.EXT;
require_once SYS_PATH.'file'.EXT;
};
set_exception_handler(function($e) use ($error_dependencies)
{
call_user_func($error_dependencies);
Exception\Handler::make($e)->handle();
});
set_error_handler(function($number, $error, $file, $line) use ($error_dependencies)
{
call_user_func($error_dependencies);
Exception\Handler::make(new \ErrorException($error, $number, 0, $file, $line))->handle();
});
register_shutdown_function(function() use ($error_dependencies)
{
if ( ! is_null($error = error_get_last()))
{
call_user_func($error_dependencies);
extract($error);
Exception\Handler::make(new \ErrorException($message, $type, 0, $file, $line))->handle();
}
});
// --------------------------------------------------------------
// Set the default timezone.
// --------------------------------------------------------------
date_default_timezone_set(Config::get('application.timezone'));
// --------------------------------------------------------------
// Load the session.
// --------------------------------------------------------------
if (Config::get('session.driver') != '') Session::load(Cookie::get('laravel_session'));
// --------------------------------------------------------------
// Load all of the core routing classes.
// --------------------------------------------------------------
require SYS_PATH.'request'.EXT;
require SYS_PATH.'response'.EXT;
require SYS_PATH.'routing/route'.EXT;
require SYS_PATH.'routing/router'.EXT;
require SYS_PATH.'routing/loader'.EXT;
require SYS_PATH.'routing/filter'.EXT;
// --------------------------------------------------------------
// Load the packages that are in the auto-loaded packages array.
// --------------------------------------------------------------
if (count(Config::get('application.packages')) > 0)
{
require SYS_PATH.'package'.EXT;
Package::load(Config::get('application.packages'));
}
// --------------------------------------------------------------
// Determine the module that should handle the request.
// --------------------------------------------------------------
$segments = explode('/', Request::uri());
define('ACTIVE_MODULE', (array_key_exists($segments[0], Module::$modules)) ? $segments[0] : DEFAULT_MODULE);
// --------------------------------------------------------------
// Determine the path to the root of the active module.
// --------------------------------------------------------------
define('ACTIVE_MODULE_PATH', Module::path(ACTIVE_MODULE));
// --------------------------------------------------------------
// Register the filters for the active module.
// --------------------------------------------------------------
if (file_exists(ACTIVE_MODULE_PATH.'filters'.EXT))
{
Routing\Filter::register(require ACTIVE_MODULE_PATH.'filters'.EXT);
}
// --------------------------------------------------------------
// Call the "before" filter for the application and module.
// --------------------------------------------------------------
foreach (array('before', ACTIVE_MODULE.'::before') as $filter)
{
$response = Routing\Filter::call($filter, array(Request::method(), Request::uri()), true);
if ( ! is_null($response)) break;
}
// --------------------------------------------------------------
// Route the request and get the response from the route.
// --------------------------------------------------------------
if (is_null($response))
{
$loader = new Routing\Loader(ACTIVE_MODULE_PATH);
$route = Routing\Router::make(Request::method(), Request::uri(), $loader)->route();
$response = (is_null($route)) ? Response::error('404') : $route->call();
}
$response = Response::prepare($response);
// --------------------------------------------------------------
// Call the "after" filter for the application and module.
// --------------------------------------------------------------
foreach (array(ACTIVE_MODULE.'::after', 'after') as $filter)
{
Routing\Filter::call($filter, array($response, Request::method(), Request::uri()));
}
// --------------------------------------------------------------
// Stringify the response.
// --------------------------------------------------------------
$response->content = (string) $response->content;
// --------------------------------------------------------------
// Close the session.
// --------------------------------------------------------------
if (Config::get('session.driver') != '') Session::close();
// --------------------------------------------------------------
// Send the response to the browser.
// --------------------------------------------------------------
$response->send();

120
laravel/loader.php Normal file
View File

@@ -0,0 +1,120 @@
<?php namespace Laravel;
class Loader {
/**
* The paths to be searched by the loader.
*
* @var array
*/
public static $paths = array(BASE_PATH);
/**
* All of the class aliases.
*
* @var array
*/
public static $aliases = array();
/**
* All of the active modules.
*
* @var array
*/
public static $modules = array();
/**
* Bootstrap the auto-loader.
*
* @param array $paths
* @return void
*/
public static function bootstrap($paths = array())
{
static::$aliases = Config::get('aliases');
foreach ($paths as $path) { static::register($path); }
}
/**
* Load a class file for a given class name.
*
* This function is registered on the SPL auto-loader stack by the front controller during each request.
* All Laravel class names follow a namespace to directory convention.
*
* @param string $class
* @return void
*/
public static function load($class)
{
$file = strtolower(str_replace('\\', '/', $class));
if (array_key_exists($class, static::$aliases)) return class_alias(static::$aliases[$class], $class);
(static::load_from_registered($file)) or static::load_from_module($file);
}
/**
* Load a class that is stored in the registered directories.
*
* @param string $file
* @return bool
*/
private static function load_from_registered($file)
{
foreach (static::$paths as $directory)
{
if (file_exists($path = $directory.$file.EXT))
{
require $path;
return true;
}
}
return false;
}
/**
* Load a class that is stored in a module.
*
* @param string $file
* @return void
*/
private static function load_from_module($file)
{
// Since all module models and libraries must be namespaced to the
// module name, we'll extract the module name from the file.
$module = substr($file, 0, strpos($file, '/'));
if (in_array($module, Module::$modules))
{
$module = MODULE_PATH.$module.'/';
// Slice the module name off of the filename. Even though module libraries
// and models are namespaced under the module, there will obviously not be
// a folder matching that namespace in the libraries or models directories
// of the module. Slicing it off will allow us to make a clean search for
// the relevant class file.
$file = substr($file, strpos($file, '/') + 1);
foreach (array($module.'models', $module.'libraries') as $directory)
{
if (file_exists($path = $directory.'/'.$file.EXT)) return require $path;
}
}
}
/**
* Register a path with the auto-loader. After registering the path, it will be
* checked similarly to the models and libraries directories.
*
* @param string $path
* @return void
*/
public static function register($path)
{
static::$paths[] = rtrim($path, '/').'/';
}
}

55
laravel/memcached.php Normal file
View File

@@ -0,0 +1,55 @@
<?php namespace Laravel;
class Memcached {
/**
* The Memcache instance.
*
* @var Memcache
*/
private static $instance = null;
/**
* Get the singleton Memcache instance.
*
* @return Memcache
*/
public static function instance()
{
if (is_null(static::$instance))
{
static::$instance = static::connect(Config::get('cache.servers'));
}
return static::$instance;
}
/**
* Connect to the configured Memcached servers.
*
* @param array $servers
* @return Memcache
*/
private static function connect($servers)
{
if ( ! class_exists('Memcache'))
{
throw new \Exception('Attempting to use Memcached, but the Memcached PHP extension is not installed on this server.');
}
$memcache = new \Memcache;
foreach ($servers as $server)
{
$memcache->addServer($server['host'], $server['port'], true, $server['weight']);
}
if ($memcache->getVersion() === false)
{
throw new \Exception('Memcached is configured. However, no connections could be made. Please verify your memcached configuration.');
}
return $memcache;
}
}

113
laravel/messages.php Normal file
View File

@@ -0,0 +1,113 @@
<?php namespace Laravel;
class Messages {
/**
* All of the messages.
*
* @var array
*/
public $messages;
/**
* Create a new Messages instance.
*
* @return void
*/
public function __construct($messages = array())
{
$this->messages = $messages;
}
/**
* Add a message to the collector.
*
* Duplicate messages will not be added.
*
* @param string $key
* @param string $message
* @return void
*/
public function add($key, $message)
{
if ( ! isset($this->messages[$key]) or array_search($message, $this->messages[$key]) === false)
{
$this->messages[$key][] = $message;
}
}
/**
* Determine if messages exist for a given key.
*
* @param string $key
* @return bool
*/
public function has($key)
{
return $this->first($key) !== '';
}
/**
* Get the first message for a given key.
*
* @param string $key
* @param string $format
* @return string
*/
public function first($key, $format = ':message')
{
return (count($messages = $this->get($key, $format)) > 0) ? $messages[0] : '';
}
/**
* Get all of the messages for a key.
*
* If no key is specified, all of the messages will be returned.
*
* @param string $key
* @param string $format
* @return array
*/
public function get($key = null, $format = ':message')
{
if (is_null($key)) return $this->all($format);
return (array_key_exists($key, $this->messages)) ? $this->format($this->messages[$key], $format) : array();
}
/**
* Get all of the messages.
*
* @param string $format
* @return array
*/
public function all($format = ':message')
{
$all = array();
foreach ($this->messages as $messages)
{
$all = array_merge($all, $this->format($messages, $format));
}
return $all;
}
/**
* Format an array of messages.
*
* @param array $messages
* @param string $format
* @return array
*/
private function format($messages, $format)
{
foreach ($messages as $key => &$message)
{
$message = str_replace(':message', $message, $format);
}
return $messages;
}
}

67
laravel/module.php Normal file
View File

@@ -0,0 +1,67 @@
<?php namespace Laravel;
class Module {
/**
* The active modules for the installation.
*
* @var array
*/
public static $modules = array();
/**
* All of the loaded module paths.
*
* @var array
*/
private static $paths = array();
/**
* Parse a modularized identifier and get the module and key.
*
* Modular identifiers follow a {module}::{key} convention.
*
* @param string $key
* @return array
*/
public static function parse($key)
{
$module = (strpos($key, '::') !== false) ? substr($key, 0, strpos($key, ':')) : DEFAULT_MODULE;
if ($module !== DEFAULT_MODULE) $key = substr($key, strpos($key, ':') + 2);
return array($module, $key);
}
/**
* Get the path for a given module.
*
* @param string $module
* @return string
*/
public static function path($module)
{
if (array_key_exists($module, static::$paths)) return static::$paths[$module];
if (array_key_exists($module, static::$modules))
{
$path = (strpos(static::$modules[$module], BASE_PATH) === 0) ? static::$modules[$module].'/' : MODULE_PATH.static::$modules[$module].'/';
}
return static::$paths[$module] = $path;
}
/**
* Get the an array of paths to all of the modules.
*
* The module paths will be determined by the modules that are specified in the application
* modules configuration item. A trailing slash will be added to the paths.
*
* @return array
*/
public static function paths()
{
return array_map(function($module) { return Laravel\Module::path($module); }, static::$modules);
}
}

42
laravel/package.php Normal file
View File

@@ -0,0 +1,42 @@
<?php namespace Laravel;
class Package {
/**
* All of the loaded packages.
*
* @var array
*/
public static $loaded = array();
/**
* Load a package or set of packages.
*
* @param string|array $packages
* @return void
*/
public static function load($packages)
{
foreach ((array) $packages as $package)
{
if ( ! static::loaded($package) and file_exists($bootstrap = PACKAGE_PATH.$package.'/bootstrap'.EXT))
{
require $bootstrap;
}
static::$loaded[] = $package;
}
}
/**
* Determine if a given package has been loaded.
*
* @param string $package
* @return bool
*/
public static function loaded($package)
{
return array_key_exists($package, static::$loaded);
}
}

265
laravel/paginator.php Normal file
View File

@@ -0,0 +1,265 @@
<?php namespace Laravel;
class Paginator {
/**
* The results for the current page.
*
* @var array
*/
public $results;
/**
* The total number of results.
*
* @var int
*/
public $total;
/**
* The current page.
*
* @var int
*/
public $page;
/**
* The number of items per page.
*
* @var int
*/
public $per_page;
/**
* The last page available for the result set.
*
* @var int
*/
public $last_page;
/**
* The language that should be used when generating page links.
*
* @var string
*/
public $language;
/**
* The values that should be appended to the end of the link query strings.
*
* @var array
*/
public $append = array();
/**
* Create a new Paginator instance.
*
* @param array $results
* @param int $page
* @param int $total
* @param int $per_page
* @param int $last_page
* @return void
*/
public function __construct($results, $page, $total, $per_page, $last_page)
{
$this->last_page = $last_page;
$this->per_page = $per_page;
$this->results = $results;
$this->total = $total;
$this->page = $page;
}
/**
* Create a new Paginator instance.
*
* @param array $results
* @param int $total
* @param int $per_page
* @return Paginator
*/
public static function make($results, $total, $per_page)
{
return new static($results, static::page($total, $per_page), $total, $per_page, ceil($total / $per_page));
}
/**
* Get the current page from the request query string.
*
* The page will be validated and adjusted if it is less than one or greater than the last page.
* For example, if the current page is not an integer or less than one, one will be returned.
* If the current page is greater than the last page, the last page will be returned.
*
* @param int $total
* @param int $per_page
* @return int
*/
public static function page($total, $per_page)
{
$page = Input::get('page', 1);
if (is_numeric($page) and $page > $last_page = ceil($total / $per_page))
{
return ($last_page > 0) ? $last_page : 1;
}
return ($page < 1 or filter_var($page, FILTER_VALIDATE_INT) === false) ? 1 : $page;
}
/**
* Create the HTML pagination links.
*
* @param int $adjacent
* @return string
*/
public function links($adjacent = 3)
{
if ($this->last_page <= 1) return '';
// The hard-coded "7" is to account for all of the constant elements in a sliding range.
// Namely: The the current page, the two ellipses, the two beginning pages, and the two ending pages.
$numbers = ($this->last_page < 7 + ($adjacent * 2)) ? $this->range(1, $this->last_page) : $this->slider($adjacent);
return '<div class="pagination">'.$this->previous().$numbers.$this->next().'</div>';
}
/**
* Build sliding list of HTML numeric page links.
*
* @param int $adjacent
* @return string
*/
private function slider($adjacent)
{
if ($this->page <= $adjacent * 2)
{
return $this->range(1, 2 + ($adjacent * 2)).$this->ending();
}
elseif ($this->page >= $this->last_page - ($adjacent * 2))
{
return $this->beginning().$this->range($this->last_page - 2 - ($adjacent * 2), $this->last_page);
}
return $this->beginning().$this->range($this->page - $adjacent, $this->page + $adjacent).$this->ending();
}
/**
* Generate the "previous" HTML link.
*
* @return string
*/
public function previous()
{
$text = Lang::line('pagination.previous')->get($this->language);
if ($this->page > 1)
{
return $this->link($this->page - 1, $text, 'prev_page').' ';
}
return HTML::span($text, array('class' => 'disabled prev_page')).' ';
}
/**
* Generate the "next" HTML link.
*
* @return string
*/
public function next()
{
$text = Lang::line('pagination.next')->get($this->language);
if ($this->page < $this->last_page)
{
return $this->link($this->page + 1, $text, 'next_page');
}
return HTML::span($text, array('class' => 'disabled next_page'));
}
/**
* Build the first two page links for a sliding page range.
*
* @return string
*/
private function beginning()
{
return $this->range(1, 2).'<span class="dots">...</span>';
}
/**
* Build the last two page links for a sliding page range.
*
* @return string
*/
private function ending()
{
return '<span class="dots">...</span>'.$this->range($this->last_page - 1, $this->last_page);
}
/**
* Build a range of page links.
*
* For the current page, an HTML span element will be generated instead of a link.
*
* @param int $start
* @param int $end
* @return string
*/
private function range($start, $end)
{
$pages = '';
for ($i = $start; $i <= $end; $i++)
{
$pages .= ($this->page == $i) ? HTML::span($i, array('class' => 'current')).' ' : $this->link($i, $i, null).' ';
}
return $pages;
}
/**
* Create a HTML page link.
*
* @param int $page
* @param string $text
* @param string $attributes
* @return string
*/
private function link($page, $text, $class)
{
$append = '';
foreach ($this->append as $key => $value)
{
$append .= '&'.$key.'='.$value;
}
return HTML::link(Request::uri().'?page='.$page.$append, $text, compact('class'), Request::is_secure());
}
/**
* Set the language that should be used when generating page links.
*
* @param string $language
* @return Paginator
*/
public function lang($language)
{
$this->language = $language;
return $this;
}
/**
* Set the items that should be appended to the link query strings.
*
* @param array $values
* @return Paginator
*/
public function append($values)
{
$this->append = $values;
return $this;
}
}

93
laravel/redirect.php Normal file
View File

@@ -0,0 +1,93 @@
<?php namespace Laravel;
class Redirect {
/**
* The redirect response.
*
* @var Response
*/
public $response;
/**
* Create a new redirect instance.
*
* @param Response $response
* @return void
*/
public function __construct($response)
{
$this->response = $response;
}
/**
* Create a redirect response.
*
* @param string $url
* @param int $status
* @param string $method
* @param bool $https
* @return Redirect
*/
public static function to($url, $status = 302, $method = 'location', $https = false)
{
$url = URL::to($url, $https);
return ($method == 'refresh')
? new static(Response::make('', $status)->header('Refresh', '0;url='.$url))
: new static(Response::make('', $status)->header('Location', $url));
}
/**
* Create a redirect response to a HTTPS URL.
*
* @param string $url
* @param int $status
* @param string $method
* @return Response
*/
public static function to_secure($url, $status = 302, $method = 'location')
{
return static::to($url, $status, $method, true);
}
/**
* Add an item to the session flash data.
*
* @param string $key
* @param mixed $value
* @return Response
*/
public function with($key, $value)
{
if (Config::get('session.driver') == '')
{
throw new \Exception("Attempting to flash data to the session, but no session driver has been specified.");
}
Session::flash($key, $value);
return $this;
}
/**
* Magic Method to handle redirecting to routes.
*/
public static function __callStatic($method, $parameters)
{
$parameters = (isset($parameters[0])) ? $parameters[0] : array();
if (strpos($method, 'to_secure_') === 0)
{
return static::to(URL::to_route(substr($method, 10), $parameters, true));
}
if (strpos($method, 'to_') === 0)
{
return static::to(URL::to_route(substr($method, 3), $parameters));
}
throw new \Exception("Method [$method] is not defined on the Redirect class.");
}
}

169
laravel/request.php Normal file
View File

@@ -0,0 +1,169 @@
<?php namespace Laravel;
class Request {
/**
* The route handling the current request.
*
* @var Route
*/
public static $route;
/**
* The request URI.
*
* @var string
*/
public static $uri;
/**
* Get the request URI.
*
* If the request is to the root of application, a single forward slash will be returned.
*
* @return string
*/
public static function uri()
{
if ( ! is_null(static::$uri)) return static::$uri;
$uri = static::raw_uri();
if (strpos($uri, $base = parse_url(Config::get('application.url'), PHP_URL_PATH)) === 0)
{
$uri = substr($uri, strlen($base));
}
if (strpos($uri, $index = '/index.php') === 0)
{
$uri = substr($uri, strlen($index));
}
return static::$uri = (($uri = trim($uri, '/')) == '') ? '/' : $uri;
}
/**
* Get the raw request URI from the $_SERVER array.
*
* @return string
*/
private static function raw_uri()
{
if (isset($_SERVER['PATH_INFO']))
{
$uri = $_SERVER['PATH_INFO'];
}
elseif (isset($_SERVER['REQUEST_URI']))
{
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
}
else
{
throw new \Exception('Unable to determine the request URI.');
}
if ($uri === false)
{
throw new \Exception("Malformed request URI. Request terminated.");
}
return $uri;
}
/**
* Get the request method.
*
* @return string
*/
public static function method()
{
return (static::spoofed()) ? $_POST['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD'];
}
/**
* Determine if the request method is being spoofed by a hidden Form element.
*
* Hidden form elements are used to spoof PUT and DELETE requests since
* they are not supported by HTML forms.
*
* @return bool
*/
public static function spoofed()
{
return is_array($_POST) and array_key_exists('REQUEST_METHOD', $_POST);
}
/**
* Get the requestor's IP address.
*
* @return string
*/
public static function ip()
{
if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
{
return $_SERVER['HTTP_X_FORWARDED_FOR'];
}
elseif (isset($_SERVER['HTTP_CLIENT_IP']))
{
return $_SERVER['HTTP_CLIENT_IP'];
}
elseif (isset($_SERVER['REMOTE_ADDR']))
{
return $_SERVER['REMOTE_ADDR'];
}
}
/**
* Get the HTTP protocol for the request.
*
* @return string
*/
public static function protocol()
{
return (isset($_SERVER['HTTPS']) and $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
}
/**
* Determine if the request is using HTTPS.
*
* @return bool
*/
public static function is_secure()
{
return (static::protocol() == 'https');
}
/**
* Determine if the request is an AJAX request.
*
* @return bool
*/
public static function is_ajax()
{
return (isset($_SERVER['HTTP_X_REQUESTED_WITH']) and strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
}
/**
* Determine if the route handling the request is a given name.
*
* @param string $name
* @return bool
*/
public static function route_is($name)
{
return (is_array(static::$route->callback) and isset(static::$route->callback['name']) and static::$route->callback['name'] === $name);
}
/**
* Magic Method to handle dynamic static methods.
*/
public static function __callStatic($method, $parameters)
{
if (strpos($method, 'route_is_') === 0)
{
return static::route_is(substr($method, 9));
}
}
}

194
laravel/response.php Normal file
View File

@@ -0,0 +1,194 @@
<?php namespace Laravel;
class Response {
/**
* The content of the response.
*
* @var mixed
*/
public $content;
/**
* The HTTP status code of the response.
*
* @var int
*/
public $status;
/**
* The response headers.
*
* @var array
*/
public $headers = array();
/**
* HTTP status codes.
*
* @var array
*/
private $statuses = array(
100 => 'Continue',
101 => 'Switching Protocols',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-Status',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
307 => 'Temporary Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
507 => 'Insufficient Storage',
509 => 'Bandwidth Limit Exceeded'
);
/**
* Create a new response instance.
*
* @param mixed $content
* @param int $status
*/
public function __construct($content, $status = 200)
{
$this->content = $content;
$this->status = $status;
}
/**
* Factory for creating new response instances.
*
* @param string $content
* @param int $status
* @return Response
*/
public static function make($content, $status = 200)
{
return new static($content, $status);
}
/**
* Factory for creating new error response instances.
*
* @param int $code
* @param array $data
* @return Response
*/
public static function error($code, $data = array())
{
return static::make(View::make('error/'.$code, $data), $code);
}
/**
* Take a value returned by a route and prepare a Response instance.
*
* @param mixed $response
* @return Response
*/
public static function prepare($response)
{
if ($response instanceof Redirect) $response = $response->response;
return ( ! $response instanceof Response) ? new static($response) : $response;
}
/**
* Send the response to the browser.
*
* @return void
*/
public function send()
{
if ( ! array_key_exists('Content-Type', $this->headers))
{
$this->header('Content-Type', 'text/html; charset=utf-8');
}
if ( ! headers_sent()) $this->send_headers();
echo (string) $this->content;
}
/**
* Send the response headers to the browser.
*
* @return void
*/
public function send_headers()
{
$protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
header($protocol.' '.$this->status.' '.$this->statuses[$this->status]);
foreach ($this->headers as $name => $value)
{
header($name.': '.$value, true);
}
}
/**
* Add a header to the response.
*
* @param string $name
* @param string $value
* @return Response
*/
public function header($name, $value)
{
$this->headers[$name] = $value;
return $this;
}
/**
* Determine if the response is a redirect.
*
* @return bool
*/
public function is_redirect()
{
return $this->status == 301 or $this->status == 302;
}
/**
* Get the parsed content of the Response.
*/
public function __toString()
{
return (string) $this->content;
}
}

View File

@@ -0,0 +1,56 @@
<?php namespace Laravel\Routing;
class Filter {
/**
* The loaded route filters.
*
* @var array
*/
private static $filters = array();
/**
* Register a set of route filters.
*
* @param array $filters
* @return void
*/
public static function register($filters)
{
static::$filters = array_merge(static::$filters, $filters);
}
/**
* Clear all of the registered route filters.
*
* @return void
*/
public static function clear()
{
static::$filters = array();
}
/**
* Call a set of route filters.
*
* @param string $filter
* @param array $parameters
* @param bool $override
* @return mixed
*/
public static function call($filters, $parameters = array(), $override = false)
{
foreach (explode(', ', $filters) as $filter)
{
if ( ! isset(static::$filters[$filter])) continue;
$response = call_user_func_array(static::$filters[$filter], $parameters);
// "Before" filters may override the request cycle. For example, an authentication
// filter may redirect a user to a login view if they are not logged in. Because of
// this, we will return the first filter response if overriding is enabled.
if ( ! is_null($response) and $override) return $response;
}
}
}

View File

@@ -0,0 +1,40 @@
<?php namespace Laravel\Routing;
class Finder {
/**
* The named routes that have been found so far.
*
* @var array
*/
public static $names = array();
/**
* Find a named route in a given array of routes.
*
* @param string $name
* @param array $routes
* @return array
*/
public static function find($name, $routes)
{
if (array_key_exists($name, static::$names)) return static::$names[$name];
$arrayIterator = new \RecursiveArrayIterator($routes);
$recursiveIterator = new \RecursiveIteratorIterator($arrayIterator);
// Since routes can be nested deep within sub-directories, we need to recursively
// iterate through each directory and gather all of the routes.
foreach ($recursiveIterator as $iterator)
{
$route = $recursiveIterator->getSubIterator();
if (isset($route['name']) and $route['name'] == $name)
{
return static::$names[$name] = array($arrayIterator->key() => iterator_to_array($route));
}
}
}
}

126
laravel/routing/loader.php Normal file
View File

@@ -0,0 +1,126 @@
<?php namespace Laravel\Routing;
use Laravel\Config;
class Loader {
/**
* All of the routes for the application.
*
* @var array
*/
private static $routes;
/**
* The path where the routes are located.
*
* @var string
*/
public $path;
/**
* Create a new route loader instance.
*
* @param string $path
* @return void
*/
public function __construct($path)
{
$this->path = $path;
}
/**
* Load the appropriate routes for the request URI.
*
* @param string
* @return array
*/
public function load($uri)
{
$base = (file_exists($path = $this->path.'routes'.EXT)) ? require $path : array();
return array_merge($this->load_nested_routes(explode('/', $uri)), $base);
}
/**
* Load the appropriate routes from the routes directory.
*
* @param array $segments
* @return array
*/
private function load_nested_routes($segments)
{
// If the request URI only more than one segment, and the last segment contains a dot, we will
// assume the request is for a specific format (users.json or users.xml) and strip off
// everything after the dot so we can load the appropriate file.
if (count($segments) > 0 and strpos(end($segments), '.') !== false)
{
$segment = array_pop($segments);
array_push($segments, substr($segment, 0, strpos($segment, '.')));
}
// Since it is no part of the route directory structure, shift the module name off of the
// beginning of the array so we can locate the appropriate route file.
if (count($segments) > 0 and ACTIVE_MODULE !== DEFAULT_MODULE)
{
array_shift($segments);
}
// Work backwards through the URI segments until we find the deepest possible
// matching route directory. Once we find it, we will return those routes.
foreach (array_reverse($segments, true) as $key => $value)
{
if (file_exists($path = $this->path.'routes/'.implode('/', array_slice($segments, 0, $key + 1)).EXT))
{
return require $path;
}
}
return array();
}
/**
* Get all of the routes for the application.
*
* To improve performance, this operation will only be performed once. The routes
* will be cached and returned on every subsequent call.
*
* @param bool $reload
* @return array
*/
public static function all($reload = false)
{
if ( ! is_null(static::$routes) and ! $reload) return static::$routes;
$routes = array();
foreach (Module::paths() as $path)
{
if (file_exists($path.'routes'.EXT))
{
$routes = array_merge($routes, require $path.'routes'.EXT);
}
if (is_dir($path.'routes'))
{
// Since route files can be nested deep within the route directory, we need to
// recursively spin through the directory to find every file.
$directoryIterator = new \RecursiveDirectoryIterator($path.'routes');
$recursiveIterator = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST);
foreach ($recursiveIterator as $file)
{
if (filetype($file) === 'file' and strpos($file, EXT) !== false)
{
$routes = array_merge($routes, require $file);
}
}
}
}
return static::$routes = $routes;
}
}

105
laravel/routing/route.php Normal file
View File

@@ -0,0 +1,105 @@
<?php namespace Laravel\Routing;
use Laravel\Package;
use Laravel\Response;
class Route {
/**
* The route key, including request method and URI.
*
* @var string
*/
public $key;
/**
* The route callback or array.
*
* @var mixed
*/
public $callback;
/**
* The parameters that will passed to the route function.
*
* @var array
*/
public $parameters;
/**
* Create a new Route instance.
*
* @param string $key
* @param mixed $callback
* @param array $parameters
* @return void
*/
public function __construct($key, $callback, $parameters = array())
{
$this->key = $key;
$this->callback = $callback;
$this->parameters = $parameters;
}
/**
* Execute the route function.
*
* @param mixed $route
* @param array $parameters
* @return Response
*/
public function call()
{
$response = null;
// The callback may be in array form, meaning it has attached filters or is named and we
// will need to evaluate it further to determine what to do. If the callback is just a
// closure, we can execute it now and return the result.
if (is_callable($this->callback))
{
$response = call_user_func_array($this->callback, $this->parameters);
}
elseif (is_array($this->callback))
{
if (isset($this->callback['needs']))
{
Package::load(explode(', ', $this->callback['needs']));
}
$response = isset($this->callback['before']) ? Filter::call($this->callback['before'], array(), true) : null;
if (is_null($response) and ! is_null($handler = $this->find_route_function()))
{
$response = call_user_func_array($handler, $this->parameters);
}
}
$response = Response::prepare($response);
if (is_array($this->callback) and isset($this->callback['after']))
{
Filter::call($this->callback['after'], array($response));
}
return $response;
}
/**
* Extract the route function from the route.
*
* If a "do" index is specified on the callback, that is the handler.
* Otherwise, we will return the first callable array value.
*
* @return Closure
*/
private function find_route_function()
{
if (isset($this->callback['do'])) return $this->callback['do'];
foreach ($this->callback as $value)
{
if (is_callable($value)) return $value;
}
}
}

116
laravel/routing/router.php Normal file
View File

@@ -0,0 +1,116 @@
<?php namespace Laravel\Routing;
use Laravel\Request;
class Router {
/**
* The request method and URI.
*
* @var string
*/
public $request;
/**
* All of the loaded routes.
*
* @var array
*/
public $routes;
/**
* Create a new router for a request method and URI.
*
* @param string $method
* @param string $uri
* @param Loader $loader
* @return void
*/
public function __construct($method, $uri, $loader)
{
// Put the request method and URI in route form. Routes begin with
// the request method and a forward slash.
$this->request = $method.' /'.trim($uri, '/');
$this->routes = $loader->load($uri);
}
/**
* Create a new router for a request method and URI.
*
* @param string $method
* @param string $uri
* @param Loader $loader
* @return Router
*/
public static function make($method, $uri, $loader)
{
return new static($method, $uri, $loader);
}
/**
* Search a set of routes for the route matching a method and URI.
*
* @return Route
*/
public function route()
{
// Check for a literal route match first. If we find one, there is
// no need to spin through all of the routes.
if (isset($this->routes[$this->request]))
{
return Request::$route = new Route($this->request, $this->routes[$this->request]);
}
foreach ($this->routes as $keys => $callback)
{
// Only check routes that have multiple URIs or wildcards.
// Other routes would have been caught by the check for literal matches.
if (strpos($keys, '(') !== false or strpos($keys, ',') !== false )
{
foreach (explode(', ', $keys) as $key)
{
if (preg_match('#^'.$this->translate_wildcards($key).'$#', $this->request))
{
return Request::$route = new Route($keys, $callback, $this->parameters($this->request, $key));
}
}
}
}
}
/**
* Translate route URI wildcards into actual regular expressions.
*
* @param string $key
* @return string
*/
private function translate_wildcards($key)
{
$replacements = 0;
// For optional parameters, first translate the wildcards to their
// regex equivalent, sans the ")?" ending. We will add the endings
// back on after we know how many replacements we made.
$key = str_replace(array('/(:num?)', '/(:any?)'), array('(?:/([0-9]+)', '(?:/([a-zA-Z0-9\.\-_]+)'), $key, $replacements);
$key .= ($replacements > 0) ? str_repeat(')?', $replacements) : '';
return str_replace(array(':num', ':any'), array('[0-9]+', '[a-zA-Z0-9\.\-_]+'), $key);
}
/**
* Extract the parameters from a URI based on a route URI.
*
* Any route segment wrapped in parentheses is considered a parameter.
*
* @param string $uri
* @param string $route
* @return array
*/
private function parameters($uri, $route)
{
return array_values(array_intersect_key(explode('/', $uri), preg_grep('/\(.+\)/', explode('/', $route))));
}
}

238
laravel/session.php Normal file
View File

@@ -0,0 +1,238 @@
<?php namespace Laravel;
class Session {
/**
* The active session driver.
*
* @var Session\Driver
*/
public static $driver;
/**
* The session payload, which contains the session ID, data and last activity timestamp.
*
* @var array
*/
public static $session = array();
/**
* Get the session driver.
*
* @return Session\Driver
*/
public static function driver()
{
if (is_null(static::$driver))
{
switch (Config::get('session.driver'))
{
case 'cookie':
return static::$driver = new Session\Cookie;
case 'file':
return static::$driver = new Session\File;
case 'db':
return static::$driver = new Session\DB;
case 'memcached':
return static::$driver = new Session\Memcached;
case 'apc':
return static::$driver = new Session\APC;
default:
throw new \Exception("Session driver [$driver] is not supported.");
}
}
return static::$driver;
}
/**
* Load a user session by ID.
*
* @param string $id
* @return void
*/
public static function load($id)
{
static::$session = ( ! is_null($id)) ? static::driver()->load($id) : null;
if (static::invalid(static::$session))
{
static::$session = array('id' => Str::random(40), 'data' => array());
}
if ( ! static::has('csrf_token'))
{
static::put('csrf_token', Str::random(16));
}
static::$session['last_activity'] = time();
}
/**
* Determine if a session is valid.
*
* A session is considered valid if it exists and has not expired.
*
* @param array $session
* @return bool
*/
private static function invalid($session)
{
return is_null($session) or (time() - $session['last_activity']) > (Config::get('session.lifetime') * 60);
}
/**
* Determine if the session or flash data contains an item.
*
* @param string $key
* @return bool
*/
public static function has($key)
{
return ( ! is_null(static::get($key)));
}
/**
* Get an item from the session or flash data.
*
* @param string $key
* @return mixed
*/
public static function get($key, $default = null)
{
foreach (array($key, ':old:'.$key, ':new:'.$key) as $possibility)
{
if (array_key_exists($possibility, static::$session['data'])) return static::$session['data'][$possibility];
}
return is_callable($default) ? call_user_func($default) : $default;
}
/**
* Write an item to the session.
*
* @param string $key
* @param mixed $value
* @return void
*/
public static function put($key, $value)
{
static::$session['data'][$key] = $value;
}
/**
* Write an item to the session flash data.
*
* @param string $key
* @param mixed $value
* @return void
*/
public static function flash($key, $value)
{
static::put(':new:'.$key, $value);
}
/**
* Remove an item from the session.
*
* @param string $key
* @return void
*/
public static function forget($key)
{
unset(static::$session['data'][$key]);
}
/**
* Remove all items from the session.
*
* @return void
*/
public static function flush()
{
static::$session['data'] = array();
}
/**
* Regenerate the session ID.
*
* @return void
*/
public static function regenerate()
{
static::driver()->delete(static::$session['id']);
static::$session['id'] = Str::random(40);
}
/**
* 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 old input data will also be
* stored in the session flash data.
*
* @return void
*/
public static function close()
{
static::flash('laravel_old_input', Input::get());
static::age_flash();
static::driver()->save(static::$session);
static::write_cookie();
if (mt_rand(1, 100) <= 2 and static::driver() instanceof Session\Sweeper)
{
static::driver()->sweep(time() - (Config::get('session.lifetime') * 60));
}
}
/**
* Age the session flash data.
*
* @return void
*/
private static function age_flash()
{
foreach (static::$session['data'] as $key => $value)
{
if (strpos($key, ':old:') === 0) static::forget($key);
}
foreach (static::$session['data'] as $key => $value)
{
if (strpos($key, ':new:') === 0)
{
static::put(':old:'.substr($key, 5), $value);
static::forget($key);
}
}
}
/**
* Write the session cookie.
*
* @return void
*/
private static function write_cookie()
{
if ( ! headers_sent())
{
extract(Config::get('session'));
$minutes = ($expire_on_close) ? 0 : $lifetime;
Cookie::put('laravel_session', static::$session['id'], $minutes, $path, $domain, $https, $http_only);
}
}
}

41
laravel/session/apc.php Normal file
View File

@@ -0,0 +1,41 @@
<?php namespace Laravel\Session;
use Laravel\Cache;
use Laravel\Config;
class APC implements Driver {
/**
* Load a session by ID.
*
* @param string $id
* @return array
*/
public function load($id)
{
return Cache::driver('apc')->get($id);
}
/**
* Save a session.
*
* @param array $session
* @return void
*/
public function save($session)
{
Cache::driver('apc')->put($session['id'], $session, Config::get('session.lifetime'));
}
/**
* Delete a session by ID.
*
* @param string $id
* @return void
*/
public function delete($id)
{
Cache::driver('apc')->forget($id);
}
}

View File

@@ -0,0 +1,73 @@
<?php namespace Laravel\Session;
use Laravel\Config;
use Laravel\Crypter;
class Cookie implements Driver {
/**
* The Crypter instance.
*
* @var Crypter
*/
private $crypter;
/**
* Create a new Cookie session driver instance.
*
* @return void
*/
public function __construct()
{
$this->crypter = new Crypter;
if (Config::get('application.key') == '')
{
throw new \Exception("You must set an application key before using the Cookie session driver.");
}
}
/**
* Load a session by ID.
*
* @param string $id
* @return array
*/
public function load($id)
{
if (\System\Cookie::has('session_payload'))
{
return unserialize($this->crypter->decrypt(\System\Cookie::get('session_payload')));
}
}
/**
* Save a session.
*
* @param array $session
* @return void
*/
public function save($session)
{
if ( ! headers_sent())
{
extract(Config::get('session'));
$payload = $this->crypter->encrypt(serialize($session));
\System\Cookie::put('session_payload', $payload, $lifetime, $path, $domain, $https, $http_only);
}
}
/**
* Delete a session by ID.
*
* @param string $id
* @return void
*/
public function delete($id)
{
\System\Cookie::forget('session_payload');
}
}

76
laravel/session/db.php Normal file
View File

@@ -0,0 +1,76 @@
<?php namespace Laravel\Session;
use Laravel\Config;
class DB implements Driver, Sweeper {
/**
* Load a session by ID.
*
* @param string $id
* @return array
*/
public function load($id)
{
$session = $this->table()->find($id);
if ( ! is_null($session))
{
return array(
'id' => $session->id,
'last_activity' => $session->last_activity,
'data' => unserialize($session->data)
);
}
}
/**
* Save a session.
*
* @param array $session
* @return void
*/
public function save($session)
{
$this->delete($session['id']);
$this->table()->insert(array(
'id' => $session['id'],
'last_activity' => $session['last_activity'],
'data' => serialize($session['data'])
));
}
/**
* Delete a session by ID.
*
* @param string $id
* @return void
*/
public function delete($id)
{
$this->table()->delete($id);
}
/**
* Delete all expired sessions.
*
* @param int $expiration
* @return void
*/
public function sweep($expiration)
{
$this->table()->where('last_activity', '<', $expiration)->delete();
}
/**
* Get a session database query.
*
* @return Query
*/
private function table()
{
return \System\DB::connection()->table(Config::get('session.table'));
}
}

View File

@@ -0,0 +1,29 @@
<?php namespace Laravel\Session;
interface Driver {
/**
* Load a session by ID.
*
* @param string $id
* @return array
*/
public function load($id);
/**
* Save a session.
*
* @param array $session
* @return void
*/
public function save($session);
/**
* Delete a session by ID.
*
* @param string $id
* @return void
*/
public function delete($id);
}

52
laravel/session/file.php Normal file
View File

@@ -0,0 +1,52 @@
<?php namespace Laravel\Session;
class File implements Driver, Sweeper {
/**
* Load a session by ID.
*
* @param string $id
* @return array
*/
public function load($id)
{
if (file_exists($path = SESSION_PATH.$id)) return unserialize(file_get_contents($path));
}
/**
* Save a session.
*
* @param array $session
* @return void
*/
public function save($session)
{
file_put_contents(SESSION_PATH.$session['id'], serialize($session), LOCK_EX);
}
/**
* Delete a session by ID.
*
* @param string $id
* @return void
*/
public function delete($id)
{
@unlink(SESSION_PATH.$id);
}
/**
* Delete all expired sessions.
*
* @param int $expiration
* @return void
*/
public function sweep($expiration)
{
foreach (glob(SESSION_PATH.'*') as $file)
{
if (filetype($file) == 'file' and filemtime($file) < $expiration) @unlink($file);
}
}
}

View File

@@ -0,0 +1,41 @@
<?php namespace Laravel\Session;
use Laravel\Cache;
use Laravel\Config;
class Memcached implements Driver {
/**
* Load a session by ID.
*
* @param string $id
* @return array
*/
public function load($id)
{
return Cache::driver('memcached')->get($id);
}
/**
* Save a session.
*
* @param array $session
* @return void
*/
public function save($session)
{
Cache::driver('memcached')->put($session['id'], $session, Config::get('session.lifetime'));
}
/**
* Delete a session by ID.
*
* @param string $id
* @return void
*/
public function delete($id)
{
Cache::driver('memcached')->forget($id);
}
}

View File

@@ -0,0 +1,13 @@
<?php namespace Laravel\Session;
interface Sweeper {
/**
* Delete all expired sessions.
*
* @param int $expiration
* @return void
*/
public function sweep($expiration);
}

116
laravel/str.php Normal file
View File

@@ -0,0 +1,116 @@
<?php namespace Laravel;
class Str {
/**
* Convert HTML characters to entities.
*
* @param string $value
* @return string
*/
public static function entities($value)
{
return htmlentities($value, ENT_QUOTES, Config::get('application.encoding'), false);
}
/**
* Convert a string to lowercase.
*
* @param string $value
* @return string
*/
public static function lower($value)
{
return function_exists('mb_strtolower') ? mb_strtolower($value, Config::get('application.encoding')) : strtolower($value);
}
/**
* Convert a string to uppercase.
*
* @param string $value
* @return string
*/
public static function upper($value)
{
return function_exists('mb_strtoupper') ? mb_strtoupper($value, Config::get('application.encoding')) : strtoupper($value);
}
/**
* Convert a string to title case (ucwords).
*
* @param string $value
* @return string
*/
public static function title($value)
{
return (function_exists('mb_convert_case')) ? mb_convert_case($value, MB_CASE_TITLE, Config::get('application.encoding')) : ucwords(strtolower($value));
}
/**
* Get the length of a string.
*
* @param string $value
* @return int
*/
public static function length($value)
{
return function_exists('mb_strlen') ? mb_strlen($value, Config::get('application.encoding')) : strlen($value);
}
/**
* Convert a string to 7-bit ASCII.
*
* @param string $value
* @return string
*/
public static function ascii($value)
{
$foreign = Config::get('ascii');
$value = preg_replace(array_keys($foreign), array_values($foreign), $value);
return preg_replace('/[^\x09\x0A\x0D\x20-\x7E]/', '', $value);
}
/**
* Generate a random alpha or alpha-numeric string.
*
* Supported types: 'alpha_num' and 'alpha'.
*
* @param int $length
* @param string $type
* @return string
*/
public static function random($length = 16, $type = 'alpha_num')
{
$value = '';
$pool_length = strlen($pool = static::pool($type)) - 1;
for ($i = 0; $i < $length; $i++)
{
$value .= $pool[mt_rand(0, $pool_length)];
}
return $value;
}
/**
* Get a chracter pool.
*
* @param string $type
* @return string
*/
private static function pool($type = 'alpha_num')
{
switch ($type)
{
case 'alpha_num':
return '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
default:
return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
}
}
}

142
laravel/url.php Normal file
View File

@@ -0,0 +1,142 @@
<?php namespace Laravel;
class URL {
/**
* Generate an application URL.
*
* If the given URL is already well-formed, it will be returned unchanged.
*
* @param string $url
* @param bool $https
* @param bool $asset
* @return string
*/
public static function to($url = '', $https = false, $asset = false)
{
if (filter_var($url, FILTER_VALIDATE_URL) !== false) return $url;
$base = Config::get('application.url').'/'.Config::get('application.index');
if ($asset and Config::get('application.index') !== '')
{
$base = str_replace('/'.Config::get('application.index'), '', $base);
}
if ($https and strpos($base, 'http://') === 0)
{
$base = 'https://'.substr($base, 7);
}
return rtrim($base, '/').'/'.trim($url, '/');
}
/**
* Generate an application URL with HTTPS.
*
* @param string $url
* @return string
*/
public static function to_secure($url = '')
{
return static::to($url, true);
}
/**
* Generate an application URL to an asset. The index file
* will not be added to the URL.
*
* @param string $url
* @return string
*/
public static function to_asset($url)
{
return static::to($url, Request::is_secure(), true);
}
/**
* Generate a URL from a route name.
*
* For routes that have wildcard parameters, an array may be passed as the
* second parameter to the method. The values of this array will be used
* to fill the wildcard segments of the route URI.
*
* @param string $name
* @param array $parameters
* @param bool $https
* @return string
*/
public static function to_route($name, $parameters = array(), $https = false)
{
if ( ! is_null($route = Routing\Finder::find($name, Routing\Loader::all())))
{
$uris = explode(', ', key($route));
$uri = substr($uris[0], strpos($uris[0], '/'));
foreach ($parameters as $parameter)
{
$uri = preg_replace('/\(.+?\)/', $parameter, $uri, 1);
}
$uri = str_replace(array('/(:any?)', '/(:num?)'), '', $uri);
return static::to($uri, $https);
}
throw new \Exception("Error generating named route for route [$name]. Route is not defined.");
}
/**
* Generate a HTTPS URL from a route name.
*
* @param string $name
* @param array $parameters
* @return string
*/
public static function to_secure_route($name, $parameters = array())
{
return static::to_route($name, $parameters, true);
}
/**
* Generate a URL friendly "slug".
*
* @param string $title
* @param string $separator
* @return string
*/
public static function slug($title, $separator = '-')
{
$title = Str::ascii($title);
// Remove all characters that are not the separator, letters, numbers, or whitespace.
$title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', Str::lower($title));
// Replace all separator characters and whitespace by a single separator
$title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);
return trim($title, $separator);
}
/**
* Magic Method for dynamically creating route URLs.
*/
public static function __callStatic($method, $parameters)
{
$parameters = (isset($parameters[0])) ? $parameters[0] : array();
if (strpos($method, 'to_secure_') === 0)
{
return static::to_route(substr($method, 10), $parameters, true);
}
if (strpos($method, 'to_') === 0)
{
return static::to_route(substr($method, 3), $parameters);
}
throw new \Exception("Method [$method] is not defined on the URL class.");
}
}

517
laravel/validator.php Normal file
View File

@@ -0,0 +1,517 @@
<?php namespace Laravel;
class Validator {
/**
* The array being validated.
*
* @var array
*/
public $attributes;
/**
* The validation rules.
*
* @var array
*/
public $rules;
/**
* The validation messages.
*
* @var array
*/
public $messages;
/**
* The post-validation error messages.
*
* @var array
*/
public $errors;
/**
* The language that should be used when retrieving error messages.
*
* @var string
*/
public $language;
/**
* The size related validation rules.
*
* @var array
*/
protected $size_rules = array('size', 'between', 'min', 'max');
/**
* The numeric related validation rules.
*
* @var array
*/
protected $numeric_rules = array('numeric', 'integer');
/**
* Create a new validator instance.
*
* @param array $attributes
* @param array $rules
* @param array $messages
* @return void
*/
public function __construct($attributes, $rules, $messages = array())
{
foreach ($rules as $key => &$rule)
{
$rule = (is_string($rule)) ? explode('|', $rule) : $rule;
}
$this->attributes = $attributes;
$this->messages = $messages;
$this->rules = $rules;
}
/**
* Factory for creating new validator instances.
*
* @param array $attributes
* @param array $rules
* @param array $messages
* @return Validator
*/
public static function make($attributes, $rules, $messages = array())
{
return new static($attributes, $rules, $messages);
}
/**
* Validate the target array using the specified validation rules.
*
* @return bool
*/
public function invalid()
{
return ! $this->valid();
}
/**
* Validate the target array using the specified validation rules.
*
* @return bool
*/
public function valid()
{
$this->errors = new Messages;
foreach ($this->rules as $attribute => $rules)
{
foreach ($rules as $rule)
{
$this->check($attribute, $rule);
}
}
return count($this->errors->messages) == 0;
}
/**
* Evaluate an attribute against a validation rule.
*
* @param string $attribute
* @param string $rule
* @return void
*/
protected function check($attribute, $rule)
{
list($rule, $parameters) = $this->parse($rule);
if ( ! method_exists($this, $validator = 'validate_'.$rule))
{
throw new \Exception("Validation rule [$rule] doesn't exist.");
}
// No validation will be run for attributes that do not exist unless the rule being validated
// is "required" or "accepted". No other rules have implicit "required" checks.
if ( ! static::validate_required($attribute) and ! in_array($rule, array('required', 'accepted'))) return;
if ( ! $this->$validator($attribute, $parameters))
{
$this->errors->add($attribute, $this->format_message($this->get_message($attribute, $rule), $attribute, $rule, $parameters));
}
}
/**
* Validate that a required attribute exists in the attributes array.
*
* @param string $attribute
* @return bool
*/
protected function validate_required($attribute)
{
if ( ! array_key_exists($attribute, $this->attributes)) return false;
if (is_string($this->attributes[$attribute]) and trim($this->attributes[$attribute]) === '') return false;
return true;
}
/**
* Validate that an attribute has a matching confirmation attribute.
*
* @param string $attribute
* @return bool
*/
protected function validate_confirmed($attribute)
{
return array_key_exists($attribute.'_confirmation', $this->attributes) and $this->attributes[$attribute] == $this->attributes[$attribute.'_confirmation'];
}
/**
* Validate that an attribute was "accepted".
*
* This validation rule implies the attribute is "required".
*
* @param string $attribute
* @return bool
*/
protected function validate_accepted($attribute)
{
return static::validate_required($attribute) and ($this->attributes[$attribute] == 'yes' or $this->attributes[$attribute] == '1');
}
/**
* Validate that an attribute is numeric.
*
* @param string $attribute
* @return bool
*/
protected function validate_numeric($attribute)
{
return is_numeric($this->attributes[$attribute]);
}
/**
* Validate that an attribute is an integer.
*
* @param string $attribute
* @return bool
*/
protected function validate_integer($attribute)
{
return filter_var($this->attributes[$attribute], FILTER_VALIDATE_INT) !== false;
}
/**
* Validate the size of an attribute.
*
* @param string $attribute
* @param array $parameters
* @return bool
*/
protected function validate_size($attribute, $parameters)
{
return $this->get_size($attribute) == $parameters[0];
}
/**
* Validate the size of an attribute is between a set of values.
*
* @param string $attribute
* @param array $parameters
* @return bool
*/
protected function validate_between($attribute, $parameters)
{
return $this->get_size($attribute) >= $parameters[0] and $this->get_size($attribute) <= $parameters[1];
}
/**
* Validate the size of an attribute is greater than a minimum value.
*
* @param string $attribute
* @param array $parameters
* @return bool
*/
protected function validate_min($attribute, $parameters)
{
return $this->get_size($attribute) >= $parameters[0];
}
/**
* Validate the size of an attribute is less than a maximum value.
*
* @param string $attribute
* @param array $parameters
* @return bool
*/
protected function validate_max($attribute, $parameters)
{
return $this->get_size($attribute) <= $parameters[0];
}
/**
* Get the size of an attribute.
*
* @param string $attribute
* @return mixed
*/
protected function get_size($attribute)
{
if (is_numeric($this->attributes[$attribute]) and $this->has_rule($attribute, $this->numeric_rules))
{
return $this->attributes[$attribute];
}
return (array_key_exists($attribute, $_FILES)) ? $this->attributes[$attribute]['size'] / 1024 : Str::length(trim($this->attributes[$attribute]));
}
/**
* Validate an attribute is contained within a list of values.
*
* @param string $attribute
* @param array $parameters
* @return bool
*/
protected function validate_in($attribute, $parameters)
{
return in_array($this->attributes[$attribute], $parameters);
}
/**
* Validate an attribute is not contained within a list of values.
*
* @param string $attribute
* @param array $parameters
* @return bool
*/
protected function validate_not_in($attribute, $parameters)
{
return ! in_array($this->attributes[$attribute], $parameters);
}
/**
* Validate the uniqueness of an attribute value on a given database table.
*
* If a database column is not specified, the attribute name will be used.
*
* @param string $attribute
* @param array $parameters
* @return bool
*/
protected function validate_unique($attribute, $parameters)
{
if ( ! isset($parameters[1])) $parameters[1] = $attribute;
return DB::connection()->table($parameters[0])->where($parameters[1], '=', $this->attributes[$attribute])->count() == 0;
}
/**
* Validate than an attribute is a valid e-mail address.
*
* @param string $attribute
* @return bool
*/
protected function validate_email($attribute)
{
return filter_var($this->attributes[$attribute], FILTER_VALIDATE_EMAIL) !== false;
}
/**
* Validate than an attribute is a valid URL.
*
* @param string $attribute
* @return bool
*/
protected function validate_url($attribute)
{
return filter_var($this->attributes[$attribute], FILTER_VALIDATE_URL) !== false;
}
/**
* Validate that an attribute is an active URL.
*
* @param string $attribute
* @return bool
*/
protected function validate_active_url($attribute)
{
$url = str_replace(array('http://', 'https://', 'ftp://'), '', Str::lower($this->attributes[$attribute]));
return checkdnsrr($url);
}
/**
* Validate the MIME type of a file is an image MIME type.
*
* @param string $attribute
* @return bool
*/
protected function validate_image($attribute)
{
return static::validate_mimes($attribute, array('jpg', 'png', 'gif', 'bmp'));
}
/**
* Validate than an attribute contains only alphabetic characters.
*
* @param string $attribute
* @return bool
*/
protected function validate_alpha($attribute)
{
return preg_match('/^([a-z])+$/i', $this->attributes[$attribute]);
}
/**
* Validate than an attribute contains only alpha-numeric characters.
*
* @param string $attribute
* @return bool
*/
protected function validate_alpha_num($attribute)
{
return preg_match('/^([a-z0-9])+$/i', $this->attributes[$attribute]);
}
/**
* Validate than an attribute contains only alpha-numeric characters, dashes, and underscores.
*
* @param string $attribute
* @return bool
*/
protected function validate_alpha_dash($attribute)
{
return preg_match('/^([-a-z0-9_-])+$/i', $this->attributes[$attribute]);
}
/**
* Validate the MIME type of a file upload attribute is in a set of MIME types.
*
* @param string $attribute
* @param array $parameters
* @return bool
*/
protected function validate_mimes($attribute, $parameters)
{
foreach ($parameters as $extension)
{
if (File::is($extension, $this->attributes[$attribute]['tmp_name'])) return true;
}
return false;
}
/**
* Get the proper error message for an attribute and rule.
*
* Developer specified attribute specific rules take first priority.
* Developer specified error rules take second priority.
*
* If the message has not been specified by the developer, the default will be used
* from the validation language file.
*
* @param string $attribute
* @param string $rule
* @return string
*/
protected function get_message($attribute, $rule)
{
if (array_key_exists($attribute.'_'.$rule, $this->messages))
{
return $this->messages[$attribute.'_'.$rule];
}
elseif (array_key_exists($rule, $this->messages))
{
return $this->messages[$rule];
}
else
{
$message = Lang::line('validation.'.$rule)->get($this->language);
// For "size" rules that are validating strings or files, we need to adjust
// the default error message for the appropriate type.
if (in_array($rule, $this->size_rules) and ! $this->has_rule($attribute, $this->numeric_rules))
{
return (array_key_exists($attribute, $_FILES))
? rtrim($message, '.').' '.Lang::line('validation.kilobytes')->get($this->language).'.'
: rtrim($message, '.').' '.Lang::line('validation.characters')->get($this->language).'.';
}
return $message;
}
}
/**
* Replace all error message place-holders with actual values.
*
* @param string $message
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return string
*/
protected function format_message($message, $attribute, $rule, $parameters)
{
$display = Lang::line('attributes.'.$attribute)->get($this->language, str_replace('_', ' ', $attribute));
$message = str_replace(':attribute', $display, $message);
if (in_array($rule, $this->size_rules))
{
$max = ($rule == 'between') ? $parameters[1] : $parameters[0];
$message = str_replace(array(':size', ':min', ':max'), array($parameters[0], $parameters[0], $max), $message);
}
elseif (in_array($rule, array('in', 'not_in', 'mimes')))
{
$message = str_replace(':values', implode(', ', $parameters), $message);
}
return $message;
}
/**
* Determine if an attribute has a rule assigned to it.
*
* @param string $attribute
* @param array $rules
* @return bool
*/
protected function has_rule($attribute, $rules)
{
foreach ($this->rules[$attribute] as $rule)
{
list($rule, $parameters) = $this->parse($rule);
if (in_array($rule, $rules)) return true;
}
return false;
}
/**
* Extract the rule name and parameters from a rule.
*
* @param string $rule
* @return array
*/
protected function parse($rule)
{
$parameters = (($colon = strpos($rule, ':')) !== false) ? explode(',', substr($rule, $colon + 1)) : array();
return array(is_numeric($colon) ? substr($rule, 0, $colon) : $rule, $parameters);
}
/**
* Set the language that should be used when retrieving error messages.
*
* @param string $language
* @return Validator
*/
public function lang($language)
{
$this->language = $language;
return $this;
}
}

253
laravel/vendor/phpass.php vendored Normal file
View File

@@ -0,0 +1,253 @@
<?php
#
# Portable PHP password hashing framework.
#
# Version 0.3 / genuine.
#
# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
# the public domain. Revised in subsequent years, still public domain.
#
# There's absolutely no warranty.
#
# The homepage URL for this framework is:
#
# http://www.openwall.com/phpass/
#
# Please be sure to update the Version line if you edit this file in any way.
# It is suggested that you leave the main version number intact, but indicate
# your project name (after the slash) and add your own revision information.
#
# Please do not change the "private" password hashing method implemented in
# here, thereby making your hashes incompatible. However, if you must, please
# change the hash type identifier (the "$P$") to something different.
#
# Obviously, since this code is in the public domain, the above are not
# requirements (there can be none), but merely suggestions.
#
class PasswordHash {
var $itoa64;
var $iteration_count_log2;
var $portable_hashes;
var $random_state;
function PasswordHash($iteration_count_log2, $portable_hashes)
{
$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
$iteration_count_log2 = 8;
$this->iteration_count_log2 = $iteration_count_log2;
$this->portable_hashes = $portable_hashes;
$this->random_state = microtime();
if (function_exists('getmypid'))
$this->random_state .= getmypid();
}
function get_random_bytes($count)
{
$output = '';
if (is_readable('/dev/urandom') &&
($fh = @fopen('/dev/urandom', 'rb'))) {
$output = fread($fh, $count);
fclose($fh);
}
if (strlen($output) < $count) {
$output = '';
for ($i = 0; $i < $count; $i += 16) {
$this->random_state =
md5(microtime() . $this->random_state);
$output .=
pack('H*', md5($this->random_state));
}
$output = substr($output, 0, $count);
}
return $output;
}
function encode64($input, $count)
{
$output = '';
$i = 0;
do {
$value = ord($input[$i++]);
$output .= $this->itoa64[$value & 0x3f];
if ($i < $count)
$value |= ord($input[$i]) << 8;
$output .= $this->itoa64[($value >> 6) & 0x3f];
if ($i++ >= $count)
break;
if ($i < $count)
$value |= ord($input[$i]) << 16;
$output .= $this->itoa64[($value >> 12) & 0x3f];
if ($i++ >= $count)
break;
$output .= $this->itoa64[($value >> 18) & 0x3f];
} while ($i < $count);
return $output;
}
function gensalt_private($input)
{
$output = '$P$';
$output .= $this->itoa64[min($this->iteration_count_log2 +
((PHP_VERSION >= '5') ? 5 : 3), 30)];
$output .= $this->encode64($input, 6);
return $output;
}
function crypt_private($password, $setting)
{
$output = '*0';
if (substr($setting, 0, 2) == $output)
$output = '*1';
$id = substr($setting, 0, 3);
# We use "$P$", phpBB3 uses "$H$" for the same thing
if ($id != '$P$' && $id != '$H$')
return $output;
$count_log2 = strpos($this->itoa64, $setting[3]);
if ($count_log2 < 7 || $count_log2 > 30)
return $output;
$count = 1 << $count_log2;
$salt = substr($setting, 4, 8);
if (strlen($salt) != 8)
return $output;
# We're kind of forced to use MD5 here since it's the only
# cryptographic primitive available in all versions of PHP
# currently in use. To implement our own low-level crypto
# in PHP would result in much worse performance and
# consequently in lower iteration counts and hashes that are
# quicker to crack (by non-PHP code).
if (PHP_VERSION >= '5') {
$hash = md5($salt . $password, TRUE);
do {
$hash = md5($hash . $password, TRUE);
} while (--$count);
} else {
$hash = pack('H*', md5($salt . $password));
do {
$hash = pack('H*', md5($hash . $password));
} while (--$count);
}
$output = substr($setting, 0, 12);
$output .= $this->encode64($hash, 16);
return $output;
}
function gensalt_extended($input)
{
$count_log2 = min($this->iteration_count_log2 + 8, 24);
# This should be odd to not reveal weak DES keys, and the
# maximum valid value is (2**24 - 1) which is odd anyway.
$count = (1 << $count_log2) - 1;
$output = '_';
$output .= $this->itoa64[$count & 0x3f];
$output .= $this->itoa64[($count >> 6) & 0x3f];
$output .= $this->itoa64[($count >> 12) & 0x3f];
$output .= $this->itoa64[($count >> 18) & 0x3f];
$output .= $this->encode64($input, 3);
return $output;
}
function gensalt_blowfish($input)
{
# This one needs to use a different order of characters and a
# different encoding scheme from the one in encode64() above.
# We care because the last character in our encoded string will
# only represent 2 bits. While two known implementations of
# bcrypt will happily accept and correct a salt string which
# has the 4 unused bits set to non-zero, we do not want to take
# chances and we also do not want to waste an additional byte
# of entropy.
$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$output = '$2a$';
$output .= chr(ord('0') + $this->iteration_count_log2 / 10);
$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
$output .= '$';
$i = 0;
do {
$c1 = ord($input[$i++]);
$output .= $itoa64[$c1 >> 2];
$c1 = ($c1 & 0x03) << 4;
if ($i >= 16) {
$output .= $itoa64[$c1];
break;
}
$c2 = ord($input[$i++]);
$c1 |= $c2 >> 4;
$output .= $itoa64[$c1];
$c1 = ($c2 & 0x0f) << 2;
$c2 = ord($input[$i++]);
$c1 |= $c2 >> 6;
$output .= $itoa64[$c1];
$output .= $itoa64[$c2 & 0x3f];
} while (1);
return $output;
}
function HashPassword($password)
{
$random = '';
if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
$random = $this->get_random_bytes(16);
$hash =
crypt($password, $this->gensalt_blowfish($random));
if (strlen($hash) == 60)
return $hash;
}
if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
if (strlen($random) < 3)
$random = $this->get_random_bytes(3);
$hash =
crypt($password, $this->gensalt_extended($random));
if (strlen($hash) == 20)
return $hash;
}
if (strlen($random) < 6)
$random = $this->get_random_bytes(6);
$hash =
$this->crypt_private($password,
$this->gensalt_private($random));
if (strlen($hash) == 34)
return $hash;
# Returning '*' on error is safe here, but would _not_ be safe
# in a crypt(3)-like function used _both_ for generating new
# hashes and for validating passwords against existing hashes.
return '*';
}
function CheckPassword($password, $stored_hash)
{
$hash = $this->crypt_private($password, $stored_hash);
if ($hash[0] == '*')
$hash = crypt($password, $stored_hash);
return $hash == $stored_hash;
}
}
?>

245
laravel/view.php Normal file
View File

@@ -0,0 +1,245 @@
<?php namespace Laravel;
class View {
/**
* The name of the view.
*
* @var string
*/
public $view;
/**
* The view data.
*
* @var array
*/
public $data = array();
/**
* The module that contains the view.
*
* @var string
*/
public $module;
/**
* The path to the view.
*
* @var string
*/
public $path;
/**
* The defined view composers.
*
* @var array
*/
public static $composers;
/**
* Create a new view instance.
*
* @param string $view
* @param array $data
* @return void
*/
public function __construct($view, $data = array())
{
$this->data = $data;
list($this->module, $this->path, $this->view) = static::parse($view);
$this->compose();
}
/**
* Create a new view instance.
*
* @param string $view
* @param array $data
* @return View
*/
public static function make($view, $data = array())
{
return new static($view, $data);
}
/**
* Create a new view instance from a view name.
*
* The view names for the active module will be searched first, followed by the view names
* for the default module. Finally, the names for all modules will be searched.
*
* @param string $name
* @param array $data
* @return View
*/
protected static function of($name, $data = array())
{
foreach (array_unique(array_merge(array(ACTIVE_MODULE, DEFAULT_MODULE), Module::$modules)) as $module)
{
static::load_composers($module);
foreach (static::$composers[$module] as $key => $value)
{
if ($name === $value or (isset($value['name']) and $name === $value['name']))
{
return new static($key, $data);
}
}
}
throw new \Exception("Named view [$name] is not defined.");
}
/**
* Parse a view identifier and get the module, path, and view name.
*
* @param string $view
* @return array
*/
protected static function parse($view)
{
list($module, $view) = Module::parse($view);
return array($module, Module::path($module).'views/', $view);
}
/**
* Call the composer for the view instance.
*
* @return void
*/
protected function compose()
{
static::load_composers($this->module);
if (isset(static::$composers[$this->module][$this->view]))
{
foreach ((array) static::$composers[$this->module][$this->view] as $key => $value)
{
if (is_callable($value)) return call_user_func($value, $this);
}
}
}
/**
* Load the view composers for a given module.
*
* @param string $module
* @return void
*/
protected static function load_composers($module)
{
if (isset(static::$composers[$module])) return;
$composers = Module::path($module).'composers'.EXT;
static::$composers[$module] = (file_exists($composers)) ? require $composers : array();
}
/**
* Get the parsed content of the view.
*
* @return string
*/
public function get()
{
$view = str_replace('.', '/', $this->view);
if ( ! file_exists($this->path.$view.EXT))
{
Exception\Handler::make(new Exception("View [$view] does not exist."))->handle();
}
foreach ($this->data as &$data)
{
if ($data instanceof View or $data instanceof Response) $data = (string) $data;
}
ob_start() and extract($this->data, EXTR_SKIP);
try { include $this->path.$view.EXT; } catch (\Exception $e) { Exception\Handler::make($e)->handle(); }
return ob_get_clean();
}
/**
* Add a view instance to the view data.
*
* @param string $key
* @param string $view
* @param array $data
* @return View
*/
public function partial($key, $view, $data = array())
{
return $this->bind($key, new static($view, $data));
}
/**
* Add a key / value pair to the view data.
*
* @param string $key
* @param mixed $value
* @return View
*/
public function bind($key, $value)
{
$this->data[$key] = $value;
return $this;
}
/**
* Magic Method for handling the dynamic creation of named views.
*/
public static function __callStatic($method, $parameters)
{
if (strpos($method, 'of_') === 0)
{
return static::of(substr($method, 3), Arr::get($parameters, 0, array()));
}
}
/**
* Magic Method for getting items from the view data.
*/
public function __get($key)
{
return $this->data[$key];
}
/**
* Magic Method for setting items in the view data.
*/
public function __set($key, $value)
{
$this->bind($key, $value);
}
/**
* Magic Method for determining if an item is in the view data.
*/
public function __isset($key)
{
return array_key_exists($key, $this->data);
}
/**
* Magic Method for removing an item from the view data.
*/
public function __unset($key)
{
unset($this->data[$key]);
}
/**
* Get the parsed content of the View.
*/
public function __toString()
{
return $this->get();
}
}