parent
0f93ac18ca
commit
dc2bcaa364
@ -1,15 +0,0 @@
|
||||
*~
|
||||
*.bak
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
.DS_Store
|
||||
.buildpath
|
||||
.project
|
||||
.settings
|
||||
fuel/app/logs/*/*/*
|
||||
fuel/app/cache/*/*
|
||||
nbproject/
|
||||
.idea
|
||||
*.tmproj
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
@ -1,3 +0,0 @@
|
||||
Please read the project contibuting guidelines before creating an issue of sending in a pull request:
|
||||
|
||||
https://github.com/fuel/fuel/wiki/Contributing
|
@ -1,572 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
// load PHP 5.6+ specific code
|
||||
if (PHP_VERSION_ID >= 50600)
|
||||
{
|
||||
include "base56.php";
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads in a core class and optionally an app class override if it exists.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $folder
|
||||
* @return void
|
||||
*/
|
||||
if ( ! function_exists('import'))
|
||||
{
|
||||
function import($path, $folder = 'classes')
|
||||
{
|
||||
$path = str_replace('/', DIRECTORY_SEPARATOR, $path);
|
||||
// load it ffrom the core if it exists
|
||||
if (is_file(COREPATH.$folder.DIRECTORY_SEPARATOR.$path.'.php'))
|
||||
{
|
||||
require_once COREPATH.$folder.DIRECTORY_SEPARATOR.$path.'.php';
|
||||
}
|
||||
// if the app has an override (or a non-core file), load that too
|
||||
if (is_file(APPPATH.$folder.DIRECTORY_SEPARATOR.$path.'.php'))
|
||||
{
|
||||
require_once APPPATH.$folder.DIRECTORY_SEPARATOR.$path.'.php';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for writing to the Log
|
||||
*
|
||||
* @param int|string the error level
|
||||
* @param string the error message
|
||||
* @param string information about the method
|
||||
* @return bool
|
||||
*/
|
||||
if ( ! function_exists('logger'))
|
||||
{
|
||||
function logger($level, $msg, $method = null)
|
||||
{
|
||||
static $labels = array(
|
||||
100 => 'DEBUG',
|
||||
200 => 'INFO',
|
||||
250 => 'NOTICE',
|
||||
300 => 'WARNING',
|
||||
400 => 'ERROR',
|
||||
500 => 'CRITICAL',
|
||||
550 => 'ALERT',
|
||||
600 => 'EMERGENCY',
|
||||
700 => 'ALL',
|
||||
);
|
||||
|
||||
// make sure $level has the correct value
|
||||
if ((is_int($level) and ! isset($labels[$level])) or (is_string($level) and ! array_search(strtoupper($level), $labels)))
|
||||
{
|
||||
throw new \FuelException('Invalid level "'.$level.'" passed to logger()');
|
||||
}
|
||||
|
||||
if(is_string($level)) $level = array_search(strtoupper($level), $labels);
|
||||
|
||||
// get the levels defined to be logged
|
||||
$loglabels = \Config::get('log_threshold');
|
||||
|
||||
// bail out if we don't need logging at all
|
||||
if ($loglabels == \Fuel::L_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// if profiling is active log the message to the profile
|
||||
if (\Config::get('profiling'))
|
||||
{
|
||||
\Console::log($method.' - '.$msg);
|
||||
}
|
||||
|
||||
// if it's not an array, assume it's an "up to" level
|
||||
if ( ! is_array($loglabels))
|
||||
{
|
||||
$a = array();
|
||||
foreach ($labels as $l => $label)
|
||||
{
|
||||
$l >= $loglabels and $a[] = $l;
|
||||
}
|
||||
$loglabels = $a;
|
||||
}
|
||||
|
||||
// do we need to log the message with this level?
|
||||
if ( ! in_array($level, $loglabels))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return \Log::instance()->log($level, (empty($method) ? '' : $method.' - ').$msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of attributes and turns it into a string for an html tag
|
||||
*
|
||||
* @param array $attr
|
||||
* @return string
|
||||
*/
|
||||
if ( ! function_exists('array_to_attr'))
|
||||
{
|
||||
function array_to_attr($attr)
|
||||
{
|
||||
$attr_str = '';
|
||||
|
||||
foreach ((array) $attr as $property => $value)
|
||||
{
|
||||
// Ignore null/false
|
||||
if ($value === null or $value === false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the key is numeric then it must be something like selected="selected"
|
||||
if (is_numeric($property))
|
||||
{
|
||||
$property = $value;
|
||||
}
|
||||
|
||||
$attr_str .= $property.'="'.str_replace('"', '"', $value).'" ';
|
||||
}
|
||||
|
||||
// We strip off the last space for return
|
||||
return trim($attr_str);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a XHTML tag
|
||||
*
|
||||
* @param string The tag name
|
||||
* @param array|string The tag attributes
|
||||
* @param string|bool The content to place in the tag, or false for no closing tag
|
||||
* @return string
|
||||
*/
|
||||
if ( ! function_exists('html_tag'))
|
||||
{
|
||||
function html_tag($tag, $attr = array(), $content = false)
|
||||
{
|
||||
// list of void elements (tags that can not have content)
|
||||
static $void_elements = array(
|
||||
// html4
|
||||
"area","base","br","col","hr","img","input","link","meta","param",
|
||||
// html5
|
||||
"command","embed","keygen","source","track","wbr",
|
||||
// html5.1
|
||||
"menuitem",
|
||||
);
|
||||
|
||||
// construct the HTML
|
||||
$html = '<'.$tag;
|
||||
$html .= ( ! empty($attr)) ? ' '.(is_array($attr) ? array_to_attr($attr) : $attr) : '';
|
||||
|
||||
// a void element?
|
||||
if (in_array(strtolower($tag), $void_elements))
|
||||
{
|
||||
// these can not have content
|
||||
$html .= ' />';
|
||||
}
|
||||
else
|
||||
{
|
||||
// add the content and close the tag
|
||||
$html .= '>'.$content.'</'.$tag.'>';
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A case-insensitive version of in_array.
|
||||
*
|
||||
* @param mixed $needle
|
||||
* @param array $haystack
|
||||
* @return bool
|
||||
*/
|
||||
if ( ! function_exists('in_arrayi'))
|
||||
{
|
||||
function in_arrayi($needle, $haystack)
|
||||
{
|
||||
return in_array(strtolower($needle), array_map('strtolower', $haystack));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the public vars for an object. Use this if you need to get all the
|
||||
* public vars of $this inside an object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
if ( ! function_exists('get_object_public_vars'))
|
||||
{
|
||||
function get_object_public_vars($obj)
|
||||
{
|
||||
return get_object_vars($obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a view and returns the output.
|
||||
*
|
||||
* @param string The view name/path
|
||||
* @param array The data for the view
|
||||
* @param bool Auto filter override
|
||||
* @return string
|
||||
*/
|
||||
if ( ! function_exists('render'))
|
||||
{
|
||||
function render($view, $data = null, $auto_filter = null)
|
||||
{
|
||||
return \View::forge($view, $data, $auto_filter)->render();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper function for Lang::get()
|
||||
*
|
||||
* @param mixed The string to translate
|
||||
* @param array The parameters
|
||||
* @return string
|
||||
*/
|
||||
if ( ! function_exists('__'))
|
||||
{
|
||||
function __($string, $params = array(), $default = null, $language = null)
|
||||
{
|
||||
return \Lang::get($string, $params, $default, $language);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given string. This is just a wrapper function for Security::htmlentities()
|
||||
*
|
||||
* @param mixed The string to encode
|
||||
* @return string
|
||||
*/
|
||||
if ( ! function_exists('e'))
|
||||
{
|
||||
function e($string)
|
||||
{
|
||||
return \Security::htmlentities($string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a classname and returns the actual classname for an alias or just the classname
|
||||
* if it's a normal class.
|
||||
*
|
||||
* @param string classname to check
|
||||
* @return string real classname
|
||||
*/
|
||||
if ( ! function_exists('get_real_class'))
|
||||
{
|
||||
function get_real_class($class)
|
||||
{
|
||||
static $classes = array();
|
||||
|
||||
if ( ! array_key_exists($class, $classes))
|
||||
{
|
||||
$reflect = new ReflectionClass($class);
|
||||
$classes[$class] = $reflect->getName();
|
||||
}
|
||||
|
||||
return $classes[$class];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an associative array in the layout of parse_url, and constructs a URL from it
|
||||
*
|
||||
* see http://www.php.net/manual/en/function.http-build-url.php#96335
|
||||
*
|
||||
* @param mixed (Part(s) of) an URL in form of a string or associative array like parse_url() returns
|
||||
* @param mixed Same as the first argument
|
||||
* @param int A bitmask of binary or'ed HTTP_URL constants (Optional)HTTP_URL_REPLACE is the default
|
||||
* @param array If set, it will be filled with the parts of the composed url like parse_url() would return
|
||||
*
|
||||
* @return string constructed URL
|
||||
*/
|
||||
if (!function_exists('http_build_url'))
|
||||
{
|
||||
define('HTTP_URL_REPLACE', 1); // Replace every part of the first URL when there's one of the second URL
|
||||
define('HTTP_URL_JOIN_PATH', 2); // Join relative paths
|
||||
define('HTTP_URL_JOIN_QUERY', 4); // Join query strings
|
||||
define('HTTP_URL_STRIP_USER', 8); // Strip any user authentication information
|
||||
define('HTTP_URL_STRIP_PASS', 16); // Strip any password authentication information
|
||||
define('HTTP_URL_STRIP_AUTH', 32); // Strip any authentication information
|
||||
define('HTTP_URL_STRIP_PORT', 64); // Strip explicit port numbers
|
||||
define('HTTP_URL_STRIP_PATH', 128); // Strip complete path
|
||||
define('HTTP_URL_STRIP_QUERY', 256); // Strip query string
|
||||
define('HTTP_URL_STRIP_FRAGMENT', 512); // Strip any fragments (#identifier)
|
||||
define('HTTP_URL_STRIP_ALL', 1024); // Strip anything but scheme and host
|
||||
|
||||
function http_build_url($url, $parts = array(), $flags = HTTP_URL_REPLACE, &$new_url = false)
|
||||
{
|
||||
$keys = array('user','pass','port','path','query','fragment');
|
||||
|
||||
// HTTP_URL_STRIP_ALL becomes all the HTTP_URL_STRIP_Xs
|
||||
if ($flags & HTTP_URL_STRIP_ALL)
|
||||
{
|
||||
$flags |= HTTP_URL_STRIP_USER;
|
||||
$flags |= HTTP_URL_STRIP_PASS;
|
||||
$flags |= HTTP_URL_STRIP_PORT;
|
||||
$flags |= HTTP_URL_STRIP_PATH;
|
||||
$flags |= HTTP_URL_STRIP_QUERY;
|
||||
$flags |= HTTP_URL_STRIP_FRAGMENT;
|
||||
}
|
||||
// HTTP_URL_STRIP_AUTH becomes HTTP_URL_STRIP_USER and HTTP_URL_STRIP_PASS
|
||||
elseif ($flags & HTTP_URL_STRIP_AUTH)
|
||||
{
|
||||
$flags |= HTTP_URL_STRIP_USER;
|
||||
$flags |= HTTP_URL_STRIP_PASS;
|
||||
}
|
||||
|
||||
// parse the original URL
|
||||
$parse_url = is_array($url) ? $url : parse_url($url);
|
||||
|
||||
// make sure we always have a scheme, host and path
|
||||
empty($parse_url['scheme']) and $parse_url['scheme'] = 'http';
|
||||
empty($parse_url['host']) and $parse_url['host'] = \Input::server('http_host');
|
||||
isset($parse_url['path']) or $parse_url['path'] = '';
|
||||
|
||||
// make the path absolute if needed
|
||||
if ( ! empty($parse_url['path']) and substr($parse_url['path'], 0, 1) != '/')
|
||||
{
|
||||
$parse_url['path'] = '/'.$parse_url['path'];
|
||||
}
|
||||
|
||||
// scheme and host are always replaced
|
||||
isset($parts['scheme']) and $parse_url['scheme'] = $parts['scheme'];
|
||||
isset($parts['host']) and $parse_url['host'] = $parts['host'];
|
||||
|
||||
// replace the original URL with it's new parts (if applicable)
|
||||
if ($flags & HTTP_URL_REPLACE)
|
||||
{
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
if (isset($parts[$key]))
|
||||
$parse_url[$key] = $parts[$key];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// join the original URL path with the new path
|
||||
if (isset($parts['path']) && ($flags & HTTP_URL_JOIN_PATH))
|
||||
{
|
||||
if (isset($parse_url['path']))
|
||||
$parse_url['path'] = rtrim(str_replace(basename($parse_url['path']), '', $parse_url['path']), '/') . '/' . ltrim($parts['path'], '/');
|
||||
else
|
||||
$parse_url['path'] = $parts['path'];
|
||||
}
|
||||
|
||||
// join the original query string with the new query string
|
||||
if (isset($parts['query']) && ($flags & HTTP_URL_JOIN_QUERY))
|
||||
{
|
||||
if (isset($parse_url['query']))
|
||||
$parse_url['query'] .= '&' . $parts['query'];
|
||||
else
|
||||
$parse_url['query'] = $parts['query'];
|
||||
}
|
||||
}
|
||||
|
||||
// strips all the applicable sections of the URL
|
||||
// note: scheme and host are never stripped
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
if ($flags & (int) constant('HTTP_URL_STRIP_' . strtoupper($key)))
|
||||
unset($parse_url[$key]);
|
||||
}
|
||||
|
||||
$new_url = $parse_url;
|
||||
|
||||
return
|
||||
((isset($parse_url['scheme'])) ? $parse_url['scheme'] . '://' : '')
|
||||
.((isset($parse_url['user'])) ? $parse_url['user'] . ((isset($parse_url['pass'])) ? ':' . $parse_url['pass'] : '') .'@' : '')
|
||||
.((isset($parse_url['host'])) ? $parse_url['host'] : '')
|
||||
.((isset($parse_url['port'])) ? ':' . $parse_url['port'] : '')
|
||||
.((isset($parse_url['path'])) ? $parse_url['path'] : '')
|
||||
.((isset($parse_url['query'])) ? '?' . $parse_url['query'] : '')
|
||||
.((isset($parse_url['fragment'])) ? '#' . $parse_url['fragment'] : '')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the common "root" path of two given paths or FQFN's
|
||||
*
|
||||
* @param array array with the paths to compare
|
||||
*
|
||||
* @return string the determined common path section
|
||||
*/
|
||||
if ( ! function_exists('get_common_path'))
|
||||
{
|
||||
function get_common_path($paths)
|
||||
{
|
||||
$lastOffset = 1;
|
||||
$common = '/';
|
||||
while (($index = strpos($paths[0], '/', $lastOffset)) !== false)
|
||||
{
|
||||
$dirLen = $index - $lastOffset + 1; // include /
|
||||
$dir = substr($paths[0], $lastOffset, $dirLen);
|
||||
foreach ($paths as $path)
|
||||
{
|
||||
if (substr($path, $lastOffset, $dirLen) != $dir)
|
||||
{
|
||||
return $common;
|
||||
}
|
||||
}
|
||||
$common .= $dir;
|
||||
$lastOffset = $index + 1;
|
||||
}
|
||||
return $common;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Faster equivalent of call_user_func_array
|
||||
*/
|
||||
if ( ! function_exists('call_fuel_func_array'))
|
||||
{
|
||||
function call_fuel_func_array($callback, array $args)
|
||||
{
|
||||
// deal with "class::method" syntax
|
||||
if (is_string($callback) and strpos($callback, '::') !== false)
|
||||
{
|
||||
$callback = explode('::', $callback);
|
||||
}
|
||||
|
||||
// if an array is passed, extract the object and method to call
|
||||
if (is_array($callback) and isset($callback[1]) and is_object($callback[0]))
|
||||
{
|
||||
// make sure our arguments array is indexed
|
||||
if ($count = count($args))
|
||||
{
|
||||
$args = array_values($args);
|
||||
}
|
||||
|
||||
list($instance, $method) = $callback;
|
||||
|
||||
// calling the method directly is faster then call_user_func_array() !
|
||||
switch ($count)
|
||||
{
|
||||
case 0:
|
||||
return $instance->$method();
|
||||
|
||||
case 1:
|
||||
return $instance->$method($args[0]);
|
||||
|
||||
case 2:
|
||||
return $instance->$method($args[0], $args[1]);
|
||||
|
||||
case 3:
|
||||
return $instance->$method($args[0], $args[1], $args[2]);
|
||||
|
||||
case 4:
|
||||
return $instance->$method($args[0], $args[1], $args[2], $args[3]);
|
||||
}
|
||||
}
|
||||
|
||||
elseif (is_array($callback) and isset($callback[1]) and is_string($callback[0]))
|
||||
{
|
||||
list($class, $method) = $callback;
|
||||
$class = '\\'.ltrim($class, '\\');
|
||||
|
||||
// calling the method directly is faster then call_user_func_array() !
|
||||
switch (count($args))
|
||||
{
|
||||
case 0:
|
||||
return $class::$method();
|
||||
|
||||
case 1:
|
||||
return $class::$method($args[0]);
|
||||
|
||||
case 2:
|
||||
return $class::$method($args[0], $args[1]);
|
||||
|
||||
case 3:
|
||||
return $class::$method($args[0], $args[1], $args[2]);
|
||||
|
||||
case 4:
|
||||
return $class::$method($args[0], $args[1], $args[2], $args[3]);
|
||||
}
|
||||
}
|
||||
|
||||
// if it's a string, it's a native function or a static method call
|
||||
elseif (is_string($callback) or $callback instanceOf \Closure)
|
||||
{
|
||||
is_string($callback) and $callback = ltrim($callback, '\\');
|
||||
|
||||
// calling the method directly is faster then call_user_func_array() !
|
||||
switch (count($args))
|
||||
{
|
||||
case 0:
|
||||
return $callback();
|
||||
|
||||
case 1:
|
||||
return $callback($args[0]);
|
||||
|
||||
case 2:
|
||||
return $callback($args[0], $args[1]);
|
||||
|
||||
case 3:
|
||||
return $callback($args[0], $args[1], $args[2]);
|
||||
|
||||
case 4:
|
||||
return $callback($args[0], $args[1], $args[2], $args[3]);
|
||||
}
|
||||
}
|
||||
|
||||
// fallback, handle the old way
|
||||
return call_user_func_array($callback, $args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* hash_pbkdf2() implementation for PHP < 5.5.0
|
||||
*/
|
||||
if ( ! function_exists('hash_pbkdf2'))
|
||||
{
|
||||
/* PBKDF2 Implementation (described in RFC 2898)
|
||||
*
|
||||
* @param string a hash algorithm to use
|
||||
* @param string p password
|
||||
* @param string s salt
|
||||
* @param int c iteration count (use 1000 or higher)
|
||||
* @param int kl derived key length
|
||||
* @param bool r when set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.
|
||||
*
|
||||
* @return string derived key
|
||||
*/
|
||||
function hash_pbkdf2($a, $p, $s, $c, $kl = 0, $r = false)
|
||||
{
|
||||
$hl = strlen(hash($a, null, true)); # Hash length
|
||||
$kb = ceil($kl / $hl); # Key blocks to compute
|
||||
$dk = ''; # Derived key
|
||||
|
||||
# Create key
|
||||
for ( $block = 1; $block <= $kb; $block ++ )
|
||||
{
|
||||
# Initial hash for this block
|
||||
$ib = $b = hash_hmac($a, $s . pack('N', $block), $p, true);
|
||||
|
||||
# Perform block iterations
|
||||
for ( $i = 1; $i < $c; $i ++ )
|
||||
{
|
||||
# XOR each iterate
|
||||
$ib ^= ($b = hash_hmac($a, $b, $p, true));
|
||||
}
|
||||
$dk .= $ib; # Append iterated block
|
||||
}
|
||||
|
||||
# Return derived key of correct length
|
||||
return substr($r ? $dk : bin2hex($dk), 0, $kl);
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
/**
|
||||
* Faster equivalent of call_user_func_array using variadics
|
||||
*/
|
||||
if ( ! function_exists('call_fuel_func_array'))
|
||||
{
|
||||
function call_fuel_func_array($callback, array $args)
|
||||
{
|
||||
// deal with "class::method" syntax
|
||||
if (is_string($callback) and strpos($callback, '::') !== false)
|
||||
{
|
||||
$callback = explode('::', $callback);
|
||||
}
|
||||
|
||||
// dynamic call on an object?
|
||||
if (is_array($callback) and isset($callback[1]) and is_object($callback[0]))
|
||||
{
|
||||
// make sure our arguments array is indexed
|
||||
if ($count = count($args))
|
||||
{
|
||||
$args = array_values($args);
|
||||
}
|
||||
|
||||
list($instance, $method) = $callback;
|
||||
|
||||
return $instance->{$method}(...$args);
|
||||
}
|
||||
|
||||
// static call?
|
||||
elseif (is_array($callback) and isset($callback[1]) and is_string($callback[0]))
|
||||
{
|
||||
list($class, $method) = $callback;
|
||||
$class = '\\'.ltrim($class, '\\');
|
||||
|
||||
return $class::{$method}(...$args);
|
||||
}
|
||||
|
||||
// if it's a string, it's a native function or a static method call
|
||||
elseif (is_string($callback) or $callback instanceOf \Closure)
|
||||
{
|
||||
is_string($callback) and $callback = ltrim($callback, '\\');
|
||||
}
|
||||
|
||||
return $callback(...$args);
|
||||
}
|
||||
}
|
@ -1,347 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('CRLF', chr(13).chr(10));
|
||||
|
||||
/**
|
||||
* Do we have access to mbstring?
|
||||
* We need this in order to work with UTF-8 strings
|
||||
*/
|
||||
define('MBSTRING', function_exists('mb_get_info'));
|
||||
|
||||
// load the base functions
|
||||
require COREPATH.'base.php';
|
||||
|
||||
// define the core classes to the autoloader
|
||||
setup_autoloader();
|
||||
|
||||
// setup the composer autoloader
|
||||
get_composer();
|
||||
|
||||
/**
|
||||
* Register all the error/shutdown handlers
|
||||
*/
|
||||
register_shutdown_function(function ()
|
||||
{
|
||||
// reset the autoloader
|
||||
\Autoloader::_reset();
|
||||
|
||||
// if we have sessions loaded, and native session emulation active
|
||||
if (\Config::get('session.native_emulation', false))
|
||||
{
|
||||
// close the name session
|
||||
session_id() and session_write_close();
|
||||
}
|
||||
|
||||
// make sure we're having an output filter so we can display errors
|
||||
// occuring before the main config file is loaded
|
||||
\Config::get('security.output_filter', null) or \Config::set('security.output_filter', 'Security::htmlentities');
|
||||
|
||||
try
|
||||
{
|
||||
// fire any app shutdown events
|
||||
\Event::instance()->trigger('shutdown', '', 'none', true);
|
||||
|
||||
// fire any framework shutdown events
|
||||
\Event::instance()->trigger('fuel-shutdown', '', 'none', true);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
if (\Fuel::$is_cli)
|
||||
{
|
||||
\Cli::error("Error: ".$e->getMessage()." in ".$e->getFile()." on ".$e->getLine());
|
||||
\Cli::beep();
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger(\Fuel::L_ERROR, 'shutdown - ' . $e->getMessage()." in ".$e->getFile()." on ".$e->getLine());
|
||||
}
|
||||
}
|
||||
return \Errorhandler::shutdown_handler();
|
||||
});
|
||||
|
||||
set_exception_handler(function ($e)
|
||||
{
|
||||
// reset the autoloader
|
||||
\Autoloader::_reset();
|
||||
|
||||
// deal with PHP bugs #42098/#54054
|
||||
if ( ! class_exists('Errorhandler'))
|
||||
{
|
||||
include COREPATH.'classes/errorhandler.php';
|
||||
class_alias('\Fuel\Core\Errorhandler', 'Errorhandler');
|
||||
class_alias('\Fuel\Core\PhpErrorException', 'PhpErrorException');
|
||||
}
|
||||
|
||||
return \Errorhandler::exception_handler($e);
|
||||
});
|
||||
|
||||
set_error_handler(function ($severity, $message, $filepath, $line)
|
||||
{
|
||||
// reset the autoloader
|
||||
\Autoloader::_reset();
|
||||
|
||||
// deal with PHP bugs #42098/#54054
|
||||
if ( ! class_exists('Errorhandler'))
|
||||
{
|
||||
include COREPATH.'classes/errorhandler.php';
|
||||
class_alias('\Fuel\Core\Errorhandler', 'Errorhandler');
|
||||
class_alias('\Fuel\Core\PhpErrorException', 'PhpErrorException');
|
||||
}
|
||||
|
||||
return \Errorhandler::error_handler($severity, $message, $filepath, $line);
|
||||
});
|
||||
|
||||
function setup_autoloader()
|
||||
{
|
||||
\Autoloader::add_namespace('Fuel\\Core', COREPATH.'classes/');
|
||||
|
||||
\Autoloader::add_classes(array(
|
||||
'Fuel\\Core\\Agent' => COREPATH.'classes/agent.php',
|
||||
|
||||
'Fuel\\Core\\Arr' => COREPATH.'classes/arr.php',
|
||||
|
||||
'Fuel\\Core\\Asset' => COREPATH.'classes/asset.php',
|
||||
'Fuel\\Core\\Asset_Instance' => COREPATH.'classes/asset/instance.php',
|
||||
|
||||
'Fuel\\Core\\Cache' => COREPATH.'classes/cache.php',
|
||||
'Fuel\\Core\\CacheNotFoundException' => COREPATH.'classes/cache/notfound.php',
|
||||
'Fuel\\Core\\CacheExpiredException' => COREPATH.'classes/cache.php',
|
||||
'Fuel\\Core\\Cache_Handler_Driver' => COREPATH.'classes/cache/handler/driver.php',
|
||||
'Fuel\\Core\\Cache_Handler_Json' => COREPATH.'classes/cache/handler/json.php',
|
||||
'Fuel\\Core\\Cache_Handler_Serialized' => COREPATH.'classes/cache/handler/serialized.php',
|
||||
'Fuel\\Core\\Cache_Handler_String' => COREPATH.'classes/cache/handler/string.php',
|
||||
'Fuel\\Core\\Cache_Storage_Driver' => COREPATH.'classes/cache/storage/driver.php',
|
||||
'Fuel\\Core\\Cache_Storage_Apc' => COREPATH.'classes/cache/storage/apc.php',
|
||||
'Fuel\\Core\\Cache_Storage_File' => COREPATH.'classes/cache/storage/file.php',
|
||||
'Fuel\\Core\\Cache_Storage_Memcached' => COREPATH.'classes/cache/storage/memcached.php',
|
||||
'Fuel\\Core\\Cache_Storage_Redis' => COREPATH.'classes/cache/storage/redis.php',
|
||||
'Fuel\\Core\\Cache_Storage_Xcache' => COREPATH.'classes/cache/storage/xcache.php',
|
||||
|
||||
'Fuel\\Core\\Config' => COREPATH.'classes/config.php',
|
||||
'Fuel\\Core\\ConfigException' => COREPATH.'classes/config.php',
|
||||
'Fuel\\Core\\Config_Db' => COREPATH.'classes/config/db.php',
|
||||
'Fuel\\Core\\Config_File' => COREPATH.'classes/config/file.php',
|
||||
'Fuel\\Core\\Config_Ini' => COREPATH.'classes/config/ini.php',
|
||||
'Fuel\\Core\\Config_Json' => COREPATH.'classes/config/json.php',
|
||||
'Fuel\\Core\\Config_Interface' => COREPATH.'classes/config/interface.php',
|
||||
'Fuel\\Core\\Config_Php' => COREPATH.'classes/config/php.php',
|
||||
'Fuel\\Core\\Config_Yml' => COREPATH.'classes/config/yml.php',
|
||||
'Fuel\\Core\\Config_Memcached' => COREPATH.'classes/config/memcached.php',
|
||||
|
||||
'Fuel\\Core\\Controller' => COREPATH.'classes/controller.php',
|
||||
'Fuel\\Core\\Controller_Rest' => COREPATH.'classes/controller/rest.php',
|
||||
'Fuel\\Core\\Controller_Template' => COREPATH.'classes/controller/template.php',
|
||||
'Fuel\\Core\\Controller_Hybrid' => COREPATH.'classes/controller/hybrid.php',
|
||||
|
||||
'Fuel\\Core\\Cookie' => COREPATH.'classes/cookie.php',
|
||||
|
||||
'Fuel\\Core\\DB' => COREPATH.'classes/db.php',
|
||||
'Fuel\\Core\\DBUtil' => COREPATH.'classes/dbutil.php',
|
||||
|
||||
'Fuel\\Core\\Database_Connection' => COREPATH.'classes/database/connection.php',
|
||||
'Fuel\\Core\\Database_Result' => COREPATH.'classes/database/result.php',
|
||||
'Fuel\\Core\\Database_Result_Cached' => COREPATH.'classes/database/result/cached.php',
|
||||
'Fuel\\Core\\Database_Exception' => COREPATH.'classes/database/exception.php',
|
||||
'Fuel\\Core\\Database_Expression' => COREPATH.'classes/database/expression.php',
|
||||
// Generic Schema builder
|
||||
'Fuel\\Core\\Database_Schema' => COREPATH.'classes/database/schema.php',
|
||||
// Specific Schema builders
|
||||
// Generic Query builder
|
||||
'Fuel\\Core\\Database_Query' => COREPATH.'classes/database/query.php',
|
||||
'Fuel\\Core\\Database_Query_Builder' => COREPATH.'classes/database/query/builder.php',
|
||||
'Fuel\\Core\\Database_Query_Builder_Insert' => COREPATH.'classes/database/query/builder/insert.php',
|
||||
'Fuel\\Core\\Database_Query_Builder_Delete' => COREPATH.'classes/database/query/builder/delete.php',
|
||||
'Fuel\\Core\\Database_Query_Builder_Update' => COREPATH.'classes/database/query/builder/update.php',
|
||||
'Fuel\\Core\\Database_Query_Builder_Select' => COREPATH.'classes/database/query/builder/select.php',
|
||||
'Fuel\\Core\\Database_Query_Builder_Where' => COREPATH.'classes/database/query/builder/where.php',
|
||||
'Fuel\\Core\\Database_Query_Builder_Join' => COREPATH.'classes/database/query/builder/join.php',
|
||||
// Specific Query builders
|
||||
'Fuel\\Core\\Database_SQLite_Builder_Delete' => COREPATH.'classes/database/sqlite/builder/delete.php',
|
||||
'Fuel\\Core\\Database_SQLite_Builder_Update' => COREPATH.'classes/database/sqlite/builder/update.php',
|
||||
// Generic PDO driver
|
||||
'Fuel\\Core\\Database_Pdo_Connection' => COREPATH.'classes/database/pdo/connection.php',
|
||||
// Specific PDO drivers
|
||||
'Fuel\\Core\\Database_MySQL_Connection' => COREPATH.'classes/database/mysql/connection.php',
|
||||
'Fuel\\Core\\Database_SQLite_Connection' => COREPATH.'classes/database/sqlite/connection.php',
|
||||
'Fuel\\Core\\Database_Sqlsrv_Connection' => COREPATH.'classes/database/sqlsrv/connection.php',
|
||||
'Fuel\\Core\\Database_Dblib_Connection' => COREPATH.'classes/database/dblib/connection.php',
|
||||
// Legacy drivers
|
||||
'Fuel\\Core\\Database_MySQLi_Connection' => COREPATH.'classes/database/mysqli/connection.php',
|
||||
'Fuel\\Core\\Database_MySQLi_Result' => COREPATH.'classes/database/mysqli/result.php',
|
||||
|
||||
'Fuel\\Core\\Fuel' => COREPATH.'classes/fuel.php',
|
||||
'Fuel\\Core\\FuelException' => COREPATH.'classes/fuel.php',
|
||||
|
||||
'Fuel\\Core\\Finder' => COREPATH.'classes/finder.php',
|
||||
|
||||
'Fuel\\Core\\Date' => COREPATH.'classes/date.php',
|
||||
|
||||
'Fuel\\Core\\Debug' => COREPATH.'classes/debug.php',
|
||||
|
||||
'Fuel\\Core\\Cli' => COREPATH.'classes/cli.php',
|
||||
|
||||
'Fuel\\Core\\Crypt' => COREPATH.'classes/crypt.php',
|
||||
|
||||
'Fuel\\Core\\Event' => COREPATH.'classes/event.php',
|
||||
'Fuel\\Core\\Event_Instance' => COREPATH.'classes/event/instance.php',
|
||||
|
||||
'Fuel\\Core\\Errorhandler' => COREPATH.'classes/errorhandler.php',
|
||||
'Fuel\\Core\\PhpErrorException' => COREPATH.'classes/errorhandler.php',
|
||||
|
||||
'Fuel\\Core\\Format' => COREPATH.'classes/format.php',
|
||||
|
||||
'Fuel\\Core\\Fieldset' => COREPATH.'classes/fieldset.php',
|
||||
'Fuel\\Core\\Fieldset_Field' => COREPATH.'classes/fieldset/field.php',
|
||||
|
||||
'Fuel\\Core\\File' => COREPATH.'classes/file.php',
|
||||
'Fuel\\Core\\FileAccessException' => COREPATH.'classes/file.php',
|
||||
'Fuel\\Core\\OutsideAreaException' => COREPATH.'classes/file.php',
|
||||
'Fuel\\Core\\InvalidPathException' => COREPATH.'classes/file.php',
|
||||
'Fuel\\Core\\File_Area' => COREPATH.'classes/file/area.php',
|
||||
'Fuel\\Core\\File_Handler_File' => COREPATH.'classes/file/handler/file.php',
|
||||
'Fuel\\Core\\File_Handler_Directory' => COREPATH.'classes/file/handler/directory.php',
|
||||
|
||||
'Fuel\\Core\\Form' => COREPATH.'classes/form.php',
|
||||
'Fuel\\Core\\Form_Instance' => COREPATH.'classes/form/instance.php',
|
||||
|
||||
'Fuel\\Core\\Ftp' => COREPATH.'classes/ftp.php',
|
||||
'Fuel\\Core\\FtpConnectionException' => COREPATH.'classes/ftp.php',
|
||||
'Fuel\\Core\\FtpFileAccessException' => COREPATH.'classes/ftp.php',
|
||||
|
||||
'Fuel\\Core\\HttpException' => COREPATH.'classes/httpexception.php',
|
||||
'Fuel\\Core\\HttpBadRequestException' => COREPATH.'classes/httpexceptions.php',
|
||||
'Fuel\\Core\\HttpNoAccessException' => COREPATH.'classes/httpexceptions.php',
|
||||
'Fuel\\Core\\HttpNotFoundException' => COREPATH.'classes/httpexceptions.php',
|
||||
'Fuel\\Core\\HttpServerErrorException' => COREPATH.'classes/httpexceptions.php',
|
||||
|
||||
'Fuel\\Core\\Html' => COREPATH.'classes/html.php',
|
||||
|
||||
'Fuel\\Core\\Image' => COREPATH.'classes/image.php',
|
||||
'Fuel\\Core\\Image_Driver' => COREPATH.'classes/image/driver.php',
|
||||
'Fuel\\Core\\Image_Gd' => COREPATH.'classes/image/gd.php',
|
||||
'Fuel\\Core\\Image_Imagemagick' => COREPATH.'classes/image/imagemagick.php',
|
||||
'Fuel\\Core\\Image_Imagick' => COREPATH.'classes/image/imagick.php',
|
||||
|
||||
'Fuel\\Core\\Inflector' => COREPATH.'classes/inflector.php',
|
||||
|
||||
'Fuel\\Core\\Input' => COREPATH.'classes/input.php',
|
||||
|
||||
'Fuel\\Core\\Lang' => COREPATH.'classes/lang.php',
|
||||
'Fuel\\Core\\LangException' => COREPATH.'classes/lang.php',
|
||||
'Fuel\\Core\\Lang_Db' => COREPATH.'classes/lang/db.php',
|
||||
'Fuel\\Core\\Lang_File' => COREPATH.'classes/lang/file.php',
|
||||
'Fuel\\Core\\Lang_Ini' => COREPATH.'classes/lang/ini.php',
|
||||
'Fuel\\Core\\Lang_Json' => COREPATH.'classes/lang/json.php',
|
||||
'Fuel\\Core\\Lang_Interface' => COREPATH.'classes/lang/interface.php',
|
||||
'Fuel\\Core\\Lang_Php' => COREPATH.'classes/lang/php.php',
|
||||
'Fuel\\Core\\Lang_Yml' => COREPATH.'classes/lang/yml.php',
|
||||
|
||||
'Fuel\\Core\\Log' => COREPATH.'classes/log.php',
|
||||
|
||||
'Fuel\\Core\\Markdown' => COREPATH.'classes/markdown.php',
|
||||
|
||||
'Fuel\\Core\\Migrate' => COREPATH.'classes/migrate.php',
|
||||
|
||||
'Fuel\\Core\\Model' => COREPATH.'classes/model.php',
|
||||
'Fuel\\Core\\Model_Crud' => COREPATH.'classes/model/crud.php',
|
||||
|
||||
'Fuel\\Core\\Module' => COREPATH.'classes/module.php',
|
||||
'Fuel\\Core\\ModuleNotFoundException' => COREPATH.'classes/module.php',
|
||||
|
||||
'Fuel\\Core\\Mongo_Db' => COREPATH.'classes/mongo/db.php',
|
||||
'Fuel\\Core\\Mongo_DbException' => COREPATH.'classes/mongo/db.php',
|
||||
|
||||
'Fuel\\Core\\Output' => COREPATH.'classes/output.php',
|
||||
|
||||
'Fuel\\Core\\Package' => COREPATH.'classes/package.php',
|
||||
'Fuel\\Core\\PackageNotFoundException' => COREPATH.'classes/package.php',
|
||||
|
||||
'Fuel\\Core\\Pagination' => COREPATH.'classes/pagination.php',
|
||||
|
||||
'Fuel\\Core\\Presenter' => COREPATH.'classes/presenter.php',
|
||||
|
||||
'Fuel\\Core\\Profiler' => COREPATH.'classes/profiler.php',
|
||||
|
||||
'Fuel\\Core\\Request' => COREPATH.'classes/request.php',
|
||||
'Fuel\\Core\\Request_Driver' => COREPATH.'classes/request/driver.php',
|
||||
'Fuel\\Core\\RequestException' => COREPATH.'classes/request/driver.php',
|
||||
'Fuel\\Core\\RequestStatusException' => COREPATH.'classes/request/driver.php',
|
||||
'Fuel\\Core\\Request_Curl' => COREPATH.'classes/request/curl.php',
|
||||
'Fuel\\Core\\Request_Soap' => COREPATH.'classes/request/soap.php',
|
||||
|
||||
'Fuel\\Core\\Redis_Db' => COREPATH.'classes/redis/db.php',
|
||||
'Fuel\\Core\\RedisException' => COREPATH.'classes/redis/db.php',
|
||||
|
||||
'Fuel\\Core\\Response' => COREPATH.'classes/response.php',
|
||||
|
||||
'Fuel\\Core\\Route' => COREPATH.'classes/route.php',
|
||||
'Fuel\\Core\\Router' => COREPATH.'classes/router.php',
|
||||
|
||||
'Fuel\\Core\\Sanitization' => COREPATH.'classes/sanitization.php',
|
||||
|
||||
'Fuel\\Core\\Security' => COREPATH.'classes/security.php',
|
||||
'Fuel\\Core\\SecurityException' => COREPATH.'classes/security.php',
|
||||
|
||||
'Fuel\\Core\\Session' => COREPATH.'classes/session.php',
|
||||
'Fuel\\Core\\Session_Driver' => COREPATH.'classes/session/driver.php',
|
||||
'Fuel\\Core\\Session_Db' => COREPATH.'classes/session/db.php',
|
||||
'Fuel\\Core\\Session_Cookie' => COREPATH.'classes/session/cookie.php',
|
||||
'Fuel\\Core\\Session_File' => COREPATH.'classes/session/file.php',
|
||||
'Fuel\\Core\\Session_Memcached' => COREPATH.'classes/session/memcached.php',
|
||||
'Fuel\\Core\\Session_Redis' => COREPATH.'classes/session/redis.php',
|
||||
'Fuel\\Core\\Session_Exception' => COREPATH.'classes/session/exception.php',
|
||||
|
||||
'Fuel\\Core\\Num' => COREPATH.'classes/num.php',
|
||||
|
||||
'Fuel\\Core\\Str' => COREPATH.'classes/str.php',
|
||||
|
||||
'Fuel\\Core\\TestCase' => COREPATH.'classes/testcase.php',
|
||||
|
||||
'Fuel\\Core\\Theme' => COREPATH.'classes/theme.php',
|
||||
'Fuel\\Core\\ThemeException' => COREPATH.'classes/theme.php',
|
||||
|
||||
'Fuel\\Core\\Uri' => COREPATH.'classes/uri.php',
|
||||
|
||||
'Fuel\\Core\\Unzip' => COREPATH.'classes/unzip.php',
|
||||
|
||||
'Fuel\\Core\\Upload' => COREPATH.'classes/upload.php',
|
||||
|
||||
'Fuel\\Core\\Validation' => COREPATH.'classes/validation.php',
|
||||
'Fuel\\Core\\Validation_Error' => COREPATH.'classes/validation/error.php',
|
||||
|
||||
'Fuel\\Core\\View' => COREPATH.'classes/view.php',
|
||||
'Fuel\\Core\\Viewmodel' => COREPATH.'classes/viewmodel.php',
|
||||
));
|
||||
};
|
||||
|
||||
function get_composer()
|
||||
{
|
||||
// storage for the composer autoloader
|
||||
static $composer;
|
||||
|
||||
// load composer
|
||||
if ( ! $composer)
|
||||
{
|
||||
// load the Composer autoloader if present
|
||||
defined('VENDORPATH') or define('VENDORPATH', realpath(COREPATH.'..'.DS.'vendor').DS);
|
||||
if ( ! is_file(VENDORPATH.'autoload.php'))
|
||||
{
|
||||
die('Composer is not installed. Please run "php composer.phar update" in the root to install Composer');
|
||||
}
|
||||
$composer = require(VENDORPATH.'autoload.php');
|
||||
}
|
||||
|
||||
return $composer;
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Set error reporting and display errors settings. You will want to change these when in production.
|
||||
*/
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
$app_path = rtrim($_SERVER['app_path'], '/').'/';
|
||||
$package_path = rtrim($_SERVER['package_path'], '/').'/';
|
||||
$vendor_path = rtrim($_SERVER['vendor_path'], '/').'/';
|
||||
$core_path = rtrim($_SERVER['core_path'], '/').'/';
|
||||
|
||||
/**
|
||||
* Website docroot
|
||||
*/
|
||||
define('DOCROOT', realpath(__DIR__.DIRECTORY_SEPARATOR.$_SERVER['doc_root']).DIRECTORY_SEPARATOR);
|
||||
|
||||
( ! is_dir($app_path) and is_dir(DOCROOT.$app_path)) and $app_path = DOCROOT.$app_path;
|
||||
( ! is_dir($core_path) and is_dir(DOCROOT.$core_path)) and $core_path = DOCROOT.$core_path;
|
||||
( ! is_dir($vendor_path) and is_dir(DOCROOT.$vendor_path)) and $vendor_path = DOCROOT.$vendor_path;
|
||||
( ! is_dir($package_path) and is_dir(DOCROOT.$package_path)) and $package_path = DOCROOT.$package_path;
|
||||
|
||||
define('APPPATH', realpath($app_path).DIRECTORY_SEPARATOR);
|
||||
define('PKGPATH', realpath($package_path).DIRECTORY_SEPARATOR);
|
||||
define('VENDORPATH', realpath($vendor_path).DIRECTORY_SEPARATOR);
|
||||
define('COREPATH', realpath($core_path).DIRECTORY_SEPARATOR);
|
||||
|
||||
unset($app_path, $core_path, $package_path, $_SERVER['app_path'], $_SERVER['core_path'], $_SERVER['package_path']);
|
||||
|
||||
// Get the start time and memory for use later
|
||||
defined('FUEL_START_TIME') or define('FUEL_START_TIME', microtime(true));
|
||||
defined('FUEL_START_MEM') or define('FUEL_START_MEM', memory_get_usage());
|
||||
|
||||
// Load the Composer autoloader if present
|
||||
defined('VENDORPATH') or define('VENDORPATH', realpath(COREPATH.'..'.DS.'vendor').DS);
|
||||
if ( ! is_file(VENDORPATH.'autoload.php'))
|
||||
{
|
||||
die('Composer is not installed. Please run "php composer.phar update" in the project root to install Composer');
|
||||
}
|
||||
require VENDORPATH.'autoload.php';
|
||||
|
||||
if (class_exists('AspectMock\Kernel'))
|
||||
{
|
||||
// Configure AspectMock
|
||||
$kernel = \AspectMock\Kernel::getInstance();
|
||||
$kernel->init(array(
|
||||
'debug' => true,
|
||||
'appDir' => __DIR__.'/../',
|
||||
'includePaths' => array(
|
||||
APPPATH, COREPATH, PKGPATH,
|
||||
),
|
||||
'excludePaths' => array(
|
||||
APPPATH.'tests', COREPATH.'tests',
|
||||
),
|
||||
'cacheDir' => APPPATH.'tmp/AspectMock',
|
||||
));
|
||||
|
||||
// Load in the Fuel autoloader
|
||||
$kernel->loadFile(COREPATH.'classes'.DIRECTORY_SEPARATOR.'autoloader.php');
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load in the Fuel autoloader
|
||||
require COREPATH.'classes'.DIRECTORY_SEPARATOR.'autoloader.php';
|
||||
}
|
||||
|
||||
class_alias('Fuel\\Core\\Autoloader', 'Autoloader');
|
||||
|
||||
// Boot the app
|
||||
require_once APPPATH.'bootstrap.php';
|
||||
|
||||
// Set test mode
|
||||
\Fuel::$is_test = true;
|
||||
|
||||
// Ad hoc fix for AspectMock error
|
||||
if (class_exists('AspectMock\Kernel'))
|
||||
{
|
||||
class_exists('Errorhandler');
|
||||
}
|
||||
|
||||
// Import the TestCase class
|
||||
import('testcase');
|
@ -1,657 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Identifies the platform, browser, robot, or mobile device from the user agent string
|
||||
*
|
||||
* This class uses PHP's get_browser() to get details from the browsers user agent
|
||||
* string. If not available, it can use a coded alternative using the php_browscap.ini
|
||||
* file from http://browsers.garykeith.com.
|
||||
*
|
||||
* @package Fuel
|
||||
* @subpackage Core
|
||||
* @category Core
|
||||
* @author Harro Verton
|
||||
*/
|
||||
|
||||
class Agent
|
||||
{
|
||||
/**
|
||||
* @var array information about the current browser
|
||||
*/
|
||||
protected static $properties = array(
|
||||
'browser' => 'unknown',
|
||||
'version' => 0,
|
||||
'majorver' => 0,
|
||||
'minorver' => 0,
|
||||
'platform' => 'unknown',
|
||||
'alpha' => false,
|
||||
'beta' => false,
|
||||
'win16' => false,
|
||||
'win32' => false,
|
||||
'win64' => false,
|
||||
'frames' => false,
|
||||
'iframes' => false,
|
||||
'tables' => false,
|
||||
'cookies' => false,
|
||||
'backgroundsounds' => false,
|
||||
'javascript' => false,
|
||||
'vbscript' => false,
|
||||
'javaapplets' => false,
|
||||
'activexcontrols' => false,
|
||||
'isbanned' => false,
|
||||
'ismobiledevice' => false,
|
||||
'issyndicationreader' => false,
|
||||
'crawler' => false,
|
||||
'cssversion' => 0,
|
||||
'aolversion' => 0,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array property to cache key mapping
|
||||
*/
|
||||
protected static $keys = array(
|
||||
'browser' => 'A',
|
||||
'version' => 'B',
|
||||
'majorver' => 'C',
|
||||
'minorver' => 'D',
|
||||
'platform' => 'E',
|
||||
'alpha' => 'F',
|
||||
'beta' => 'G',
|
||||
'win16' => 'H',
|
||||
'win32' => 'I',
|
||||
'win64' => 'J',
|
||||
'frames' => 'K',
|
||||
'iframes' => 'L',
|
||||
'tables' => 'M',
|
||||
'cookies' => 'N',
|
||||
'backgroundsounds' => 'O',
|
||||
'javascript' => 'P',
|
||||
'vbscript' => 'Q',
|
||||
'javaapplets' => 'R',
|
||||
'activexcontrols' => 'S',
|
||||
'isbanned' => 'T',
|
||||
'ismobiledevice' => 'U',
|
||||
'issyndicationreader' => 'V',
|
||||
'crawler' => 'W',
|
||||
'cssversion' => 'X',
|
||||
'aolversion' => 'Y',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array global config defaults
|
||||
*/
|
||||
protected static $defaults = array(
|
||||
'browscap' => array(
|
||||
'enabled' => true,
|
||||
'url' => 'http://browscap.org/stream?q=Lite_PHP_BrowsCapINI',
|
||||
'method' => 'wrapper',
|
||||
'proxy' => array(
|
||||
'host' => null,
|
||||
'port' => null,
|
||||
'auth' => 'none',
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
),
|
||||
'file' => '',
|
||||
),
|
||||
'cache' => array(
|
||||
'driver' => '',
|
||||
'expiry' => 604800,
|
||||
'identifier' => 'fuel.agent',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array global config items
|
||||
*/
|
||||
protected static $config = array(
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string detected user agent string
|
||||
*/
|
||||
protected static $user_agent = '';
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// public static methods
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* map the user agent string to browser specifications
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
// fetch and store the user agent
|
||||
static::$user_agent = \Input::server('http_user_agent', '');
|
||||
|
||||
// fetch and process the configuration
|
||||
\Config::load('agent', true);
|
||||
|
||||
static::$config = array_merge(static::$defaults, \Config::get('agent', array()));
|
||||
|
||||
// validate the browscap configuration
|
||||
if ( ! is_array(static::$config['browscap']))
|
||||
{
|
||||
static::$config['browscap'] = static::$defaults['browscap'];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! array_key_exists('enabled', static::$config['browscap']) or ! is_bool(static::$config['browscap']['enabled']))
|
||||
{
|
||||
static::$config['browscap']['enabled'] = true;
|
||||
}
|
||||
|
||||
if ( ! array_key_exists('url', static::$config['browscap']) or ! is_string(static::$config['browscap']['url']))
|
||||
{
|
||||
static::$config['browscap']['url'] = static::$defaults['browscap']['url'];
|
||||
}
|
||||
|
||||
if ( ! array_key_exists('file', static::$config['browscap']) or ! is_string(static::$config['browscap']['file']))
|
||||
{
|
||||
static::$config['browscap']['file'] = static::$defaults['browscap']['file'];
|
||||
}
|
||||
|
||||
if ( ! array_key_exists('method', static::$config['browscap']) or ! is_string(static::$config['browscap']['method']))
|
||||
{
|
||||
static::$config['browscap']['method'] = static::$defaults['browscap']['method'];
|
||||
}
|
||||
static::$config['browscap']['method'] = strtolower(static::$config['browscap']['method']);
|
||||
}
|
||||
|
||||
// validate the cache configuration
|
||||
if ( ! is_array(static::$config['cache']))
|
||||
{
|
||||
static::$config['cache'] = static::$defaults['cache'];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! array_key_exists('driver', static::$config['cache']) or ! is_string(static::$config['cache']['driver']))
|
||||
{
|
||||
static::$config['cache']['driver'] = static::$defaults['cache']['driver'];
|
||||
}
|
||||
|
||||
if ( ! array_key_exists('expiry', static::$config['cache']) or ! is_numeric(static::$config['cache']['expiry']) or static::$config['cache']['expiry'] < 7200)
|
||||
{
|
||||
static::$config['cache']['expiry'] = static::$defaults['cache']['expiry'];
|
||||
}
|
||||
|
||||
if ( ! array_key_exists('identifier', static::$config['cache']) or ! is_string(static::$config['cache']['identifier']))
|
||||
{
|
||||
static::$config['cache']['identifier'] = static::$defaults['cache']['identifier'];
|
||||
}
|
||||
}
|
||||
|
||||
// do we have a user agent?
|
||||
if (static::$user_agent)
|
||||
{
|
||||
// try the build in get_browser() method
|
||||
if (static::$config['browscap']['method'] == 'local' or ini_get('browscap') == '' or false === $browser = get_browser(static::$user_agent, true))
|
||||
{
|
||||
// if it fails, emulate get_browser()
|
||||
$browser = static::get_from_browscap();
|
||||
}
|
||||
|
||||
if ($browser)
|
||||
{
|
||||
// save it for future reference
|
||||
static::$properties = array_change_key_case($browser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* get the normalized browser name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function browser()
|
||||
{
|
||||
return static::$properties['browser'];
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get the browser platform
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function platform()
|
||||
{
|
||||
return static::$properties['platform'];
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get the Browser Version
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function version()
|
||||
{
|
||||
return static::$properties['version'];
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get any browser property
|
||||
*
|
||||
* @param string $property
|
||||
* @return string|null
|
||||
*/
|
||||
public static function property($property = null)
|
||||
{
|
||||
$property = strtolower($property);
|
||||
return array_key_exists($property, static::$properties) ? static::$properties[$property] : null;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get all browser properties
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function properties()
|
||||
{
|
||||
return static::$properties;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* check if the current browser is a robot or crawler
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_robot()
|
||||
{
|
||||
return static::$properties['crawler'];
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* check if the current browser is mobile device
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_mobiledevice()
|
||||
{
|
||||
return static::$properties['ismobiledevice'];
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* check if the current browser accepts a specific language
|
||||
*
|
||||
* @param string $language optional ISO language code, defaults to 'en'
|
||||
* @return bool
|
||||
*/
|
||||
public static function accepts_language($language = 'en')
|
||||
{
|
||||
return (in_array(strtolower($language), static::languages(), true)) ? true : false;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* check if the current browser accepts a specific character set
|
||||
*
|
||||
* @param string $charset optional character set, defaults to 'utf-8'
|
||||
* @return bool
|
||||
*/
|
||||
public static function accepts_charset($charset = 'utf-8')
|
||||
{
|
||||
return (in_array(strtolower($charset), static::charsets(), true)) ? true : false;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* get the list of browser accepted languages
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function languages()
|
||||
{
|
||||
return explode(',', preg_replace('/(;q=[0-9\.]+)/i', '', strtolower(trim(\Input::server('http_accept_language')))));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* get the list of browser accepted charactersets
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function charsets()
|
||||
{
|
||||
return explode(',', preg_replace('/(;q=.+)/i', '', strtolower(trim(\Input::server('http_accept_charset')))));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
// internal static methods
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* use the parsed php_browscap.ini file to find a user agent match
|
||||
*
|
||||
* @return mixed array if a match is found, of false if not cached yet
|
||||
*/
|
||||
protected static function get_from_browscap()
|
||||
{
|
||||
$cache = \Cache::forge(static::$config['cache']['identifier'].'.browscap', static::$config['cache']['driver']);
|
||||
|
||||
// load the cached browscap data
|
||||
try
|
||||
{
|
||||
$browscap = $cache->get();
|
||||
}
|
||||
// browscap not cached
|
||||
catch (\Exception $e)
|
||||
{
|
||||
$browscap = static::$config['browscap']['enabled'] ? static::parse_browscap() : array();
|
||||
}
|
||||
|
||||
$search = array('\*', '\?');
|
||||
$replace = array('.*', '.');
|
||||
|
||||
$result = false;
|
||||
|
||||
// find a match for the user agent string
|
||||
foreach($browscap as $browser => $properties)
|
||||
{
|
||||
$pattern = '@^'.str_replace($search, $replace, preg_quote($browser, '@')).'$@i';
|
||||
if (preg_match($pattern, static::$user_agent))
|
||||
{
|
||||
// store the browser name
|
||||
$properties['browser'] = $browser;
|
||||
|
||||
// fetch possible parent info
|
||||
if (array_key_exists('Parent', $properties))
|
||||
{
|
||||
if ($properties['Parent'] > 0)
|
||||
{
|
||||
$parent = array_slice($browscap, $properties['Parent'], 1);
|
||||
unset($properties['Parent']);
|
||||
$properties = array_merge(current($parent), $properties);
|
||||
|
||||
// store the browser name
|
||||
$properties['browser'] = key($parent);
|
||||
}
|
||||
}
|
||||
|
||||
// normalize keys
|
||||
$properties = \Arr::replace_key($properties, array_flip(static::$keys));
|
||||
|
||||
// merge it with the defaults to add missing values
|
||||
$result = array_merge(static::$properties, $properties);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* download and parse the browscap file
|
||||
*
|
||||
* @return array array with parsed download info, or empty if the download is disabled of failed
|
||||
* @throws \Exception
|
||||
* @throws \FuelException
|
||||
*/
|
||||
protected static function parse_browscap()
|
||||
{
|
||||
$cache = \Cache::forge(static::$config['cache']['identifier'].'.browscap_file', static::$config['cache']['driver']);
|
||||
|
||||
// get the browscap.ini file
|
||||
switch (static::$config['browscap']['method'])
|
||||
{
|
||||
case 'local':
|
||||
if ( ! is_file(static::$config['browscap']['file']) or filesize(static::$config['browscap']['file']) == 0)
|
||||
{
|
||||
throw new \Exception('Agent class: could not open the local browscap.ini file: '.static::$config['browscap']['file']);
|
||||
}
|
||||
$data = @file_get_contents(static::$config['browscap']['file']);
|
||||
break;
|
||||
|
||||
// socket connections are not implemented yet!
|
||||
case 'sockets':
|
||||
$data = false;
|
||||
break;
|
||||
|
||||
case 'curl':
|
||||
// initialize the proxy request
|
||||
$curl = curl_init();
|
||||
curl_setopt($curl, CURLOPT_BINARYTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_setopt($curl, CURLOPT_MAXREDIRS, 5);
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_HEADER, 0);
|
||||
curl_setopt($curl, CURLOPT_USERAGENT, 'Fuel PHP framework - Agent class (http://fuelphp.com)');
|
||||
curl_setopt($curl, CURLOPT_URL, static::$config['browscap']['url']);
|
||||
|
||||
// add a proxy configuration if needed
|
||||
if ( ! empty(static::$config['browscap']['proxy']['host']) and ! empty(static::$config['browscap']['proxy']['port']))
|
||||
{
|
||||
curl_setopt($curl, CURLOPT_PROXY, static::$config['browscap']['proxy']['host']);
|
||||
curl_setopt($curl, CURLOPT_PROXYPORT, static::$config['browscap']['proxy']['port']);
|
||||
}
|
||||
|
||||
// authentication set?
|
||||
switch (static::$config['browscap']['proxy']['auth'])
|
||||
{
|
||||
case 'basic':
|
||||
curl_setopt($curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
|
||||
break;
|
||||
|
||||
case 'ntlm':
|
||||
curl_setopt($curl, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
|
||||
break;
|
||||
|
||||
default:
|
||||
// no action
|
||||
}
|
||||
|
||||
// do we need to pass credentials?
|
||||
switch (static::$config['browscap']['proxy']['auth'])
|
||||
{
|
||||
case 'basic':
|
||||
case 'ntlm':
|
||||
if (empty(static::$config['browscap']['proxy']['username']) or empty(static::$config['browscap']['proxy']['password']))
|
||||
{
|
||||
logger(\Fuel::L_ERROR, 'Failed to set a proxy for Agent, cURL auth configured but no username or password configured');
|
||||
}
|
||||
else
|
||||
{
|
||||
curl_setopt($curl, CURLOPT_PROXYUSERPWD, static::$config['browscap']['proxy']['username'].':'.static::$config['browscap']['proxy']['password']);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// no action
|
||||
}
|
||||
|
||||
// execute the request
|
||||
$data = curl_exec($curl);
|
||||
|
||||
// check the response
|
||||
$result = curl_getinfo($curl);
|
||||
|
||||
if ($result['http_code'] !== 200)
|
||||
{
|
||||
logger(\Fuel::L_ERROR, 'Failed to download browscap.ini file. cURL response code was '.$result['http_code'], 'Agent::parse_browscap');
|
||||
logger(\Fuel::L_ERROR, $data);
|
||||
$data = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'wrapper':
|
||||
// set our custom user agent
|
||||
ini_set('user_agent', 'Fuel PHP framework - Agent class (http://fuelphp.com)');
|
||||
|
||||
// create a stream context if needed
|
||||
$context = null;
|
||||
if ( ! empty(static::$config['browscap']['proxy']['host']) and ! empty(static::$config['browscap']['proxy']['port']))
|
||||
{
|
||||
$context = array (
|
||||
'http' => array (
|
||||
'proxy' => 'tcp://'.static::$config['browscap']['proxy']['host'].':'.static::$config['browscap']['proxy']['port'],
|
||||
'request_fulluri' => true,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// add credentials if needed
|
||||
if ( ! empty(static::$config['browscap']['proxy']['auth']) and static::$config['browscap']['proxy']['auth'] == 'basic')
|
||||
{
|
||||
if ( ! empty(static::$config['browscap']['proxy']['username']) and ! empty(static::$config['browscap']['proxy']['password']))
|
||||
{
|
||||
$context['http']['header'] = 'Proxy-Authorization: Basic '.base64_encode(static::$config['browscap']['proxy']['username'].':'.static::$config['browscap']['proxy']['password']);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger(\Fuel::L_ERROR, 'Failed to set a proxy for Agent, "basic" auth configured but no username or password configured');
|
||||
$context = null;
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to download the file
|
||||
try
|
||||
{
|
||||
if ($context)
|
||||
{
|
||||
$context = stream_context_create($context);
|
||||
}
|
||||
$data = file_get_contents(static::$config['browscap']['url'], false, $context);
|
||||
}
|
||||
catch (\ErrorException $e)
|
||||
{
|
||||
logger(\Fuel::L_ERROR, 'Failed to download browscap.ini file.', 'Agent::parse_browscap');
|
||||
logger(\Fuel::L_ERROR, $e->getMessage());
|
||||
$data = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ($data === false)
|
||||
{
|
||||
// if no data could be download, try retrieving a cached version
|
||||
try
|
||||
{
|
||||
$data = $cache->get(false);
|
||||
|
||||
// if the cached version is used, only cache the parsed result for a day
|
||||
static::$config['cache']['expiry'] = 86400;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
logger(\Fuel::L_ERROR, 'Failed to get the cache of browscap.ini file.', 'Agent::parse_browscap');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// store the downloaded data in the cache as a backup for future use
|
||||
$cache->set($data, null);
|
||||
}
|
||||
|
||||
// parse the downloaded data
|
||||
$browsers = @parse_ini_string($data, true, INI_SCANNER_RAW) or $browsers = array();
|
||||
|
||||
// remove the version and timestamp entry
|
||||
array_shift($browsers);
|
||||
|
||||
$result = array();
|
||||
|
||||
// reverse sort on key string length
|
||||
uksort($browsers, function($a, $b) { return strlen($a) < strlen($b) ? 1 : -1; } );
|
||||
|
||||
$index = array();
|
||||
$count = 0;
|
||||
|
||||
// reduce the array keys
|
||||
foreach($browsers as $browser => $properties)
|
||||
{
|
||||
$index[$browser] = $count++;
|
||||
|
||||
// fix any type issues
|
||||
foreach ($properties as $var => $value)
|
||||
{
|
||||
if (is_numeric($value))
|
||||
{
|
||||
$properties[$var] = $value + 0;
|
||||
}
|
||||
elseif ($value == 'true')
|
||||
{
|
||||
$properties[$var] = true;
|
||||
}
|
||||
elseif ($value == 'false')
|
||||
{
|
||||
$properties[$var] = false;
|
||||
}
|
||||
}
|
||||
|
||||
$result[$browser] = \Arr::replace_key($properties, static::$keys);
|
||||
|
||||
}
|
||||
|
||||
// reduce parent links to
|
||||
foreach($result as $browser => &$properties)
|
||||
{
|
||||
if (array_key_exists('Parent', $properties))
|
||||
{
|
||||
if ($properties['Parent'] == 'DefaultProperties')
|
||||
{
|
||||
unset($properties['Parent']);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (array_key_exists($properties['Parent'], $index))
|
||||
{
|
||||
$properties['Parent'] = $index[$properties['Parent']];
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($properties['Parent']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save the result to the cache
|
||||
if ( ! empty($result))
|
||||
{
|
||||
$cache = \Cache::forge(static::$config['cache']['identifier'].'.browscap', static::$config['cache']['driver']);
|
||||
$cache->set($result, static::$config['cache']['expiry']);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,254 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* The Asset class allows you to easily work with your apps assets.
|
||||
* It allows you to specify multiple paths to be searched for the
|
||||
* assets.
|
||||
*
|
||||
* You can configure the paths by copying the core/config/asset.php
|
||||
* config file into your app/config folder and changing the settings.
|
||||
*
|
||||
* @package Fuel
|
||||
* @subpackage Core
|
||||
*/
|
||||
class Asset
|
||||
{
|
||||
/**
|
||||
* default instance
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $_instance = null;
|
||||
|
||||
/**
|
||||
* All the Asset instances
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $_instances = array();
|
||||
|
||||
/**
|
||||
* Default configuration values
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $default_config = array(
|
||||
'paths' => array('assets/'),
|
||||
'img_dir' => 'img/',
|
||||
'js_dir' => 'js/',
|
||||
'css_dir' => 'css/',
|
||||
'folders' => array(
|
||||
'css' => array(),
|
||||
'js' => array(),
|
||||
'img' => array(),
|
||||
),
|
||||
'url' => '/',
|
||||
'add_mtime' => true,
|
||||
'indent_level' => 1,
|
||||
'indent_with' => "\t",
|
||||
'auto_render' => true,
|
||||
'fail_silently' => false,
|
||||
);
|
||||
|
||||
/**
|
||||
* This is called automatically by the Autoloader. It loads in the config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
\Config::load('asset', true, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a specific instance, or the default instance (is created if necessary)
|
||||
*
|
||||
* @param string $instance instance name
|
||||
* @return Asset_Instance
|
||||
*/
|
||||
public static function instance($instance = null)
|
||||
{
|
||||
if ($instance !== null)
|
||||
{
|
||||
if ( ! array_key_exists($instance, static::$_instances))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return static::$_instances[$instance];
|
||||
}
|
||||
|
||||
if (static::$_instance === null)
|
||||
{
|
||||
static::$_instance = static::forge();
|
||||
}
|
||||
|
||||
return static::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new instance of the Asset class.
|
||||
*
|
||||
* @param string $name instance name
|
||||
* @param array $config default config overrides
|
||||
* @return Asset_Instance
|
||||
*/
|
||||
public static function forge($name = 'default', array $config = array())
|
||||
{
|
||||
if ($exists = static::instance($name))
|
||||
{
|
||||
\Errorhandler::notice('Asset with this name exists already, cannot be overwritten.');
|
||||
return $exists;
|
||||
}
|
||||
|
||||
static::$_instances[$name] = new \Asset_Instance(array_merge(static::$default_config, \Config::get('asset'), $config));
|
||||
|
||||
if ($name == 'default')
|
||||
{
|
||||
static::$_instance = static::$_instances[$name];
|
||||
}
|
||||
|
||||
return static::$_instances[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given path to the front of the asset paths array. It adds paths
|
||||
* in a way so that asset paths are used First in Last Out.
|
||||
*
|
||||
* @param string $path the path to add
|
||||
* @param string $type optional path type (js, css or img)
|
||||
* @return void
|
||||
*/
|
||||
public static function add_path($path, $type = null)
|
||||
{
|
||||
static::instance()->add_path($path, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given path from the asset paths array
|
||||
*
|
||||
* @param string $path the path to remove
|
||||
* @return void
|
||||
*/
|
||||
public static function remove_path($path, $type = null)
|
||||
{
|
||||
static::instance()->remove_path($path, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the given group. Each tag will be separated by a line break.
|
||||
* You can optionally tell it to render the files raw. This means that
|
||||
* all CSS and JS files in the group will be read and the contents included
|
||||
* in the returning value.
|
||||
*
|
||||
* @param mixed $group the group to render
|
||||
* @param bool $raw whether to return the raw file or not
|
||||
* @return string the group's output
|
||||
*/
|
||||
public static function render($group = null, $raw = false)
|
||||
{
|
||||
return static::instance()->render($group, $raw);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* CSS
|
||||
*
|
||||
* Either adds the stylesheet to the group, or returns the CSS tag.
|
||||
*
|
||||
* @param mixed $stylesheets The file name, or an array files.
|
||||
* @param array $attr An array of extra attributes
|
||||
* @param string $group The asset group name
|
||||
* @param bool $raw whether to return the raw file or not when group is not set
|
||||
* @return string
|
||||
*/
|
||||
public static function css($stylesheets = array(), $attr = array(), $group = NULL, $raw = false)
|
||||
{
|
||||
return static::instance()->assettype('css', $stylesheets, $attr, $group, $raw);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* JS
|
||||
*
|
||||
* Either adds the javascript to the group, or returns the script tag.
|
||||
*
|
||||
* @param mixed $scripts The file name, or an array files.
|
||||
* @param array $attr An array of extra attributes
|
||||
* @param string $group The asset group name
|
||||
* @param bool $raw whether to return the raw file or not when group is not set
|
||||
* @return string
|
||||
*/
|
||||
public static function js($scripts = array(), $attr = array(), $group = NULL, $raw = false)
|
||||
{
|
||||
return static::instance()->assettype('js', $scripts, $attr, $group, $raw);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Img
|
||||
*
|
||||
* Either adds the image to the group, or returns the image tag.
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $images The file name, or an array files.
|
||||
* @param array $attr An array of extra attributes
|
||||
* @param string $group The asset group name
|
||||
* @return string
|
||||
*/
|
||||
public static function img($images = array(), $attr = array(), $group = NULL)
|
||||
{
|
||||
return static::instance()->assettype('img', $images, $attr, $group);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get File
|
||||
*
|
||||
* Locates a file in all the asset paths, and return it relative to the docroot
|
||||
*
|
||||
* @access public
|
||||
* @param string $file The filename to locate
|
||||
* @param string $type The type of asset file
|
||||
* @param string $folder The sub-folder to look in (optional)
|
||||
* @return mixed Either the path to the file or false if not found
|
||||
*/
|
||||
public static function get_file($file, $type, $folder = '')
|
||||
{
|
||||
return static::instance()->get_file($file, $type, $folder);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Find File
|
||||
*
|
||||
* Locates a file in all the asset paths.
|
||||
*
|
||||
* @access public
|
||||
* @param string $file The filename to locate
|
||||
* @param string $type The type of asset file to search
|
||||
* @param string $folder The sub-folder to look in (optional)
|
||||
* @return mixed Either the path to the file or false if not found
|
||||
*/
|
||||
public static function find_file($file, $type, $folder = '')
|
||||
{
|
||||
return static::instance()->find_file($file, $type, $folder);
|
||||
}
|
||||
}
|
@ -1,632 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* The Asset class allows you to easily work with your apps assets.
|
||||
* It allows you to specify multiple paths to be searched for the
|
||||
* assets.
|
||||
*
|
||||
* You can configure the paths by copying the core/config/asset.php
|
||||
* config file into your app/config folder and changing the settings.
|
||||
*
|
||||
* @package Fuel
|
||||
* @subpackage Core
|
||||
*/
|
||||
class Asset_Instance
|
||||
{
|
||||
/**
|
||||
* @var array the asset paths to be searched
|
||||
*/
|
||||
protected $_asset_paths = array(
|
||||
'css' => array(),
|
||||
'js' => array(),
|
||||
'img' => array(),
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array the sub-folders to be searched
|
||||
*/
|
||||
protected $_path_folders = array(
|
||||
'css' => 'css/',
|
||||
'js' => 'js/',
|
||||
'img' => 'img/',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array custom type renderers
|
||||
*/
|
||||
protected $_renderers = array(
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string the URL to be prepended to all assets
|
||||
*/
|
||||
protected $_asset_url = '/';
|
||||
|
||||
/**
|
||||
* @var bool whether to append the file mtime to the url
|
||||
*/
|
||||
protected $_add_mtime = true;
|
||||
|
||||
/**
|
||||
* @var array holds the groups of assets
|
||||
*/
|
||||
protected $_groups = array();
|
||||
|
||||
/**
|
||||
* @var string prefix for generated output to provide proper indentation
|
||||
*/
|
||||
protected $_indent = '';
|
||||
|
||||
/**
|
||||
* @var bool if true, directly renders the output of no group name is given
|
||||
*/
|
||||
protected $_auto_render = true;
|
||||
|
||||
/**
|
||||
* @var bool if true the 'not found' exception will not be thrown and the asset is ignored.
|
||||
*/
|
||||
protected $_fail_silently = false;
|
||||
|
||||
/**
|
||||
* @var bool if true, will always true to resolve assets. if false, it will only try to resolve if the asset url is relative.
|
||||
*/
|
||||
protected $_always_resolve = false;
|
||||
|
||||
/**
|
||||
* Parse the config and initialize the object instance
|
||||
*
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(Array $config)
|
||||
{
|
||||
// look for global search path folders
|
||||
foreach ($config as $key => $value)
|
||||
{
|
||||
if (\Str::ends_with($key, '_dir'))
|
||||
{
|
||||
$key = substr($key, 0, -4);
|
||||
$this->_path_folders[$key] = $this->_unify_path($value);
|
||||
}
|
||||
}
|
||||
|
||||
// global search paths
|
||||
foreach ($config['paths'] as $path)
|
||||
{
|
||||
$this->add_path($path);
|
||||
}
|
||||
|
||||
// per-type search paths
|
||||
foreach ($config['folders'] as $type => $folders)
|
||||
{
|
||||
is_array($folders) or $folders = array($folders);
|
||||
|
||||
foreach ($folders as $path)
|
||||
{
|
||||
$this->add_path($path, $type);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_add_mtime = (bool) $config['add_mtime'];
|
||||
$this->_asset_url = $config['url'];
|
||||
$this->_indent = str_repeat($config['indent_with'], $config['indent_level']);
|
||||
$this->_auto_render = (bool) $config['auto_render'];
|
||||
$this->_fail_silently = (bool) $config['fail_silently'];
|
||||
$this->_always_resolve = (bool) $config['always_resolve'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide backward compatibility for old type methods
|
||||
*
|
||||
* @param $method
|
||||
* @param $args
|
||||
* @return mixed
|
||||
* @throws \ErrorException
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
// check if we can render this type
|
||||
if ( ! isset($this->_path_folders[$method]))
|
||||
{
|
||||
throw new \ErrorException('Call to undefined method Fuel\Core\Asset_Instance::'.$method.'()');
|
||||
}
|
||||
|
||||
// add the type to the arguments
|
||||
array_unshift($args, $method);
|
||||
|
||||
// call assettype to store the info
|
||||
return call_user_func_array(array($this, 'assettype'), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new asset type to the list so we can load files of this type
|
||||
*
|
||||
* @param string $type new path type
|
||||
* @param string $path optional default path
|
||||
* @param Closure $renderer function to custom render this type
|
||||
*
|
||||
* @return object current instance
|
||||
*/
|
||||
public function add_type($type, $path = null, $renderer = null)
|
||||
{
|
||||
isset($this->_asset_paths[$type]) or $this->_asset_paths[$type] = array();
|
||||
isset($this->_path_folders[$type]) or $this->_path_folders[$type] = $type.'/';
|
||||
|
||||
if ( ! is_null($path))
|
||||
{
|
||||
$path = $this->_unify_path($path);
|
||||
$this->_asset_paths[$type][] = $path;
|
||||
}
|
||||
|
||||
if ( ! is_null($renderer))
|
||||
{
|
||||
if ( ! $renderer instanceOf \Closure)
|
||||
{
|
||||
throw new \OutOfBoundsException('Asset type renderer must be passed as a Closure!');
|
||||
}
|
||||
|
||||
$this->_renderers[$type] = $renderer;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given path to the front of the asset paths array. It adds paths
|
||||
* in a way so that asset paths are used First in Last Out.
|
||||
*
|
||||
* @param string $path the path to add
|
||||
* @param string $type optional path type (js, css or img)
|
||||
* @return object current instance
|
||||
*/
|
||||
public function add_path($path, $type = null)
|
||||
{
|
||||
is_null($type) and $type = $this->_path_folders;
|
||||
empty($path) and $path = DOCROOT;
|
||||
|
||||
if( is_array($type))
|
||||
{
|
||||
foreach ($type as $key => $folder)
|
||||
{
|
||||
is_numeric($key) and $key = $folder;
|
||||
$folder = $this->_unify_path($path).ltrim($this->_unify_path($folder), DS);
|
||||
in_array($folder, $this->_asset_paths[$key]) or array_unshift($this->_asset_paths[$key], $folder);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// create the asset type if it doesn't exist
|
||||
if ( ! isset($this->_asset_paths[$type]))
|
||||
{
|
||||
$this->_asset_paths[$type] = array();
|
||||
$this->_path_folders[$type] = $type.'/';
|
||||
}
|
||||
|
||||
$path = $this->_unify_path($path);
|
||||
in_array($path, $this->_asset_paths[$type]) or array_unshift($this->_asset_paths[$type], $path);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given path from the asset paths array
|
||||
*
|
||||
* @param string $path the path to remove
|
||||
* @param string $type optional path type (js, css or img)
|
||||
* @return object current instance
|
||||
*/
|
||||
public function remove_path($path, $type = null)
|
||||
{
|
||||
is_null($type) and $type = $this->_path_folders;
|
||||
|
||||
if( is_array($type))
|
||||
{
|
||||
foreach ($type as $key => $folder)
|
||||
{
|
||||
is_numeric($key) and $key = $folder;
|
||||
$folder = $this->_unify_path($path).ltrim($this->_unify_path($folder), DS);
|
||||
if (($found = array_search($folder, $this->_asset_paths[$key])) !== false)
|
||||
{
|
||||
unset($this->_asset_paths[$key][$found]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$path = $this->_unify_path($path);
|
||||
if (($key = array_search($path, $this->_asset_paths[$type])) !== false)
|
||||
{
|
||||
unset($this->_asset_paths[$type][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Asset type store.
|
||||
*
|
||||
* Either adds the asset to the group, or directly return the tag.
|
||||
*
|
||||
* @param string $type The asset type
|
||||
* @param mixed $files The file name, or an array files.
|
||||
* @param array $attr An array of extra attributes
|
||||
* @param string $group The asset group name
|
||||
* @param boolean $raw whether to return the raw file or not when group is not set (optional)
|
||||
* @return string|object Rendered asset or current instance when adding to group
|
||||
*/
|
||||
public function assettype($type, $files = array(), $attr = array(), $group = null, $raw = false)
|
||||
{
|
||||
static $temp_group = 50000000;
|
||||
|
||||
if ($group === null)
|
||||
{
|
||||
$render = $this->_auto_render;
|
||||
$group = $render ? (string) (++$temp_group) : '_default_';
|
||||
}
|
||||
else
|
||||
{
|
||||
$render = false;
|
||||
}
|
||||
|
||||
$this->_parse_assets($type, $files, $attr, $group, $raw);
|
||||
|
||||
if ($render)
|
||||
{
|
||||
return $this->render($group, $raw);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Find File
|
||||
*
|
||||
* Locates a file in all the asset paths.
|
||||
*
|
||||
* @param string $file The filename to locate
|
||||
* @param string $type The type of asset file to search
|
||||
* @param string $folder The sub-folder to look in (optional)
|
||||
* @return mixed Either the path to the file or false if not found
|
||||
*/
|
||||
public function find_file($file, $type, $folder = '')
|
||||
{
|
||||
foreach ($this->_asset_paths[$type] as $path)
|
||||
{
|
||||
empty($folder) or $folder = $this->_unify_path($folder);
|
||||
|
||||
if (is_file($newfile = $path.$folder.$this->_unify_path($file, null, false)))
|
||||
{
|
||||
// return the file found, make sure it uses forward slashes on Windows
|
||||
return str_replace(DS, '/', $newfile);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get File
|
||||
*
|
||||
* Locates a file in all the asset paths, and return it relative to the docroot
|
||||
*
|
||||
* @param string $file The filename to locate
|
||||
* @param string $type The type of asset file
|
||||
* @param string $folder The sub-folder to look in (optional)
|
||||
* @return mixed Either the path to the file or false if not found
|
||||
*/
|
||||
public function get_file($file, $type, $folder = '')
|
||||
{
|
||||
if ($file = $this->find_file($file, $type, $folder))
|
||||
{
|
||||
strpos($file, DOCROOT) === 0 and $file = substr($file, strlen(DOCROOT));
|
||||
|
||||
return $this->_asset_url.$file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the given group. Each tag will be separated by a line break.
|
||||
* You can optionally tell it to render the files raw. This means that
|
||||
* all CSS and JS files in the group will be read and the contents included
|
||||
* in the returning value.
|
||||
*
|
||||
* @param mixed $group the group to render
|
||||
* @param bool $raw whether to return the raw file or not
|
||||
* @return string the group's output
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public function render($group = null, $raw = false)
|
||||
{
|
||||
// determine the group to render
|
||||
is_null($group) and $group = '_default_';
|
||||
|
||||
if (is_string($group))
|
||||
{
|
||||
isset($this->_groups[$group]) and $group = $this->_groups[$group];
|
||||
}
|
||||
|
||||
is_array($group) or $group = array();
|
||||
|
||||
// storage for the result
|
||||
$result = array();
|
||||
|
||||
// pre-define known types so the order is correct
|
||||
foreach($this->_path_folders as $type => $unused)
|
||||
{
|
||||
$result[$type] = '';
|
||||
}
|
||||
|
||||
// loop over the group entries
|
||||
foreach ($group as $key => $item)
|
||||
{
|
||||
// determine file name and inline status
|
||||
$type = $item['type'];
|
||||
$filename = $item['file'];
|
||||
$attr = $item['attr'];
|
||||
$inline = $item['raw'];
|
||||
|
||||
// make sure we have storage space for this result
|
||||
if ( ! isset($result[$type]))
|
||||
{
|
||||
$result[$type] = '';
|
||||
}
|
||||
|
||||
// only do a file search if the asset is not a URI
|
||||
if ($this->_always_resolve or ! preg_match('|^(\w+:)?//|', $filename))
|
||||
{
|
||||
// and only if the asset is local to the applications base_url
|
||||
if ($this->_always_resolve or ! preg_match('|^(\w+:)?//|', $this->_asset_url) or strpos($this->_asset_url, \Config::get('base_url')) === 0)
|
||||
{
|
||||
if ( ! ($file = $this->find_file($filename, $type)))
|
||||
{
|
||||
if ($raw or $inline)
|
||||
{
|
||||
$file = $filename;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($this->_fail_silently)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new \FuelException('Could not find asset: '.$filename);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($raw or $inline)
|
||||
{
|
||||
$file = file_get_contents($file);
|
||||
$inline = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$file = $this->_asset_url.$file.($this->_add_mtime ? '?'.filemtime($file) : '');
|
||||
$file = str_replace(str_replace(DS, '/', DOCROOT), '', $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// a remote file and multiple paths? use the first one!
|
||||
$path = reset($this->_asset_paths[$type]);
|
||||
$file = $this->_asset_url.$path.$filename;
|
||||
if ($raw or $inline)
|
||||
{
|
||||
$file = file_get_contents($file);
|
||||
$inline = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$file = str_replace(str_replace(DS, '/', DOCROOT), '', $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$file = $filename;
|
||||
}
|
||||
|
||||
// deal with stray backslashes on Windows
|
||||
$file = str_replace('\\', '/', $file);
|
||||
|
||||
// call the renderer for this type
|
||||
if (isset($this->_renderers[$type]))
|
||||
{
|
||||
$method = $this->_renderers[$type];
|
||||
$result[$type] .= $method($file, $attr, $inline);
|
||||
}
|
||||
else
|
||||
{
|
||||
$method = 'render_'.$type;
|
||||
if (method_exists($this, $method))
|
||||
{
|
||||
$result[$type] .= $this->$method($file, $attr, $inline);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \OutOfBoundsException('Asset does not know how to render files of type "'.$type.'"!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return them in the correct order, as a string
|
||||
return implode("", $result);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* CSS tag renderer
|
||||
*
|
||||
* @param $file
|
||||
* @param $attr
|
||||
* @param $inline
|
||||
* @return string
|
||||
*/
|
||||
protected function render_css($file, $attr, $inline)
|
||||
{
|
||||
// storage for the result
|
||||
$result = '';
|
||||
|
||||
// make sure we have a type
|
||||
isset($attr['type']) or $attr['type'] = 'text/css';
|
||||
|
||||
// render inline. or not
|
||||
if ($inline)
|
||||
{
|
||||
$result = html_tag('style', $attr, PHP_EOL.$file.PHP_EOL).PHP_EOL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! isset($attr['rel']) or empty($attr['rel']))
|
||||
{
|
||||
$attr['rel'] = 'stylesheet';
|
||||
}
|
||||
$attr['href'] = $file;
|
||||
|
||||
$result = $this->_indent.html_tag('link', $attr).PHP_EOL;
|
||||
}
|
||||
|
||||
// return the result
|
||||
return $result;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* JS tag renderer
|
||||
*
|
||||
* @param $file
|
||||
* @param $attr
|
||||
* @param $inline
|
||||
* @return string
|
||||
*/
|
||||
protected function render_js($file, $attr, $inline)
|
||||
{
|
||||
// storage for the result
|
||||
$result = '';
|
||||
|
||||
// render inline. or not
|
||||
$attr['type'] = 'text/javascript';
|
||||
if ($inline)
|
||||
{
|
||||
$result = html_tag('script', $attr, PHP_EOL.$file.PHP_EOL).PHP_EOL;
|
||||
}
|
||||
else
|
||||
{
|
||||
$attr['src'] = $file;
|
||||
|
||||
$result = $this->_indent.html_tag('script', $attr, '').PHP_EOL;
|
||||
}
|
||||
|
||||
// return the result
|
||||
return $result;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* IMG tag renderer
|
||||
*
|
||||
* @param $file
|
||||
* @param $attr
|
||||
* @param $inline
|
||||
* @return string
|
||||
*/
|
||||
protected function render_img($file, $attr, $inline)
|
||||
{
|
||||
// storage for the result
|
||||
$result = '';
|
||||
|
||||
// render the image
|
||||
$attr['src'] = $file;
|
||||
$attr['alt'] = isset($attr['alt']) ? $attr['alt'] : '';
|
||||
|
||||
$result = html_tag('img', $attr );
|
||||
|
||||
// return the result
|
||||
return $result;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Parse Assets
|
||||
*
|
||||
* Pareses the assets and adds them to the group
|
||||
*
|
||||
* @param string $type The asset type
|
||||
* @param mixed $assets The file name, or an array files.
|
||||
* @param array $attr An array of extra attributes
|
||||
* @param string $group The asset group name
|
||||
* @param bool $raw
|
||||
* @return string
|
||||
*/
|
||||
protected function _parse_assets($type, $assets, $attr, $group, $raw = false)
|
||||
{
|
||||
if ( ! is_array($assets))
|
||||
{
|
||||
$assets = array($assets);
|
||||
}
|
||||
|
||||
foreach ($assets as $key => $asset)
|
||||
{
|
||||
// Prevent duplicate files in a group.
|
||||
if (\Arr::get($this->_groups, "$group.$key.file") == $asset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->_groups[$group][] = array(
|
||||
'type' => $type,
|
||||
'file' => $asset,
|
||||
'raw' => $raw,
|
||||
'attr' => (array) $attr,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Unify the path
|
||||
*
|
||||
* make sure the directory separator in the path is correct for the
|
||||
* platform used, is terminated with a directory separator, and all
|
||||
* relative path references are removed
|
||||
*
|
||||
* @param string $path The path
|
||||
* @param mixed $ds Optional directory separator
|
||||
* @param boolean $trailing Optional whether to add trailing directory separator
|
||||
* @return string
|
||||
*/
|
||||
protected function _unify_path($path, $ds = null, $trailing = true)
|
||||
{
|
||||
$ds === null and $ds = DS;
|
||||
|
||||
return rtrim(str_replace(array('\\', '/'), $ds, $path), $ds).($trailing ? $ds : '');
|
||||
}
|
||||
|
||||
}
|
@ -1,404 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* The Autoloader is responsible for all class loading. It allows you to define
|
||||
* different load paths based on namespaces. It also lets you set explicit paths
|
||||
* for classes to be loaded from.
|
||||
*
|
||||
* @package Fuel
|
||||
* @subpackage Core
|
||||
*/
|
||||
class Autoloader
|
||||
{
|
||||
/**
|
||||
* @var array $classes holds all the classes and paths
|
||||
*/
|
||||
protected static $classes = array();
|
||||
|
||||
/**
|
||||
* @var array holds all the namespace paths
|
||||
*/
|
||||
protected static $namespaces = array();
|
||||
|
||||
/**
|
||||
* Holds all the PSR-0 compliant namespaces. These namespaces should
|
||||
* be loaded according to the PSR-0 standard.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $psr_namespaces = array();
|
||||
|
||||
/**
|
||||
* @var array list off namespaces of which classes will be aliased to global namespace
|
||||
*/
|
||||
protected static $core_namespaces = array(
|
||||
'Fuel\\Core',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array the default path to look in if the class is not in a package
|
||||
*/
|
||||
protected static $default_path = null;
|
||||
|
||||
/**
|
||||
* @var bool whether to initialize a loaded class
|
||||
*/
|
||||
protected static $auto_initialize = null;
|
||||
|
||||
/**
|
||||
* Adds a namespace search path. Any class in the given namespace will be
|
||||
* looked for in the given path.
|
||||
*
|
||||
* @param string $namespace the namespace
|
||||
* @param string $path the path
|
||||
* @param bool $psr whether this is a PSR-0 compliant class
|
||||
* @return void
|
||||
*/
|
||||
public static function add_namespace($namespace, $path, $psr = false)
|
||||
{
|
||||
static::$namespaces[$namespace] = $path;
|
||||
if ($psr)
|
||||
{
|
||||
static::$psr_namespaces[$namespace] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an array of namespace paths. See {add_namespace}.
|
||||
*
|
||||
* @param array $namespaces the namespaces
|
||||
* @param bool $prepend whether to prepend the namespace to the search path
|
||||
* @return void
|
||||
*/
|
||||
public static function add_namespaces(array $namespaces, $prepend = false)
|
||||
{
|
||||
if ( ! $prepend)
|
||||
{
|
||||
static::$namespaces = array_merge(static::$namespaces, $namespaces);
|
||||
}
|
||||
else
|
||||
{
|
||||
static::$namespaces = $namespaces + static::$namespaces;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the namespace's path or false when it doesn't exist.
|
||||
*
|
||||
* @param string $namespace the namespace to get the path for
|
||||
* @return array|bool the namespace path or false
|
||||
*/
|
||||
public static function namespace_path($namespace)
|
||||
{
|
||||
if ( ! array_key_exists($namespace, static::$namespaces))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return static::$namespaces[$namespace];
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a classes load path. Any class added here will not be searched for
|
||||
* but explicitly loaded from the path.
|
||||
*
|
||||
* @param string $class the class name
|
||||
* @param string $path the path to the class file
|
||||
* @return void
|
||||
*/
|
||||
public static function add_class($class, $path)
|
||||
{
|
||||
static::$classes[strtolower($class)] = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds multiple class paths to the load path. See {@see Autoloader::add_class}.
|
||||
*
|
||||
* @param array $classes the class names and paths
|
||||
* @return void
|
||||
*/
|
||||
public static function add_classes($classes)
|
||||
{
|
||||
foreach ($classes as $class => $path)
|
||||
{
|
||||
static::$classes[strtolower($class)] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aliases the given class into the given Namespace. By default it will
|
||||
* add it to the global namespace.
|
||||
*
|
||||
* <code>
|
||||
* Autoloader::alias_to_namespace('Foo\\Bar');
|
||||
* Autoloader::alias_to_namespace('Foo\\Bar', '\\Baz');
|
||||
* </code>
|
||||
*
|
||||
* @param string $class the class name
|
||||
* @param string $namespace the namespace to alias to
|
||||
*/
|
||||
public static function alias_to_namespace($class, $namespace = '')
|
||||
{
|
||||
empty($namespace) or $namespace = rtrim($namespace, '\\').'\\';
|
||||
$parts = explode('\\', $class);
|
||||
$root_class = $namespace.array_pop($parts);
|
||||
class_alias($class, $root_class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register's the autoloader to the SPL autoload stack.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register()
|
||||
{
|
||||
spl_autoload_register('Autoloader::load', true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class with namespace prefix when available
|
||||
*
|
||||
* @param string $class
|
||||
* @return bool|string
|
||||
*/
|
||||
protected static function find_core_class($class)
|
||||
{
|
||||
foreach (static::$core_namespaces as $ns)
|
||||
{
|
||||
if (array_key_exists(strtolower($ns_class = $ns.'\\'.$class), static::$classes))
|
||||
{
|
||||
return $ns_class;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a namespace for which classes may be used without the namespace prefix and
|
||||
* will be auto-aliased to the global namespace.
|
||||
* Prefixing the classes will overwrite core classes and previously added namespaces.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param bool $prefix
|
||||
* @return void
|
||||
*/
|
||||
public static function add_core_namespace($namespace, $prefix = true)
|
||||
{
|
||||
if ($prefix)
|
||||
{
|
||||
array_unshift(static::$core_namespaces, $namespace);
|
||||
}
|
||||
else
|
||||
{
|
||||
static::$core_namespaces[] = $namespace;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a class.
|
||||
*
|
||||
* @param string $class Class to load
|
||||
* @return bool If it loaded the class
|
||||
*/
|
||||
public static function load($class)
|
||||
{
|
||||
// deal with funny is_callable('static::classname') side-effect
|
||||
if (strpos($class, 'static::') === 0)
|
||||
{
|
||||
// is called from within the class, so it's already loaded
|
||||
return true;
|
||||
}
|
||||
|
||||
$loaded = false;
|
||||
$class = ltrim($class, '\\');
|
||||
$pos = strripos($class, '\\');
|
||||
|
||||
if (empty(static::$auto_initialize))
|
||||
{
|
||||
static::$auto_initialize = $class;
|
||||
}
|
||||
|
||||
if (isset(static::$classes[strtolower($class)]))
|
||||
{
|
||||
static::init_class($class, str_replace('/', DS, static::$classes[strtolower($class)]));
|
||||
$loaded = true;
|
||||
}
|
||||
elseif ($full_class = static::find_core_class($class))
|
||||
{
|
||||
if ( ! class_exists($full_class, false) and ! interface_exists($full_class, false))
|
||||
{
|
||||
include static::prep_path(static::$classes[strtolower($full_class)]);
|
||||
}
|
||||
if ( ! class_exists($class, false))
|
||||
{
|
||||
class_alias($full_class, $class);
|
||||
}
|
||||
static::init_class($class);
|
||||
$loaded = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$full_ns = substr($class, 0, $pos);
|
||||
|
||||
if ($full_ns)
|
||||
{
|
||||
foreach (static::$namespaces as $ns => $path)
|
||||
{
|
||||
$ns = ltrim($ns, '\\');
|
||||
if (stripos($full_ns, $ns) === 0)
|
||||
{
|
||||
$path .= static::class_to_path(
|
||||
substr($class, strlen($ns) + 1),
|
||||
array_key_exists($ns, static::$psr_namespaces)
|
||||
);
|
||||
if (is_file($path))
|
||||
{
|
||||
static::init_class($class, $path);
|
||||
$loaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $loaded)
|
||||
{
|
||||
$path = APPPATH.'classes'.DS.static::class_to_path($class);
|
||||
|
||||
if (is_file($path))
|
||||
{
|
||||
static::init_class($class, $path);
|
||||
$loaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent failed load from keeping other classes from initializing
|
||||
if (static::$auto_initialize == $class)
|
||||
{
|
||||
static::$auto_initialize = null;
|
||||
}
|
||||
|
||||
return $loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the auto initialize state after an autoloader exception.
|
||||
* This method is called by the exception handler, and is considered an
|
||||
* internal method!
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
public static function _reset()
|
||||
{
|
||||
static::$auto_initialize = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a class name and turns it into a path. It follows the PSR-0
|
||||
* standard, except for makes the entire path lower case, unless you
|
||||
* tell it otherwise.
|
||||
*
|
||||
* Note: This does not check if the file exists...just gets the path
|
||||
*
|
||||
* @param string $class Class name
|
||||
* @param bool $psr Whether this is a PSR-0 compliant class
|
||||
* @return string Path for the class
|
||||
*/
|
||||
protected static function class_to_path($class, $psr = false)
|
||||
{
|
||||
$file = '';
|
||||
if ($last_ns_pos = strripos($class, '\\'))
|
||||
{
|
||||
$namespace = substr($class, 0, $last_ns_pos);
|
||||
$class = substr($class, $last_ns_pos + 1);
|
||||
$file = str_replace('\\', DS, $namespace).DS;
|
||||
}
|
||||
$file .= str_replace('_', DS, $class).'.php';
|
||||
|
||||
if ( ! $psr)
|
||||
{
|
||||
$file = strtolower($file);
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a given path by making sure the directory separators are correct.
|
||||
*
|
||||
* @param string $path Path to prepare
|
||||
* @return string Prepped path
|
||||
*/
|
||||
protected static function prep_path($path)
|
||||
{
|
||||
return str_replace(array('/', '\\'), DS, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the given class has a static _init() method. If so then
|
||||
* it calls it.
|
||||
*
|
||||
* @param string $class the class name
|
||||
* @param string $file the file containing the class to include
|
||||
* @throws \Exception
|
||||
* @throws \FuelException
|
||||
*/
|
||||
protected static function init_class($class, $file = null)
|
||||
{
|
||||
// include the file if needed
|
||||
if ($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
||||
|
||||
// if the loaded file contains a class...
|
||||
if (class_exists($class, false))
|
||||
{
|
||||
// call the classes static init if needed
|
||||
if (static::$auto_initialize === $class)
|
||||
{
|
||||
static::$auto_initialize = null;
|
||||
if (method_exists($class, '_init') and is_callable($class.'::_init'))
|
||||
{
|
||||
call_user_func($class.'::_init');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// or an interface...
|
||||
elseif (interface_exists($class, false))
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
// or a trait if you're not on 5.3 anymore...
|
||||
elseif (function_exists('trait_exists') and trait_exists($class, false))
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
// else something went wrong somewhere, barf and exit now
|
||||
elseif ($file)
|
||||
{
|
||||
throw new \Exception('File "'.\Fuel::clean_path($file).'" does not contain class "'.$class.'"');
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \FuelException('Class "'.$class.'" is not defined');
|
||||
}
|
||||
}
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
// Exception thrown when the Cache was found but expired (auto deleted)
|
||||
class CacheExpiredException extends \CacheNotFoundException {}
|
||||
|
||||
class Cache
|
||||
{
|
||||
/**
|
||||
* Loads any default caching settings when available
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
\Config::load('cache', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cache instance.
|
||||
*
|
||||
* @param mixed $identifier The identifier of the cache, can be anything but empty
|
||||
* @param array $config Either an array of settings or the storage driver to be used
|
||||
* @return Cache_Storage_Driver The new cache object
|
||||
*/
|
||||
public static function forge($identifier, $config = array())
|
||||
{
|
||||
// load the default config
|
||||
$defaults = \Config::get('cache', array());
|
||||
|
||||
// $config can be either an array of config settings or the name of the storage driver
|
||||
if ( ! empty($config) and ! is_array($config) and ! is_null($config))
|
||||
{
|
||||
$config = array('driver' => $config);
|
||||
}
|
||||
|
||||
// Overwrite default values with given config
|
||||
$config = array_merge($defaults, (array) $config);
|
||||
|
||||
if (empty($config['driver']))
|
||||
{
|
||||
throw new \FuelException('No cache driver given or no default cache driver set.');
|
||||
}
|
||||
|
||||
$class = '\\Cache_Storage_'.ucfirst($config['driver']);
|
||||
|
||||
// Convert the name to a string when necessary
|
||||
$identifier = call_user_func($class.'::stringify_identifier', $identifier);
|
||||
|
||||
// Return instance of the requested cache object
|
||||
return new $class($identifier, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Front for writing the cache, ensures interchangeability of storage drivers. Actual writing
|
||||
* is being done by the _set() method which needs to be extended.
|
||||
*
|
||||
* @param mixed $identifier The identifier of the cache, can be anything but empty
|
||||
* @param mixed $contents The content to be cached
|
||||
* @param bool $expiration The time in seconds until the cache will expire, =< 0 or null means no expiration
|
||||
* @param array $dependencies Contains the identifiers of caches this one will depend on (not supported by all drivers!)
|
||||
* @return Cache_Storage_Driver The new Cache object
|
||||
*/
|
||||
public static function set($identifier, $contents = null, $expiration = false, $dependencies = array())
|
||||
{
|
||||
$contents = \Fuel::value($contents);
|
||||
|
||||
$cache = static::forge($identifier);
|
||||
return $cache->set($contents, $expiration, $dependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does get() & set() in one call that takes a callback and it's arguments to generate the contents
|
||||
*
|
||||
* @param mixed $identifier The identifier of the cache, can be anything but empty
|
||||
* @param string|array $callback Valid PHP callback
|
||||
* @param array $args Arguments for the above function/method
|
||||
* @param int $expiration Cache expiration in seconds
|
||||
* @param array $dependencies Contains the identifiers of caches this one will depend on (not supported by all drivers!)
|
||||
* @return mixed
|
||||
*/
|
||||
public static function call($identifier, $callback, $args = array(), $expiration = null, $dependencies = array())
|
||||
{
|
||||
$cache = static::forge($identifier);
|
||||
return $cache->call($callback, $args, $expiration, $dependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Front for reading the cache, ensures interchangeability of storage drivers. Actual reading
|
||||
* is being done by the _get() method which needs to be extended.
|
||||
*
|
||||
* @param mixed $identifier The identifier of the cache, can be anything but empty
|
||||
* @param bool $use_expiration
|
||||
* @return mixed
|
||||
*/
|
||||
public static function get($identifier, $use_expiration = true)
|
||||
{
|
||||
$cache = static::forge($identifier);
|
||||
return $cache->get($use_expiration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frontend for deleting item from the cache, interchangeable storage methods. Actual operation
|
||||
* handled by delete() call on storage driver class
|
||||
*
|
||||
* @param mixed $identifier The identifier of the cache, can be anything but empty
|
||||
*/
|
||||
public static function delete($identifier)
|
||||
{
|
||||
$cache = static::forge($identifier);
|
||||
return $cache->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the whole cache for a specific storage driver or just a part of it when $section is set
|
||||
* (might not work with all storage drivers), defaults to the default storage driver
|
||||
*
|
||||
* @param null|string
|
||||
* @param null|string
|
||||
* @return bool
|
||||
*/
|
||||
public static function delete_all($section = null, $driver = null)
|
||||
{
|
||||
$cache = static::forge('__NOT_USED__', $driver);
|
||||
return $cache->delete_all($section);
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
interface Cache_Handler_Driver {
|
||||
/**
|
||||
* Should make the contents readable
|
||||
*
|
||||
* @param mixed
|
||||
* @return mixed
|
||||
*/
|
||||
public function readable($contents);
|
||||
|
||||
/**
|
||||
* Should make the contents writable
|
||||
*
|
||||
* @param mixed
|
||||
* @return mixed
|
||||
*/
|
||||
public function writable($contents);
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Cache_Handler_Json implements \Cache_Handler_Driver
|
||||
{
|
||||
public function readable($contents)
|
||||
{
|
||||
$array = false;
|
||||
if (substr($contents, 0, 1) == 'a')
|
||||
{
|
||||
$contents = substr($contents, 1);
|
||||
$array = true;
|
||||
}
|
||||
|
||||
return json_decode($contents, $array);
|
||||
}
|
||||
|
||||
public function writable($contents)
|
||||
{
|
||||
$array = '';
|
||||
if (is_array($contents))
|
||||
{
|
||||
$array = 'a';
|
||||
}
|
||||
|
||||
return $array.json_encode($contents);
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Cache_Handler_Serialized implements \Cache_Handler_Driver
|
||||
{
|
||||
public function readable($contents)
|
||||
{
|
||||
return unserialize($contents);
|
||||
}
|
||||
|
||||
public function writable($contents)
|
||||
{
|
||||
return serialize($contents);
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Cache_Handler_String implements \Cache_Handler_Driver
|
||||
{
|
||||
public function readable($contents)
|
||||
{
|
||||
return (string) $contents;
|
||||
}
|
||||
|
||||
public function writable($contents)
|
||||
{
|
||||
return (string) $contents;
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
// Exception thrown when the Cache wasn't found
|
||||
class CacheNotFoundException extends \OutOfBoundsException {}
|
@ -1,382 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Cache_Storage_Apc extends \Cache_Storage_Driver
|
||||
{
|
||||
/**
|
||||
* @const string Tag used for opening & closing cache properties
|
||||
*/
|
||||
const PROPS_TAG = 'Fuel_Cache_Properties';
|
||||
|
||||
/**
|
||||
* @var array driver specific configuration
|
||||
*/
|
||||
protected $config = array();
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
public function __construct($identifier, $config)
|
||||
{
|
||||
parent::__construct($identifier, $config);
|
||||
|
||||
$this->config = isset($config['apc']) ? $config['apc'] : array();
|
||||
|
||||
// make sure we have an id
|
||||
$this->config['cache_id'] = $this->_validate_config('cache_id', isset($this->config['cache_id'])
|
||||
? $this->config['cache_id'] : 'fuel');
|
||||
|
||||
// check for an expiration override
|
||||
$this->expiration = $this->_validate_config('expiration', isset($this->config['expiration'])
|
||||
? $this->config['expiration'] : $this->expiration);
|
||||
|
||||
// do we have the PHP APC extension available
|
||||
if ( ! function_exists('apc_store') )
|
||||
{
|
||||
throw new \FuelException('Your PHP installation doesn\'t have APC loaded.');
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Check if other caches or files have been changed since cache creation
|
||||
*
|
||||
* @param array
|
||||
* @return bool
|
||||
*/
|
||||
public function check_dependencies(array $dependencies)
|
||||
{
|
||||
foreach($dependencies as $dep)
|
||||
{
|
||||
// get the section name and identifier
|
||||
$sections = explode('.', $dep);
|
||||
if (count($sections) > 1)
|
||||
{
|
||||
$identifier = array_pop($sections);
|
||||
$sections = '.'.implode('.', $sections);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$identifier = $dep;
|
||||
$sections = '';
|
||||
}
|
||||
|
||||
// get the cache index
|
||||
$index = apc_fetch($this->config['cache_id'].$sections);
|
||||
|
||||
// get the key from the index
|
||||
$key = isset($index[$identifier][0]) ? $index[$identifier] : false;
|
||||
|
||||
// key found and newer?
|
||||
if ($key === false or $key[1] > $this->created)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Cache
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
// get the APC key for the cache identifier
|
||||
$key = $this->_get_key(true);
|
||||
|
||||
// delete the key from the apc store
|
||||
$key and apc_delete($key);
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge all caches
|
||||
*
|
||||
* @param string $section limit purge to subsection
|
||||
* @return bool
|
||||
*/
|
||||
public function delete_all($section)
|
||||
{
|
||||
// determine the section index name
|
||||
$section = $this->config['cache_id'].(empty($section) ? '' : '.'.$section);
|
||||
|
||||
// get the directory index
|
||||
$index = apc_fetch($this->config['cache_id'].'__DIR__');
|
||||
|
||||
if (is_array($index))
|
||||
{
|
||||
// limit the delete if we have a valid section
|
||||
if ( ! empty($section))
|
||||
{
|
||||
$dirs = in_array($section, $index) ? array($section) : array();
|
||||
}
|
||||
else
|
||||
{
|
||||
$dirs = $index;
|
||||
}
|
||||
|
||||
// loop through the indexes, delete all stored keys, then delete the indexes
|
||||
foreach ($dirs as $dir)
|
||||
{
|
||||
$list = apc_fetch($dir);
|
||||
foreach ($list as $item)
|
||||
{
|
||||
apc_delete($item[0]);
|
||||
}
|
||||
apc_delete($dir);
|
||||
}
|
||||
|
||||
// update the directory index
|
||||
$index = array_diff($index, $dirs);
|
||||
apc_store($this->config['cache_id'].'__DIR__', $index);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepend the cache properties
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prep_contents()
|
||||
{
|
||||
$properties = array(
|
||||
'created' => $this->created,
|
||||
'expiration' => $this->expiration,
|
||||
'dependencies' => $this->dependencies,
|
||||
'content_handler' => $this->content_handler,
|
||||
);
|
||||
$properties = '{{'.static::PROPS_TAG.'}}'.json_encode($properties).'{{/'.static::PROPS_TAG.'}}';
|
||||
|
||||
return $properties.$this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the prepended cache properties and save them in class properties
|
||||
*
|
||||
* @param string
|
||||
* @throws UnexpectedValueException
|
||||
*/
|
||||
protected function unprep_contents($payload)
|
||||
{
|
||||
$properties_end = strpos($payload, '{{/'.static::PROPS_TAG.'}}');
|
||||
if ($properties_end === FALSE)
|
||||
{
|
||||
throw new \UnexpectedValueException('Cache has bad formatting');
|
||||
}
|
||||
|
||||
$this->contents = substr($payload, $properties_end + strlen('{{/'.static::PROPS_TAG.'}}'));
|
||||
$props = substr(substr($payload, 0, $properties_end), strlen('{{'.static::PROPS_TAG.'}}'));
|
||||
$props = json_decode($props, true);
|
||||
if ($props === NULL)
|
||||
{
|
||||
throw new \UnexpectedValueException('Cache properties retrieval failed');
|
||||
}
|
||||
|
||||
$this->created = $props['created'];
|
||||
$this->expiration = is_null($props['expiration']) ? null : (int) ($props['expiration'] - time());
|
||||
$this->dependencies = $props['dependencies'];
|
||||
$this->content_handler = $props['content_handler'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a cache, this does the generic pre-processing
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
protected function _set()
|
||||
{
|
||||
// get the apc key for the cache identifier
|
||||
$key = $this->_get_key();
|
||||
|
||||
$payload = $this->prep_contents();
|
||||
|
||||
// adjust the expiration, apc uses a TTL instead of a timestamp
|
||||
$expiration = is_null($this->expiration) ? 0 : (int) ($this->expiration - $this->created);
|
||||
|
||||
// write it to the apc store
|
||||
if (apc_store($key, $payload, $expiration) === false)
|
||||
{
|
||||
throw new \RuntimeException('APC returned failed to write. Check your configuration.');
|
||||
}
|
||||
|
||||
// update the index
|
||||
$this->_update_index($key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a cache, this does the generic post-processing
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
protected function _get()
|
||||
{
|
||||
// get the apc key for the cache identifier
|
||||
$key = $this->_get_key();
|
||||
|
||||
// fetch the cached data from the apc store
|
||||
$payload = apc_fetch($key);
|
||||
|
||||
try
|
||||
{
|
||||
$this->unprep_contents($payload);
|
||||
}
|
||||
catch (\UnexpectedValueException $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate a driver config value
|
||||
*
|
||||
* @param string name of the config variable to validate
|
||||
* @param mixed value
|
||||
* @return mixed
|
||||
*/
|
||||
private function _validate_config($name, $value)
|
||||
{
|
||||
switch ($name)
|
||||
{
|
||||
case 'cache_id':
|
||||
if (empty($value) or ! is_string($value))
|
||||
{
|
||||
$value = 'fuel';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'expiration':
|
||||
if (empty($value) or ! is_numeric($value))
|
||||
{
|
||||
$value = null;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* get's the apc key belonging to the cache identifier
|
||||
*
|
||||
* @param bool if true, remove the key retrieved from the index
|
||||
* @return string
|
||||
*/
|
||||
protected function _get_key($remove = false)
|
||||
{
|
||||
// get the current index information
|
||||
list($identifier, $sections, $index) = $this->_get_index();
|
||||
|
||||
// get the key from the index
|
||||
$key = isset($index[$identifier][0]) ? $index[$identifier][0] : false;
|
||||
|
||||
if ($remove === true)
|
||||
{
|
||||
if ( $key !== false )
|
||||
{
|
||||
unset($index[$identifier]);
|
||||
apc_store($this->config['cache_id'].$sections, $index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// create a new key if needed
|
||||
$key === false and $key = $this->_new_key();
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate a new unique key for the current identifier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _new_key()
|
||||
{
|
||||
$key = '';
|
||||
while (strlen($key) < 32)
|
||||
{
|
||||
$key .= mt_rand(0, mt_getrandmax());
|
||||
}
|
||||
return md5($this->config['cache_id'].'_'.uniqid($key, TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the section index
|
||||
*
|
||||
* @return array containing the identifier, the sections, and the section index
|
||||
*/
|
||||
protected function _get_index()
|
||||
{
|
||||
// get the section name and identifier
|
||||
$sections = explode('.', $this->identifier);
|
||||
if (count($sections) > 1)
|
||||
{
|
||||
$identifier = array_pop($sections);
|
||||
$sections = '.'.implode('.', $sections);
|
||||
}
|
||||
else
|
||||
{
|
||||
$identifier = $this->identifier;
|
||||
$sections = '';
|
||||
}
|
||||
|
||||
// get the cache index and return it
|
||||
return array($identifier, $sections, apc_fetch($this->config['cache_id'].$sections));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the section index
|
||||
*
|
||||
* @param string cache key
|
||||
*/
|
||||
protected function _update_index($key)
|
||||
{
|
||||
// get the current index information
|
||||
list($identifier, $sections, $index) = $this->_get_index();
|
||||
|
||||
// store the key in the index and write the index back
|
||||
$index[$identifier] = array($key, $this->created);
|
||||
apc_store($this->config['cache_id'].$sections, array_merge($index, array($identifier => array($key, $this->created))), 0);
|
||||
|
||||
// get the directory index
|
||||
$index = apc_fetch($this->config['cache_id'].'__DIR__');
|
||||
|
||||
if (is_array($index))
|
||||
{
|
||||
if (!in_array($this->config['cache_id'].$sections, $index))
|
||||
{
|
||||
$index[] = $this->config['cache_id'].$sections;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$index = array($this->config['cache_id'].$sections);
|
||||
}
|
||||
|
||||
// update the directory index
|
||||
apc_store($this->config['cache_id'].'__DIR__', $index, 0);
|
||||
}
|
||||
}
|
@ -1,426 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
abstract class Cache_Storage_Driver
|
||||
{
|
||||
/**
|
||||
* @var array defines which class properties are gettable with get_... in the __call() method
|
||||
*/
|
||||
protected static $_gettable = array('created', 'expiration', 'dependencies', 'identifier');
|
||||
|
||||
/**
|
||||
* @var array defines which class properties are settable with set_... in the __call() method
|
||||
*/
|
||||
protected static $_settable = array('expiration', 'dependencies', 'identifier');
|
||||
|
||||
/**
|
||||
* @var string name of the content handler driver
|
||||
*/
|
||||
protected $content_handler = null;
|
||||
|
||||
/**
|
||||
* @var Cache_Handler_Driver handles and formats the cache's contents
|
||||
*/
|
||||
protected $handler_object = null;
|
||||
|
||||
/**
|
||||
* @var string the cache's name, either string or md5'd serialization of something else
|
||||
*/
|
||||
protected $identifier = null;
|
||||
|
||||
/**
|
||||
* @var int timestamp of creation of the cache
|
||||
*/
|
||||
protected $created = null;
|
||||
|
||||
/**
|
||||
* @var int timestamp when this cache will expire
|
||||
*/
|
||||
protected $expiration = null;
|
||||
|
||||
/**
|
||||
* @var array contains identifiers of other caches this one depends on
|
||||
*/
|
||||
protected $dependencies = array();
|
||||
|
||||
/**
|
||||
* @var mixed the contents of this
|
||||
*/
|
||||
protected $contents = null;
|
||||
|
||||
/**
|
||||
* @var string loaded driver
|
||||
*/
|
||||
protected $driver = null;
|
||||
|
||||
/**
|
||||
* Abstract method that should take care of the storage engine specific reading. Needs to set the object properties:
|
||||
* - created
|
||||
* - expiration
|
||||
* - dependencies
|
||||
* - contents
|
||||
* - content_handler
|
||||
*
|
||||
* @return bool success of the operation
|
||||
*/
|
||||
abstract protected function _get();
|
||||
|
||||
/**
|
||||
* Abstract method that should take care of the storage engine specific writing. Needs to write the object properties:
|
||||
* - created
|
||||
* - expiration
|
||||
* - dependencies
|
||||
* - contents
|
||||
* - content_handler
|
||||
*/
|
||||
abstract protected function _set();
|
||||
|
||||
/**
|
||||
* Should delete this cache instance, should also run reset() afterwards
|
||||
*/
|
||||
abstract public function delete();
|
||||
|
||||
/**
|
||||
* Flushes the whole cache for a specific storage type or just a part of it when $section is set
|
||||
* (might not work with all storage drivers), defaults to the default storage type
|
||||
*
|
||||
* @param string
|
||||
*/
|
||||
abstract public function delete_all($section);
|
||||
|
||||
/**
|
||||
* Should check all dependencies against the creation timestamp.
|
||||
* This is static to make it possible in the future to check dependencies from other storages then the current one,
|
||||
* though I don't have a clue yet how to make that possible.
|
||||
*
|
||||
* @param array $dependencies
|
||||
* @return bool either true or false on any failure
|
||||
*/
|
||||
abstract public function check_dependencies(array $dependencies);
|
||||
|
||||
/**
|
||||
* Default constructor, any extension should either load this first or act similar
|
||||
*
|
||||
* @param string $identifier the identifier for this cache
|
||||
* @param array $config additional config values
|
||||
*/
|
||||
public function __construct($identifier, $config)
|
||||
{
|
||||
$this->identifier = $identifier;
|
||||
|
||||
// fetch options from config and set them
|
||||
$this->expiration = array_key_exists('expiration', $config) ? $config['expiration'] : \Config::get('cache.expiration', null);
|
||||
$this->dependencies = array_key_exists('dependencies', $config) ? $config['dependencies'] : array();
|
||||
$this->content_handler = array_key_exists('content_handler', $config) ? new $config['content_handler']() : null;
|
||||
$this->driver = array_key_exists('driver', $config) ? $config['driver'] : 'file';
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows for default getting and setting
|
||||
*
|
||||
* @param string
|
||||
* @param array
|
||||
* @return void|mixed
|
||||
*/
|
||||
public function __call($method, $args = array())
|
||||
{
|
||||
// Allow getting any properties set in static::$_gettable
|
||||
if (substr($method, 0, 3) == 'get')
|
||||
{
|
||||
$name = substr($method, 4);
|
||||
if (in_array($name, static::$_gettable))
|
||||
{
|
||||
return $this->{$name};
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \BadMethodCallException('This property doesn\'t exist or can\'t be read.');
|
||||
}
|
||||
}
|
||||
// Allow setting any properties set in static::$_settable
|
||||
elseif (substr($method, 0, 3) == 'set')
|
||||
{
|
||||
$name = substr($method, 4);
|
||||
if (in_array($name, static::$_settable))
|
||||
{
|
||||
$this->{$name} = @$args[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \BadMethodCallException('This property doesn\'t exist or can\'t be set.');
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \BadMethodCallException('Illegal method call: ' . $method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the identifier to a string when necessary:
|
||||
* A int is just converted to a string, all others are serialized and then md5'd
|
||||
*
|
||||
* @param mixed $identifier
|
||||
* @return string
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public static function stringify_identifier($identifier)
|
||||
{
|
||||
// Identifier may not be empty, but can be false or 0
|
||||
if ($identifier === '' || $identifier === null)
|
||||
{
|
||||
throw new \FuelException('The identifier cannot be empty, must contain a value of any kind other than null or an empty string.');
|
||||
}
|
||||
|
||||
// In case of string or int just return it as a string
|
||||
if (is_string($identifier) || is_int($identifier))
|
||||
{
|
||||
// cleanup to only allow alphanumeric chars, dashes, dots & underscores
|
||||
if (preg_match('/^([a-z0-9_\.\-]*)$/iuD', $identifier) === 0)
|
||||
{
|
||||
throw new \FuelException('Cache identifier can only contain alphanumeric characters, underscores, dashes & dots.');
|
||||
}
|
||||
|
||||
return (string) $identifier;
|
||||
}
|
||||
// In case of array, bool or object return the md5 of the $identifier's serialization
|
||||
else
|
||||
{
|
||||
return '_hashes.'.md5(serialize($identifier));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all properties except for the identifier, should be run by default when a delete() is triggered
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->contents = null;
|
||||
$this->created = null;
|
||||
$this->expiration = null;
|
||||
$this->dependencies = array();
|
||||
$this->content_handler = null;
|
||||
$this->handler_object = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Front for writing the cache, ensures interchangeability of storage engines. Actual writing
|
||||
* is being done by the _set() method which needs to be extended.
|
||||
*
|
||||
* @param mixed $contents The content to be cached
|
||||
* @param bool $expiration The time in seconds until the cache will expire, =< 0 or null means no expiration
|
||||
* @param array $dependencies array of names on which this cache depends for
|
||||
*/
|
||||
final public function set($contents = null, $expiration = false, $dependencies = array())
|
||||
{
|
||||
$contents = \Fuel::value($contents);
|
||||
// save the current expiration
|
||||
$current_expiration = $this->expiration;
|
||||
|
||||
// Use either the given value or the class property
|
||||
if ( ! is_null($contents))
|
||||
{
|
||||
$this->set_contents($contents);
|
||||
}
|
||||
$this->expiration = ($expiration !== false) ? $expiration : $this->expiration;
|
||||
$this->dependencies = ( ! empty($dependencies)) ? $dependencies : $this->dependencies;
|
||||
|
||||
$this->created = time();
|
||||
|
||||
// Create expiration timestamp when other then null
|
||||
if ( ! is_null($this->expiration))
|
||||
{
|
||||
if ( ! is_numeric($this->expiration))
|
||||
{
|
||||
throw new \InvalidArgumentException('Expiration must be a valid number.');
|
||||
}
|
||||
$this->expiration = $this->created + intval($this->expiration);
|
||||
}
|
||||
|
||||
// Convert dependency identifiers to string when set
|
||||
$this->dependencies = ( ! is_array($this->dependencies)) ? array($this->dependencies) : $this->dependencies;
|
||||
if ( ! empty( $this->dependencies ) )
|
||||
{
|
||||
foreach($this->dependencies as $key => $id)
|
||||
{
|
||||
$this->dependencies[$key] = $this->stringify_identifier($id);
|
||||
}
|
||||
}
|
||||
|
||||
// Turn everything over to the storage specific method
|
||||
$this->_set();
|
||||
|
||||
// restore the expiration
|
||||
$this->expiration = $current_expiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Front for reading the cache, ensures interchangeability of storage engines. Actual reading
|
||||
* is being done by the _get() method which needs to be extended.
|
||||
*
|
||||
* @param bool $use_expiration
|
||||
* @return Cache_Storage_Driver
|
||||
* @throws \CacheExpiredException
|
||||
* @throws \CacheNotFoundException
|
||||
*/
|
||||
final public function get($use_expiration = true)
|
||||
{
|
||||
if ( ! $this->_get())
|
||||
{
|
||||
throw new \CacheNotFoundException('not found');
|
||||
}
|
||||
|
||||
if ($use_expiration)
|
||||
{
|
||||
if ( ! is_null($this->expiration) and $this->expiration < 0)
|
||||
{
|
||||
$this->delete();
|
||||
throw new \CacheExpiredException('expired');
|
||||
}
|
||||
|
||||
// Check dependencies and handle as expired on failure
|
||||
if ( ! $this->check_dependencies($this->dependencies))
|
||||
{
|
||||
$this->delete();
|
||||
throw new \CacheExpiredException('expired');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->get_contents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does get() & set() in one call that takes a callback and it's arguments to generate the contents
|
||||
*
|
||||
* @param string|array $callback Valid PHP callback
|
||||
* @param array $args Arguments for the above function/method
|
||||
* @param int|null $expiration Cache expiration in seconds
|
||||
* @param array $dependencies Contains the identifiers of caches this one will depend on
|
||||
* @return mixed
|
||||
*/
|
||||
final public function call($callback, $args = array(), $expiration = null, $dependencies = array())
|
||||
{
|
||||
try
|
||||
{
|
||||
$this->get();
|
||||
}
|
||||
catch (\CacheNotFoundException $e)
|
||||
{
|
||||
// Create the contents
|
||||
$contents = call_fuel_func_array($callback, $args);
|
||||
|
||||
$this->set($contents, $expiration, $dependencies);
|
||||
}
|
||||
|
||||
return $this->get_contents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the contents with optional handler instead of the default
|
||||
*
|
||||
* @param mixed
|
||||
* @param string
|
||||
* @return Cache_Storage_Driver
|
||||
*/
|
||||
public function set_contents($contents, $handler = NULL)
|
||||
{
|
||||
$this->contents = $contents;
|
||||
$this->set_content_handler($handler);
|
||||
$this->contents = $this->handle_writing($contents);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches contents
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_contents()
|
||||
{
|
||||
return $this->handle_reading($this->contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides a content handler that makes it possible to write non-strings to a file
|
||||
*
|
||||
* @param string
|
||||
* @return Cache_Storage_Driver
|
||||
*/
|
||||
protected function set_content_handler($handler)
|
||||
{
|
||||
$this->handler_object = null;
|
||||
$this->content_handler = (string) $handler;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a specific content handler
|
||||
*
|
||||
* @param string
|
||||
* @return Cache_Handler_Driver
|
||||
*/
|
||||
public function get_content_handler($handler = null)
|
||||
{
|
||||
if ( ! empty($this->handler_object))
|
||||
{
|
||||
return $this->handler_object;
|
||||
}
|
||||
|
||||
// When not yet set, use $handler or detect the preferred handler (string = string, otherwise serialize)
|
||||
if (empty($this->content_handler) && empty($handler))
|
||||
{
|
||||
if ( ! empty($handler))
|
||||
{
|
||||
$this->content_handler = $handler;
|
||||
}
|
||||
if (is_string($this->contents))
|
||||
{
|
||||
$this->content_handler = \Config::get('cache.string_handler', 'string');
|
||||
}
|
||||
else
|
||||
{
|
||||
$type = is_object($this->contents) ? get_class($this->contents) : gettype($this->contents);
|
||||
$this->content_handler = \Config::get('cache.'.$type.'_handler', 'serialized');
|
||||
}
|
||||
}
|
||||
|
||||
$class = '\\Cache_Handler_'.ucfirst($this->content_handler);
|
||||
$this->handler_object = new $class();
|
||||
|
||||
return $this->handler_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the contents the cachable format
|
||||
*
|
||||
* @param $contents
|
||||
* @return string
|
||||
*/
|
||||
protected function handle_writing($contents)
|
||||
{
|
||||
return $this->get_content_handler()->writable($contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the cachable format to the original value
|
||||
*
|
||||
* @param $contents
|
||||
* @return mixed
|
||||
*/
|
||||
protected function handle_reading($contents)
|
||||
{
|
||||
return $this->get_content_handler()->readable($contents);
|
||||
}
|
||||
}
|
@ -1,389 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Cache_Storage_File extends \Cache_Storage_Driver
|
||||
{
|
||||
/**
|
||||
* @const string Tag used for opening & closing cache properties
|
||||
*/
|
||||
const PROPS_TAG = 'Fuel_Cache_Properties';
|
||||
|
||||
/**
|
||||
* @var string File caching basepath
|
||||
*/
|
||||
protected static $path = '';
|
||||
|
||||
/**
|
||||
* @var array driver specific configuration
|
||||
*/
|
||||
protected $config = array();
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
public static function _init()
|
||||
{
|
||||
\Config::load('file', true);
|
||||
|
||||
// make sure the configured chmod values are octal
|
||||
$chmod = \Config::get('file.chmod.folders', 0777);
|
||||
is_string($chmod) and \Config::set('file.chmod.folders', octdec($chmod));
|
||||
$chmod = \Config::get('file.chmod.files', 0666);
|
||||
is_string($chmod) and \Config::set('file.chmod.files', octdec($chmod));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
public function __construct($identifier, $config)
|
||||
{
|
||||
parent::__construct($identifier, $config);
|
||||
|
||||
$this->config = isset($config['file']) ? $config['file'] : array();
|
||||
|
||||
// check for an expiration override
|
||||
$this->expiration = $this->_validate_config('expiration', isset($this->config['expiration'])
|
||||
? $this->config['expiration'] : $this->expiration);
|
||||
|
||||
// determine the file cache path
|
||||
static::$path = !empty($this->config['path'])
|
||||
? $this->config['path'] : \Config::get('cache_dir', APPPATH.'cache'.DS);
|
||||
|
||||
if ( ! is_dir(static::$path) || ! is_writable(static::$path))
|
||||
{
|
||||
throw new \FuelException('Cache directory does not exist or is not writable.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if other caches or files have been changed since cache creation
|
||||
*
|
||||
* @param array
|
||||
* @return bool
|
||||
*/
|
||||
public function check_dependencies(array $dependencies)
|
||||
{
|
||||
foreach($dependencies as $dep)
|
||||
{
|
||||
if (is_file($file = static::$path.str_replace('.', DS, $dep).'.cache'))
|
||||
{
|
||||
$filemtime = filemtime($file);
|
||||
if ($filemtime === false || $filemtime > $this->created)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
elseif (is_file($dep))
|
||||
{
|
||||
$filemtime = filemtime($dep);
|
||||
if ($filemtime === false || $filemtime > $this->created)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Cache
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
if (is_file($file = static::$path.$this->identifier_to_path($this->identifier).'.cache'))
|
||||
{
|
||||
unlink($file);
|
||||
$this->reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge all caches
|
||||
*
|
||||
* @param string $section limit purge to subsection
|
||||
* @return bool
|
||||
*/
|
||||
public function delete_all($section)
|
||||
{
|
||||
// get the cache root path and prep the requested section
|
||||
$path = rtrim(static::$path, '\\/').DS;
|
||||
$section = $section === null ? '' : static::identifier_to_path($section).DS;
|
||||
|
||||
// if the path does not exist, bail out immediately
|
||||
if ( ! is_dir($path.$section))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// get all files in this section
|
||||
$files = \File::read_dir($path.$section, -1, array('\.cache$' => 'file'));
|
||||
|
||||
// closure to recusively delete the files
|
||||
$delete = function($folder, $files) use(&$delete, $path)
|
||||
{
|
||||
$folder = rtrim($folder, '\\/').DS;
|
||||
|
||||
foreach ($files as $dir => $file)
|
||||
{
|
||||
if (is_numeric($dir))
|
||||
{
|
||||
if ( ! $result = \File::delete($folder.$file))
|
||||
{
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! $result = ($delete($folder.$dir, $file)))
|
||||
{
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we're processing a sub directory
|
||||
if ($folder != $path)
|
||||
{
|
||||
// remove the folder if no more files are left
|
||||
$files = \File::read_dir($folder);
|
||||
empty($files) and rmdir($folder);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return $delete($path.$section, $files);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Translates a given identifier to a valid path
|
||||
*
|
||||
* @param string
|
||||
* @return string
|
||||
*/
|
||||
protected function identifier_to_path($identifier)
|
||||
{
|
||||
// replace dots with dashes
|
||||
$identifier = str_replace('.', DS, $identifier);
|
||||
|
||||
return $identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend the cache properties
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prep_contents()
|
||||
{
|
||||
$properties = array(
|
||||
'created' => $this->created,
|
||||
'expiration' => $this->expiration,
|
||||
'dependencies' => $this->dependencies,
|
||||
'content_handler' => $this->content_handler,
|
||||
);
|
||||
$properties = '{{'.self::PROPS_TAG.'}}'.json_encode($properties).'{{/'.self::PROPS_TAG.'}}';
|
||||
|
||||
return $properties.$this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the prepended cache properties and save them in class properties
|
||||
*
|
||||
* @param string
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
protected function unprep_contents($payload)
|
||||
{
|
||||
$properties_end = strpos($payload, '{{/'.self::PROPS_TAG.'}}');
|
||||
if ($properties_end === false)
|
||||
{
|
||||
throw new \UnexpectedValueException('Cache has bad formatting');
|
||||
}
|
||||
|
||||
$this->contents = substr($payload, $properties_end + strlen('{{/'.self::PROPS_TAG.'}}'));
|
||||
$props = substr(substr($payload, 0, $properties_end), strlen('{{'.self::PROPS_TAG.'}}'));
|
||||
$props = json_decode($props, true);
|
||||
if ($props === null)
|
||||
{
|
||||
throw new \UnexpectedValueException('Cache properties retrieval failed');
|
||||
}
|
||||
|
||||
$this->created = $props['created'];
|
||||
$this->expiration = is_null($props['expiration']) ? null : (int) ($props['expiration'] - time());
|
||||
$this->dependencies = $props['dependencies'];
|
||||
$this->content_handler = $props['content_handler'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a cache, this does the generic pre-processing
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
protected function _set()
|
||||
{
|
||||
$payload = $this->prep_contents();
|
||||
$id_path = $this->identifier_to_path($this->identifier);
|
||||
|
||||
// create directory if necessary
|
||||
$subdirs = explode(DS, $id_path);
|
||||
if (count($subdirs) > 1)
|
||||
{
|
||||
array_pop($subdirs);
|
||||
|
||||
// check if specified subdir exists
|
||||
if ( ! @is_dir(static::$path.implode(DS, $subdirs)))
|
||||
{
|
||||
// recursively create the directory. we can't use mkdir permissions or recursive
|
||||
// due to the fact that mkdir is restricted by the current users umask
|
||||
$basepath = rtrim(static::$path, DS);
|
||||
$chmod = \Config::get('file.chmod.folders', 0775);
|
||||
foreach ($subdirs as $dir)
|
||||
{
|
||||
$basepath .= DS.$dir;
|
||||
if ( ! is_dir($basepath))
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( ! mkdir($basepath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
chmod($basepath, $chmod);
|
||||
}
|
||||
catch (\PHPErrorException $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write the cache
|
||||
$file = static::$path.$id_path.'.cache';
|
||||
|
||||
$handle = fopen($file, 'c');
|
||||
|
||||
if ( ! $handle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// wait for a lock
|
||||
while ( ! flock($handle, LOCK_EX));
|
||||
|
||||
// truncate the file
|
||||
ftruncate($handle, 0);
|
||||
|
||||
// write the cache data
|
||||
fwrite($handle, $payload);
|
||||
|
||||
// flush any pending output
|
||||
fflush($handle);
|
||||
|
||||
//release the lock
|
||||
flock($handle, LOCK_UN);
|
||||
|
||||
// close the file
|
||||
fclose($handle);
|
||||
|
||||
// set the correct rights on the file
|
||||
chmod($file, \Config::get('file.chmod.files', 0666));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a cache, this does the generic post-processing
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
protected function _get()
|
||||
{
|
||||
$payload = false;
|
||||
|
||||
$id_path = $this->identifier_to_path( $this->identifier );
|
||||
$file = static::$path.$id_path.'.cache';
|
||||
|
||||
// normalize the file
|
||||
$file = realpath($file);
|
||||
|
||||
// make sure it exists
|
||||
if (is_file($file))
|
||||
{
|
||||
$handle = fopen($file, 'r');
|
||||
if ($handle)
|
||||
{
|
||||
// wait for a lock
|
||||
while( ! flock($handle, LOCK_SH));
|
||||
|
||||
// read the cache data
|
||||
$payload = file_get_contents($file);
|
||||
|
||||
//release the lock
|
||||
flock($handle, LOCK_UN);
|
||||
|
||||
// close the file
|
||||
fclose($handle);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$this->unprep_contents($payload);
|
||||
}
|
||||
catch (\UnexpectedValueException $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate a driver config value
|
||||
*
|
||||
* @param string $name name of the config variable to validate
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _validate_config($name, $value)
|
||||
{
|
||||
switch ($name)
|
||||
{
|
||||
case 'cache_id':
|
||||
if (empty($value) or ! is_string($value))
|
||||
{
|
||||
$value = 'fuel';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'expiration':
|
||||
if (empty($value) or ! is_numeric($value))
|
||||
{
|
||||
$value = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
@ -1,454 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Cache_Storage_Memcached extends \Cache_Storage_Driver
|
||||
{
|
||||
/**
|
||||
* @const string Tag used for opening & closing cache properties
|
||||
*/
|
||||
const PROPS_TAG = 'Fuel_Cache_Properties';
|
||||
|
||||
/**
|
||||
* @var array driver specific configuration
|
||||
*/
|
||||
protected $config = array();
|
||||
|
||||
/*
|
||||
* @var Memcached storage for the memcached object
|
||||
*/
|
||||
protected static $memcached = false;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
public function __construct($identifier, $config)
|
||||
{
|
||||
parent::__construct($identifier, $config);
|
||||
|
||||
$this->config = isset($config['memcached']) ? $config['memcached'] : array();
|
||||
|
||||
// make sure we have a memcache id
|
||||
$this->config['cache_id'] = $this->_validate_config('cache_id', isset($this->config['cache_id'])
|
||||
? $this->config['cache_id'] : 'fuel');
|
||||
|
||||
// check for an expiration override
|
||||
$this->expiration = $this->_validate_config('expiration', isset($this->config['expiration'])
|
||||
? $this->config['expiration'] : $this->expiration);
|
||||
|
||||
if (static::$memcached === false)
|
||||
{
|
||||
// make sure we have memcached servers configured
|
||||
$this->config['servers'] = $this->_validate_config('servers', $this->config['servers']);
|
||||
|
||||
// do we have the PHP memcached extension available
|
||||
if ( ! class_exists('Memcached') )
|
||||
{
|
||||
throw new \FuelException('Memcached cache are configured, but your PHP installation doesn\'t have the Memcached extension loaded.');
|
||||
}
|
||||
|
||||
// instantiate the memcached object
|
||||
static::$memcached = new \Memcached();
|
||||
|
||||
// add the configured servers
|
||||
static::$memcached->addServers($this->config['servers']);
|
||||
|
||||
// check if we can connect to all the server(s)
|
||||
$added = static::$memcached->getStats();
|
||||
foreach ($this->config['servers'] as $server)
|
||||
{
|
||||
$server = $server['host'].':'.$server['port'];
|
||||
if ( ! isset($added[$server]) or $added[$server]['pid'] == -1)
|
||||
{
|
||||
throw new \FuelException('Memcached cache is configured, but there is no connection possible. Check your configuration.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Check if other caches or files have been changed since cache creation
|
||||
*
|
||||
* @param array
|
||||
* @return bool
|
||||
*/
|
||||
public function check_dependencies(array $dependencies)
|
||||
{
|
||||
foreach($dependencies as $dep)
|
||||
{
|
||||
// get the section name and identifier
|
||||
$sections = explode('.', $dep);
|
||||
if (count($sections) > 1)
|
||||
{
|
||||
$identifier = array_pop($sections);
|
||||
$sections = '.'.implode('.', $sections);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$identifier = $dep;
|
||||
$sections = '';
|
||||
}
|
||||
|
||||
// get the cache index
|
||||
$index = static::$memcached->get($this->config['cache_id'].$sections);
|
||||
|
||||
// get the key from the index
|
||||
$key = isset($index[$identifier][0]) ? $index[$identifier] : false;
|
||||
|
||||
// key found and newer?
|
||||
if ($key === false or $key[1] > $this->created)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Cache
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
// get the memcached key for the cache identifier
|
||||
$key = $this->_get_key(true);
|
||||
|
||||
// delete the key from the memcached server
|
||||
if ($key and static::$memcached->delete($key) === false)
|
||||
{
|
||||
if (static::$memcached->getResultCode() !== \Memcached::RES_NOTFOUND)
|
||||
{
|
||||
throw new \FuelException('Memcached returned error code "'.static::$memcached->getResultCode().'" on delete. Check your configuration.');
|
||||
}
|
||||
}
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge all caches
|
||||
*
|
||||
* @param string $section limit purge to subsection
|
||||
* @return bool
|
||||
*/
|
||||
public function delete_all($section)
|
||||
{
|
||||
// determine the section index name
|
||||
$section = $this->config['cache_id'].(empty($section) ? '' : '.'.$section);
|
||||
|
||||
// get the directory index
|
||||
$index = static::$memcached->get($this->config['cache_id'].'__DIR__');
|
||||
|
||||
if (is_array($index))
|
||||
{
|
||||
// limit the delete if we have a valid section
|
||||
if ( ! empty($section))
|
||||
{
|
||||
$dirs = in_array($section, $index) ? array($section) : array();
|
||||
}
|
||||
else
|
||||
{
|
||||
$dirs = $index;
|
||||
}
|
||||
|
||||
// loop through the indexes, delete all stored keys, then delete the indexes
|
||||
foreach ($dirs as $dir)
|
||||
{
|
||||
$list = static::$memcached->get($dir);
|
||||
foreach ($list as $item)
|
||||
{
|
||||
static::$memcached->delete($item[0]);
|
||||
}
|
||||
static::$memcached->delete($dir);
|
||||
}
|
||||
|
||||
// update the directory index
|
||||
$index = array_diff($index, $dirs);
|
||||
static::$memcached->set($this->config['cache_id'].'__DIR__', $index);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepend the cache properties
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prep_contents()
|
||||
{
|
||||
$properties = array(
|
||||
'created' => $this->created,
|
||||
'expiration' => $this->expiration,
|
||||
'dependencies' => $this->dependencies,
|
||||
'content_handler' => $this->content_handler,
|
||||
);
|
||||
$properties = '{{'.static::PROPS_TAG.'}}'.json_encode($properties).'{{/'.static::PROPS_TAG.'}}';
|
||||
|
||||
return $properties.$this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the prepended cache properties and save them in class properties
|
||||
*
|
||||
* @param string
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
protected function unprep_contents($payload)
|
||||
{
|
||||
$properties_end = strpos($payload, '{{/'.static::PROPS_TAG.'}}');
|
||||
if ($properties_end === FALSE)
|
||||
{
|
||||
throw new \UnexpectedValueException('Cache has bad formatting');
|
||||
}
|
||||
|
||||
$this->contents = substr($payload, $properties_end + strlen('{{/'.static::PROPS_TAG.'}}'));
|
||||
$props = substr(substr($payload, 0, $properties_end), strlen('{{'.static::PROPS_TAG.'}}'));
|
||||
$props = json_decode($props, true);
|
||||
if ($props === NULL)
|
||||
{
|
||||
throw new \UnexpectedValueException('Cache properties retrieval failed');
|
||||
}
|
||||
|
||||
$this->created = $props['created'];
|
||||
$this->expiration = is_null($props['expiration']) ? null : (int) ($props['expiration'] - time());
|
||||
$this->dependencies = $props['dependencies'];
|
||||
$this->content_handler = $props['content_handler'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a cache, this does the generic pre-processing
|
||||
*
|
||||
* @return bool success
|
||||
* @throws \FuelException
|
||||
*/
|
||||
protected function _set()
|
||||
{
|
||||
// get the memcached key for the cache identifier
|
||||
$key = $this->_get_key();
|
||||
|
||||
$payload = $this->prep_contents();
|
||||
|
||||
// calculate relative expiration time (eg. 60s)
|
||||
$expiration = !is_null($this->expiration) ? $this->expiration - time() : 0;
|
||||
|
||||
// if expiration value is less than 30 days, use relative value, otherwise use unix timestamp:
|
||||
$expiration = $expiration <= 2592000 ? (int) $expiration : (int) $this->expiration;
|
||||
|
||||
// write it to the memcached server
|
||||
if (static::$memcached->set($key, $payload, $expiration) === false)
|
||||
{
|
||||
throw new \FuelException('Memcached returned error code "'.static::$memcached->getResultCode().'" on write. Check your configuration.');
|
||||
}
|
||||
|
||||
// update the index
|
||||
$this->_update_index($key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a cache, this does the generic post-processing
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
protected function _get()
|
||||
{
|
||||
// get the memcached key for the cache identifier
|
||||
$key = $this->_get_key();
|
||||
|
||||
// fetch the cached data from the Memcached server
|
||||
$payload = static::$memcached->get($key);
|
||||
|
||||
try
|
||||
{
|
||||
$this->unprep_contents($payload);
|
||||
}
|
||||
catch (\UnexpectedValueException $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate a driver config value
|
||||
*
|
||||
* @param string $name name of the config variable to validate
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
* @throws \FuelException
|
||||
*/
|
||||
protected function _validate_config($name, $value)
|
||||
{
|
||||
switch ($name)
|
||||
{
|
||||
case 'cache_id':
|
||||
if (empty($value) or ! is_string($value))
|
||||
{
|
||||
$value = 'fuel';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'expiration':
|
||||
if (empty($value) or ! is_numeric($value))
|
||||
{
|
||||
$value = null;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'servers':
|
||||
// do we have a servers config
|
||||
if ( empty($value) OR ! is_array($value))
|
||||
{
|
||||
$value = array('default' => array('host' => '127.0.0.1', 'port' => '11211'));
|
||||
}
|
||||
|
||||
// validate the servers
|
||||
foreach ($value as $key => $server)
|
||||
{
|
||||
// do we have a host?
|
||||
if ( ! isset($server['host']) OR ! is_string($server['host']))
|
||||
{
|
||||
throw new \FuelException('Invalid Memcached server definition in the cache configuration.');
|
||||
}
|
||||
// do we have a port number?
|
||||
if ( ! isset($server['port']) OR ! is_numeric($server['port']) OR $server['port'] < 1025 OR $server['port'] > 65535)
|
||||
{
|
||||
throw new \FuelException('Invalid Memcached server definition in the cache configuration.');
|
||||
}
|
||||
// do we have a relative server weight?
|
||||
if ( ! isset($server['weight']) OR ! is_numeric($server['weight']) OR $server['weight'] < 0)
|
||||
{
|
||||
// set a default
|
||||
$value[$key]['weight'] = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's the memcached key belonging to the cache identifier
|
||||
*
|
||||
* @param bool $remove if true, remove the key retrieved from the index
|
||||
* @return string
|
||||
*/
|
||||
protected function _get_key($remove = false)
|
||||
{
|
||||
// get the current index information
|
||||
list($identifier, $sections, $index) = $this->_get_index();
|
||||
|
||||
// get the key from the index
|
||||
$key = isset($index[$identifier][0]) ? $index[$identifier][0] : false;
|
||||
|
||||
if ($remove === true)
|
||||
{
|
||||
if ( $key !== false )
|
||||
{
|
||||
unset($index[$identifier]);
|
||||
static::$memcached->set($this->config['cache_id'].$sections, $index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// create a new key if needed
|
||||
$key === false and $key = $this->_new_key();
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new unique key for the current identifier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _new_key()
|
||||
{
|
||||
$key = '';
|
||||
while (strlen($key) < 32)
|
||||
{
|
||||
$key .= mt_rand(0, mt_getrandmax());
|
||||
}
|
||||
return md5($this->config['cache_id'].'_'.uniqid($key, TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the section index
|
||||
*
|
||||
* @return array containing the identifier, the sections, and the section index
|
||||
*/
|
||||
protected function _get_index()
|
||||
{
|
||||
// get the section name and identifier
|
||||
$sections = explode('.', $this->identifier);
|
||||
if (count($sections) > 1)
|
||||
{
|
||||
$identifier = array_pop($sections);
|
||||
$sections = '.'.implode('.', $sections);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$identifier = $this->identifier;
|
||||
$sections = '';
|
||||
}
|
||||
|
||||
// get the cache index and return it
|
||||
return array($identifier, $sections, static::$memcached->get($this->config['cache_id'].$sections));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the section index
|
||||
*
|
||||
* @param string cache key
|
||||
*/
|
||||
protected function _update_index($key)
|
||||
{
|
||||
// get the current index information
|
||||
list($identifier, $sections, $index) = $this->_get_index();
|
||||
|
||||
// create a new index and store the key
|
||||
is_array($index) or $index = array();
|
||||
|
||||
// store the key in the index and write the index back
|
||||
$index[$identifier] = array($key, $this->created);
|
||||
static::$memcached->set($this->config['cache_id'].$sections, $index, 0);
|
||||
|
||||
// get the directory index
|
||||
$index = static::$memcached->get($this->config['cache_id'].'__DIR__');
|
||||
|
||||
if (is_array($index))
|
||||
{
|
||||
if (!in_array($this->config['cache_id'].$sections, $index))
|
||||
{
|
||||
$index[] = $this->config['cache_id'].$sections;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$index = array($this->config['cache_id'].$sections);
|
||||
}
|
||||
|
||||
// update the directory index
|
||||
static::$memcached->set($this->config['cache_id'].'__DIR__', $index, 0);
|
||||
}
|
||||
}
|
@ -1,509 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Cache_Storage_Redis extends \Cache_Storage_Driver
|
||||
{
|
||||
/**
|
||||
* @const string Tag used for opening & closing cache properties
|
||||
*/
|
||||
const PROPS_TAG = 'Fuel_Cache_Properties';
|
||||
|
||||
/**
|
||||
* @var array driver specific configuration
|
||||
*/
|
||||
protected $config = array();
|
||||
|
||||
/*
|
||||
* @var Redis storage for the redis object
|
||||
*/
|
||||
protected static $redis = false;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
public function __construct($identifier, $config)
|
||||
{
|
||||
parent::__construct($identifier, $config);
|
||||
|
||||
$this->config = isset($config['redis']) ? $config['redis'] : array();
|
||||
|
||||
// make sure we have a redis id
|
||||
$this->config['cache_id'] = $this->_validate_config('cache_id', isset($this->config['cache_id'])
|
||||
? $this->config['cache_id'] : 'fuel');
|
||||
|
||||
// check for an expiration override
|
||||
$this->expiration = $this->_validate_config('expiration', isset($this->config['expiration'])
|
||||
? $this->config['expiration'] : $this->expiration);
|
||||
|
||||
// make sure we have a redis database configured
|
||||
$this->config['database'] = $this->_validate_config('database', isset($this->config['database'])
|
||||
? $this->config['database'] : 'default');
|
||||
|
||||
if (static::$redis === false)
|
||||
{
|
||||
// get the redis database instance
|
||||
try
|
||||
{
|
||||
static::$redis = \Redis_Db::instance($this->config['database']);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
throw new \FuelException('Can not connect to the Redis engine. The error message says "'.$e->getMessage().'".');
|
||||
}
|
||||
|
||||
// get the redis version
|
||||
preg_match('/redis_version:(.*?)\n/', static::$redis->info(), $info);
|
||||
if (version_compare(trim($info[1]), '1.2') < 0)
|
||||
{
|
||||
throw new \FuelException('Version 1.2 or higher of the Redis NoSQL engine is required to use the redis cache driver.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Check if other caches or files have been changed since cache creation
|
||||
*
|
||||
* @param array
|
||||
* @return bool
|
||||
*/
|
||||
public function check_dependencies(array $dependencies)
|
||||
{
|
||||
foreach($dependencies as $dep)
|
||||
{
|
||||
// get the section name and identifier
|
||||
$sections = explode('.', $dep);
|
||||
if (count($sections) > 1)
|
||||
{
|
||||
$identifier = array_pop($sections);
|
||||
$sections = '.'.implode('.', $sections);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$identifier = $dep;
|
||||
$sections = '';
|
||||
}
|
||||
|
||||
// get the cache index
|
||||
$index = static::$redis->get($this->config['cache_id'].':index:'.$sections);
|
||||
is_null($index) or $index = $this->_unserialize($index);
|
||||
|
||||
// get the key from the index
|
||||
$key = isset($index[$identifier][0]) ? $index[$identifier] : false;
|
||||
|
||||
// key found and newer?
|
||||
if ($key === false or $key[1] > $this->created)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Cache
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
// get the key for the cache identifier
|
||||
$key = $this->_get_key(true);
|
||||
|
||||
// delete the key from the redis server
|
||||
if ($key and static::$redis->del($key) === false)
|
||||
{
|
||||
// do something here?
|
||||
}
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge all caches
|
||||
*
|
||||
* @param string $section limit purge to subsection
|
||||
* @return bool
|
||||
*/
|
||||
public function delete_all($section)
|
||||
{
|
||||
// determine the section index name
|
||||
$section = empty($section) ? '' : '.'.$section;
|
||||
|
||||
// get the directory index
|
||||
$index = static::$redis->get($this->config['cache_id'].':dir:');
|
||||
is_null($index) or $index = $this->_unserialize($index);
|
||||
|
||||
if (is_array($index))
|
||||
{
|
||||
if (!empty($section))
|
||||
{
|
||||
// limit the delete if we have a valid section
|
||||
$dirs = array();
|
||||
foreach ($index as $entry)
|
||||
{
|
||||
if ($entry == $section or strpos($entry, $section.'.') === 0)
|
||||
{
|
||||
$dirs[] = $entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// else delete the entire contents of the cache
|
||||
$dirs = $index;
|
||||
}
|
||||
|
||||
// loop through the selected indexes
|
||||
foreach ($dirs as $dir)
|
||||
{
|
||||
// get the stored cache entries for this index
|
||||
$list = static::$redis->get($this->config['cache_id'].':index:'.$dir);
|
||||
if (is_null($list))
|
||||
{
|
||||
$list = array();
|
||||
}
|
||||
else
|
||||
{
|
||||
$list = $this->_unserialize($list);
|
||||
}
|
||||
|
||||
// delete all stored keys
|
||||
foreach($list as $item)
|
||||
{
|
||||
static::$redis->del($item[0]);
|
||||
}
|
||||
|
||||
// and delete the index itself
|
||||
static::$redis->del($this->config['cache_id'].':index:'.$dir);
|
||||
}
|
||||
|
||||
// update the directory index
|
||||
static::$redis->set($this->config['cache_id'].':dir:', $this->_serialize(array_diff($index, $dirs)));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Translates a given identifier to a valid redis key
|
||||
*
|
||||
* @param string
|
||||
* @return string
|
||||
*/
|
||||
protected function identifier_to_key( $identifier )
|
||||
{
|
||||
return $this->config['cache_id'].':'.$identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepend the cache properties
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prep_contents()
|
||||
{
|
||||
$properties = array(
|
||||
'created' => $this->created,
|
||||
'expiration' => $this->expiration,
|
||||
'dependencies' => $this->dependencies,
|
||||
'content_handler' => $this->content_handler,
|
||||
);
|
||||
$properties = '{{'.static::PROPS_TAG.'}}'.json_encode($properties).'{{/'.static::PROPS_TAG.'}}';
|
||||
|
||||
return $properties.$this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the prepended cache properties and save them in class properties
|
||||
*
|
||||
* @param string
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
protected function unprep_contents($payload)
|
||||
{
|
||||
$properties_end = strpos($payload, '{{/'.static::PROPS_TAG.'}}');
|
||||
if ($properties_end === FALSE)
|
||||
{
|
||||
throw new \UnexpectedValueException('Cache has bad formatting');
|
||||
}
|
||||
|
||||
$this->contents = substr($payload, $properties_end + strlen('{{/'.static::PROPS_TAG.'}}'));
|
||||
$props = substr(substr($payload, 0, $properties_end), strlen('{{'.static::PROPS_TAG.'}}'));
|
||||
$props = json_decode($props, true);
|
||||
if ($props === NULL)
|
||||
{
|
||||
throw new \UnexpectedValueException('Cache properties retrieval failed');
|
||||
}
|
||||
|
||||
$this->created = $props['created'];
|
||||
$this->expiration = is_null($props['expiration']) ? null : (int) ($props['expiration'] - time());
|
||||
$this->dependencies = $props['dependencies'];
|
||||
$this->content_handler = $props['content_handler'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a cache, this does the generic pre-processing
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
protected function _set()
|
||||
{
|
||||
// get the key for the cache identifier
|
||||
$key = $this->_get_key();
|
||||
|
||||
// write the cache
|
||||
static::$redis->set($key, $this->prep_contents());
|
||||
if ( ! empty($this->expiration))
|
||||
{
|
||||
static::$redis->expireat($key, $this->expiration);
|
||||
}
|
||||
|
||||
// update the index
|
||||
$this->_update_index($key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a cache, this does the generic post-processing
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
protected function _get()
|
||||
{
|
||||
// get the key for the cache identifier
|
||||
$key = $this->_get_key();
|
||||
|
||||
// fetch the cache data from the redis server
|
||||
$payload = static::$redis->get($key);
|
||||
try
|
||||
{
|
||||
$this->unprep_contents($payload);
|
||||
}
|
||||
catch (\UnexpectedValueException $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate a driver config value
|
||||
*
|
||||
* @param string $name name of the config variable to validate
|
||||
* @param mixed $value value
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _validate_config($name, $value)
|
||||
{
|
||||
switch ($name)
|
||||
{
|
||||
case 'database':
|
||||
// do we have a database config
|
||||
if (empty($value) or ! is_string($value))
|
||||
{
|
||||
$value = 'default';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'cache_id':
|
||||
if (empty($value) or ! is_string($value))
|
||||
{
|
||||
$value = 'fuel';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'expiration':
|
||||
if (empty($value) or ! is_numeric($value))
|
||||
{
|
||||
$value = null;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* get's the redis key belonging to the cache identifier
|
||||
*
|
||||
* @param bool $remove if true, remove the key retrieved from the index
|
||||
* @return string
|
||||
*/
|
||||
protected function _get_key($remove = false)
|
||||
{
|
||||
// get the current index information
|
||||
list($identifier, $sections, $index) = $this->_get_index();
|
||||
$index = $index === null ? array() : $index = $this->_unserialize($index);
|
||||
|
||||
// get the key from the index
|
||||
$key = isset($index[$identifier][0]) ? $index[$identifier][0] : false;
|
||||
|
||||
if ($remove === true)
|
||||
{
|
||||
if ( $key !== false )
|
||||
{
|
||||
unset($index[$identifier]);
|
||||
static::$redis->set($this->config['cache_id'].':index:'.$sections, $this->_serialize($index));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// create a new key if needed
|
||||
$key === false and $key = $this->_new_key();
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate a new unique key for the current identifier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _new_key()
|
||||
{
|
||||
$key = '';
|
||||
while (strlen($key) < 32)
|
||||
{
|
||||
$key .= mt_rand(0, mt_getrandmax());
|
||||
}
|
||||
return md5($this->config['cache_id'].'_'.uniqid($key, TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the section index
|
||||
*
|
||||
* @return array containing the identifier, the sections, and the section index
|
||||
*/
|
||||
protected function _get_index()
|
||||
{
|
||||
// get the section name and identifier
|
||||
$sections = explode('.', $this->identifier);
|
||||
if (count($sections) > 1)
|
||||
{
|
||||
$identifier = array_pop($sections);
|
||||
$sections = '.'.implode('.', $sections);
|
||||
}
|
||||
else
|
||||
{
|
||||
$identifier = $this->identifier;
|
||||
$sections = '';
|
||||
}
|
||||
|
||||
// get the cache index and return it
|
||||
return array($identifier, $sections, static::$redis->get($this->config['cache_id'].':index:'.$sections));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the section index
|
||||
*
|
||||
* @param string cache key
|
||||
*/
|
||||
protected function _update_index($key)
|
||||
{
|
||||
// get the current index information
|
||||
list($identifier, $sections, $index) = $this->_get_index();
|
||||
$index = $index === null ? array() : $index = $this->_unserialize($index);
|
||||
|
||||
// store the key in the index and write the index back
|
||||
$index[$identifier] = array($key, $this->created);
|
||||
|
||||
static::$redis->set($this->config['cache_id'].':index:'.$sections, $this->_serialize($index));
|
||||
|
||||
// get the directory index
|
||||
$index = static::$redis->get($this->config['cache_id'].':dir:');
|
||||
$index = $index === null ? array() : $index = $this->_unserialize($index);
|
||||
|
||||
if (is_array($index))
|
||||
{
|
||||
if ( ! in_array($sections, $index))
|
||||
{
|
||||
$index[] = $sections;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$index = array($sections);
|
||||
}
|
||||
|
||||
// update the directory index
|
||||
static::$redis->set($this->config['cache_id'].':dir:', $this->_serialize($index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize an array
|
||||
*
|
||||
* This function first converts any slashes found in the array to a temporary
|
||||
* marker, so when it gets unserialized the slashes will be preserved
|
||||
*
|
||||
* @param array
|
||||
* @return string
|
||||
*/
|
||||
protected function _serialize($data)
|
||||
{
|
||||
if (is_array($data))
|
||||
{
|
||||
foreach ($data as $key => $val)
|
||||
{
|
||||
if (is_string($val))
|
||||
{
|
||||
$data[$key] = str_replace('\\', '{{slash}}', $val);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_string($data))
|
||||
{
|
||||
$data = str_replace('\\', '{{slash}}', $data);
|
||||
}
|
||||
}
|
||||
|
||||
return serialize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserialize
|
||||
*
|
||||
* This function unserializes a data string, then converts any
|
||||
* temporary slash markers back to actual slashes
|
||||
*
|
||||
* @param array
|
||||
* @return string
|
||||
*/
|
||||
protected function _unserialize($data)
|
||||
{
|
||||
$data = @unserialize(stripslashes($data));
|
||||
|
||||
if (is_array($data))
|
||||
{
|
||||
foreach ($data as $key => $val)
|
||||
{
|
||||
if (is_string($val))
|
||||
{
|
||||
$data[$key] = str_replace('{{slash}}', '\\', $val);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
return (is_string($data)) ? str_replace('{{slash}}', '\\', $data) : $data;
|
||||
}
|
||||
}
|
@ -1,375 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Cache_Storage_Xcache extends \Cache_Storage_Driver
|
||||
{
|
||||
/**
|
||||
* @const string Tag used for opening & closing cache properties
|
||||
*/
|
||||
const PROPS_TAG = 'Fuel_Cache_Properties';
|
||||
|
||||
/**
|
||||
* @var array driver specific configuration
|
||||
*/
|
||||
protected $config = array();
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
public function __construct($identifier, $config)
|
||||
{
|
||||
parent::__construct($identifier, $config);
|
||||
|
||||
$this->config = isset($config['xcache']) ? $config['xcache'] : array();
|
||||
|
||||
// make sure we have an id
|
||||
$this->config['cache_id'] = $this->_validate_config('cache_id', isset($this->config['cache_id'])
|
||||
? $this->config['cache_id'] : 'fuel');
|
||||
|
||||
// check for an expiration override
|
||||
$this->expiration = $this->_validate_config('expiration', isset($this->config['expiration'])
|
||||
? $this->config['expiration'] : $this->expiration);
|
||||
|
||||
// do we have the PHP XCache extension available
|
||||
if ( ! function_exists('xcache_set') )
|
||||
{
|
||||
throw new \FuelException('Your PHP installation doesn\'t have XCache loaded.');
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Check if other caches or files have been changed since cache creation
|
||||
*
|
||||
* @param array
|
||||
* @return bool
|
||||
*/
|
||||
public function check_dependencies(array $dependencies)
|
||||
{
|
||||
foreach($dependencies as $dep)
|
||||
{
|
||||
// get the section name and identifier
|
||||
$sections = explode('.', $dep);
|
||||
if (count($sections) > 1)
|
||||
{
|
||||
$identifier = array_pop($sections);
|
||||
$sections = '.'.implode('.', $sections);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$identifier = $dep;
|
||||
$sections = '';
|
||||
}
|
||||
|
||||
// get the cache index
|
||||
$index = xcache_get($this->config['cache_id'].$sections);
|
||||
|
||||
// get the key from the index
|
||||
$key = isset($index[$identifier][0]) ? $index[$identifier] : false;
|
||||
|
||||
// key found and newer?
|
||||
if ($key === false or $key[1] > $this->created)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Cache
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
// get the XCache key for the cache identifier
|
||||
$key = $this->_get_key(true);
|
||||
|
||||
// delete the key from the xcache store
|
||||
$key and xcache_unset($key);
|
||||
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge all caches
|
||||
*
|
||||
* @param string $section limit purge to subsection
|
||||
* @return bool
|
||||
*/
|
||||
public function delete_all($section)
|
||||
{
|
||||
// determine the section index name
|
||||
$section = $this->config['cache_id'].(empty($section) ? '' : '.'.$section);
|
||||
|
||||
// get the directory index
|
||||
$index = xcache_get($this->config['cache_id'].'__DIR__');
|
||||
|
||||
if (is_array($index))
|
||||
{
|
||||
$dirs = array();
|
||||
foreach ($index as $dir)
|
||||
{
|
||||
if (strpos($dir, $section) === 0)
|
||||
{
|
||||
$dirs[] = $dir;
|
||||
$list = xcache_get($dir);
|
||||
foreach ($list as $item)
|
||||
{
|
||||
xcache_unset($item[0]);
|
||||
}
|
||||
xcache_unset($dir);
|
||||
}
|
||||
}
|
||||
|
||||
// update the directory index
|
||||
$dirs and xcache_set($this->config['cache_id'].'__DIR__', array_diff($index, $dirs));
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Prepend the cache properties
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function prep_contents()
|
||||
{
|
||||
$properties = array(
|
||||
'created' => $this->created,
|
||||
'expiration' => $this->expiration,
|
||||
'dependencies' => $this->dependencies,
|
||||
'content_handler' => $this->content_handler,
|
||||
);
|
||||
$properties = '{{'.static::PROPS_TAG.'}}'.json_encode($properties).'{{/'.static::PROPS_TAG.'}}';
|
||||
|
||||
return $properties.$this->contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the prepended cache properties and save them in class properties
|
||||
*
|
||||
* @param string $payload
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
protected function unprep_contents($payload)
|
||||
{
|
||||
$properties_end = strpos($payload, '{{/'.static::PROPS_TAG.'}}');
|
||||
if ($properties_end === FALSE)
|
||||
{
|
||||
throw new \UnexpectedValueException('Cache has bad formatting');
|
||||
}
|
||||
|
||||
$this->contents = substr($payload, $properties_end + strlen('{{/'.static::PROPS_TAG.'}}'));
|
||||
$props = substr(substr($payload, 0, $properties_end), strlen('{{'.static::PROPS_TAG.'}}'));
|
||||
$props = json_decode($props, true);
|
||||
if ($props === NULL)
|
||||
{
|
||||
throw new \UnexpectedValueException('Cache properties retrieval failed');
|
||||
}
|
||||
|
||||
$this->created = $props['created'];
|
||||
$this->expiration = is_null($props['expiration']) ? null : (int) ($props['expiration'] - time());
|
||||
$this->dependencies = $props['dependencies'];
|
||||
$this->content_handler = $props['content_handler'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a cache, this does the generic pre-processing
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
protected function _set()
|
||||
{
|
||||
// get the xcache key for the cache identifier
|
||||
$key = $this->_get_key();
|
||||
|
||||
$payload = $this->prep_contents();
|
||||
|
||||
// adjust the expiration, xcache uses a TTL instead of a timestamp
|
||||
$expiration = is_null($this->expiration) ? 0 : (int) ($this->expiration - $this->created);
|
||||
|
||||
// write it to the xcache store
|
||||
if (xcache_set($key, $payload, $expiration) === false)
|
||||
{
|
||||
throw new \RuntimeException('Xcache returned failed to write. Check your configuration.');
|
||||
}
|
||||
|
||||
// update the index
|
||||
$this->_update_index($key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a cache, this does the generic post-processing
|
||||
*
|
||||
* @return bool success
|
||||
*/
|
||||
protected function _get()
|
||||
{
|
||||
// get the xcache key for the cache identifier
|
||||
$key = $this->_get_key();
|
||||
|
||||
// fetch the cached data from the xcache store
|
||||
$payload = xcache_get($key);
|
||||
|
||||
try
|
||||
{
|
||||
$this->unprep_contents($payload);
|
||||
}
|
||||
catch (\UnexpectedValueException $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* validate a driver config value
|
||||
*
|
||||
* @param string $name name of the config variable to validate
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
private function _validate_config($name, $value)
|
||||
{
|
||||
switch ($name)
|
||||
{
|
||||
case 'cache_id':
|
||||
if (empty($value) or ! is_string($value))
|
||||
{
|
||||
$value = 'fuel';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'expiration':
|
||||
if (empty($value) or ! is_numeric($value))
|
||||
{
|
||||
$value = null;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* get's the xcache key belonging to the cache identifier
|
||||
*
|
||||
* @param bool $remove if true, remove the key retrieved from the index
|
||||
* @return string
|
||||
*/
|
||||
protected function _get_key($remove = false)
|
||||
{
|
||||
// get the current index information
|
||||
list($identifier, $sections, $index) = $this->_get_index();
|
||||
|
||||
// get the key from the index
|
||||
$key = isset($index[$identifier][0]) ? $index[$identifier][0] : false;
|
||||
|
||||
if ($remove === true)
|
||||
{
|
||||
if ( $key !== false )
|
||||
{
|
||||
unset($index[$identifier]);
|
||||
xcache_set($this->config['cache_id'].$sections, $index);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// create a new key if needed
|
||||
$key === false and $key = $this->_new_key();
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate a new unique key for the current identifier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _new_key()
|
||||
{
|
||||
$key = '';
|
||||
while (strlen($key) < 32)
|
||||
{
|
||||
$key .= mt_rand(0, mt_getrandmax());
|
||||
}
|
||||
return md5($this->config['cache_id'].'_'.uniqid($key, TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the section index
|
||||
*
|
||||
* @return array containing the identifier, the sections, and the section index
|
||||
*/
|
||||
protected function _get_index()
|
||||
{
|
||||
// get the section name and identifier
|
||||
$sections = explode('.', $this->identifier);
|
||||
if (count($sections) > 1)
|
||||
{
|
||||
$identifier = array_pop($sections);
|
||||
$sections = '.'.implode('.', $sections);
|
||||
}
|
||||
else
|
||||
{
|
||||
$identifier = $this->identifier;
|
||||
$sections = '';
|
||||
}
|
||||
|
||||
// get the cache index and return it
|
||||
return array($identifier, $sections, xcache_get($this->config['cache_id'].$sections));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the section index
|
||||
*
|
||||
* @param string cache key
|
||||
*/
|
||||
protected function _update_index($key)
|
||||
{
|
||||
// get the current index information
|
||||
list($identifier, $sections, $index) = $this->_get_index();
|
||||
|
||||
// store the key in the index and write the index back
|
||||
$index[$identifier] = array($key, $this->created);
|
||||
xcache_set($this->config['cache_id'].$sections, array_merge($index, array($identifier => array($key, $this->created))));
|
||||
|
||||
// get the directory index
|
||||
$index = xcache_get($this->config['cache_id'].'__DIR__');
|
||||
|
||||
if (is_array($index))
|
||||
{
|
||||
if (!in_array($this->config['cache_id'].$sections, $index))
|
||||
{
|
||||
$index[] = $this->config['cache_id'].$sections;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$index = array($this->config['cache_id'].$sections);
|
||||
}
|
||||
|
||||
// update the directory index
|
||||
xcache_set($this->config['cache_id'].'__DIR__', $index, 0);
|
||||
}
|
||||
}
|
@ -1,532 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Cli class
|
||||
*
|
||||
* Interact with the command line by accepting input options, parameters and output text
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
* @author Phil Sturgeon
|
||||
* @link http://docs.fuelphp.com/classes/cli.html
|
||||
*/
|
||||
class Cli
|
||||
{
|
||||
public static $readline_support = false;
|
||||
|
||||
public static $wait_msg = 'Press any key to continue...';
|
||||
|
||||
public static $nocolor = false;
|
||||
|
||||
protected static $args = array();
|
||||
|
||||
protected static $foreground_colors = array(
|
||||
'black' => '0;30',
|
||||
'dark_gray' => '1;30',
|
||||
'blue' => '0;34',
|
||||
'dark_blue' => '1;34',
|
||||
'light_blue' => '1;34',
|
||||
'green' => '0;32',
|
||||
'light_green' => '1;32',
|
||||
'cyan' => '0;36',
|
||||
'light_cyan' => '1;36',
|
||||
'red' => '0;31',
|
||||
'light_red' => '1;31',
|
||||
'purple' => '0;35',
|
||||
'light_purple' => '1;35',
|
||||
'light_yellow' => '0;33',
|
||||
'yellow' => '1;33',
|
||||
'light_gray' => '0;37',
|
||||
'white' => '1;37',
|
||||
);
|
||||
|
||||
protected static $background_colors = array(
|
||||
'black' => '40',
|
||||
'red' => '41',
|
||||
'green' => '42',
|
||||
'yellow' => '43',
|
||||
'blue' => '44',
|
||||
'magenta' => '45',
|
||||
'cyan' => '46',
|
||||
'light_gray' => '47',
|
||||
);
|
||||
|
||||
protected static $STDOUT;
|
||||
protected static $STDERR;
|
||||
|
||||
/**
|
||||
* Static constructor. Parses all the CLI params.
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
if ( ! \Fuel::$is_cli)
|
||||
{
|
||||
throw new \Exception('Cli class cannot be used outside of the command line.');
|
||||
}
|
||||
for ($i = 1; $i < $_SERVER['argc']; $i++)
|
||||
{
|
||||
$arg = explode('=', $_SERVER['argv'][$i]);
|
||||
|
||||
static::$args[$i] = $arg[0];
|
||||
|
||||
if (count($arg) > 1 || strncmp($arg[0], '-', 1) === 0)
|
||||
{
|
||||
static::$args[ltrim($arg[0], '-')] = isset($arg[1]) ? $arg[1] : true;
|
||||
}
|
||||
}
|
||||
|
||||
// Readline is an extension for PHP that makes interactive with PHP much more bash-like
|
||||
// http://www.php.net/manual/en/readline.installation.php
|
||||
static::$readline_support = extension_loaded('readline');
|
||||
|
||||
static::$STDERR = STDERR;
|
||||
static::$STDOUT = STDOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the option with the given name. You can also give the option
|
||||
* number.
|
||||
*
|
||||
* Named options must be in the following formats:
|
||||
* php index.php user -v --v -name=John --name=John
|
||||
*
|
||||
* @param string|int $name the name of the option (int if unnamed)
|
||||
* @param mixed $default value to return if the option is not defined
|
||||
* @return mixed
|
||||
*/
|
||||
public static function option($name, $default = null)
|
||||
{
|
||||
if ( ! isset(static::$args[$name]))
|
||||
{
|
||||
return \Fuel::value($default);
|
||||
}
|
||||
return static::$args[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to set a commandline option from code
|
||||
*
|
||||
* @param string|int $name the name of the option (int if unnamed)
|
||||
* @param mixed|null $value value to set, or null to delete the option
|
||||
* @return mixed
|
||||
*/
|
||||
public static function set_option($name, $value = null)
|
||||
{
|
||||
if ($value === null)
|
||||
{
|
||||
if (isset(static::$args[$name]))
|
||||
{
|
||||
unset(static::$args[$name]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static::$args[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get input from the shell, using readline or the standard STDIN
|
||||
*
|
||||
* Named options must be in the following formats:
|
||||
* php index.php user -v --v -name=John --name=John
|
||||
*
|
||||
* @param string|int $prefix the name of the option (int if unnamed)
|
||||
* @return string
|
||||
*/
|
||||
public static function input($prefix = '')
|
||||
{
|
||||
if (static::$readline_support)
|
||||
{
|
||||
return readline($prefix);
|
||||
}
|
||||
|
||||
echo $prefix;
|
||||
return fgets(STDIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the user for input. This can have either 1 or 2 arguments.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* // Waits for any key press
|
||||
* CLI::prompt();
|
||||
*
|
||||
* // Takes any input
|
||||
* $color = CLI::prompt('What is your favorite color?');
|
||||
*
|
||||
* // Takes any input, but offers default
|
||||
* $color = CLI::prompt('What is your favourite color?', 'white');
|
||||
*
|
||||
* // Will only accept the options in the array
|
||||
* $ready = CLI::prompt('Are you ready?', array('y','n'));
|
||||
*
|
||||
* @return string the user input
|
||||
*/
|
||||
public static function prompt()
|
||||
{
|
||||
$args = func_get_args();
|
||||
|
||||
$options = array();
|
||||
$output = '';
|
||||
$default = null;
|
||||
|
||||
// How many we got
|
||||
$arg_count = count($args);
|
||||
|
||||
// Is the last argument a boolean? True means required
|
||||
$required = end($args) === true;
|
||||
|
||||
// Reduce the argument count if required was passed, we don't care about that anymore
|
||||
$required === true and --$arg_count;
|
||||
|
||||
// This method can take a few crazy combinations of arguments, so lets work it out
|
||||
switch ($arg_count)
|
||||
{
|
||||
case 2:
|
||||
|
||||
// E.g: $ready = CLI::prompt('Are you ready?', array('y','n'));
|
||||
if (is_array($args[1]))
|
||||
{
|
||||
list($output, $options)=$args;
|
||||
}
|
||||
|
||||
// E.g: $color = CLI::prompt('What is your favourite color?', 'white');
|
||||
elseif (is_string($args[1]))
|
||||
{
|
||||
list($output, $default)=$args;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
|
||||
// No question (probably been asked already) so just show options
|
||||
// E.g: $ready = CLI::prompt(array('y','n'));
|
||||
if (is_array($args[0]))
|
||||
{
|
||||
$options = $args[0];
|
||||
}
|
||||
|
||||
// Question without options
|
||||
// E.g: $ready = CLI::prompt('What did you do today?');
|
||||
elseif (is_string($args[0]))
|
||||
{
|
||||
$output = $args[0];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// If a question has been asked with the read
|
||||
if ($output !== '')
|
||||
{
|
||||
$extra_output = '';
|
||||
|
||||
if ($default !== null)
|
||||
{
|
||||
$extra_output = ' [ Default: "'.$default.'" ]';
|
||||
}
|
||||
|
||||
elseif ($options !== array())
|
||||
{
|
||||
$extra_output = ' [ '.implode(', ', $options).' ]';
|
||||
}
|
||||
|
||||
fwrite(static::$STDOUT, $output.$extra_output.': ');
|
||||
}
|
||||
|
||||
// Read the input from keyboard.
|
||||
$input = trim(static::input()) ?: $default;
|
||||
|
||||
// No input provided and we require one (default will stop this being called)
|
||||
if (empty($input) and $required === true)
|
||||
{
|
||||
static::write('This is required.');
|
||||
static::new_line();
|
||||
|
||||
$input = forward_static_call_array(array(__CLASS__, 'prompt'), $args);
|
||||
}
|
||||
|
||||
// If options are provided and the choice is not in the array, tell them to try again
|
||||
if ( ! empty($options) and ! in_array($input, $options))
|
||||
{
|
||||
static::write('This is not a valid option. Please try again.');
|
||||
static::new_line();
|
||||
|
||||
$input = forward_static_call_array(array(__CLASS__, 'prompt'), $args);
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs a string to the cli. If you send an array it will implode them
|
||||
* with a line break.
|
||||
*
|
||||
* @param string|array $text the text to output, or array of lines
|
||||
* @param string $foreground the foreground color
|
||||
* @param string $background the foreground color
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public static function write($text = '', $foreground = null, $background = null)
|
||||
{
|
||||
if (is_array($text))
|
||||
{
|
||||
$text = implode(PHP_EOL, $text);
|
||||
}
|
||||
|
||||
if ($foreground or $background)
|
||||
{
|
||||
$text = static::color($text, $foreground, $background);
|
||||
}
|
||||
|
||||
fwrite(static::$STDOUT, $text.PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs an error to the CLI using STDERR instead of STDOUT
|
||||
*
|
||||
* @param string|array $text the text to output, or array of errors
|
||||
* @param string $foreground the foreground color
|
||||
* @param string $background the foreground color
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public static function error($text = '', $foreground = 'light_red', $background = null)
|
||||
{
|
||||
if (is_array($text))
|
||||
{
|
||||
$text = implode(PHP_EOL, $text);
|
||||
}
|
||||
|
||||
if ($foreground OR $background)
|
||||
{
|
||||
$text = static::color($text, $foreground, $background);
|
||||
}
|
||||
|
||||
fwrite(static::$STDERR, $text.PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Beeps a certain number of times.
|
||||
*
|
||||
* @param int $num the number of times to beep
|
||||
*/
|
||||
public static function beep($num = 1)
|
||||
{
|
||||
echo str_repeat("\x07", $num);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits a certain number of seconds, optionally showing a wait message and
|
||||
* waiting for a key press.
|
||||
*
|
||||
* @param int $seconds number of seconds
|
||||
* @param bool $countdown show a countdown or not
|
||||
*/
|
||||
public static function wait($seconds = 0, $countdown = false)
|
||||
{
|
||||
if ($countdown === true)
|
||||
{
|
||||
$time = $seconds;
|
||||
|
||||
while ($time > 0)
|
||||
{
|
||||
fwrite(static::$STDOUT, $time.'... ');
|
||||
sleep(1);
|
||||
$time--;
|
||||
}
|
||||
static::write();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if ($seconds > 0)
|
||||
{
|
||||
sleep($seconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
static::write(static::$wait_msg);
|
||||
static::input();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* if operating system === windows
|
||||
*/
|
||||
public static function is_windows()
|
||||
{
|
||||
return 'win' === strtolower(substr(php_uname("s"), 0, 3));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter a number of empty lines
|
||||
*
|
||||
* @param integer Number of lines to output
|
||||
* @return void
|
||||
*/
|
||||
public static function new_line($num = 1)
|
||||
{
|
||||
// Do it once or more, write with empty string gives us a new line
|
||||
for($i = 0; $i < $num; $i++)
|
||||
{
|
||||
static::write();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the screen of output
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clear_screen()
|
||||
{
|
||||
static::is_windows()
|
||||
|
||||
// Windows is a bit crap at this, but their terminal is tiny so shove this in
|
||||
? static::new_line(40)
|
||||
|
||||
// Anything with a flair of Unix will handle these magic characters
|
||||
: fwrite(static::$STDOUT, chr(27)."[H".chr(27)."[2J");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given text with the correct color codes for a foreground and
|
||||
* optionally a background color.
|
||||
*
|
||||
* @param string $text the text to color
|
||||
* @param string $foreground the foreground color
|
||||
* @param string $background the background color
|
||||
* @param string $format other formatting to apply. Currently only 'underline' is understood
|
||||
* @return string the color coded string
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public static function color($text, $foreground, $background = null, $format=null)
|
||||
{
|
||||
if (static::is_windows() and ! \Input::server('ANSICON'))
|
||||
{
|
||||
return $text;
|
||||
}
|
||||
|
||||
if (static::$nocolor)
|
||||
{
|
||||
return $text;
|
||||
}
|
||||
|
||||
if ( ! array_key_exists($foreground, static::$foreground_colors))
|
||||
{
|
||||
throw new \FuelException('Invalid CLI foreground color: '.$foreground);
|
||||
}
|
||||
|
||||
if ( $background !== null and ! array_key_exists($background, static::$background_colors))
|
||||
{
|
||||
throw new \FuelException('Invalid CLI background color: '.$background);
|
||||
}
|
||||
|
||||
$string = "\033[".static::$foreground_colors[$foreground]."m";
|
||||
|
||||
if ($background !== null)
|
||||
{
|
||||
$string .= "\033[".static::$background_colors[$background]."m";
|
||||
}
|
||||
|
||||
if ($format === 'underline')
|
||||
{
|
||||
$string .= "\033[4m";
|
||||
}
|
||||
|
||||
$string .= $text."\033[0m";
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn Background Process
|
||||
*
|
||||
* Launches a background process (note, provides no security itself, $call must be sanitised prior to use)
|
||||
* @param string $call the system call to make
|
||||
* @param string $output
|
||||
* @return void
|
||||
* @author raccettura
|
||||
* @link http://robert.accettura.com/blog/2006/09/14/asynchronous-processing-with-php/
|
||||
*/
|
||||
public static function spawn($call, $output = '/dev/null')
|
||||
{
|
||||
// Windows
|
||||
if(static::is_windows())
|
||||
{
|
||||
pclose(popen('start /b '.$call, 'r'));
|
||||
}
|
||||
|
||||
// Some sort of UNIX
|
||||
else
|
||||
{
|
||||
pclose(popen($call.' > '.$output.' &', 'r'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect STDERR writes to this file or fh
|
||||
*
|
||||
* Call with no argument to retrieve the current filehandle.
|
||||
*
|
||||
* Is not smart about opening the file if it's a string. Existing files will be truncated.
|
||||
*
|
||||
* @param resource|string $fh Opened filehandle or string filename.
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public static function stderr($fh = null)
|
||||
{
|
||||
$orig = static::$STDERR;
|
||||
|
||||
if (! is_null($fh)) {
|
||||
if (is_string($fh)) {
|
||||
$fh = fopen($fh, "w");
|
||||
}
|
||||
static::$STDERR = $fh;
|
||||
}
|
||||
|
||||
return $orig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect STDOUT writes to this file or fh
|
||||
*
|
||||
* Call with no argument to retrieve the current filehandle.
|
||||
*
|
||||
* Is not smart about opening the file if it's a string. Existing files will be truncated.
|
||||
*
|
||||
* @param resource|string|null $fh Opened filehandle or string filename.
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public static function stdout($fh = null)
|
||||
{
|
||||
$orig = static::$STDOUT;
|
||||
|
||||
if (! is_null($fh)) {
|
||||
if (is_string($fh)) {
|
||||
$fh = fopen($fh, "w");
|
||||
}
|
||||
static::$STDOUT = $fh;
|
||||
}
|
||||
|
||||
return $orig;
|
||||
}
|
||||
}
|
@ -1,231 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class ConfigException extends \FuelException { }
|
||||
|
||||
class Config
|
||||
{
|
||||
/**
|
||||
* @var array $loaded_files array of loaded files
|
||||
*/
|
||||
public static $loaded_files = array();
|
||||
|
||||
/**
|
||||
* @var array $items the master config array
|
||||
*/
|
||||
public static $items = array();
|
||||
|
||||
/**
|
||||
* @var array $itemcache the dot-notated item cache
|
||||
*/
|
||||
protected static $itemcache = array();
|
||||
|
||||
/**
|
||||
* Loads a config file.
|
||||
*
|
||||
* @param mixed $file string file | config array | Config_Interface instance
|
||||
* @param mixed $group null for no group, true for group is filename, false for not storing in the master config
|
||||
* @param bool $reload true to force a reload even if the file is already loaded
|
||||
* @param bool $overwrite true for array_merge, false for \Arr::merge
|
||||
* @return array the (loaded) config array
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public static function load($file, $group = null, $reload = false, $overwrite = false)
|
||||
{
|
||||
if ( ! $reload and
|
||||
! is_array($file) and
|
||||
! is_object($file) and
|
||||
array_key_exists($file, static::$loaded_files))
|
||||
{
|
||||
$group === true and $group = $file;
|
||||
if ($group === null or $group === false or ! isset(static::$items[$group]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return static::$items[$group];
|
||||
}
|
||||
|
||||
$config = array();
|
||||
if (is_array($file))
|
||||
{
|
||||
$config = $file;
|
||||
}
|
||||
elseif (is_string($file))
|
||||
{
|
||||
$info = pathinfo($file);
|
||||
$type = 'php';
|
||||
if (isset($info['extension']))
|
||||
{
|
||||
$type = $info['extension'];
|
||||
// Keep extension when it's an absolute path, because the finder won't add it
|
||||
if ($file[0] !== '/' and $file[1] !== ':')
|
||||
{
|
||||
$file = substr($file, 0, -(strlen($type) + 1));
|
||||
}
|
||||
}
|
||||
$class = '\\Config_'.ucfirst($type);
|
||||
|
||||
if (class_exists($class))
|
||||
{
|
||||
static::$loaded_files[$file] = true;
|
||||
$file = new $class($file);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \FuelException(sprintf('Invalid config type "%s".', $type));
|
||||
}
|
||||
}
|
||||
|
||||
if ($file instanceof Config_Interface)
|
||||
{
|
||||
try
|
||||
{
|
||||
$config = $file->load($overwrite, ! $reload);
|
||||
}
|
||||
catch (\ConfigException $e)
|
||||
{
|
||||
$config = array();
|
||||
}
|
||||
$group = $group === true ? $file->group() : $group;
|
||||
}
|
||||
|
||||
if ($group === null)
|
||||
{
|
||||
static::$items = $reload ? $config : ($overwrite ? array_merge(static::$items, $config) : \Arr::merge(static::$items, $config));
|
||||
static::$itemcache = array();
|
||||
}
|
||||
else
|
||||
{
|
||||
$group = ($group === true) ? $file : $group;
|
||||
if ( ! isset(static::$items[$group]) or $reload)
|
||||
{
|
||||
static::$items[$group] = array();
|
||||
}
|
||||
static::$items[$group] = $overwrite ? array_merge(static::$items[$group], $config) : \Arr::merge(static::$items[$group], $config);
|
||||
$group .= '.';
|
||||
foreach (static::$itemcache as $key => $value)
|
||||
{
|
||||
if (strpos($key, $group) === 0)
|
||||
{
|
||||
unset(static::$itemcache[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a config array to disc.
|
||||
*
|
||||
* @param string $file desired file name
|
||||
* @param string|array $config master config array key or config array
|
||||
* @return bool false when config is empty or invalid else \File::update result
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public static function save($file, $config)
|
||||
{
|
||||
if ( ! is_array($config))
|
||||
{
|
||||
if ( ! isset(static::$items[$config]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$config = static::$items[$config];
|
||||
}
|
||||
|
||||
$info = pathinfo($file);
|
||||
$type = 'php';
|
||||
|
||||
if (isset($info['extension']))
|
||||
{
|
||||
$type = $info['extension'];
|
||||
// Keep extension when it's an absolute path, because the finder won't add it
|
||||
if ($file[0] !== '/' and $file[1] !== ':')
|
||||
{
|
||||
$file = substr($file, 0, -(strlen($type) + 1));
|
||||
}
|
||||
}
|
||||
|
||||
$class = '\\Config_'.ucfirst($type);
|
||||
|
||||
if ( ! class_exists($class))
|
||||
{
|
||||
throw new \FuelException(sprintf('Invalid config type "%s".', $type));
|
||||
}
|
||||
|
||||
$driver = new $class($file);
|
||||
|
||||
return $driver->save($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a (dot notated) config setting
|
||||
*
|
||||
* @param string $item name of the config item, can be dot notated
|
||||
* @param mixed $default the return value if the item isn't found
|
||||
* @return mixed the config setting or default if not found
|
||||
*/
|
||||
public static function get($item, $default = null)
|
||||
{
|
||||
if (array_key_exists($item, static::$items))
|
||||
{
|
||||
return static::$items[$item];
|
||||
}
|
||||
elseif ( ! array_key_exists($item, static::$itemcache))
|
||||
{
|
||||
// cook up something unique
|
||||
$miss = new \stdClass();
|
||||
|
||||
$val = \Arr::get(static::$items, $item, $miss);
|
||||
|
||||
// so we can detect a miss here...
|
||||
if ($val === $miss)
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
|
||||
static::$itemcache[$item] = $val;
|
||||
}
|
||||
|
||||
return \Fuel::value(static::$itemcache[$item]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a (dot notated) config item
|
||||
*
|
||||
* @param string $item a (dot notated) config key
|
||||
* @param mixed $value the config value
|
||||
*/
|
||||
public static function set($item, $value)
|
||||
{
|
||||
strpos($item, '.') === false or static::$itemcache[$item] = $value;
|
||||
\Arr::set(static::$items, $item, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a (dot notated) config item
|
||||
*
|
||||
* @param string $item a (dot notated) config key
|
||||
* @return array|bool the \Arr::delete result, success boolean or array of success booleans
|
||||
*/
|
||||
public static function delete($item)
|
||||
{
|
||||
if (isset(static::$itemcache[$item]))
|
||||
{
|
||||
unset(static::$itemcache[$item]);
|
||||
}
|
||||
return \Arr::delete(static::$items, $item);
|
||||
}
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* DB config data parser
|
||||
*/
|
||||
class Config_Db implements Config_Interface
|
||||
{
|
||||
protected $identifier;
|
||||
|
||||
protected $ext = '.db';
|
||||
|
||||
protected $vars = array();
|
||||
|
||||
protected $database;
|
||||
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* Sets up the file to be parsed and variables
|
||||
*
|
||||
* @param string $identifier Config identifier name
|
||||
* @param array $vars Variables to parse in the data retrieved
|
||||
*/
|
||||
public function __construct($identifier = null, $vars = array())
|
||||
{
|
||||
$this->identifier = $identifier;
|
||||
|
||||
$this->vars = array(
|
||||
'APPPATH' => APPPATH,
|
||||
'COREPATH' => COREPATH,
|
||||
'PKGPATH' => PKGPATH,
|
||||
'DOCROOT' => DOCROOT,
|
||||
) + $vars;
|
||||
|
||||
$this->database = \Config::get('config.database', null);
|
||||
$this->table = \Config::get('config.table_name', 'config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the config file(s).
|
||||
*
|
||||
* @param bool $overwrite Whether to overwrite existing values
|
||||
* @param bool $cache This parameter will ignore in this implement.
|
||||
* @return array the config array
|
||||
* @throws \Database_Exception
|
||||
*/
|
||||
public function load($overwrite = false, $cache = true)
|
||||
{
|
||||
$config = array();
|
||||
|
||||
// try to retrieve the config from the database
|
||||
try
|
||||
{
|
||||
$result = \DB::select('config')->from($this->table)->where('identifier', '=', $this->identifier)->execute($this->database);
|
||||
}
|
||||
catch (Database_Exception $e)
|
||||
{
|
||||
// strip the actual query from the message
|
||||
$msg = $e->getMessage();
|
||||
$msg = substr($msg, 0, strlen($msg) - strlen(strrchr($msg, ':')));
|
||||
|
||||
// and rethrow it
|
||||
throw new \Database_Exception($msg);
|
||||
}
|
||||
|
||||
// did we succeed?
|
||||
if ($result->count())
|
||||
{
|
||||
empty($result[0]['config']) or $config = unserialize($this->parse_vars($result[0]['config']));
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default group name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function group()
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string using all of the previously set variables. Allows you to
|
||||
* use something like %APPPATH% in non-PHP files.
|
||||
*
|
||||
* @param string $string String to parse
|
||||
* @return string
|
||||
*/
|
||||
protected function parse_vars($string)
|
||||
{
|
||||
foreach ($this->vars as $var => $val)
|
||||
{
|
||||
$string = str_replace("%$var%", $val, $string);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces FuelPHP's path constants to their string counterparts.
|
||||
*
|
||||
* @param array $array array to be prepped
|
||||
* @return array prepped array
|
||||
*/
|
||||
protected function prep_vars(&$array)
|
||||
{
|
||||
static $replacements = false;
|
||||
|
||||
if ($replacements === false)
|
||||
{
|
||||
foreach ($this->vars as $i => $v)
|
||||
{
|
||||
$replacements['#^('.preg_quote($v).'){1}(.*)?#'] = "%".$i."%$2";
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($array as $i => $value)
|
||||
{
|
||||
if (is_string($value))
|
||||
{
|
||||
$array[$i] = preg_replace(array_keys($replacements), array_values($replacements), $value);
|
||||
}
|
||||
elseif(is_array($value))
|
||||
{
|
||||
$this->prep_vars($array[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the output and saved it to disc.
|
||||
*
|
||||
* @param $contents $contents config array to save
|
||||
* @return bool DB result
|
||||
*/
|
||||
public function save($contents)
|
||||
{
|
||||
// prep the contents
|
||||
$this->prep_vars($contents);
|
||||
$contents = serialize($contents);
|
||||
|
||||
// update the config in the database
|
||||
$result = \DB::update($this->table)->set(array('config' => $contents, 'hash' => uniqid()))->where('identifier', '=', $this->identifier)->execute($this->database);
|
||||
|
||||
// if there wasn't an update, do an insert
|
||||
if ($result === 0)
|
||||
{
|
||||
list($notused, $result) = \DB::insert($this->table)->set(array('identifier' => $this->identifier, 'config' => $contents, 'hash' => uniqid()))->execute($this->database);
|
||||
}
|
||||
|
||||
return $result === 1;
|
||||
}
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* A base Config File class for File based configs.
|
||||
*/
|
||||
abstract class Config_File implements Config_Interface
|
||||
{
|
||||
protected $file;
|
||||
|
||||
protected $vars = array();
|
||||
|
||||
/**
|
||||
* Sets up the file to be parsed and variables
|
||||
*
|
||||
* @param string $file Config file name
|
||||
* @param array $vars Variables to parse in the file
|
||||
*/
|
||||
public function __construct($file = null, $vars = array())
|
||||
{
|
||||
$this->file = $file;
|
||||
|
||||
$this->vars = array(
|
||||
'APPPATH' => APPPATH,
|
||||
'COREPATH' => COREPATH,
|
||||
'PKGPATH' => PKGPATH,
|
||||
'DOCROOT' => DOCROOT,
|
||||
) + $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the config file(s).
|
||||
*
|
||||
* @param bool $overwrite Whether to overwrite existing values
|
||||
* @param bool $cache Whether to cache this path or not
|
||||
* @return array the config array
|
||||
*/
|
||||
public function load($overwrite = false, $cache = true)
|
||||
{
|
||||
$paths = $this->find_file($cache);
|
||||
$config = array();
|
||||
|
||||
foreach ($paths as $path)
|
||||
{
|
||||
$config = $overwrite ?
|
||||
array_merge($config, $this->load_file($path)) :
|
||||
\Arr::merge($config, $this->load_file($path));
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default group name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function group()
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string using all of the previously set variables. Allows you to
|
||||
* use something like %APPPATH% in non-PHP files.
|
||||
*
|
||||
* @param string $string String to parse
|
||||
* @return string
|
||||
*/
|
||||
protected function parse_vars($string)
|
||||
{
|
||||
foreach ($this->vars as $var => $val)
|
||||
{
|
||||
$string = str_replace("%$var%", $val, $string);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces FuelPHP's path constants to their string counterparts.
|
||||
*
|
||||
* @param array $array array to be prepped
|
||||
* @return array prepped array
|
||||
*/
|
||||
protected function prep_vars(&$array)
|
||||
{
|
||||
static $replacements = false;
|
||||
|
||||
if ($replacements === false)
|
||||
{
|
||||
foreach ($this->vars as $i => $v)
|
||||
{
|
||||
$replacements['#^('.preg_quote($v).'){1}(.*)?#'] = "%".$i."%$2";
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($array as $i => $value)
|
||||
{
|
||||
if (is_string($value))
|
||||
{
|
||||
$array[$i] = preg_replace(array_keys($replacements), array_values($replacements), $value);
|
||||
}
|
||||
elseif(is_array($value))
|
||||
{
|
||||
$this->prep_vars($array[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the given config files
|
||||
*
|
||||
* @param bool $cache Whether to cache this path or not
|
||||
* @return array
|
||||
* @throws \ConfigException
|
||||
*/
|
||||
protected function find_file($cache = true)
|
||||
{
|
||||
if (($this->file[0] === '/' or (isset($this->file[1]) and $this->file[1] === ':')) and is_file($this->file))
|
||||
{
|
||||
$paths = array($this->file);
|
||||
}
|
||||
else
|
||||
{
|
||||
$paths = array_merge(
|
||||
\Finder::search('config/'.\Fuel::$env, $this->file, $this->ext, true, $cache),
|
||||
\Finder::search('config', $this->file, $this->ext, true, $cache)
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($paths))
|
||||
{
|
||||
throw new \ConfigException(sprintf('File "%s" does not exist.', $this->file));
|
||||
}
|
||||
|
||||
return array_reverse($paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the output and saved it to disc.
|
||||
*
|
||||
* @param array $contents config array to save
|
||||
* @return bool \File::update result
|
||||
*/
|
||||
public function save($contents)
|
||||
{
|
||||
// get the formatted output
|
||||
$output = $this->export_format($contents);
|
||||
|
||||
if ( ! $output)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $path = \Finder::search('config', $this->file, $this->ext))
|
||||
{
|
||||
if ($pos = strripos($this->file, '::'))
|
||||
{
|
||||
// get the namespace path
|
||||
if ($path = \Autoloader::namespace_path('\\'.ucfirst(substr($this->file, 0, $pos))))
|
||||
{
|
||||
// strip the namespace from the filename
|
||||
$this->file = substr($this->file, $pos+2);
|
||||
|
||||
// strip the classes directory as we need the module root
|
||||
$path = substr($path, 0, -8).'config'.DS.$this->file.$this->ext;
|
||||
}
|
||||
else
|
||||
{
|
||||
// invalid namespace requested
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// absolute path requested?
|
||||
if ($this->file[0] === '/' or (isset($this->file[1]) and $this->file[1] === ':'))
|
||||
{
|
||||
$path = $this->file;
|
||||
}
|
||||
|
||||
// make sure we have a fallback
|
||||
$path or $path = APPPATH.'config'.DS.$this->file.$this->ext;
|
||||
|
||||
$path = pathinfo($path);
|
||||
if ( ! is_dir($path['dirname']))
|
||||
{
|
||||
mkdir($path['dirname'], 0777, true);
|
||||
}
|
||||
|
||||
$return = \File::update($path['dirname'], $path['basename'], $output);
|
||||
if ($return)
|
||||
{
|
||||
try
|
||||
{
|
||||
\Config::load('file', true);
|
||||
chmod($path['dirname'].DS.$path['basename'], \Config::get('file.chmod.files', 0666));
|
||||
}
|
||||
catch (\PhpErrorException $e)
|
||||
{
|
||||
// if we get something else then a chmod error, bail out
|
||||
if (substr($e->getMessage(), 0, 8) !== 'chmod():')
|
||||
{
|
||||
throw new $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be implemented by child class. Gets called for each file to load.
|
||||
*
|
||||
* @param string $file the path to the file
|
||||
*/
|
||||
abstract protected function load_file($file);
|
||||
|
||||
/**
|
||||
* Must be implemented by child class. Gets called when saving a config file.
|
||||
*
|
||||
* @param array $contents config array to save
|
||||
* @return string formatted output
|
||||
*/
|
||||
abstract protected function export_format($contents);
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* INI Config file parser
|
||||
*/
|
||||
class Config_Ini extends \Config_File
|
||||
{
|
||||
/**
|
||||
* @var string the extension used by this ini file parser
|
||||
*/
|
||||
protected $ext = '.ini';
|
||||
|
||||
/**
|
||||
* Loads in the given file and parses it.
|
||||
*
|
||||
* @param string $file File to load
|
||||
* @return array
|
||||
*/
|
||||
protected function load_file($file)
|
||||
{
|
||||
$contents = $this->parse_vars(file_get_contents($file));
|
||||
return parse_ini_string($contents, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatted config file contents.
|
||||
*
|
||||
* @param array $contents config array
|
||||
* @return string formatted config file contents
|
||||
* @throws \ConfigException
|
||||
*/
|
||||
protected function export_format($contents)
|
||||
{
|
||||
throw new \ConfigException('Saving config to ini is not supported at this time');
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
interface Config_Interface
|
||||
{
|
||||
public function load($overwrite = false);
|
||||
public function group();
|
||||
public function save($contents);
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* JSON Config file parser
|
||||
*/
|
||||
class Config_Json extends \Config_File
|
||||
{
|
||||
/**
|
||||
* @var string the extension used by this JSON file parser
|
||||
*/
|
||||
protected $ext = '.json';
|
||||
|
||||
/**
|
||||
* Loads in the given file and parses it.
|
||||
*
|
||||
* @param string $file File to load
|
||||
* @return array
|
||||
*/
|
||||
protected function load_file($file)
|
||||
{
|
||||
$contents = $this->parse_vars(file_get_contents($file));
|
||||
return json_decode($contents, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatted config file contents.
|
||||
*
|
||||
* @param array $contents config array
|
||||
* @return string formatted config file contents
|
||||
*/
|
||||
protected function export_format($contents)
|
||||
{
|
||||
$this->prep_vars($contents);
|
||||
return \Format::forge()->to_json($contents, true);
|
||||
}
|
||||
}
|
@ -1,174 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* DB config data parser
|
||||
*/
|
||||
class Config_Memcached implements Config_Interface
|
||||
{
|
||||
/**
|
||||
* @var array of driver config defaults
|
||||
*/
|
||||
protected static $config = array(
|
||||
'identifier' => 'config',
|
||||
'servers' => array(
|
||||
array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* @var \Memcached storage for the memcached object
|
||||
*/
|
||||
protected static $memcached = false;
|
||||
|
||||
/**
|
||||
* driver initialisation
|
||||
*
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
static::$config = array_merge(static::$config, \Config::get('config.memcached', array()));
|
||||
|
||||
if (static::$memcached === false)
|
||||
{
|
||||
// do we have the PHP memcached extension available
|
||||
if ( ! class_exists('Memcached') )
|
||||
{
|
||||
throw new \FuelException('Memcached config storage is required, but your PHP installation doesn\'t have the Memcached extension loaded.');
|
||||
}
|
||||
|
||||
// instantiate the memcached object
|
||||
static::$memcached = new \Memcached();
|
||||
|
||||
// add the configured servers
|
||||
static::$memcached->addServers(static::$config['servers']);
|
||||
|
||||
// check if we can connect to all the server(s)
|
||||
$added = static::$memcached->getStats();
|
||||
foreach (static::$config['servers'] as $server)
|
||||
{
|
||||
$server = $server['host'].':'.$server['port'];
|
||||
if ( ! isset($added[$server]) or $added[$server]['pid'] == -1)
|
||||
{
|
||||
throw new \FuelException('Memcached config storage is required, but there is no connection possible. Check your configuration.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
protected $identifier;
|
||||
|
||||
protected $ext = '.mem';
|
||||
|
||||
protected $vars = array();
|
||||
|
||||
/**
|
||||
* Sets up the file to be parsed and variables
|
||||
*
|
||||
* @param string $identifier Config identifier name
|
||||
* @param array $vars Variables to parse in the data retrieved
|
||||
*/
|
||||
public function __construct($identifier = null, $vars = array())
|
||||
{
|
||||
$this->identifier = $identifier;
|
||||
|
||||
$this->vars = array(
|
||||
'APPPATH' => APPPATH,
|
||||
'COREPATH' => COREPATH,
|
||||
'PKGPATH' => PKGPATH,
|
||||
'DOCROOT' => DOCROOT,
|
||||
) + $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the config file(s).
|
||||
*
|
||||
* @param bool $overwrite Whether to overwrite existing values
|
||||
* @param bool $cache This parameter will ignore in this implement.
|
||||
* @return array the config array
|
||||
*/
|
||||
public function load($overwrite = false, $cache = true)
|
||||
{
|
||||
// fetch the config data from the Memcached server
|
||||
$result = static::$memcached->get(static::$config['identifier'].'_'.$this->identifier);
|
||||
|
||||
return $result === false ? array() : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default group name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function group()
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string using all of the previously set variables. Allows you to
|
||||
* use something like %APPPATH% in non-PHP files.
|
||||
*
|
||||
* @param string $string String to parse
|
||||
* @return string
|
||||
*/
|
||||
protected function parse_vars($string)
|
||||
{
|
||||
foreach ($this->vars as $var => $val)
|
||||
{
|
||||
$string = str_replace("%$var%", $val, $string);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces FuelPHP's path constants to their string counterparts.
|
||||
*
|
||||
* @param array $array array to be prepped
|
||||
* @return array prepped array
|
||||
*/
|
||||
protected function prep_vars(&$array)
|
||||
{
|
||||
static $replacements = false;
|
||||
|
||||
if ($replacements === false)
|
||||
{
|
||||
foreach ($this->vars as $i => $v)
|
||||
{
|
||||
$replacements['#^('.preg_quote($v).'){1}(.*)?#'] = "%".$i."%$2";
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($array as $i => $value)
|
||||
{
|
||||
if (is_string($value))
|
||||
{
|
||||
$array[$i] = preg_replace(array_keys($replacements), array_values($replacements), $value);
|
||||
}
|
||||
elseif(is_array($value))
|
||||
{
|
||||
$this->prep_vars($array[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the output and saved it to disc.
|
||||
*
|
||||
* @param $contents $contents config array to save
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public function save($contents)
|
||||
{
|
||||
// write it to the memcached server
|
||||
if (static::$memcached->set(static::$config['identifier'].'_'.$this->identifier, $contents, 0) === false)
|
||||
{
|
||||
throw new \FuelException('Memcached returned error code "'.static::$memcached->getResultCode().'" on write. Check your configuration.');
|
||||
}
|
||||
}
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* PHP Config file parser
|
||||
*/
|
||||
class Config_Php extends \Config_File
|
||||
{
|
||||
/**
|
||||
* @var bool whether or not opcache is in use
|
||||
*/
|
||||
protected static $uses_opcache = false;
|
||||
|
||||
/**
|
||||
* @var bool whether or not APC is in use
|
||||
*/
|
||||
protected static $uses_apc = false;
|
||||
|
||||
/**
|
||||
* @var bool whether or not we need to flush the opcode cache after a save
|
||||
*/
|
||||
protected static $flush_needed = false;
|
||||
|
||||
/**
|
||||
* check the status of any opcache mechanism in use
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
// do we have Opcache active?
|
||||
static::$uses_opcache = (PHP_VERSION_ID >= 50500 and function_exists('opcache_invalidate'));
|
||||
|
||||
// do we have APC active?
|
||||
static::$uses_apc = function_exists('apc_compile_file');
|
||||
|
||||
// determine if we have an opcode cache active
|
||||
static::$flush_needed = static::$uses_opcache or static::$uses_apc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string the extension used by this config file parser
|
||||
*/
|
||||
protected $ext = '.php';
|
||||
|
||||
/**
|
||||
* Formats the output and saved it to disk.
|
||||
*
|
||||
* @param $contents $contents config array to save
|
||||
* @return bool \File::update result
|
||||
*/
|
||||
public function save($contents)
|
||||
{
|
||||
// store the current filename
|
||||
$file = $this->file;
|
||||
|
||||
// save it
|
||||
$return = parent::save($contents);
|
||||
|
||||
// existing file? saved? and do we need to flush the opcode cache?
|
||||
if ($file == $this->file and $return and static::$flush_needed)
|
||||
{
|
||||
if ($this->file[0] !== '/' and ( ! isset($this->file[1]) or $this->file[1] !== ':'))
|
||||
{
|
||||
// locate the file
|
||||
$file = \Finder::search('config', $this->file, $this->ext);
|
||||
}
|
||||
|
||||
// make sure we have a fallback
|
||||
$file or $file = APPPATH.'config'.DS.$this->file.$this->ext;
|
||||
|
||||
// flush the opcode caches that are active
|
||||
static::$uses_opcache and opcache_invalidate($file, true);
|
||||
static::$uses_apc and apc_compile_file($file);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads in the given file and parses it.
|
||||
*
|
||||
* @param string $file File to load
|
||||
* @return array
|
||||
*/
|
||||
protected function load_file($file)
|
||||
{
|
||||
return \Fuel::load($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatted config file contents.
|
||||
*
|
||||
* @param array $contents config array
|
||||
* @return string formatted config file contents
|
||||
*/
|
||||
protected function export_format($contents)
|
||||
{
|
||||
$output = <<<CONF
|
||||
<?php
|
||||
|
||||
CONF;
|
||||
$output .= 'return '.str_replace(array('array ('.PHP_EOL, '\''.APPPATH, '\''.DOCROOT, '\''.COREPATH, '\''.PKGPATH), array('array('.PHP_EOL, 'APPPATH.\'', 'DOCROOT.\'', 'COREPATH.\'', 'PKGPATH.\''), var_export($contents, true)).";\n";
|
||||
return $output;
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Yaml Config file parser
|
||||
*/
|
||||
class Config_Yml extends \Config_File
|
||||
{
|
||||
/**
|
||||
* @var string the extension used by this yaml file parser
|
||||
*/
|
||||
protected $ext = '.yml';
|
||||
|
||||
/**
|
||||
* Loads in the given file and parses it.
|
||||
*
|
||||
* @param string $file File to load
|
||||
* @return array
|
||||
*/
|
||||
protected function load_file($file)
|
||||
{
|
||||
$contents = $this->parse_vars(file_get_contents($file));
|
||||
return \Format::forge($contents, 'yaml')->to_array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatted config file contents.
|
||||
*
|
||||
* @param array $contents config array
|
||||
* @return string formatted config file contents
|
||||
*/
|
||||
protected function export_format($contents)
|
||||
{
|
||||
if ( ! function_exists('spyc_load'))
|
||||
{
|
||||
import('spyc/spyc', 'vendor');
|
||||
}
|
||||
|
||||
$this->prep_vars($contents);
|
||||
return \Spyc::YAMLDump($contents);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
/**
|
||||
* @var Request The current Request object
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* @var Integer The default response status
|
||||
*/
|
||||
public $response_status = 200;
|
||||
|
||||
/**
|
||||
* Sets the controller request object.
|
||||
*
|
||||
* @param \Request $request The current request object
|
||||
*/
|
||||
public function __construct(\Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method gets called before the action is called
|
||||
*/
|
||||
public function before() {}
|
||||
|
||||
/**
|
||||
* This method gets called after the action is called
|
||||
* @param \Response|string $response
|
||||
* @return \Response
|
||||
*/
|
||||
public function after($response)
|
||||
{
|
||||
// Make sure the $response is a Response object
|
||||
if ( ! $response instanceof Response)
|
||||
{
|
||||
$response = \Response::forge($response, $this->response_status);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the named parameter requested, or all of them
|
||||
* if no parameter is given.
|
||||
*
|
||||
* @param string $param The name of the parameter
|
||||
* @param mixed $default Default value
|
||||
* @return mixed
|
||||
*/
|
||||
public function param($param, $default = null)
|
||||
{
|
||||
return $this->request->param($param, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns all of the named parameters.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function params()
|
||||
{
|
||||
return $this->request->params();
|
||||
}
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Hybrid Controller class
|
||||
*
|
||||
* A base controller that combines both templated and REST output
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
* @author Fuel Development Team
|
||||
*/
|
||||
abstract class Controller_Hybrid extends \Controller_Rest
|
||||
{
|
||||
/**
|
||||
* @var string page template
|
||||
*/
|
||||
public $template = 'template';
|
||||
|
||||
/**
|
||||
* Load the template and create the $this->template object if needed
|
||||
*/
|
||||
public function before()
|
||||
{
|
||||
// setup the template if this isn't a RESTful call
|
||||
if ( ! $this->is_restful())
|
||||
{
|
||||
if ( ! empty($this->template) and is_string($this->template))
|
||||
{
|
||||
// Load the template
|
||||
$this->template = \View::forge($this->template);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::before();
|
||||
}
|
||||
|
||||
/**
|
||||
* router
|
||||
*
|
||||
* this router will call action methods for normal requests,
|
||||
* and REST methods for RESTful calls
|
||||
*
|
||||
* @param string $resource
|
||||
* @param array $arguments
|
||||
* @return mixed
|
||||
* @throws \HttpNotFoundException
|
||||
*/
|
||||
public function router($resource, $arguments)
|
||||
{
|
||||
// if this is an ajax call
|
||||
if ($this->is_restful())
|
||||
{
|
||||
// have the Controller_Rest router deal with it
|
||||
return parent::router($resource, $arguments);
|
||||
}
|
||||
|
||||
// check if a input specific method exists
|
||||
$controller_method = strtolower(\Input::method()) . '_' . $resource;
|
||||
|
||||
// fall back to action_ if no rest method is provided
|
||||
if ( ! method_exists($this, $controller_method))
|
||||
{
|
||||
$controller_method = 'action_'.$resource;
|
||||
}
|
||||
|
||||
// check if the action method exists
|
||||
if (method_exists($this, $controller_method))
|
||||
{
|
||||
return call_fuel_func_array(array($this, $controller_method), $arguments);
|
||||
}
|
||||
|
||||
// if not, we got ourselfs a genuine 404!
|
||||
throw new \HttpNotFoundException();
|
||||
}
|
||||
|
||||
/**
|
||||
* After controller method has run output the template
|
||||
*
|
||||
* @param Response $response
|
||||
*/
|
||||
public function after($response)
|
||||
{
|
||||
// return the template if no response is present and this isn't a RESTful call
|
||||
if ( ! $this->is_restful())
|
||||
{
|
||||
// do we have a response passed?
|
||||
if ($response === null)
|
||||
{
|
||||
// maybe one in the rest body?
|
||||
$response = $this->response->body;
|
||||
if ($response === null)
|
||||
{
|
||||
// fall back to the defined template
|
||||
$response = $this->template;
|
||||
}
|
||||
}
|
||||
|
||||
// deal with returned array's in non-restful calls
|
||||
elseif (is_array($response))
|
||||
{
|
||||
$response = \Format::forge()->to_json($response, true);
|
||||
}
|
||||
|
||||
// and make sure we have a valid Response object
|
||||
if ( ! $response instanceof Response)
|
||||
{
|
||||
$response = \Response::forge($response, $this->response_status);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::after($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide whether to return RESTful or templated response
|
||||
* Override in subclass to introduce custom switching logic.
|
||||
*
|
||||
* @param boolean
|
||||
*/
|
||||
public function is_restful()
|
||||
{
|
||||
return \Input::is_ajax();
|
||||
}
|
||||
}
|
@ -1,515 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
abstract class Controller_Rest extends \Controller
|
||||
{
|
||||
/**
|
||||
* @var null|string Set this in a controller to use a default format
|
||||
*/
|
||||
protected $rest_format = null;
|
||||
|
||||
/**
|
||||
* @var array contains a list of method properties such as limit, log and level
|
||||
*/
|
||||
protected $methods = array();
|
||||
|
||||
/**
|
||||
* @var integer status code to return in case a not defined action is called
|
||||
*/
|
||||
protected $no_method_status = 405;
|
||||
|
||||
/**
|
||||
* @var integer status code to return in case the called action doesn't return data
|
||||
*/
|
||||
protected $no_data_status = 204;
|
||||
|
||||
/**
|
||||
* @var string authentication to be used for this controller
|
||||
*/
|
||||
protected $auth = null;
|
||||
|
||||
/**
|
||||
* @var string the detected response format
|
||||
*/
|
||||
protected $format = null;
|
||||
|
||||
/**
|
||||
* @var integer default response http status
|
||||
*/
|
||||
protected $http_status = 200;
|
||||
|
||||
/**
|
||||
* @var string xml basenode name
|
||||
*/
|
||||
protected $xml_basenode = null;
|
||||
|
||||
/**
|
||||
* @var array List all supported methods
|
||||
*/
|
||||
protected $_supported_formats = array(
|
||||
'xml' => 'application/xml',
|
||||
'rawxml' => 'application/xml',
|
||||
'json' => 'application/json',
|
||||
'jsonp'=> 'text/javascript',
|
||||
'serialized' => 'application/vnd.php.serialized',
|
||||
'php' => 'text/plain',
|
||||
'html' => 'text/html',
|
||||
'csv' => 'application/csv',
|
||||
);
|
||||
|
||||
public function before()
|
||||
{
|
||||
parent::before();
|
||||
|
||||
// Some Methods cant have a body
|
||||
$this->request->body = null;
|
||||
|
||||
// Which format should the data be returned in?
|
||||
$this->request->lang = $this->_detect_lang();
|
||||
|
||||
$this->response = \Response::forge();
|
||||
}
|
||||
|
||||
public function after($response)
|
||||
{
|
||||
// If the response is an array
|
||||
if (is_array($response))
|
||||
{
|
||||
// set the response
|
||||
$response = $this->response($response);
|
||||
}
|
||||
|
||||
// If the response is a Response object, we will use their
|
||||
// instead of ours.
|
||||
if ( ! $response instanceof \Response)
|
||||
{
|
||||
$response = $this->response;
|
||||
}
|
||||
|
||||
return parent::after($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Router
|
||||
*
|
||||
* Requests are not made to methods directly The request will be for an "object".
|
||||
* this simply maps the object and method to the correct Controller method.
|
||||
*
|
||||
* @param string $resource
|
||||
* @param array $arguments
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public function router($resource, $arguments)
|
||||
{
|
||||
\Config::load('rest', true);
|
||||
|
||||
// If no (or an invalid) format is given, auto detect the format
|
||||
if (is_null($this->format) or ! array_key_exists($this->format, $this->_supported_formats))
|
||||
{
|
||||
// auto-detect the format
|
||||
$this->format = array_key_exists(\Input::extension(), $this->_supported_formats) ? \Input::extension() : $this->_detect_format();
|
||||
}
|
||||
|
||||
// Get the configured auth method if none is defined
|
||||
$this->auth === null and $this->auth = \Config::get('rest.auth');
|
||||
|
||||
//Check method is authorized if required, and if we're authorized
|
||||
if ($this->auth == 'basic')
|
||||
{
|
||||
$valid_login = $this->_prepare_basic_auth();
|
||||
}
|
||||
elseif ($this->auth == 'digest')
|
||||
{
|
||||
$valid_login = $this->_prepare_digest_auth();
|
||||
}
|
||||
elseif (method_exists($this, $this->auth))
|
||||
{
|
||||
if (($valid_login = $this->{$this->auth}()) instanceOf \Response)
|
||||
{
|
||||
return $valid_login;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$valid_login = false;
|
||||
}
|
||||
|
||||
//If the request passes auth then execute as normal
|
||||
if(empty($this->auth) or $valid_login)
|
||||
{
|
||||
// If they call user, go to $this->post_user();
|
||||
$controller_method = strtolower(\Input::method()) . '_' . $resource;
|
||||
|
||||
// Fall back to action_ if no rest method is provided
|
||||
if ( ! method_exists($this, $controller_method))
|
||||
{
|
||||
$controller_method = 'action_'.$resource;
|
||||
}
|
||||
|
||||
// If method is not available, set status code to 404
|
||||
if (method_exists($this, $controller_method))
|
||||
{
|
||||
return call_fuel_func_array(array($this, $controller_method), $arguments);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->response->status = $this->no_method_status;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->response(array('status'=> 0, 'error'=> 'Not Authorized'), 401);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Response
|
||||
*
|
||||
* Takes pure data and optionally a status code, then creates the response
|
||||
*
|
||||
* @param mixed
|
||||
* @param int
|
||||
* @return object Response instance
|
||||
*/
|
||||
protected function response($data = array(), $http_status = null)
|
||||
{
|
||||
// set the correct response header
|
||||
if (method_exists('Format', 'to_'.$this->format))
|
||||
{
|
||||
$this->response->set_header('Content-Type', $this->_supported_formats[$this->format]);
|
||||
}
|
||||
|
||||
// no data returned?
|
||||
if ((is_array($data) and empty($data)) or ($data == ''))
|
||||
{
|
||||
// override the http status with the NO CONTENT status
|
||||
$http_status = $this->no_data_status;
|
||||
}
|
||||
|
||||
// make sure we have a valid return status
|
||||
$http_status or $http_status = $this->http_status;
|
||||
|
||||
// If the format method exists, call and return the output in that format
|
||||
if (method_exists('Format', 'to_'.$this->format))
|
||||
{
|
||||
// Handle XML output
|
||||
if ($this->format === 'xml')
|
||||
{
|
||||
// Detect basenode
|
||||
$xml_basenode = $this->xml_basenode;
|
||||
$xml_basenode or $xml_basenode = \Config::get('rest.xml_basenode', 'xml');
|
||||
|
||||
// Set the XML response
|
||||
$this->response->body(\Format::forge($data)->{'to_'.$this->format}(null, null, $xml_basenode));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the formatted response
|
||||
$this->response->body(\Format::forge($data)->{'to_'.$this->format}());
|
||||
}
|
||||
}
|
||||
|
||||
// Format not supported, but the output is an array or an object that can not be cast to string
|
||||
elseif (is_array($data) or (is_object($data) and ! method_exists($data, '__toString')))
|
||||
{
|
||||
if (\Fuel::$env == \Fuel::PRODUCTION)
|
||||
{
|
||||
// not acceptable in production
|
||||
if ($http_status == 200)
|
||||
{ $http_status = 406;
|
||||
}
|
||||
$this->response->body('The requested REST method returned an array or object, which is not compatible with the output format "'.$this->format.'"');
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert it to json so we can at least read it while we're developing
|
||||
$this->response->body('The requested REST method returned an array or object:<br /><br />'.\Format::forge($data)->to_json(null, true));
|
||||
}
|
||||
}
|
||||
|
||||
// Format not supported, output directly
|
||||
else
|
||||
{
|
||||
$this->response->body($data);
|
||||
}
|
||||
|
||||
// Set the reponse http status
|
||||
$http_status and $this->response->status = $http_status;
|
||||
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Response http status.
|
||||
*
|
||||
* @param integer $status response http status code
|
||||
* @return void
|
||||
*/
|
||||
protected function http_status($status)
|
||||
{
|
||||
$this->http_status = $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect format
|
||||
*
|
||||
* Detect which format should be used to output the data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _detect_format()
|
||||
{
|
||||
// A format has been passed as a named parameter in the route
|
||||
if ($this->param('format') and array_key_exists($this->param('format'), $this->_supported_formats))
|
||||
{
|
||||
return $this->param('format');
|
||||
}
|
||||
|
||||
// A format has been passed as an argument in the URL and it is supported
|
||||
if (\Input::param('format') and array_key_exists(\Input::param('format'), $this->_supported_formats))
|
||||
{
|
||||
return \Input::param('format');
|
||||
}
|
||||
|
||||
// Otherwise, check the HTTP_ACCEPT (if it exists and we are allowed)
|
||||
if ($acceptable = \Input::server('HTTP_ACCEPT') and \Config::get('rest.ignore_http_accept') !== true)
|
||||
{
|
||||
// If anything is accepted, and we have a default, return that
|
||||
if ($acceptable == '*/*' and ! empty($this->rest_format))
|
||||
{
|
||||
return $this->rest_format;
|
||||
}
|
||||
|
||||
// Split the Accept header and build an array of quality scores for each format
|
||||
$fragments = new \CachingIterator(new \ArrayIterator(preg_split('/[,;]/', \Input::server('HTTP_ACCEPT'))));
|
||||
$acceptable = array();
|
||||
$next_is_quality = false;
|
||||
foreach ($fragments as $fragment)
|
||||
{
|
||||
$quality = 1;
|
||||
// Skip the fragment if it is a quality score
|
||||
if ($next_is_quality)
|
||||
{
|
||||
$next_is_quality = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If next fragment exists and is a quality score, set the quality score
|
||||
elseif ($fragments->hasNext())
|
||||
{
|
||||
$next = $fragments->getInnerIterator()->current();
|
||||
if (strpos($next, 'q=') === 0)
|
||||
{
|
||||
list($key, $quality) = explode('=', $next);
|
||||
$next_is_quality = true;
|
||||
}
|
||||
}
|
||||
|
||||
$acceptable[$fragment] = $quality;
|
||||
}
|
||||
|
||||
// Sort the formats by score in descending order
|
||||
uasort($acceptable, function($a, $b)
|
||||
{
|
||||
$a = (float) $a;
|
||||
$b = (float) $b;
|
||||
return ($a > $b) ? -1 : 1;
|
||||
});
|
||||
|
||||
// Check each of the acceptable formats against the supported formats
|
||||
$find = array('\*', '/');
|
||||
$replace = array('.*', '\/');
|
||||
foreach ($acceptable as $pattern => $quality)
|
||||
{
|
||||
// The Accept header can contain wildcards in the format
|
||||
$pattern = '/^' . str_replace($find, $replace, preg_quote($pattern)) . '$/';
|
||||
foreach ($this->_supported_formats as $format => $mime)
|
||||
{
|
||||
if (preg_match($pattern, $mime))
|
||||
{
|
||||
return $format;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // End HTTP_ACCEPT checking
|
||||
|
||||
// Well, none of that has worked! Let's see if the controller has a default
|
||||
if ( ! empty($this->rest_format))
|
||||
{
|
||||
return $this->rest_format;
|
||||
}
|
||||
|
||||
// Just use the default format
|
||||
return \Config::get('rest.default_format');
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect language(s)
|
||||
*
|
||||
* What language do they want it in?
|
||||
*
|
||||
* @return null|array|string
|
||||
*/
|
||||
protected function _detect_lang()
|
||||
{
|
||||
if (!$lang = \Input::server('HTTP_ACCEPT_LANGUAGE'))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// They might have sent a few, make it an array
|
||||
if (strpos($lang, ',') !== false)
|
||||
{
|
||||
$langs = explode(',', $lang);
|
||||
|
||||
$return_langs = array();
|
||||
|
||||
foreach ($langs as $lang)
|
||||
{
|
||||
// Remove weight and strip space
|
||||
list($lang) = explode(';', $lang);
|
||||
$return_langs[] = trim($lang);
|
||||
}
|
||||
|
||||
return $return_langs;
|
||||
}
|
||||
|
||||
// Nope, just return the string
|
||||
return $lang;
|
||||
}
|
||||
|
||||
// SECURITY FUNCTIONS ---------------------------------------------------------
|
||||
|
||||
protected function _check_login($username = '', $password = null)
|
||||
{
|
||||
if (empty($username))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$valid_logins = \Config::get('rest.valid_logins');
|
||||
|
||||
if (!array_key_exists($username, $valid_logins))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If actually null (not empty string) then do not check it
|
||||
if ($password !== null and $valid_logins[$username] != $password)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function _prepare_basic_auth()
|
||||
{
|
||||
$username = null;
|
||||
$password = null;
|
||||
|
||||
// mod_php
|
||||
if (\Input::server('PHP_AUTH_USER'))
|
||||
{
|
||||
$username = \Input::server('PHP_AUTH_USER');
|
||||
$password = \Input::server('PHP_AUTH_PW');
|
||||
}
|
||||
|
||||
// most other servers
|
||||
elseif (\Input::server('HTTP_AUTHENTICATION'))
|
||||
{
|
||||
if (strpos(strtolower(\Input::server('HTTP_AUTHENTICATION')), 'basic') === 0)
|
||||
{
|
||||
list($username, $password) = explode(':', base64_decode(substr(\Input::server('HTTP_AUTHORIZATION'), 6)));
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! static::_check_login($username, $password))
|
||||
{
|
||||
static::_force_login();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function _prepare_digest_auth()
|
||||
{
|
||||
// Empty argument for backward compatibility
|
||||
$uniqid = uniqid("");
|
||||
|
||||
// We need to test which server authentication variable to use
|
||||
// because the PHP ISAPI module in IIS acts different from CGI
|
||||
if (\Input::server('PHP_AUTH_DIGEST'))
|
||||
{
|
||||
$digest_string = \Input::server('PHP_AUTH_DIGEST');
|
||||
}
|
||||
elseif (\Input::server('HTTP_AUTHORIZATION'))
|
||||
{
|
||||
$digest_string = \Input::server('HTTP_AUTHORIZATION');
|
||||
}
|
||||
else
|
||||
{
|
||||
$digest_string = '';
|
||||
}
|
||||
|
||||
// Prompt for authentication if we don't have a digest string
|
||||
if (empty($digest_string))
|
||||
{
|
||||
static::_force_login($uniqid);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to retrieve authentication informations from the $digest_string variable
|
||||
$digest_params = explode(', ', $digest_string);
|
||||
foreach ($digest_params as $digest_param)
|
||||
{
|
||||
$digest_param = explode('=', $digest_param, 2);
|
||||
if (isset($digest_param[1]))
|
||||
{
|
||||
$digest[$digest_param[0]] = trim($digest_param[1], '"');
|
||||
}
|
||||
}
|
||||
|
||||
// if no username, or an invalid username found, re-authenticate
|
||||
if ( ! array_key_exists('username', $digest) or ! static::_check_login($digest['username']))
|
||||
{
|
||||
static::_force_login($uniqid);
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate the configured login/password
|
||||
$valid_logins = \Config::get('rest.valid_logins');
|
||||
$valid_pass = $valid_logins[$digest['username']];
|
||||
|
||||
// This is the valid response expected
|
||||
$A1 = md5($digest['username'] . ':' . \Config::get('rest.realm') . ':' . $valid_pass);
|
||||
$A2 = md5(strtoupper(\Input::method()) . ':' . $digest['uri']);
|
||||
$valid_response = md5($A1 . ':' . $digest['nonce'] . ':' . $digest['nc'] . ':' . $digest['cnonce'] . ':' . $digest['qop'] . ':' . $A2);
|
||||
|
||||
if ($digest['response'] != $valid_response)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function _force_login($nonce = '')
|
||||
{
|
||||
// Get the configured auth method if none is defined
|
||||
$this->auth === null and $this->auth = \Config::get('rest.auth');
|
||||
|
||||
if ($this->auth == 'basic')
|
||||
{
|
||||
$this->response->set_header('WWW-Authenticate', 'Basic realm="'. \Config::get('rest.realm') . '"');
|
||||
}
|
||||
elseif ($this->auth == 'digest')
|
||||
{
|
||||
$this->response->set_header('WWW-Authenticate', 'Digest realm="' . \Config::get('rest.realm') . '", qop="auth", nonce="' . $nonce . '", opaque="' . md5(\Config::get('rest.realm')) . '"');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Template Controller class
|
||||
*
|
||||
* A base controller for easily creating templated output.
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
* @author Fuel Development Team
|
||||
*/
|
||||
abstract class Controller_Template extends \Controller
|
||||
{
|
||||
/**
|
||||
* @var string page template
|
||||
*/
|
||||
public $template = 'template';
|
||||
|
||||
/**
|
||||
* Load the template and create the $this->template object
|
||||
*/
|
||||
public function before()
|
||||
{
|
||||
if ( ! empty($this->template) and is_string($this->template))
|
||||
{
|
||||
// Load the template
|
||||
$this->template = \View::forge($this->template);
|
||||
}
|
||||
|
||||
return parent::before();
|
||||
}
|
||||
|
||||
/**
|
||||
* After controller method has run output the template
|
||||
*
|
||||
* @param Response $response
|
||||
*/
|
||||
public function after($response)
|
||||
{
|
||||
// If nothing was returned default to the template
|
||||
if ($response === null)
|
||||
{
|
||||
$response = $this->template;
|
||||
}
|
||||
|
||||
return parent::after($response);
|
||||
}
|
||||
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Cookie class
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Helpers
|
||||
* @author Kohana Team
|
||||
* @modified Fuel Development Team
|
||||
* @copyright (c) 2008-2010 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
* @link http://docs.fuelphp.com/classes/cookie.html
|
||||
*/
|
||||
class Cookie
|
||||
{
|
||||
/**
|
||||
* @var array Cookie class configuration defaults
|
||||
*/
|
||||
protected static $config = array(
|
||||
'expiration' => 0,
|
||||
'path' => '/',
|
||||
'domain' => null,
|
||||
'secure' => false,
|
||||
'http_only' => false,
|
||||
);
|
||||
|
||||
/*
|
||||
* initialisation and auto configuration
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
static::$config = array_merge(static::$config, \Config::get('cookie', array()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a signed cookie. Cookies without signatures will not
|
||||
* be returned. If the cookie signature is present, but invalid, the cookie
|
||||
* will be deleted.
|
||||
*
|
||||
* // Get the "theme" cookie, or use "blue" if the cookie does not exist
|
||||
* $theme = Cookie::get('theme', 'blue');
|
||||
*
|
||||
* @param string $name cookie name
|
||||
* @param mixed $default default value to return
|
||||
* @return string
|
||||
*/
|
||||
public static function get($name = null, $default = null)
|
||||
{
|
||||
return \Input::cookie($name, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a signed cookie. Note that all cookie values must be strings and no
|
||||
* automatic serialization will be performed!
|
||||
*
|
||||
* // Set the "theme" cookie
|
||||
* Cookie::set('theme', 'red');
|
||||
*
|
||||
* @param string $name name of cookie
|
||||
* @param string $value value of cookie
|
||||
* @param integer $expiration lifetime in seconds
|
||||
* @param string $path path of the cookie
|
||||
* @param string $domain domain of the cookie
|
||||
* @param boolean $secure if true, the cookie should only be transmitted over a secure HTTPS connection
|
||||
* @param boolean $http_only if true, the cookie will be made accessible only through the HTTP protocol
|
||||
* @return boolean
|
||||
*/
|
||||
public static function set($name, $value, $expiration = null, $path = null, $domain = null, $secure = null, $http_only = null)
|
||||
{
|
||||
// you can't set cookies in CLi mode
|
||||
if (\Fuel::$is_cli)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = \Fuel::value($value);
|
||||
|
||||
// use the class defaults for the other parameters if not provided
|
||||
is_null($expiration) and $expiration = static::$config['expiration'];
|
||||
is_null($path) and $path = static::$config['path'];
|
||||
is_null($domain) and $domain = static::$config['domain'];
|
||||
is_null($secure) and $secure = static::$config['secure'];
|
||||
is_null($http_only) and $http_only = static::$config['http_only'];
|
||||
|
||||
// add the current time so we have an offset
|
||||
$expiration = $expiration > 0 ? $expiration + time() : 0;
|
||||
|
||||
return setcookie($name, $value, $expiration, $path, $domain, $secure, $http_only);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a cookie by making the value null and expiring it.
|
||||
*
|
||||
* Cookie::delete('theme');
|
||||
*
|
||||
* @param string $name cookie name
|
||||
* @param string $path path of the cookie
|
||||
* @param string $domain domain of the cookie
|
||||
* @param boolean $secure if true, the cookie should only be transmitted over a secure HTTPS connection
|
||||
* @param boolean $http_only if true, the cookie will be made accessible only through the HTTP protocol
|
||||
* @return boolean
|
||||
* @uses static::set
|
||||
*/
|
||||
public static function delete($name, $path = null, $domain = null, $secure = null, $http_only = null)
|
||||
{
|
||||
// Remove the cookie
|
||||
unset($_COOKIE[$name]);
|
||||
|
||||
// Nullify the cookie and make it expire
|
||||
return static::set($name, null, -86400, $path, $domain, $secure, $http_only);
|
||||
}
|
||||
}
|
@ -1,332 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
use \phpseclib\Crypt\AES;
|
||||
use \phpseclib\Crypt\Hash;
|
||||
|
||||
class Crypt
|
||||
{
|
||||
/**
|
||||
* Crypto default configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $defaults = array();
|
||||
|
||||
/**
|
||||
* Defined Crypto instances
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $_instances = array();
|
||||
|
||||
/**
|
||||
* initialisation and auto configuration
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
// load the config
|
||||
\Config::load('crypt', true);
|
||||
static::$defaults = \Config::get('crypt', array());
|
||||
|
||||
// generate random crypto keys if we don't have them or they are incorrect length
|
||||
$update = false;
|
||||
foreach(array('crypto_key', 'crypto_iv', 'crypto_hmac') as $key)
|
||||
{
|
||||
if ( empty(static::$defaults[$key]) or (strlen(static::$defaults[$key]) % 4) != 0)
|
||||
{
|
||||
$crypto = '';
|
||||
for ($i = 0; $i < 8; $i++)
|
||||
{
|
||||
$crypto .= static::safe_b64encode(pack('n', mt_rand(0, 0xFFFF)));
|
||||
}
|
||||
static::$defaults[$key] = $crypto;
|
||||
$update = true;
|
||||
}
|
||||
}
|
||||
|
||||
// update the config if needed
|
||||
if ($update === true)
|
||||
{
|
||||
try
|
||||
{
|
||||
\Config::save('crypt', static::$defaults);
|
||||
}
|
||||
catch (\FileAccessException $e)
|
||||
{
|
||||
// failed to write the config file, inform the user
|
||||
echo \View::forge('errors/crypt_keys', array(
|
||||
'keys' => static::$defaults,
|
||||
));
|
||||
die();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* forge
|
||||
*
|
||||
* create a new named instance
|
||||
*
|
||||
* @param string $name instance name
|
||||
* @param array $config optional runtime configuration
|
||||
* @return \Crypt
|
||||
*/
|
||||
public static function forge($name = '__default__', array $config = array())
|
||||
{
|
||||
if ( ! array_key_exists($name, static::$_instances))
|
||||
{
|
||||
static::$_instances[$name] = new static($config);
|
||||
}
|
||||
|
||||
return static::$_instances[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a specific named instance
|
||||
*
|
||||
* @param string $name instance name
|
||||
* @return mixed Crypt if the instance exists, false if not
|
||||
*/
|
||||
public static function instance($name = '__default__')
|
||||
{
|
||||
if ( ! array_key_exists($name, static::$_instances))
|
||||
{
|
||||
return static::forge($name);
|
||||
}
|
||||
|
||||
return static::$_instances[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* capture static calls to methods
|
||||
*
|
||||
* @param mixed $method
|
||||
* @param array $args The arguments will passed to $method.
|
||||
* @return mixed return value of $method.
|
||||
*/
|
||||
public static function __callstatic($method, $args)
|
||||
{
|
||||
// static method calls are called on the default instance
|
||||
return call_user_func_array(array(static::instance(), $method), $args);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* generate a URI safe base64 encoded string
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected static function safe_b64encode($value)
|
||||
{
|
||||
$data = base64_encode($value);
|
||||
$data = str_replace(array('+', '/', '='), array('-', '_', ''), $data);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* decode a URI safe base64 encoded string
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
protected static function safe_b64decode($value)
|
||||
{
|
||||
$data = str_replace(array('-', '_'), array('+', '/'), $value);
|
||||
$mod4 = strlen($data) % 4;
|
||||
if ($mod4)
|
||||
{
|
||||
$data .= substr('====', $mod4);
|
||||
}
|
||||
return base64_decode($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* compare two strings in a timing-insensitive way to prevent time-based attacks
|
||||
*
|
||||
* @param string $a
|
||||
* @param string $b
|
||||
* @return bool
|
||||
*/
|
||||
protected static function secure_compare($a, $b)
|
||||
{
|
||||
// make sure we're only comparing equal length strings
|
||||
if (strlen($a) !== strlen($b))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// and that all comparisons take equal time
|
||||
$result = 0;
|
||||
for ($i = 0; $i < strlen($a); $i++)
|
||||
{
|
||||
$result |= ord($a[$i]) ^ ord($b[$i]);
|
||||
}
|
||||
return $result === 0;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Crypto object used to encrypt/decrypt
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $crypter = null;
|
||||
|
||||
/**
|
||||
* Hash object used to generate hashes
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $hasher = null;
|
||||
|
||||
/**
|
||||
* Crypto configuration
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $config = array();
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $config = array())
|
||||
{
|
||||
$this->config = array_merge(static::$defaults, $config);
|
||||
|
||||
$this->crypter = new AES();
|
||||
$this->hasher = new Hash('sha256');
|
||||
|
||||
$this->crypter->enableContinuousBuffer();
|
||||
$this->hasher->setKey(static::safe_b64decode($this->config['crypto_hmac']));
|
||||
}
|
||||
|
||||
/**
|
||||
* encrypt a string value, optionally with a custom key
|
||||
*
|
||||
* @param string $value value to encrypt
|
||||
* @param string|bool $key optional custom key to be used for this encryption
|
||||
* @param int|bool $keylength optional key length
|
||||
* @return string encrypted value
|
||||
*/
|
||||
protected function encode($value, $key = false, $keylength = false)
|
||||
{
|
||||
if ( ! $key)
|
||||
{
|
||||
$key = static::safe_b64decode($this->config['crypto_key']);
|
||||
// Used for backwards compatibility with encrypted data prior
|
||||
// to FuelPHP 1.7.2, when phpseclib was updated, and became a
|
||||
// bit smarter about figuring out key lengths.
|
||||
$keylength = 128;
|
||||
}
|
||||
|
||||
if ($keylength)
|
||||
{
|
||||
$this->crypter->setKeyLength($keylength);
|
||||
}
|
||||
|
||||
$this->crypter->setKey($key);
|
||||
$this->crypter->setIV(static::safe_b64decode($this->config['crypto_iv']));
|
||||
|
||||
$value = $this->crypter->encrypt($value);
|
||||
return static::safe_b64encode($this->add_hmac($value));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* capture calls to normal methods
|
||||
*
|
||||
* @param mixed $method
|
||||
* @param array $args The arguments will passed to $method.
|
||||
* @return mixed return value of $method.
|
||||
* @throws \ErrorException
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
// validate the method called
|
||||
if ( ! in_array($method, array('encode', 'decode')))
|
||||
{
|
||||
throw new \ErrorException('Call to undefined method '.__CLASS__.'::'.$method.'()', E_ERROR, 0, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
// static method calls are called on the default instance
|
||||
return call_user_func_array(array($this, $method), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* decrypt a string value, optionally with a custom key
|
||||
*
|
||||
* @param string $value value to decrypt
|
||||
* @param string|bool $key optional custom key to be used for this encryption
|
||||
* @param int|bool $keylength optional key length
|
||||
* @access public
|
||||
* @return string encrypted value
|
||||
*/
|
||||
protected function decode($value, $key = false, $keylength = false)
|
||||
{
|
||||
if ( ! $key)
|
||||
{
|
||||
$key = static::safe_b64decode($this->config['crypto_key']);
|
||||
// Used for backwards compatibility with encrypted data prior
|
||||
// to FuelPHP 1.7.2, when phpseclib was updated, and became a
|
||||
// bit smarter about figuring out key lengths.
|
||||
$keylength = 128;
|
||||
}
|
||||
|
||||
if ($keylength)
|
||||
{
|
||||
$this->crypter->setKeyLength($keylength);
|
||||
}
|
||||
|
||||
$this->crypter->setKey($key);
|
||||
$this->crypter->setIV(static::safe_b64decode($this->config['crypto_iv']));
|
||||
|
||||
$value = static::safe_b64decode($value);
|
||||
if ($value = $this->validate_hmac($value))
|
||||
{
|
||||
return $this->crypter->decrypt($value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function add_hmac($value)
|
||||
{
|
||||
// calculate the hmac-sha256 hash of this value
|
||||
$hmac = static::safe_b64encode($this->hasher->hash($value));
|
||||
|
||||
// append it and return the hmac protected string
|
||||
return $value.$hmac;
|
||||
}
|
||||
|
||||
protected function validate_hmac($value)
|
||||
{
|
||||
// strip the hmac-sha256 hash from the value
|
||||
$hmac = substr($value, strlen($value)-43);
|
||||
|
||||
// and remove it from the value
|
||||
$value = substr($value, 0, strlen($value)-43);
|
||||
|
||||
// only return the value if it wasn't tampered with
|
||||
return (static::secure_compare(static::safe_b64encode($this->hasher->hash($value)), $hmac)) ? $value : false;
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,154 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @author cocteau666@gmail.com
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_Dblib_Connection extends \Database_PDO_Connection
|
||||
{
|
||||
/**
|
||||
* Stores the database configuration locally and name the instance.
|
||||
*
|
||||
* [!!] This method cannot be accessed directly, you must use [static::instance].
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $config
|
||||
*/
|
||||
protected function __construct($name, array $config)
|
||||
{
|
||||
// this driver only works on Windows
|
||||
if (php_uname('s') === 'Windows')
|
||||
{
|
||||
throw new \Database_Exception('The "Dblib" database driver does not work well on Windows. Use the "Sqlsrv" driver instead.');
|
||||
}
|
||||
|
||||
parent::__construct($name, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* List tables
|
||||
*
|
||||
* @param string $like
|
||||
*
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public function list_tables($like = null)
|
||||
{
|
||||
$query = "SELECT name FROM sys.objects WHERE type = 'U' AND name != 'sysdiagrams'";
|
||||
|
||||
if (is_string($like))
|
||||
{
|
||||
$query .= " AND name LIKE ".$this->quote($like);
|
||||
}
|
||||
|
||||
// Find all table names
|
||||
$result = $this->query(\DB::SELECT, $query, false);
|
||||
|
||||
$tables = array();
|
||||
foreach ($result as $row)
|
||||
{
|
||||
$tables[] = reset($row);
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* List table columns
|
||||
*
|
||||
* @param string $table table name
|
||||
* @param string $like column name pattern
|
||||
* @return array array of column structure
|
||||
*/
|
||||
public function list_columns($table, $like = null)
|
||||
{
|
||||
$query = "SELECT * FROM Sys.Columns WHERE id = object_id('" . $this->quote_table($table) . "')";
|
||||
|
||||
if (is_string($like))
|
||||
{
|
||||
// Search for column names
|
||||
$query .= " AND name LIKE ".$this->quote($like);
|
||||
}
|
||||
|
||||
// Find all column names
|
||||
$result = $this->query(\DB::SELECT, $query, false);
|
||||
|
||||
$count = 0;
|
||||
$columns = array();
|
||||
foreach ($result as $row)
|
||||
{
|
||||
list($type, $length) = $this->_parse_type($row['Type']);
|
||||
$column = $this->datatype($type);
|
||||
$column['name'] = $row['Field'];
|
||||
$column['default'] = $row['Default'];
|
||||
$column['data_type'] = $type;
|
||||
$column['null'] = ($row['Null'] == 'YES');
|
||||
$column['ordinal_position'] = ++$count;
|
||||
switch ($column['type'])
|
||||
{
|
||||
case 'float':
|
||||
if (isset($length))
|
||||
{
|
||||
list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length);
|
||||
}
|
||||
break;
|
||||
case 'int':
|
||||
if (isset($length))
|
||||
{
|
||||
$column['display'] = $length;
|
||||
}
|
||||
break;
|
||||
case 'string':
|
||||
switch ($column['data_type'])
|
||||
{
|
||||
case 'binary':
|
||||
case 'varbinary':
|
||||
$column['character_maximum_length'] = $length;
|
||||
break;
|
||||
case 'char':
|
||||
case 'varchar':
|
||||
$column['character_maximum_length'] = $length;
|
||||
case 'text':
|
||||
case 'tinytext':
|
||||
case 'mediumtext':
|
||||
case 'longtext':
|
||||
$column['collation_name'] = $row['Collation'];
|
||||
break;
|
||||
case 'enum':
|
||||
case 'set':
|
||||
$column['collation_name'] = $row['Collation'];
|
||||
$column['options'] = explode('\',\'', substr($length, 1, -1));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$column['comment'] = $row['Comment'];
|
||||
$column['extra'] = $row['Extra'];
|
||||
$column['key'] = $row['Key'];
|
||||
$column['privileges'] = $row['Privileges'];
|
||||
$columns[$row['Field']] = $column;
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the charset
|
||||
*
|
||||
* @param string $charset
|
||||
*/
|
||||
public function set_charset($charset)
|
||||
{
|
||||
// does not support charsets
|
||||
}
|
||||
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_Exception extends \FuelException {}
|
@ -1,59 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_Expression
|
||||
{
|
||||
// Raw expression string
|
||||
protected $_value;
|
||||
|
||||
/**
|
||||
* Sets the expression string.
|
||||
*
|
||||
* $expression = new Database_Expression('COUNT(users.id)');
|
||||
*
|
||||
* @param string $value expression string
|
||||
*/
|
||||
public function __construct($value)
|
||||
{
|
||||
// Set the expression string
|
||||
$this->_value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the expression value as a string.
|
||||
*
|
||||
* $sql = $expression->value();
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function value()
|
||||
{
|
||||
return (string) $this->_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the expression as a string.
|
||||
*
|
||||
* echo $expression;
|
||||
*
|
||||
* @return string
|
||||
* @uses Database_Expression::value
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->value();
|
||||
}
|
||||
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_MySQL_Connection extends \Database_PDO_Connection
|
||||
{
|
||||
/**
|
||||
* List tables
|
||||
*
|
||||
* @param string $like
|
||||
*
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public function list_tables($like = null)
|
||||
{
|
||||
$query = 'SHOW TABLES';
|
||||
|
||||
if (is_string($like))
|
||||
{
|
||||
$query .= ' LIKE ' . $this->quote($like);
|
||||
}
|
||||
|
||||
$q = $this->_connection->prepare($query);
|
||||
$q->execute();
|
||||
$result = $q->fetchAll();
|
||||
|
||||
$tables = array();
|
||||
foreach ($result as $row)
|
||||
{
|
||||
$tables[] = reset($row);
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
/**
|
||||
* Create a new PDO instance
|
||||
*
|
||||
* @return PDO
|
||||
*/
|
||||
protected function _connect()
|
||||
{
|
||||
// enable compression if needed
|
||||
if ($this->_config['connection']['compress'])
|
||||
{
|
||||
// use client compression with mysql or mysqli (doesn't work with mysqlnd)
|
||||
$this->_config['attrs'][\PDO::MYSQL_ATTR_COMPRESS] = true;
|
||||
}
|
||||
|
||||
// add the charset to the DSN if needed
|
||||
if ($this->_config['charset'] and strpos($this->_config['connection']['dsn'], ';charset=') === false)
|
||||
{
|
||||
$config['dsn'] .= ';charset='.$this->_config['charset'];
|
||||
}
|
||||
|
||||
// create the PDO instance
|
||||
parent::_connect();
|
||||
}
|
||||
|
||||
}
|
@ -1,582 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_MySQLi_Connection extends \Database_Connection
|
||||
{
|
||||
/**
|
||||
* @var \MySQLi Raw server connection
|
||||
*/
|
||||
protected $_connection;
|
||||
|
||||
/**
|
||||
* @var array Database in use by each connection
|
||||
*/
|
||||
protected static $_current_databases = array();
|
||||
|
||||
/**
|
||||
* @var bool Use SET NAMES to set the character set
|
||||
*/
|
||||
protected static $_set_names;
|
||||
|
||||
/**
|
||||
* @var string Identifier for this connection within the PHP driver
|
||||
*/
|
||||
protected $_connection_id;
|
||||
|
||||
/**
|
||||
* @var string MySQL uses a backtick for identifiers
|
||||
*/
|
||||
protected $_identifier = '`';
|
||||
|
||||
/**
|
||||
* @var string Which kind of DB is used
|
||||
*/
|
||||
public $_db_type = 'mysql';
|
||||
|
||||
public function connect()
|
||||
{
|
||||
if ($this->_connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (static::$_set_names === null)
|
||||
{
|
||||
// Determine if we can use mysqli_set_charset(), which is only
|
||||
// available on PHP 5.2.3+ when compiled against MySQL 5.0+
|
||||
static::$_set_names = ! function_exists('mysqli_set_charset');
|
||||
}
|
||||
|
||||
// Extract the connection parameters, adding required variables
|
||||
extract($this->_config['connection'] + array(
|
||||
'database' => '',
|
||||
'hostname' => '',
|
||||
'port' => '',
|
||||
'socket' => '',
|
||||
'username' => '',
|
||||
'password' => '',
|
||||
'persistent' => false,
|
||||
'compress' => true,
|
||||
));
|
||||
|
||||
try
|
||||
{
|
||||
if ($socket != '')
|
||||
{
|
||||
$port = null;
|
||||
}
|
||||
elseif ($port != '')
|
||||
{
|
||||
$socket = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$socket = null;
|
||||
$port = null;
|
||||
}
|
||||
if ($persistent)
|
||||
{
|
||||
// Create a persistent connection
|
||||
if ($compress)
|
||||
{
|
||||
$mysqli = mysqli_init();
|
||||
$mysqli->real_connect('p:'.$hostname, $username, $password, $database, $port, $socket, MYSQLI_CLIENT_COMPRESS);
|
||||
|
||||
$this->_connection = $mysqli;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_connection = new \MySQLi('p:'.$hostname, $username, $password, $database, $port, $socket);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a connection and force it to be a new link
|
||||
if ($compress)
|
||||
{
|
||||
$mysqli = mysqli_init();
|
||||
$mysqli->real_connect($hostname, $username, $password, $database, $port, $socket, MYSQLI_CLIENT_COMPRESS);
|
||||
|
||||
$this->_connection = $mysqli;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_connection = new \MySQLi($hostname, $username, $password, $database, $port, $socket);
|
||||
}
|
||||
}
|
||||
if ($this->_connection->error)
|
||||
{
|
||||
// Unable to connect, select database, etc
|
||||
throw new \Database_Exception(str_replace($password, str_repeat('*', 10), $this->_connection->error), $this->_connection->errno);
|
||||
}
|
||||
}
|
||||
catch (\ErrorException $e)
|
||||
{
|
||||
// No connection exists
|
||||
$this->_connection = null;
|
||||
|
||||
$error_code = is_numeric($e->getCode()) ? $e->getCode() : 0;
|
||||
throw new \Database_Exception(str_replace($password, str_repeat('*', 10), $e->getMessage()), $error_code, $e);
|
||||
}
|
||||
|
||||
// \xFF is a better delimiter, but the PHP driver uses underscore
|
||||
$this->_connection_id = sha1($hostname.'_'.$username.'_'.$password);
|
||||
|
||||
if ( ! empty($this->_config['charset']))
|
||||
{
|
||||
// Set the character set
|
||||
$this->set_charset($this->_config['charset']);
|
||||
}
|
||||
|
||||
static::$_current_databases[$this->_connection_id] = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the database
|
||||
*
|
||||
* @param string Database
|
||||
* @return void
|
||||
*/
|
||||
protected function _select_db($database)
|
||||
{
|
||||
if ($this->_config['connection']['database'] !== static::$_current_databases[$this->_connection_id])
|
||||
{
|
||||
if ($this->_connection->select_db($database) !== true)
|
||||
{
|
||||
// Unable to select database
|
||||
throw new \Database_Exception($this->_connection->error, $this->_connection->errno);
|
||||
}
|
||||
}
|
||||
|
||||
static::$_current_databases[$this->_connection_id] = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the database
|
||||
*
|
||||
* @throws \Exception when the mysql database is not disconnected properly
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Database is assumed disconnected
|
||||
$status = true;
|
||||
|
||||
if ($this->_connection instanceof \MySQLi)
|
||||
{
|
||||
if ($status = $this->_connection->close())
|
||||
{
|
||||
// clear the connection
|
||||
$this->_connection = null;
|
||||
|
||||
// and reset the savepoint depth
|
||||
$this->_transaction_depth = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// Database is probably not disconnected
|
||||
$status = ! ($this->_connection instanceof \MySQLi);
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function set_charset($charset)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
$status = $this->_connection->set_charset($charset);
|
||||
|
||||
if ($status === false)
|
||||
{
|
||||
throw new \Database_Exception($this->_connection->error, $this->_connection->errno);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute query
|
||||
*
|
||||
* @param integer $type query type (\DB::SELECT, \DB::INSERT, etc.)
|
||||
* @param string $sql SQL string
|
||||
* @param mixed $as_object used when query type is SELECT
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
*
|
||||
* @return mixed when SELECT then return an iterator of results,<br>
|
||||
* when UPDATE then return a list of insert id and rows created,<br>
|
||||
* in other case return the number of rows affected
|
||||
*/
|
||||
public function query($type, $sql, $as_object)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
if ($this->_connection)
|
||||
{
|
||||
// Make sure the connection is still alive
|
||||
if ( ! $this->_connection->ping())
|
||||
{
|
||||
throw new \Database_Exception($this->_connection->error.' [ '.$sql.' ]', $this->_connection->errno);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
if ( ! empty($this->_config['profiling']))
|
||||
{
|
||||
// Get the paths defined in config
|
||||
$paths = \Config::get('profiling_paths');
|
||||
|
||||
// Storage for the trace information
|
||||
$stacktrace = array();
|
||||
|
||||
// Get the execution trace of this query
|
||||
$include = false;
|
||||
foreach (debug_backtrace() as $index => $page)
|
||||
{
|
||||
// Skip first entry and entries without a filename
|
||||
if ($index > 0 and empty($page['file']) === false)
|
||||
{
|
||||
// Checks to see what paths you want backtrace
|
||||
foreach($paths as $index => $path)
|
||||
{
|
||||
if (strpos($page['file'], $path) !== false)
|
||||
{
|
||||
$include = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Only log if no paths we defined, or we have a path match
|
||||
if ($include or empty($paths))
|
||||
{
|
||||
$stacktrace[] = array('file' => \Fuel::clean_path($page['file']), 'line' => $page['line']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$benchmark = \Profiler::start($this->_instance, $sql, $stacktrace);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_config['connection']['persistent']) and $this->_config['connection']['database'] !== static::$_current_databases[$this->_connection_id])
|
||||
{
|
||||
// Select database on persistent connections
|
||||
$this->_select_db($this->_config['connection']['database']);
|
||||
}
|
||||
|
||||
// Execute the query
|
||||
if (($result = $this->_connection->query($sql)) === false)
|
||||
{
|
||||
if (isset($benchmark))
|
||||
{
|
||||
// This benchmark is worthless
|
||||
\Profiler::delete($benchmark);
|
||||
}
|
||||
|
||||
throw new \Database_Exception($this->_connection->error.' [ '.$sql.' ]', $this->_connection->errno);
|
||||
}
|
||||
|
||||
// check for multiresults, we don't support those at the moment
|
||||
while($this->_connection->more_results() and $this->_connection->next_result())
|
||||
{
|
||||
if ($more_result = $this->_connection->use_result())
|
||||
{
|
||||
throw new \Database_Exception('The MySQLi driver does not support multiple resultsets', 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($benchmark))
|
||||
{
|
||||
\Profiler::stop($benchmark);
|
||||
}
|
||||
|
||||
// Set the last query
|
||||
$this->last_query = $sql;
|
||||
|
||||
if ($type === \DB::SELECT)
|
||||
{
|
||||
// Return an iterator of results
|
||||
return new \Database_MySQLi_Result($result, $sql, $as_object);
|
||||
}
|
||||
elseif ($type === \DB::INSERT)
|
||||
{
|
||||
// Return a list of insert id and rows created
|
||||
return array(
|
||||
$this->_connection->insert_id,
|
||||
$this->_connection->affected_rows,
|
||||
);
|
||||
}
|
||||
elseif ($type === \DB::UPDATE or $type === \DB::DELETE)
|
||||
{
|
||||
// Return the number of rows affected
|
||||
return $this->_connection->affected_rows;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function datatype($type)
|
||||
{
|
||||
static $types = array(
|
||||
'blob' => array('type' => 'string', 'binary' => true, 'character_maximum_length' => '65535'),
|
||||
'bool' => array('type' => 'bool'),
|
||||
'bigint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '18446744073709551615'),
|
||||
'datetime' => array('type' => 'string'),
|
||||
'decimal unsigned' => array('type' => 'float', 'exact' => true, 'min' => '0'),
|
||||
'double' => array('type' => 'float'),
|
||||
'double precision unsigned' => array('type' => 'float', 'min' => '0'),
|
||||
'double unsigned' => array('type' => 'float', 'min' => '0'),
|
||||
'enum' => array('type' => 'string'),
|
||||
'fixed' => array('type' => 'float', 'exact' => true),
|
||||
'fixed unsigned' => array('type' => 'float', 'exact' => true, 'min' => '0'),
|
||||
'float unsigned' => array('type' => 'float', 'min' => '0'),
|
||||
'int unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'),
|
||||
'integer unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'),
|
||||
'longblob' => array('type' => 'string', 'binary' => true, 'character_maximum_length' => '4294967295'),
|
||||
'longtext' => array('type' => 'string', 'character_maximum_length' => '4294967295'),
|
||||
'mediumblob' => array('type' => 'string', 'binary' => true, 'character_maximum_length' => '16777215'),
|
||||
'mediumint' => array('type' => 'int', 'min' => '-8388608', 'max' => '8388607'),
|
||||
'mediumint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '16777215'),
|
||||
'mediumtext' => array('type' => 'string', 'character_maximum_length' => '16777215'),
|
||||
'national varchar' => array('type' => 'string'),
|
||||
'numeric unsigned' => array('type' => 'float', 'exact' => true, 'min' => '0'),
|
||||
'nvarchar' => array('type' => 'string'),
|
||||
'point' => array('type' => 'string', 'binary' => true),
|
||||
'real unsigned' => array('type' => 'float', 'min' => '0'),
|
||||
'set' => array('type' => 'string'),
|
||||
'smallint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '65535'),
|
||||
'text' => array('type' => 'string', 'character_maximum_length' => '65535'),
|
||||
'tinyblob' => array('type' => 'string', 'binary' => true, 'character_maximum_length' => '255'),
|
||||
'tinyint' => array('type' => 'int', 'min' => '-128', 'max' => '127'),
|
||||
'tinyint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '255'),
|
||||
'tinytext' => array('type' => 'string', 'character_maximum_length' => '255'),
|
||||
'varchar' => array('type' => 'string', 'exact' => true),
|
||||
'year' => array('type' => 'string'),
|
||||
);
|
||||
|
||||
$type = str_replace(' zerofill', '', $type);
|
||||
|
||||
if (isset($types[$type]))
|
||||
{
|
||||
return $types[$type];
|
||||
}
|
||||
|
||||
return parent::datatype($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* List tables
|
||||
*
|
||||
* @param string $like pattern of table name
|
||||
* @return array array of table names
|
||||
*/
|
||||
public function list_tables($like = null)
|
||||
{
|
||||
if (is_string($like))
|
||||
{
|
||||
// Search for table names
|
||||
$result = $this->query(\DB::SELECT, 'SHOW TABLES LIKE '.$this->quote($like), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find all table names
|
||||
$result = $this->query(\DB::SELECT, 'SHOW TABLES', false);
|
||||
}
|
||||
|
||||
$tables = array();
|
||||
foreach ($result as $row)
|
||||
{
|
||||
$tables[] = reset($row);
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* List table columns
|
||||
*
|
||||
* @param string $table table name
|
||||
* @param string $like column name pattern
|
||||
* @return array array of column structure
|
||||
*/
|
||||
public function list_columns($table, $like = null)
|
||||
{
|
||||
// Quote the table name
|
||||
$table = $this->quote_table($table);
|
||||
|
||||
if (is_string($like))
|
||||
{
|
||||
// Search for column names
|
||||
$result = $this->query(\DB::SELECT, 'SHOW FULL COLUMNS FROM '.$table.' LIKE '.$this->quote($like), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find all column names
|
||||
$result = $this->query(\DB::SELECT, 'SHOW FULL COLUMNS FROM '.$table, false);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$columns = array();
|
||||
foreach ($result as $row)
|
||||
{
|
||||
list($type, $length) = $this->_parse_type($row['Type']);
|
||||
|
||||
$column = $this->datatype($type);
|
||||
|
||||
$column['name'] = $row['Field'];
|
||||
$column['default'] = $row['Default'];
|
||||
$column['data_type'] = $type;
|
||||
$column['null'] = ($row['Null'] == 'YES');
|
||||
$column['ordinal_position'] = ++$count;
|
||||
|
||||
switch ($column['type'])
|
||||
{
|
||||
case 'float':
|
||||
if (isset($length))
|
||||
{
|
||||
list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length);
|
||||
}
|
||||
break;
|
||||
case 'int':
|
||||
if (isset($length))
|
||||
{
|
||||
// MySQL attribute
|
||||
$column['display'] = $length;
|
||||
}
|
||||
break;
|
||||
case 'string':
|
||||
switch ($column['data_type'])
|
||||
{
|
||||
case 'binary':
|
||||
case 'varbinary':
|
||||
$column['character_maximum_length'] = $length;
|
||||
break;
|
||||
|
||||
case 'char':
|
||||
case 'varchar':
|
||||
$column['character_maximum_length'] = $length;
|
||||
case 'text':
|
||||
case 'tinytext':
|
||||
case 'mediumtext':
|
||||
case 'longtext':
|
||||
$column['collation_name'] = $row['Collation'];
|
||||
break;
|
||||
|
||||
case 'enum':
|
||||
case 'set':
|
||||
$column['collation_name'] = $row['Collation'];
|
||||
$column['options'] = explode('\',\'', substr($length, 1, -1));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// MySQL attributes
|
||||
$column['comment'] = $row['Comment'];
|
||||
$column['extra'] = $row['Extra'];
|
||||
$column['key'] = $row['Key'];
|
||||
$column['privileges'] = $row['Privileges'];
|
||||
|
||||
$columns[$row['Field']] = $column;
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape query for sql
|
||||
*
|
||||
* @param mixed $value value of string castable
|
||||
* @return string escaped sql string
|
||||
*/
|
||||
public function escape($value)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
|
||||
if (($value = $this->_connection->real_escape_string((string) $value)) === false)
|
||||
{
|
||||
throw new \Database_Exception($this->_connection->error, $this->_connection->errno);
|
||||
}
|
||||
|
||||
// SQL standard is to use single-quotes for all values
|
||||
return "'$value'";
|
||||
}
|
||||
|
||||
public function error_info()
|
||||
{
|
||||
$errno = $this->_connection->errno;
|
||||
return array($errno, empty($errno) ? null : $errno, empty($errno) ? null : $this->_connection->error);
|
||||
}
|
||||
|
||||
protected function driver_start_transaction()
|
||||
{
|
||||
$this->query(0, 'START TRANSACTION', false);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function driver_commit()
|
||||
{
|
||||
$this->query(0, 'COMMIT', false);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function driver_rollback()
|
||||
{
|
||||
$this->query(0, 'ROLLBACK', false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets savepoint of the transaction
|
||||
*
|
||||
* @param string $name name of the savepoint
|
||||
* @return boolean true - savepoint was set successfully;
|
||||
* false - failed to set savepoint;
|
||||
*/
|
||||
protected function set_savepoint($name) {
|
||||
$this->query(0, 'SAVEPOINT LEVEL'.$name, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release savepoint of the transaction
|
||||
*
|
||||
* @param string $name name of the savepoint
|
||||
* @return boolean true - savepoint was set successfully;
|
||||
* false - failed to set savepoint;
|
||||
*/
|
||||
protected function release_savepoint($name) {
|
||||
$this->query(0, 'RELEASE SAVEPOINT LEVEL'.$name, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback savepoint of the transaction
|
||||
*
|
||||
* @param string $name name of the savepoint
|
||||
* @return boolean true - savepoint was set successfully;
|
||||
* false - failed to set savepoint;
|
||||
*/
|
||||
protected function rollback_savepoint($name) {
|
||||
$this->query(0, 'ROLLBACK TO SAVEPOINT LEVEL'.$name, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_MySQLi_Result extends \Database_Result
|
||||
{
|
||||
protected $_internal_row = 0;
|
||||
|
||||
public function __construct($result, $sql, $as_object)
|
||||
{
|
||||
parent::__construct($result, $sql, $as_object);
|
||||
|
||||
// Find the number of rows in the result
|
||||
$this->_total_rows = $result->num_rows;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->_result instanceof \MySQLi_Result)
|
||||
{
|
||||
$this->_result->free();
|
||||
}
|
||||
}
|
||||
|
||||
public function seek($offset)
|
||||
{
|
||||
if ($this->offsetExists($offset) and $this->_result->data_seek($offset))
|
||||
{
|
||||
// Set the current row to the offset
|
||||
$this->_current_row = $this->_internal_row = $offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function current()
|
||||
{
|
||||
if ($this->_current_row !== $this->_internal_row and ! $this->seek($this->_current_row))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Increment internal row for optimization assuming rows are fetched in order
|
||||
$this->_internal_row++;
|
||||
|
||||
if ($this->_as_object === true)
|
||||
{
|
||||
// Return an stdClass
|
||||
return $this->_result->fetch_object();
|
||||
}
|
||||
elseif (is_string($this->_as_object))
|
||||
{
|
||||
// Return an object of given class name
|
||||
//! TODO: add the $params parameter
|
||||
return $this->_result->fetch_object($this->_as_object);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return an array of the row
|
||||
return $this->_result->fetch_assoc();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,562 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_PDO_Connection extends \Database_Connection
|
||||
{
|
||||
/**
|
||||
* @var \PDO $_connection raw server connection
|
||||
*/
|
||||
protected $_connection;
|
||||
|
||||
/**
|
||||
* @var string $_identifier PDO uses no quoting by default for identifiers
|
||||
*/
|
||||
protected $_identifier = '';
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $config
|
||||
*/
|
||||
protected function __construct($name, array $config)
|
||||
{
|
||||
// construct a custom schema driver
|
||||
// $this->_schema = new \Database_Drivername_Schema($name, $this);
|
||||
|
||||
// call the parent consructor
|
||||
parent::__construct($name, $config);
|
||||
|
||||
if (isset($config['identifier']))
|
||||
{
|
||||
// Allow the identifier to be overloaded per-connection
|
||||
$this->_identifier = (string) $this->_config['identifier'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the database
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if ($this->_connection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure we have all connection parameters
|
||||
$this->_config = array_merge(array(
|
||||
'connection' => array(
|
||||
'dsn' => '',
|
||||
'hostname' => '',
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
'database' => '',
|
||||
'persistent' => false,
|
||||
'compress' => false,
|
||||
),
|
||||
'identifier' => '`',
|
||||
'table_prefix' => '',
|
||||
'charset' => 'utf8',
|
||||
'collation' => false,
|
||||
'enable_cache' => true,
|
||||
'profiling' => false,
|
||||
'readonly' => false,
|
||||
'attrs' => array(),
|
||||
), $this->_config);
|
||||
|
||||
// Force PDO to use exceptions for all errors
|
||||
$this->_config['attrs'] = array(
|
||||
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
|
||||
);
|
||||
|
||||
if ( ! empty($this->_config['connection']['persistent']))
|
||||
{
|
||||
// Make the connection persistent
|
||||
$this->_config['attrs'][\PDO::ATTR_PERSISTENT] = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Create a new PDO connection
|
||||
$this->_connect();
|
||||
}
|
||||
catch (\PDOException $e)
|
||||
{
|
||||
// and convert the exception in a database exception
|
||||
if ( ! is_numeric($error_code = $e->getCode()))
|
||||
{
|
||||
if ($this->_connection)
|
||||
{
|
||||
$error_code = $this->_connection->errorinfo();
|
||||
$error_code = $error_code[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$error_code = 0;
|
||||
}
|
||||
}
|
||||
throw new \Database_Exception(str_replace($this->_config['connection']['password'], str_repeat('*', 10), $e->getMessage()), $error_code, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
// destroy the PDO object
|
||||
$this->_connection = null;
|
||||
|
||||
// and reset the savepoint depth
|
||||
$this->_transaction_depth = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current PDO Driver name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function driver_name()
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
|
||||
// Getting driver name
|
||||
return $this->_connection->getAttribute(\PDO::ATTR_DRIVER_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the charset
|
||||
*
|
||||
* @param string $charset
|
||||
*/
|
||||
public function set_charset($charset)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
|
||||
if ($charset)
|
||||
{
|
||||
$this->_connection->exec('SET NAMES '.$this->quote($charset));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the database
|
||||
*
|
||||
* @param integer $type
|
||||
* @param string $sql
|
||||
* @param mixed $as_object
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
*/
|
||||
public function query($type, $sql, $as_object)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
|
||||
if ( ! empty($this->_config['profiling']))
|
||||
{
|
||||
// Get the paths defined in config
|
||||
$paths = \Config::get('profiling_paths');
|
||||
|
||||
// Storage for the trace information
|
||||
$stacktrace = array();
|
||||
|
||||
// Get the execution trace of this query
|
||||
$include = false;
|
||||
foreach (debug_backtrace() as $index => $page)
|
||||
{
|
||||
// Skip first entry and entries without a filename
|
||||
if ($index > 0 and empty($page['file']) === false)
|
||||
{
|
||||
// Checks to see what paths you want backtrace
|
||||
foreach($paths as $index => $path)
|
||||
{
|
||||
if (strpos($page['file'], $path) !== false)
|
||||
{
|
||||
$include = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Only log if no paths we defined, or we have a path match
|
||||
if ($include or empty($paths))
|
||||
{
|
||||
$stacktrace[] = array('file' => \Fuel::clean_path($page['file']), 'line' => $page['line']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$benchmark = \Profiler::start($this->_instance, $sql, $stacktrace);
|
||||
}
|
||||
|
||||
// run the query. if the connection is lost, try 3 times to reconnect
|
||||
$attempts = 3;
|
||||
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
// try to run the query
|
||||
$result = $this->_connection->query($sql);
|
||||
break;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// if failed and we have attempts left
|
||||
if ($attempts > 0)
|
||||
{
|
||||
// try reconnecting if it was a MySQL disconnected error
|
||||
if (strpos($e->getMessage(), '2006 MySQL') !== false)
|
||||
{
|
||||
$this->disconnect();
|
||||
$this->connect();
|
||||
}
|
||||
else
|
||||
{
|
||||
// other database error, cleanup the profiler
|
||||
isset($benchmark) and \Profiler::delete($benchmark);
|
||||
|
||||
// and convert the exception in a database exception
|
||||
if ( ! is_numeric($error_code = $e->getCode()))
|
||||
{
|
||||
if ($this->_connection)
|
||||
{
|
||||
$error_code = $this->_connection->errorinfo();
|
||||
$error_code = $error_code[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$error_code = 0;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Database_Exception($e->getMessage().' with query: "'.$sql.'"', $error_code, $e);
|
||||
}
|
||||
}
|
||||
|
||||
// no more attempts left, bail out
|
||||
else
|
||||
{
|
||||
// and convert the exception in a database exception
|
||||
if ( ! is_numeric($error_code = $e->getCode()))
|
||||
{
|
||||
if ($this->_connection)
|
||||
{
|
||||
$error_code = $this->_connection->errorinfo();
|
||||
$error_code = $error_code[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$error_code = 0;
|
||||
}
|
||||
}
|
||||
throw new \Database_Exception($e->getMessage().' with query: "'.$sql.'"', $error_code, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
while ($attempts-- > 0);
|
||||
|
||||
if (isset($benchmark))
|
||||
{
|
||||
\Profiler::stop($benchmark);
|
||||
}
|
||||
|
||||
// Set the last query
|
||||
$this->last_query = $sql;
|
||||
|
||||
if ($type === \DB::SELECT)
|
||||
{
|
||||
// Convert the result into an array, as PDOStatement::rowCount is not reliable
|
||||
if ($as_object === false)
|
||||
{
|
||||
$result = $result->fetchAll(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
elseif (is_string($as_object))
|
||||
{
|
||||
$result = $result->fetchAll(\PDO::FETCH_CLASS, $as_object);
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = $result->fetchAll(\PDO::FETCH_CLASS, 'stdClass');
|
||||
}
|
||||
|
||||
// Return an iterator of results
|
||||
return new \Database_Result_Cached($result, $sql, $as_object);
|
||||
}
|
||||
elseif ($type === \DB::INSERT)
|
||||
{
|
||||
// Return a list of insert id and rows created
|
||||
return array(
|
||||
$this->_connection->lastInsertId(),
|
||||
$result->rowCount(),
|
||||
);
|
||||
}
|
||||
elseif ($type === \DB::UPDATE or $type === \DB::DELETE)
|
||||
{
|
||||
// Return the number of rows affected
|
||||
return $result->errorCode() === '00000' ? $result->rowCount() : -1;
|
||||
}
|
||||
|
||||
return $result->errorCode() === '00000' ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* List tables
|
||||
*
|
||||
* @param string $like
|
||||
*
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public function list_tables($like = null)
|
||||
{
|
||||
throw new \FuelException('Database method '.__METHOD__.' is not supported by '.__CLASS__);
|
||||
}
|
||||
|
||||
/**
|
||||
* List table columns
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $like
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function list_columns($table, $like = null)
|
||||
{
|
||||
$this->_connection or $this->connect();
|
||||
$q = $this->_connection->prepare("DESCRIBE ".$table);
|
||||
$q->execute();
|
||||
$result = $q->fetchAll();
|
||||
$count = 0;
|
||||
$columns = array();
|
||||
! is_null($like) and $like = str_replace('%', '.*', $like);
|
||||
foreach ($result as $row)
|
||||
{
|
||||
if ( ! is_null($like) and ! preg_match('#'.$like.'#', $row['Field']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
list($type, $length) = $this->_parse_type($row['Type']);
|
||||
|
||||
$column = $this->datatype($type);
|
||||
|
||||
$column['name'] = $row['Field'];
|
||||
$column['default'] = $row['Default'];
|
||||
$column['data_type'] = $type;
|
||||
$column['null'] = ($row['Null'] == 'YES');
|
||||
$column['ordinal_position'] = ++$count;
|
||||
switch ($column['type'])
|
||||
{
|
||||
case 'float':
|
||||
if (isset($length))
|
||||
{
|
||||
list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length);
|
||||
}
|
||||
break;
|
||||
case 'int':
|
||||
if (isset($length))
|
||||
{
|
||||
// MySQL attribute
|
||||
$column['display'] = $length;
|
||||
}
|
||||
break;
|
||||
case 'string':
|
||||
switch ($column['data_type'])
|
||||
{
|
||||
case 'binary':
|
||||
case 'varbinary':
|
||||
$column['character_maximum_length'] = $length;
|
||||
break;
|
||||
|
||||
case 'char':
|
||||
case 'varchar':
|
||||
$column['character_maximum_length'] = $length;
|
||||
case 'text':
|
||||
case 'tinytext':
|
||||
case 'mediumtext':
|
||||
case 'longtext':
|
||||
$column['collation_name'] = isset($row['Collation']) ? $row['Collation'] : null;
|
||||
break;
|
||||
|
||||
case 'enum':
|
||||
case 'set':
|
||||
$column['collation_name'] = isset($row['Collation']) ? $row['Collation'] : null;
|
||||
$column['options'] = explode('\',\'', substr($length, 1, - 1));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// MySQL attributes
|
||||
$column['comment'] = isset($row['Comment']) ? $row['Comment'] : null;
|
||||
$column['extra'] = $row['Extra'];
|
||||
$column['key'] = $row['Key'];
|
||||
$column['privileges'] = isset($row['Privileges']) ? $row['Privileges'] : null;
|
||||
|
||||
$columns[$row['Field']] = $column;
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a datatype
|
||||
*
|
||||
* @param integer $type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function datatype($type)
|
||||
{
|
||||
// try to determine the datatype
|
||||
$datatype = parent::datatype($type);
|
||||
|
||||
// if not an ANSI database, assume it's string
|
||||
return empty($datatype) ? array('type' => 'string') : $datatype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape a value
|
||||
*
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function escape($value)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
|
||||
$result = $this->_connection->quote($value);
|
||||
|
||||
// poor-mans workaround for the fact that not all drivers implement quote()
|
||||
if (empty($result))
|
||||
{
|
||||
if ( ! is_numeric($value))
|
||||
{
|
||||
$result = "'".str_replace("'", "''", $value)."'";
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve error info
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function error_info()
|
||||
{
|
||||
return $this->_connection->errorInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new PDO instance
|
||||
*
|
||||
* @return PDO
|
||||
*/
|
||||
protected function _connect()
|
||||
{
|
||||
$this->_connection = new \PDO(
|
||||
$this->_config['connection']['dsn'],
|
||||
$this->_config['connection']['username'],
|
||||
$this->_config['connection']['password'],
|
||||
$this->_config['attrs']
|
||||
);
|
||||
|
||||
// set the DB charset if needed
|
||||
$this->set_charset($this->_config['charset']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a transaction
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function driver_start_transaction()
|
||||
{
|
||||
$this->_connection or $this->connect();
|
||||
return $this->_connection->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit a transaction
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function driver_commit()
|
||||
{
|
||||
return $this->_connection->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback a transaction
|
||||
* @return bool
|
||||
*/
|
||||
protected function driver_rollback()
|
||||
{
|
||||
return $this->_connection->rollBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets savepoint of the transaction
|
||||
*
|
||||
* @param string $name name of the savepoint
|
||||
* @return boolean true - savepoint was set successfully;
|
||||
* false - failed to set savepoint;
|
||||
* null - RDBMS does not support savepoints
|
||||
*/
|
||||
protected function set_savepoint($name)
|
||||
{
|
||||
$result = $this->_connection->exec('SAVEPOINT '.$name);
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release savepoint of the transaction
|
||||
*
|
||||
* @param string $name name of the savepoint
|
||||
* @return boolean true - savepoint was set successfully;
|
||||
* false - failed to set savepoint;
|
||||
* null - RDBMS does not support savepoints
|
||||
*/
|
||||
protected function release_savepoint($name)
|
||||
{
|
||||
$result = $this->_connection->exec('RELEASE SAVEPOINT '.$name);
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback savepoint of the transaction
|
||||
*
|
||||
* @param string $name name of the savepoint
|
||||
* @return boolean true - savepoint was set successfully;
|
||||
* false - failed to set savepoint;
|
||||
* null - RDBMS does not support savepoints
|
||||
*/
|
||||
protected function rollback_savepoint($name)
|
||||
{
|
||||
$result = $this->_connection->exec('ROLLBACK TO SAVEPOINT '.$name);
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
}
|
@ -1,325 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_Query
|
||||
{
|
||||
/**
|
||||
* @var int Query type
|
||||
*/
|
||||
protected $_type;
|
||||
|
||||
/**
|
||||
* @var int Cache lifetime
|
||||
*/
|
||||
protected $_lifetime;
|
||||
|
||||
/**
|
||||
* @var string Cache key
|
||||
*/
|
||||
protected $_cache_key = null;
|
||||
|
||||
/**
|
||||
* @var boolean Cache all results
|
||||
*/
|
||||
protected $_cache_all = true;
|
||||
|
||||
/**
|
||||
* @var string SQL statement
|
||||
*/
|
||||
protected $_sql;
|
||||
|
||||
/**
|
||||
* @var array Quoted query parameters
|
||||
*/
|
||||
protected $_parameters = array();
|
||||
|
||||
/**
|
||||
* @var bool Return results as associative arrays or objects
|
||||
*/
|
||||
protected $_as_object = false;
|
||||
|
||||
/**
|
||||
* @var Database_Connection Connection to use when compiling the SQL
|
||||
*/
|
||||
protected $_connection = null;
|
||||
|
||||
/**
|
||||
* Creates a new SQL query of the specified type.
|
||||
*
|
||||
* @param string $sql query string
|
||||
* @param integer $type query type: DB::SELECT, DB::INSERT, etc
|
||||
*/
|
||||
public function __construct($sql, $type = null)
|
||||
{
|
||||
$this->_type = $type;
|
||||
$this->_sql = $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL query string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final public function __toString()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Return the SQL string
|
||||
return $this->compile();
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the query.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return $this->_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the query to be cached for a specified amount of time.
|
||||
*
|
||||
* @param integer $lifetime number of seconds to cache or null for default
|
||||
* @param string $cache_key name of the cache key to be used or null for default
|
||||
* @param boolean $cache_all if true, cache all results, even empty ones
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function cached($lifetime = null, $cache_key = null, $cache_all = true)
|
||||
{
|
||||
$this->_lifetime = $lifetime;
|
||||
$this->_cache_all = (bool) $cache_all;
|
||||
is_string($cache_key) and $this->_cache_key = $cache_key;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns results as associative arrays
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function as_assoc()
|
||||
{
|
||||
$this->_as_object = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns results as objects
|
||||
*
|
||||
* @param mixed $class classname or true for stdClass
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function as_object($class = true)
|
||||
{
|
||||
$this->_as_object = $class;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a parameter in the query.
|
||||
*
|
||||
* @param string $param parameter key to replace
|
||||
* @param mixed $value value to use
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function param($param, $value)
|
||||
{
|
||||
// Add or overload a new parameter
|
||||
$this->_parameters[$param] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a variable to a parameter in the query.
|
||||
*
|
||||
* @param string $param parameter key to replace
|
||||
* @param mixed $var variable to use
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function bind($param, & $var)
|
||||
{
|
||||
// Bind a value to a variable
|
||||
$this->_parameters[$param] =& $var;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple parameters to the query.
|
||||
*
|
||||
* @param array $params list of parameters
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function parameters(array $params)
|
||||
{
|
||||
// Merge the new parameters in
|
||||
$this->_parameters = $params + $this->_parameters;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a DB connection to use when compiling the SQL
|
||||
*
|
||||
* @param mixed $db
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function set_connection($db)
|
||||
{
|
||||
if ( ! $db instanceof \Database_Connection)
|
||||
{
|
||||
// Get the database instance
|
||||
$db = \Database_Connection::instance($db);
|
||||
}
|
||||
$this->_connection = $db;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL query and return it. Replaces any parameters with their
|
||||
* given values.
|
||||
*
|
||||
* @param mixed $db Database instance or instance name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compile($db = null)
|
||||
{
|
||||
if ($this->_connection !== null and $db === null)
|
||||
{
|
||||
$db = $this->_connection;
|
||||
}
|
||||
|
||||
if ( ! $db instanceof \Database_Connection)
|
||||
{
|
||||
// Get the database instance
|
||||
$db = $this->_connection ?: \Database_Connection::instance($db);
|
||||
}
|
||||
|
||||
// Import the SQL locally
|
||||
$sql = $this->_sql;
|
||||
|
||||
if ( ! empty($this->_parameters))
|
||||
{
|
||||
// Quote all of the values
|
||||
$values = array_map(array($db, 'quote'), $this->_parameters);
|
||||
|
||||
// Replace the values in the SQL
|
||||
$sql = \Str::tr($sql, $values);
|
||||
}
|
||||
|
||||
return trim($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the current query on the given database.
|
||||
*
|
||||
* @param mixed $db Database instance or name of instance
|
||||
*
|
||||
* @return object Database_Result for SELECT queries
|
||||
* @return mixed the insert id for INSERT queries
|
||||
* @return integer number of affected rows for all other queries
|
||||
*/
|
||||
public function execute($db = null)
|
||||
{
|
||||
if ($this->_connection !== null and $db === null)
|
||||
{
|
||||
$db = $this->_connection;
|
||||
}
|
||||
|
||||
if ( ! is_object($db))
|
||||
{
|
||||
// Get the database instance. If this query is a instance of
|
||||
// Database_Query_Builder_Select then use the slave connection if configured
|
||||
$db = \Database_Connection::instance($db, null, ! $this instanceof \Database_Query_Builder_Select);
|
||||
}
|
||||
|
||||
// Compile the SQL query
|
||||
$sql = $this->compile($db);
|
||||
|
||||
// make sure we have a SQL type to work with
|
||||
if (is_null($this->_type))
|
||||
{
|
||||
// get the SQL statement type without having to duplicate the entire statement
|
||||
$stmt = preg_split("/[\s]+/", substr($sql, 0, 10), 2);
|
||||
switch(strtoupper(reset($stmt)))
|
||||
{
|
||||
case 'DESCRIBE':
|
||||
case 'EXECUTE':
|
||||
case 'EXPLAIN':
|
||||
case 'SELECT':
|
||||
case 'SHOW':
|
||||
$this->_type = \DB::SELECT;
|
||||
break;
|
||||
case 'INSERT':
|
||||
case 'REPLACE':
|
||||
$this->_type = \DB::INSERT;
|
||||
break;
|
||||
case 'UPDATE':
|
||||
$this->_type = \DB::UPDATE;
|
||||
break;
|
||||
case 'DELETE':
|
||||
$this->_type = \DB::DELETE;
|
||||
break;
|
||||
default:
|
||||
$this->_type = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ($db->caching() and ! empty($this->_lifetime) and $this->_type === \DB::SELECT)
|
||||
{
|
||||
$cache_key = empty($this->_cache_key) ?
|
||||
'db.'.md5('Database_Connection::query("'.$db.'", "'.$sql.'")') : $this->_cache_key;
|
||||
$cache = \Cache::forge($cache_key);
|
||||
try
|
||||
{
|
||||
$result = $cache->get();
|
||||
return new \Database_Result_Cached($result, $sql, $this->_as_object);
|
||||
}
|
||||
catch (\CacheNotFoundException $e) {}
|
||||
}
|
||||
|
||||
// Execute the query
|
||||
\DB::$query_count++;
|
||||
$result = $db->query($this->_type, $sql, $this->_as_object);
|
||||
|
||||
// Cache the result if needed
|
||||
if (isset($cache) and ($this->_cache_all or $result->count()))
|
||||
{
|
||||
$cache->set_expiration($this->_lifetime)->set_contents($result->as_array())->set();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
abstract class Database_Query_Builder extends \Database_Query
|
||||
{
|
||||
/**
|
||||
* Compiles an array of JOIN statements into an SQL partial.
|
||||
*
|
||||
* @param object $db Database instance
|
||||
* @param array $joins join statements
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _compile_join(\Database_Connection$db, array $joins)
|
||||
{
|
||||
$statements = array();
|
||||
|
||||
foreach ($joins as $join)
|
||||
{
|
||||
// Compile each of the join statements
|
||||
$statements[] = $join->compile($db);
|
||||
}
|
||||
|
||||
return implode(' ', $statements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles an array of conditions into an SQL partial. Used for WHERE
|
||||
* and HAVING.
|
||||
*
|
||||
* @param object $db Database instance
|
||||
* @param array $conditions condition statements
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _compile_conditions(\Database_Connection$db, array $conditions)
|
||||
{
|
||||
$last_condition = NULL;
|
||||
|
||||
$sql = '';
|
||||
foreach ($conditions as $group)
|
||||
{
|
||||
// Process groups of conditions
|
||||
foreach ($group as $logic => $condition)
|
||||
{
|
||||
if ($condition === '(')
|
||||
{
|
||||
if ( ! empty($sql) AND $last_condition !== '(')
|
||||
{
|
||||
// Include logic operator
|
||||
$sql .= ' '.$logic.' ';
|
||||
}
|
||||
|
||||
$sql .= '(';
|
||||
}
|
||||
elseif ($condition === ')')
|
||||
{
|
||||
$sql .= ')';
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! empty($sql) AND $last_condition !== '(')
|
||||
{
|
||||
// Add the logic operator
|
||||
$sql .= ' '.$logic.' ';
|
||||
}
|
||||
|
||||
// Split the condition
|
||||
list($column, $op, $value) = $condition;
|
||||
|
||||
// Support DB::expr() as where clause
|
||||
if ($column instanceOf Database_Expression and $op === null and $value === null)
|
||||
{
|
||||
$sql .= (string) $column;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($value === NULL)
|
||||
{
|
||||
if ($op === '=')
|
||||
{
|
||||
// Convert "val = NULL" to "val IS NULL"
|
||||
$op = 'IS';
|
||||
}
|
||||
elseif ($op === '!=')
|
||||
{
|
||||
// Convert "val != NULL" to "valu IS NOT NULL"
|
||||
$op = 'IS NOT';
|
||||
}
|
||||
}
|
||||
|
||||
// Database operators are always uppercase
|
||||
$op = strtoupper($op);
|
||||
|
||||
if (($op === 'BETWEEN' OR $op === 'NOT BETWEEN') AND is_array($value))
|
||||
{
|
||||
// BETWEEN always has exactly two arguments
|
||||
list($min, $max) = $value;
|
||||
|
||||
if (is_string($min) AND array_key_exists($min, $this->_parameters))
|
||||
{
|
||||
// Set the parameter as the minimum
|
||||
$min = $this->_parameters[$min];
|
||||
}
|
||||
|
||||
if (is_string($max) AND array_key_exists($max, $this->_parameters))
|
||||
{
|
||||
// Set the parameter as the maximum
|
||||
$max = $this->_parameters[$max];
|
||||
}
|
||||
|
||||
// Quote the min and max value
|
||||
$value = $db->quote($min).' AND '.$db->quote($max);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_string($value) AND array_key_exists($value, $this->_parameters))
|
||||
{
|
||||
// Set the parameter as the value
|
||||
$value = $this->_parameters[$value];
|
||||
}
|
||||
|
||||
// Quote the entire value normally
|
||||
$value = $db->quote($value);
|
||||
}
|
||||
|
||||
// Append the statement to the query
|
||||
$sql .= $db->quote_identifier($column).' '.$op.' '.$value;
|
||||
}
|
||||
}
|
||||
|
||||
$last_condition = $condition;
|
||||
}
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles an array of set values into an SQL partial. Used for UPDATE.
|
||||
*
|
||||
* @param object $db Database instance
|
||||
* @param array $values updated values
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _compile_set(\Database_Connection$db, array $values)
|
||||
{
|
||||
$set = array();
|
||||
foreach ($values as $group)
|
||||
{
|
||||
// Split the set
|
||||
list($column, $value) = $group;
|
||||
|
||||
// Quote the column name
|
||||
$column = $db->quote_identifier($column);
|
||||
|
||||
if (is_string($value) AND array_key_exists($value, $this->_parameters))
|
||||
{
|
||||
// Use the parameter value
|
||||
$value = $this->_parameters[$value];
|
||||
}
|
||||
|
||||
$set[$column] = $column.' = '.$db->quote($value);
|
||||
}
|
||||
|
||||
return implode(', ', $set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles an array of ORDER BY statements into an SQL partial.
|
||||
*
|
||||
* @param object $db Database instance
|
||||
* @param array $columns sorting columns
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _compile_order_by(\Database_Connection $db, array $columns)
|
||||
{
|
||||
$sort = array();
|
||||
|
||||
foreach ($columns as $group)
|
||||
{
|
||||
list($column, $direction) = $group;
|
||||
|
||||
$direction = strtoupper($direction);
|
||||
if ( ! empty($direction))
|
||||
{
|
||||
// Make the direction uppercase
|
||||
$direction = ' '.($direction == 'ASC' ? 'ASC' : 'DESC');
|
||||
}
|
||||
|
||||
$sort[] = $db->quote_identifier($column).$direction;
|
||||
}
|
||||
|
||||
return 'ORDER BY '.implode(', ', $sort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the current builder status.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
abstract public function reset();
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_Query_Builder_Delete extends \Database_Query_Builder_Where
|
||||
{
|
||||
// DELETE FROM ...
|
||||
protected $_table;
|
||||
|
||||
/**
|
||||
* Set the table for a delete.
|
||||
*
|
||||
* @param mixed $table table name or array($table, $alias) or object
|
||||
*/
|
||||
public function __construct($table = null)
|
||||
{
|
||||
if ($table)
|
||||
{
|
||||
// Set the initial table name
|
||||
$this->_table = $table;
|
||||
}
|
||||
|
||||
// Start the query with no SQL
|
||||
parent::__construct('', \DB::DELETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the table to delete from.
|
||||
*
|
||||
* @param mixed $table table name or array($table, $alias) or object
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function table($table)
|
||||
{
|
||||
$this->_table = $table;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL query and return it.
|
||||
*
|
||||
* @param mixed $db Database_Connection instance or instance name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compile($db = null)
|
||||
{
|
||||
if ( ! $db instanceof \Database_Connection)
|
||||
{
|
||||
// Get the database instance
|
||||
$db = \Database_Connection::instance($db);
|
||||
}
|
||||
|
||||
// Start a deletion query
|
||||
$query = 'DELETE FROM '.$db->quote_table($this->_table);
|
||||
|
||||
if ( ! empty($this->_where))
|
||||
{
|
||||
// Add deletion conditions
|
||||
$query .= ' WHERE '.$this->_compile_conditions($db, $this->_where);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_order_by))
|
||||
{
|
||||
// Add sorting
|
||||
$query .= ' '.$this->_compile_order_by($db, $this->_order_by);
|
||||
}
|
||||
|
||||
if ($this->_limit !== null)
|
||||
{
|
||||
// Add limiting
|
||||
$query .= ' LIMIT '.$this->_limit;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the query parameters
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->_table = NULL;
|
||||
|
||||
$this->_where = array();
|
||||
$this->_order_by = array();
|
||||
|
||||
$this->_parameters = array();
|
||||
|
||||
$this->_limit = NULL;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,219 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_Query_Builder_Insert extends \Database_Query_Builder
|
||||
{
|
||||
/**
|
||||
* @var string $_table table
|
||||
*/
|
||||
protected $_table;
|
||||
|
||||
/**
|
||||
* @var array $_columns columns
|
||||
*/
|
||||
protected $_columns = array();
|
||||
|
||||
/**
|
||||
* @var array $_values values
|
||||
*/
|
||||
protected $_values = array();
|
||||
|
||||
/**
|
||||
* Set the table and columns for an insert.
|
||||
*
|
||||
* @param mixed $table table name or array($table, $alias) or object
|
||||
* @param array $columns column names
|
||||
*/
|
||||
public function __construct($table = null, array $columns = null)
|
||||
{
|
||||
if ($table)
|
||||
{
|
||||
// Set the initial table name
|
||||
$this->_table = $table;
|
||||
}
|
||||
|
||||
if ($columns)
|
||||
{
|
||||
// Set the column names
|
||||
$this->_columns = $columns;
|
||||
}
|
||||
|
||||
// Start the query with no SQL
|
||||
parent::__construct('', \DB::INSERT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the table to insert into.
|
||||
*
|
||||
* @param mixed $table table name or array($table, $alias) or object
|
||||
* @return $this
|
||||
*/
|
||||
public function table($table)
|
||||
{
|
||||
$this->_table = $table;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the columns that will be inserted.
|
||||
*
|
||||
* @param array $columns column names
|
||||
* @return $this
|
||||
*/
|
||||
public function columns(array $columns)
|
||||
{
|
||||
$this->_columns = array_merge($this->_columns, $columns);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds values. Multiple value sets can be added.
|
||||
*
|
||||
* @throws \FuelException
|
||||
* @param array $values
|
||||
* @return $this
|
||||
*/
|
||||
public function values(array $values)
|
||||
{
|
||||
if ( ! is_array($this->_values))
|
||||
{
|
||||
throw new \FuelException('INSERT INTO ... SELECT statements cannot be combined with INSERT INTO ... VALUES');
|
||||
}
|
||||
|
||||
// Get all of the passed values
|
||||
$values = func_get_args();
|
||||
|
||||
// And process them
|
||||
foreach ($values as $value)
|
||||
{
|
||||
if (is_array(reset($value)))
|
||||
{
|
||||
$this->_values = array_merge($this->_values, $value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_values[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a wrapper function for calling columns() and values().
|
||||
*
|
||||
* @param array $pairs column value pairs
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function set(array $pairs)
|
||||
{
|
||||
$this->columns(array_keys($pairs));
|
||||
$this->values($pairs);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a sub-query to for the inserted values.
|
||||
*
|
||||
* @param Database_Query $query Database_Query of SELECT type
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public function select(Database_Query $query)
|
||||
{
|
||||
if ($query->type() !== \DB::SELECT)
|
||||
{
|
||||
throw new \FuelException('Only SELECT queries can be combined with INSERT queries');
|
||||
}
|
||||
|
||||
$this->_values = $query;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL query and return it.
|
||||
*
|
||||
* @param mixed $db Database instance or instance name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compile($db = null)
|
||||
{
|
||||
if ( ! $db instanceof \Database_Connection)
|
||||
{
|
||||
// Get the database instance
|
||||
$db = \Database_Connection::instance($db);
|
||||
}
|
||||
|
||||
// Start an insertion query
|
||||
$query = 'INSERT INTO '.$db->quote_table($this->_table);
|
||||
|
||||
// Add the column names
|
||||
$query .= ' ('.implode(', ', array_map(array($db, 'quote_identifier'), $this->_columns)).') ';
|
||||
|
||||
if (is_array($this->_values))
|
||||
{
|
||||
// Callback for quoting values
|
||||
$quote = array($db, 'quote');
|
||||
|
||||
$groups = array();
|
||||
foreach ($this->_values as $group)
|
||||
{
|
||||
foreach ($group as $i => $value)
|
||||
{
|
||||
if (is_string($value) AND isset($this->_parameters[$value]))
|
||||
{
|
||||
// Use the parameter value
|
||||
$group[$i] = $this->_parameters[$value];
|
||||
}
|
||||
}
|
||||
|
||||
$groups[] = '('.implode(', ', array_map($quote, $group)).')';
|
||||
}
|
||||
|
||||
// Add the values
|
||||
$query .= 'VALUES '.implode(', ', $groups);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the sub-query
|
||||
$query .= (string) $this->_values;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the query parameters
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->_table = null;
|
||||
$this->_columns = array();
|
||||
$this->_values = array();
|
||||
$this->_parameters = array();
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_Query_Builder_Join extends \Database_Query_Builder
|
||||
{
|
||||
/**
|
||||
* @var string $_type join type
|
||||
*/
|
||||
protected $_type;
|
||||
|
||||
/**
|
||||
* @var string $_table join table
|
||||
*/
|
||||
protected $_table;
|
||||
|
||||
/**
|
||||
* @var array $_on ON clauses
|
||||
*/
|
||||
protected $_on = array();
|
||||
|
||||
/**
|
||||
* Creates a new JOIN statement for a table. Optionally, the type of JOIN
|
||||
* can be specified as the second parameter.
|
||||
*
|
||||
* @param mixed $table column name or array($column, $alias) or object
|
||||
* @param string $type type of JOIN: INNER, RIGHT, LEFT, etc
|
||||
*/
|
||||
public function __construct($table, $type = null)
|
||||
{
|
||||
// Set the table to JOIN on
|
||||
$this->_table = $table;
|
||||
|
||||
if ($type !== null)
|
||||
{
|
||||
// Set the JOIN type
|
||||
$this->_type = (string) $type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new OR condition for joining.
|
||||
*
|
||||
* @param mixed $c1 column name or array($column, $alias) or object
|
||||
* @param string $op logic operator
|
||||
* @param mixed $c2 column name or array($column, $alias) or object
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function or_on($c1, $op, $c2)
|
||||
{
|
||||
$this->_on[] = array($c1, $op, $c2, 'OR');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new AND condition for joining.
|
||||
*
|
||||
* @param mixed $c1 column name or array($column, $alias) or object
|
||||
* @param string $op logic operator
|
||||
* @param mixed $c2 column name or array($column, $alias) or object
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function on($c1, $op, $c2)
|
||||
{
|
||||
$this->_on[] = array($c1, $op, $c2, 'AND');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new AND condition for joining.
|
||||
*
|
||||
* @param mixed $c1 column name or array($column, $alias) or object
|
||||
* @param string $op logic operator
|
||||
* @param mixed $c2 column name or array($column, $alias) or object
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function and_on($c1, $op, $c2)
|
||||
{
|
||||
return $this->on($c1, $op, $c2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL partial for a JOIN statement and return it.
|
||||
*
|
||||
* @param mixed $db Database_Connection instance or instance name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compile($db = null)
|
||||
{
|
||||
if ( ! $db instanceof \Database_Connection)
|
||||
{
|
||||
// Get the database instance
|
||||
$db = \Database_Connection::instance($db);
|
||||
}
|
||||
|
||||
if ($this->_type)
|
||||
{
|
||||
$sql = strtoupper($this->_type).' JOIN';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = 'JOIN';
|
||||
}
|
||||
|
||||
// Quote the table name that is being joined
|
||||
$sql .= ' '.$db->quote_table($this->_table);
|
||||
|
||||
$conditions = array();
|
||||
|
||||
foreach ($this->_on as $condition)
|
||||
{
|
||||
// Split the condition
|
||||
list($c1, $op, $c2, $chaining) = $condition;
|
||||
|
||||
// Add chain type
|
||||
$conditions[] = ' '.$chaining.' ';
|
||||
|
||||
if ($op)
|
||||
{
|
||||
// Make the operator uppercase and spaced
|
||||
$op = ' '.strtoupper($op);
|
||||
}
|
||||
|
||||
// Quote each of the identifiers used for the condition
|
||||
$conditions[] = $db->quote_identifier($c1).$op.' '.(is_null($c2) ? 'NULL' : $db->quote_identifier($c2));
|
||||
}
|
||||
|
||||
// remove the first chain type
|
||||
array_shift($conditions);
|
||||
|
||||
// if there are conditions, concat the conditions "... AND ..." and glue them on...
|
||||
empty($conditions) or $sql .= ' ON ('.implode('', $conditions).')';
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the join values.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->_type =
|
||||
$this->_table = NULL;
|
||||
$this->_on = array();
|
||||
}
|
||||
}
|
@ -1,502 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
use Fuel\Core\Database_Query_Builder_Where;
|
||||
|
||||
class Database_Query_Builder_Select extends Database_Query_Builder_Where
|
||||
{
|
||||
/**
|
||||
* @var array $_select columns to select
|
||||
*/
|
||||
protected $_select = array();
|
||||
|
||||
/**
|
||||
* @var bool $_distinct whether to select distinct values
|
||||
*/
|
||||
protected $_distinct = false;
|
||||
|
||||
/**
|
||||
* @var array $_from table name
|
||||
*/
|
||||
protected $_from = array();
|
||||
|
||||
/**
|
||||
* @var array $_join join objects
|
||||
*/
|
||||
protected $_join = array();
|
||||
|
||||
/**
|
||||
* @var array $_group_by group by clauses
|
||||
*/
|
||||
protected $_group_by = array();
|
||||
|
||||
/**
|
||||
* @var array $_having having clauses
|
||||
*/
|
||||
protected $_having = array();
|
||||
|
||||
/**
|
||||
* @var integer $_offset offset
|
||||
*/
|
||||
protected $_offset = null;
|
||||
|
||||
/**
|
||||
* @var Database_Query_Builder_Join $_last_join last join statement
|
||||
*/
|
||||
protected $_last_join;
|
||||
|
||||
/**
|
||||
* Sets the initial columns to select from.
|
||||
*
|
||||
* @param array $columns column list
|
||||
*/
|
||||
public function __construct(array $columns = null)
|
||||
{
|
||||
if ( ! empty($columns))
|
||||
{
|
||||
// Set the initial columns
|
||||
$this->_select = $columns;
|
||||
}
|
||||
|
||||
// Start the query with no actual SQL statement
|
||||
parent::__construct('', \DB::SELECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables selecting only unique columns using "SELECT DISTINCT"
|
||||
*
|
||||
* @param boolean $value enable or disable distinct columns
|
||||
* @return $this
|
||||
*/
|
||||
public function distinct($value = true)
|
||||
{
|
||||
$this->_distinct = (bool) $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the columns to select from.
|
||||
*
|
||||
* @param mixed $columns column name or array($column, $alias) or object
|
||||
* @param ...
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function select($columns = null)
|
||||
{
|
||||
$columns = func_get_args();
|
||||
|
||||
$this->_select = array_merge($this->_select, $columns);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the columns to select from, using an array.
|
||||
*
|
||||
* @param array $columns list of column names or aliases
|
||||
* @param bool $reset if true, don't merge but overwrite
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function select_array(array $columns, $reset = false)
|
||||
{
|
||||
$this->_select = $reset ? $columns : array_merge($this->_select, $columns);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the tables to select "FROM ..."
|
||||
*
|
||||
* @param mixed $tables table name or array($table, $alias)
|
||||
* @param ...
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function from($tables)
|
||||
{
|
||||
$tables = func_get_args();
|
||||
|
||||
$this->_from = array_merge($this->_from, $tables);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds addition tables to "JOIN ...".
|
||||
*
|
||||
* @param mixed $table column name or array($column, $alias)
|
||||
* @param string $type join type (LEFT, RIGHT, INNER, etc)
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function join($table, $type = NULL)
|
||||
{
|
||||
$this->_join[] = $this->_last_join = new \Database_Query_Builder_Join($table, $type);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds "ON ..." conditions for the last created JOIN statement.
|
||||
*
|
||||
* @param mixed $c1 column name or array($column, $alias) or object
|
||||
* @param string $op logic operator
|
||||
* @param mixed $c2 column name or array($column, $alias) or object
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function on($c1, $op, $c2)
|
||||
{
|
||||
$this->_last_join->on($c1, $op, $c2);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds "AND ON ..." conditions for the last created JOIN statement.
|
||||
*
|
||||
* @param mixed $c1 column name or array($column, $alias) or object
|
||||
* @param string $op logic operator
|
||||
* @param mixed $c2 column name or array($column, $alias) or object
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function and_on($c1, $op, $c2)
|
||||
{
|
||||
$this->_last_join->and_on($c1, $op, $c2);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds "OR ON ..." conditions for the last created JOIN statement.
|
||||
*
|
||||
* @param mixed $c1 column name or array($column, $alias) or object
|
||||
* @param string $op logic operator
|
||||
* @param mixed $c2 column name or array($column, $alias) or object
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function or_on($c1, $op, $c2)
|
||||
{
|
||||
$this->_last_join->or_on($c1, $op, $c2);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a "GROUP BY ..." filter.
|
||||
*
|
||||
* @param mixed $columns column name or array($column, $column) or object
|
||||
* @param ...
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function group_by($columns)
|
||||
{
|
||||
$columns = func_get_args();
|
||||
|
||||
foreach($columns as $idx => $column)
|
||||
{
|
||||
// if an array of columns is passed, flatten it
|
||||
if (is_array($column))
|
||||
{
|
||||
foreach($column as $c)
|
||||
{
|
||||
$columns[] = $c;
|
||||
}
|
||||
unset($columns[$idx]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_group_by = array_merge($this->_group_by, $columns);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of and_having()
|
||||
*
|
||||
* @param mixed $column column name or array($column, $alias) or object
|
||||
* @param string $op logic operator
|
||||
* @param mixed $value column value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function having($column, $op = null, $value = null)
|
||||
{
|
||||
return call_fuel_func_array(array($this, 'and_having'), func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new "AND HAVING" condition for the query.
|
||||
*
|
||||
* @param mixed $column column name or array($column, $alias) or object
|
||||
* @param string $op logic operator
|
||||
* @param mixed $value column value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function and_having($column, $op = null, $value = null)
|
||||
{
|
||||
if($column instanceof \Closure)
|
||||
{
|
||||
$this->and_having_open();
|
||||
$column($this);
|
||||
$this->and_having_close();
|
||||
return $this;
|
||||
}
|
||||
|
||||
if(func_num_args() === 2)
|
||||
{
|
||||
$value = $op;
|
||||
$op = '=';
|
||||
}
|
||||
|
||||
$this->_having[] = array('AND' => array($column, $op, $value));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new "OR HAVING" condition for the query.
|
||||
*
|
||||
* @param mixed $column column name or array($column, $alias) or object
|
||||
* @param string $op logic operator
|
||||
* @param mixed $value column value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function or_having($column, $op = null, $value = null)
|
||||
{
|
||||
if($column instanceof \Closure)
|
||||
{
|
||||
$this->or_having_open();
|
||||
$column($this);
|
||||
$this->or_having_close();
|
||||
return $this;
|
||||
}
|
||||
|
||||
if(func_num_args() === 2)
|
||||
{
|
||||
$value = $op;
|
||||
$op = '=';
|
||||
}
|
||||
|
||||
$this->_having[] = array('OR' => array($column, $op, $value));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of and_having_open()
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function having_open()
|
||||
{
|
||||
return $this->and_having_open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new "AND HAVING (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function and_having_open()
|
||||
{
|
||||
$this->_having[] = array('AND' => '(');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new "OR HAVING (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function or_having_open()
|
||||
{
|
||||
$this->_having[] = array('OR' => '(');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an open "AND HAVING (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function having_close()
|
||||
{
|
||||
return $this->and_having_close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an open "AND HAVING (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function and_having_close()
|
||||
{
|
||||
$this->_having[] = array('AND' => ')');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an open "OR HAVING (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function or_having_close()
|
||||
{
|
||||
$this->_having[] = array('OR' => ')');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start returning results after "OFFSET ..."
|
||||
*
|
||||
* @param integer $number starting result number
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function offset($number)
|
||||
{
|
||||
$this->_offset = (int) $number;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL query and return it.
|
||||
*
|
||||
* @param mixed $db Database_Connection instance or instance name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compile($db = null)
|
||||
{
|
||||
if ( ! $db instanceof \Database_Connection)
|
||||
{
|
||||
// Get the database instance
|
||||
$db = $this->_connection ?: \Database_Connection::instance($db);
|
||||
}
|
||||
|
||||
// Callback to quote identifiers
|
||||
$quote_ident = array($db, 'quote_identifier');
|
||||
|
||||
// Callback to quote tables
|
||||
$quote_table = array($db, 'quote_table');
|
||||
|
||||
// Start a selection query
|
||||
$query = 'SELECT ';
|
||||
|
||||
if ($this->_distinct === TRUE)
|
||||
{
|
||||
// Select only unique results
|
||||
$query .= 'DISTINCT ';
|
||||
}
|
||||
|
||||
if (empty($this->_select))
|
||||
{
|
||||
// Select all columns
|
||||
$query .= '*';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select all columns
|
||||
$query .= implode(', ', array_unique(array_map($quote_ident, $this->_select)));
|
||||
}
|
||||
|
||||
if ( ! empty($this->_from))
|
||||
{
|
||||
// Set tables to select from
|
||||
$query .= ' FROM '.implode(', ', array_unique(array_map($quote_table, $this->_from)));
|
||||
}
|
||||
|
||||
if ( ! empty($this->_join))
|
||||
{
|
||||
// Add tables to join
|
||||
$query .= ' '.$this->_compile_join($db, $this->_join);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_where))
|
||||
{
|
||||
// Add selection conditions
|
||||
$query .= ' WHERE '.$this->_compile_conditions($db, $this->_where);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_group_by))
|
||||
{
|
||||
// Add sorting
|
||||
$query .= ' GROUP BY '.implode(', ', array_map($quote_ident, $this->_group_by));
|
||||
}
|
||||
|
||||
if ( ! empty($this->_having))
|
||||
{
|
||||
// Add filtering conditions
|
||||
$query .= ' HAVING '.$this->_compile_conditions($db, $this->_having);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_order_by))
|
||||
{
|
||||
// Add sorting
|
||||
$query .= ' '.$this->_compile_order_by($db, $this->_order_by);
|
||||
}
|
||||
|
||||
if ($this->_limit !== NULL)
|
||||
{
|
||||
// Add limiting
|
||||
$query .= ' LIMIT '.$this->_limit;
|
||||
}
|
||||
|
||||
if ($this->_offset !== NULL)
|
||||
{
|
||||
// Add offsets
|
||||
$query .= ' OFFSET '.$this->_offset;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the query parameters
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->_select = array();
|
||||
$this->_from = array();
|
||||
$this->_join = array();
|
||||
$this->_where = array();
|
||||
$this->_group_by = array();
|
||||
$this->_having = array();
|
||||
$this->_order_by = array();
|
||||
$this->_distinct = false;
|
||||
$this->_limit = null;
|
||||
$this->_offset = null;
|
||||
$this->_last_join = null;
|
||||
$this->_parameters = array();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
@ -1,200 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_Query_Builder_Update extends \Database_Query_Builder_Where
|
||||
{
|
||||
/**
|
||||
* @var string $_table table name
|
||||
*/
|
||||
protected $_table;
|
||||
|
||||
/**
|
||||
* @var array $_set update values
|
||||
*/
|
||||
protected $_set = array();
|
||||
|
||||
/**
|
||||
* @var array $_join join statements
|
||||
*/
|
||||
protected $_join = array();
|
||||
|
||||
/**
|
||||
* @var Database_Query_Builder_Join $_last_join last join statement
|
||||
*/
|
||||
protected $_last_join;
|
||||
|
||||
/**
|
||||
* Set the table for a update.
|
||||
*
|
||||
* @param mixed $table table name or array($table, $alias) or object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($table = NULL)
|
||||
{
|
||||
if ($table)
|
||||
{
|
||||
// Set the initial table name
|
||||
$this->_table = $table;
|
||||
}
|
||||
|
||||
// Start the query with no SQL
|
||||
parent::__construct('', \DB::UPDATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the table to update.
|
||||
*
|
||||
* @param mixed $table table name or array($table, $alias)
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function table($table)
|
||||
{
|
||||
$this->_table = $table;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the values to update with an associative array.
|
||||
*
|
||||
* @param array $pairs associative (column => value) list
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function set(array $pairs)
|
||||
{
|
||||
foreach ($pairs as $column => $value)
|
||||
{
|
||||
$this->_set[] = array($column, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a single column.
|
||||
*
|
||||
* @param mixed $column table name or array($table, $alias) or object
|
||||
* @param mixed $value column value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function value($column, $value)
|
||||
{
|
||||
$this->_set[] = array($column, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the SQL query and return it.
|
||||
*
|
||||
* @param mixed $db Database instance or instance name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compile($db = null)
|
||||
{
|
||||
if ( ! $db instanceof \Database_Connection)
|
||||
{
|
||||
// Get the database instance
|
||||
$db = \Database_Connection::instance($db);
|
||||
}
|
||||
|
||||
// Start an update query
|
||||
$query = 'UPDATE '.$db->quote_table($this->_table);
|
||||
|
||||
if ( ! empty($this->_join))
|
||||
{
|
||||
// Add tables to join
|
||||
$query .= ' '.$this->_compile_join($db, $this->_join);
|
||||
}
|
||||
|
||||
// Add the columns to update
|
||||
$query .= ' SET '.$this->_compile_set($db, $this->_set);
|
||||
|
||||
if ( ! empty($this->_where))
|
||||
{
|
||||
// Add selection conditions
|
||||
$query .= ' WHERE '.$this->_compile_conditions($db, $this->_where);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_order_by))
|
||||
{
|
||||
// Add sorting
|
||||
$query .= ' '.$this->_compile_order_by($db, $this->_order_by);
|
||||
}
|
||||
|
||||
if ($this->_limit !== null)
|
||||
{
|
||||
// Add limiting
|
||||
$query .= ' LIMIT '.$this->_limit;
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the query parameters
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->_table = null;
|
||||
$this->_join = array();
|
||||
$this->_set = array();
|
||||
$this->_where = array();
|
||||
$this->_order_by = array();
|
||||
$this->_limit = null;
|
||||
$this->_last_join = null;
|
||||
$this->_parameters = array();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds addition tables to "JOIN ...".
|
||||
*
|
||||
* @param mixed $table column name or array($column, $alias) or object
|
||||
* @param string $type join type (LEFT, RIGHT, INNER, etc)
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function join($table, $type = null)
|
||||
{
|
||||
$this->_join[] = $this->_last_join = new \Database_Query_Builder_Join($table, $type);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds "ON ..." conditions for the last created JOIN statement.
|
||||
*
|
||||
* @param mixed $c1 column name or array($column, $alias) or object
|
||||
* @param string $op logic operator
|
||||
* @param mixed $c2 column name or array($column, $alias) or object
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function on($c1, $op, $c2)
|
||||
{
|
||||
$this->_last_join->on($c1, $op, $c2);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,230 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
abstract class Database_Query_Builder_Where extends \Database_Query_Builder
|
||||
{
|
||||
/**
|
||||
* @var array $_where where statements
|
||||
*/
|
||||
protected $_where = array();
|
||||
|
||||
/**
|
||||
* @var array $_order_by order by clause
|
||||
*/
|
||||
protected $_order_by = array();
|
||||
|
||||
/**
|
||||
* @var integer $_limit
|
||||
*/
|
||||
protected $_limit = null;
|
||||
|
||||
/**
|
||||
* Alias of and_where()
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function where()
|
||||
{
|
||||
return call_fuel_func_array(array($this, 'and_where'), func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new "AND WHERE" condition for the query.
|
||||
*
|
||||
* @param mixed $column column name or array($column, $alias) or object
|
||||
* @param string $op logic operator
|
||||
* @param mixed $value column value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function and_where($column, $op = null, $value = null)
|
||||
{
|
||||
if($column instanceof \Closure)
|
||||
{
|
||||
$this->and_where_open();
|
||||
$column($this);
|
||||
$this->and_where_close();
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (is_array($column))
|
||||
{
|
||||
foreach ($column as $key => $val)
|
||||
{
|
||||
if (is_array($val))
|
||||
{
|
||||
$this->and_where($val[0], $val[1], $val[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->and_where($key, '=', $val);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(func_num_args() === 2)
|
||||
{
|
||||
$value = $op;
|
||||
$op = '=';
|
||||
}
|
||||
$this->_where[] = array('AND' => array($column, $op, $value));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new "OR WHERE" condition for the query.
|
||||
*
|
||||
* @param mixed $column column name or array($column, $alias) or object
|
||||
* @param string $op logic operator
|
||||
* @param mixed $value column value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function or_where($column, $op = null, $value = null)
|
||||
{
|
||||
if($column instanceof \Closure)
|
||||
{
|
||||
$this->or_where_open();
|
||||
$column($this);
|
||||
$this->or_where_close();
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (is_array($column))
|
||||
{
|
||||
foreach ($column as $key => $val)
|
||||
{
|
||||
if (is_array($val))
|
||||
{
|
||||
$this->or_where($val[0], $val[1], $val[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->or_where($key, '=', $val);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(func_num_args() === 2)
|
||||
{
|
||||
$value = $op;
|
||||
$op = '=';
|
||||
}
|
||||
$this->_where[] = array('OR' => array($column, $op, $value));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of and_where_open()
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function where_open()
|
||||
{
|
||||
return $this->and_where_open();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new "AND WHERE (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function and_where_open()
|
||||
{
|
||||
$this->_where[] = array('AND' => '(');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new "OR WHERE (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function or_where_open()
|
||||
{
|
||||
$this->_where[] = array('OR' => '(');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an open "AND WHERE (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function where_close()
|
||||
{
|
||||
return $this->and_where_close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an open "AND WHERE (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function and_where_close()
|
||||
{
|
||||
$this->_where[] = array('AND' => ')');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes an open "OR WHERE (...)" grouping.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function or_where_close()
|
||||
{
|
||||
$this->_where[] = array('OR' => ')');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies sorting with "ORDER BY ..."
|
||||
*
|
||||
* @param mixed $column column name or array($column, $alias) or object
|
||||
* @param string $direction direction of sorting
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function order_by($column, $direction = null)
|
||||
{
|
||||
$this->_order_by[] = array($column, $direction);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return up to "LIMIT ..." results
|
||||
*
|
||||
* @param integer $number maximum results to return
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function limit($number)
|
||||
{
|
||||
$this->_limit = (int) $number;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,417 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
abstract class Database_Result implements \Countable, \Iterator, \SeekableIterator, \ArrayAccess, \Sanitization
|
||||
{
|
||||
/**
|
||||
* @var string Executed SQL for this result
|
||||
*/
|
||||
protected $_query;
|
||||
|
||||
/**
|
||||
* @var resource $_result raw result resource
|
||||
*/
|
||||
protected $_result;
|
||||
|
||||
/**
|
||||
* @var int $_total_rows total number of rows
|
||||
*/
|
||||
protected $_total_rows = 0;
|
||||
|
||||
/**
|
||||
* @var int $_current_row current row number
|
||||
*/
|
||||
protected $_current_row = 0;
|
||||
|
||||
/**
|
||||
* @var bool $_as_object return rows as an object or associative array
|
||||
*/
|
||||
protected $_as_object;
|
||||
|
||||
/**
|
||||
* @var bool $_sanitization_enabled If this is a records data will be sanitized on get
|
||||
*/
|
||||
protected $_sanitization_enabled = false;
|
||||
|
||||
/**
|
||||
* Sets the total number of rows and stores the result locally.
|
||||
*
|
||||
* @param mixed $result query result
|
||||
* @param string $sql SQL query
|
||||
* @param mixed $as_object object
|
||||
*/
|
||||
public function __construct($result, $sql, $as_object)
|
||||
{
|
||||
// Store the result locally
|
||||
$this->_result = $result;
|
||||
|
||||
// Store the SQL locally
|
||||
$this->_query = $sql;
|
||||
|
||||
if (is_object($as_object))
|
||||
{
|
||||
// Get the object class name
|
||||
$as_object = get_class($as_object);
|
||||
}
|
||||
|
||||
// Results as objects or associative arrays
|
||||
$this->_as_object = $as_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result destruction cleans up all open result sets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function __destruct();
|
||||
|
||||
/**
|
||||
* Get a cached database result from the current result iterator.
|
||||
*
|
||||
* $cachable = serialize($result->cached());
|
||||
*
|
||||
* @return Database_Result_Cached
|
||||
* @since 3.0.5
|
||||
*/
|
||||
public function cached()
|
||||
{
|
||||
return new \Database_Result_Cached($this->as_array(), $this->_query, $this->_as_object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all of the rows in the result as an array.
|
||||
*
|
||||
* // Indexed array of all rows
|
||||
* $rows = $result->as_array();
|
||||
*
|
||||
* // Associative array of rows by "id"
|
||||
* $rows = $result->as_array('id');
|
||||
*
|
||||
* // Associative array of rows, "id" => "name"
|
||||
* $rows = $result->as_array('id', 'name');
|
||||
*
|
||||
* @param string $key column for associative keys
|
||||
* @param string $value column for values
|
||||
* @return array
|
||||
*/
|
||||
public function as_array($key = null, $value = null)
|
||||
{
|
||||
$results = array();
|
||||
|
||||
if ($key === null and $value === null)
|
||||
{
|
||||
// Indexed rows
|
||||
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[] = $row;
|
||||
}
|
||||
}
|
||||
elseif ($key === null)
|
||||
{
|
||||
// Indexed columns
|
||||
|
||||
if ($this->_as_object)
|
||||
{
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[] = $row->$value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[] = $row[$value];
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($value === null)
|
||||
{
|
||||
// Associative rows
|
||||
|
||||
if ($this->_as_object)
|
||||
{
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[$row->$key] = $row;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[$row[$key]] = $row;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Associative columns
|
||||
|
||||
if ($this->_as_object)
|
||||
{
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[$row->$key] = $row->$value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ($this as $row)
|
||||
{
|
||||
$results[$row[$key]] = $row[$value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->rewind();
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named column from the current row.
|
||||
*
|
||||
* // Get the "id" value
|
||||
* $id = $result->get('id');
|
||||
*
|
||||
* @param string $name column to get
|
||||
* @param mixed $default default value if the column does not exist
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default = null)
|
||||
{
|
||||
$row = $this->current();
|
||||
|
||||
if ($this->_as_object)
|
||||
{
|
||||
if (isset($row->$name))
|
||||
{
|
||||
// sanitize the data if needed
|
||||
if ( ! $this->_sanitization_enabled)
|
||||
{
|
||||
$result = $row->$name;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = \Security::clean($row->$name, null, 'security.output_filter');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset($row[$name]))
|
||||
{
|
||||
// sanitize the data if needed
|
||||
if ( ! $this->_sanitization_enabled)
|
||||
{
|
||||
$result = $row[$name];
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = \Security::clean($row[$name], null, 'security.output_filter');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
return \Fuel::value($default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [Countable::count], returns the total number of rows.
|
||||
*
|
||||
* echo count($result);
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
return $this->_total_rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [ArrayAccess::offsetExists], determines if row exists.
|
||||
*
|
||||
* if (isset($result[10]))
|
||||
* {
|
||||
* // Row 10 exists
|
||||
* }
|
||||
*
|
||||
* @param integer $offset
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return ($offset >= 0 and $offset < $this->_total_rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [ArrayAccess::offsetGet], gets a given row.
|
||||
*
|
||||
* $row = $result[10];
|
||||
*
|
||||
* @param integer $offset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
if ( ! $this->seek($offset))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = $this->current();
|
||||
|
||||
// sanitize the data if needed
|
||||
if ($this->_sanitization_enabled)
|
||||
{
|
||||
$result = \Security::clean($result, null, 'security.output_filter');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [ArrayAccess::offsetSet], throws an error.
|
||||
* [!!] You cannot modify a database result.
|
||||
*
|
||||
* @param integer $offset
|
||||
* @param mixed $value
|
||||
*
|
||||
* @throws \FuelException
|
||||
*/
|
||||
final public function offsetSet($offset, $value)
|
||||
{
|
||||
throw new \FuelException('Database results are read-only');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [ArrayAccess::offsetUnset], throws an error.
|
||||
* [!!] You cannot modify a database result.
|
||||
*
|
||||
* @param integer $offset
|
||||
*
|
||||
* @throws \FuelException
|
||||
*/
|
||||
final public function offsetUnset($offset)
|
||||
{
|
||||
throw new \FuelException('Database results are read-only');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [Iterator::key], returns the current row number.
|
||||
*
|
||||
* echo key($result);
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->_current_row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [Iterator::next], moves to the next row.
|
||||
*
|
||||
* next($result);
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
++$this->_current_row;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [Iterator::prev], moves to the previous row.
|
||||
*
|
||||
* prev($result);
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function prev()
|
||||
{
|
||||
--$this->_current_row;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [Iterator::rewind], sets the current row to zero.
|
||||
*
|
||||
* rewind($result);
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->_current_row = 0;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements [Iterator::valid], checks if the current row exists.
|
||||
*
|
||||
* [!!] This method is only used internally.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->offsetExists($this->_current_row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable sanitization mode in the object
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function sanitize()
|
||||
{
|
||||
$this->_sanitization_enabled = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable sanitization mode in the object
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function unsanitize()
|
||||
{
|
||||
$this->_sanitization_enabled = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current sanitization state of the object
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function sanitized()
|
||||
{
|
||||
return $this->_sanitization_enabled;
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_Result_Cached extends \Database_Result
|
||||
{
|
||||
/**
|
||||
* @param array $result
|
||||
* @param string $sql
|
||||
* @param mixed $as_object
|
||||
*/
|
||||
public function __construct(array $result, $sql, $as_object = null)
|
||||
{
|
||||
parent::__construct($result, $sql, $as_object);
|
||||
|
||||
// Find the number of rows in the result
|
||||
$this->_total_rows = count($result);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
// Cached results do not use resources
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function cached()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $offset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function seek($offset)
|
||||
{
|
||||
if ( ! $this->offsetExists($offset))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->_current_row = $offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
if ($this->valid())
|
||||
{
|
||||
// sanitize the data if needed
|
||||
if ( ! $this->_sanitization_enabled)
|
||||
{
|
||||
$result = $this->_result[$this->_current_row];
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = \Security::clean($this->_result[$this->_current_row], null, 'security.output_filter');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,647 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_Schema
|
||||
{
|
||||
/**
|
||||
* @var Database_Connection database connection instance
|
||||
*/
|
||||
protected $_connection;
|
||||
|
||||
/**
|
||||
* @var string database connection config name
|
||||
*/
|
||||
protected $_name;
|
||||
|
||||
/**
|
||||
* Stores the database instance to be used.
|
||||
*
|
||||
* @param string database connection instance
|
||||
*/
|
||||
public function __construct($name, $connection)
|
||||
{
|
||||
// Set the connection config name
|
||||
$this->_name = $name;
|
||||
|
||||
// Set the connection instance
|
||||
$this->_connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a database. Will throw a Database_Exception if it cannot.
|
||||
*
|
||||
* @throws Fuel\Database_Exception
|
||||
* @param string $database the database name
|
||||
* @param string $charset the character set
|
||||
* @param boolean $if_not_exists whether to add an IF NOT EXISTS statement.
|
||||
* @return int the number of affected rows
|
||||
*/
|
||||
public function create_database($database, $charset = null, $if_not_exists = true)
|
||||
{
|
||||
$sql = 'CREATE DATABASE';
|
||||
$sql .= $if_not_exists ? ' IF NOT EXISTS ' : ' ';
|
||||
|
||||
$sql .= $this->_connection->quote_identifier($database).$this->process_charset($charset, true);
|
||||
|
||||
return $this->_connection->query(0, $sql, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a database. Will throw a Database_Exception if it cannot.
|
||||
*
|
||||
* @throws Fuel\Database_Exception
|
||||
* @param string $database the database name
|
||||
* @return int the number of affected rows
|
||||
*/
|
||||
public function drop_database($database)
|
||||
{
|
||||
$sql = 'DROP DATABASE ';
|
||||
$sql .= $this->_connection->quote_identifier($database);
|
||||
|
||||
return $this->_connection->query(0, $sql, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a table. Will throw a Database_Exception if it cannot.
|
||||
*
|
||||
* @throws Fuel\Database_Exception
|
||||
* @param string $table the table name
|
||||
* @return int the number of affected rows
|
||||
*/
|
||||
public function drop_table($table)
|
||||
{
|
||||
$sql = 'DROP TABLE IF EXISTS ';
|
||||
$sql .= $this->_connection->quote_identifier($this->_connection->table_prefix($table));
|
||||
|
||||
return $this->_connection->query(0, $sql, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a table. Will throw a Database_Exception if it cannot.
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
* @param string $table the old table name
|
||||
* @param string $new_table_name the new table name
|
||||
* @return int the number of affected
|
||||
*/
|
||||
public function rename_table($table, $new_table_name)
|
||||
{
|
||||
$sql = 'RENAME TABLE ';
|
||||
$sql .= $this->_connection->quote_identifier($this->_connection->table_prefix($table));
|
||||
$sql .= ' TO ';
|
||||
$sql .= $this->_connection->quote_identifier($this->_connection->table_prefix($new_table_name));
|
||||
|
||||
return $this->_connection->query(0, $sql, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table.
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
* @param string $table the table name
|
||||
* @param array $fields the fields array
|
||||
* @param array $primary_keys an array of primary keys
|
||||
* @param boolean $if_not_exists whether to add an IF NOT EXISTS statement.
|
||||
* @param string|boolean $engine storage engine overwrite
|
||||
* @param string $charset default charset overwrite
|
||||
* @param array $foreign_keys an array of foreign keys
|
||||
* @return int number of affected rows.
|
||||
*/
|
||||
public function create_table($table, $fields, $primary_keys = array(), $if_not_exists = true, $engine = false, $charset = null, $foreign_keys = array())
|
||||
{
|
||||
$sql = 'CREATE TABLE';
|
||||
|
||||
$sql .= $if_not_exists ? ' IF NOT EXISTS ' : ' ';
|
||||
|
||||
$sql .= $this->_connection->quote_identifier($this->_connection->table_prefix($table)).' (';
|
||||
$sql .= $this->process_fields($fields, '');
|
||||
if ( ! empty($primary_keys))
|
||||
{
|
||||
foreach ($primary_keys as $index => $primary_key)
|
||||
{
|
||||
$primary_keys[$index] = $this->_connection->quote_identifier($primary_key);
|
||||
}
|
||||
$sql .= ",\n\tPRIMARY KEY (".implode(', ', $primary_keys).')';
|
||||
}
|
||||
|
||||
empty($foreign_keys) or $sql .= $this->process_foreign_keys($foreign_keys);
|
||||
|
||||
$sql .= "\n)";
|
||||
$sql .= ($engine !== false) ? ' ENGINE = '.$engine.' ' : '';
|
||||
$sql .= $this->process_charset($charset, true).";";
|
||||
|
||||
return $this->_connection->query(0, $sql, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates a table.
|
||||
*
|
||||
* @throws Fuel\Database_Exception
|
||||
* @param string $table the table name
|
||||
* @return int the number of affected rows
|
||||
*/
|
||||
public function truncate_table($table)
|
||||
{
|
||||
$sql = 'TRUNCATE TABLE ';
|
||||
$sql .= $this->_connection->quote_identifier($this->_connection->table_prefix($table));
|
||||
|
||||
return $this->_connection->query(\DB::DELETE, $sql, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic check if a given table exists.
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
* @param string $table Table name
|
||||
* @return bool
|
||||
*/
|
||||
public function table_exists($table)
|
||||
{
|
||||
$sql = 'SELECT * FROM ';
|
||||
$sql .= $this->_connection->quote_identifier($this->_connection->table_prefix($table));
|
||||
$sql .= ' LIMIT 1';
|
||||
|
||||
try
|
||||
{
|
||||
$this->_connection->query(\DB::SELECT, $sql, false);
|
||||
return true;
|
||||
}
|
||||
catch (\Database_Exception $e)
|
||||
{
|
||||
// check if we have a DB connection at all
|
||||
if ( ! $this->_connection->has_connection())
|
||||
{
|
||||
// if no connection could be made, re throw the exception
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if given field(s) in a given table exists.
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
* @param string $table Table name
|
||||
* @param string|array $columns columns to check
|
||||
* @return bool
|
||||
*/
|
||||
public function field_exists($table, $columns)
|
||||
{
|
||||
if ( ! is_array($columns))
|
||||
{
|
||||
$columns = array($columns);
|
||||
}
|
||||
|
||||
$sql = 'SELECT ';
|
||||
$sql .= implode(', ', array_unique(array_map(array($this->_connection, 'quote_identifier'), $columns)));
|
||||
$sql .= ' FROM ';
|
||||
$sql .= $this->_connection->quote_identifier($this->_connection->table_prefix($table));
|
||||
$sql .= ' LIMIT 1';
|
||||
|
||||
try
|
||||
{
|
||||
$this->_connection->query(\DB::SELECT, $sql, false);
|
||||
return true;
|
||||
}
|
||||
catch (\Database_Exception $e)
|
||||
{
|
||||
// check if we have a DB connection at all
|
||||
if ( ! $this->_connection->has_connection())
|
||||
{
|
||||
// if no connection could be made, re throw the exception
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an index on that table.
|
||||
*
|
||||
* @access public
|
||||
* @param string $table
|
||||
* @param string $index_name
|
||||
* @param string $index_columns
|
||||
* @param string $index (should be 'unique', 'fulltext', 'spatial' or 'nonclustered')
|
||||
* @return bool
|
||||
* @author Thomas Edwards
|
||||
*/
|
||||
public function create_index($table, $index_columns, $index_name = '', $index = '')
|
||||
{
|
||||
static $accepted_index = array('UNIQUE', 'FULLTEXT', 'SPATIAL', 'NONCLUSTERED', 'PRIMARY');
|
||||
|
||||
// make sure the index type is uppercase
|
||||
$index !== '' and $index = strtoupper($index);
|
||||
|
||||
if (empty($index_name))
|
||||
{
|
||||
if (is_array($index_columns))
|
||||
{
|
||||
foreach ($index_columns as $key => $value)
|
||||
{
|
||||
if (is_numeric($key))
|
||||
{
|
||||
$index_name .= ($index_name == '' ? '' : '_').$value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$index_name .= ($index_name == '' ? '' : '_').str_replace(array('(', ')', ' '), '', $key);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$index_name = $index_columns;
|
||||
}
|
||||
}
|
||||
|
||||
if ($index == 'PRIMARY')
|
||||
{
|
||||
$sql = 'ALTER TABLE ';
|
||||
$sql .= $this->_connection->quote_identifier($this->_connection->table_prefix($table));
|
||||
$sql .= ' ADD PRIMARY KEY ';
|
||||
if (is_array($index_columns))
|
||||
{
|
||||
$columns = '';
|
||||
foreach ($index_columns as $key => $value)
|
||||
{
|
||||
if (is_numeric($key))
|
||||
{
|
||||
$columns .= ($columns=='' ? '' : ', ').$this->_connection->quote_identifier($value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$columns .= ($columns=='' ? '' : ', ').$this->_connection->quote_identifier($key).' '.strtoupper($value);
|
||||
}
|
||||
}
|
||||
$sql .= ' ('.$columns.')';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = 'CREATE ';
|
||||
|
||||
$index !== '' and $sql .= (in_array($index, $accepted_index)) ? $index.' ' : '';
|
||||
|
||||
$sql .= 'INDEX ';
|
||||
$sql .= $this->_connection->quote_identifier($index_name);
|
||||
$sql .= ' ON ';
|
||||
$sql .= $this->_connection->quote_identifier($this->_connection->table_prefix($table));
|
||||
if (is_array($index_columns))
|
||||
{
|
||||
$columns = '';
|
||||
foreach ($index_columns as $key => $value)
|
||||
{
|
||||
if (is_numeric($key))
|
||||
{
|
||||
$columns .= ($columns=='' ? '' : ', ').$this->_connection->quote_identifier($value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$columns .= ($columns=='' ? '' : ', ').$this->_connection->quote_identifier($key).' '.strtoupper($value);
|
||||
}
|
||||
}
|
||||
$sql .= ' ('.$columns.')';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql .= ' ('.$this->_connection->quote_identifier($index_columns).')';
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_connection->query(0, $sql, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop an index from a table.
|
||||
*
|
||||
* @access public
|
||||
* @param string $table
|
||||
* @param string $index_name
|
||||
* @return bool
|
||||
* @author Thomas Edwards
|
||||
*/
|
||||
public function drop_index($table, $index_name)
|
||||
{
|
||||
if (strtoupper($index_name) == 'PRIMARY')
|
||||
{
|
||||
$sql = 'ALTER TABLE '.$this->_connection->quote_identifier($this->_connection->table_prefix($table));
|
||||
$sql .= ' DROP PRIMARY KEY';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql = 'DROP INDEX '.$this->_connection->quote_identifier($index_name);
|
||||
$sql .= ' ON '.$this->_connection->quote_identifier($this->_connection->table_prefix($table));
|
||||
}
|
||||
|
||||
return $this->_connection->query(0, $sql, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a single foreign key to a table
|
||||
*
|
||||
* @param string $table the table name
|
||||
* @param array $foreign_key a single foreign key
|
||||
* @return int number of affected rows
|
||||
*/
|
||||
public function add_foreign_key($table, $foreign_key)
|
||||
{
|
||||
if ( ! is_array($foreign_key))
|
||||
{
|
||||
throw new \InvalidArgumentException('Foreign key for add_foreign_key() must be specified as an array');
|
||||
}
|
||||
|
||||
$sql = 'ALTER TABLE ';
|
||||
$sql .= $this->_connection->quote_identifier($this->_connection->table_prefix($table)).' ';
|
||||
$sql .= 'ADD ';
|
||||
$sql .= ltrim($this->process_foreign_keys(array($foreign_key), $this->_connection), ',');
|
||||
|
||||
return $this->_connection->query(0, $sql, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a foreign key from a table
|
||||
*
|
||||
* @param string $table the table name
|
||||
* @param string $fk_name the foreign key name
|
||||
* @return int number of affected rows
|
||||
*/
|
||||
public function drop_foreign_key($table, $fk_name)
|
||||
{
|
||||
$sql = 'ALTER TABLE ';
|
||||
$sql .= $this->_connection->quote_identifier($this->_connection->table_prefix($table)).' ';
|
||||
$sql .= 'DROP FOREIGN KEY '.$this->_connection->quote_identifier($fk_name);
|
||||
|
||||
return $this->_connection->query(0, $sql, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string of foreign keys
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
* @param array $foreign_keys Array of foreign key rules
|
||||
* @return string the formatted foreign key string
|
||||
*/
|
||||
public function process_foreign_keys($foreign_keys)
|
||||
{
|
||||
if ( ! is_array($foreign_keys))
|
||||
{
|
||||
throw new \Database_Exception('Foreign keys on create_table() must be specified as an array');
|
||||
}
|
||||
|
||||
$fk_list = array();
|
||||
|
||||
foreach($foreign_keys as $definition)
|
||||
{
|
||||
// some sanity checks
|
||||
if (empty($definition['key']))
|
||||
{
|
||||
throw new \Database_Exception('Foreign keys on create_table() must specify a foreign key name');
|
||||
}
|
||||
if ( empty($definition['reference']))
|
||||
{
|
||||
throw new \Database_Exception('Foreign keys on create_table() must specify a foreign key reference');
|
||||
}
|
||||
if (empty($definition['reference']['table']) or empty($definition['reference']['column']))
|
||||
{
|
||||
throw new \Database_Exception('Foreign keys on create_table() must specify a reference table and column name');
|
||||
}
|
||||
|
||||
$sql = '';
|
||||
! empty($definition['constraint']) and $sql .= " CONSTRAINT ".$this->_connection->quote_identifier($definition['constraint']);
|
||||
$sql .= " FOREIGN KEY (".$this->_connection->quote_identifier($definition['key']).')';
|
||||
$sql .= " REFERENCES ".$this->_connection->quote_identifier($this->_connection->table_prefix($definition['reference']['table'])).' (';
|
||||
if (is_array($definition['reference']['column']))
|
||||
{
|
||||
$sql .= implode(', ', $this->_connection->quote_identifier($definition['reference']['column']));
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql .= $this->_connection->quote_identifier($definition['reference']['column']);
|
||||
}
|
||||
$sql .= ')';
|
||||
! empty($definition['on_update']) and $sql .= " ON UPDATE ".$definition['on_update'];
|
||||
! empty($definition['on_delete']) and $sql .= " ON DELETE ".$definition['on_delete'];
|
||||
|
||||
$fk_list[] = "\n\t".ltrim($sql);
|
||||
}
|
||||
|
||||
return ', '.implode(',', $fk_list);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function alter_fields($type, $table, $fields)
|
||||
{
|
||||
$sql = 'ALTER TABLE '.$this->_connection->quote_identifier($this->_connection->table_prefix($table)).' ';
|
||||
|
||||
if ($type === 'DROP')
|
||||
{
|
||||
if ( ! is_array($fields))
|
||||
{
|
||||
$fields = array($fields);
|
||||
}
|
||||
|
||||
$drop_fields = array();
|
||||
foreach ($fields as $field)
|
||||
{
|
||||
$drop_fields[] = 'DROP '.$this->_connection->quote_identifier($field);
|
||||
}
|
||||
$sql .= implode(', ', $drop_fields);
|
||||
}
|
||||
else
|
||||
{
|
||||
$use_brackets = ! in_array($type, array('ADD', 'CHANGE', 'MODIFY'));
|
||||
$use_brackets and $sql .= $type.' ';
|
||||
$use_brackets and $sql .= '(';
|
||||
$sql .= $this->process_fields($fields, (( ! $use_brackets) ? $type.' ' : ''));
|
||||
$use_brackets and $sql .= ')';
|
||||
}
|
||||
|
||||
return $this->_connection->query(0, $sql, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes table maintenance. Will throw FuelException when the operation is not supported.
|
||||
*
|
||||
* @throws FuelException
|
||||
* @param string $table the table name
|
||||
* @return bool whether the operation has succeeded
|
||||
*/
|
||||
public function table_maintenance($operation, $table)
|
||||
{
|
||||
$sql = $operation.' '.$this->_connection->quote_identifier($this->_connection->table_prefix($table));
|
||||
$result = $this->_connection->query(\DB::SELECT, $sql, false);
|
||||
|
||||
$type = $result->get('Msg_type');
|
||||
$message = $result->get('Msg_text');
|
||||
$table = $result->get('Table');
|
||||
|
||||
if ($type === 'status' and in_array(strtolower($message), array('ok', 'table is already up to date')))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// make sure we have a type logger can handle
|
||||
if (in_array($type, array('info', 'warning', 'error')))
|
||||
{
|
||||
$type = strtoupper($type);
|
||||
}
|
||||
else
|
||||
{
|
||||
$type = \Fuel::L_INFO;
|
||||
}
|
||||
|
||||
logger($type, 'Table: '.$table.', Operation: '.$operation.', Message: '.$result->get('Msg_text'), 'DBUtil::table_maintenance');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the default charset.
|
||||
*
|
||||
* @param string $charset the character set
|
||||
* @param bool $is_default whether to use default
|
||||
* @param string $collation the collating sequence to be used
|
||||
* @return string the formatted charset sql
|
||||
*/
|
||||
protected function process_charset($charset = null, $is_default = false, $collation = null)
|
||||
{
|
||||
$charset or $charset = \Config::get('db.'.$this->_name.'.charset', null);
|
||||
|
||||
if (empty($charset))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
$collation or $collation = \Config::get('db.'.$this->_name.'.collation', null);
|
||||
|
||||
if (empty($collation) and ($pos = stripos($charset, '_')) !== false)
|
||||
{
|
||||
$collation = $charset;
|
||||
$charset = substr($charset, 0, $pos);
|
||||
}
|
||||
|
||||
$charset = 'CHARACTER SET '.$charset;
|
||||
|
||||
if ($is_default)
|
||||
{
|
||||
$charset = 'DEFAULT '.$charset;
|
||||
}
|
||||
|
||||
if ( ! empty($collation))
|
||||
{
|
||||
if ($is_default)
|
||||
{
|
||||
$charset .= ' DEFAULT';
|
||||
}
|
||||
$charset .= ' COLLATE '.$collation;
|
||||
}
|
||||
|
||||
return $charset;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected function process_fields($fields, $prefix = '')
|
||||
{
|
||||
$sql_fields = array();
|
||||
|
||||
foreach ($fields as $field => $attr)
|
||||
{
|
||||
$attr = array_change_key_case($attr, CASE_UPPER);
|
||||
$_prefix = $prefix;
|
||||
if(array_key_exists('NAME', $attr) and $field !== $attr['NAME'] and $_prefix === 'MODIFY ')
|
||||
{
|
||||
$_prefix = 'CHANGE ';
|
||||
}
|
||||
$sql = "\n\t".$_prefix;
|
||||
$sql .= $this->_connection->quote_identifier($field);
|
||||
$sql .= (array_key_exists('NAME', $attr) and $attr['NAME'] !== $field) ? ' '.$this->_connection->quote_identifier($attr['NAME']).' ' : '';
|
||||
$sql .= array_key_exists('TYPE', $attr) ? ' '.$attr['TYPE'] : '';
|
||||
|
||||
if(array_key_exists('CONSTRAINT', $attr))
|
||||
{
|
||||
if(is_array($attr['CONSTRAINT']))
|
||||
{
|
||||
$sql .= "(";
|
||||
foreach($attr['CONSTRAINT'] as $constraint)
|
||||
{
|
||||
$sql .= (is_string($constraint) ? "'".$constraint."'" : $constraint).", ";
|
||||
}
|
||||
$sql = rtrim($sql, ', '). ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql .= '('.$attr['CONSTRAINT'].')';
|
||||
}
|
||||
}
|
||||
|
||||
$sql .= array_key_exists('CHARSET', $attr) ? $this->process_charset($attr['CHARSET'], false) : '';
|
||||
|
||||
if (array_key_exists('UNSIGNED', $attr) and $attr['UNSIGNED'] === true)
|
||||
{
|
||||
$sql .= ' UNSIGNED';
|
||||
}
|
||||
|
||||
if(array_key_exists('DEFAULT', $attr))
|
||||
{
|
||||
$sql .= ' DEFAULT '.(($attr['DEFAULT'] instanceof \Database_Expression) ? $attr['DEFAULT'] : $this->_connection->quote($attr['DEFAULT']));
|
||||
}
|
||||
|
||||
if(array_key_exists('NULL', $attr) and $attr['NULL'] === true)
|
||||
{
|
||||
$sql .= ' NULL';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sql .= ' NOT NULL';
|
||||
}
|
||||
|
||||
if (array_key_exists('AUTO_INCREMENT', $attr) and $attr['AUTO_INCREMENT'] === true)
|
||||
{
|
||||
$sql .= ' AUTO_INCREMENT';
|
||||
}
|
||||
|
||||
if (array_key_exists('PRIMARY_KEY', $attr) and $attr['PRIMARY_KEY'] === true)
|
||||
{
|
||||
$sql .= ' PRIMARY KEY';
|
||||
}
|
||||
|
||||
if (array_key_exists('COMMENT', $attr))
|
||||
{
|
||||
$sql .= ' COMMENT '.$this->_connection->escape($attr['COMMENT']);
|
||||
}
|
||||
|
||||
if (array_key_exists('FIRST', $attr) and $attr['FIRST'] === true)
|
||||
{
|
||||
$sql .= ' FIRST';
|
||||
}
|
||||
elseif (array_key_exists('AFTER', $attr) and strval($attr['AFTER']))
|
||||
{
|
||||
$sql .= ' AFTER '.$this->_connection->quote_identifier($attr['AFTER']);
|
||||
}
|
||||
|
||||
$sql_fields[] = $sql;
|
||||
}
|
||||
|
||||
return implode(',', $sql_fields);
|
||||
}
|
||||
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_SQLite_Builder_Delete extends \Database_Query_Builder_Delete
|
||||
{
|
||||
/**
|
||||
* Compile the SQL query and return it.
|
||||
*
|
||||
* @param mixed $db Database_Connection instance or instance name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compile($db = null)
|
||||
{
|
||||
if ( ! $db instanceof \Database_Connection)
|
||||
{
|
||||
// Get the database instance
|
||||
$db = \Database_Connection::instance($db);
|
||||
}
|
||||
|
||||
// Start a deletion query
|
||||
$query = 'DELETE FROM '.$db->quote_table($this->_table);
|
||||
|
||||
if ( ! empty($this->_where))
|
||||
{
|
||||
// Add deletion conditions
|
||||
$query .= ' WHERE '.$this->_compile_conditions($db, $this->_where);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_order_by))
|
||||
{
|
||||
// Add sorting
|
||||
$query .= ' '.$this->_compile_order_by($db, $this->_order_by);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_SQLite_Builder_Update extends \Database_Query_Builder_Update
|
||||
{
|
||||
/**
|
||||
* Compile the SQL query and return it.
|
||||
*
|
||||
* @param mixed $db Database instance or instance name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function compile($db = null)
|
||||
{
|
||||
if ( ! $db instanceof \Database_Connection)
|
||||
{
|
||||
// Get the database instance
|
||||
$db = \Database_Connection::instance($db);
|
||||
}
|
||||
|
||||
// Start an update query
|
||||
$query = 'UPDATE '.$db->quote_table($this->_table);
|
||||
|
||||
if ( ! empty($this->_join))
|
||||
{
|
||||
// Add tables to join
|
||||
$query .= ' '.$this->_compile_join($db, $this->_join);
|
||||
}
|
||||
|
||||
// Add the columns to update
|
||||
$query .= ' SET '.$this->_compile_set($db, $this->_set);
|
||||
|
||||
if ( ! empty($this->_where))
|
||||
{
|
||||
// Add selection conditions
|
||||
$query .= ' WHERE '.$this->_compile_conditions($db, $this->_where);
|
||||
}
|
||||
|
||||
if ( ! empty($this->_order_by))
|
||||
{
|
||||
// Add sorting
|
||||
$query .= ' '.$this->_compile_order_by($db, $this->_order_by);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_SQLite_Connection extends \Database_PDO_Connection
|
||||
{
|
||||
/**
|
||||
* Create a new [Database_Query_Builder_Update].
|
||||
*
|
||||
* // UPDATE users
|
||||
* $query = $db->update('users');
|
||||
*
|
||||
* @param string table to update
|
||||
* @return Database_Query_Builder_Update
|
||||
*/
|
||||
public function update($table = null)
|
||||
{
|
||||
return new Database_SQLite_Builder_Update($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query_Builder_Delete].
|
||||
*
|
||||
* // DELETE FROM users
|
||||
* $query = $db->delete('users');
|
||||
*
|
||||
* @param string table to delete from
|
||||
* @return Database_Query_Builder_Delete
|
||||
*/
|
||||
public function delete($table = null)
|
||||
{
|
||||
return new Database_SQLite_Builder_Delete($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* List tables
|
||||
*
|
||||
* @param string $like
|
||||
*
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public function list_tables($like = null)
|
||||
{
|
||||
$query = 'SELECT name FROM sqlite_master WHERE type = "table" AND name != "sqlite_sequence" AND name != "geometry_columns" AND name != "spatial_ref_sys"'
|
||||
. 'UNION ALL SELECT name FROM sqlite_temp_master '
|
||||
. 'WHERE type = "table"';
|
||||
|
||||
if (is_string($like))
|
||||
{
|
||||
$query .= ' AND name LIKE ' . $this->quote($like);
|
||||
}
|
||||
|
||||
$query .= ' ORDER BY name';
|
||||
|
||||
$q = $this->_connection->prepare($query);
|
||||
$q->execute();
|
||||
$result = $q->fetchAll();
|
||||
|
||||
$tables = array();
|
||||
foreach ($result as $row)
|
||||
{
|
||||
$tables[] = reset($row);
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* List table columns
|
||||
*
|
||||
* @param string $table table name
|
||||
* @param string $like column name pattern
|
||||
* @return array array of column structure
|
||||
*/
|
||||
public function list_columns($table, $like = null)
|
||||
{
|
||||
$query = "PRAGMA table_info('" . $this->quote_table($table) . "')";
|
||||
$q = $this->_connection->prepare($query);
|
||||
$q->execute();
|
||||
$result = $q->fetchAll();
|
||||
|
||||
$count = 0;
|
||||
$columns = array();
|
||||
foreach ($result as $row)
|
||||
{
|
||||
$column = $this->datatype($row['type']);
|
||||
|
||||
$column['name'] = $row['name'];
|
||||
$column['default'] = $row['dflt_value'];
|
||||
$column['data_type'] = $row['type'];
|
||||
$column['null'] = $row['notnull'];
|
||||
$column['ordinal_position'] = ++$count;
|
||||
$column['comment'] = '';
|
||||
$column['extra'] = $row['cid'];
|
||||
$column['key'] = $row['pk'];
|
||||
$column['privileges'] = '';
|
||||
|
||||
$columns[$row['name']] = $column;
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the charset
|
||||
*
|
||||
* @param string $charset
|
||||
*/
|
||||
public function set_charset($charset)
|
||||
{
|
||||
// Make sure the database is connected
|
||||
$this->_connection or $this->connect();
|
||||
|
||||
if ($charset)
|
||||
{
|
||||
$this->_connection->exec('PRAGMA encoding = ' . $this->quote($charset));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,170 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @author cocteau666@gmail.com
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @copyright 2008 - 2009 Kohana Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Database_Sqlsrv_Connection extends \Database_PDO_Connection
|
||||
{
|
||||
/**
|
||||
* Stores the database configuration locally and name the instance.
|
||||
*
|
||||
* [!!] This method cannot be accessed directly, you must use [static::instance].
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $config
|
||||
*/
|
||||
protected function __construct($name, array $config)
|
||||
{
|
||||
// this driver only works on Windows
|
||||
if (php_uname('s') !== 'Windows')
|
||||
{
|
||||
throw new \Database_Exception('The "SQLSRV" database driver works only on Windows. On *nix, use the "DBLib" driver instead.');
|
||||
}
|
||||
|
||||
parent::__construct($name, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* List tables
|
||||
*
|
||||
* @param string $like
|
||||
*
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public function list_tables($like = null)
|
||||
{
|
||||
$query = "SELECT name FROM sys.objects WHERE type = 'U' AND name != 'sysdiagrams'";
|
||||
|
||||
if (is_string($like))
|
||||
{
|
||||
$query .= " AND name LIKE ".$this->quote($like);
|
||||
}
|
||||
|
||||
// Find all table names
|
||||
$result = $this->query(\DB::SELECT, $query, false);
|
||||
|
||||
$tables = array();
|
||||
foreach ($result as $row)
|
||||
{
|
||||
$tables[] = reset($row);
|
||||
}
|
||||
|
||||
return $tables;
|
||||
}
|
||||
|
||||
/**
|
||||
* List table columns
|
||||
*
|
||||
* @param string $table table name
|
||||
* @param string $like column name pattern
|
||||
* @return array array of column structure
|
||||
*/
|
||||
public function list_columns($table, $like = null)
|
||||
{
|
||||
$query = "SELECT * FROM Sys.Columns WHERE id = object_id('" . $this->quote_table($table) . "')";
|
||||
|
||||
if (is_string($like))
|
||||
{
|
||||
// Search for column names
|
||||
$query .= " AND name LIKE ".$this->quote($like);
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$columns = array();
|
||||
foreach ($result as $row)
|
||||
{
|
||||
list($type, $length) = $this->_parse_type($row['Type']);
|
||||
$column = $this->datatype($type);
|
||||
$column['name'] = $row['Field'];
|
||||
$column['default'] = $row['Default'];
|
||||
$column['data_type'] = $type;
|
||||
$column['null'] = ($row['Null'] == 'YES');
|
||||
$column['ordinal_position'] = ++$count;
|
||||
switch ($column['type'])
|
||||
{
|
||||
case 'float':
|
||||
if (isset($length))
|
||||
{
|
||||
list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length);
|
||||
}
|
||||
break;
|
||||
case 'int':
|
||||
if (isset($length))
|
||||
{
|
||||
$column['display'] = $length;
|
||||
}
|
||||
break;
|
||||
case 'string':
|
||||
switch ($column['data_type'])
|
||||
{
|
||||
case 'binary':
|
||||
case 'varbinary':
|
||||
$column['character_maximum_length'] = $length;
|
||||
break;
|
||||
case 'char':
|
||||
case 'varchar':
|
||||
$column['character_maximum_length'] = $length;
|
||||
case 'text':
|
||||
case 'tinytext':
|
||||
case 'mediumtext':
|
||||
case 'longtext':
|
||||
$column['collation_name'] = $row['Collation'];
|
||||
break;
|
||||
case 'enum':
|
||||
case 'set':
|
||||
$column['collation_name'] = $row['Collation'];
|
||||
$column['options'] = explode('\',\'', substr($length, 1, -1));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$column['comment'] = $row['Comment'];
|
||||
$column['extra'] = $row['Extra'];
|
||||
$column['key'] = $row['Key'];
|
||||
$column['privileges'] = $row['Privileges'];
|
||||
$columns[$row['Field']] = $column;
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the charset
|
||||
*
|
||||
* @param string $charset
|
||||
*/
|
||||
public function set_charset($charset)
|
||||
{
|
||||
if ($charset == 'utf8' or $charset = 'utf-8')
|
||||
{
|
||||
// use utf8 encoding
|
||||
$this->_connection->setAttribute(\PDO::SQLSRV_ATTR_ENCODING, \PDO::SQLSRV_ENCODING_UTF8);
|
||||
}
|
||||
elseif ($charset == 'system')
|
||||
{
|
||||
// use system encoding
|
||||
$this->_connection->setAttribute(\PDO::SQLSRV_ATTR_ENCODING, \PDO::SQLSRV_ENCODING_SYSTEM);
|
||||
}
|
||||
elseif (is_numeric($charset))
|
||||
{
|
||||
// charset code passed directly
|
||||
$this->_connection->setAttribute(\PDO::SQLSRV_ATTR_ENCODING, $charset);
|
||||
}
|
||||
else
|
||||
{
|
||||
// unknown charset, use the default encoding
|
||||
$this->_connection->setAttribute(\PDO::SQLSRV_ATTR_ENCODING, \PDO::SQLSRV_ENCODING_DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,429 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Date Class
|
||||
*
|
||||
* DateTime replacement that supports internationalization and does correction to GMT
|
||||
* when your webserver isn't configured correctly.
|
||||
*
|
||||
* @package Fuel
|
||||
* @subpackage Core
|
||||
* @category Core
|
||||
* @link http://docs.fuelphp.com/classes/date.html
|
||||
*
|
||||
* Notes:
|
||||
* - Always returns Date objects, will accept both Date objects and UNIX timestamps
|
||||
* - create_time() uses strptime and has currently a very bad hack to use strtotime for windows servers
|
||||
* - Uses strftime formatting for dates www.php.net/manual/en/function.strftime.php
|
||||
*/
|
||||
class Date
|
||||
{
|
||||
/**
|
||||
* Time constants (and only those that are constant, thus not MONTH/YEAR)
|
||||
*/
|
||||
const WEEK = 604800;
|
||||
const DAY = 86400;
|
||||
const HOUR = 3600;
|
||||
const MINUTE = 60;
|
||||
|
||||
/**
|
||||
* @var int server's time() offset from gmt in seconds
|
||||
*/
|
||||
protected static $server_gmt_offset = 0;
|
||||
|
||||
/**
|
||||
* @var string the timezone to be used to output formatted data
|
||||
*/
|
||||
public static $display_timezone = null;
|
||||
|
||||
public static function _init()
|
||||
{
|
||||
static::$server_gmt_offset = \Config::get('server_gmt_offset', 0);
|
||||
|
||||
static::$display_timezone = \Config::get('default_timezone') ?: date_default_timezone_get();
|
||||
|
||||
// Ugly temporary windows fix because windows doesn't support strptime()
|
||||
// It attempts conversion between glibc style formats and PHP's internal style format (no 100% match!)
|
||||
if ( ! function_exists('strptime') && ! function_exists('Fuel\Core\strptime'))
|
||||
{
|
||||
function strptime($input, $format)
|
||||
{
|
||||
// convert the format string from glibc to date format (where possible)
|
||||
$new_format = str_replace(
|
||||
array('%a', '%A', '%d', '%e', '%j', '%u', '%w', '%U', '%V', '%W', '%b', '%B', '%h', '%m', '%C', '%g', '%G', '%y', '%Y', '%H', '%k', '%I', '%l', '%M', '%p', '%P', '%r', '%R', '%S', '%T', '%X', '%z', '%Z', '%c', '%D', '%F', '%s', '%x', '%n', '%t', '%%'),
|
||||
array('D', 'l', 'd', 'j', 'N', 'z', 'w', '[^^]', 'W', '[^^]', 'M', 'F', 'M', 'm', '[^^]', 'Y', 'o', 'y', 'Y', 'H', 'G', 'h', 'g', 'i', 'A', 'a', 'H:i:s A', 'H:i', 's', 'H:i:s', '[^^]', 'O', 'T ', '[^^]', 'm/d/Y', 'Y-m-d', 'U', '[^^]', "\n", "\t", '%'),
|
||||
$format
|
||||
);
|
||||
|
||||
// parse the input
|
||||
$parsed = date_parse_from_format($new_format, $input);
|
||||
|
||||
// parse succesful?
|
||||
if (is_array($parsed) and empty($parsed['errors']))
|
||||
{
|
||||
return array(
|
||||
'tm_year' => $parsed['year'] - 1900,
|
||||
'tm_mon' => $parsed['month'] - 1,
|
||||
'tm_mday' => $parsed['day'],
|
||||
'tm_hour' => $parsed['hour'] ?: 0,
|
||||
'tm_min' => $parsed['minute'] ?: 0,
|
||||
'tm_sec' => $parsed['second'] ?: 0,
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$masks = array(
|
||||
'%d' => '(?P<d>[0-9]{2})',
|
||||
'%m' => '(?P<m>[0-9]{2})',
|
||||
'%Y' => '(?P<Y>[0-9]{4})',
|
||||
'%H' => '(?P<H>[0-9]{2})',
|
||||
'%M' => '(?P<M>[0-9]{2})',
|
||||
'%S' => '(?P<S>[0-9]{2})',
|
||||
);
|
||||
|
||||
$rexep = "#" . strtr(preg_quote($format), $masks) . "#";
|
||||
|
||||
if ( ! preg_match($rexep, $input, $result))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return array(
|
||||
"tm_sec" => isset($result['S']) ? (int) $result['S'] : 0,
|
||||
"tm_min" => isset($result['M']) ? (int) $result['M'] : 0,
|
||||
"tm_hour" => isset($result['H']) ? (int) $result['H'] : 0,
|
||||
"tm_mday" => isset($result['d']) ? (int) $result['d'] : 0,
|
||||
"tm_mon" => isset($result['m']) ? ($result['m'] ? $result['m'] - 1 : 0) : 0,
|
||||
"tm_year" => isset($result['Y']) ? ($result['Y'] > 1900 ? $result['Y'] - 1900 : 0) : 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// This really is some fugly code, but someone at PHP HQ decided strptime should
|
||||
// output this awful array instead of a timestamp LIKE EVERYONE ELSE DOES!!!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Date object from timestamp, timezone is optional
|
||||
*
|
||||
* @param int $timestamp UNIX timestamp from current server
|
||||
* @param string $timezone valid PHP timezone from www.php.net/timezones
|
||||
* @return Date
|
||||
*/
|
||||
public static function forge($timestamp = null, $timezone = null)
|
||||
{
|
||||
return new static($timestamp, $timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current time with offset
|
||||
*
|
||||
* @param string $timezone valid PHP timezone from www.php.net/timezones
|
||||
* @return Date
|
||||
*/
|
||||
public static function time($timezone = null)
|
||||
{
|
||||
return static::forge(null, $timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current time with offset
|
||||
*
|
||||
* @param string $timezone valid PHP timezone from www.php.net/timezones
|
||||
* @return string
|
||||
*/
|
||||
public static function display_timezone($timezone = null)
|
||||
{
|
||||
is_string($timezone) and static::$display_timezone = $timezone;
|
||||
|
||||
return static::$display_timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the date config file to translate string input to timestamp
|
||||
*
|
||||
* @param string $input date/time input
|
||||
* @param string $pattern_key key name of pattern in config file
|
||||
* @return Date
|
||||
*/
|
||||
public static function create_from_string($input, $pattern_key = 'local')
|
||||
{
|
||||
\Config::load('date', 'date');
|
||||
|
||||
$pattern = \Config::get('date.patterns.'.$pattern_key, null);
|
||||
empty($pattern) and $pattern = $pattern_key;
|
||||
|
||||
$time = strptime($input, $pattern);
|
||||
if ($time === false)
|
||||
{
|
||||
throw new \UnexpectedValueException('Input was not recognized by pattern.');
|
||||
}
|
||||
|
||||
// convert it into a timestamp
|
||||
$timestamp = mktime($time['tm_hour'], $time['tm_min'], $time['tm_sec'],
|
||||
$time['tm_mon'] + 1, $time['tm_mday'], $time['tm_year'] + 1900);
|
||||
|
||||
if ($timestamp === false)
|
||||
{
|
||||
throw new \OutOfBoundsException('Input was invalid.'.(PHP_INT_SIZE == 4 ? ' A 32-bit system only supports dates between 1901 and 2038.' : ''));
|
||||
}
|
||||
|
||||
return static::forge($timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches an array of Date objects per interval within a range
|
||||
*
|
||||
* @param int|Date $start start of the range
|
||||
* @param int|Date $end end of the range
|
||||
* @param int|string $interval Length of the interval in seconds or valid strtotime time difference
|
||||
* @return array array of Date objects
|
||||
*/
|
||||
public static function range_to_array($start, $end, $interval = '+1 Day')
|
||||
{
|
||||
// make sure start and end are date objects
|
||||
$start = ( ! $start instanceof Date) ? static::forge($start) : $start;
|
||||
$end = ( ! $end instanceof Date) ? static::forge($end) : $end;
|
||||
|
||||
$range = array();
|
||||
|
||||
// if end > start, the range is empty
|
||||
if ($end->get_timestamp() >= $start->get_timestamp())
|
||||
{
|
||||
$current = $start;
|
||||
$increment = $interval;
|
||||
|
||||
do
|
||||
{
|
||||
$range[] = $current;
|
||||
|
||||
if ( ! is_int($interval))
|
||||
{
|
||||
$increment = strtotime($interval, $current->get_timestamp()) - $current->get_timestamp();
|
||||
if ($increment <= 0)
|
||||
{
|
||||
throw new \UnexpectedValueException('Input was not recognized by pattern.');
|
||||
}
|
||||
}
|
||||
|
||||
$current = static::forge($current->get_timestamp() + $increment);
|
||||
}
|
||||
while ($current->get_timestamp() <= $end->get_timestamp());
|
||||
}
|
||||
|
||||
return $range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of days in the requested month
|
||||
*
|
||||
* @param int $month month as a number (1-12)
|
||||
* @param int $year the year, leave empty for current
|
||||
* @return int the number of days in the month
|
||||
*/
|
||||
public static function days_in_month($month, $year = null)
|
||||
{
|
||||
$year = ! empty($year) ? (int) $year : (int) date('Y');
|
||||
$month = (int) $month;
|
||||
|
||||
if ($month < 1 or $month > 12)
|
||||
{
|
||||
throw new \UnexpectedValueException('Invalid input for month given.');
|
||||
}
|
||||
elseif ($month == 2)
|
||||
{
|
||||
if ($year % 400 == 0 or ($year % 4 == 0 and $year % 100 != 0))
|
||||
{
|
||||
return 29;
|
||||
}
|
||||
}
|
||||
|
||||
$days_in_month = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
|
||||
return $days_in_month[$month-1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time ago
|
||||
*
|
||||
* @param int $timestamp UNIX timestamp from current server
|
||||
* @param int $from_timestamp UNIX timestamp to compare against. Default to the current time
|
||||
* @param string $unit Unit to return the result in
|
||||
* @return string Time ago
|
||||
*/
|
||||
public static function time_ago($timestamp, $from_timestamp = null, $unit = null)
|
||||
{
|
||||
if ($timestamp === null)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
! is_numeric($timestamp) and $timestamp = static::create_from_string($timestamp)->get_timestamp();
|
||||
|
||||
$from_timestamp == null and $from_timestamp = time();
|
||||
|
||||
\Lang::load('date', true);
|
||||
|
||||
$difference = $from_timestamp - $timestamp;
|
||||
$periods = array('second', 'minute', 'hour', 'day', 'week', 'month', 'year', 'decade');
|
||||
$lengths = array(60, 60, 24, 7, 4.35, 12, 10);
|
||||
|
||||
for ($j = 0; isset($lengths[$j]) and $difference >= $lengths[$j] and (empty($unit) or $unit != $periods[$j]); $j++)
|
||||
{
|
||||
$difference /= $lengths[$j];
|
||||
}
|
||||
|
||||
$difference = round($difference);
|
||||
|
||||
if ($difference != 1)
|
||||
{
|
||||
$periods[$j] = \Inflector::pluralize($periods[$j]);
|
||||
}
|
||||
|
||||
$text = \Lang::get('date.text', array(
|
||||
'time' => \Lang::get('date.'.$periods[$j], array('t' => $difference)),
|
||||
));
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var int instance timestamp
|
||||
*/
|
||||
protected $timestamp;
|
||||
|
||||
/**
|
||||
* @var string output timezone
|
||||
*/
|
||||
protected $timezone;
|
||||
|
||||
public function __construct($timestamp = null, $timezone = null)
|
||||
{
|
||||
is_null($timestamp) and $timestamp = time() + static::$server_gmt_offset;
|
||||
! $timezone and $timezone = \Fuel::$timezone;
|
||||
|
||||
$this->timestamp = $timestamp;
|
||||
$this->set_timezone($timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date formatted according to the current locale
|
||||
*
|
||||
* @param string $pattern_key either a named pattern from date config file or a pattern, defaults to 'local'
|
||||
* @param mixed $timezone vald timezone, or if true, output the time in local time instead of system time
|
||||
* @return string
|
||||
*/
|
||||
public function format($pattern_key = 'local', $timezone = null)
|
||||
{
|
||||
\Config::load('date', 'date');
|
||||
|
||||
$pattern = \Config::get('date.patterns.'.$pattern_key, $pattern_key);
|
||||
|
||||
// determine the timezone to switch to
|
||||
$timezone === true and $timezone = static::$display_timezone;
|
||||
is_string($timezone) or $timezone = $this->timezone;
|
||||
|
||||
// Temporarily change timezone when different from default
|
||||
if (\Fuel::$timezone != $timezone)
|
||||
{
|
||||
date_default_timezone_set($timezone);
|
||||
}
|
||||
|
||||
// Create output
|
||||
$output = strftime($pattern, $this->timestamp);
|
||||
|
||||
// Change timezone back to default if changed previously
|
||||
if (\Fuel::$timezone != $timezone)
|
||||
{
|
||||
date_default_timezone_set(\Fuel::$timezone);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal timestamp
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_timestamp()
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal timezone
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_timezone()
|
||||
{
|
||||
return $this->timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal timezone or the display timezone abbreviation
|
||||
*
|
||||
* @param boolean $display_timezone
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_timezone_abbr($display_timezone = false)
|
||||
{
|
||||
// determine the timezone to switch to
|
||||
$display_timezone and $timezone = static::$display_timezone;
|
||||
empty($timezone) and $timezone = $this->timezone;
|
||||
|
||||
// Temporarily change timezone when different from default
|
||||
if (\Fuel::$timezone != $timezone)
|
||||
{
|
||||
date_default_timezone_set($timezone);
|
||||
}
|
||||
|
||||
// Create output
|
||||
$output = date('T');
|
||||
|
||||
// Change timezone back to default if changed previously
|
||||
if (\Fuel::$timezone != $timezone)
|
||||
{
|
||||
date_default_timezone_set(\Fuel::$timezone);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the timezone
|
||||
*
|
||||
* @param string $timezone timezone from www.php.net/timezones
|
||||
* @return Date
|
||||
*/
|
||||
public function set_timezone($timezone)
|
||||
{
|
||||
$this->timezone = $timezone;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows you to just put the object in a string and get it inserted in the default pattern
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->format();
|
||||
}
|
||||
}
|
@ -1,415 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Database object creation helper methods.
|
||||
*
|
||||
* @package Fuel\Database
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class DB
|
||||
{
|
||||
// Query types
|
||||
const SELECT = 1;
|
||||
const INSERT = 2;
|
||||
const UPDATE = 3;
|
||||
const DELETE = 4;
|
||||
|
||||
public static $query_count = 0;
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query] of the given type.
|
||||
*
|
||||
* // Create a new SELECT query
|
||||
* $query = DB::query('SELECT * FROM users');
|
||||
*
|
||||
* // Create a new DELETE query
|
||||
* $query = DB::query('DELETE FROM users WHERE id = 5');
|
||||
*
|
||||
* Specifying the type changes the returned result. When using
|
||||
* `DB::SELECT`, a [Database_Query_Result] will be returned.
|
||||
* `DB::INSERT` queries will return the insert id and number of rows.
|
||||
* For all other queries, the number of affected rows is returned.
|
||||
*
|
||||
* @param string SQL statement
|
||||
* @param integer type: DB::SELECT, DB::UPDATE, etc
|
||||
* @return Database_Query
|
||||
*/
|
||||
public static function query($sql, $type = null)
|
||||
{
|
||||
return new \Database_Query($sql, $type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the last query
|
||||
*
|
||||
* @return string the last query
|
||||
*/
|
||||
public static function last_query($db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db)->last_query;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the DB drivers error info
|
||||
*
|
||||
* @return mixed the DB drivers error info
|
||||
*/
|
||||
public static function error_info($db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db)->error_info();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a database instance
|
||||
*
|
||||
* @return Database_Connection
|
||||
*/
|
||||
public static function instance($db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query_Builder_Select]. Each argument will be
|
||||
* treated as a column. To generate a `foo AS bar` alias, use an array.
|
||||
*
|
||||
* // SELECT id, username
|
||||
* $query = DB::select('id', 'username');
|
||||
*
|
||||
* // SELECT id AS user_id
|
||||
* $query = DB::select(array('id', 'user_id'));
|
||||
*
|
||||
* @param mixed column name or array($column, $alias) or object
|
||||
* @param ...
|
||||
* @return Database_Query_Builder_Select
|
||||
*/
|
||||
public static function select($args = null)
|
||||
{
|
||||
return \Database_Connection::instance()->select(func_get_args());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query_Builder_Select] from an array of columns.
|
||||
*
|
||||
* // SELECT id, username
|
||||
* $query = DB::select_array(array('id', 'username'));
|
||||
*
|
||||
* @param array columns to select
|
||||
* @return Database_Query_Builder_Select
|
||||
*/
|
||||
public static function select_array(array $columns = null)
|
||||
{
|
||||
return \Database_Connection::instance()->select($columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query_Builder_Insert].
|
||||
*
|
||||
* // INSERT INTO users (id, username)
|
||||
* $query = DB::insert('users', array('id', 'username'));
|
||||
*
|
||||
* @param string table to insert into
|
||||
* @param array list of column names or array($column, $alias) or object
|
||||
* @return Database_Query_Builder_Insert
|
||||
*/
|
||||
public static function insert($table = null, array $columns = null)
|
||||
{
|
||||
return \Database_Connection::instance()->insert($table, $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query_Builder_Update].
|
||||
*
|
||||
* // UPDATE users
|
||||
* $query = DB::update('users');
|
||||
*
|
||||
* @param string table to update
|
||||
* @return Database_Query_Builder_Update
|
||||
*/
|
||||
public static function update($table = null)
|
||||
{
|
||||
return \Database_Connection::instance()->update($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Query_Builder_Delete].
|
||||
*
|
||||
* // DELETE FROM users
|
||||
* $query = DB::delete('users');
|
||||
*
|
||||
* @param string table to delete from
|
||||
* @return Database_Query_Builder_Delete
|
||||
*/
|
||||
public static function delete($table = null)
|
||||
{
|
||||
return \Database_Connection::instance()->delete($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Expression] which is not escaped. An expression
|
||||
* is the only way to use SQL functions within query builders.
|
||||
*
|
||||
* $expression = DB::expr('COUNT(users.id)');
|
||||
*
|
||||
* @param string $string expression
|
||||
* @return Database_Expression
|
||||
*/
|
||||
public static function expr($string)
|
||||
{
|
||||
return new \Database_Expression($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new [Database_Expression] containing a quoted identifier. An expression
|
||||
* is the only way to use SQL functions within query builders.
|
||||
*
|
||||
* $expression = DB::identifier('users.id'); // returns `users`.`id` for MySQL
|
||||
*
|
||||
* @param string $string the string to quote
|
||||
* @param string $db the database connection to use
|
||||
* @return Database_Expression
|
||||
*/
|
||||
public static function identifier($string, $db = null)
|
||||
{
|
||||
return new \Database_Expression(static::quote_identifier($string, $db));
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote a value for an SQL query.
|
||||
*
|
||||
* @param string $string the string to quote
|
||||
* @param string $db the database connection to use
|
||||
* @return string the quoted value
|
||||
*/
|
||||
public static function quote($string, $db = null)
|
||||
{
|
||||
if (is_array($string))
|
||||
{
|
||||
foreach ($string as $k => $s)
|
||||
{
|
||||
$string[$k] = static::quote($s, $db);
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
return \Database_Connection::instance($db)->quote($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quotes an identifier so it is ready to use in a query.
|
||||
*
|
||||
* @param string $string the string to quote
|
||||
* @param string $db the database connection to use
|
||||
* @return string the quoted identifier
|
||||
*/
|
||||
public static function quote_identifier($string, $db = null)
|
||||
{
|
||||
if (is_array($string))
|
||||
{
|
||||
foreach ($string as $k => $s)
|
||||
{
|
||||
$string[$k] = static::quote_identifier($s, $db);
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
return \Database_Connection::instance($db)->quote_identifier($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote a database table name and adds the table prefix if needed.
|
||||
*
|
||||
* @param string $string the string to quote
|
||||
* @param string $db the database connection to use
|
||||
* @return string the quoted identifier
|
||||
*/
|
||||
public static function quote_table($string, $db = null)
|
||||
{
|
||||
if (is_array($string))
|
||||
{
|
||||
foreach ($string as $k => $s)
|
||||
{
|
||||
$string[$k] = static::quote_table($s, $db);
|
||||
}
|
||||
return $string;
|
||||
}
|
||||
return \Database_Connection::instance($db)->quote_table($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a string to be ready for use in a sql query
|
||||
*
|
||||
* @param string $string the string to escape
|
||||
* @param string $db the database connection to use
|
||||
* @return string the escaped string
|
||||
*/
|
||||
public static function escape($string, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db)->escape($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* If a table name is given it will return the table name with the configured
|
||||
* prefix. If not, then just the prefix is returned
|
||||
*
|
||||
* @param string $table the table name to prefix
|
||||
* @param string $db the database connection to use
|
||||
* @return string the prefixed table name or the prefix
|
||||
*/
|
||||
public static function table_prefix($table = null, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db)->table_prefix($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all of the columns in a table. Optionally, a LIKE string can be
|
||||
* used to search for specific fields.
|
||||
*
|
||||
* // Get all columns from the "users" table
|
||||
* $columns = DB::list_columns('users');
|
||||
*
|
||||
* // Get all name-related columns
|
||||
* $columns = DB::list_columns('users', '%name%');
|
||||
*
|
||||
* @param string table to get columns from
|
||||
* @param string column to search for
|
||||
* @param string the database connection to use
|
||||
* @return array
|
||||
*/
|
||||
public static function list_columns($table = null, $like = null, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db)->list_columns($table, $like);
|
||||
}
|
||||
|
||||
/**
|
||||
* If a table name is given it will return the table name with the configured
|
||||
* prefix. If not, then just the prefix is returned
|
||||
*
|
||||
* @param string $table the table name to prefix
|
||||
* @param string $db the database connection to use
|
||||
* @return string the prefixed table name or the prefix
|
||||
*/
|
||||
public static function list_tables($like = null, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db)->list_tables($like);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a normalized array describing the SQL data type
|
||||
*
|
||||
* DB::datatype('char');
|
||||
*
|
||||
* @param string SQL data type
|
||||
* @param string db connection
|
||||
* @return array
|
||||
*/
|
||||
public static function datatype($type, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db)->datatype($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of records in a table.
|
||||
*
|
||||
* // Get the total number of records in the "users" table
|
||||
* $count = DB::count_records('users');
|
||||
*
|
||||
* @param mixed table name string or array(query, alias)
|
||||
* @param string db connection
|
||||
* @return integer
|
||||
*/
|
||||
public static function count_records($table, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db)->count_records($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of records in the last query, without LIMIT or OFFSET applied.
|
||||
*
|
||||
* // Get the total number of records that match the last query
|
||||
* $count = $db->count_last_query();
|
||||
*
|
||||
* @param string db connection
|
||||
* @return integer
|
||||
*/
|
||||
public static function count_last_query($db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db)->count_last_query();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection character set. This is called automatically by [static::connect].
|
||||
*
|
||||
* DB::set_charset('utf8');
|
||||
*
|
||||
* @throws Database_Exception
|
||||
* @param string character set name
|
||||
* @param string db connection
|
||||
* @return void
|
||||
*/
|
||||
public static function set_charset($charset, $db = null)
|
||||
{
|
||||
\Database_Connection::instance($db)->set_charset($charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a connection is in transaction.
|
||||
*
|
||||
* DB::in_transaction();
|
||||
*
|
||||
* @param string db connection
|
||||
* @return bool
|
||||
*/
|
||||
public static function in_transaction($db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db)->in_transaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins a transaction on instance
|
||||
*
|
||||
* DB::start_transaction();
|
||||
*
|
||||
* @param string db connection
|
||||
* @return bool
|
||||
*/
|
||||
public static function start_transaction($db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db)->start_transaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Commits all pending transactional queries
|
||||
*
|
||||
* DB::commit_transaction();
|
||||
*
|
||||
* @param string db connection
|
||||
* @return bool
|
||||
*/
|
||||
public static function commit_transaction($db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db)->commit_transaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollsback pending transactional queries
|
||||
* Rollback to the current level uses SAVEPOINT,
|
||||
* it does not work if current RDBMS does not support them.
|
||||
* In this case system rollsback all queries and closes the transaction
|
||||
*
|
||||
* DB::rollback_transaction();
|
||||
*
|
||||
* @param string $db connection
|
||||
* @param bool $rollback_all:
|
||||
* true - rollback everything and close transaction;
|
||||
* false - rollback only current level
|
||||
* @return bool
|
||||
*/
|
||||
public static function rollback_transaction($db = null, $rollback_all = true)
|
||||
{
|
||||
return \Database_Connection::instance($db)->rollback_transaction($rollback_all);
|
||||
}
|
||||
|
||||
}
|
@ -1,445 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* DBUtil Class
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
* @author Dan Horrigan
|
||||
*/
|
||||
class DBUtil
|
||||
{
|
||||
/**
|
||||
* @var string $connection the database connection (identifier)
|
||||
*/
|
||||
protected static $connection = null;
|
||||
|
||||
/*
|
||||
* Load the db config, the Database_Connection might not have fired jet.
|
||||
*
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
\Config::load('db', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the database connection to use for following DBUtil calls.
|
||||
*
|
||||
* @throws \FuelException
|
||||
* @param string $connection connection name, null for default
|
||||
*/
|
||||
public static function set_connection($connection)
|
||||
{
|
||||
if ($connection !== null and ! is_string($connection))
|
||||
{
|
||||
throw new \FuelException('A connection must be supplied as a string.');
|
||||
}
|
||||
|
||||
static::$connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a database. Will throw a Database_Exception if it cannot.
|
||||
*
|
||||
* @throws Fuel\Database_Exception
|
||||
* @param string $database the database name
|
||||
* @param string $charset the character set
|
||||
* @param boolean $if_not_exists whether to add an IF NOT EXISTS statement.
|
||||
* @param string $db the database connection to use
|
||||
* @return int the number of affected rows
|
||||
*/
|
||||
public static function create_database($database, $charset = null, $if_not_exists = true, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'create_database',
|
||||
array(
|
||||
$database,
|
||||
$charset,
|
||||
$if_not_exists,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a database. Will throw a Database_Exception if it cannot.
|
||||
*
|
||||
* @throws Fuel\Database_Exception
|
||||
* @param string $database the database name
|
||||
* @param string $db the database connection to use
|
||||
* @return int the number of affected rows
|
||||
*/
|
||||
public static function drop_database($database, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'drop_database',
|
||||
array(
|
||||
$database,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a table. Will throw a Database_Exception if it cannot.
|
||||
*
|
||||
* @throws Fuel\Database_Exception
|
||||
* @param string $table the table name
|
||||
* @param string $db the database connection to use
|
||||
* @return int the number of affected rows
|
||||
*/
|
||||
public static function drop_table($table, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'drop_table',
|
||||
array(
|
||||
$table,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a table. Will throw a Database_Exception if it cannot.
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
* @param string $table the old table name
|
||||
* @param string $new_table_name the new table name
|
||||
* @param string $db the database connection to use
|
||||
* @return int the number of affected
|
||||
*/
|
||||
public static function rename_table($table, $new_table_name, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'rename_table',
|
||||
array(
|
||||
$table,
|
||||
$new_table_name,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table.
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
* @param string $table the table name
|
||||
* @param array $fields the fields array
|
||||
* @param array $primary_keys an array of primary keys
|
||||
* @param boolean $if_not_exists whether to add an IF NOT EXISTS statement.
|
||||
* @param string|boolean $engine storage engine overwrite
|
||||
* @param string $charset default charset overwrite
|
||||
* @param array $foreign_keys an array of foreign keys
|
||||
* @param string $db the database connection to use
|
||||
* @return int number of affected rows.
|
||||
*/
|
||||
public static function create_table($table, $fields, $primary_keys = array(), $if_not_exists = true, $engine = false, $charset = null, $foreign_keys = array(), $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'create_table',
|
||||
array(
|
||||
$table,
|
||||
$fields,
|
||||
$primary_keys,
|
||||
$if_not_exists,
|
||||
$engine,
|
||||
$charset,
|
||||
$foreign_keys,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds fields to a table a table. Will throw a Database_Exception if it cannot.
|
||||
*
|
||||
* @throws Fuel\Database_Exception
|
||||
* @param string $table the table name
|
||||
* @param array $fields the new fields
|
||||
* @param string $db the database connection to use
|
||||
* @return int the number of affected
|
||||
*/
|
||||
public static function add_fields($table, $fields, $db = null)
|
||||
{
|
||||
return static::alter_fields('ADD', $table, $fields, $db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies fields in a table. Will throw a Database_Exception if it cannot.
|
||||
*
|
||||
* @throws Fuel\Database_Exception
|
||||
* @param string $table the table name
|
||||
* @param array $fields the modified fields
|
||||
* @param string $db the database connection to use
|
||||
* @return int the number of affected
|
||||
*/
|
||||
public static function modify_fields($table, $fields, $db = null)
|
||||
{
|
||||
return static::alter_fields('MODIFY', $table, $fields, $db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops fields from a table a table. Will throw a Database_Exception if it cannot.
|
||||
*
|
||||
* @throws Fuel\Database_Exception
|
||||
* @param string $table the table name
|
||||
* @param string|array $fields the fields
|
||||
* @param string $db the database connection to use
|
||||
* @return int the number of affected
|
||||
*/
|
||||
public static function drop_fields($table, $fields, $db = null)
|
||||
{
|
||||
return static::alter_fields('DROP', $table, $fields, $db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an index on that table.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $table
|
||||
* @param string $index_name
|
||||
* @param string $index_columns
|
||||
* @param string $index (should be 'unique', 'fulltext', 'spatial' or 'nonclustered')
|
||||
* @param string $db the database connection to use
|
||||
* @return bool
|
||||
* @author Thomas Edwards
|
||||
*/
|
||||
public static function create_index($table, $index_columns, $index_name = '', $index = '', $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'create_index',
|
||||
array(
|
||||
$table,
|
||||
$index_columns,
|
||||
$index_name,
|
||||
$index,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop an index from a table.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $table
|
||||
* @param string $index_name
|
||||
* @param string $db the database connection to use
|
||||
* @return bool
|
||||
* @author Thomas Edwards
|
||||
*/
|
||||
public static function drop_index($table, $index_name, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'drop_index',
|
||||
array(
|
||||
$table,
|
||||
$index_name,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a single foreign key to a table
|
||||
*
|
||||
* @param string $table the table name
|
||||
* @param array $foreign_key a single foreign key
|
||||
* @param string $db the database connection to use
|
||||
* @return int number of affected rows
|
||||
*/
|
||||
public static function add_foreign_key($table, $foreign_key, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'add_foreign_key',
|
||||
array(
|
||||
$table,
|
||||
$foreign_key,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a foreign key from a table
|
||||
*
|
||||
* @param string $table the table name
|
||||
* @param string $fk_name the foreign key name
|
||||
* @param string $db the database connection to use
|
||||
* @return int number of affected rows
|
||||
*/
|
||||
public static function drop_foreign_key($table, $fk_name, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'drop_foreign_key',
|
||||
array(
|
||||
$table,
|
||||
$fk_name,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string of foreign keys
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
* @param array $foreign_keys Array of foreign key rules
|
||||
* @param string $db the database connection to use
|
||||
* @return string the formatted foreign key string
|
||||
*/
|
||||
public static function process_foreign_keys($foreign_keys, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'process_foreign_keys',
|
||||
array(
|
||||
$foreign_keys,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates a table.
|
||||
*
|
||||
* @throws Fuel\Database_Exception
|
||||
* @param string $table the table name
|
||||
* @param string $db the database connection to use
|
||||
* @return int the number of affected rows
|
||||
*/
|
||||
public static function truncate_table($table, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'truncate_table',
|
||||
array(
|
||||
$table,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes a table.
|
||||
*
|
||||
* @param string $table the table name
|
||||
* @param string $db the database connection to use
|
||||
* @return bool whether the table is OK
|
||||
*/
|
||||
public static function analyze_table($table, $db = null)
|
||||
{
|
||||
return static::table_maintenance('ANALYZE TABLE', $table, $db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a table.
|
||||
*
|
||||
* @param string $table the table name
|
||||
* @param string $db the database connection to use
|
||||
* @return bool whether the table is OK
|
||||
*/
|
||||
public static function check_table($table, $db = null)
|
||||
{
|
||||
return static::table_maintenance('CHECK TABLE', $table, $db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimizes a table.
|
||||
*
|
||||
* @param string $table the table name
|
||||
* @param string $db the database connection to use
|
||||
* @return bool whether the table has been optimized
|
||||
*/
|
||||
public static function optimize_table($table, $db = null)
|
||||
{
|
||||
return static::table_maintenance('OPTIMIZE TABLE', $table, $db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Repairs a table.
|
||||
*
|
||||
* @param string $table the table name
|
||||
* @param string $db the database connection to use
|
||||
* @return bool whether the table has been repaired
|
||||
*/
|
||||
public static function repair_table($table, $db = null)
|
||||
{
|
||||
return static::table_maintenance('REPAIR TABLE', $table, $db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given table exists.
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
* @param string $table Table name
|
||||
* @param string $db the database connection to use
|
||||
* @return bool
|
||||
*/
|
||||
public static function table_exists($table, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'table_exists',
|
||||
array(
|
||||
$table,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if given field(s) in a given table exists.
|
||||
*
|
||||
* @throws \Database_Exception
|
||||
* @param string $table Table name
|
||||
* @param string|array $columns columns to check
|
||||
* @param string $db the database connection to use
|
||||
* @return bool
|
||||
*/
|
||||
public static function field_exists($table, $columns, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'field_exists',
|
||||
array(
|
||||
$table,
|
||||
$columns,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected static function alter_fields($type, $table, $fields, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'alter_fields',
|
||||
array(
|
||||
$type,
|
||||
$table,
|
||||
$fields,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes table maintenance. Will throw FuelException when the operation is not supported.
|
||||
*
|
||||
* @throws FuelException
|
||||
* @param string $table the table name
|
||||
* @param string $db the database connection to use
|
||||
* @return bool whether the operation has succeeded
|
||||
*/
|
||||
protected static function table_maintenance($operation, $table, $db = null)
|
||||
{
|
||||
return \Database_Connection::instance($db ? $db : static::$connection)->schema(
|
||||
'table_maintenance',
|
||||
array(
|
||||
$operation,
|
||||
$table,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -1,551 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Debug class
|
||||
*
|
||||
* The Debug class is a simple utility for debugging variables, objects, arrays, etc by outputting information to the display.
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
* @link http://docs.fuelphp.com/classes/debug.html
|
||||
*/
|
||||
class Debug
|
||||
{
|
||||
public static $max_nesting_level = 5;
|
||||
|
||||
public static $js_toggle_open = false;
|
||||
|
||||
protected static $js_displayed = false;
|
||||
|
||||
protected static $files = array();
|
||||
|
||||
/**
|
||||
* Quick and nice way to output a mixed variable to the browser
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public static function dump()
|
||||
{
|
||||
if (\Fuel::$is_cli)
|
||||
{
|
||||
// no fancy flying, jump dump 'm
|
||||
foreach (func_get_args() as $arg)
|
||||
{
|
||||
var_dump($arg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$backtrace = debug_backtrace();
|
||||
|
||||
// locate the first file entry that isn't this class itself
|
||||
foreach ($backtrace as $stack => $trace)
|
||||
{
|
||||
if (isset($trace['file']))
|
||||
{
|
||||
// If being called from within, show the file above in the backtrack
|
||||
if (strpos($trace['file'], 'core/classes/debug.php') !== false)
|
||||
{
|
||||
$callee = $backtrace[$stack+1];
|
||||
$label = \Inflector::humanize($backtrace[$stack+1]['function']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$callee = $trace;
|
||||
$label = 'Debug';
|
||||
}
|
||||
|
||||
$callee['file'] = \Fuel::clean_path($callee['file']);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$arguments = func_get_args();
|
||||
|
||||
if ( ! static::$js_displayed)
|
||||
{
|
||||
echo <<<JS
|
||||
<script type="text/javascript">function fuel_debug_toggle(a){if(document.getElementById){if(document.getElementById(a).style.display=="none"){document.getElementById(a).style.display="block"}else{document.getElementById(a).style.display="none"}}else{if(document.layers){if(document.id.display=="none"){document.id.display="block"}else{document.id.display="none"}}else{if(document.all.id.style.display=="none"){document.all.id.style.display="block"}else{document.all.id.style.display="none"}}}};</script>
|
||||
JS;
|
||||
static::$js_displayed = true;
|
||||
}
|
||||
echo '<div class="fuelphp-dump" style="font-size: 13px;background: #EEE !important; border:1px solid #666; color: #000 !important; padding:10px;">';
|
||||
echo '<h1 style="border-bottom: 1px solid #CCC; padding: 0 0 5px 0; margin: 0 0 5px 0; font: bold 120% sans-serif;">'.$callee['file'].' @ line: '.$callee['line'].'</h1>';
|
||||
echo '<pre style="overflow:auto;font-size:100%;">';
|
||||
|
||||
$count = count($arguments);
|
||||
for ($i = 1; $i <= $count; $i++)
|
||||
{
|
||||
echo '<strong>Variable #'.$i.':</strong>'.PHP_EOL;
|
||||
echo static::format('', $arguments[$i - 1]);
|
||||
echo PHP_EOL.PHP_EOL;
|
||||
}
|
||||
|
||||
echo "</pre>";
|
||||
echo "</div>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick and nice way to output a mixed variable to the browser
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public static function inspect()
|
||||
{
|
||||
$backtrace = debug_backtrace();
|
||||
|
||||
// If being called from within, show the file above in the backtrack
|
||||
if (strpos($backtrace[0]['file'], 'core/classes/debug.php') !== false)
|
||||
{
|
||||
$callee = $backtrace[1];
|
||||
$label = \Inflector::humanize($backtrace[1]['function']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$callee = $backtrace[0];
|
||||
$label = 'Debug';
|
||||
}
|
||||
|
||||
$arguments = func_get_args();
|
||||
$total_arguments = count($arguments);
|
||||
|
||||
$callee['file'] = \Fuel::clean_path($callee['file']);
|
||||
|
||||
if ( ! static::$js_displayed)
|
||||
{
|
||||
echo <<<JS
|
||||
<script type="text/javascript">function fuel_debug_toggle(a){if(document.getElementById){if(document.getElementById(a).style.display=="none"){document.getElementById(a).style.display="block"}else{document.getElementById(a).style.display="none"}}else{if(document.layers){if(document.id.display=="none"){document.id.display="block"}else{document.id.display="none"}}else{if(document.all.id.style.display=="none"){document.all.id.style.display="block"}else{document.all.id.style.display="none"}}}};</script>
|
||||
JS;
|
||||
static::$js_displayed = true;
|
||||
}
|
||||
echo '<div class="fuelphp-inspect" style="font-size: 13px;background: #EEE !important; border:1px solid #666; color: #000 !important; padding:10px;">';
|
||||
echo '<h1 style="border-bottom: 1px solid #CCC; padding: 0 0 5px 0; margin: 0 0 5px 0; font: bold 120% sans-serif;">'.$callee['file'].' @ line: '.$callee['line'].'</h1>';
|
||||
echo '<pre style="overflow:auto;font-size:100%;">';
|
||||
$i = 0;
|
||||
foreach ($arguments as $argument)
|
||||
{
|
||||
echo '<strong>'.$label.' #'.(++$i).' of '.$total_arguments.'</strong>:<br />';
|
||||
echo static::format('...', $argument);
|
||||
echo '<br />';
|
||||
}
|
||||
|
||||
echo "</pre>";
|
||||
echo "</div>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given $var's output in a nice looking, Foldable interface.
|
||||
*
|
||||
* @param string $name the name of the var
|
||||
* @param mixed $var the variable
|
||||
* @param int $level the indentation level
|
||||
* @param string $indent_char the indentation character
|
||||
* @param string $scope
|
||||
* @return string the formatted string.
|
||||
*/
|
||||
public static function format($name, $var, $level = 0, $indent_char = ' ', $scope = '')
|
||||
{
|
||||
$return = str_repeat($indent_char, $level);
|
||||
if (is_array($var))
|
||||
{
|
||||
$id = 'fuel_debug_'.mt_rand();
|
||||
$return .= "<i>{$scope}</i> <strong>".htmlentities($name)."</strong>";
|
||||
$return .= " (Array, ".count($var)." element".(count($var)!=1 ? "s" : "").")";
|
||||
if (count($var) > 0 and static::$max_nesting_level > $level)
|
||||
{
|
||||
$return .= " <a href=\"javascript:fuel_debug_toggle('$id');\" title=\"Click to ".(static::$js_toggle_open ? "close" : "open")."\">↵</a>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$return .= "\n";
|
||||
}
|
||||
|
||||
if (static::$max_nesting_level <= $level)
|
||||
{
|
||||
$return .= str_repeat($indent_char, $level + 1)."...\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sub_return = '';
|
||||
foreach ($var as $key => $val)
|
||||
{
|
||||
$sub_return .= static::format($key, $val, $level + 1);
|
||||
}
|
||||
if (count($var) > 0)
|
||||
{
|
||||
$return .= "<span id=\"$id\" style=\"display: ".(static::$js_toggle_open ? "block" : "none").";\">$sub_return</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$return .= $sub_return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
elseif (is_string($var))
|
||||
{
|
||||
$return .= "<i>{$scope}</i> <strong>".htmlentities($name)."</strong> (String): <span style=\"color:#E00000;\">\"".\Security::htmlentities($var)."\"</span> (".strlen($var)." characters)\n";
|
||||
}
|
||||
elseif (is_float($var))
|
||||
{
|
||||
$return .= "<i>{$scope}</i> <strong>".htmlentities($name)."</strong> (Float): {$var}\n";
|
||||
}
|
||||
elseif (is_long($var))
|
||||
{
|
||||
$return .= "<i>{$scope}</i> <strong>".htmlentities($name)."</strong> (Integer): {$var}\n";
|
||||
}
|
||||
elseif (is_null($var))
|
||||
{
|
||||
$return .= "<i>{$scope}</i> <strong>".htmlentities($name)."</strong> : null\n";
|
||||
}
|
||||
elseif (is_bool($var))
|
||||
{
|
||||
$return .= "<i>{$scope}</i> <strong>".htmlentities($name)."</strong> (Boolean): ".($var ? 'true' : 'false')."\n";
|
||||
}
|
||||
elseif (is_double($var))
|
||||
{
|
||||
$return .= "<i>{$scope}</i> <strong>".htmlentities($name)."</strong> (Double): {$var}\n";
|
||||
}
|
||||
elseif (is_object($var))
|
||||
{
|
||||
// dirty hack to get the object id
|
||||
ob_start();
|
||||
var_dump($var);
|
||||
$contents = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
// process it based on the xdebug presence and configuration
|
||||
if (extension_loaded('xdebug') and ini_get('xdebug.overload_var_dump') === '1')
|
||||
{
|
||||
if (ini_get('html_errors'))
|
||||
{
|
||||
preg_match('~(.*?)\)\[<i>(\d+)(.*)~', $contents, $matches);
|
||||
}
|
||||
else
|
||||
{
|
||||
preg_match('~class (.*?)#(\d+)(.*)~', $contents, $matches);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
preg_match('~object\((.*?)#(\d+)(.*)~', $contents, $matches);
|
||||
}
|
||||
|
||||
$id = 'fuel_debug_'.mt_rand();
|
||||
$rvar = new \ReflectionObject($var);
|
||||
$vars = $rvar->getProperties();
|
||||
$return .= "<i>{$scope}</i> <strong>{$name}</strong> (Object #".$matches[2]."): ".get_class($var);
|
||||
if (count($vars) > 0 and static::$max_nesting_level > $level)
|
||||
{
|
||||
$return .= " <a href=\"javascript:fuel_debug_toggle('$id');\" title=\"Click to ".(static::$js_toggle_open ? "close" : "open")."\">↵</a>\n";
|
||||
}
|
||||
$return .= "\n";
|
||||
|
||||
$sub_return = '';
|
||||
foreach ($rvar->getProperties() as $prop)
|
||||
{
|
||||
$prop->isPublic() or $prop->setAccessible(true);
|
||||
if ($prop->isPrivate())
|
||||
{
|
||||
$scope = 'private';
|
||||
}
|
||||
elseif ($prop->isProtected())
|
||||
{
|
||||
$scope = 'protected';
|
||||
}
|
||||
else
|
||||
{
|
||||
$scope = 'public';
|
||||
}
|
||||
if (static::$max_nesting_level <= $level)
|
||||
{
|
||||
$sub_return .= str_repeat($indent_char, $level + 1)."...\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sub_return .= static::format($prop->name, $prop->getValue($var), $level + 1, $indent_char, $scope);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($vars) > 0)
|
||||
{
|
||||
$return .= "<span id=\"$id\" style=\"display: ".(static::$js_toggle_open ? "block" : "none").";\">$sub_return</span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$return .= $sub_return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$return .= "<i>{$scope}</i> <strong>".htmlentities($name)."</strong>: {$var}\n";
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the debug lines from the specified file
|
||||
*
|
||||
* @access protected
|
||||
* @param string $filepath the file path
|
||||
* @param int $line_num the line number
|
||||
* @param bool $highlight whether to use syntax highlighting or not
|
||||
* @param int $padding the amount of line padding
|
||||
* @return array
|
||||
*/
|
||||
public static function file_lines($filepath, $line_num, $highlight = true, $padding = 5)
|
||||
{
|
||||
// deal with eval'd code and runtime-created function
|
||||
if (strpos($filepath, 'eval()\'d code') !== false or strpos($filepath, 'runtime-created function') !== false)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
// We cache the entire file to reduce disk IO for multiple errors
|
||||
if ( ! isset(static::$files[$filepath]))
|
||||
{
|
||||
static::$files[$filepath] = file($filepath, FILE_IGNORE_NEW_LINES);
|
||||
array_unshift(static::$files[$filepath], '');
|
||||
}
|
||||
|
||||
$start = $line_num - $padding;
|
||||
if ($start < 0)
|
||||
{
|
||||
$start = 0;
|
||||
}
|
||||
|
||||
$length = ($line_num - $start) + $padding + 1;
|
||||
if (($start + $length) > count(static::$files[$filepath]) - 1)
|
||||
{
|
||||
$length = NULL;
|
||||
}
|
||||
|
||||
$debug_lines = array_slice(static::$files[$filepath], $start, $length, TRUE);
|
||||
|
||||
if ($highlight)
|
||||
{
|
||||
$to_replace = array('<code>', '</code>', '<span style="color: #0000BB"><?php ', "\n");
|
||||
$replace_with = array('', '', '<span style="color: #0000BB">', '');
|
||||
|
||||
foreach ($debug_lines as & $line)
|
||||
{
|
||||
$line = str_replace($to_replace, $replace_with, highlight_string('<?php ' . $line, TRUE));
|
||||
}
|
||||
}
|
||||
|
||||
return $debug_lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the call stack from here, or the supplied one.
|
||||
*
|
||||
* @param array $trace (optional) A backtrace to output
|
||||
* @return string Formatted backtrace
|
||||
*/
|
||||
public static function backtrace($trace = null)
|
||||
{
|
||||
$trace or $trace = debug_backtrace();
|
||||
|
||||
if (\Fuel::$is_cli) {
|
||||
// Special case for CLI since the var_dump of a backtrace is of little use.
|
||||
$str = '';
|
||||
foreach ($trace as $i => $frame)
|
||||
{
|
||||
$line = "#$i\t";
|
||||
|
||||
if ( ! isset($frame['file']))
|
||||
{
|
||||
$line .= "[internal function]";
|
||||
}
|
||||
else
|
||||
{
|
||||
$line .= $frame['file'] . ":" . $frame['line'];
|
||||
}
|
||||
|
||||
$line .= "\t";
|
||||
|
||||
if (isset($frame['function']))
|
||||
{
|
||||
if (isset($frame['class']))
|
||||
{
|
||||
$line .= $frame['class'] . '::';
|
||||
}
|
||||
|
||||
$line .= $frame['function'] . "()";
|
||||
}
|
||||
|
||||
$str .= $line . "\n";
|
||||
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
else
|
||||
{
|
||||
return static::dump($trace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a list of all currently declared classes.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function classes()
|
||||
{
|
||||
return static::dump(get_declared_classes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a list of all currently declared interfaces (PHP5 only).
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function interfaces()
|
||||
{
|
||||
return static::dump(get_declared_interfaces());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a list of all currently included (or required) files.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function includes()
|
||||
{
|
||||
return static::dump(get_included_files());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a list of all currently declared functions.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function functions()
|
||||
{
|
||||
return static::dump(get_defined_functions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a list of all currently declared constants.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function constants()
|
||||
{
|
||||
return static::dump(get_defined_constants());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a list of all currently loaded PHP extensions.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function extensions()
|
||||
{
|
||||
return static::dump(get_loaded_extensions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a list of all HTTP request headers.
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function headers()
|
||||
{
|
||||
// get the current request headers and dump them
|
||||
return static::dump(\Input::headers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a list of the configuration settings read from <i>php.ini</i>
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
*/
|
||||
public static function phpini()
|
||||
{
|
||||
if ( ! is_readable(get_cfg_var('cfg_file_path')))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// render it
|
||||
return static::dump(parse_ini_file(get_cfg_var('cfg_file_path'), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Benchmark anything that is callable
|
||||
*
|
||||
* @access public
|
||||
* @param callable $callable
|
||||
* @param array $params
|
||||
* @static
|
||||
* @return array
|
||||
*/
|
||||
public static function benchmark($callable, array $params = array())
|
||||
{
|
||||
// get the before-benchmark time
|
||||
if (function_exists('getrusage'))
|
||||
{
|
||||
$dat = getrusage();
|
||||
$utime_before = $dat['ru_utime.tv_sec'] + round($dat['ru_utime.tv_usec']/1000000, 4);
|
||||
$stime_before = $dat['ru_stime.tv_sec'] + round($dat['ru_stime.tv_usec']/1000000, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
list($usec, $sec) = explode(" ", microtime());
|
||||
$utime_before = ((float) $usec + (float) $sec);
|
||||
$stime_before = 0;
|
||||
}
|
||||
|
||||
// call the function to be benchmarked
|
||||
$result = is_callable($callable) ? call_fuel_func_array($callable, $params) : null;
|
||||
|
||||
// get the after-benchmark time
|
||||
if (function_exists('getrusage'))
|
||||
{
|
||||
$dat = getrusage();
|
||||
$utime_after = $dat['ru_utime.tv_sec'] + round($dat['ru_utime.tv_usec']/1000000, 4);
|
||||
$stime_after = $dat['ru_stime.tv_sec'] + round($dat['ru_stime.tv_usec']/1000000, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
list($usec, $sec) = explode(" ", microtime());
|
||||
$utime_after = ((float) $usec + (float) $sec);
|
||||
$stime_after = 0;
|
||||
}
|
||||
|
||||
return array(
|
||||
'user' => sprintf('%1.6f', $utime_after - $utime_before),
|
||||
'system' => sprintf('%1.6f', $stime_after - $stime_before),
|
||||
'result' => $result,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,325 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Exception class for standard PHP errors, this will make them catchable
|
||||
*/
|
||||
class PhpErrorException extends \ErrorException
|
||||
{
|
||||
public static $count = 0;
|
||||
|
||||
public static $loglevel = \Fuel::L_ERROR;
|
||||
|
||||
/**
|
||||
* Allow the error handler from recovering from error types defined in the config
|
||||
*/
|
||||
public function recover()
|
||||
{
|
||||
// handle the error based on the config and the environment we're in
|
||||
if (static::$count <= \Config::get('errors.throttle', 10))
|
||||
{
|
||||
logger(static::$loglevel, $this->code.' - '.$this->message.' in '.$this->file.' on line '.$this->line);
|
||||
|
||||
if (\Fuel::$env != \Fuel::PRODUCTION and ($this->code & error_reporting()) == $this->code)
|
||||
{
|
||||
static::$count++;
|
||||
\Errorhandler::exception_handler(new \ErrorException($this->message, $this->code, 0, $this->file, $this->line));
|
||||
}
|
||||
}
|
||||
elseif (\Fuel::$env != \Fuel::PRODUCTION
|
||||
and static::$count == (\Config::get('errors.throttle', 10) + 1)
|
||||
and ($this->severity & error_reporting()) == $this->severity)
|
||||
{
|
||||
static::$count++;
|
||||
\Errorhandler::notice('Error throttling threshold was reached, no more full error reports are shown.', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class Errorhandler
|
||||
{
|
||||
public static $loglevel = \Fuel::L_ERROR;
|
||||
|
||||
public static $levels = array(
|
||||
0 => 'Error',
|
||||
E_ERROR => 'Fatal 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',
|
||||
E_RECOVERABLE_ERROR => 'Runtime Recoverable error',
|
||||
E_DEPRECATED => 'Runtime Deprecated code usage',
|
||||
E_USER_DEPRECATED => 'User Deprecated code usage',
|
||||
);
|
||||
|
||||
public static $fatal_levels = array(E_PARSE, E_ERROR, E_USER_ERROR, E_COMPILE_ERROR);
|
||||
|
||||
public static $non_fatal_cache = array();
|
||||
|
||||
/**
|
||||
* Native PHP shutdown handler
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function shutdown_handler()
|
||||
{
|
||||
$last_error = error_get_last();
|
||||
|
||||
// Only show valid fatal errors
|
||||
if ($last_error AND in_array($last_error['type'], static::$fatal_levels))
|
||||
{
|
||||
$severity = static::$levels[$last_error['type']];
|
||||
logger(static::$loglevel, $severity.' - '.$last_error['message'].' in '.$last_error['file'].' on line '.$last_error['line']);
|
||||
|
||||
$error = new \ErrorException($last_error['message'], $last_error['type'], 0, $last_error['file'], $last_error['line']);
|
||||
if (\Fuel::$env != \Fuel::PRODUCTION)
|
||||
{
|
||||
static::show_php_error($error);
|
||||
}
|
||||
else
|
||||
{
|
||||
static::show_production_error($error);
|
||||
}
|
||||
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP Exception handler
|
||||
*
|
||||
* @param Exception $e the exception
|
||||
* @return bool
|
||||
*/
|
||||
public static function exception_handler($e)
|
||||
{
|
||||
// make sure we've got something useful passed
|
||||
if ($e instanceOf \Exception or (PHP_VERSION_ID >= 70000 and $e instanceOf \Error))
|
||||
{
|
||||
if (method_exists($e, 'handle'))
|
||||
{
|
||||
return $e->handle();
|
||||
}
|
||||
|
||||
$severity = ( ! isset(static::$levels[$e->getCode()])) ? $e->getCode() : static::$levels[$e->getCode()];
|
||||
logger(static::$loglevel, $severity.' - '.$e->getMessage().' in '.$e->getFile().' on line '.$e->getLine());
|
||||
|
||||
if (\Fuel::$env != \Fuel::PRODUCTION)
|
||||
{
|
||||
static::show_php_error($e);
|
||||
}
|
||||
else
|
||||
{
|
||||
static::show_production_error($e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
die('Something was passed to the Exception handler that was neither an Error or an Exception !!!');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* PHP Error handler
|
||||
*
|
||||
* @param int $severity the severity code
|
||||
* @param string $message the error message
|
||||
* @param string $filepath the path to the file throwing the error
|
||||
* @param int $line the line number of the error
|
||||
* @return bool whether to continue with execution
|
||||
*/
|
||||
public static function error_handler($severity, $message, $filepath, $line)
|
||||
{
|
||||
// don't do anything if error reporting is disabled
|
||||
if (error_reporting() !== 0)
|
||||
{
|
||||
$fatal = (bool) ( ! in_array($severity, \Config::get('errors.continue_on', array())));
|
||||
|
||||
if ($fatal)
|
||||
{
|
||||
throw new \PhpErrorException($message, $severity, 0, $filepath, $line);
|
||||
}
|
||||
else
|
||||
{
|
||||
// non-fatal, recover from the error
|
||||
$e = new \PhpErrorException($message, $severity, 0, $filepath, $line);
|
||||
$e->recover();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a small notice error, only when not in production or when forced.
|
||||
* This is used by several libraries to notify the developer of certain things.
|
||||
*
|
||||
* @param string $msg the message to display
|
||||
* @param bool $always_show whether to force display the notice or not
|
||||
* @return void
|
||||
*/
|
||||
public static function notice($msg, $always_show = false)
|
||||
{
|
||||
$trace = array_merge(array('file' => '(unknown)', 'line' => '(unknown)'), \Arr::get(debug_backtrace(), 1));
|
||||
logger(\Fuel::L_DEBUG, 'Notice - '.$msg.' in '.$trace['file'].' on line '.$trace['line']);
|
||||
|
||||
if (\Fuel::$is_test or ( ! $always_show and (\Fuel::$env == \Fuel::PRODUCTION or \Config::get('errors.notices', true) === false)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$data['message'] = $msg;
|
||||
$data['type'] = 'Notice';
|
||||
$data['filepath'] = \Fuel::clean_path($trace['file']);
|
||||
$data['line'] = $trace['line'];
|
||||
$data['function'] = $trace['function'];
|
||||
|
||||
echo \View::forge('errors'.DS.'php_short', $data, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows an error. It will stop script execution if the error code is not
|
||||
* in the errors.continue_on whitelist.
|
||||
*
|
||||
* @param Exception $e the exception to show
|
||||
* @return void
|
||||
*/
|
||||
protected static function show_php_error($e)
|
||||
{
|
||||
$fatal = (bool) ( ! in_array($e->getCode(), \Config::get('errors.continue_on', array())));
|
||||
$data = static::prepare_exception($e, $fatal);
|
||||
|
||||
if ($fatal)
|
||||
{
|
||||
$data['contents'] = ob_get_contents();
|
||||
while (ob_get_level() > 0)
|
||||
{
|
||||
ob_end_clean();
|
||||
}
|
||||
ob_start(\Config::get('ob_callback', null));
|
||||
}
|
||||
else
|
||||
{
|
||||
static::$non_fatal_cache[] = $data;
|
||||
}
|
||||
|
||||
if (\Fuel::$is_cli)
|
||||
{
|
||||
\Cli::write(\Cli::color($data['severity'].' - '.$data['message'].' in '.\Fuel::clean_path($data['filepath']).' on line '.$data['error_line'], 'red'));
|
||||
if (\Config::get('cli_backtrace'))
|
||||
{
|
||||
\Cli::write('Stack trace:');
|
||||
\Cli::write(\Debug::backtrace($e->getTrace()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($fatal)
|
||||
{
|
||||
if ( ! headers_sent())
|
||||
{
|
||||
$protocol = \Input::server('SERVER_PROTOCOL') ? \Input::server('SERVER_PROTOCOL') : 'HTTP/1.1';
|
||||
header($protocol.' 500 Internal Server Error');
|
||||
}
|
||||
|
||||
$data['non_fatal'] = static::$non_fatal_cache;
|
||||
|
||||
try
|
||||
{
|
||||
exit(\View::forge('errors'.DS.'php_fatal_error', $data, false));
|
||||
}
|
||||
catch (\FuelException $view_exception)
|
||||
{
|
||||
exit($data['severity'].' - '.$data['message'].' in '.\Fuel::clean_path($data['filepath']).' on line '.$data['error_line']);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
echo \View::forge('errors'.DS.'php_error', $data, false);
|
||||
}
|
||||
catch (\FuelException $e)
|
||||
{
|
||||
echo $e->getMessage().'<br />';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the errors/production view and exits. This only gets
|
||||
* called when an error occurs in production mode.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function show_production_error($e)
|
||||
{
|
||||
// when we're on CLI, always show the php error
|
||||
if (\Fuel::$is_cli)
|
||||
{
|
||||
return static::show_php_error($e);
|
||||
}
|
||||
|
||||
if ( ! headers_sent())
|
||||
{
|
||||
$protocol = \Input::server('SERVER_PROTOCOL') ? \Input::server('SERVER_PROTOCOL') : 'HTTP/1.1';
|
||||
header($protocol.' 500 Internal Server Error');
|
||||
}
|
||||
exit(\View::forge('errors'.DS.'production'));
|
||||
}
|
||||
|
||||
protected static function prepare_exception($e, $fatal = true)
|
||||
{
|
||||
$data = array();
|
||||
$data['type'] = get_class($e);
|
||||
$data['severity'] = $e->getCode();
|
||||
$data['message'] = $e->getMessage();
|
||||
$data['filepath'] = $e->getFile();
|
||||
$data['error_line'] = $e->getLine();
|
||||
$data['backtrace'] = $e->getTrace();
|
||||
|
||||
$data['severity'] = ( ! isset(static::$levels[$data['severity']])) ? $data['severity'] : static::$levels[$data['severity']];
|
||||
|
||||
foreach ($data['backtrace'] as $key => $trace)
|
||||
{
|
||||
if ( ! isset($trace['file']))
|
||||
{
|
||||
unset($data['backtrace'][$key]);
|
||||
}
|
||||
elseif ($trace['file'] == COREPATH.'classes/error.php')
|
||||
{
|
||||
unset($data['backtrace'][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$data['debug_lines'] = \Debug::file_lines($data['filepath'], $data['error_line'], $fatal);
|
||||
$data['orig_filepath'] = $data['filepath'];
|
||||
$data['filepath'] = \Fuel::clean_path($data['filepath']);
|
||||
|
||||
$data['filepath'] = str_replace("\\", "/", $data['filepath']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Event Class
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
* @author Eric Barnes
|
||||
* @author Harro "WanWizard" Verton
|
||||
*/
|
||||
abstract class Event
|
||||
{
|
||||
/**
|
||||
* @var array $instances Event_Instance container
|
||||
*/
|
||||
protected static $instances = array();
|
||||
|
||||
/**
|
||||
* Event instance forge.
|
||||
*
|
||||
* @param array $events events array
|
||||
* @return object new Event_Instance instance
|
||||
*/
|
||||
public static function forge(array $events = array())
|
||||
{
|
||||
return new \Event_Instance($events);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiton Event instance.
|
||||
*
|
||||
* @param string $name instance name
|
||||
* @param array $events events array
|
||||
* @return object Event_Instance object
|
||||
*/
|
||||
public static function instance($name = 'fuelphp', array $events = array())
|
||||
{
|
||||
if ( ! array_key_exists($name, static::$instances))
|
||||
{
|
||||
$events = array_merge(\Config::get('event.'.$name, array()), $events);
|
||||
$instance = static::forge($events);
|
||||
static::$instances[$name] = &$instance;
|
||||
}
|
||||
|
||||
return static::$instances[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Static call forwarder
|
||||
*
|
||||
* @param string $func method name
|
||||
* @param array $args passed arguments
|
||||
* @return mixed
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public static function __callStatic($func, $args)
|
||||
{
|
||||
$instance = static::instance();
|
||||
|
||||
if (method_exists($instance, $func))
|
||||
{
|
||||
return call_fuel_func_array(array($instance, $func), $args);
|
||||
}
|
||||
|
||||
throw new \BadMethodCallException('Call to undefined method: '.get_called_class().'::'.$func);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load events config
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
\Config::load('event', true);
|
||||
}
|
||||
}
|
@ -1,223 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Event Class
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
* @author Eric Barnes
|
||||
* @author Harro "WanWizard" Verton
|
||||
*/
|
||||
class Event_Instance
|
||||
{
|
||||
/**
|
||||
* @var array An array of listeners
|
||||
*/
|
||||
protected $_events = array();
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructor, sets all initial events.
|
||||
*
|
||||
* @param array $events events array
|
||||
*/
|
||||
public function __construct(array $events = array())
|
||||
{
|
||||
foreach($events as $event => $callback)
|
||||
{
|
||||
$this->register($event, $callback);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Register
|
||||
*
|
||||
* Registers a Callback for a given event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
// get any arguments passed
|
||||
$callback = func_get_args();
|
||||
|
||||
// if the arguments are valid, register the event
|
||||
if (isset($callback[0]) and is_string($callback[0]) and isset($callback[1]) and is_callable($callback[1]))
|
||||
{
|
||||
// make sure we have an array for this event
|
||||
isset($this->_events[$callback[0]]) or $this->_events[$callback[0]] = array();
|
||||
|
||||
// store the callback on the call stack
|
||||
if (empty($callback[2]))
|
||||
{
|
||||
array_unshift($this->_events[$callback[0]], $callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_events[$callback[0]][] = $callback;
|
||||
}
|
||||
|
||||
// and report success
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// can't register the event
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Unregister/remove one or all callbacks from event
|
||||
*
|
||||
* @param string $event event to remove from
|
||||
* @param mixed $callback callback to remove [optional, null for all]
|
||||
* @return boolean whether one or all callbacks have been removed
|
||||
*/
|
||||
public function unregister($event, $callback = null)
|
||||
{
|
||||
if (isset($this->_events[$event]))
|
||||
{
|
||||
if ($callback === null)
|
||||
{
|
||||
unset($this->_events[$event]);
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->_events[$event] as $i => $arguments)
|
||||
{
|
||||
if($callback === $arguments[1])
|
||||
{
|
||||
unset($this->_events[$event][$i]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Trigger
|
||||
*
|
||||
* Triggers an event and returns the results. The results can be returned
|
||||
* in the following formats:
|
||||
*
|
||||
* 'array'
|
||||
* 'json'
|
||||
* 'serialized'
|
||||
* 'string'
|
||||
*
|
||||
* @param string $event The name of the event
|
||||
* @param mixed $data Any data that is to be passed to the listener
|
||||
* @param string $return_type The return type
|
||||
* @param boolean $reversed Whether to fire events ordered LIFO instead of FIFO
|
||||
* @return mixed The return of the listeners, in the return type
|
||||
*/
|
||||
public function trigger($event, $data = '', $return_type = 'string', $reversed = false)
|
||||
{
|
||||
$calls = array();
|
||||
|
||||
// check if we have events registered
|
||||
if ($this->has_events($event))
|
||||
{
|
||||
$events = $reversed ? array_reverse($this->_events[$event], true) : $this->_events[$event];
|
||||
|
||||
// process them
|
||||
foreach ($events as $arguments)
|
||||
{
|
||||
// get rid of the event name
|
||||
array_shift($arguments);
|
||||
|
||||
// get the callback method
|
||||
$callback = array_shift($arguments);
|
||||
|
||||
// call the callback event
|
||||
if (is_callable($callback))
|
||||
{
|
||||
$calls[] = call_user_func($callback, $data, $arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_format_return($calls, $return_type);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Has Listeners
|
||||
*
|
||||
* Checks if the event has listeners
|
||||
*
|
||||
* @param string $event The name of the event
|
||||
* @return bool Whether the event has listeners
|
||||
*/
|
||||
public function has_events($event)
|
||||
{
|
||||
if (isset($this->_events[$event]) and count($this->_events[$event]) > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Format Return
|
||||
*
|
||||
* Formats the return in the given type
|
||||
*
|
||||
* @param array $calls The array of returns
|
||||
* @param string $return_type The return type
|
||||
* @return mixed The formatted return
|
||||
*/
|
||||
protected function _format_return(array $calls, $return_type)
|
||||
{
|
||||
switch ($return_type)
|
||||
{
|
||||
case 'array':
|
||||
return $calls;
|
||||
break;
|
||||
case 'json':
|
||||
return json_encode($calls);
|
||||
break;
|
||||
case 'none':
|
||||
return null;
|
||||
case 'serialized':
|
||||
return serialize($calls);
|
||||
break;
|
||||
case 'string':
|
||||
$str = '';
|
||||
foreach ($calls as $call)
|
||||
{
|
||||
$str .= $call;
|
||||
}
|
||||
return $str;
|
||||
break;
|
||||
default:
|
||||
return $calls;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,888 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Fieldset Class
|
||||
*
|
||||
* Define a set of fields that can be used to generate a form or to validate input.
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
*/
|
||||
class Fieldset
|
||||
{
|
||||
/**
|
||||
* @var Fieldset
|
||||
*/
|
||||
protected static $_instance;
|
||||
|
||||
/**
|
||||
* @var array contains references to all instantiations of Fieldset
|
||||
*/
|
||||
protected static $_instances = array();
|
||||
|
||||
/**
|
||||
* Create Fieldset object
|
||||
*
|
||||
* @param string $name Identifier for this fieldset
|
||||
* @param array $config Configuration array
|
||||
* @return Fieldset
|
||||
*/
|
||||
public static function forge($name = 'default', array $config = array())
|
||||
{
|
||||
if ($exists = static::instance($name))
|
||||
{
|
||||
\Errorhandler::notice('Fieldset with this name exists already, cannot be overwritten.');
|
||||
return $exists;
|
||||
}
|
||||
|
||||
static::$_instances[$name] = new static($name, $config);
|
||||
|
||||
if ($name == 'default')
|
||||
{
|
||||
static::$_instance = static::$_instances[$name];
|
||||
}
|
||||
|
||||
return static::$_instances[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a specific instance, or the default instance (is created if necessary)
|
||||
*
|
||||
* @param Fieldset $instance
|
||||
* @return Fieldset
|
||||
*/
|
||||
public static function instance($instance = null)
|
||||
{
|
||||
if ($instance !== null)
|
||||
{
|
||||
if ( ! array_key_exists($instance, static::$_instances))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return static::$_instances[$instance];
|
||||
}
|
||||
|
||||
if (static::$_instance === null)
|
||||
{
|
||||
static::$_instance = static::forge();
|
||||
}
|
||||
|
||||
return static::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string instance id
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var string tag used to wrap this instance
|
||||
*/
|
||||
protected $fieldset_tag = null;
|
||||
|
||||
/**
|
||||
* @var Fieldset instance to which this instance belongs
|
||||
*/
|
||||
protected $fieldset_parent = null;
|
||||
|
||||
/**
|
||||
* @var array instances that belong to this one
|
||||
*/
|
||||
protected $fieldset_children = array();
|
||||
|
||||
/**
|
||||
* @var array array of Fieldset_Field objects
|
||||
*/
|
||||
protected $fields = array();
|
||||
|
||||
/**
|
||||
* @var Validation instance of validation
|
||||
*/
|
||||
protected $validation;
|
||||
|
||||
/**
|
||||
* @var Form instance of form
|
||||
*/
|
||||
protected $form;
|
||||
|
||||
/**
|
||||
* @var array configuration array
|
||||
*/
|
||||
protected $config = array();
|
||||
|
||||
/**
|
||||
* @var array disabled fields array
|
||||
*/
|
||||
protected $disabled = array();
|
||||
|
||||
/**
|
||||
* @var string name of class providing the tabular form
|
||||
*/
|
||||
protected $tabular_form_model = null;
|
||||
|
||||
/**
|
||||
* @var string name of the relation of the parent object this tabular form is modeled on
|
||||
*/
|
||||
protected $tabular_form_relation = null;
|
||||
|
||||
/**
|
||||
* Object constructor
|
||||
*
|
||||
* @param string
|
||||
* @param array
|
||||
*/
|
||||
public function __construct($name = '', array $config = array())
|
||||
{
|
||||
// support new Fieldset($config) syntax
|
||||
if (is_array($name))
|
||||
{
|
||||
$config = $name;
|
||||
$name = '';
|
||||
}
|
||||
|
||||
if (isset($config['validation_instance']))
|
||||
{
|
||||
$this->validation($config['validation_instance']);
|
||||
unset($config['validation_instance']);
|
||||
}
|
||||
if (isset($config['form_instance']))
|
||||
{
|
||||
$this->form($config['form_instance']);
|
||||
unset($config['form_instance']);
|
||||
}
|
||||
|
||||
$this->name = (string) $name;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get related Validation instance or create it
|
||||
*
|
||||
* @param bool|Validation $instance
|
||||
* @return Validation
|
||||
*/
|
||||
public function validation($instance = true)
|
||||
{
|
||||
if ($instance instanceof Validation)
|
||||
{
|
||||
$this->validation = $instance;
|
||||
return $instance;
|
||||
}
|
||||
|
||||
if (empty($this->validation) and $instance === true)
|
||||
{
|
||||
$this->validation = \Validation::forge($this);
|
||||
}
|
||||
|
||||
return $this->validation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get related Form instance or create it
|
||||
*
|
||||
* @param bool|Form $instance
|
||||
* @return Form
|
||||
*/
|
||||
public function form($instance = true)
|
||||
{
|
||||
if ($instance instanceof Form)
|
||||
{
|
||||
$this->form = $instance;
|
||||
return $instance;
|
||||
}
|
||||
|
||||
if (empty($this->form) and $instance === true)
|
||||
{
|
||||
$this->form = \Form::forge($this);
|
||||
}
|
||||
|
||||
return $this->form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tag to be used for this fieldset
|
||||
*
|
||||
* @param string $tag
|
||||
* @return Fieldset this, to allow chaining
|
||||
*/
|
||||
public function set_fieldset_tag($tag)
|
||||
{
|
||||
$this->fieldset_tag = $tag;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the parent Fieldset instance
|
||||
*
|
||||
* @param Fieldset $fieldset parent fieldset to which this belongs
|
||||
* @return Fieldset
|
||||
*/
|
||||
public function set_parent(Fieldset $fieldset)
|
||||
{
|
||||
if ( ! empty($this->fieldset_parent))
|
||||
{
|
||||
throw new \RuntimeException('Fieldset already has a parent, belongs to "'.$this->parent()->name.'".');
|
||||
}
|
||||
|
||||
$children = $fieldset->children();
|
||||
while ($child = array_shift($children))
|
||||
{
|
||||
if ($child === $this)
|
||||
{
|
||||
throw new \RuntimeException('Circular reference detected, adding a Fieldset that\'s already a child as a parent.');
|
||||
}
|
||||
$children = array_merge($child->children(), $children);
|
||||
}
|
||||
|
||||
$this->fieldset_parent = $fieldset;
|
||||
$fieldset->add_child($this);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child Fieldset instance
|
||||
*
|
||||
* @param Fieldset $fieldset
|
||||
* @return Fieldset
|
||||
*/
|
||||
protected function add_child(Fieldset $fieldset)
|
||||
{
|
||||
if (is_null($fieldset->fieldset_tag))
|
||||
{
|
||||
$fieldset->fieldset_tag = 'fieldset';
|
||||
}
|
||||
|
||||
$this->fieldset_children[$fieldset->name] = $fieldset;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for Fieldset_Field objects
|
||||
*
|
||||
* @param string
|
||||
* @param string
|
||||
* @param array
|
||||
* @param array
|
||||
* @return Fieldset_Field
|
||||
*/
|
||||
public function add($name, $label = '', array $attributes = array(), array $rules = array())
|
||||
{
|
||||
if ($name instanceof Fieldset_Field)
|
||||
{
|
||||
if ($name->name == '' or $this->field($name->name) !== false)
|
||||
{
|
||||
throw new \RuntimeException('Fieldname empty or already exists in this Fieldset: "'.$name->name.'".');
|
||||
}
|
||||
|
||||
$name->set_fieldset($this);
|
||||
$this->fields[$name->name] = $name;
|
||||
return $name;
|
||||
}
|
||||
elseif ($name instanceof Fieldset)
|
||||
{
|
||||
if (empty($name->name) or $this->field($name->name) !== false)
|
||||
{
|
||||
throw new \RuntimeException('Fieldset name empty or already exists in this Fieldset: "'.$name->name.'".');
|
||||
}
|
||||
|
||||
$name->set_parent($this);
|
||||
$this->fields[$name->name] = $name;
|
||||
return $name;
|
||||
}
|
||||
|
||||
if (empty($name) || (is_array($name) and empty($name['name'])))
|
||||
{
|
||||
throw new \InvalidArgumentException('Cannot create field without name.');
|
||||
}
|
||||
|
||||
// Allow passing the whole config in an array, will overwrite other values if that's the case
|
||||
if (is_array($name))
|
||||
{
|
||||
$attributes = $name;
|
||||
$label = isset($name['label']) ? $name['label'] : '';
|
||||
$rules = isset($name['rules']) ? $name['rules'] : array();
|
||||
$name = $name['name'];
|
||||
}
|
||||
|
||||
// Check if it exists already, if so: return and give notice
|
||||
if ($field = $this->field($name))
|
||||
{
|
||||
\Errorhandler::notice('Field with this name exists already in this fieldset: "'.$name.'".');
|
||||
return $field;
|
||||
}
|
||||
|
||||
$this->fields[$name] = new \Fieldset_Field($name, $label, $attributes, $rules, $this);
|
||||
|
||||
return $this->fields[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new Fieldset_Field before an existing field in a Fieldset
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $label
|
||||
* @param array $attributes
|
||||
* @param array $rules
|
||||
* @param string $fieldname fieldname before which the new field is inserted in the fieldset
|
||||
* @return Fieldset_Field
|
||||
*/
|
||||
public function add_before($name, $label = '', array $attributes = array(), array $rules = array(), $fieldname = null)
|
||||
{
|
||||
$field = $this->add($name, $label, $attributes, $rules);
|
||||
|
||||
// Remove from tail and reinsert at correct location
|
||||
unset($this->fields[$field->name]);
|
||||
|
||||
if ( ! \Arr::insert_before_key($this->fields, array($field->name => $field), $fieldname, true))
|
||||
{
|
||||
throw new \RuntimeException('Field "'.$fieldname.'" does not exist in this Fieldset. Field "'.$name.'" can not be added.');
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new Fieldset_Field after an existing field in a Fieldset
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $label
|
||||
* @param array $attributes
|
||||
* @param array $rules
|
||||
* @param string $fieldname fieldname after which the new field is inserted in the fieldset
|
||||
* @return Fieldset_Field
|
||||
*/
|
||||
public function add_after($name, $label = '', array $attributes = array(), array $rules = array(), $fieldname = null)
|
||||
{
|
||||
$field = $this->add($name, $label, $attributes, $rules);
|
||||
|
||||
// Remove from tail and reinsert at correct location
|
||||
unset($this->fields[$field->name]);
|
||||
if ( ! \Arr::insert_after_key($this->fields, array($field->name => $field), $fieldname, true))
|
||||
{
|
||||
throw new \RuntimeException('Field "'.$fieldname.'" does not exist in this Fieldset. Field "'.$name.'" can not be added.');
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a field instance
|
||||
*
|
||||
* @param string field name or null to fetch an array of all
|
||||
* @return Fieldset this fieldset, for chaining
|
||||
*/
|
||||
public function delete($name)
|
||||
{
|
||||
if (isset($this->fields[$name]))
|
||||
{
|
||||
unset($this->fields[$name]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Field instance
|
||||
*
|
||||
* @param string|null $name field name or null to fetch an array of all
|
||||
* @param bool $flatten whether to get the fields array or flattened array
|
||||
* @param bool $tabular_form whether to include tabular form fields in the flattened array
|
||||
* @return Fieldset_Field|false returns false when field wasn't found
|
||||
*/
|
||||
public function field($name = null, $flatten = false, $tabular_form = true)
|
||||
{
|
||||
if ($name === null)
|
||||
{
|
||||
$fields = $this->fields;
|
||||
|
||||
if ($flatten)
|
||||
{
|
||||
foreach ($this->fieldset_children as $fs_name => $fieldset)
|
||||
{
|
||||
if ($tabular_form or ! $fieldset->get_tabular_form())
|
||||
{
|
||||
\Arr::insert_after_key($fields, $fieldset->field(null, true), $fs_name);
|
||||
}
|
||||
unset($fields[$fs_name]);
|
||||
}
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
|
||||
if ( ! array_key_exists($name, $this->fields))
|
||||
{
|
||||
if ($flatten)
|
||||
{
|
||||
foreach ($this->fieldset_children as $fieldset)
|
||||
{
|
||||
if (($field = $fieldset->field($name)) !== false)
|
||||
{
|
||||
return $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->fields[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a model's fields
|
||||
* The model must have a method "set_form_fields" that takes this Fieldset instance
|
||||
* and adds fields to it.
|
||||
*
|
||||
* @param string|Object $class either a full classname (including full namespace) or object instance
|
||||
* @param array|Object $instance array or object that has the exactly same named properties to populate the fields
|
||||
* @param string $method method name to call on model for field fetching
|
||||
* @return Fieldset this, to allow chaining
|
||||
*/
|
||||
public function add_model($class, $instance = null, $method = 'set_form_fields')
|
||||
{
|
||||
// Add model to validation callables for validation rules
|
||||
$this->validation()->add_callable($class);
|
||||
|
||||
if ((is_string($class) and is_callable($callback = array('\\'.$class, $method)))
|
||||
|| is_callable($callback = array($class, $method)))
|
||||
{
|
||||
$instance ? call_user_func($callback, $this, $instance) : call_user_func($callback, $this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a config value on the fieldset
|
||||
*
|
||||
* @param string $config
|
||||
* @param mixed $value
|
||||
* @return Fieldset this, to allow chaining
|
||||
*/
|
||||
public function set_config($config, $value = null)
|
||||
{
|
||||
$config = is_array($config) ? $config : array($config => $value);
|
||||
foreach ($config as $key => $value)
|
||||
{
|
||||
if (strpos($key, '.') === false)
|
||||
{
|
||||
$this->config[$key] = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
\Arr::set($this->config, $key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single or multiple config values by key
|
||||
*
|
||||
* @param string|array $key a single key or multiple in an array, empty to fetch all
|
||||
* @param mixed $default default output when config wasn't set
|
||||
* @return mixed|array a single config value or multiple in an array when $key input was an array
|
||||
*/
|
||||
public function get_config($key = null, $default = null)
|
||||
{
|
||||
if ($key === null)
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
if (is_array($key))
|
||||
{
|
||||
$output = array();
|
||||
foreach ($key as $k)
|
||||
{
|
||||
$output[$k] = $this->get_config($k, $default);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
if (strpos($key, '.') === false)
|
||||
{
|
||||
return array_key_exists($key, $this->config) ? $this->config[$key] : $default;
|
||||
}
|
||||
else
|
||||
{
|
||||
return \Arr::get($this->config, $key, $default);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the form's values using an input array or object
|
||||
*
|
||||
* @param array|object $input
|
||||
* @param bool $repopulate
|
||||
* @return Fieldset this, to allow chaining
|
||||
*/
|
||||
public function populate($input, $repopulate = false)
|
||||
{
|
||||
$fields = $this->field(null, true, false);
|
||||
foreach ($fields as $f)
|
||||
{
|
||||
if (is_array($input) or $input instanceof \ArrayAccess)
|
||||
{
|
||||
// convert form field array's to Fuel dotted notation
|
||||
$name = str_replace(array('[', ']'), array('.', ''), $f->name);
|
||||
|
||||
// fetch the value for this field, and set it if found
|
||||
$value = \Arr::get($input, $name, null);
|
||||
$value === null and $value = \Arr::get($input, $f->basename, null);
|
||||
$value !== null and $f->set_value($value, true);
|
||||
}
|
||||
elseif (is_object($input) and property_exists($input, $f->basename))
|
||||
{
|
||||
$f->set_value($input->{$f->basename}, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally overwrite values using post/get
|
||||
if ($repopulate)
|
||||
{
|
||||
$this->repopulate();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all fields to the input from get or post (depends on the form method attribute)
|
||||
*
|
||||
* @return Fieldset this, to allow chaining
|
||||
*/
|
||||
public function repopulate()
|
||||
{
|
||||
$fields = $this->field(null, true);
|
||||
foreach ($fields as $f)
|
||||
{
|
||||
// Don't repopulate the CSRF field
|
||||
if ($f->name === \Config::get('security.csrf_token_key', 'fuel_csrf_token'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($value = $f->input()) !== null)
|
||||
{
|
||||
$f->set_value($value, true);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the fieldset HTML
|
||||
*
|
||||
* @param mixed $action
|
||||
* @return string
|
||||
*/
|
||||
public function build($action = null)
|
||||
{
|
||||
$attributes = $this->get_config('form_attributes');
|
||||
if ($action and ($this->fieldset_tag == 'form' or empty($this->fieldset_tag)))
|
||||
{
|
||||
$attributes['action'] = $action;
|
||||
}
|
||||
|
||||
$open = ($this->fieldset_tag == 'form' or empty($this->fieldset_tag))
|
||||
? $this->form()->open($attributes).PHP_EOL
|
||||
: $this->form()->{$this->fieldset_tag.'_open'}($attributes);
|
||||
|
||||
$fields_output = '';
|
||||
|
||||
// construct the tabular form table header
|
||||
if ($this->tabular_form_relation)
|
||||
{
|
||||
$properties = call_user_func($this->tabular_form_model.'::properties');
|
||||
$primary_keys = call_user_func($this->tabular_form_model.'::primary_key');
|
||||
$fields_output .= '<thead><tr>'.PHP_EOL;
|
||||
foreach ($properties as $field => $settings)
|
||||
{
|
||||
if ((isset($settings['skip']) and $settings['skip']) or in_array($field, $primary_keys))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (isset($settings['form']['type']) and ($settings['form']['type'] === false or $settings['form']['type'] === 'hidden'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$fields_output .= "\t".'<th class="'.$this->tabular_form_relation.'_col_'.$field.'">'.(isset($settings['label']) ? \Lang::get($settings['label'], array(), $settings['label']) : '').'</th>'.PHP_EOL;
|
||||
}
|
||||
$fields_output .= "\t".'<th>'.\Config::get('form.tabular_delete_label', 'Delete?').'</th>'.PHP_EOL;
|
||||
|
||||
$fields_output .= '</tr></thead>'.PHP_EOL;
|
||||
}
|
||||
|
||||
foreach ($this->field() as $f)
|
||||
{
|
||||
in_array($f->name, $this->disabled) or $fields_output .= $f->build().PHP_EOL;
|
||||
}
|
||||
|
||||
$close = ($this->fieldset_tag == 'form' or empty($this->fieldset_tag))
|
||||
? $this->form()->close($attributes).PHP_EOL
|
||||
: $this->form()->{$this->fieldset_tag.'_close'}($attributes);
|
||||
|
||||
$template = $this->form()->get_config((empty($this->fieldset_tag) ? 'form' : $this->fieldset_tag).'_template',
|
||||
"\n\t\t{open}\n\t\t<table>\n{fields}\n\t\t</table>\n\t\t{close}\n");
|
||||
|
||||
$template = str_replace(array('{form_open}', '{open}', '{fields}', '{form_close}', '{close}'),
|
||||
array($open, $open, $fields_output, $close, $close),
|
||||
$template);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a disabled field from being build
|
||||
*
|
||||
* @param mixed $name
|
||||
* @return Fieldset this, to allow chaining
|
||||
*/
|
||||
public function enable($name = null)
|
||||
{
|
||||
// Check if it exists. if not, bail out
|
||||
if ( ! $this->field($name))
|
||||
{
|
||||
throw new \RuntimeException('Field "'.$name.'" does not exist in this Fieldset.');
|
||||
}
|
||||
|
||||
if (isset($this->disabled[$name]))
|
||||
{
|
||||
unset($this->disabled[$name]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a field from being build
|
||||
*
|
||||
* @param mixed $name
|
||||
* @return Fieldset this, to allow chaining
|
||||
*/
|
||||
public function disable($name = null)
|
||||
{
|
||||
// Check if it exists. if not, bail out
|
||||
if ( ! $this->field($name))
|
||||
{
|
||||
throw new \RuntimeException('Field "'.$name.'" does not exist in this Fieldset.');
|
||||
}
|
||||
|
||||
isset($this->disabled[$name]) or $this->disabled[$name] = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method toString that will build this as a form
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return parent Fieldset
|
||||
*
|
||||
* @return Fieldset
|
||||
*/
|
||||
public function parent()
|
||||
{
|
||||
return $this->fieldset_parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the child fieldset instances
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function children()
|
||||
{
|
||||
return $this->fieldset_children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->validation()->input()
|
||||
*
|
||||
* @param string $field
|
||||
* @return mixed
|
||||
*/
|
||||
public function input($field = null)
|
||||
{
|
||||
return $this->validation()->input($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->validation()->validated()
|
||||
*
|
||||
* @param string $field
|
||||
* @return mixed
|
||||
*/
|
||||
public function validated($field = null)
|
||||
{
|
||||
return $this->validation()->validated($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->validation()->error()
|
||||
*
|
||||
* @param string $field
|
||||
* @return Validation_Error|array
|
||||
*/
|
||||
public function error($field = null)
|
||||
{
|
||||
return $this->validation()->error($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->validation()->show_errors()
|
||||
*
|
||||
* @param array $config
|
||||
* @return string
|
||||
*/
|
||||
public function show_errors(array $config = array())
|
||||
{
|
||||
return $this->validation()->show_errors($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the fieldset name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the tabular form feature of this fieldset
|
||||
*
|
||||
* @param string $model Model on which to define the tabular form
|
||||
* @param string $relation Relation of the Model on the tabular form is modeled
|
||||
* @param array $parent Collection of Model objects from a many relation
|
||||
* @param int $blanks Number of empty rows to generate
|
||||
*
|
||||
* @return Fieldset this, to allow chaining
|
||||
*/
|
||||
public function set_tabular_form($model, $relation, $parent, $blanks = 1)
|
||||
{
|
||||
// make sure our parent is an ORM model instance
|
||||
if ( ! $parent instanceOf \Orm\Model)
|
||||
{
|
||||
throw new \RuntimeException('Parent passed to set_tabular_form() is not an ORM model object.');
|
||||
}
|
||||
|
||||
// validate the model and relation
|
||||
// fetch the relations of the parent model
|
||||
$relations = call_user_func(array($parent, 'relations'));
|
||||
if ( ! array_key_exists($relation, $relations))
|
||||
{
|
||||
throw new \RuntimeException('Relation passed to set_tabular_form() is not a valid relation of the ORM parent model object.');
|
||||
}
|
||||
|
||||
// check for compound primary keys
|
||||
try
|
||||
{
|
||||
// fetch the relations of the parent model
|
||||
$primary_key = call_user_func($model.'::primary_key');
|
||||
|
||||
// we don't support compound primary keys
|
||||
if (count($primary_key) !== 1)
|
||||
{
|
||||
throw new \RuntimeException('set_tabular_form() does not supports models with compound primary keys.');
|
||||
}
|
||||
|
||||
// store the primary key name, we need that later
|
||||
$primary_key = reset($primary_key);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
throw new \RuntimeException('Unable to fetch the models primary key information.');
|
||||
}
|
||||
|
||||
// store the tabular form class name
|
||||
$this->tabular_form_model = $model;
|
||||
|
||||
// and the relation on which we model the rows
|
||||
$this->tabular_form_relation = $relation;
|
||||
|
||||
// load the form config if not loaded yet
|
||||
\Config::load('form', true);
|
||||
|
||||
// load the config for embedded forms
|
||||
$this->set_config(array(
|
||||
'form_template' => \Config::get('form.tabular_form_template', "<table>{fields}</table>\n"),
|
||||
'field_template' => \Config::get('form.tabular_field_template', "{field}"),
|
||||
));
|
||||
|
||||
// add the rows to the tabular form fieldset
|
||||
foreach ($parent->{$relation} as $row)
|
||||
{
|
||||
// add the row fieldset to the tabular form fieldset
|
||||
$this->add($fieldset = \Fieldset::forge($this->tabular_form_relation.'_row_'.$row->{$primary_key}));
|
||||
|
||||
// and add the model fields to the row fielset
|
||||
$fieldset->add_model($model, $row)->set_fieldset_tag(false);
|
||||
$fieldset->set_config(array(
|
||||
'form_template' => \Config::get('form.tabular_row_template', "<table>{fields}</table>\n"),
|
||||
'field_template' => \Config::get('form.tabular_row_field_template', "{field}"),
|
||||
));
|
||||
$fieldset->add($this->tabular_form_relation.'['.$row->{$primary_key}.'][_delete]', '', array('type' => 'checkbox', 'value' => 1));
|
||||
}
|
||||
|
||||
// and finish with zero or more empty rows so we can add new data
|
||||
if ( ! is_numeric($blanks) or $blanks < 0)
|
||||
{
|
||||
$blanks = 1;
|
||||
}
|
||||
for ($i = 0; $i < $blanks; $i++)
|
||||
{
|
||||
$this->add($fieldset = \Fieldset::forge($this->tabular_form_relation.'_new_'.$i));
|
||||
$fieldset->add_model($model)->set_fieldset_tag(false);
|
||||
$fieldset->set_config(array(
|
||||
'form_template' => \Config::get('form.tabular_row_template', "<tr>{fields}</tr>"),
|
||||
'field_template' => \Config::get('form.tabular_row_field_template', "{field}"),
|
||||
));
|
||||
$fieldset->add($this->tabular_form_relation.'_new['.$i.'][_delete]', '', array('type' => 'checkbox', 'value' => 0, 'disabled' => 'disabled'));
|
||||
|
||||
// no required rules on this row
|
||||
foreach ($fieldset->field() as $f)
|
||||
{
|
||||
$f->delete_rule('required', false)->delete_rule('required_with', false);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the tabular form relation of this fieldset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_tabular_form()
|
||||
{
|
||||
return $this->tabular_form_relation;
|
||||
}
|
||||
}
|
@ -1,666 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Fieldset Class
|
||||
*
|
||||
* Define a set of fields that can be used to generate a form or to validate input.
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
*/
|
||||
class Fieldset_Field
|
||||
{
|
||||
/**
|
||||
* @var Fieldset Fieldset this field belongs to
|
||||
*/
|
||||
protected $fieldset;
|
||||
|
||||
/**
|
||||
* @var string Name of this field
|
||||
*/
|
||||
protected $name = '';
|
||||
|
||||
/**
|
||||
* @var string Base name of this field
|
||||
*/
|
||||
protected $basename = '';
|
||||
|
||||
/**
|
||||
* @var string Field type for form generation, false to prevent it showing
|
||||
*/
|
||||
protected $type = 'text';
|
||||
|
||||
/**
|
||||
* @var string Field label for validation errors and form label generation
|
||||
*/
|
||||
protected $label = '';
|
||||
|
||||
/**
|
||||
* @var mixed (Default) value of this field
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* @var string Description text to show with the field
|
||||
*/
|
||||
protected $description = '';
|
||||
|
||||
/**
|
||||
* @var array Rules for validation
|
||||
*/
|
||||
protected $rules = array();
|
||||
|
||||
/**
|
||||
* @var array Attributes for form generation
|
||||
*/
|
||||
protected $attributes = array();
|
||||
|
||||
/**
|
||||
* @var array Options, only available for select, radio & checkbox types
|
||||
*/
|
||||
protected $options = array();
|
||||
|
||||
/**
|
||||
* @var string Template for form building
|
||||
*/
|
||||
protected $template;
|
||||
|
||||
/**
|
||||
* @var array overwrites for default error messages
|
||||
*/
|
||||
protected $error_messages = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $label
|
||||
* @param array $attributes
|
||||
* @param array $rules
|
||||
* @param Fieldset $fieldset
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function __construct($name, $label = '', array $attributes = array(), array $rules = array(), $fieldset = null)
|
||||
{
|
||||
$this->name = (string) $name;
|
||||
|
||||
if ($this->name === "")
|
||||
{
|
||||
throw new \RuntimeException('Fieldset field name may not be empty.');
|
||||
}
|
||||
|
||||
// determine the field's base name (for fields with array indices)
|
||||
$this->basename = ($pos = strpos($this->name, '[')) ? rtrim(substr(strrchr($this->name, '['), 1), ']') : $this->name;
|
||||
|
||||
$this->fieldset = $fieldset instanceof Fieldset ? $fieldset : null;
|
||||
|
||||
// Don't allow name in attributes
|
||||
unset($attributes['name']);
|
||||
|
||||
// Take rules out of attributes
|
||||
unset($attributes['rules']);
|
||||
|
||||
// Use specific setter when available
|
||||
foreach ($attributes as $attr => $val)
|
||||
{
|
||||
if (method_exists($this, $method = 'set_'.$attr))
|
||||
{
|
||||
$this->{$method}($val);
|
||||
unset($attributes[$attr]);
|
||||
}
|
||||
}
|
||||
|
||||
// Add default "type" attribute if not specified
|
||||
empty($attributes['type']) and $this->set_type($this->type);
|
||||
|
||||
// only when non-empty, will supersede what was given in $attributes
|
||||
$label and $this->set_label($label);
|
||||
|
||||
$this->attributes = array_merge($this->attributes, $attributes);
|
||||
|
||||
foreach ($rules as $rule)
|
||||
{
|
||||
call_fuel_func_array(array($this, 'add_rule'), (array) $rule);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Fieldset $fieldset Fieldset to assign the field to
|
||||
* @return Fieldset_Field
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function set_fieldset(Fieldset $fieldset)
|
||||
{
|
||||
// if we currently have a fieldset
|
||||
if ($this->fieldset)
|
||||
{
|
||||
// remove the field from the fieldset
|
||||
$this->fieldset->delete($this->name);
|
||||
|
||||
// reset the fieldset
|
||||
$this->fieldset = null;
|
||||
|
||||
// add this field to the new fieldset
|
||||
$fieldset->add($this);
|
||||
}
|
||||
|
||||
// assign the new fieldset
|
||||
$this->fieldset = $fieldset;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the field label
|
||||
*
|
||||
* @param string $label
|
||||
* @return Fieldset_Field this, to allow chaining
|
||||
*/
|
||||
public function set_label($label)
|
||||
{
|
||||
$this->label = $label;
|
||||
$this->set_attribute('label', $label);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the field type for form generation
|
||||
*
|
||||
* @param string $type
|
||||
* @return Fieldset_Field this, to allow chaining
|
||||
*/
|
||||
public function set_type($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->set_attribute('type', $type);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the field's current or default value
|
||||
*
|
||||
* @param string $value
|
||||
* @param bool $repopulate
|
||||
* @return Fieldset_Field this, to allow chaining
|
||||
*/
|
||||
public function set_value($value, $repopulate = false)
|
||||
{
|
||||
// Repopulation is handled slightly different in some cases
|
||||
if ($repopulate)
|
||||
{
|
||||
if (($this->type == 'radio' or $this->type == 'checkbox') and empty($this->options))
|
||||
{
|
||||
if ($this->value == $value)
|
||||
{
|
||||
$this->set_attribute('checked', 'checked');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
$this->value = $value;
|
||||
$this->set_attribute('value', $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the field description
|
||||
*
|
||||
* @param string $description
|
||||
* @return Fieldset_Field this, to allow chaining
|
||||
*/
|
||||
public function set_description($description)
|
||||
{
|
||||
$this->description = strval($description);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template the output
|
||||
*
|
||||
* @param string $template
|
||||
* @return Fieldset_Field this, to allow chaining
|
||||
*/
|
||||
public function set_template($template = null)
|
||||
{
|
||||
$this->template = $template;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite a default error message
|
||||
*
|
||||
* @param string $rule
|
||||
* @param string $msg
|
||||
* @return Fieldset_Field
|
||||
*/
|
||||
public function set_error_message($rule, $msg)
|
||||
{
|
||||
empty($rule) and $rule = 0;
|
||||
$this->error_messages[$rule] = strval($msg);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a rule has an error message overwrite
|
||||
*
|
||||
* @param string $rule
|
||||
* @return null|string
|
||||
*/
|
||||
public function get_error_message($rule)
|
||||
{
|
||||
if (isset($this->error_messages[$rule]))
|
||||
{
|
||||
return $this->error_messages[$rule];
|
||||
}
|
||||
elseif (isset($this->error_messages[0]))
|
||||
{
|
||||
return $this->error_messages[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a validation rule
|
||||
* any further arguements after the callback will be used as arguements for the callback
|
||||
*
|
||||
* @param string|Callback either a validation rule or full callback
|
||||
* @return Fieldset_Field this, to allow chaining
|
||||
*/
|
||||
public function add_rule($callback)
|
||||
{
|
||||
$args = array_slice(func_get_args(), 1);
|
||||
$this->rules[] = array($callback, $args);
|
||||
|
||||
// Set required setting for forms when rule was applied
|
||||
if ($callback === 'required')
|
||||
{
|
||||
$this->set_attribute('required', 'required');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a validation rule
|
||||
*
|
||||
* @param string|Callback either a validation rule or full callback
|
||||
* @param bool whether to also reset related attributes
|
||||
* @return Fieldset_Field this, to allow chaining
|
||||
*/
|
||||
public function delete_rule($callback, $set_attr = true)
|
||||
{
|
||||
foreach($this->rules as $index => $rule)
|
||||
{
|
||||
if ($rule[0] === $callback)
|
||||
{
|
||||
unset($this->rules[$index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($callback === 'required' and $set_attr)
|
||||
{
|
||||
unset($this->attributes[$callback]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute on the field
|
||||
*
|
||||
* @param string
|
||||
* @param mixed new value or null to unset
|
||||
* @return Fieldset_Field this, to allow chaining
|
||||
*/
|
||||
public function set_attribute($attr, $value = null)
|
||||
{
|
||||
$attr = is_array($attr) ? $attr : array($attr => $value);
|
||||
foreach ($attr as $key => $value)
|
||||
{
|
||||
if ($value === null)
|
||||
{
|
||||
unset($this->attributes[$key]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->attributes[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single or multiple attributes by key
|
||||
*
|
||||
* @param string|array a single key or multiple in an array, empty to fetch all
|
||||
* @param mixed default output when attribute wasn't set
|
||||
* @return mixed|array a single attribute or multiple in an array when $key input was an array
|
||||
*/
|
||||
public function get_attribute($key = null, $default = null)
|
||||
{
|
||||
if ($key === null)
|
||||
{
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
if (is_array($key))
|
||||
{
|
||||
$output = array();
|
||||
foreach ($key as $k)
|
||||
{
|
||||
$output[$k] = array_key_exists($k, $this->attributes) ? $this->attributes[$k] : $default;
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
return array_key_exists($key, $this->attributes) ? $this->attributes[$key] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an option value with label
|
||||
*
|
||||
* @param string|array one option value, or multiple value=>label pairs in an array
|
||||
* @param string
|
||||
* @param bool Whether or not to replace the current options
|
||||
* @return Fieldset_Field this, to allow chaining
|
||||
*/
|
||||
public function set_options($value, $label = null, $replace_options = false)
|
||||
{
|
||||
if ( ! is_array($value))
|
||||
{
|
||||
\Arr::set($this->options, $value, $label);
|
||||
return $this;
|
||||
}
|
||||
|
||||
$merge = function(&$array, $new, $merge)
|
||||
{
|
||||
foreach ($new as $k => $v)
|
||||
{
|
||||
if (isset($array[$k]) and is_array($array[$k]) and is_array($v))
|
||||
{
|
||||
$merge($array[$k], $v);
|
||||
}
|
||||
else
|
||||
{
|
||||
$array[$k] = $v;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($replace_options or empty($this->options)) ? $this->options = $value : $merge($this->options, $value, $merge);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic get method to allow getting class properties but still having them protected
|
||||
* to disallow writing.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($property)
|
||||
{
|
||||
return $this->$property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
try
|
||||
{
|
||||
return $this->build();
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the parent Fieldset object
|
||||
*
|
||||
* @return Fieldset
|
||||
*/
|
||||
public function fieldset()
|
||||
{
|
||||
return $this->fieldset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->fieldset->add() to allow chaining
|
||||
*
|
||||
* @return Fieldset_Field
|
||||
*/
|
||||
public function add($name, $label = '', array $attributes = array(), array $rules = array())
|
||||
{
|
||||
return $this->fieldset()->add($name, $label, $attributes, $rules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->fieldset->add_before() to allow chaining
|
||||
*
|
||||
* @return Fieldset_Field
|
||||
*/
|
||||
public function add_before($name, $label = '', array $attributes = array(), array $rules = array(), $fieldname = null)
|
||||
{
|
||||
return $this->fieldset()->add_before($name, $label, $attributes, $rules, $fieldname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->fieldset->add_after() to allow chaining
|
||||
*
|
||||
* @return Fieldset_Field
|
||||
*/
|
||||
public function add_after($name, $label = '', array $attributes = array(), array $rules = array(), $fieldname = null)
|
||||
{
|
||||
return $this->fieldset()->add_after($name, $label, $attributes, $rules, $fieldname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$form = $this->fieldset()->form();
|
||||
|
||||
// Add IDs when auto-id is on
|
||||
if ($form->get_config('auto_id', false) === true and $this->get_attribute('id') == '')
|
||||
{
|
||||
$auto_id = $form->get_config('auto_id_prefix', '')
|
||||
.str_replace(array('[', ']'), array('-', ''), $this->name);
|
||||
$this->set_attribute('id', $auto_id);
|
||||
}
|
||||
|
||||
switch( ! empty($this->attributes['tag']) ? $this->attributes['tag'] : $this->type)
|
||||
{
|
||||
case 'hidden':
|
||||
$build_field = $form->hidden($this->name, $this->value, $this->attributes);
|
||||
break;
|
||||
|
||||
case 'radio':
|
||||
case 'checkbox':
|
||||
if ($this->options)
|
||||
{
|
||||
$build_field = array();
|
||||
$i = 0;
|
||||
foreach ($this->options as $value => $label)
|
||||
{
|
||||
$attributes = $this->attributes;
|
||||
$attributes['name'] = $this->name;
|
||||
$this->type == 'checkbox' and $attributes['name'] .= '['.$i.']';
|
||||
|
||||
$attributes['value'] = $value;
|
||||
$attributes['label'] = $label;
|
||||
|
||||
if (is_array($this->value) ? in_array($value, $this->value) : $value == $this->value)
|
||||
{
|
||||
$attributes['checked'] = 'checked';
|
||||
}
|
||||
|
||||
if( ! empty($attributes['id']))
|
||||
{
|
||||
$attributes['id'] .= '_'.$i;
|
||||
}
|
||||
else
|
||||
{
|
||||
$attributes['id'] = null;
|
||||
}
|
||||
$build_field[$form->label($label, null, array('for' => $attributes['id']))] = $this->type == 'radio'
|
||||
? $form->radio($attributes)
|
||||
: $form->checkbox($attributes);
|
||||
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$build_field = $this->type == 'radio'
|
||||
? $form->radio($this->name, $this->value, $this->attributes)
|
||||
: $form->checkbox($this->name, $this->value, $this->attributes);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
$attributes = $this->attributes;
|
||||
$name = $this->name;
|
||||
unset($attributes['type']);
|
||||
array_key_exists('multiple', $attributes) and $name .= '[]';
|
||||
$build_field = $form->select($name, $this->value, $this->options, $attributes);
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
$attributes = $this->attributes;
|
||||
unset($attributes['type']);
|
||||
$build_field = $form->textarea($this->name, $this->value, $attributes);
|
||||
break;
|
||||
|
||||
case 'button':
|
||||
$build_field = $form->button($this->name, $this->value, $this->attributes);
|
||||
break;
|
||||
|
||||
case false:
|
||||
$build_field = '';
|
||||
break;
|
||||
|
||||
default:
|
||||
$build_field = $form->input($this->name, $this->value, $this->attributes);
|
||||
break;
|
||||
}
|
||||
|
||||
if (empty($build_field) or $this->type == 'hidden')
|
||||
{
|
||||
return $build_field;
|
||||
}
|
||||
|
||||
return $this->template($build_field);
|
||||
}
|
||||
|
||||
protected function template($build_field)
|
||||
{
|
||||
$form = $this->fieldset()->form();
|
||||
|
||||
$required_mark = $this->get_attribute('required', null) ? $form->get_config('required_mark', null) : null;
|
||||
$label = $this->label ? $form->label($this->label, null, array('id' => 'label_'.$this->name, 'for' => $this->get_attribute('id', null), 'class' => $form->get_config('label_class', null))) : '';
|
||||
$error_template = $form->get_config('error_template', '');
|
||||
$error_msg = ($form->get_config('inline_errors') && $this->error()) ? str_replace('{error_msg}', $this->error(), $error_template) : '';
|
||||
$error_class = $this->error() ? $form->get_config('error_class') : '';
|
||||
|
||||
if (is_array($build_field))
|
||||
{
|
||||
$label = $this->label ? str_replace('{label}', $this->label, $form->get_config('group_label', '<span>{label}</span>')) : '';
|
||||
$template = $this->template ?: $form->get_config('multi_field_template', "\t\t<tr>\n\t\t\t<td class=\"{error_class}\">{group_label}{required}</td>\n\t\t\t<td class=\"{error_class}\">{fields}\n\t\t\t\t{field} {label}<br />\n{fields}\t\t\t{error_msg}\n\t\t\t</td>\n\t\t</tr>\n");
|
||||
if ($template && preg_match('#\{fields\}(.*)\{fields\}#Dus', $template, $match) > 0)
|
||||
{
|
||||
$build_fields = '';
|
||||
foreach ($build_field as $lbl => $bf)
|
||||
{
|
||||
$bf_temp = str_replace('{label}', $lbl, $match[1]);
|
||||
$bf_temp = str_replace('{required}', $required_mark, $bf_temp);
|
||||
$bf_temp = str_replace('{field}', $bf, $bf_temp);
|
||||
$build_fields .= $bf_temp;
|
||||
}
|
||||
|
||||
$template = str_replace($match[0], '{fields}', $template);
|
||||
$template = str_replace(array('{group_label}', '{required}', '{fields}', '{error_msg}', '{error_class}', '{description}'), array($label, $required_mark, $build_fields, $error_msg, $error_class, $this->description), $template);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
// still here? wasn't a multi field template available, try the normal one with imploded $build_field
|
||||
$build_field = implode(' ', $build_field);
|
||||
}
|
||||
|
||||
// determine the field_id, which allows us to identify the field for CSS purposes
|
||||
$field_id = 'col_'.$this->name;
|
||||
if ($parent = $this->fieldset()->parent())
|
||||
{
|
||||
$parent->get_tabular_form() and $field_id = $parent->get_tabular_form().'_col_'.$this->basename;
|
||||
}
|
||||
|
||||
$template = $this->template ?: $form->get_config('field_template', "\t\t<tr>\n\t\t\t<td class=\"{error_class}\">{label}{required}</td>\n\t\t\t<td class=\"{error_class}\">{field} {description} {error_msg}</td>\n\t\t</tr>\n");
|
||||
$template = str_replace(array('{label}', '{required}', '{field}', '{error_msg}', '{error_class}', '{description}', '{field_id}'),
|
||||
array($label, $required_mark, $build_field, $error_msg, $error_class, $this->description, $field_id),
|
||||
$template);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->fieldset->validation->input() for this field
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function input()
|
||||
{
|
||||
return $this->fieldset()->validation()->input($this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->fieldset->validation->validated() for this field
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function validated()
|
||||
{
|
||||
return $this->fieldset()->validation()->validated($this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->fieldset->validation->error() for this field
|
||||
*
|
||||
* @return Validation_Error
|
||||
*/
|
||||
public function error()
|
||||
{
|
||||
return $this->fieldset()->validation()->error($this->name);
|
||||
}
|
||||
}
|
@ -1,922 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class FileAccessException extends \FuelException {}
|
||||
class OutsideAreaException extends \OutOfBoundsException {}
|
||||
class InvalidPathException extends \FileAccessException {}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* File Class
|
||||
*
|
||||
* @package Fuel
|
||||
* @subpackage Core
|
||||
* @category Core
|
||||
*/
|
||||
class File
|
||||
{
|
||||
/**
|
||||
* @var array loaded area's
|
||||
*/
|
||||
protected static $areas = array();
|
||||
|
||||
public static function _init()
|
||||
{
|
||||
\Config::load('file', true);
|
||||
|
||||
// make sure the configured chmod values are octal
|
||||
$chmod = \Config::get('file.chmod.folders', 0777);
|
||||
is_string($chmod) and \Config::set('file.chmod.folders', octdec($chmod));
|
||||
$chmod = \Config::get('file.chmod.files', 0666);
|
||||
is_string($chmod) and \Config::set('file.chmod.files', octdec($chmod));
|
||||
|
||||
static::$areas[null] = \File_Area::forge(\Config::get('file.base_config', array()));
|
||||
|
||||
foreach (\Config::get('file.areas', array()) as $name => $config)
|
||||
{
|
||||
static::$areas[$name] = \File_Area::forge($config);
|
||||
}
|
||||
}
|
||||
|
||||
public static function forge(array $config = array())
|
||||
{
|
||||
return \File_Area::forge($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance
|
||||
*
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return File_Area
|
||||
*/
|
||||
public static function instance($area = null)
|
||||
{
|
||||
if ($area instanceof File_Area)
|
||||
{
|
||||
return $area;
|
||||
}
|
||||
|
||||
$instance = array_key_exists($area, static::$areas) ? static::$areas[$area] : false;
|
||||
|
||||
if ($instance === false)
|
||||
{
|
||||
throw new \InvalidArgumentException('There is no file instance named "'.$area.'".');
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* File & directory objects factory
|
||||
*
|
||||
* @param string $path path to the file or directory
|
||||
* @param array $config configuration items
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return File_Handler_File
|
||||
*/
|
||||
public static function get($path, array $config = array(), $area = null)
|
||||
{
|
||||
return static::instance($area)->get_handler($path, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url.
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $config
|
||||
* @param null $area
|
||||
* @return bool
|
||||
*/
|
||||
public static function get_url($path, array $config = array(), $area = null)
|
||||
{
|
||||
return static::get($path, $config, $area)->get_url();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for file existence
|
||||
*
|
||||
* @param string $path path to file to check
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return bool
|
||||
*/
|
||||
public static function exists($path, $area = null)
|
||||
{
|
||||
$path = rtrim(static::instance($area)->get_path($path), '\\/');
|
||||
|
||||
// resolve symlinks
|
||||
while ($path and is_link($path))
|
||||
{
|
||||
$path = readlink($path);
|
||||
}
|
||||
|
||||
return is_file($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file
|
||||
*
|
||||
* @param string $basepath directory where to create file
|
||||
* @param string $name filename
|
||||
* @param null $contents contents of file
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return bool
|
||||
* @throws \FileAccessException
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function create($basepath, $name, $contents = null, $area = null)
|
||||
{
|
||||
$basepath = rtrim(static::instance($area)->get_path($basepath), '\\/').DS;
|
||||
$new_file = static::instance($area)->get_path($basepath.$name);
|
||||
|
||||
if ( ! is_dir($basepath) or ! is_writable($basepath))
|
||||
{
|
||||
throw new \InvalidPathException('Invalid basepath: "'.$basepath.'", cannot create file at this location.');
|
||||
}
|
||||
elseif (is_file($new_file))
|
||||
{
|
||||
throw new \FileAccessException('File: "'.$new_file.'" already exists, cannot be created.');
|
||||
}
|
||||
|
||||
$file = static::open_file(@fopen($new_file, 'c'), true, $area);
|
||||
fwrite($file, $contents);
|
||||
static::close_file($file, $area);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty directory
|
||||
*
|
||||
* @param string directory where to create new dir
|
||||
* @param string dirname
|
||||
* @param int (octal) file permissions
|
||||
* @param string|File_Area|null file area name, object or null for non-specific
|
||||
* @return bool
|
||||
* @throws \FileAccessException
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function create_dir($basepath, $name, $chmod = null, $area = null)
|
||||
{
|
||||
$basepath = rtrim(static::instance($area)->get_path($basepath), '\\/').DS;
|
||||
$new_dir = static::instance($area)->get_path($basepath.trim($name, '\\/'));
|
||||
is_null($chmod) and $chmod = \Config::get('file.chmod.folders', 0777);
|
||||
|
||||
if ( ! is_dir($basepath) or ! is_writable($basepath))
|
||||
{
|
||||
throw new \InvalidPathException('Invalid basepath: "'.$basepath.'", cannot create directory at this location.');
|
||||
}
|
||||
elseif (is_dir($new_dir))
|
||||
{
|
||||
throw new \FileAccessException('Directory: "'.$new_dir.'" exists already, cannot be created.');
|
||||
}
|
||||
|
||||
// unify the path separators, and get the part we need to add to the basepath
|
||||
$new_dir = str_replace(array('\\', '/'), DS, $new_dir);
|
||||
|
||||
// recursively create the directory. we can't use mkdir permissions or recursive
|
||||
// due to the fact that mkdir is restricted by the current users umask
|
||||
$path = '';
|
||||
foreach (explode(DS, $new_dir) as $dir)
|
||||
{
|
||||
// some security checking
|
||||
if ($dir == '.' or $dir == '..')
|
||||
{
|
||||
throw new \FileAccessException('Directory to be created contains illegal segments.');
|
||||
}
|
||||
|
||||
$path .= DS.$dir;
|
||||
if ( ! is_dir($path))
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( ! mkdir($path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
chmod($path, $chmod);
|
||||
}
|
||||
catch (\PHPErrorException $e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read file
|
||||
*
|
||||
* @param string $path file to read
|
||||
* @param bool $as_string whether to use readfile() or file_get_contents()
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return IO|string file contents
|
||||
* @throws \FileAccessException
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function read($path, $as_string = false, $area = null)
|
||||
{
|
||||
$path = static::instance($area)->get_path($path);
|
||||
|
||||
if ( ! is_file($path))
|
||||
{
|
||||
throw new \InvalidPathException('Cannot read file: "'.$path.'", file does not exists.');
|
||||
}
|
||||
|
||||
$file = static::open_file(@fopen($path, 'r'), LOCK_SH, $area);
|
||||
$return = $as_string ? file_get_contents($path) : readfile($path);
|
||||
static::close_file($file, $area);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read directory
|
||||
*
|
||||
* @param string $path directory to read
|
||||
* @param int $depth depth to recurse directory, 1 is only current and 0 or smaller is unlimited
|
||||
* @param Array|null $filter array of partial regexps or non-array for default
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return array
|
||||
* @throws \FileAccessException
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function read_dir($path, $depth = 0, $filter = null, $area = null)
|
||||
{
|
||||
$path = rtrim(static::instance($area)->get_path($path), '\\/').DS;
|
||||
|
||||
if ( ! is_dir($path))
|
||||
{
|
||||
throw new \InvalidPathException('Invalid path: "'.$path.'", directory cannot be read.');
|
||||
}
|
||||
|
||||
if ( ! $fp = @opendir($path))
|
||||
{
|
||||
throw new \FileAccessException('Could not open directory: "'.$path.'" for reading.');
|
||||
}
|
||||
|
||||
// Use default when not set
|
||||
if ( ! is_array($filter))
|
||||
{
|
||||
$filter = array('!^\.');
|
||||
if ($extensions = static::instance($area)->extensions())
|
||||
{
|
||||
foreach($extensions as $ext)
|
||||
{
|
||||
$filter[] = '\.'.$ext.'$';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$files = array();
|
||||
$dirs = array();
|
||||
$new_depth = $depth - 1;
|
||||
|
||||
while (false !== ($file = readdir($fp)))
|
||||
{
|
||||
// Remove '.', '..'
|
||||
if (in_array($file, array('.', '..')))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// use filters when given
|
||||
elseif ( ! empty($filter))
|
||||
{
|
||||
$continue = false; // whether or not to continue
|
||||
$matched = false; // whether any positive pattern matched
|
||||
$positive = false; // whether positive filters are present
|
||||
foreach($filter as $f => $type)
|
||||
{
|
||||
if (is_numeric($f))
|
||||
{
|
||||
// generic rule
|
||||
$f = $type;
|
||||
}
|
||||
else
|
||||
{
|
||||
// type specific rule
|
||||
$is_file = is_file($path.$file);
|
||||
if (($type === 'file' and ! $is_file) or ($type !== 'file' and $is_file))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$not = substr($f, 0, 1) === '!'; // whether it's a negative condition
|
||||
$f = $not ? substr($f, 1) : $f;
|
||||
// on negative condition a match leads to a continue
|
||||
if (($match = preg_match('/'.$f.'/uiD', $file) > 0) and $not)
|
||||
{
|
||||
$continue = true;
|
||||
}
|
||||
|
||||
$positive = $positive ?: ! $not; // whether a positive condition was encountered
|
||||
$matched = $matched ?: ($match and ! $not); // whether one of the filters has matched
|
||||
}
|
||||
|
||||
// continue when negative matched or when positive filters and nothing matched
|
||||
if ($continue or $positive and ! $matched)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (@is_dir($path.$file))
|
||||
{
|
||||
// Use recursion when depth not depleted or not limited...
|
||||
if ($depth < 1 or $new_depth > 0)
|
||||
{
|
||||
$dirs[$file.DS] = static::read_dir($path.$file.DS, $new_depth, $filter, $area);
|
||||
}
|
||||
// ... or set dir to false when not read
|
||||
else
|
||||
{
|
||||
$dirs[$file.DS] = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$files[] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
closedir($fp);
|
||||
|
||||
// sort dirs & files naturally and return array with dirs on top and files
|
||||
uksort($dirs, 'strnatcasecmp');
|
||||
natcasesort($files);
|
||||
return array_merge($dirs, $files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a file
|
||||
*
|
||||
* @param string $basepath directory where to write the file
|
||||
* @param string $name filename
|
||||
* @param string $contents contents of file
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return bool
|
||||
* @throws \InvalidPathException
|
||||
* @throws \FileAccessException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function update($basepath, $name, $contents = null, $area = null)
|
||||
{
|
||||
$basepath = rtrim(static::instance($area)->get_path($basepath), '\\/').DS;
|
||||
$new_file = static::instance($area)->get_path($basepath.$name);
|
||||
|
||||
if ( ! $file = static::open_file(@fopen($new_file, 'w'), true, $area))
|
||||
{
|
||||
if ( ! is_dir($basepath) or ! is_writable($basepath))
|
||||
{
|
||||
throw new \InvalidPathException('Invalid basepath: "'.$basepath.'", cannot update a file at this location.');
|
||||
}
|
||||
|
||||
throw new \FileAccessException('No write access to: "'.$basepath.'", cannot update a file.');
|
||||
}
|
||||
|
||||
fwrite($file, $contents);
|
||||
static::close_file($file, $area);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append to a file
|
||||
*
|
||||
* @param string $basepath directory where to write the file
|
||||
* @param string $name filename
|
||||
* @param string $contents contents of file
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return bool
|
||||
* @throws \InvalidPathException
|
||||
* @throws \FileAccessException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function append($basepath, $name, $contents = null, $area = null)
|
||||
{
|
||||
$basepath = rtrim(static::instance($area)->get_path($basepath), '\\/').DS;
|
||||
$new_file = static::instance($area)->get_path($basepath.$name);
|
||||
|
||||
if ( ! is_file($new_file))
|
||||
{
|
||||
throw new \FileAccessException('File: "'.$new_file.'" does not exist, cannot be appended.');
|
||||
}
|
||||
|
||||
if ( ! $file = static::open_file(@fopen($new_file, 'a'), true, $area))
|
||||
{
|
||||
if ( ! is_dir($basepath) or ! is_writable($basepath))
|
||||
{
|
||||
throw new \InvalidPathException('Invalid basepath: "'.$basepath.'", cannot append to a file at this location.');
|
||||
}
|
||||
|
||||
throw new \FileAccessException('No write access, cannot append to the file: "'.$file.'".');
|
||||
}
|
||||
|
||||
fwrite($file, $contents);
|
||||
static::close_file($file, $area);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the octal permissions for a file or directory
|
||||
*
|
||||
* @param string $path path to the file or directory
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return string octal file permissions
|
||||
* @throws \FileAccessException
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function get_permissions($path, $area = null)
|
||||
{
|
||||
$path = static::instance($area)->get_path($path);
|
||||
|
||||
if ( ! file_exists($path))
|
||||
{
|
||||
throw new \InvalidPathException('Path: "'.$path.'" is not a directory or a file, cannot get permissions.');
|
||||
}
|
||||
|
||||
return substr(sprintf('%o', fileperms($path)), -4);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file's or directory's created or modified timestamp.
|
||||
*
|
||||
* @param string $path path to the file or directory
|
||||
* @param string $type modified or created
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return int Unix Timestamp
|
||||
* @throws \FileAccessException
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function get_time($path, $type = 'modified', $area = null)
|
||||
{
|
||||
$path = static::instance($area)->get_path($path);
|
||||
|
||||
if ( ! file_exists($path))
|
||||
{
|
||||
throw new \InvalidPathException('Path: "'.$path.'" is not a directory or a file, cannot get creation timestamp.');
|
||||
}
|
||||
|
||||
if ($type === 'modified')
|
||||
{
|
||||
return filemtime($path);
|
||||
}
|
||||
elseif ($type === 'created')
|
||||
{
|
||||
return filectime($path);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \UnexpectedValueException('File::time $type must be "modified" or "created".');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file's size.
|
||||
*
|
||||
* @param string $path path to the file or directory
|
||||
* @param mixed $area file area name, object or null for base area
|
||||
* @return int the file's size in bytes
|
||||
* @throws \FileAccessException
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function get_size($path, $area = null)
|
||||
{
|
||||
$path = static::instance($area)->get_path($path);
|
||||
|
||||
if ( ! file_exists($path))
|
||||
{
|
||||
throw new \InvalidPathException('Path: "'.$path.'" is not a directory or a file, cannot get size.');
|
||||
}
|
||||
|
||||
return filesize($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename directory or file
|
||||
*
|
||||
* @param string $path path to file or directory to rename
|
||||
* @param string $new_path new path (full path, can also cause move)
|
||||
* @param string|File_Area|null $source_area source path file area name, object or null for non-specific
|
||||
* @param string|File_Area|null $target_area target path file area name, object or null for non-specific. Defaults to source_area if not set.
|
||||
* @return bool
|
||||
* @throws \FileAccessException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function rename($path, $new_path, $source_area = null, $target_area = null)
|
||||
{
|
||||
$path = static::instance($source_area)->get_path($path);
|
||||
$new_path = static::instance($target_area ?: $source_area)->get_path($new_path);
|
||||
|
||||
return rename($path, $new_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for rename(), not needed but consistent with other methods
|
||||
*
|
||||
* @param string $path path to directory to rename
|
||||
* @param string $new_path new path (full path, can also cause move)
|
||||
* @param string|File_Area|null $source_area source path file area name, object or null for non-specific
|
||||
* @param string|File_Area|null $target_area target path file area name, object or null for non-specific. Defaults to source_area if not set.
|
||||
* @return bool
|
||||
* @throws \FileAccessException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function rename_dir($path, $new_path, $source_area = null, $target_area = null)
|
||||
{
|
||||
return static::rename($path, $new_path, $source_area, $target_area);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy file
|
||||
*
|
||||
* @param string path path to file to copy
|
||||
* @param string new_path new base directory (full path)
|
||||
* @param string|File_Area|null source_area source path file area name, object or null for non-specific
|
||||
* @param string|File_Area|null target_area target path file area name, object or null for non-specific. Defaults to source_area if not set.
|
||||
* @throws \FileAccessException
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
* @return bool
|
||||
*/
|
||||
public static function copy($path, $new_path, $source_area = null, $target_area = null)
|
||||
{
|
||||
$path = static::instance($source_area)->get_path($path);
|
||||
$new_path = static::instance($target_area ?: $source_area)->get_path($new_path);
|
||||
|
||||
if ( ! is_file($path))
|
||||
{
|
||||
throw new \InvalidPathException('Cannot copy file: given path: "'.$path.'" is not a file.');
|
||||
}
|
||||
elseif (file_exists($new_path))
|
||||
{
|
||||
throw new \FileAccessException('Cannot copy file: new path: "'.$new_path.'" already exists.');
|
||||
}
|
||||
return copy($path, $new_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy directory
|
||||
*
|
||||
* @param string $path path to directory which contents will be copied
|
||||
* @param string $new_path new base directory (full path)
|
||||
* @param string|File_Area|null $source_area source path file area name, object or null for non-specific
|
||||
* @param string|File_Area|null $target_area target path file area name, object or null for non-specific. Defaults to source_area if not set.
|
||||
* @throws \FileAccessException when something went wrong
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function copy_dir($path, $new_path, $source_area = null, $target_area = null)
|
||||
{
|
||||
$target_area = $target_area ?: $source_area;
|
||||
|
||||
$path = rtrim(static::instance($source_area)->get_path($path), '\\/').DS;
|
||||
$new_path = rtrim(static::instance($target_area)->get_path($new_path), '\\/').DS;
|
||||
|
||||
if ( ! is_dir($path))
|
||||
{
|
||||
throw new \InvalidPathException('Cannot copy directory: given path: "'.$path.'" is not a directory: '.$path);
|
||||
}
|
||||
elseif ( ! file_exists($new_path))
|
||||
{
|
||||
$newpath_dirname = pathinfo($new_path, PATHINFO_DIRNAME);
|
||||
static::create_dir($newpath_dirname, pathinfo($new_path, PATHINFO_BASENAME), fileperms($newpath_dirname) ?: 0777, $target_area);
|
||||
}
|
||||
|
||||
$files = static::read_dir($path, -1, array(), $source_area);
|
||||
foreach ($files as $dir => $file)
|
||||
{
|
||||
if (is_array($file))
|
||||
{
|
||||
$check = static::create_dir($new_path.DS, substr($dir, 0, -1), fileperms($path.$dir) ?: 0777, $target_area);
|
||||
$check and static::copy_dir($path.$dir.DS, $new_path.$dir, $source_area, $target_area);
|
||||
}
|
||||
else
|
||||
{
|
||||
$check = static::copy($path.$file, $new_path.$file, $source_area, $target_area);
|
||||
}
|
||||
|
||||
// abort if something went wrong
|
||||
if ( ! $check)
|
||||
{
|
||||
throw new \FileAccessException('Directory copy aborted prematurely, part of the operation failed during copying: '.(is_array($file) ? $dir : $file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new symlink
|
||||
*
|
||||
* @param string $path target of symlink
|
||||
* @param string $link_path destination of symlink
|
||||
* @param bool $is_file true for file, false for directory
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return bool
|
||||
* @throws \FileAccessException
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function symlink($path, $link_path, $is_file = true, $area = null)
|
||||
{
|
||||
$path = rtrim(static::instance($area)->get_path($path), '\\/');
|
||||
$link_path = rtrim(static::instance($area)->get_path($link_path), '\\/');
|
||||
|
||||
if ($is_file and ! is_file($path))
|
||||
{
|
||||
throw new \InvalidPathException('Cannot symlink: given file: "'.$path.'" does not exist.');
|
||||
}
|
||||
elseif ( ! $is_file and ! is_dir($path))
|
||||
{
|
||||
throw new \InvalidPathException('Cannot symlink: given directory: "'.$path.'" does not exist.');
|
||||
}
|
||||
elseif (file_exists($link_path))
|
||||
{
|
||||
throw new \FileAccessException('Cannot symlink: link: "'.$link_path.'" already exists.');
|
||||
}
|
||||
|
||||
return symlink($path, $link_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete file
|
||||
*
|
||||
* @param string $path path to file to delete
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return bool
|
||||
* @throws \FileAccessException
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function delete($path, $area = null)
|
||||
{
|
||||
$path = rtrim(static::instance($area)->get_path($path), '\\/');
|
||||
|
||||
if ( ! is_file($path) and ! is_link($path))
|
||||
{
|
||||
throw new \InvalidPathException('Cannot delete file: given path "'.$path.'" is not a file.');
|
||||
}
|
||||
|
||||
return unlink($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete directory
|
||||
*
|
||||
* @param string $path path to directory to delete
|
||||
* @param bool $recursive whether to also delete contents of subdirectories
|
||||
* @param bool $delete_top whether to delete the parent dir itself when empty
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return bool
|
||||
* @throws \FileAccessException
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function delete_dir($path, $recursive = true, $delete_top = true, $area = null)
|
||||
{
|
||||
$path = rtrim(static::instance($area)->get_path($path), '\\/').DS;
|
||||
if ( ! is_dir($path))
|
||||
{
|
||||
throw new \InvalidPathException('Cannot delete directory: given path: "'.$path.'" is not a directory.');
|
||||
}
|
||||
|
||||
$files = static::read_dir($path, -1, array(), $area);
|
||||
|
||||
$not_empty = false;
|
||||
$check = true;
|
||||
foreach ($files as $dir => $file)
|
||||
{
|
||||
if (is_array($file))
|
||||
{
|
||||
if ($recursive)
|
||||
{
|
||||
$check = static::delete_dir($path.$dir, true, true, $area);
|
||||
}
|
||||
else
|
||||
{
|
||||
$not_empty = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$check = static::delete($path.$file, $area);
|
||||
}
|
||||
|
||||
// abort if something went wrong
|
||||
if ( ! $check)
|
||||
{
|
||||
throw new \FileAccessException('Directory deletion aborted prematurely, part of the operation failed.');
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $not_empty and $delete_top)
|
||||
{
|
||||
return rmdir($path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open and lock file
|
||||
*
|
||||
* @param resource|string $path file resource or path
|
||||
* @param constant|bool $lock either valid lock constant or true=LOCK_EX / false=LOCK_UN
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return bool|resource
|
||||
* @throws \FileAccessException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function open_file($path, $lock = true, $area = null)
|
||||
{
|
||||
if (is_string($path))
|
||||
{
|
||||
$path = static::instance($area)->get_path($path);
|
||||
$resource = fopen($path, 'r+');
|
||||
}
|
||||
else
|
||||
{
|
||||
$resource = $path;
|
||||
}
|
||||
|
||||
// Make sure the parameter is a valid resource
|
||||
if ( ! is_resource($resource))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If locks aren't used, don't lock
|
||||
if ( ! static::instance($area)->use_locks())
|
||||
{
|
||||
return $resource;
|
||||
}
|
||||
|
||||
// Accept valid lock constant or set to LOCK_EX
|
||||
$lock = in_array($lock, array(LOCK_SH, LOCK_EX, LOCK_NB)) ? $lock : LOCK_EX;
|
||||
|
||||
// Try to get a lock, timeout after 5 seconds
|
||||
$lock_mtime = microtime(true);
|
||||
while ( ! flock($resource, $lock))
|
||||
{
|
||||
if (microtime(true) - $lock_mtime > 5)
|
||||
{
|
||||
throw new \FileAccessException('Could not secure file lock, timed out after 5 seconds.');
|
||||
}
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close file resource & unlock
|
||||
*
|
||||
* @param resource $resource open file resource
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
*/
|
||||
public static function close_file($resource, $area = null)
|
||||
{
|
||||
// If locks aren't used, don't unlock
|
||||
if ( static::instance($area)->use_locks())
|
||||
{
|
||||
flock($resource, LOCK_UN);
|
||||
}
|
||||
|
||||
fclose($resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get detailed information about a file
|
||||
*
|
||||
* @param string $path file path
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @return array
|
||||
* @throws \FileAccessException
|
||||
* @throws \InvalidPathException
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public static function file_info($path, $area = null)
|
||||
{
|
||||
$info = array(
|
||||
'original' => $path,
|
||||
'realpath' => '',
|
||||
'dirname' => '',
|
||||
'basename' => '',
|
||||
'filename' => '',
|
||||
'extension' => '',
|
||||
'mimetype' => '',
|
||||
'charset' => '',
|
||||
'size' => 0,
|
||||
'permissions' => '',
|
||||
'time_created' => '',
|
||||
'time_modified' => '',
|
||||
);
|
||||
|
||||
if ( ! $info['realpath'] = static::instance($area)->get_path($path) or ! is_file($info['realpath']))
|
||||
{
|
||||
throw new \InvalidPathException('Filename given is not a valid file.');
|
||||
}
|
||||
|
||||
$info = array_merge($info, pathinfo($info['realpath']));
|
||||
|
||||
if ( ! $fileinfo = new \finfo(FILEINFO_MIME, \Config::get('file.magic_file', null)))
|
||||
{
|
||||
throw new \InvalidArgumentException('Can not retrieve information about this file.');
|
||||
}
|
||||
|
||||
$fileinfo = explode(';', $fileinfo->file($info['realpath']));
|
||||
|
||||
$info['mimetype'] = isset($fileinfo[0]) ? $fileinfo[0] : 'application/octet-stream';
|
||||
|
||||
if (isset($fileinfo[1]))
|
||||
{
|
||||
$fileinfo = explode('=', $fileinfo[1]);
|
||||
$info['charset'] = isset($fileinfo[1]) ? $fileinfo[1] : '';
|
||||
}
|
||||
|
||||
$info['size'] = static::get_size($info['realpath'], $area);
|
||||
$info['permissions'] = static::get_permissions($info['realpath'], $area);
|
||||
$info['time_created'] = static::get_time($info['realpath'], 'created', $area);
|
||||
$info['time_modified'] = static::get_time($info['realpath'], 'modified', $area);
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a file
|
||||
*
|
||||
* @param string $path file path
|
||||
* @param string|null $name custom name for the file to be downloaded
|
||||
* @param string|null $mime custom mime type or null for file mime type
|
||||
* @param string|File_Area|null $area file area name, object or null for base area
|
||||
* @param bool $delete delete the file after download when true
|
||||
* @param string $disposition disposition, must be 'attachment' or 'inline'
|
||||
*/
|
||||
public static function download($path, $name = null, $mime = null, $area = null, $delete = false, $disposition = 'attachment')
|
||||
{
|
||||
$info = static::file_info($path, $area);
|
||||
$class = get_called_class();
|
||||
empty($mime) or $info['mimetype'] = $mime;
|
||||
empty($name) or $info['basename'] = $name;
|
||||
in_array($disposition, array('inline', 'attachment')) or $disposition = 'attachment';
|
||||
|
||||
\Event::register('fuel-shutdown', function () use($info, $area, $class, $delete, $disposition) {
|
||||
|
||||
if ( ! $file = call_user_func(array($class, 'open_file'), @fopen($info['realpath'], 'rb'), LOCK_SH, $area))
|
||||
{
|
||||
throw new \FileAccessException('Filename given could not be opened for download.');
|
||||
}
|
||||
|
||||
while (ob_get_level() > 0)
|
||||
{
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
ini_get('zlib.output_compression') and ini_set('zlib.output_compression', 0);
|
||||
! ini_get('safe_mode') and set_time_limit(0);
|
||||
|
||||
header('Content-Type: '.$info['mimetype']);
|
||||
header('Content-Disposition: '.$disposition.'; filename="'.$info['basename'].'"');
|
||||
$disposition === 'attachment' and header('Content-Description: File Transfer');
|
||||
header('Content-Length: '.$info['size']);
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
$disposition === 'attachment' and header('Expires: 0');
|
||||
$disposition === 'attachment' and header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
|
||||
|
||||
while( ! feof($file))
|
||||
{
|
||||
echo fread($file, 2048);
|
||||
}
|
||||
|
||||
call_user_func(array($class, 'close_file'), $file, $area);
|
||||
|
||||
if ($delete)
|
||||
{
|
||||
call_user_func(array($class, 'delete'), $info['realpath'], $area);
|
||||
}
|
||||
});
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
}
|
@ -1,277 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class File_Area
|
||||
{
|
||||
/**
|
||||
* @var string path to basedir restriction, null for no restriction
|
||||
*/
|
||||
protected $basedir = null;
|
||||
|
||||
/**
|
||||
* @var array array of allowed extensions, null for all
|
||||
*/
|
||||
protected $extensions = null;
|
||||
|
||||
/**
|
||||
* @var string base url for files, null for not available
|
||||
*/
|
||||
protected $url = null;
|
||||
|
||||
/**
|
||||
* @var bool whether or not to use file locks when doing file operations
|
||||
*/
|
||||
protected $use_locks = false;
|
||||
|
||||
/**
|
||||
* @var array contains file handler per file extension
|
||||
*/
|
||||
protected $file_handlers = array();
|
||||
|
||||
protected function __construct(array $config = array())
|
||||
{
|
||||
foreach ($config as $key => $value)
|
||||
{
|
||||
if (property_exists($this, $key))
|
||||
{
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty($this->basedir))
|
||||
{
|
||||
$this->basedir = realpath($this->basedir) ?: $this->basedir;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for area objects
|
||||
*
|
||||
* @param array
|
||||
* @return File_Area
|
||||
*/
|
||||
public static function forge(array $config = array())
|
||||
{
|
||||
return new static($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler factory for given path
|
||||
*
|
||||
* @param string $path path to file or directory
|
||||
* @param array $config optional config
|
||||
* @param array $content
|
||||
* @return File_Handler_File
|
||||
* @throws \FileAccessException when outside basedir restriction or disallowed file extension
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public function get_handler($path, array $config = array(), $content = array())
|
||||
{
|
||||
$path = $this->get_path($path);
|
||||
|
||||
if (is_file($path))
|
||||
{
|
||||
$info = pathinfo($path);
|
||||
|
||||
// deal with path names without an extension
|
||||
isset($info['extension']) or $info['extension'] = '';
|
||||
|
||||
// check file extension
|
||||
if ( ! empty($this->extensions) && ! in_array($info['extension'], $this->extensions))
|
||||
{
|
||||
throw new \FileAccessException('File operation not allowed: disallowed file extension.');
|
||||
}
|
||||
|
||||
// create specific handler when available
|
||||
if (array_key_exists($info['extension'], $this->file_handlers))
|
||||
{
|
||||
$class = '\\'.ltrim($this->file_handlers[$info['extension']], '\\');
|
||||
return $class::forge($path, $config, $this);
|
||||
}
|
||||
|
||||
return \File_Handler_File::forge($path, $config, $this);
|
||||
}
|
||||
elseif (is_dir($path))
|
||||
{
|
||||
return \File_Handler_Directory::forge($path, $config, $this, $content);
|
||||
}
|
||||
|
||||
// still here? path is invalid
|
||||
throw new \FileAccessException('Invalid path for file or directory.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this area use file locks?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function use_locks()
|
||||
{
|
||||
return $this->use_locks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are the shown extensions limited, and if so to which?
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extensions()
|
||||
{
|
||||
return $this->extensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate relative path to real path, throws error when operation is not allowed
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
* @throws \FileAccessException when outside basedir restriction or disallowed file extension
|
||||
* @throws \OutsideAreaException
|
||||
*/
|
||||
public function get_path($path)
|
||||
{
|
||||
$pathinfo = is_dir($path) ? array('dirname' => $path, 'extension' => null, 'basename' => '') : pathinfo($path);
|
||||
|
||||
// make sure we have a dirname to work with
|
||||
isset($pathinfo['dirname']) or $pathinfo['dirname'] = '';
|
||||
|
||||
// do we have a basedir, and is the path already prefixed by the basedir? then just deal with the double dots...
|
||||
if ( ! empty($this->basedir) && substr($pathinfo['dirname'], 0, strlen($this->basedir)) == $this->basedir)
|
||||
{
|
||||
$pathinfo['dirname'] = realpath($pathinfo['dirname']);
|
||||
}
|
||||
else
|
||||
{
|
||||
// attempt to get the realpath(), otherwise just use path with any double dots taken out when basedir is set (for security)
|
||||
$pathinfo['dirname'] = ( ! empty($this->basedir) ? realpath($this->basedir.DS.$pathinfo['dirname']) : realpath($pathinfo['dirname']) )
|
||||
?: ( ! empty($this->basedir) ? $this->basedir.DS.str_replace('..', '', $pathinfo['dirname']) : $pathinfo['dirname']);
|
||||
}
|
||||
|
||||
// basedir prefix is required when it is set (may cause unexpected errors when realpath doesn't work)
|
||||
if ( ! empty($this->basedir) && substr($pathinfo['dirname'], 0, strlen($this->basedir)) != $this->basedir)
|
||||
{
|
||||
throw new \OutsideAreaException('File operation not allowed: given path is outside the basedir for this area.');
|
||||
}
|
||||
|
||||
// check file extension
|
||||
if ( ! empty(static::$extensions) && array_key_exists($pathinfo['extension'], static::$extensions))
|
||||
{
|
||||
throw new \FileAccessException('File operation not allowed: disallowed file extension.');
|
||||
}
|
||||
|
||||
return $pathinfo['dirname'].DS.$pathinfo['basename'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate relative path to accessible path, throws error when operation is not allowed
|
||||
*
|
||||
* @param string
|
||||
* @return string
|
||||
* @throws \LogicException when no url is set or no basedir is set and file is outside DOCROOT
|
||||
*/
|
||||
public function get_url($path)
|
||||
{
|
||||
if(empty($this->url))
|
||||
{
|
||||
throw new \LogicException('File operation now allowed: cannot create a file url without an area url.');
|
||||
}
|
||||
|
||||
$path = $this->get_path($path);
|
||||
|
||||
$basedir = $this->basedir;
|
||||
empty($basedir) and $basedir = DOCROOT;
|
||||
|
||||
if(stripos($path, $basedir) !== 0)
|
||||
{
|
||||
throw new \LogicException('File operation not allowed: cannot create file url whithout a basedir and file outside DOCROOT.');
|
||||
}
|
||||
|
||||
return rtrim($this->url, '/').'/'.ltrim(str_replace(DS, '/', substr($path, strlen($basedir))), '/');
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------------------
|
||||
* Allow all File methods to be used from an area directly
|
||||
* ------------------------------------------------------------------------------------- */
|
||||
|
||||
public function create($basepath, $name, $contents = null)
|
||||
{
|
||||
return \File::create($basepath, $name, $contents, $this);
|
||||
}
|
||||
|
||||
public function create_dir($basepath, $name, $chmod = null)
|
||||
{
|
||||
return \File::create_dir($basepath, $name, $chmod, $this);
|
||||
}
|
||||
|
||||
public function read($path, $as_string = false)
|
||||
{
|
||||
return \File::read($path, $as_string, $this);
|
||||
}
|
||||
|
||||
public function read_dir($path, $depth = 0, $filter = null)
|
||||
{
|
||||
$content = \File::read_dir($path, $depth, $filter, $this);
|
||||
return $this->get_handler($path, array(), $content);
|
||||
}
|
||||
|
||||
public function rename($path, $new_path)
|
||||
{
|
||||
return \File::rename($path, $new_path, $this);
|
||||
}
|
||||
|
||||
public function rename_dir($path, $new_path)
|
||||
{
|
||||
return \File::rename_dir($path, $new_path, $this);
|
||||
}
|
||||
|
||||
public function copy($path, $new_path)
|
||||
{
|
||||
return \File::copy($path, $new_path, $this);
|
||||
}
|
||||
|
||||
public function copy_dir($path, $new_path)
|
||||
{
|
||||
return \File::copy_dir($path, $new_path, $this);
|
||||
}
|
||||
|
||||
public function delete($path)
|
||||
{
|
||||
return \File::delete($path, $this);
|
||||
}
|
||||
|
||||
public function delete_dir($path, $recursive = true, $delete_top = true)
|
||||
{
|
||||
return \File::delete_dir($path, $recursive, $delete_top, $this);
|
||||
}
|
||||
|
||||
public function update($basepath, $name, $new_content)
|
||||
{
|
||||
return \File::update($basepath, $name, $new_content, $this);
|
||||
}
|
||||
|
||||
public function get_permissions($path)
|
||||
{
|
||||
return \File::get_permissions($path, $this);
|
||||
}
|
||||
|
||||
public function get_time($path, $type)
|
||||
{
|
||||
return \File::get_time($path, $type, $this);
|
||||
}
|
||||
|
||||
public function get_size($path)
|
||||
{
|
||||
return \File::get_size($path, $this);
|
||||
}
|
||||
}
|
@ -1,191 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class File_Handler_Directory
|
||||
{
|
||||
/**
|
||||
* @var string path to the file
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var File_Area
|
||||
*/
|
||||
protected $area;
|
||||
|
||||
/**
|
||||
* @var array listing of files and directories within this directory
|
||||
*/
|
||||
protected $content = array();
|
||||
|
||||
protected function __construct($path, array &$config, File_Area $area, $content = array())
|
||||
{
|
||||
$this->path = rtrim($path, '\\/').DS;
|
||||
$this->area = $area;
|
||||
|
||||
foreach ($content as $key => $value)
|
||||
{
|
||||
if ( ! is_int($key))
|
||||
{
|
||||
$this->content[$key] = $value === false ? false : $area->get_handler($path.DS.$key, $config, $value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->content[$key] = $area->get_handler($path.DS.$value, $config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function forge($path, array $config = array(), File_Area $area = null, $content = array())
|
||||
{
|
||||
return new static($path, $config, $area, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read directory
|
||||
*
|
||||
* @param int $depth whether or not to read recursive
|
||||
* @param array $filters array of partial regexps or non-array for default
|
||||
* @return array
|
||||
*/
|
||||
public function read($depth = 0, $filters = null)
|
||||
{
|
||||
return $this->area->read_dir($this->path, $depth, $filters, $this->area);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename file, only within current directory
|
||||
*
|
||||
* @param string $new_name new directory name
|
||||
* @return bool
|
||||
*/
|
||||
public function rename($new_name)
|
||||
{
|
||||
$info = pathinfo($this->path);
|
||||
|
||||
$new_name = str_replace(array('..', '/', '\\'), array('', '', ''), $new_name);
|
||||
|
||||
$new_path = $info['dirname'].DS.$new_name;
|
||||
|
||||
$return = $this->area->rename_dir($this->path, $new_path);
|
||||
$return and $this->path = $new_path;
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move directory to new parent directory
|
||||
*
|
||||
* @param string $new_path path to new parent directory, must be valid
|
||||
* @return bool
|
||||
*/
|
||||
public function move($new_path)
|
||||
{
|
||||
$info = pathinfo($this->path);
|
||||
$new_path = $this->area->get_path($new_path);
|
||||
|
||||
$new_path = rtrim($new_path, '\\/').DS.$info['basename'];
|
||||
|
||||
$return = $this->area->rename_dir($this->path, $new_path);
|
||||
$return and $this->path = $new_path;
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy directory
|
||||
*
|
||||
* @param string $new_path path to parent directory, must be valid
|
||||
* @return bool
|
||||
*/
|
||||
public function copy($new_path)
|
||||
{
|
||||
$info = pathinfo($this->path);
|
||||
$new_path = $this->area->get_path($new_path);
|
||||
|
||||
$new_path = rtrim($new_path, '\\/').DS.$info['basename'];
|
||||
|
||||
return $this->area->copy_dir($this->path, $new_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update contents
|
||||
*
|
||||
* This method is unavailable on this implement, that will surely cause exception.
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
throw new \BadMethodCallException('Update method is unavailable on directories.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete directory
|
||||
*
|
||||
* @param bool $recursive
|
||||
* @param bool $delete_top
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($recursive = true, $delete_top = true)
|
||||
{
|
||||
// should also destroy object but not possible in PHP right?
|
||||
return $this->area->delete_dir($this->path, $recursive, $delete_top);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url.
|
||||
*
|
||||
* This method is unavailable on this implement, that will surely cause exception.
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function get_url()
|
||||
{
|
||||
throw new \BadMethodCallException('Get_url method is unavailable on directories.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the directory permissions.
|
||||
*
|
||||
* @return string file permissions
|
||||
*/
|
||||
public function get_permissions()
|
||||
{
|
||||
return $this->area->get_permissions($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get directory's the created or modified timestamp.
|
||||
*
|
||||
* @param string $type modified or created
|
||||
* @return int Unix Timestamp
|
||||
*/
|
||||
public function get_time($type = 'modified')
|
||||
{
|
||||
return $this->area->get_time($this->path, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size.
|
||||
*
|
||||
* This method is unavailable on this implement, that will surely cause exception.
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function get_size()
|
||||
{
|
||||
throw new \BadMethodCallException('Get_size method is unavailable on directories.');
|
||||
}
|
||||
}
|
@ -1,205 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class File_Handler_File
|
||||
{
|
||||
/**
|
||||
* @var string path to the file
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* @var File_Area
|
||||
*/
|
||||
protected $area;
|
||||
|
||||
/**
|
||||
* @var Resource file resource
|
||||
*/
|
||||
protected $resource;
|
||||
|
||||
/**
|
||||
* @var bool whether the current object is read only
|
||||
*/
|
||||
protected $readonly = false;
|
||||
|
||||
protected function __construct($path, array $config, File_Area $area, $content = array())
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->area = $area;
|
||||
}
|
||||
|
||||
public static function forge($path, array $config = array(), File_Area $area = null, $content = array())
|
||||
{
|
||||
$obj = new static($path, $config, \File::instance($area), $content);
|
||||
|
||||
$config['path'] = $path;
|
||||
$config['area'] = $area;
|
||||
foreach ($config as $key => $value)
|
||||
{
|
||||
if (property_exists($obj, $key) && empty($obj->$key))
|
||||
{
|
||||
$obj->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read file
|
||||
*
|
||||
* @param bool $as_string whether to use file_get_contents() or readfile()
|
||||
* @return string|IO
|
||||
*/
|
||||
public function read($as_string = false)
|
||||
{
|
||||
return $this->area->read($this->path, $as_string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename file, only within current directory
|
||||
*
|
||||
* @param string $new_name new filename
|
||||
* @param string|bool $new_extension new extension, false to keep current
|
||||
* @return bool
|
||||
*/
|
||||
public function rename($new_name, $new_extension = false)
|
||||
{
|
||||
$info = pathinfo($this->path);
|
||||
|
||||
$new_name = str_replace(array('..', '/', '\\'), array('', '', ''), $new_name);
|
||||
$extension = $new_extension === false
|
||||
? $info['extension']
|
||||
: ltrim($new_extension, '.');
|
||||
$extension = ! empty($extension) ? '.'.$extension : '';
|
||||
|
||||
$new_path = $info['dirname'].DS.$new_name.$extension;
|
||||
|
||||
$return = $this->area->rename($this->path, $new_path);
|
||||
$return and $this->path = $new_path;
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move file to new directory
|
||||
*
|
||||
* @param string $new_path path to new directory, must be valid
|
||||
* @return bool
|
||||
*/
|
||||
public function move($new_path)
|
||||
{
|
||||
$info = pathinfo($this->path);
|
||||
|
||||
$new_path = $this->area->get_path($new_path);
|
||||
$new_path = rtrim($new_path, '\\/').DS.$info['basename'];
|
||||
|
||||
$return = $this->area->rename($this->path, $new_path);
|
||||
$return and $this->path = $new_path;
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy file
|
||||
*
|
||||
* @param string $new_path path to target directory, must be valid
|
||||
* @return bool
|
||||
*/
|
||||
public function copy($new_path)
|
||||
{
|
||||
$info = pathinfo($this->path);
|
||||
$new_path = $this->area->get_path($new_path);
|
||||
|
||||
$new_path = rtrim($new_path, '\\/').DS.$info['basename'];
|
||||
|
||||
return $this->area->copy($this->path, $new_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update contents
|
||||
*
|
||||
* @param mixed $new_content new file contents
|
||||
* @return bool
|
||||
*/
|
||||
public function update($new_content)
|
||||
{
|
||||
$info = pathinfo($this->path);
|
||||
return $this->area->update($info['dirname'], $info['basename'], $new_content, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
// should also destroy object but not possible in PHP right?
|
||||
return $this->area->delete($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function get_url()
|
||||
{
|
||||
return $this->area->get_url($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file's permissions.
|
||||
*
|
||||
* @return string file permissions
|
||||
*/
|
||||
public function get_permissions()
|
||||
{
|
||||
return $this->area->get_permissions($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file's created or modified timestamp.
|
||||
*
|
||||
* @param string $type modified or created
|
||||
* @return int Unix Timestamp
|
||||
*/
|
||||
public function get_time($type = 'modified')
|
||||
{
|
||||
return $this->area->get_time($this->path, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file's size.
|
||||
*
|
||||
* @return int File size
|
||||
*/
|
||||
public function get_size()
|
||||
{
|
||||
return $this->area->get_size($this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file's path.
|
||||
*
|
||||
* @return string File path
|
||||
*/
|
||||
public function get_path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
}
|
@ -1,559 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* The Finder class allows for searching through a search path for a given
|
||||
* file, as well as loading a given file.
|
||||
*
|
||||
* @package Fuel
|
||||
* @subpackage Core
|
||||
*/
|
||||
class Finder
|
||||
{
|
||||
/**
|
||||
* @var Finder $instance Singleton master instance
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
public static function _init()
|
||||
{
|
||||
\Config::load('file', true);
|
||||
|
||||
// make sure the configured chmod values are octal
|
||||
$chmod = \Config::get('file.chmod.folders', 0777);
|
||||
is_string($chmod) and \Config::set('file.chmod.folders', octdec($chmod));
|
||||
$chmod = \Config::get('file.chmod.files', 0666);
|
||||
is_string($chmod) and \Config::set('file.chmod.files', octdec($chmod));
|
||||
}
|
||||
/**
|
||||
* An alias for Finder::instance()->locate();
|
||||
*
|
||||
* @param string $dir Directory to look in
|
||||
* @param string $file File to find
|
||||
* @param string $ext File extension
|
||||
* @param bool $multiple Whether to find multiple files
|
||||
* @param bool $cache Whether to cache this path or not
|
||||
* @return mixed Path, or paths, or false
|
||||
*/
|
||||
public static function search($dir, $file, $ext = '.php', $multiple = false, $cache = true)
|
||||
{
|
||||
return static::instance()->locate($dir, $file, $ext, $multiple, $cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a singleton instance of Finder
|
||||
*
|
||||
* @return Finder
|
||||
*/
|
||||
public static function instance()
|
||||
{
|
||||
if ( ! static::$instance)
|
||||
{
|
||||
static::$instance = static::forge(array(APPPATH, COREPATH));
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forges new Finders.
|
||||
*
|
||||
* @param array $paths The paths to initialize with
|
||||
* @return Finder
|
||||
*/
|
||||
public static function forge($paths = array())
|
||||
{
|
||||
return new static($paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array $paths Holds all of the search paths
|
||||
*/
|
||||
protected $paths = array();
|
||||
|
||||
/**
|
||||
* @var array $flash_paths Search paths that only last for one lookup
|
||||
*/
|
||||
protected $flash_paths = array();
|
||||
|
||||
/**
|
||||
* @var int $cache_lifetime the amount of time to cache in seconds
|
||||
*/
|
||||
protected $cache_lifetime = null;
|
||||
|
||||
/**
|
||||
* @var string $cache_dir path to the cache file location
|
||||
*/
|
||||
protected $cache_dir = null;
|
||||
|
||||
/**
|
||||
* @var array $cached_paths Cached lookup paths
|
||||
*/
|
||||
protected $cached_paths = array();
|
||||
|
||||
/**
|
||||
* @var bool $cache_valid Whether the path cache is valid or not
|
||||
*/
|
||||
protected $cache_valid = true;
|
||||
|
||||
/**
|
||||
* Takes in an array of paths, preps them and gets the party started.
|
||||
*
|
||||
* @param array $paths The paths to initialize with
|
||||
*/
|
||||
public function __construct($paths = array())
|
||||
{
|
||||
$this->add_path($paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a path (or paths) to the search path at a given position.
|
||||
*
|
||||
* Possible positions:
|
||||
* (null): Append to the end of the search path
|
||||
* (-1): Prepend to the start of the search path
|
||||
* (index): The path will get inserted AFTER the given index
|
||||
*
|
||||
* @param string|array $paths The path to add
|
||||
* @param int $pos The position to add the path
|
||||
* @return $this
|
||||
* @throws \OutOfBoundsException
|
||||
*/
|
||||
public function add_path($paths, $pos = null)
|
||||
{
|
||||
if ( ! is_array($paths))
|
||||
{
|
||||
$paths = array($paths);
|
||||
}
|
||||
|
||||
foreach ($paths as $path)
|
||||
{
|
||||
if ($pos === null)
|
||||
{
|
||||
$this->paths[] = $this->prep_path($path);
|
||||
}
|
||||
elseif ($pos === -1)
|
||||
{
|
||||
array_unshift($this->paths, $this->prep_path($path));
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($pos > count($this->paths))
|
||||
{
|
||||
throw new \OutOfBoundsException(sprintf('Position "%s" is out of range.', $pos));
|
||||
}
|
||||
array_splice($this->paths, $pos, 0, $this->prep_path($path));
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a path from the search path.
|
||||
*
|
||||
* @param string $path Path to remove
|
||||
* @return $this
|
||||
*/
|
||||
public function remove_path($path)
|
||||
{
|
||||
foreach ($this->paths as $i => $p)
|
||||
{
|
||||
if ($p === $path)
|
||||
{
|
||||
unset($this->paths[$i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds multiple flash paths.
|
||||
*
|
||||
* @param array $paths The paths to add
|
||||
* @return $this
|
||||
*/
|
||||
public function flash($paths)
|
||||
{
|
||||
if ( ! is_array($paths))
|
||||
{
|
||||
$paths = array($paths);
|
||||
}
|
||||
|
||||
foreach ($paths as $path)
|
||||
{
|
||||
$this->flash_paths[] = $this->prep_path($path);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the flash paths.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function clear_flash()
|
||||
{
|
||||
$this->flash_paths = array();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current search paths...including flash paths.
|
||||
*
|
||||
* @return array Search paths
|
||||
*/
|
||||
public function paths()
|
||||
{
|
||||
return array_merge($this->flash_paths, $this->paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a path for usage. It ensures that the path has a trailing
|
||||
* Directory Separator.
|
||||
*
|
||||
* @param string $path The path to prepare
|
||||
* @return string
|
||||
*/
|
||||
public function prep_path($path)
|
||||
{
|
||||
$path = str_replace(array('/', '\\'), DS, $path);
|
||||
return rtrim($path, DS).DS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an array of paths.
|
||||
*
|
||||
* @param array $paths The paths to prepare
|
||||
* @return array
|
||||
*/
|
||||
public function prep_paths(array $paths)
|
||||
{
|
||||
foreach ($paths as &$path)
|
||||
{
|
||||
$path = $this->prep_path($path);
|
||||
}
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of all the files in a given directory inside all of the
|
||||
* loaded search paths (e.g. the cascading file system). This is useful
|
||||
* for things like finding all the config files in all the search paths.
|
||||
*
|
||||
* @param string $directory The directory to look in
|
||||
* @param string $filter The file filter
|
||||
* @return array the array of files
|
||||
*/
|
||||
public function list_files($directory = null, $filter = '*.php')
|
||||
{
|
||||
$paths = $this->paths;
|
||||
|
||||
// get extra information of the active request
|
||||
if (class_exists('Request', false) and ($uri = \Uri::string()) !== null)
|
||||
{
|
||||
$paths = array_merge(\Request::active()->get_paths(), $paths);
|
||||
}
|
||||
|
||||
// Merge in the flash paths then reset the flash paths
|
||||
$paths = array_merge($this->flash_paths, $paths);
|
||||
$this->clear_flash();
|
||||
|
||||
$found = array();
|
||||
foreach ($paths as $path)
|
||||
{
|
||||
$files = new \GlobIterator(rtrim($path.$directory, DS).DS.$filter);
|
||||
foreach($files as $file)
|
||||
{
|
||||
$found[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates a given file in the search paths.
|
||||
*
|
||||
* @param string $dir Directory to look in
|
||||
* @param string $file File to find
|
||||
* @param string $ext File extension
|
||||
* @param bool $multiple Whether to find multiple files
|
||||
* @param bool $cache Whether to cache this path or not
|
||||
* @return mixed Path, or paths, or false
|
||||
*/
|
||||
public function locate($dir, $file, $ext = '.php', $multiple = false, $cache = true)
|
||||
{
|
||||
$found = $multiple ? array() : false;
|
||||
|
||||
// absolute path requested?
|
||||
if ($file[0] === '/' or substr($file, 1, 2) === ':\\')
|
||||
{
|
||||
// if the base file does not exist, stick the extension to the back of it
|
||||
if ( ! is_file($file))
|
||||
{
|
||||
$file .= $ext;
|
||||
}
|
||||
if ( ! is_file($file))
|
||||
{
|
||||
// at this point, found would be either empty array or false
|
||||
return $found;
|
||||
}
|
||||
return $multiple ? array($file) : $file;
|
||||
}
|
||||
|
||||
// determine the cache prefix
|
||||
if ($multiple)
|
||||
{
|
||||
// make sure cache is not used if the loaded package and module list is changed
|
||||
$cachekey = '';
|
||||
class_exists('Module', false) and $cachekey .= implode('|', \Module::loaded());
|
||||
$cachekey .= '|';
|
||||
class_exists('Package', false) and $cachekey .= implode('|', \Package::loaded());
|
||||
$cache_id = md5($cachekey).'.';
|
||||
}
|
||||
else
|
||||
{
|
||||
$cache_id = 'S.';
|
||||
}
|
||||
|
||||
$paths = array();
|
||||
|
||||
// If a filename contains a :: then it is trying to be found in a namespace.
|
||||
// This is sometimes used to load a view from a non-loaded module.
|
||||
if ($pos = strripos($file, '::'))
|
||||
{
|
||||
// get the namespace path
|
||||
if ($path = \Autoloader::namespace_path('\\'.ucfirst(substr($file, 0, $pos))))
|
||||
{
|
||||
$cache_id .= substr($file, 0, $pos);
|
||||
|
||||
// and strip the classes directory as we need the module root
|
||||
$paths = array(substr($path, 0, -8));
|
||||
|
||||
// strip the namespace from the filename
|
||||
$file = substr($file, $pos + 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$paths = $this->paths;
|
||||
|
||||
// get extra information of the active request
|
||||
if (class_exists('Request', false) and ($request = \Request::active()))
|
||||
{
|
||||
$request->module and $cache_id .= $request->module;
|
||||
$paths = array_merge($request->get_paths(), $paths);
|
||||
}
|
||||
}
|
||||
|
||||
// Merge in the flash paths then reset the flash paths
|
||||
$paths = array_merge($this->flash_paths, $paths);
|
||||
$this->clear_flash();
|
||||
|
||||
$file = $this->prep_path($dir).$file.$ext;
|
||||
$cache_id .= $file;
|
||||
|
||||
if ($cache and $cached_path = $this->from_cache($cache_id))
|
||||
{
|
||||
return $cached_path;
|
||||
}
|
||||
|
||||
foreach ($paths as $dir)
|
||||
{
|
||||
$file_path = $dir.$file;
|
||||
|
||||
if (is_file($file_path))
|
||||
{
|
||||
if ( ! $multiple)
|
||||
{
|
||||
$found = $file_path;
|
||||
break;
|
||||
}
|
||||
|
||||
$found[] = $file_path;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty($found) and $cache)
|
||||
{
|
||||
$this->add_to_cache($cache_id, $found);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in the cached paths with the given cache id.
|
||||
*
|
||||
* @param string $cache_id Cache id to read
|
||||
* @return void
|
||||
*/
|
||||
public function read_cache($cache_id)
|
||||
{
|
||||
// make sure we have all config data
|
||||
empty($this->cache_dir) and $this->cache_dir = \Config::get('cache_dir', APPPATH.'cache/');
|
||||
empty($this->cache_lifetime) and $this->cache_lifetime = \Config::get('cache_lifetime', 3600);
|
||||
|
||||
if ($cached = $this->cache($cache_id))
|
||||
{
|
||||
$this->cached_paths = $cached;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out the cached paths if they need to be.
|
||||
*
|
||||
* @param string $cache_id Cache id to read
|
||||
* @return void
|
||||
*/
|
||||
public function write_cache($cache_id)
|
||||
{
|
||||
$this->cache_valid or $this->cache($cache_id, $this->cached_paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads in the given cache_id from the cache if it exists.
|
||||
*
|
||||
* @param string $cache_id Cache id to load
|
||||
* @return string|bool Path or false if not found
|
||||
*/
|
||||
protected function from_cache($cache_id)
|
||||
{
|
||||
$cache_id = md5($cache_id);
|
||||
if (array_key_exists($cache_id, $this->cached_paths))
|
||||
{
|
||||
return $this->cached_paths[$cache_id];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads in the given cache_id from the cache if it exists.
|
||||
*
|
||||
* @param string $cache_id Cache id to load
|
||||
* @return string|bool Path or false if not found
|
||||
*/
|
||||
protected function add_to_cache($cache_id, $path)
|
||||
{
|
||||
$cache_id = md5($cache_id);
|
||||
$this->cached_paths[$cache_id] = $path;
|
||||
$this->cache_valid = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method does basic filesystem caching. It is used for things like path caching.
|
||||
*
|
||||
* This method is from KohanaPHP's Kohana class.
|
||||
*
|
||||
* @param string $name the cache name
|
||||
* @param array $data the data to cache (if non given it returns)
|
||||
* @param int $lifetime the number of seconds for the cache too live
|
||||
* @return bool|null
|
||||
*/
|
||||
protected function cache($name, $data = null, $lifetime = null)
|
||||
{
|
||||
// Cache file is a hash of the name
|
||||
$file = $name.'.pathcache';
|
||||
|
||||
// Cache directories are split by keys to prevent filesystem overload
|
||||
$dir = rtrim($this->cache_dir, DS).DS;
|
||||
|
||||
if ($lifetime === NULL)
|
||||
{
|
||||
// Use the default lifetime
|
||||
$lifetime = $this->cache_lifetime;
|
||||
}
|
||||
|
||||
if ($data === null)
|
||||
{
|
||||
if (is_file($dir.$file))
|
||||
{
|
||||
if ((time() - filemtime($dir.$file)) < $lifetime)
|
||||
{
|
||||
// Return the cache
|
||||
try
|
||||
{
|
||||
return unserialize(file_get_contents($dir.$file));
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// Cache exists but could not be read, ignore it
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// Cache has expired
|
||||
unlink($dir.$file);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// Cache has mostly likely already been deleted,
|
||||
// let return happen normally.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cache not found
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ! is_dir($dir))
|
||||
{
|
||||
// Create the cache directory
|
||||
mkdir($dir, \Config::get('file.chmod.folders', 0777), true);
|
||||
|
||||
// Set permissions (must be manually set to fix umask issues)
|
||||
chmod($dir, \Config::get('file.chmod.folders', 0777));
|
||||
}
|
||||
|
||||
// Force the data to be a string
|
||||
$data = serialize($data);
|
||||
|
||||
try
|
||||
{
|
||||
// Write the cache, and set permissions
|
||||
if ($result = (bool) file_put_contents($dir.$file, $data, LOCK_EX))
|
||||
{
|
||||
try
|
||||
{
|
||||
chmod($dir.$file, \Config::get('file.chmod.files', 0666));
|
||||
}
|
||||
catch (\PhpErrorException $e)
|
||||
{
|
||||
// if we get something else then a chmod error, bail out
|
||||
if (substr($e->getMessage(), 0, 8) !== 'chmod():')
|
||||
{
|
||||
throw new $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
// Failed to write cache
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,312 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Form Class
|
||||
*
|
||||
* Helper for creating forms with support for creating dynamic form objects.
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
*/
|
||||
class Form
|
||||
{
|
||||
/*
|
||||
* @var Form_Instance the default form instance
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* When autoloaded this will method will be fired, load once and once only
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
\Config::load('form', true);
|
||||
|
||||
static::$instance = static::forge('_default_', \Config::get('form'));
|
||||
}
|
||||
|
||||
public static function forge($fieldset = 'default', array $config = array())
|
||||
{
|
||||
if (is_string($fieldset))
|
||||
{
|
||||
($set = \Fieldset::instance($fieldset)) and $fieldset = $set;
|
||||
}
|
||||
|
||||
if ($fieldset instanceof Fieldset)
|
||||
{
|
||||
if ($fieldset->form(false) != null)
|
||||
{
|
||||
throw new \DomainException('Form instance already exists, cannot be recreated. Use instance() instead of forge() to retrieve the existing instance.');
|
||||
}
|
||||
}
|
||||
|
||||
return new \Form_Instance($fieldset, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 'default' instance of Form
|
||||
*
|
||||
* @param null|string $name
|
||||
* @return Form_Instance
|
||||
*/
|
||||
public static function instance($name = null)
|
||||
{
|
||||
$fieldset = \Fieldset::instance($name);
|
||||
return $fieldset === false ? false : $fieldset->form();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a form open tag
|
||||
*
|
||||
* @param string|array $attributes action string or array with more tag attribute settings
|
||||
* @param array $hidden
|
||||
* @return string
|
||||
*/
|
||||
public static function open($attributes = array(), array $hidden = array())
|
||||
{
|
||||
return static::$instance->open($attributes, $hidden);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a form close tag
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function close()
|
||||
{
|
||||
return static::$instance->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fieldset open tag
|
||||
*
|
||||
* @param array $attributes array with tag attribute settings
|
||||
* @param string $legend string for the fieldset legend
|
||||
* @return string
|
||||
*/
|
||||
public static function fieldset_open($attributes = array(), $legend = null)
|
||||
{
|
||||
return static::$instance->fieldset_open($attributes, $legend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fieldset close tag
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function fieldset_close()
|
||||
{
|
||||
return static::$instance->fieldset_close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a form input
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function input($field, $value = null, array $attributes = array())
|
||||
{
|
||||
return static::$instance->input($field, $value, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hidden field
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function hidden($field, $value = null, array $attributes = array())
|
||||
{
|
||||
return static::$instance->hidden($field, $value, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a CSRF hidden field
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function csrf()
|
||||
{
|
||||
return static::hidden(\Config::get('security.csrf_token_key', 'fuel_csrf_token'), \Security::fetch_token());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a password input field
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function password($field, $value = null, array $attributes = array())
|
||||
{
|
||||
return static::$instance->password($field, $value, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a radio button
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param mixed $checked either attributes (array) or bool/string to set checked status
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function radio($field, $value = null, $checked = null, array $attributes = array())
|
||||
{
|
||||
return static::$instance->radio($field, $value, $checked, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a checkbox
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param mixed $checked either attributes (array) or bool/string to set checked status
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function checkbox($field, $value = null, $checked = null, array $attributes = array())
|
||||
{
|
||||
return static::$instance->checkbox($field, $value, $checked, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file upload input field
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function file($field, array $attributes = array())
|
||||
{
|
||||
return static::$instance->file($field, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a button
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function button($field, $value = null, array $attributes = array())
|
||||
{
|
||||
return static::$instance->button($field, $value, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reset button
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function reset($field = 'reset', $value = 'Reset', array $attributes = array())
|
||||
{
|
||||
return static::$instance->reset($field, $value, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a submit button
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function submit($field = 'submit', $value = 'Submit', array $attributes = array())
|
||||
{
|
||||
return static::$instance->submit($field, $value, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textarea field
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function textarea($field, $value = null, array $attributes = array())
|
||||
{
|
||||
return static::$instance->textarea($field, $value, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select
|
||||
*
|
||||
* Generates a html select element based on the given parameters
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $values selected value(s)
|
||||
* @param array $options array of options and option groups
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function select($field, $values = null, array $options = array(), array $attributes = array())
|
||||
{
|
||||
return static::$instance->select($field, $values, $options, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a label field
|
||||
*
|
||||
* @param string|array $label either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $id
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function label($label, $id = null, array $attributes = array())
|
||||
{
|
||||
return static::$instance->label($label, $id, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prep Value
|
||||
*
|
||||
* Prepares the value for display in the form
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public static function prep_value($value)
|
||||
{
|
||||
return static::$instance->prep_value($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attr to String
|
||||
*
|
||||
* Wraps the global attributes function and does some form specific work
|
||||
*
|
||||
* @param array $attr
|
||||
* @return string
|
||||
*/
|
||||
protected static function attr_to_string($attr)
|
||||
{
|
||||
return static::$instance->attr_to_string($attr);
|
||||
}
|
||||
|
||||
}
|
@ -1,820 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Form Class
|
||||
*
|
||||
* Helper for creating forms with support for creating dynamic form objects.
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
*/
|
||||
class Form_Instance
|
||||
{
|
||||
/**
|
||||
* Valid types for input tags (including HTML5)
|
||||
*/
|
||||
protected static $_valid_inputs = array(
|
||||
'button', 'checkbox', 'color', 'date', 'datetime',
|
||||
'datetime-local', 'email', 'file', 'hidden', 'image',
|
||||
'month', 'number', 'password', 'radio', 'range',
|
||||
'reset', 'search', 'submit', 'tel', 'text', 'time',
|
||||
'url', 'week',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var Fieldset
|
||||
*/
|
||||
protected $fieldset;
|
||||
|
||||
public function __construct($fieldset, array $config = array())
|
||||
{
|
||||
if ($fieldset instanceof Fieldset)
|
||||
{
|
||||
$fieldset->form($this);
|
||||
$this->fieldset = $fieldset;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->fieldset = \Fieldset::forge($fieldset, array('form_instance' => $this));
|
||||
}
|
||||
|
||||
foreach ($config as $key => $val)
|
||||
{
|
||||
$this->set_config($key, $val);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set form attribute
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return \Form_Instance
|
||||
*/
|
||||
public function set_attribute($key, $value)
|
||||
{
|
||||
$attributes = $this->get_config('form_attributes', array());
|
||||
$attributes[$key] = $value;
|
||||
$this->set_config('form_attributes', $attributes);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get form attribute
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_attribute($key, $default = null)
|
||||
{
|
||||
$attributes = $this->get_config('form_attributes', array());
|
||||
|
||||
return array_key_exists($key, $attributes) ? $attributes[$key] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method toString that will build this as a form
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a form open tag
|
||||
*
|
||||
* @param string|array $attributes action string or array with more tag attribute settings
|
||||
* @param array $hidden
|
||||
* @return string
|
||||
*/
|
||||
public function open($attributes = array(), array $hidden = array())
|
||||
{
|
||||
$attributes = ! is_array($attributes) ? array('action' => $attributes) : $attributes;
|
||||
|
||||
// If there is still no action set, Form-post
|
||||
if( ! array_key_exists('action', $attributes) or empty($attributes['action']))
|
||||
{
|
||||
$attributes['action'] = \Uri::main();
|
||||
}
|
||||
|
||||
// If not a full URL, create one
|
||||
elseif ( ! strpos($attributes['action'], '://'))
|
||||
{
|
||||
$attributes['action'] = \Uri::create($attributes['action']);
|
||||
}
|
||||
|
||||
if (empty($attributes['accept-charset']))
|
||||
{
|
||||
$attributes['accept-charset'] = strtolower(\Fuel::$encoding);
|
||||
}
|
||||
|
||||
// If method is empty, use POST
|
||||
! empty($attributes['method']) || $attributes['method'] = $this->get_config('form_method', 'post');
|
||||
|
||||
$form = '<form';
|
||||
foreach ($attributes as $prop => $value)
|
||||
{
|
||||
$form .= ' '.$prop.'="'.$value.'"';
|
||||
}
|
||||
$form .= '>';
|
||||
|
||||
// Add hidden fields when given
|
||||
foreach ($hidden as $field => $value)
|
||||
{
|
||||
$form .= PHP_EOL.$this->hidden($field, $value);
|
||||
}
|
||||
|
||||
// Add CSRF token automatically
|
||||
if (Config::get('security.csrf_auto_token', false))
|
||||
{
|
||||
$form .= PHP_EOL.\Form::csrf();
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a form close tag
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return '</form>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fieldset open tag
|
||||
*
|
||||
* @param array $attributes array with tag attribute settings
|
||||
* @param string $legend string for the fieldset legend
|
||||
* @return string
|
||||
*/
|
||||
public function fieldset_open($attributes = array(), $legend = null)
|
||||
{
|
||||
$fieldset_open = '<fieldset ' . array_to_attr($attributes) . ' >';
|
||||
|
||||
! is_null($legend) and $attributes['legend'] = $legend;
|
||||
if ( ! empty($attributes['legend']))
|
||||
{
|
||||
$fieldset_open.= "\n<legend>".$attributes['legend']."</legend>";
|
||||
}
|
||||
|
||||
return $fieldset_open;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fieldset close tag
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function fieldset_close()
|
||||
{
|
||||
return '</fieldset>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a form input
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function input($field, $value = null, array $attributes = array())
|
||||
{
|
||||
if (is_array($field))
|
||||
{
|
||||
$attributes = $field;
|
||||
! array_key_exists('value', $attributes) and $attributes['value'] = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$attributes['name'] = (string) $field;
|
||||
$attributes['value'] = (string) $value;
|
||||
}
|
||||
|
||||
$attributes['type'] = empty($attributes['type']) ? 'text' : $attributes['type'];
|
||||
|
||||
if ( ! in_array($attributes['type'], static::$_valid_inputs))
|
||||
{
|
||||
throw new \InvalidArgumentException(sprintf('"%s" is not a valid input type.', $attributes['type']));
|
||||
}
|
||||
|
||||
if ($this->get_config('prep_value', true) && empty($attributes['dont_prep']))
|
||||
{
|
||||
$attributes['value'] = $this->prep_value($attributes['value']);
|
||||
}
|
||||
unset($attributes['dont_prep']);
|
||||
|
||||
if (empty($attributes['id']) && $this->get_config('auto_id', false) == true)
|
||||
{
|
||||
$attributes['id'] = $this->get_config('auto_id_prefix', 'form_').$attributes['name'];
|
||||
}
|
||||
|
||||
$tag = ! empty($attributes['tag']) ? $attributes['tag'] : 'input';
|
||||
unset($attributes['tag']);
|
||||
|
||||
return html_tag($tag, $this->attr_to_string($attributes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hidden field
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function hidden($field, $value = null, array $attributes = array())
|
||||
{
|
||||
if (is_array($field))
|
||||
{
|
||||
$attributes = $field;
|
||||
}
|
||||
else
|
||||
{
|
||||
$attributes['name'] = (string) $field;
|
||||
$attributes['value'] = (string) $value;
|
||||
}
|
||||
$attributes['type'] = 'hidden';
|
||||
|
||||
return $this->input($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a password input field
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function password($field, $value = null, array $attributes = array())
|
||||
{
|
||||
if (is_array($field))
|
||||
{
|
||||
$attributes = $field;
|
||||
}
|
||||
else
|
||||
{
|
||||
$attributes['name'] = (string) $field;
|
||||
$attributes['value'] = (string) $value;
|
||||
}
|
||||
$attributes['type'] = 'password';
|
||||
|
||||
return $this->input($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a radio button
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param mixed $checked either attributes (array) or bool/string to set checked status
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function radio($field, $value = null, $checked = null, array $attributes = array())
|
||||
{
|
||||
if (is_array($field))
|
||||
{
|
||||
$attributes = $field;
|
||||
}
|
||||
else
|
||||
{
|
||||
is_array($checked) and $attributes = $checked;
|
||||
$attributes['name'] = (string) $field;
|
||||
$attributes['value'] = (string) $value;
|
||||
|
||||
# Added for 1.2 to allow checked true/false. in 3rd argument, used to be attributes
|
||||
if ( ! is_array($checked))
|
||||
{
|
||||
// If it's true, then go for it
|
||||
if (is_bool($checked))
|
||||
{
|
||||
if($checked === true)
|
||||
{
|
||||
$attributes['checked'] = 'checked';
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, if the string/number/whatever matches then do it
|
||||
elseif (is_scalar($checked) and $checked == $value)
|
||||
{
|
||||
$attributes['checked'] = 'checked';
|
||||
}
|
||||
}
|
||||
}
|
||||
$attributes['type'] = 'radio';
|
||||
|
||||
return $this->input($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a checkbox
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param mixed $checked either attributes (array) or bool/string to set checked status
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function checkbox($field, $value = null, $checked = null, array $attributes = array())
|
||||
{
|
||||
if (is_array($field))
|
||||
{
|
||||
$attributes = $field;
|
||||
}
|
||||
else
|
||||
{
|
||||
is_array($checked) and $attributes = $checked;
|
||||
$attributes['name'] = (string) $field;
|
||||
$attributes['value'] = (string) $value;
|
||||
|
||||
# Added for 1.2 to allow checked true/false. in 3rd argument, used to be attributes
|
||||
if ( ! is_array($checked))
|
||||
{
|
||||
// If it's true, then go for it
|
||||
if (is_bool($checked))
|
||||
{
|
||||
if($checked === true)
|
||||
{
|
||||
$attributes['checked'] = 'checked';
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, if the string/number/whatever matches then do it
|
||||
elseif (is_scalar($checked) and $checked == $value)
|
||||
{
|
||||
$attributes['checked'] = 'checked';
|
||||
}
|
||||
}
|
||||
}
|
||||
$attributes['type'] = 'checkbox';
|
||||
|
||||
return $this->input($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file upload input field
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function file($field, array $attributes = array())
|
||||
{
|
||||
if (is_array($field))
|
||||
{
|
||||
$attributes = $field;
|
||||
}
|
||||
else
|
||||
{
|
||||
$attributes['name'] = (string) $field;
|
||||
}
|
||||
$attributes['type'] = 'file';
|
||||
|
||||
return $this->input($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a button
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function button($field, $value = null, array $attributes = array())
|
||||
{
|
||||
if (is_array($field))
|
||||
{
|
||||
$attributes = $field;
|
||||
$value = isset($attributes['value']) ? $attributes['value'] : $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$attributes['name'] = (string) $field;
|
||||
$value = isset($value) ? $value : $attributes['name'];
|
||||
}
|
||||
|
||||
return html_tag('button', $this->attr_to_string($attributes), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reset button
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function reset($field = 'reset', $value = 'Reset', array $attributes = array())
|
||||
{
|
||||
if (is_array($field))
|
||||
{
|
||||
$attributes = $field;
|
||||
}
|
||||
else
|
||||
{
|
||||
$attributes['name'] = (string) $field;
|
||||
$attributes['value'] = (string) $value;
|
||||
}
|
||||
$attributes['type'] = 'reset';
|
||||
|
||||
return $this->input($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a submit button
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function submit($field = 'submit', $value = 'Submit', array $attributes = array())
|
||||
{
|
||||
if (is_array($field))
|
||||
{
|
||||
$attributes = $field;
|
||||
}
|
||||
else
|
||||
{
|
||||
$attributes['name'] = (string) $field;
|
||||
$attributes['value'] = (string) $value;
|
||||
}
|
||||
$attributes['type'] = 'submit';
|
||||
|
||||
return $this->input($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a textarea field
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function textarea($field, $value = null, array $attributes = array())
|
||||
{
|
||||
if (is_array($field))
|
||||
{
|
||||
$attributes = $field;
|
||||
}
|
||||
else
|
||||
{
|
||||
$attributes['name'] = (string) $field;
|
||||
$attributes['value'] = (string) $value;
|
||||
}
|
||||
$value = is_scalar($attributes['value']) ? $attributes['value'] : '';
|
||||
unset($attributes['value']);
|
||||
|
||||
if ($this->get_config('prep_value', true) && empty($attributes['dont_prep']))
|
||||
{
|
||||
$value = $this->prep_value($value);
|
||||
}
|
||||
unset($attributes['dont_prep']);
|
||||
|
||||
if (empty($attributes['id']) && $this->get_config('auto_id', false) == true)
|
||||
{
|
||||
$attributes['id'] = $this->get_config('auto_id_prefix', '').$attributes['name'];
|
||||
}
|
||||
|
||||
return html_tag('textarea', $this->attr_to_string($attributes), $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select
|
||||
*
|
||||
* Generates a html select element based on the given parameters
|
||||
*
|
||||
* @param string|array $field either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $values selected value(s)
|
||||
* @param array $options array of options and option groups
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function select($field, $values = null, array $options = array(), array $attributes = array())
|
||||
{
|
||||
if (is_array($field))
|
||||
{
|
||||
$attributes = $field;
|
||||
|
||||
if ( ! isset($attributes['selected']))
|
||||
{
|
||||
$attributes['selected'] = ! isset($attributes['value']) ? (isset($attributes['default']) ? $attributes['default'] : null) : $attributes['value'];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$attributes['name'] = (string) $field;
|
||||
$attributes['selected'] = ($values === null or $values === array()) ? (isset($attributes['default']) ? $attributes['default'] : $values) : $values;
|
||||
$attributes['options'] = $options;
|
||||
}
|
||||
unset($attributes['value']);
|
||||
unset($attributes['default']);
|
||||
|
||||
if ( ! isset($attributes['options']) || ! is_array($attributes['options']))
|
||||
{
|
||||
throw new \InvalidArgumentException(sprintf('Select element "%s" is either missing the "options" or "options" is not array.', $attributes['name']));
|
||||
}
|
||||
// Get the options then unset them from the array
|
||||
$options = $attributes['options'];
|
||||
unset($attributes['options']);
|
||||
|
||||
// Get the selected options then unset it from the array
|
||||
// and make sure they're all strings to avoid type conversions
|
||||
$selected = ! isset($attributes['selected']) ? array() : array_map(function($a) { return (string) $a; }, array_values((array) $attributes['selected']));
|
||||
|
||||
unset($attributes['selected']);
|
||||
|
||||
// workaround to access the current object context in the closure
|
||||
$current_obj =& $this;
|
||||
|
||||
// closure to recursively process the options array
|
||||
$listoptions = function (array $options, $selected, $level = 1) use (&$listoptions, &$current_obj, &$attributes)
|
||||
{
|
||||
$input = PHP_EOL;
|
||||
foreach ($options as $key => $val)
|
||||
{
|
||||
if (is_array($val))
|
||||
{
|
||||
$optgroup = $listoptions($val, $selected, $level + 1);
|
||||
$optgroup .= str_repeat("\t", $level);
|
||||
$input .= str_repeat("\t", $level).html_tag('optgroup', array('label' => $key, 'style' => 'text-indent: '.(20+10*($level-1)).'px;'), $optgroup).PHP_EOL;
|
||||
}
|
||||
else
|
||||
{
|
||||
$opt_attr = array('value' => $key);
|
||||
$level > 1 and $opt_attr['style'] = 'text-indent: '.(10*($level-1)).'px;';
|
||||
(in_array((string) $key, $selected, true)) && $opt_attr[] = 'selected';
|
||||
$input .= str_repeat("\t", $level);
|
||||
$opt_attr['value'] = ($current_obj->get_config('prep_value', true) && empty($attributes['dont_prep'])) ?
|
||||
$current_obj->prep_value($opt_attr['value']) : $opt_attr['value'];
|
||||
$val = ($current_obj->get_config('prep_value', true) && empty($attributes['dont_prep'])) ?
|
||||
$current_obj->prep_value($val) : $val;
|
||||
$input .= html_tag('option', $opt_attr, $val).PHP_EOL;
|
||||
}
|
||||
}
|
||||
unset($attributes['dont_prep']);
|
||||
|
||||
return $input;
|
||||
};
|
||||
|
||||
// generate the select options list
|
||||
$input = $listoptions($options, $selected).str_repeat("\t", 0);
|
||||
|
||||
if (empty($attributes['id']) && $this->get_config('auto_id', false) == true)
|
||||
{
|
||||
$attributes['id'] = $this->get_config('auto_id_prefix', '').$attributes['name'];
|
||||
}
|
||||
|
||||
// if it's a multiselect, make sure the name is an array
|
||||
if (isset($attributes['multiple']) and substr($attributes['name'], -2) != '[]')
|
||||
{
|
||||
$attributes['name'] .= '[]';
|
||||
}
|
||||
|
||||
return html_tag('select', $this->attr_to_string($attributes), $input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a label field
|
||||
*
|
||||
* @param string|array $label either fieldname or full attributes array (when array other params are ignored)
|
||||
* @param string $id
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
*/
|
||||
public function label($label, $id = null, array $attributes = array())
|
||||
{
|
||||
if (is_array($label))
|
||||
{
|
||||
$attributes = $label;
|
||||
$label = $attributes['label'];
|
||||
isset($attributes['id']) and $id = $attributes['id'];
|
||||
}
|
||||
|
||||
if (empty($attributes['for']) and ! empty($id))
|
||||
{
|
||||
if ($this->get_config('auto_id', false) == true)
|
||||
{
|
||||
$attributes['for'] = $this->get_config('auto_id_prefix', 'form_').$id;
|
||||
}
|
||||
else
|
||||
{
|
||||
$attributes['for'] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
unset($attributes['label']);
|
||||
|
||||
return html_tag('label', $attributes, \Lang::get($label, array(), false) ?: $label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prep Value
|
||||
*
|
||||
* Prepares the value for display in the form
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public function prep_value($value)
|
||||
{
|
||||
$value = \Security::htmlentities($value, ENT_QUOTES);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attr to String
|
||||
*
|
||||
* Wraps the global attributes function and does some form specific work
|
||||
*
|
||||
* @param array $attr
|
||||
* @return string
|
||||
*/
|
||||
protected function attr_to_string($attr)
|
||||
{
|
||||
unset($attr['label']);
|
||||
return array_to_attr($attr);
|
||||
}
|
||||
|
||||
// fieldset related methods
|
||||
|
||||
/**
|
||||
* Returns the related fieldset
|
||||
*
|
||||
* @return Fieldset
|
||||
*/
|
||||
public function fieldset()
|
||||
{
|
||||
return $this->fieldset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build & template individual field
|
||||
*
|
||||
* @param string|Fieldset_Field $field field instance or name of a field in this form's fieldset
|
||||
* @return string
|
||||
* @deprecated until v1.2
|
||||
*/
|
||||
public function build_field($field)
|
||||
{
|
||||
! $field instanceof Fieldset_Field && $field = $this->field($field);
|
||||
|
||||
return $field->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a CSRF token and a validation rule to check it
|
||||
*/
|
||||
public function add_csrf()
|
||||
{
|
||||
$this->add(\Config::get('security.csrf_token_key', 'fuel_csrf_token'), 'CSRF Token')
|
||||
->set_type('hidden')
|
||||
->set_value(\Security::fetch_token())
|
||||
->add_rule(array('Security', 'check_token'));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a config value on the fieldset
|
||||
*
|
||||
* @param string $config
|
||||
* @param mixed $value
|
||||
* @return Fieldset this, to allow chaining
|
||||
*/
|
||||
public function set_config($config, $value = null)
|
||||
{
|
||||
$this->fieldset->set_config($config, $value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single or multiple config values by key
|
||||
*
|
||||
* @param string|array $key a single key or multiple in an array, empty to fetch all
|
||||
* @param mixed $default default output when config wasn't set
|
||||
* @return mixed|array a single config value or multiple in an array when $key input was an array
|
||||
*/
|
||||
public function get_config($key = null, $default = null)
|
||||
{
|
||||
if ($key === null)
|
||||
{
|
||||
return $this->fieldset->get_config();
|
||||
}
|
||||
|
||||
if (is_array($key))
|
||||
{
|
||||
$output = array();
|
||||
foreach ($key as $k)
|
||||
{
|
||||
$output[$k] = $this->fieldset->get_config($k, null) !== null
|
||||
? $this->fieldset->get_config($k, $default)
|
||||
: \Config::get('form.'.$k, $default);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
return $this->fieldset->get_config($key, null) !== null
|
||||
? $this->fieldset->get_config($key, $default)
|
||||
: \Config::get('form.'.$key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->fieldset->build()
|
||||
*
|
||||
* @param mixed $action
|
||||
* @return string
|
||||
*/
|
||||
public function build($action = null)
|
||||
{
|
||||
return $this->fieldset()->build($action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->fieldset->add()
|
||||
*
|
||||
* @param string
|
||||
* @param string
|
||||
* @param array
|
||||
* @param array
|
||||
* @return Fieldset_Field
|
||||
*/
|
||||
public function add($name, $label = '', array $attributes = array(), array $rules = array())
|
||||
{
|
||||
return $this->fieldset->add($name, $label, $attributes, $rules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->fieldset->add_model()
|
||||
*
|
||||
* @param string|Object $class either a full classname (including full namespace) or object instance
|
||||
* @param array|Object $instance array or object that has the exactly same named properties to populate the fields
|
||||
* @param string $method method name to call on model for field fetching
|
||||
* @return Validation this, to allow chaining
|
||||
*/
|
||||
public function add_model($class, $instance = null, $method = 'set_form_fields')
|
||||
{
|
||||
$this->fieldset->add_model($class, $instance, $method);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->fieldset->field()
|
||||
*
|
||||
* @param string|null $name field name or null to fetch an array of all
|
||||
* @param bool $flatten whether to get the fields array or flattened array
|
||||
* @return Fieldset_Field|false
|
||||
*/
|
||||
public function field($name = null, $flatten = false)
|
||||
{
|
||||
return $this->fieldset->field($name, $flatten);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->fieldset->populate() for this fieldset
|
||||
*
|
||||
* @param array|object $input
|
||||
* @param bool $repopulate
|
||||
* @return Fieldset
|
||||
*/
|
||||
public function populate($input, $repopulate = false)
|
||||
{
|
||||
$this->fieldset->populate($input, $repopulate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for $this->fieldset->repopulate() for this fieldset
|
||||
*
|
||||
* @return Fieldset_Field
|
||||
*/
|
||||
public function repopulate()
|
||||
{
|
||||
$this->fieldset->repopulate();
|
||||
}
|
||||
}
|
@ -1,639 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Format class
|
||||
*
|
||||
* Help convert between various formats such as XML, JSON, CSV, etc.
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
* @author Fuel Development Team
|
||||
* @copyright 2010 - 2012 Fuel Development Team
|
||||
* @link http://docs.fuelphp.com/classes/format.html
|
||||
*/
|
||||
class Format
|
||||
{
|
||||
/**
|
||||
* Returns an instance of the Format object.
|
||||
*
|
||||
* echo Format::forge(array('foo' => 'bar'))->to_xml();
|
||||
*
|
||||
* @param mixed $data general date to be converted
|
||||
* @param string $from_type data format the file was provided in
|
||||
* @param mixed $param additional parameter that can be passed on to a 'from' method
|
||||
* @return Format
|
||||
*/
|
||||
public static function forge($data = null, $from_type = null, $param = null)
|
||||
{
|
||||
return new static($data, $from_type, $param);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array|mixed input to convert
|
||||
*/
|
||||
protected $_data = array();
|
||||
|
||||
/**
|
||||
* @var bool whether to ignore namespaces when parsing xml
|
||||
*/
|
||||
protected $ignore_namespaces = true;
|
||||
|
||||
/**
|
||||
* Do not use this directly, call forge()
|
||||
*
|
||||
* @param mixed $data general date to be converted
|
||||
* @param string $from_type data format the file was provided in
|
||||
* @param mixed $param additional parameter that can be passed on to a 'from' method
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public function __construct($data = null, $from_type = null, $param = null)
|
||||
{
|
||||
// If the provided data is already formatted we should probably convert it to an array
|
||||
if ($from_type !== null)
|
||||
{
|
||||
|
||||
if ($from_type == 'xml:ns')
|
||||
{
|
||||
$this->ignore_namespaces = false;
|
||||
$from_type = 'xml';
|
||||
}
|
||||
|
||||
if (method_exists($this, '_from_' . $from_type))
|
||||
{
|
||||
$data = call_user_func_array(array($this, '_from_' . $from_type), array($data, $param));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
throw new \FuelException('Format class does not support conversion from "' . $from_type . '".');
|
||||
}
|
||||
}
|
||||
|
||||
$this->_data = $data;
|
||||
}
|
||||
|
||||
// FORMATING OUTPUT ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* To array conversion
|
||||
*
|
||||
* Goes through the input and makes sure everything is either a scalar value or array
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return array
|
||||
*/
|
||||
public function to_array($data = null)
|
||||
{
|
||||
if ($data === null)
|
||||
{
|
||||
$data = $this->_data;
|
||||
}
|
||||
|
||||
$array = array();
|
||||
|
||||
if (is_object($data) and ! $data instanceof \Iterator)
|
||||
{
|
||||
$data = get_object_vars($data);
|
||||
}
|
||||
|
||||
if (empty($data))
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value)
|
||||
{
|
||||
if (is_object($value) or is_array($value))
|
||||
{
|
||||
$array[$key] = $this->to_array($value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$array[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* To XML conversion
|
||||
*
|
||||
* @param mixed $data
|
||||
* @param null $structure
|
||||
* @param null|string $basenode
|
||||
* @param null|bool $use_cdata whether to use CDATA in nodes
|
||||
* @param mixed $bool_representation if true, element values are true/false. if 1, 1/0.
|
||||
* @return string
|
||||
*/
|
||||
public function to_xml($data = null, $structure = null, $basenode = null, $use_cdata = null, $bool_representation = null)
|
||||
{
|
||||
if ($data == null)
|
||||
{
|
||||
$data = $this->_data;
|
||||
}
|
||||
|
||||
is_null($basenode) and $basenode = \Config::get('format.xml.basenode', 'xml');
|
||||
is_null($use_cdata) and $use_cdata = \Config::get('format.xml.use_cdata', false);
|
||||
is_null($bool_representation) and $bool_representation = \Config::get('format.xml.bool_representation', null);
|
||||
|
||||
// turn off compatibility mode as simple xml throws a wobbly if you don't.
|
||||
if (ini_get('zend.ze1_compatibility_mode') == 1)
|
||||
{
|
||||
ini_set('zend.ze1_compatibility_mode', 0);
|
||||
}
|
||||
|
||||
if ($structure == null)
|
||||
{
|
||||
$structure = simplexml_load_string("<?xml version='1.0' encoding='utf-8'?><$basenode />");
|
||||
}
|
||||
|
||||
// Force it to be something useful
|
||||
if ( ! is_array($data) and ! is_object($data))
|
||||
{
|
||||
$data = (array) $data;
|
||||
}
|
||||
|
||||
foreach ($data as $key => $value)
|
||||
{
|
||||
// replace anything not alpha numeric
|
||||
$key = preg_replace('/[^a-z_\-0-9]/i', '', $key);
|
||||
|
||||
// no numeric keys in our xml please!
|
||||
if (is_numeric($key))
|
||||
{
|
||||
// make string key...
|
||||
$key = (\Inflector::singularize($basenode) != $basenode) ? \Inflector::singularize($basenode) : 'item';
|
||||
}
|
||||
|
||||
// if there is another array found recrusively call this function
|
||||
if (is_array($value) or is_object($value))
|
||||
{
|
||||
$node = $structure->addChild($key);
|
||||
|
||||
// recursive call if value is not empty
|
||||
if( ! empty($value))
|
||||
{
|
||||
$this->to_xml($value, $node, $key, $use_cdata, $bool_representation);
|
||||
}
|
||||
}
|
||||
elseif ($bool_representation and is_bool($value))
|
||||
{
|
||||
if ($bool_representation === true)
|
||||
{
|
||||
$bool = $value ? 'true' : 'false';
|
||||
}
|
||||
else
|
||||
{
|
||||
$bool = $value ? '1' : '0';
|
||||
}
|
||||
$structure->addChild($key, $bool);
|
||||
}
|
||||
else
|
||||
{
|
||||
// add single node.
|
||||
$encoded = htmlspecialchars(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), ENT_QUOTES, "UTF-8");
|
||||
|
||||
if ($use_cdata and ($encoded !== (string) $value))
|
||||
{
|
||||
$dom = dom_import_simplexml($structure->addChild($key));
|
||||
$owner = $dom->ownerDocument;
|
||||
$dom->appendChild($owner->createCDATASection($value));
|
||||
}
|
||||
else
|
||||
{
|
||||
$structure->addChild($key, $encoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pass back as string. or simple xml object if you want!
|
||||
return $structure->asXML();
|
||||
}
|
||||
|
||||
/**
|
||||
* To CSV conversion
|
||||
*
|
||||
* @param mixed $data
|
||||
* @param mixed $delimiter
|
||||
* @param mixed $enclose_numbers
|
||||
* @param array $headings Custom headings to use
|
||||
* @return string
|
||||
*/
|
||||
public function to_csv($data = null, $delimiter = null, $enclose_numbers = null, array $headings = array())
|
||||
{
|
||||
// csv format settings
|
||||
$newline = \Config::get('format.csv.newline', \Config::get('format.csv.export.newline', "\n"));
|
||||
$delimiter or $delimiter = \Config::get('format.csv.delimiter', \Config::get('format.csv.export.delimiter', ','));
|
||||
$enclosure = \Config::get('format.csv.enclosure', \Config::get('format.csv.export.enclosure', '"'));
|
||||
$escape = \Config::get('format.csv.escape', \Config::get('format.csv.export.escape', '\\'));
|
||||
is_null($enclose_numbers) and $enclose_numbers = \Config::get('format.csv.enclose_numbers', true);
|
||||
|
||||
// escape, delimit and enclose function
|
||||
$escaper = function($items, $enclose_numbers) use($enclosure, $escape, $delimiter) {
|
||||
return implode($delimiter, array_map(function($item) use($enclosure, $escape, $delimiter, $enclose_numbers) {
|
||||
if ( ! is_numeric($item) or $enclose_numbers)
|
||||
{
|
||||
$item = $enclosure.str_replace($enclosure, $escape.$enclosure, $item).$enclosure;
|
||||
}
|
||||
return $item;
|
||||
}, $items));
|
||||
};
|
||||
|
||||
if ($data === null)
|
||||
{
|
||||
$data = $this->_data;
|
||||
}
|
||||
|
||||
if (is_object($data) and ! $data instanceof \Iterator)
|
||||
{
|
||||
$data = $this->to_array($data);
|
||||
}
|
||||
|
||||
// Multi-dimensional array
|
||||
if (empty($headings))
|
||||
{
|
||||
if (is_array($data) and \Arr::is_multi($data))
|
||||
{
|
||||
$data = array_values($data);
|
||||
|
||||
if (\Arr::is_assoc($data[0]))
|
||||
{
|
||||
$headings = array_keys($data[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$headings = array_shift($data);
|
||||
}
|
||||
}
|
||||
// Single array
|
||||
else
|
||||
{
|
||||
$headings = array_keys((array) $data);
|
||||
$data = array($data);
|
||||
}
|
||||
}
|
||||
|
||||
$output = $escaper($headings, true).$newline;
|
||||
|
||||
foreach ($data as $row)
|
||||
{
|
||||
$output .= $escaper($row, $enclose_numbers).$newline;
|
||||
}
|
||||
|
||||
return rtrim($output, $newline);
|
||||
}
|
||||
|
||||
/**
|
||||
* To JSON conversion
|
||||
*
|
||||
* @param mixed $data
|
||||
* @param bool $pretty whether to make the json pretty
|
||||
* @return string
|
||||
*/
|
||||
public function to_json($data = null, $pretty = false)
|
||||
{
|
||||
if ($data === null)
|
||||
{
|
||||
$data = $this->_data;
|
||||
}
|
||||
|
||||
// To allow exporting ArrayAccess objects like Orm\Model instances they need to be
|
||||
// converted to an array first
|
||||
$data = (is_array($data) or is_object($data)) ? $this->to_array($data) : $data;
|
||||
return $pretty ? static::pretty_json($data) : json_encode($data, \Config::get('format.json.encode.options', JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP));
|
||||
}
|
||||
|
||||
/**
|
||||
* To JSONP conversion
|
||||
*
|
||||
* @param mixed $data
|
||||
* @param bool $pretty whether to make the json pretty
|
||||
* @param string $callback JSONP callback
|
||||
* @return string formatted JSONP
|
||||
*/
|
||||
public function to_jsonp($data = null, $pretty = false, $callback = null)
|
||||
{
|
||||
$callback or $callback = \Input::param('callback');
|
||||
is_null($callback) and $callback = 'response';
|
||||
|
||||
return $callback.'('.$this->to_json($data, $pretty).')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
*/
|
||||
public function to_serialized($data = null)
|
||||
{
|
||||
if ($data === null)
|
||||
{
|
||||
$data = $this->_data;
|
||||
}
|
||||
|
||||
return serialize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return as a string representing the PHP structure
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
*/
|
||||
public function to_php($data = null)
|
||||
{
|
||||
if ($data === null)
|
||||
{
|
||||
$data = $this->_data;
|
||||
}
|
||||
|
||||
return var_export($data, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to YAML
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
*/
|
||||
public function to_yaml($data = null)
|
||||
{
|
||||
if ($data == null)
|
||||
{
|
||||
$data = $this->_data;
|
||||
}
|
||||
|
||||
if ( ! function_exists('spyc_load'))
|
||||
{
|
||||
import('spyc/spyc', 'vendor');
|
||||
}
|
||||
|
||||
return \Spyc::YAMLDump($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import XML data
|
||||
*
|
||||
* @param string $string
|
||||
* @param bool $recursive
|
||||
* @return array
|
||||
*/
|
||||
protected function _from_xml($string, $recursive = false)
|
||||
{
|
||||
// If it forged with 'xml:ns'
|
||||
if ( ! $this->ignore_namespaces)
|
||||
{
|
||||
static $escape_keys = array();
|
||||
$recursive or $escape_keys = array('_xmlns' => 'xmlns');
|
||||
|
||||
if ( ! $recursive and strpos($string, 'xmlns') !== false and preg_match_all('/(\<.+?\>)/s', $string, $matches))
|
||||
{
|
||||
foreach ($matches[1] as $tag)
|
||||
{
|
||||
$escaped_tag = $tag;
|
||||
|
||||
strpos($tag, 'xmlns=') !== false and $escaped_tag = str_replace('xmlns=', '_xmlns=', $tag);
|
||||
|
||||
if (preg_match_all('/[\s\<\/]([^\/\s\'"]*?:\S*?)[=\/\>\s]/s', $escaped_tag, $xmlns))
|
||||
{
|
||||
foreach ($xmlns[1] as $ns)
|
||||
{
|
||||
$escaped = \Arr::search($escape_keys, $ns);
|
||||
$escaped or $escape_keys[$escaped = str_replace(':', '_', $ns)] = $ns;
|
||||
$string = str_replace($tag, $escaped_tag = str_replace($ns, $escaped, $escaped_tag), $string);
|
||||
$tag = $escaped_tag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$_arr = is_string($string) ? simplexml_load_string($string, 'SimpleXMLElement', LIBXML_NOCDATA) : $string;
|
||||
|
||||
// Convert all objects SimpleXMLElement to array recursively
|
||||
$arr = array();
|
||||
foreach ((array) $_arr as $key => $val)
|
||||
{
|
||||
$this->ignore_namespaces or $key = \Arr::get($escape_keys, $key, $key);
|
||||
if ( ! $val instanceOf \SimpleXMLElement or $val->count() or $val->attributes())
|
||||
{
|
||||
$arr[$key] = (is_array($val) or is_object($val)) ? $this->_from_xml($val, true) : $val;
|
||||
}
|
||||
else
|
||||
{
|
||||
$arr[$val->getName()] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import YAML data
|
||||
*
|
||||
* @param string $string
|
||||
* @return array
|
||||
*/
|
||||
protected function _from_yaml($string)
|
||||
{
|
||||
if ( ! function_exists('spyc_load'))
|
||||
{
|
||||
import('spyc/spyc', 'vendor');
|
||||
}
|
||||
|
||||
return \Spyc::YAMLLoadString($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import CSV data
|
||||
*
|
||||
* @param string $string
|
||||
* @param bool $no_headings
|
||||
* @return array
|
||||
*/
|
||||
protected function _from_csv($string, $no_headings = false)
|
||||
{
|
||||
$data = array();
|
||||
|
||||
// csv config
|
||||
$newline = \Config::get('format.csv.regex_newline', "\n");
|
||||
$delimiter = \Config::get('format.csv.delimiter', \Config::get('format.csv.import.delimiter', ','));
|
||||
$escape = \Config::get('format.csv.escape', \Config::get('format.csv.import.escape', '"'));
|
||||
// have to do this in two steps, empty string is a valid value for enclosure!
|
||||
$enclosure = \Config::get('format.csv.enclosure', \Config::get('format.csv.import.enclosure', null));
|
||||
$enclosure === null and $enclosure = '"';
|
||||
|
||||
if (empty($enclosure))
|
||||
{
|
||||
$rows = preg_split('/(['.$newline.'])/m', trim($string), -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
else
|
||||
{
|
||||
$rows = preg_split('/(?<=[0-9'.preg_quote($enclosure).'])'.$newline.'/', trim($string));
|
||||
}
|
||||
|
||||
// Get the headings
|
||||
if ($no_headings !== false)
|
||||
{
|
||||
$headings = str_replace($escape.$enclosure, $enclosure, str_getcsv(array_shift($rows), $delimiter, $enclosure, $escape));
|
||||
$headcount = count($headings);
|
||||
}
|
||||
|
||||
// Process the rows
|
||||
$incomplete = '';
|
||||
foreach ($rows as $row)
|
||||
{
|
||||
// process the row
|
||||
$data_fields = str_replace($escape.$enclosure, $enclosure, str_getcsv($incomplete.($incomplete ? $newline : '').$row, $delimiter, $enclosure, $escape));
|
||||
|
||||
// if we didn't have headers, the first row determines the number of fields
|
||||
if ( ! isset($headcount))
|
||||
{
|
||||
$headcount = count($data_fields);
|
||||
}
|
||||
|
||||
// finish the row if the have the correct field count, otherwise add the data to the next row
|
||||
if (count($data_fields) == $headcount)
|
||||
{
|
||||
$data[] = $no_headings === false ? $data_fields : array_combine($headings, $data_fields);
|
||||
$incomplete = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$incomplete = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import JSON data
|
||||
*
|
||||
* @param string $string
|
||||
* @return mixed
|
||||
*/
|
||||
private function _from_json($string)
|
||||
{
|
||||
return json_decode(trim($string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Import Serialized data
|
||||
*
|
||||
* @param string $string
|
||||
* @return mixed
|
||||
*/
|
||||
private function _from_serialize($string)
|
||||
{
|
||||
return unserialize(trim($string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes json pretty the json output.
|
||||
* Borrowed from http://www.php.net/manual/en/function.json-encode.php#80339
|
||||
*
|
||||
* @param string $data json encoded array
|
||||
* @return string|false pretty json output or false when the input was not valid
|
||||
*/
|
||||
protected static function pretty_json($data)
|
||||
{
|
||||
$json = json_encode($data, \Config::get('format.json.encode.options', JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP));
|
||||
|
||||
if ( ! $json)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$tab = "\t";
|
||||
$newline = "\n";
|
||||
$new_json = "";
|
||||
$indent_level = 0;
|
||||
$in_string = false;
|
||||
$len = strlen($json);
|
||||
|
||||
for ($c = 0; $c < $len; $c++)
|
||||
{
|
||||
$char = $json[$c];
|
||||
switch($char)
|
||||
{
|
||||
case '{':
|
||||
case '[':
|
||||
if ( ! $in_string)
|
||||
{
|
||||
$new_json .= $char.$newline.str_repeat($tab, $indent_level+1);
|
||||
$indent_level++;
|
||||
}
|
||||
else
|
||||
{
|
||||
$new_json .= $char;
|
||||
}
|
||||
break;
|
||||
case '}':
|
||||
case ']':
|
||||
if ( ! $in_string)
|
||||
{
|
||||
$indent_level--;
|
||||
$new_json .= $newline.str_repeat($tab, $indent_level).$char;
|
||||
}
|
||||
else
|
||||
{
|
||||
$new_json .= $char;
|
||||
}
|
||||
break;
|
||||
case ',':
|
||||
if ( ! $in_string)
|
||||
{
|
||||
$new_json .= ','.$newline.str_repeat($tab, $indent_level);
|
||||
}
|
||||
else
|
||||
{
|
||||
$new_json .= $char;
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
if ( ! $in_string)
|
||||
{
|
||||
$new_json .= ': ';
|
||||
}
|
||||
else
|
||||
{
|
||||
$new_json .= $char;
|
||||
}
|
||||
break;
|
||||
case '"':
|
||||
if ($c > 0 and $json[$c-1] !== '\\')
|
||||
{
|
||||
$in_string = ! $in_string;
|
||||
}
|
||||
default:
|
||||
$new_json .= $char;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $new_json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Format config.
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
\Config::load('format', true);
|
||||
}
|
||||
}
|
@ -1,651 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class FtpConnectionException extends \FuelException {}
|
||||
|
||||
class FtpFileAccessException extends \FuelException {}
|
||||
|
||||
/**
|
||||
* FTP Class
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
* @author Phil Sturgeon
|
||||
* @link http://docs.fuelphp.com/classes/ftp.html
|
||||
*/
|
||||
class Ftp
|
||||
{
|
||||
public static $initialized = false;
|
||||
|
||||
protected $_hostname = 'localhost';
|
||||
protected $_username = '';
|
||||
protected $_password = '';
|
||||
protected $_port = 21;
|
||||
protected $_timeout = 90;
|
||||
protected $_passive = true;
|
||||
protected $_debug = false;
|
||||
protected $_conn_id = false;
|
||||
|
||||
/**
|
||||
* Returns a new Ftp object. If you do not define the "file" parameter,
|
||||
*
|
||||
* $ftp = static::forge('group');
|
||||
*
|
||||
* @param string|array $config The name of the config group to use, or a configuration array.
|
||||
* @param bool $connect Automatically connect to this server.
|
||||
* @return Ftp
|
||||
*/
|
||||
public static function forge($config = 'default', $connect = true)
|
||||
{
|
||||
$ftp = new static($config);
|
||||
|
||||
// Unless told not to, connect automatically
|
||||
$connect === true and $ftp->connect();
|
||||
|
||||
return $ftp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial Ftp filename and local data.
|
||||
*
|
||||
* @param string|array $config The name of the config group to use, or a configuration array.
|
||||
*/
|
||||
public function __construct($config = 'default')
|
||||
{
|
||||
\Config::load('ftp', true);
|
||||
|
||||
// If it is a string we're looking at a predefined config group
|
||||
if (is_string($config))
|
||||
{
|
||||
$config_arr = \Config::get('ftp.'.$config);
|
||||
|
||||
// Check that it exists
|
||||
if ( ! is_array($config_arr) or $config_arr === array())
|
||||
{
|
||||
throw new \UnexpectedValueException('You have specified an invalid ftp connection group: '.$config);
|
||||
}
|
||||
|
||||
$config = $config_arr;
|
||||
}
|
||||
|
||||
// Prep the hostname
|
||||
$this->_hostname = preg_replace('|.+?://|', '', $config['hostname']);
|
||||
$this->_username = $config['username'];
|
||||
$this->_password = $config['password'];
|
||||
$this->_timeout = ! empty($config['timeout']) ? (int) $config['timeout'] : 90;
|
||||
$this->_port = ! empty($config['port']) ? (int) $config['port'] : 21;
|
||||
$this->_passive = (bool) $config['passive'];
|
||||
$this->_ssl_mode = (bool) $config['ssl_mode'];
|
||||
$this->_debug = (bool) $config['debug'];
|
||||
|
||||
static::$initialized = true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* FTP Connect
|
||||
*
|
||||
* @return \Ftp
|
||||
* @throws \FtpConnectionException
|
||||
*/
|
||||
public function connect()
|
||||
{
|
||||
if($this->_ssl_mode === true)
|
||||
{
|
||||
if( ! function_exists('ftp_ssl_connect'))
|
||||
{
|
||||
throw new \RuntimeException('ftp_ssl_connect() function is missing.');
|
||||
}
|
||||
|
||||
$this->_conn_id = @ftp_ssl_connect($this->_hostname, $this->_port, $this->_timeout);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$this->_conn_id = @ftp_connect($this->_hostname, $this->_port, $this->_timeout);
|
||||
}
|
||||
|
||||
if ($this->_conn_id === false)
|
||||
{
|
||||
if ($this->_debug == true)
|
||||
{
|
||||
throw new \FtpConnectionException('Unable to establish a connection');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $this->_login())
|
||||
{
|
||||
if ($this->_debug == true)
|
||||
{
|
||||
throw new \FtpConnectionException('Unable to login');
|
||||
}
|
||||
}
|
||||
|
||||
// Set passive mode if needed
|
||||
if ($this->_passive == true)
|
||||
{
|
||||
ftp_pasv($this->_conn_id, true);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* FTP Login
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function _login()
|
||||
{
|
||||
return @ftp_login($this->_conn_id, $this->_username, $this->_password);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Validates the connection ID
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function _is_conn()
|
||||
{
|
||||
if ( ! is_resource($this->_conn_id))
|
||||
{
|
||||
if ($this->_debug == true)
|
||||
{
|
||||
throw new \InvalidArgumentException('Invalid connection');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Change directory
|
||||
*
|
||||
* The second parameter lets us momentarily turn off debugging so that
|
||||
* this function can be used to test for the existence of a folder
|
||||
* without throwing an error. There's no FTP equivalent to is_dir()
|
||||
* so we do it by trying to change to a particular directory.
|
||||
* Internally, this parameter is only used by the "mirror" function below.
|
||||
*
|
||||
* @param string $path
|
||||
* @return bool
|
||||
* @throws \FtpFileAccessException
|
||||
*/
|
||||
public function change_dir($path = '')
|
||||
{
|
||||
if ($path == '' or ! $this->_is_conn())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = @ftp_chdir($this->_conn_id, $path);
|
||||
|
||||
if ($result === false)
|
||||
{
|
||||
if ($this->_debug == true)
|
||||
{
|
||||
throw new \FtpFileAccessException('Unable to change the directory');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create a directory
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $permissions
|
||||
* @return bool
|
||||
* @throws \FtpFileAccessException
|
||||
*/
|
||||
public function mkdir($path, $permissions = null)
|
||||
{
|
||||
if ( ! $this->_is_conn())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = ftp_mkdir($this->_conn_id, $path);
|
||||
|
||||
if ($result === false)
|
||||
{
|
||||
if ($this->_debug == true)
|
||||
{
|
||||
throw new \FtpFileAccessException('Unable to create directory');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set file permissions if needed
|
||||
if ($permissions !== null)
|
||||
{
|
||||
$this->chmod($path, (int) $permissions);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Upload a file to the server
|
||||
*
|
||||
* @param string $local_path
|
||||
* @param string $remote_path
|
||||
* @param string $mode
|
||||
* @param string $permissions
|
||||
* @return bool
|
||||
* @throws \FtpFileAccessException
|
||||
*/
|
||||
public function upload($local_path, $remote_path, $mode = 'auto', $permissions = null)
|
||||
{
|
||||
if ( ! $this->_is_conn())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! is_file($local_path))
|
||||
{
|
||||
throw new \FtpFileAccessException('No source file');
|
||||
}
|
||||
|
||||
// Set the mode if not specified
|
||||
if ($mode == 'auto')
|
||||
{
|
||||
// Get the file extension so we can set the upload type
|
||||
$ext = pathinfo($local_path, PATHINFO_EXTENSION);
|
||||
$mode = $this->_settype($ext);
|
||||
}
|
||||
|
||||
$mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
|
||||
|
||||
$result = @ftp_put($this->_conn_id, $remote_path, $local_path, $mode);
|
||||
|
||||
if ($result === false)
|
||||
{
|
||||
if ($this->_debug == true)
|
||||
{
|
||||
throw new \FtpFileAccessException('Unable to upload');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set file permissions if needed
|
||||
if ($permissions !== null)
|
||||
{
|
||||
$this->chmod($remote_path, (int) $permissions);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Download a file from a remote server to the local server
|
||||
*
|
||||
* @param string $remote_path
|
||||
* @param string $local_path
|
||||
* @param string $mode
|
||||
* @return bool
|
||||
* @throws \FtpFileAccessException
|
||||
*/
|
||||
public function download($remote_path, $local_path, $mode = 'auto')
|
||||
{
|
||||
if ( ! $this->_is_conn())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the mode if not specified
|
||||
if ($mode == 'auto')
|
||||
{
|
||||
// Get the file extension so we can set the upload type
|
||||
$ext = pathinfo($remote_path, PATHINFO_BASENAME);
|
||||
$mode = $this->_settype($ext);
|
||||
}
|
||||
|
||||
$mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
|
||||
|
||||
$result = @ftp_get($this->_conn_id, $local_path, $remote_path, $mode);
|
||||
|
||||
if ($result === false)
|
||||
{
|
||||
if ($this->_debug === true)
|
||||
{
|
||||
throw new \FtpFileAccessException('Unable to download');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Rename (or move) a file
|
||||
*
|
||||
* @param $old_file string
|
||||
* @param $new_file string
|
||||
* @param $move bool
|
||||
* @return bool
|
||||
* @throws \FtpFileAccessException
|
||||
*/
|
||||
public function rename($old_file, $new_file, $move = false)
|
||||
{
|
||||
if ( ! $this->_is_conn())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = @ftp_rename($this->_conn_id, $old_file, $new_file);
|
||||
|
||||
if ($result === false)
|
||||
{
|
||||
if ($this->_debug == true)
|
||||
{
|
||||
$msg = ($move == false) ? 'Unable to rename' : 'Unable to move';
|
||||
|
||||
throw new \FtpFileAccessException($msg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Move a file
|
||||
*
|
||||
* @param string $old_file
|
||||
* @param string $new_file
|
||||
* @return bool
|
||||
*/
|
||||
public function move($old_file, $new_file)
|
||||
{
|
||||
return $this->rename($old_file, $new_file, true);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Rename (or move) a file
|
||||
*
|
||||
* @param string $filepath
|
||||
* @return bool
|
||||
* @throws \FtpFileAccessException
|
||||
*/
|
||||
function delete_file($filepath)
|
||||
{
|
||||
if ( ! $this->_is_conn())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = @ftp_delete($this->_conn_id, $filepath);
|
||||
|
||||
if ($result === false)
|
||||
{
|
||||
if ($this->_debug == true)
|
||||
{
|
||||
throw new \FtpFileAccessException('Unable to delete');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Delete a folder and recursively delete everything (including sub-folders)
|
||||
* contained within it.
|
||||
*
|
||||
* @param string $filepath
|
||||
* @return bool
|
||||
* @throws \FtpFileAccessException
|
||||
*/
|
||||
function delete_dir($filepath)
|
||||
{
|
||||
if ( ! $this->_is_conn())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add a trailing slash to the file path if needed
|
||||
$filepath = preg_replace("/(.+?)\/*$/", "\\1/", $filepath);
|
||||
|
||||
$list = $this->list_files($filepath);
|
||||
|
||||
if ($list !== false and count($list) > 0)
|
||||
{
|
||||
foreach ($list as $item)
|
||||
{
|
||||
// If we can't delete the item it's probaly a folder so
|
||||
// we'll recursively call delete_dir()
|
||||
if ( ! @ftp_delete($this->_conn_id, $item))
|
||||
{
|
||||
// don't recurse into current of parent directory
|
||||
if ( ! preg_match('/\/\.\.|\/\.$/', $item))
|
||||
{
|
||||
$this->delete_dir($item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = @ftp_rmdir($this->_conn_id, $filepath);
|
||||
|
||||
if ($result === false)
|
||||
{
|
||||
if ($this->_debug == true)
|
||||
{
|
||||
throw new \FtpFileAccessException('Unable to delete');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Set file permissions
|
||||
*
|
||||
* @param string $path the file path
|
||||
* @param string $permissions the permissions
|
||||
* @return bool
|
||||
* @throws \FtpFileAccessException
|
||||
*/
|
||||
public function chmod($path, $permissions)
|
||||
{
|
||||
if ( ! $this->_is_conn())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Permissions can only be set when running PHP 5
|
||||
if ( ! function_exists('ftp_chmod'))
|
||||
{
|
||||
if ($this->_debug == true)
|
||||
{
|
||||
throw new \FtpFileAccessException('CHMOD function does not exist');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = @ftp_chmod($this->_conn_id, $permissions, $path);
|
||||
|
||||
if ($result === false)
|
||||
{
|
||||
if ($this->_debug == true)
|
||||
{
|
||||
throw new \FtpFileAccessException('Unable to CHMOD');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* FTP List files in the specified directory
|
||||
*
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
public function list_files($path = '.')
|
||||
{
|
||||
if ( ! $this->_is_conn())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ftp_nlist($this->_conn_id, $path);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Read a directory and recreate it remotely
|
||||
*
|
||||
* This function recursively reads a folder and everything it contains (including
|
||||
* sub-folders) and creates a mirror via FTP based on it. Whatever the directory structure
|
||||
* of the original file path will be recreated on the server.
|
||||
*
|
||||
* @param string $local_path path to source with trailing slash
|
||||
* @param string $remote_path path to destination - include the base folder with trailing slash
|
||||
* @return bool
|
||||
*/
|
||||
public function mirror($local_path, $remote_path)
|
||||
{
|
||||
if ( ! $this->_is_conn())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open the local file path
|
||||
if ($fp = @opendir($local_path))
|
||||
{
|
||||
// Attempt to open the remote file path.
|
||||
if ( ! $this->change_dir($remote_path, true))
|
||||
{
|
||||
// If it doesn't exist we'll attempt to create the directory
|
||||
if ( ! $this->mkdir($remote_path) or ! $this->change_dir($remote_path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively read the local directory
|
||||
while (false !== ($file = readdir($fp)))
|
||||
{
|
||||
if (@is_dir($local_path.$file) and substr($file, 0, 1) != '.')
|
||||
{
|
||||
$this->mirror($local_path.$file."/", $remote_path.$file."/");
|
||||
}
|
||||
elseif (substr($file, 0, 1) != ".")
|
||||
{
|
||||
// Get the file extension so we can se the upload type
|
||||
$ext = pathinfo($file, PATHINFO_EXTENSION);
|
||||
$mode = $this->_settype($ext);
|
||||
|
||||
$this->upload($local_path.$file, $remote_path.$file, $mode);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Set the upload type
|
||||
*
|
||||
* @param string $ext
|
||||
* @return string
|
||||
*/
|
||||
protected function _settype($ext)
|
||||
{
|
||||
$text_types = array(
|
||||
'txt',
|
||||
'text',
|
||||
'php',
|
||||
'phps',
|
||||
'php4',
|
||||
'js',
|
||||
'css',
|
||||
'htm',
|
||||
'html',
|
||||
'phtml',
|
||||
'shtml',
|
||||
'log',
|
||||
'xml',
|
||||
);
|
||||
|
||||
return in_array($ext, $text_types) ? 'ascii' : 'binary';
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Close the connection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if ( ! $this->_is_conn())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ftp_close($this->_conn_id);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Close the connection when the class is unset
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
|
||||
}
|
@ -1,391 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* General Fuel Exception class
|
||||
*/
|
||||
class FuelException extends \Exception {}
|
||||
|
||||
/**
|
||||
* The core of the framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @subpackage Core
|
||||
*/
|
||||
class Fuel
|
||||
{
|
||||
/**
|
||||
* @var string The version of Fuel
|
||||
*/
|
||||
const VERSION = '1.8';
|
||||
|
||||
/**
|
||||
* @var string constant used for when in testing mode
|
||||
*/
|
||||
const TEST = 'test';
|
||||
|
||||
/**
|
||||
* @var string constant used for when in development
|
||||
*/
|
||||
const DEVELOPMENT = 'development';
|
||||
|
||||
/**
|
||||
* @var string constant used for when in production
|
||||
*/
|
||||
const PRODUCTION = 'production';
|
||||
|
||||
/**
|
||||
* @var string constant used for when testing the app in a staging env.
|
||||
*/
|
||||
const STAGING = 'staging';
|
||||
|
||||
/**
|
||||
* @var int No logging
|
||||
*/
|
||||
const L_NONE = 0;
|
||||
|
||||
/**
|
||||
* @var int Log everything
|
||||
*/
|
||||
const L_ALL = 99;
|
||||
|
||||
/**
|
||||
* @var int Log debug massages and below
|
||||
*/
|
||||
const L_DEBUG = 100;
|
||||
|
||||
/**
|
||||
* @var int Log info massages and below
|
||||
*/
|
||||
const L_INFO = 200;
|
||||
|
||||
/**
|
||||
* @var int Log warning massages and below
|
||||
*/
|
||||
const L_WARNING = 300;
|
||||
|
||||
/**
|
||||
* @var int Log errors only
|
||||
*/
|
||||
const L_ERROR = 400;
|
||||
|
||||
/**
|
||||
* @var bool Whether Fuel has been initialized
|
||||
*/
|
||||
public static $initialized = false;
|
||||
|
||||
/**
|
||||
* @var string The Fuel environment
|
||||
*/
|
||||
public static $env = \Fuel::DEVELOPMENT;
|
||||
|
||||
/**
|
||||
* @var bool Whether to display the profiling information
|
||||
*/
|
||||
public static $profiling = false;
|
||||
|
||||
public static $locale = 'en_US';
|
||||
|
||||
public static $timezone = 'UTC';
|
||||
|
||||
public static $encoding = 'UTF-8';
|
||||
|
||||
public static $is_cli = false;
|
||||
|
||||
public static $is_test = false;
|
||||
|
||||
public static $volatile_paths = array();
|
||||
|
||||
protected static $_paths = array();
|
||||
|
||||
protected static $packages = array();
|
||||
|
||||
final private function __construct() { }
|
||||
|
||||
/**
|
||||
* Initializes the framework. This can only be called once.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function init($config)
|
||||
{
|
||||
if (static::$initialized)
|
||||
{
|
||||
throw new \FuelException("You can't initialize Fuel more than once.");
|
||||
}
|
||||
|
||||
static::$_paths = array(APPPATH, COREPATH);
|
||||
|
||||
// Is Fuel running on the command line?
|
||||
static::$is_cli = (bool) defined('STDIN');
|
||||
|
||||
\Config::load($config);
|
||||
|
||||
// Disable output compression if the client doesn't support it
|
||||
if (static::$is_cli or ! in_array('gzip', explode(', ', \Input::headers('Accept-Encoding', ''))))
|
||||
{
|
||||
\Config::set('ob_callback', null);
|
||||
}
|
||||
|
||||
// Start up output buffering
|
||||
static::$is_cli or ob_start(\Config::get('ob_callback'));
|
||||
|
||||
if (\Config::get('caching', false))
|
||||
{
|
||||
\Finder::instance()->read_cache('FuelFileFinder');
|
||||
}
|
||||
|
||||
static::$profiling = \Config::get('profiling', false);
|
||||
static::$profiling and \Profiler::init();
|
||||
|
||||
// set a default timezone if one is defined
|
||||
try
|
||||
{
|
||||
static::$timezone = \Config::get('default_timezone') ?: date_default_timezone_get();
|
||||
date_default_timezone_set(static::$timezone);
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
date_default_timezone_set('UTC');
|
||||
throw new \PHPErrorException($e->getMessage());
|
||||
}
|
||||
|
||||
static::$encoding = \Config::get('encoding', static::$encoding);
|
||||
MBSTRING and mb_internal_encoding(static::$encoding);
|
||||
|
||||
static::$locale = \Config::get('locale', static::$locale);
|
||||
|
||||
// Set locale, log warning when it fails
|
||||
if (static::$locale)
|
||||
{
|
||||
setlocale(LC_ALL, static::$locale) or
|
||||
logger(\Fuel::L_WARNING, 'The configured locale '.static::$locale.' is not installed on your system.', __METHOD__);
|
||||
}
|
||||
|
||||
if ( ! static::$is_cli)
|
||||
{
|
||||
if (\Config::get('base_url') === null)
|
||||
{
|
||||
\Config::set('base_url', static::generate_base_url());
|
||||
}
|
||||
}
|
||||
|
||||
// Run Input Filtering
|
||||
\Security::clean_input();
|
||||
|
||||
\Event::register('fuel-shutdown', 'Fuel::finish');
|
||||
|
||||
// Always load classes, config & language set in always_load.php config
|
||||
static::always_load();
|
||||
|
||||
// Load in the routes
|
||||
\Config::load('routes', true);
|
||||
\Router::add(\Config::get('routes'));
|
||||
|
||||
// BC FIX FOR APPLICATIONS <= 1.6.1, makes Redis_Db available as Redis,
|
||||
// like it was in versions before 1.7
|
||||
class_exists('Redis', false) or class_alias('Redis_Db', 'Redis');
|
||||
|
||||
// BC FIX FOR PHP < 7.0 to make the error class available
|
||||
if (PHP_VERSION_ID < 70000)
|
||||
{
|
||||
// alias the error class to the new errorhandler
|
||||
class_alias('\Fuel\Core\Errorhandler', '\Fuel\Core\Error');
|
||||
|
||||
// does the app have an overloaded Error class?
|
||||
if (class_exists('Error'))
|
||||
{
|
||||
// then alias that too
|
||||
class_alias('Error', 'Errorhandler');
|
||||
}
|
||||
}
|
||||
|
||||
static::$initialized = true;
|
||||
|
||||
// fire any app created events
|
||||
\Event::instance()->has_events('app_created') and \Event::instance()->trigger('app_created', '', 'none');
|
||||
|
||||
if (static::$profiling)
|
||||
{
|
||||
\Profiler::mark(__METHOD__.' End');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up Fuel execution, ends the output buffering, and outputs the
|
||||
* buffer contents.
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public static function finish()
|
||||
{
|
||||
if (\Config::get('caching', false))
|
||||
{
|
||||
\Finder::instance()->write_cache('FuelFileFinder');
|
||||
}
|
||||
|
||||
if (static::$profiling and ! static::$is_cli and ! \Input::is_ajax())
|
||||
{
|
||||
// Grab the output buffer and flush it, we will rebuffer later
|
||||
$output = ob_get_clean();
|
||||
|
||||
$headers = headers_list();
|
||||
$show = true;
|
||||
|
||||
foreach ($headers as $header)
|
||||
{
|
||||
if (stripos($header, 'content-type') === 0 and stripos($header, 'text/html') === false)
|
||||
{
|
||||
$show = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($show)
|
||||
{
|
||||
\Profiler::mark('End of Fuel Execution');
|
||||
if (preg_match("|</body>.*?</html>|is", $output))
|
||||
{
|
||||
$output = preg_replace("|</body>.*?</html>|is", '', $output);
|
||||
$output .= \Profiler::output();
|
||||
$output .= '</body></html>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$output .= \Profiler::output();
|
||||
}
|
||||
}
|
||||
// Restart the output buffer and send the new output
|
||||
ob_start(\Config::get('ob_callback'));
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a base url.
|
||||
*
|
||||
* @return string the base url
|
||||
*/
|
||||
protected static function generate_base_url()
|
||||
{
|
||||
$base_url = '';
|
||||
if(\Input::server('http_host'))
|
||||
{
|
||||
$base_url .= \Input::protocol().'://'.\Input::server('http_host');
|
||||
}
|
||||
if (\Input::server('script_name'))
|
||||
{
|
||||
$common = get_common_path(array(\Input::server('request_uri'), \Input::server('script_name')));
|
||||
$base_url .= $common;
|
||||
}
|
||||
|
||||
// Add a slash if it is missing and return it
|
||||
return rtrim($base_url, '/').'/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes the given file and returns the results.
|
||||
*
|
||||
* @param string the path to the file
|
||||
* @return mixed the results of the include
|
||||
*/
|
||||
public static function load($file)
|
||||
{
|
||||
return include $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always load packages, modules, classes, config & language files set in always_load.php config
|
||||
*
|
||||
* @param array what to autoload
|
||||
*/
|
||||
public static function always_load($array = null)
|
||||
{
|
||||
is_null($array) and $array = \Config::get('always_load', array());
|
||||
|
||||
isset($array['packages']) and \Package::load($array['packages']);
|
||||
|
||||
isset($array['modules']) and \Module::load($array['modules']);
|
||||
|
||||
if (isset($array['classes']))
|
||||
{
|
||||
foreach ($array['classes'] as $class)
|
||||
{
|
||||
if ( ! class_exists($class = \Str::ucwords($class)))
|
||||
{
|
||||
throw new \FuelException('Class '.$class.' defined in your "always_load" config could not be loaded.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Config and Lang must be either just the filename, example: array(filename)
|
||||
* or the filename as key and the group as value, example: array(filename => some_group)
|
||||
*/
|
||||
|
||||
if (isset($array['config']))
|
||||
{
|
||||
foreach ($array['config'] as $config => $config_group)
|
||||
{
|
||||
\Config::load((is_int($config) ? $config_group : $config), (is_int($config) ? true : $config_group));
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($array['language']))
|
||||
{
|
||||
foreach ($array['language'] as $lang => $lang_group)
|
||||
{
|
||||
\Lang::load((is_int($lang) ? $lang_group : $lang), (is_int($lang) ? true : $lang_group));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a value and checks if it is a Closure or not, if it is it
|
||||
* will return the result of the closure, if not, it will simply return the
|
||||
* value.
|
||||
*
|
||||
* @param mixed $var The value to get
|
||||
* @return mixed
|
||||
*/
|
||||
public static function value($var)
|
||||
{
|
||||
return ($var instanceof \Closure) ? $var() : $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans a file path so that it does not contain absolute file paths.
|
||||
*
|
||||
* @param string the filepath
|
||||
* @return string the clean path
|
||||
*/
|
||||
public static function clean_path($path)
|
||||
{
|
||||
// framework default paths
|
||||
static $search = array('\\', APPPATH, COREPATH, PKGPATH, DOCROOT);
|
||||
static $replace = array('/', 'APPPATH/', 'COREPATH/', 'PKGPATH/', 'DOCROOT/');
|
||||
|
||||
// additional paths configured than need cleaning
|
||||
$extra = \Config::get('security.clean_paths', array());
|
||||
foreach ($extra as $r => $s)
|
||||
{
|
||||
$search[] = $s;
|
||||
$replace[] = $r.'/';
|
||||
}
|
||||
|
||||
// clean up and return it
|
||||
return str_ireplace($search, $replace, $path);
|
||||
}
|
||||
}
|
@ -1,289 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Html Class
|
||||
*
|
||||
* @package Fuel
|
||||
* @subpackage Core
|
||||
* @category Core
|
||||
* @author Alfredo Rivera
|
||||
* @link http://docs.fuelphp.com/classes/html.html
|
||||
*/
|
||||
class Html
|
||||
{
|
||||
public static $doctypes = null;
|
||||
public static $html5 = true;
|
||||
|
||||
/**
|
||||
* Creates an html link
|
||||
*
|
||||
* @param string $href the url
|
||||
* @param string $text the text value
|
||||
* @param array $attr the attributes array
|
||||
* @param bool $secure true to force https, false to force http
|
||||
* @return string the html link
|
||||
*/
|
||||
public static function anchor($href, $text = null, $attr = array(), $secure = null)
|
||||
{
|
||||
if ( ! preg_match('#^(\w+://|javascript:|\#)# i', $href))
|
||||
{
|
||||
$urlparts = explode('?', $href, 2);
|
||||
$href = \Uri::create($urlparts[0], array(), isset($urlparts[1]) ? $urlparts[1] : array(), $secure);
|
||||
}
|
||||
elseif ( ! preg_match('#^(javascript:|\#)# i', $href) and is_bool($secure))
|
||||
{
|
||||
$href = http_build_url($href, array('scheme' => $secure ? 'https' : 'http'));
|
||||
|
||||
// Trim the trailing slash
|
||||
$href = rtrim($href, '/');
|
||||
}
|
||||
|
||||
// Create and display a URL hyperlink
|
||||
is_null($text) and $text = $href;
|
||||
|
||||
$attr['href'] = $href;
|
||||
|
||||
return html_tag('a', $attr, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an html image tag
|
||||
*
|
||||
* Sets the alt attribute to filename of it is not supplied.
|
||||
*
|
||||
* @param string $src the source
|
||||
* @param array $attr the attributes array
|
||||
* @return string the image tag
|
||||
*/
|
||||
public static function img($src, $attr = array())
|
||||
{
|
||||
if ( ! preg_match('#^(\w+://)# i', $src))
|
||||
{
|
||||
$src = \Uri::base(false).$src;
|
||||
}
|
||||
$attr['src'] = $src;
|
||||
$attr['alt'] = (isset($attr['alt'])) ? $attr['alt'] : pathinfo($src, PATHINFO_FILENAME);
|
||||
return html_tag('img', $attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given schema to the given URL if it is not already there.
|
||||
*
|
||||
* @param string $url the url
|
||||
* @param string $schema the schema
|
||||
* @return string url with schema
|
||||
*/
|
||||
public static function prep_url($url, $schema = 'http')
|
||||
{
|
||||
if ( ! preg_match('#^(\w+://|javascript:)# i', $url))
|
||||
{
|
||||
$url = $schema.'://'.$url;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mailto link.
|
||||
*
|
||||
* @param string $email The email address
|
||||
* @param string $text The text value
|
||||
* @param string $subject The subject
|
||||
* @param array $attr attributes for the tag
|
||||
* @return string The mailto link
|
||||
*/
|
||||
public static function mail_to($email, $text = null, $subject = null, $attr = array())
|
||||
{
|
||||
$text or $text = $email;
|
||||
|
||||
$subject and $subject = '?subject='.$subject;
|
||||
|
||||
return html_tag('a', array(
|
||||
'href' => 'mailto:'.$email.$subject,
|
||||
) + $attr, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mailto link with Javascript to prevent bots from picking up the
|
||||
* email address.
|
||||
*
|
||||
* @param string $email the email address
|
||||
* @param string $text the text value
|
||||
* @param string $subject the subject
|
||||
* @param array $attr attributes for the tag
|
||||
* @return string the javascript code containing email
|
||||
*/
|
||||
public static function mail_to_safe($email, $text = null, $subject = null, $attr = array())
|
||||
{
|
||||
$text or $text = str_replace('@', '[at]', $email);
|
||||
|
||||
$email = explode("@", $email);
|
||||
|
||||
$subject and $subject = '?subject='.$subject;
|
||||
|
||||
$attr = array_to_attr($attr);
|
||||
$attr = ($attr == '' ? '' : ' ').$attr;
|
||||
|
||||
$output = '<script type="text/javascript">';
|
||||
$output .= '(function() {';
|
||||
$output .= 'var user = "'.$email[0].'";';
|
||||
$output .= 'var at = "@";';
|
||||
$output .= 'var server = "'.$email[1].'";';
|
||||
$output .= "document.write('<a href=\"' + 'mail' + 'to:' + user + at + server + '$subject\"$attr>$text</a>');";
|
||||
$output .= '})();';
|
||||
$output .= '</script>';
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a html meta tag
|
||||
*
|
||||
* @param string|array $name multiple inputs or name/http-equiv value
|
||||
* @param string $content content value
|
||||
* @param string $type name or http-equiv
|
||||
* @return string
|
||||
*/
|
||||
public static function meta($name = '', $content = '', $type = 'name')
|
||||
{
|
||||
if( ! is_array($name))
|
||||
{
|
||||
$result = html_tag('meta', array($type => $name, 'content' => $content));
|
||||
}
|
||||
elseif(is_array($name))
|
||||
{
|
||||
$result = "";
|
||||
foreach($name as $array)
|
||||
{
|
||||
$meta = $array;
|
||||
$result .= "\n" . html_tag('meta', $meta);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a html doctype tag
|
||||
*
|
||||
* @param string $type doctype declaration key from doctypes config
|
||||
* @return string
|
||||
*/
|
||||
public static function doctype($type = 'xhtml1-trans')
|
||||
{
|
||||
if(static::$doctypes === null)
|
||||
{
|
||||
\Config::load('doctypes', true);
|
||||
static::$doctypes = \Config::get('doctypes', array());
|
||||
}
|
||||
|
||||
if(is_array(static::$doctypes) and isset(static::$doctypes[$type]))
|
||||
{
|
||||
if($type == "html5")
|
||||
{
|
||||
static::$html5 = true;
|
||||
}
|
||||
return static::$doctypes[$type];
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a html5 audio tag
|
||||
* It is required that you set html5 as the doctype to use this method
|
||||
*
|
||||
* @param string|array $src one or multiple audio sources
|
||||
* @param array $attr tag attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function audio($src = '', $attr = false)
|
||||
{
|
||||
if(static::$html5)
|
||||
{
|
||||
if(is_array($src))
|
||||
{
|
||||
$source = '';
|
||||
foreach($src as $item)
|
||||
{
|
||||
$source .= html_tag('source', array('src' => $item));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$source = html_tag('source', array('src' => $src));
|
||||
}
|
||||
return html_tag('audio', $attr, $source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a html un-ordered list tag
|
||||
*
|
||||
* @param array $list list items, may be nested
|
||||
* @param array|string $attr outer list attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function ul(array $list = array(), $attr = false)
|
||||
{
|
||||
return static::build_list('ul', $list, $attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a html ordered list tag
|
||||
*
|
||||
* @param array $list list items, may be nested
|
||||
* @param array|string $attr outer list attributes
|
||||
* @return string
|
||||
*/
|
||||
public static function ol(array $list = array(), $attr = false)
|
||||
{
|
||||
return static::build_list('ol', $list, $attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the html for the list methods
|
||||
*
|
||||
* @param string $type list type (ol or ul)
|
||||
* @param array $list list items, may be nested
|
||||
* @param array $attr tag attributes
|
||||
* @param string $indent indentation
|
||||
* @return string
|
||||
*/
|
||||
protected static function build_list($type = 'ul', array $list = array(), $attr = false, $indent = '')
|
||||
{
|
||||
if ( ! is_array($list))
|
||||
{
|
||||
$result = false;
|
||||
}
|
||||
|
||||
$out = '';
|
||||
foreach ($list as $key => $val)
|
||||
{
|
||||
if ( ! is_array($val))
|
||||
{
|
||||
$out .= $indent."\t".html_tag('li', array(), $val).PHP_EOL;
|
||||
}
|
||||
else
|
||||
{
|
||||
$out .= $indent."\t".html_tag('li', array(), $key.PHP_EOL.static::build_list($type, $val, '', $indent."\t\t").$indent."\t").PHP_EOL;
|
||||
}
|
||||
}
|
||||
$result = $indent.html_tag($type, $attr, PHP_EOL.$out.$indent).PHP_EOL;
|
||||
return $result;
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
abstract class HttpException extends \FuelException
|
||||
{
|
||||
/**
|
||||
* Must return a response object for the handle method
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
abstract protected function response();
|
||||
|
||||
/**
|
||||
* When this type of exception isn't caught this method is called by
|
||||
* Errorhandler::exception_handler() to deal with the problem.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// get the exception response
|
||||
$response = $this->response();
|
||||
|
||||
// fire any app shutdown events
|
||||
\Event::instance()->trigger('shutdown', '', 'none', true);
|
||||
|
||||
// fire any framework shutdown events
|
||||
\Event::instance()->trigger('fuel-shutdown', '', 'none', true);
|
||||
|
||||
// send the response out
|
||||
$response->send(true);
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class HttpBadRequestException extends HttpException
|
||||
{
|
||||
public function response()
|
||||
{
|
||||
return new \Response(\View::forge('400'), 400);
|
||||
}
|
||||
}
|
||||
|
||||
class HttpNoAccessException extends HttpException
|
||||
{
|
||||
public function response()
|
||||
{
|
||||
return new \Response(\View::forge('403'), 403);
|
||||
}
|
||||
}
|
||||
|
||||
class HttpNotFoundException extends HttpException
|
||||
{
|
||||
public function response()
|
||||
{
|
||||
return new \Response(\View::forge('404'), 404);
|
||||
}
|
||||
}
|
||||
|
||||
class HttpServerErrorException extends HttpException
|
||||
{
|
||||
public function response()
|
||||
{
|
||||
return new \Response(\View::forge('500'), 500);
|
||||
}
|
||||
}
|
@ -1,301 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Image
|
||||
{
|
||||
protected static $_instance = null;
|
||||
|
||||
/**
|
||||
* Holds the config until an instance is initiated.
|
||||
*
|
||||
* @var array Config options to be passed when the instance is created.
|
||||
*/
|
||||
protected static $_config = array();
|
||||
|
||||
/**
|
||||
* Initialize by loading config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
\Config::load('image', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance for static use of the class.
|
||||
*
|
||||
* @return Image_Driver
|
||||
*/
|
||||
protected static function instance()
|
||||
{
|
||||
if (static::$_instance == null)
|
||||
{
|
||||
static::$_instance = static::forge(static::$_config);
|
||||
}
|
||||
return static::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the image driver
|
||||
*
|
||||
* @param array $config
|
||||
* @param string $filename The file to load
|
||||
* @return Image_Driver
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public static function forge($config = array(), $filename = null)
|
||||
{
|
||||
!is_array($config) and $config = array();
|
||||
|
||||
$config = array_merge(\Config::get('image', array()), $config);
|
||||
|
||||
$protocol = ucfirst( ! empty($config['driver']) ? $config['driver'] : 'gd');
|
||||
$class = 'Image_'.$protocol;
|
||||
if ($protocol == 'Driver' || ! class_exists($class))
|
||||
{
|
||||
throw new \FuelException('Driver '.$protocol.' is not a valid driver for image manipulation.');
|
||||
}
|
||||
$return = new $class($config);
|
||||
if ($filename !== null)
|
||||
{
|
||||
$return->load($filename);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to set configuration options.
|
||||
*
|
||||
* Sending the config options through the static reference initializes the
|
||||
* instance. If you need to send a driver config through the static reference,
|
||||
* make sure its the first one sent! If errors arise, create a new instance using
|
||||
* forge().
|
||||
*
|
||||
* @param array $index An array of configuration settings.
|
||||
* @param mixed $value
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function config($index = array(), $value = null)
|
||||
{
|
||||
if (static::$_instance === null)
|
||||
{
|
||||
if ($value !== null)
|
||||
{
|
||||
$index = array($index => $value);
|
||||
}
|
||||
if (is_array($index))
|
||||
{
|
||||
static::$_config = array_merge(static::$_config, $index);
|
||||
}
|
||||
static::instance();
|
||||
return static::instance();
|
||||
} else {
|
||||
return static::instance()->config($index, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the image and checks if its compatible.
|
||||
*
|
||||
* @param string $filename The file to load
|
||||
* @param string $return_data Decides if it should return the images data, or just "$this".
|
||||
* @param mixed $force_extension Whether or not to force the image extension
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function load($filename, $return_data = false, $force_extension = false)
|
||||
{
|
||||
return static::instance()->load($filename, $return_data, $force_extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crops the image using coordinates or percentages.
|
||||
*
|
||||
* Absolute integer or percentages accepted for all 4.
|
||||
*
|
||||
* @param integer $x1 X-Coordinate based from the top-left corner.
|
||||
* @param integer $y1 Y-Coordinate based from the top-left corner.
|
||||
* @param integer $x2 X-Coordinate based from the bottom-right corner.
|
||||
* @param integer $y2 Y-Coordinate based from the bottom-right corner.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function crop($x1, $y1, $x2, $y2)
|
||||
{
|
||||
return static::instance()->crop($x1, $y1, $x2, $y2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize the image. If the width or height is null, it will resize retaining the original aspect ratio.
|
||||
*
|
||||
* @param integer $width The new width of the image.
|
||||
* @param integer $height The new height of the image.
|
||||
* @param boolean $keepar Defaults to true. If false, allows resizing without keeping AR.
|
||||
* @param boolean $pad If set to true and $keepar is true, it will pad the image with the configured bgcolor
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function resize($width, $height, $keepar = true, $pad = false)
|
||||
{
|
||||
return static::instance()->resize($width, $height, $keepar, $pad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize the image. If the width or height is null, it will resize retaining the original aspect ratio.
|
||||
*
|
||||
* @param integer $width The new width of the image.
|
||||
* @param integer $height The new height of the image.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function crop_resize($width, $height)
|
||||
{
|
||||
return static::instance()->crop_resize($width, $height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the image
|
||||
*
|
||||
* @param integer $degrees The degrees to rotate, negatives integers allowed.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function rotate($degrees)
|
||||
{
|
||||
return static::instance()->rotate($degrees);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a vertical / horizontal or both mirror image.
|
||||
*
|
||||
* @param string $direction 'vertical', 'horizontal', 'both'
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function flip($direction)
|
||||
{
|
||||
return static::instance()->flip($direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a watermark to the image.
|
||||
*
|
||||
* @param string $filename The filename of the watermark file to use.
|
||||
* @param string $position The position of the watermark, ex: "bottom right", "center center", "top left"
|
||||
* @param integer $padding The spacing between the edge of the image.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function watermark($filename, $position, $padding = 5)
|
||||
{
|
||||
return static::instance()->watermark($filename, $position, $padding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a border to the image.
|
||||
*
|
||||
* @param integer $size The side of the border, in pixels.
|
||||
* @param string $color A hexadecimal color.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function border($size, $color = null)
|
||||
{
|
||||
return static::instance()->border($size, $color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Masks the image using the alpha channel of the image input.
|
||||
*
|
||||
* @param string $maskimage The location of the image to use as the mask
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function mask($maskimage)
|
||||
{
|
||||
return static::instance()->mask($maskimage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds rounded corners to the image.
|
||||
*
|
||||
* @param integer $radius
|
||||
* @param integer $sides Accepts any combination of "tl tr bl br" separated by spaces, or null for all sides
|
||||
* @param integer $antialias Sets the anti-alias range.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function rounded($radius, $sides = null, $antialias = null)
|
||||
{
|
||||
return static::instance()->rounded($radius, $sides, $antialias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the image into a grayscale version
|
||||
*
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function grayscale()
|
||||
{
|
||||
return static::instance()->grayscale();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the image, and optionally attempts to set permissions
|
||||
*
|
||||
* @param string $filename The location where to save the image.
|
||||
* @param string $permissions Allows unix style permissions
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function save($filename = null, $permissions = null)
|
||||
{
|
||||
return static::instance()->save($filename, $permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the image, and optionally attempts to set permissions
|
||||
*
|
||||
* @param string $prepend The text to add to the beginning of the filename.
|
||||
* @param string $append The text to add to the end of the filename.
|
||||
* @param string $permissions Allows unix style permissions
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function save_pa($prepend, $append = null, $permissions = null)
|
||||
{
|
||||
return static::instance()->save_pa($prepend, $append, $permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the file directly to the user.
|
||||
*
|
||||
* @param string $filetype The extension type to use. Ex: png, jpg, bmp, gif
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function output($filetype = null)
|
||||
{
|
||||
return static::instance()->output($filetype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sizes for the currently loaded image, or the image given in the $filename.
|
||||
*
|
||||
* @param string $filename The location of the file to get sizes for.
|
||||
* @return object An object containing width and height variables.
|
||||
*/
|
||||
public static function sizes($filename = null)
|
||||
{
|
||||
return static::instance()->sizes($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the image.
|
||||
*
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public static function reload()
|
||||
{
|
||||
return static::instance()->reload();
|
||||
}
|
||||
}
|
@ -1,912 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
abstract class Image_Driver
|
||||
{
|
||||
protected $image_fullpath = null;
|
||||
protected $image_directory = null;
|
||||
protected $image_filename = null;
|
||||
protected $image_extension = null;
|
||||
protected $new_extension = null;
|
||||
protected $config = array();
|
||||
protected $queued_actions = array();
|
||||
protected $accepted_extensions;
|
||||
|
||||
/**
|
||||
* Initialize by loading config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
\Config::load('image', true);
|
||||
}
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
if (is_array($config))
|
||||
{
|
||||
$this->config = array_merge(\Config::get('image', array()), $config);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->config = \Config::get('image', array());
|
||||
}
|
||||
$this->debug("Image Class was initialized using the " . $this->config['driver'] . " driver.");
|
||||
}
|
||||
/**
|
||||
* Accepts configuration in either an array (as $index) or a pairing using $index and $value
|
||||
*
|
||||
* @param string $index The index to be set, or an array of configuration options.
|
||||
* @param mixed $value The value to be set if $index is not an array.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function config($index = null, $value = null)
|
||||
{
|
||||
if (is_array($index))
|
||||
{
|
||||
if (isset($index['driver']))
|
||||
{
|
||||
throw new \RuntimeException("The driver cannot be changed after initialization!");
|
||||
}
|
||||
$this->config = array_merge($this->config, $index);
|
||||
}
|
||||
elseif ($index != null)
|
||||
{
|
||||
if ($index == 'driver')
|
||||
{
|
||||
throw new \RuntimeException("The driver cannot be changed after initialization!");
|
||||
}
|
||||
$this->config[$index] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the presets set in the config. Additional parameters replace the $1, $2, ect.
|
||||
*
|
||||
* @param string $name The name of the preset.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function preset($name)
|
||||
{
|
||||
$vars = func_get_args();
|
||||
if (isset($this->config['presets'][$name]))
|
||||
{
|
||||
$old_config = $this->config;
|
||||
$this->config = array_merge($this->config, $this->config['presets'][$name]);
|
||||
foreach ($this->config['actions'] AS $action)
|
||||
{
|
||||
$func = $action[0];
|
||||
array_shift($action);
|
||||
for ($i = 0; $i < count($action); $i++)
|
||||
{
|
||||
for ($x = count($vars) - 1; $x >= 0; $x--)
|
||||
{
|
||||
$action[$i] = preg_replace('#\$' . $x . '#', $vars[$x], $action[$i]);
|
||||
}
|
||||
}
|
||||
call_fuel_func_array(array($this, $func), $action);
|
||||
}
|
||||
$this->config = $old_config;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \InvalidArgumentException("Could not load preset $name, you sure it exists?");
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the image and checks if its compatible.
|
||||
*
|
||||
* @param string $filename The file to load
|
||||
* @param string $return_data Decides if it should return the images data, or just "$this".
|
||||
* @param mixed $force_extension Decides if it should force the extension with this (or false)
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function load($filename, $return_data = false, $force_extension = false)
|
||||
{
|
||||
// First check if the filename exists
|
||||
$filename = realpath($filename);
|
||||
$return = array(
|
||||
'filename' => $filename,
|
||||
'return_data' => $return_data,
|
||||
);
|
||||
if (is_file($filename))
|
||||
{
|
||||
// Check the extension
|
||||
$ext = $this->check_extension($filename, false, $force_extension);
|
||||
if ($ext !== false)
|
||||
{
|
||||
$return = array_merge($return, array(
|
||||
'image_fullpath' => $filename,
|
||||
'image_directory' => dirname($filename),
|
||||
'image_filename' => basename($filename),
|
||||
'image_extension' => $ext,
|
||||
));
|
||||
if ( ! $return_data)
|
||||
{
|
||||
$this->image_fullpath = $filename;
|
||||
$this->image_directory = dirname($filename);
|
||||
$this->image_filename = basename($filename);
|
||||
$this->image_extension = $ext;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \RuntimeException("The library does not support this filetype for $filename.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \OutOfBoundsException("Image file $filename does not exist.");
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crops the image using coordinates or percentages.
|
||||
*
|
||||
* Positive whole numbers or percentages are coordinates from the top left.
|
||||
*
|
||||
* Negative whole numbers or percentages are coordinates from the bottom right.
|
||||
*
|
||||
* @param integer $x1 X-Coordinate for first set.
|
||||
* @param integer $y1 Y-Coordinate for first set.
|
||||
* @param integer $x2 X-Coordinate for second set.
|
||||
* @param integer $y2 Y-Coordinate for second set.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function crop($x1, $y1, $x2, $y2)
|
||||
{
|
||||
$this->queue('crop', $x1, $y1, $x2, $y2);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the crop event when the queue is ran.
|
||||
*
|
||||
* Formats the crop method input for use with driver specific methods
|
||||
*
|
||||
* @param integer $x1 X-Coordinate for first set.
|
||||
* @param integer $y1 Y-Coordinate for first set.
|
||||
* @param integer $x2 X-Coordinate for second set.
|
||||
* @param integer $y2 Y-Coordinate for second set.
|
||||
* @return array An array of variables for the specific driver.
|
||||
*/
|
||||
protected function _crop($x1, $y1, $x2, $y2)
|
||||
{
|
||||
$y1 === null and $y1 = $x1;
|
||||
$x2 === null and $x2 = "-" . $x1;
|
||||
$y2 === null and $y2 = "-" . $y1;
|
||||
|
||||
$x1 = $this->convert_number($x1, true);
|
||||
$y1 = $this->convert_number($y1, false);
|
||||
$x2 = $this->convert_number($x2, true);
|
||||
$y2 = $this->convert_number($y2, false);
|
||||
|
||||
return array(
|
||||
'x1' => $x1,
|
||||
'y1' => $y1,
|
||||
'x2' => $x2,
|
||||
'y2' => $y2,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize the image. If the width or height is null, it will resize retaining the original aspect ratio.
|
||||
*
|
||||
* @param integer $width The new width of the image.
|
||||
* @param integer $height The new height of the image.
|
||||
* @param boolean $keepar If false, allows stretching of the image.
|
||||
* @param boolean $pad Adds padding to the image when resizing.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function resize($width, $height = null, $keepar = true, $pad = false)
|
||||
{
|
||||
$this->queue('resize', $width, $height, $keepar, $pad);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a vertical / horizontal or both mirror image.
|
||||
*
|
||||
* @param mixed $direction 'vertical', 'horizontal', 'both'
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function flip($direction)
|
||||
{
|
||||
$this->queue('flip', $direction);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the resize event when the queue is ran.
|
||||
*
|
||||
* Formats the resize method input for use with driver specific methods.
|
||||
*
|
||||
* @param integer $width The new width of the image.
|
||||
* @param integer $height The new height of the image.
|
||||
* @param boolean $keepar If false, allows stretching of the image.
|
||||
* @param boolean $pad Adds padding to the image when resizing.
|
||||
* @return array An array of variables for the specific driver.
|
||||
*/
|
||||
protected function _resize($width, $height = null, $keepar = true, $pad = true)
|
||||
{
|
||||
if ($height == null or $width == null)
|
||||
{
|
||||
if ($height == null and substr($width, -1) == '%')
|
||||
{
|
||||
$height = $width;
|
||||
}
|
||||
elseif (substr($height, -1) == '%' and $width == null)
|
||||
{
|
||||
$width = $height;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sizes = $this->sizes();
|
||||
if ($height == null and $width != null)
|
||||
{
|
||||
$height = $width * ($sizes->height / $sizes->width);
|
||||
}
|
||||
elseif ($height != null and $width == null)
|
||||
{
|
||||
$width = $height * ($sizes->width / $sizes->height);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \InvalidArgumentException("Width and height cannot be null.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$origwidth = $this->convert_number($width, true);
|
||||
$origheight = $this->convert_number($height, false);
|
||||
$width = $origwidth;
|
||||
$height = $origheight;
|
||||
$sizes = $this->sizes();
|
||||
$x = 0;
|
||||
$y = 0;
|
||||
if ($keepar)
|
||||
{
|
||||
// See which is the biggest ratio
|
||||
if (function_exists('bcdiv'))
|
||||
{
|
||||
$width_ratio = bcdiv($width, $sizes->width, 10);
|
||||
$height_ratio = bcdiv($height, $sizes->height, 10);
|
||||
$compare = bccomp($width_ratio, $height_ratio, 10);
|
||||
if ($compare > -1)
|
||||
{
|
||||
$height = ceil((float) bcmul($sizes->height, $height_ratio, 10));
|
||||
$width = ceil((float) bcmul($sizes->width, $height_ratio, 10));
|
||||
}
|
||||
else
|
||||
{
|
||||
$height = ceil((float) bcmul($sizes->height, $width_ratio, 10));
|
||||
$width = ceil((float) bcmul($sizes->width, $width_ratio, 10));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$width_ratio = $width / $sizes->width;
|
||||
$height_ratio = $height / $sizes->height;
|
||||
if ($width_ratio >= $height_ratio)
|
||||
{
|
||||
$height = ceil($sizes->height * $height_ratio);
|
||||
$width = ceil($sizes->width * $height_ratio);
|
||||
}
|
||||
else
|
||||
{
|
||||
$height = ceil($sizes->height * $width_ratio);
|
||||
$width = ceil($sizes->width * $width_ratio);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($pad)
|
||||
{
|
||||
$x = floor(($origwidth - $width) / 2);
|
||||
$y = floor(($origheight - $height) / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
$origwidth = $width;
|
||||
$origheight = $height;
|
||||
}
|
||||
|
||||
return array(
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'cwidth' => $origwidth,
|
||||
'cheight' => $origheight,
|
||||
'x' => $x,
|
||||
'y' => $y,
|
||||
);
|
||||
}
|
||||
|
||||
public function crop_resize($width, $height = null)
|
||||
{
|
||||
is_null($height) and $height = $width;
|
||||
$this->queue('crop_resize', $width, $height);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function _crop_resize($width, $height)
|
||||
{
|
||||
// Determine the crop size
|
||||
$sizes = $this->sizes();
|
||||
$width = $this->convert_number($width, true);
|
||||
$height = $this->convert_number($height, false);
|
||||
|
||||
if (function_exists('bcdiv'))
|
||||
{
|
||||
if (bccomp(bcdiv($sizes->width, $width, 10), bcdiv($sizes->height, $height, 10), 10) < 1)
|
||||
{
|
||||
$this->_resize($width, 0, true, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_resize(0, $height, true, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($sizes->width / $width < $sizes->height / $height)
|
||||
{
|
||||
$this->_resize($width, 0, true, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_resize(0, $height, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
$sizes = $this->sizes();
|
||||
$y = floor(max(0, $sizes->height - $height) / 2);
|
||||
$x = floor(max(0, $sizes->width - $width) / 2);
|
||||
$this->_crop($x, $y, $x + $width, $y + $height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the image
|
||||
*
|
||||
* @param integer $degrees The degrees to rotate, negatives integers allowed.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function rotate($degrees)
|
||||
{
|
||||
$this->queue('rotate', $degrees);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the rotate event when the queue is ran.
|
||||
*
|
||||
* Formats the rotate method input for use with driver specific methods
|
||||
*
|
||||
* @param integer $degrees The degrees to rotate, negatives integers allowed.
|
||||
* @return array An array of variables for the specific driver.
|
||||
*/
|
||||
protected function _rotate($degrees)
|
||||
{
|
||||
$degrees %= 360;
|
||||
if ($degrees < 0)
|
||||
{
|
||||
$degrees = 360 + $degrees;
|
||||
}
|
||||
return array(
|
||||
'degrees' => $degrees,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a watermark to the image.
|
||||
*
|
||||
* @param string $filename The filename of the watermark file to use.
|
||||
* @param string $position The position of the watermark, ex: "bottom right", "center center", "top left"
|
||||
* @param integer $padding The amount of padding (in pixels) from the position.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function watermark($filename, $position, $padding = 5)
|
||||
{
|
||||
$this->queue('watermark', $filename, $position, $padding);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the watermark event when the queue is ran.
|
||||
*
|
||||
* Formats the watermark method input for use with driver specific methods
|
||||
*
|
||||
* @param string $filename The filename of the watermark file to use.
|
||||
* @param string $position The position of the watermark, ex: "bottom right", "center center", "top left"
|
||||
* @param integer $padding The amount of padding (in pixels) from the position.
|
||||
* @return array An array of variables for the specific driver.
|
||||
*/
|
||||
protected function _watermark($filename, $position, $padding = 5)
|
||||
{
|
||||
$filename = realpath($filename);
|
||||
$return = false;
|
||||
if (is_file($filename) and $this->check_extension($filename, false))
|
||||
{
|
||||
$x = 0;
|
||||
$y = 0;
|
||||
$wsizes = $this->sizes($filename);
|
||||
$sizes = $this->sizes();
|
||||
// Get the x and y positions.
|
||||
list($ypos, $xpos) = explode(' ', $position);
|
||||
switch ($xpos)
|
||||
{
|
||||
case 'left':
|
||||
$x = $padding;
|
||||
break;
|
||||
case 'middle':
|
||||
case 'center':
|
||||
$x = ($sizes->width / 2) - ($wsizes->width / 2);
|
||||
break;
|
||||
case 'right':
|
||||
$x = $sizes->width - $wsizes->width - $padding;
|
||||
break;
|
||||
}
|
||||
switch ($ypos)
|
||||
{
|
||||
case 'top':
|
||||
$y = $padding;
|
||||
break;
|
||||
case 'middle':
|
||||
case 'center':
|
||||
$y = ($sizes->height / 2) - ($wsizes->height / 2);
|
||||
break;
|
||||
case 'bottom':
|
||||
$y = $sizes->height - $wsizes->height - $padding;
|
||||
break;
|
||||
}
|
||||
$this->debug("Watermark being placed at $x,$y");
|
||||
$return = array(
|
||||
'filename' => $filename,
|
||||
'x' => $x,
|
||||
'y' => $y,
|
||||
'padding' => $padding,
|
||||
);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a border to the image.
|
||||
*
|
||||
* @param integer $size The side of the border, in pixels.
|
||||
* @param string $color A hexadecimal color.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function border($size, $color = null)
|
||||
{
|
||||
$this->queue('border', $size, $color);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the border event when the queue is ran.
|
||||
*
|
||||
* Formats the border method input for use with driver specific methods
|
||||
*
|
||||
* @param integer $size The side of the border, in pixels.
|
||||
* @param string $color A hexadecimal color.
|
||||
* @return array An array of variables for the specific driver.
|
||||
*/
|
||||
protected function _border($size, $color = null)
|
||||
{
|
||||
empty($color) and $color = $this->config['bgcolor'];
|
||||
|
||||
return array(
|
||||
'size' => $size,
|
||||
'color' => $color,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Masks the image using the alpha channel of the image input.
|
||||
*
|
||||
* @param string $maskimage The location of the image to use as the mask
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function mask($maskimage)
|
||||
{
|
||||
$this->queue('mask', $maskimage);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the mask event when the queue is ran.
|
||||
*
|
||||
* Formats the mask method input for use with driver specific methods
|
||||
*
|
||||
* @param string $maskimage The location of the image to use as the mask
|
||||
* @return array An array of variables for the specific driver.
|
||||
*/
|
||||
protected function _mask($maskimage)
|
||||
{
|
||||
return array(
|
||||
'maskimage' => $maskimage,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds rounded corners to the image.
|
||||
*
|
||||
* @param integer $radius
|
||||
* @param integer $sides Accepts any combination of "tl tr bl br" separated by spaces, or null for all sides
|
||||
* @param integer $antialias Sets the anti-alias range.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function rounded($radius, $sides = null, $antialias = null)
|
||||
{
|
||||
$this->queue('rounded', $radius, $sides, $antialias);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the rounded event when the queue is ran.
|
||||
*
|
||||
* Formats the rounded method input for use with driver specific methods
|
||||
*
|
||||
* @param integer $radius
|
||||
* @param integer $sides Accepts any combination of "tl tr bl br" separated by spaces, or null for all sides
|
||||
* @param integer $antialias Sets the anti-alias range.
|
||||
* @return array An array of variables for the specific driver.
|
||||
*/
|
||||
protected function _rounded($radius, $sides, $antialias)
|
||||
{
|
||||
$radius < 0 and $radius = 0;
|
||||
$tl = $tr = $bl = $br = $sides == null;
|
||||
|
||||
if ($sides != null)
|
||||
{
|
||||
$sides = explode(' ', $sides);
|
||||
foreach ($sides as $side)
|
||||
{
|
||||
if ($side == 'tl' or $side == 'tr' or $side == 'bl' or $side == 'br')
|
||||
{
|
||||
$$side = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$antialias == null and $antialias = 1;
|
||||
|
||||
return array(
|
||||
'radius' => $radius,
|
||||
'tl' => $tl,
|
||||
'tr' => $tr,
|
||||
'bl' => $bl,
|
||||
'br' => $br,
|
||||
'antialias' => $antialias,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the image into a grayscale version
|
||||
*
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function grayscale()
|
||||
{
|
||||
$this->queue('grayscale');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the grayscale event when the queue is ran.
|
||||
*/
|
||||
abstract protected function _grayscale();
|
||||
|
||||
/**
|
||||
* Saves the image, and optionally attempts to set permissions
|
||||
*
|
||||
* @param string $filename The location where to save the image.
|
||||
* @param string $permissions Allows unix style permissions
|
||||
* @return array
|
||||
*/
|
||||
public function save($filename = null, $permissions = null)
|
||||
{
|
||||
if (empty($filename))
|
||||
{
|
||||
$filename = $this->image_filename;
|
||||
}
|
||||
|
||||
$directory = dirname($filename);
|
||||
if ( ! is_dir($directory))
|
||||
{
|
||||
throw new \OutOfBoundsException("Could not find directory \"$directory\"");
|
||||
}
|
||||
|
||||
if ( ! $this->check_extension($filename, true))
|
||||
{
|
||||
$filename .= "." . $this->image_extension;
|
||||
}
|
||||
// Touch the file
|
||||
if ( ! touch($filename))
|
||||
{
|
||||
throw new \RuntimeException("Do not have permission to write to \"$filename\"");
|
||||
}
|
||||
|
||||
// Set the new permissions
|
||||
if ($permissions != null and ! chmod($filename, $permissions))
|
||||
{
|
||||
throw new \RuntimeException("Could not set permissions on the file.");
|
||||
}
|
||||
|
||||
$this->debug("", "Saving image as <code>$filename</code>");
|
||||
return array(
|
||||
'filename' => $filename,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the file in the original location, adding the append and prepend to the filename.
|
||||
*
|
||||
* @param string $append The string to append to the filename
|
||||
* @param string $prepend The string to prepend to the filename
|
||||
* @param string $extension The extension to save the image as, null defaults to the loaded images extension.
|
||||
* @param integer $permissions The permissions to attempt to set on the file.
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function save_pa($append, $prepend = null, $extension = null, $permissions = null)
|
||||
{
|
||||
$filename = substr($this->image_filename, 0, -(strlen($this->image_extension) + 1));
|
||||
$fullpath = $this->image_directory.'/'.$append.$filename.$prepend.'.'.
|
||||
($extension !== null ? $extension : $this->image_extension);
|
||||
$this->save($fullpath, $permissions);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the file directly to the user.
|
||||
*
|
||||
* @param string $filetype The extension type to use. Ex: png, jpg, gif
|
||||
* @return array
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public function output($filetype = null)
|
||||
{
|
||||
if ($filetype == null)
|
||||
{
|
||||
$filetype = $this->config['filetype'] == null ? $this->image_extension : $this->config['filetype'];
|
||||
}
|
||||
|
||||
if ($this->check_extension($filetype, false))
|
||||
{
|
||||
if ( ! $this->config['debug'])
|
||||
{
|
||||
$mimetype = $filetype === 'jpg' ? 'jpeg' : $filetype;
|
||||
header('Content-Type: image/' . $mimetype);
|
||||
}
|
||||
$this->new_extension = $filetype;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \FuelException("Image extension $filetype is unsupported.");
|
||||
}
|
||||
|
||||
$this->debug('', "Outputting image as $filetype");
|
||||
return array(
|
||||
'filetype' => $filetype,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sizes for the currently loaded image, or the image given in the $filename.
|
||||
*
|
||||
* @param string $filename The location of the file to get sizes for.
|
||||
* @return object An object containing width and height variables.
|
||||
*/
|
||||
abstract public function sizes($filename = null);
|
||||
|
||||
/**
|
||||
* Adds a background to the image using the 'bgcolor' config option.
|
||||
*/
|
||||
abstract protected function add_background();
|
||||
|
||||
/**
|
||||
* Creates a new color usable by all drivers.
|
||||
*
|
||||
* @param string $hex The hex code of the color
|
||||
* @return array rgba representation of the hex and alpha values.
|
||||
*/
|
||||
protected function create_hex_color($hex)
|
||||
{
|
||||
if ($hex == null)
|
||||
{
|
||||
$red = 0;
|
||||
$green = 0;
|
||||
$blue = 0;
|
||||
$alpha = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if theres a # in front
|
||||
if (substr($hex, 0, 1) == '#')
|
||||
{
|
||||
$hex = substr($hex, 1);
|
||||
}
|
||||
|
||||
// Break apart the hex
|
||||
if (strlen($hex) == 6 or strlen($hex) == 8)
|
||||
{
|
||||
$red = hexdec(substr($hex, 0, 2));
|
||||
$green = hexdec(substr($hex, 2, 2));
|
||||
$blue = hexdec(substr($hex, 4, 2));
|
||||
$alpha = (strlen($hex) == 8) ? hexdec(substr($hex, 6, 2)) : 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
$red = hexdec(substr($hex, 0, 1).substr($hex, 0, 1));
|
||||
$green = hexdec(substr($hex, 1, 1).substr($hex, 1, 1));
|
||||
$blue = hexdec(substr($hex, 2, 1).substr($hex, 2, 1));
|
||||
$alpha = (strlen($hex) > 3) ? hexdec(substr($hex, 3, 1).substr($hex, 3, 1)) : 255;
|
||||
}
|
||||
}
|
||||
|
||||
$alpha = floor($alpha / 2.55);
|
||||
|
||||
return array(
|
||||
'red' => $red,
|
||||
'green' => $green,
|
||||
'blue' => $blue,
|
||||
'alpha' => $alpha,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the extension is accepted by this library, and if its valid sets the $this->image_extension variable.
|
||||
*
|
||||
* @param string $filename
|
||||
* @param boolean $writevar Decides if the extension should be written to $this->image_extension
|
||||
* @param mixed $force_extension Decides if the extension should be overridden with this (or false)
|
||||
* @return boolean
|
||||
*/
|
||||
protected function check_extension($filename, $writevar = true, $force_extension = false)
|
||||
{
|
||||
$return = false;
|
||||
|
||||
if ($force_extension !== false and in_array($force_extension, $this->accepted_extensions))
|
||||
{
|
||||
return $force_extension;
|
||||
}
|
||||
|
||||
foreach ($this->accepted_extensions as $ext)
|
||||
{
|
||||
if (strtolower(substr($filename, strlen($ext) * -1)) == strtolower($ext))
|
||||
{
|
||||
$writevar and $this->image_extension = $ext;
|
||||
$return = $ext;
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts percentages, negatives, and other values to absolute integers.
|
||||
*
|
||||
* @param string $input
|
||||
* @param boolean $x Determines if the number relates to the x-axis or y-axis.
|
||||
* @return integer The converted number, usable with the image being edited.
|
||||
*/
|
||||
protected function convert_number($input, $x = null)
|
||||
{
|
||||
// Sanitize double negatives
|
||||
$input = str_replace('--', '', $input);
|
||||
|
||||
// Depending on php configuration, float are sometimes converted to strings
|
||||
// using commas instead of points. This notation can create issues since the
|
||||
// conversion from string to float will return an integer.
|
||||
// For instance: "1.2" / 10 == 0.12 but "1,2" / 10 == 0.1...
|
||||
$input = str_replace(',', '.', $input);
|
||||
|
||||
$orig = $input;
|
||||
$sizes = $this->sizes();
|
||||
$size = $x ? $sizes->width : $sizes->height;
|
||||
// Convert percentages to absolutes
|
||||
if (substr($input, -1) == '%')
|
||||
{
|
||||
$input = floor((substr($input, 0, -1) / 100) * $size);
|
||||
}
|
||||
// Negatives are based off the bottom right
|
||||
if ($x !== null and $input < 0)
|
||||
{
|
||||
$input = $size + $input;
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a function to run at a later time.
|
||||
*
|
||||
* @param string $function The name of the function to be ran, without the leading _
|
||||
*/
|
||||
protected function queue($function)
|
||||
{
|
||||
$func = func_get_args();
|
||||
$tmpfunc = array();
|
||||
for ($i = 0; $i < count($func); $i++)
|
||||
{
|
||||
$tmpfunc[$i] = var_export($func[$i], true);
|
||||
}
|
||||
|
||||
$this->debug("Queued <code>" . implode(", ", $tmpfunc) . "</code>");
|
||||
$this->queued_actions[] = $func;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all queued actions on the loaded image.
|
||||
*
|
||||
* @param boolean $clear Decides if the queue should be cleared once completed.
|
||||
*/
|
||||
public function run_queue($clear = null)
|
||||
{
|
||||
foreach ($this->queued_actions as $action)
|
||||
{
|
||||
$tmpfunc = array();
|
||||
for ($i = 0; $i < count($action); $i++)
|
||||
{
|
||||
$tmpfunc[$i] = var_export($action[$i], true);
|
||||
}
|
||||
$this->debug('', "<b>Executing <code>" . implode(", ", $tmpfunc) . "</code></b>");
|
||||
call_user_func_array(array(&$this, '_' . $action[0]), array_slice($action, 1));
|
||||
}
|
||||
if (($clear === null and $this->config['clear_queue']) or $clear === true)
|
||||
{
|
||||
$this->queued_actions = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the image.
|
||||
*
|
||||
* @return Image_Driver
|
||||
*/
|
||||
public function reload()
|
||||
{
|
||||
$this->debug("Reloading was called!");
|
||||
$this->load($this->image_fullpath);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file extension (type) worked out on construct
|
||||
*
|
||||
* @return string File extension
|
||||
*/
|
||||
public function extension()
|
||||
{
|
||||
return $this->image_extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for debugging image output.
|
||||
*/
|
||||
protected function debug()
|
||||
{
|
||||
if ($this->config['debug'])
|
||||
{
|
||||
$messages = func_get_args();
|
||||
foreach ($messages as $message)
|
||||
{
|
||||
echo '<div>' . $message . ' </div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,551 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Image_Gd extends \Image_Driver
|
||||
{
|
||||
protected $image_data = null;
|
||||
protected $accepted_extensions = array('png', 'gif', 'jpg', 'jpeg');
|
||||
protected $gdresizefunc = "imagecopyresampled";
|
||||
|
||||
public function load($filename, $return_data = false, $force_extension = false)
|
||||
{
|
||||
extract(parent::load($filename, $return_data, $force_extension));
|
||||
$return = false;
|
||||
$image_extension == 'jpg' and $image_extension = 'jpeg';
|
||||
|
||||
if ( ! $return_data)
|
||||
{
|
||||
$this->image_data !== null and imagedestroy($this->image_data);
|
||||
$this->image_data = null;
|
||||
}
|
||||
|
||||
// Check if the function exists
|
||||
if (function_exists('imagecreatefrom'.$image_extension))
|
||||
{
|
||||
// Create a new transparent image.
|
||||
$sizes = $this->sizes($image_fullpath);
|
||||
$tmpImage = call_user_func('imagecreatefrom'.$image_extension, $image_fullpath);
|
||||
$image = $this->create_transparent_image($sizes->width, $sizes->height, $tmpImage);
|
||||
if ( ! $return_data)
|
||||
{
|
||||
$this->image_data = $image;
|
||||
$return = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$return = $image;
|
||||
}
|
||||
$this->debug('', "<strong>Loaded</strong> <code>".$image_fullpath."</code> with size of ".$sizes->width."x".$sizes->height);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \RuntimeException("Function imagecreatefrom".$image_extension."() does not exist (Missing GD?)");
|
||||
}
|
||||
return $return_data ? $return : $this;
|
||||
}
|
||||
|
||||
protected function _crop($x1, $y1, $x2, $y2)
|
||||
{
|
||||
extract(parent::_crop($x1, $y1, $x2, $y2));
|
||||
$width = $x2 - $x1;
|
||||
$height = $y2 - $y1;
|
||||
$this->debug("Cropping image ".$width."x".$height."+$x1+$y1 based on coords ($x1, $y1), ($x2, $y2)");
|
||||
$image = $this->create_transparent_image($width, $height);
|
||||
|
||||
imagecopy($image, $this->image_data, 0, 0, $x1, $y1, $width, $height);
|
||||
$this->image_data = $image;
|
||||
}
|
||||
|
||||
protected function _resize($width, $height = null, $keepar = true, $pad = true)
|
||||
{
|
||||
extract(parent::_resize($width, $height, $keepar, $pad));
|
||||
$sizes = $this->sizes();
|
||||
|
||||
$this->debug("Resizing image to $width, $height with" . ($keepar ? '' : 'out') . " keeping AR and with" . ($pad ? '' : 'out') . " padding.");
|
||||
|
||||
// Add the original image.
|
||||
$image = $this->create_transparent_image($cwidth, $cheight);
|
||||
call_user_func($this->gdresizefunc, $image, $this->image_data, $x, $y, 0, 0, $width, $height, $sizes->width, $sizes->height);
|
||||
$this->image_data = $image;
|
||||
}
|
||||
|
||||
protected function _rotate($degrees)
|
||||
{
|
||||
extract(parent::_rotate($degrees));
|
||||
$degrees = 360 - $degrees;
|
||||
$bgcolor = $this->config['bgcolor'] !== null ? $this->config['bgcolor'] : '#000';
|
||||
$color = $this->create_color($this->image_data, $bgcolor, 100);
|
||||
$this->image_data = imagerotate($this->image_data, $degrees, $color, false);
|
||||
}
|
||||
|
||||
protected function _watermark($filename, $position, $padding = 5)
|
||||
{
|
||||
$values = parent::_watermark($filename, $position, $padding);
|
||||
if ($values == false)
|
||||
{
|
||||
throw new \InvalidArgumentException("Watermark image not found or invalid filetype.");
|
||||
}
|
||||
else
|
||||
{
|
||||
extract($values);
|
||||
$wsizes = $this->sizes($filename);
|
||||
$sizes = $this->sizes();
|
||||
|
||||
// Load the watermark preserving transparency
|
||||
$watermark = $this->load($filename, true);
|
||||
|
||||
// Below is to prevent glitch in GD with negative $x coords
|
||||
if ($x < 0 || $y < 0)
|
||||
{
|
||||
$this->debug("Modifying watermark to remove negative coords.");
|
||||
// Generate a new width and height for the watermark.
|
||||
$newwidth = ($x < 0 ? $wsizes->width + $x : $wsizes->width);
|
||||
$newheight = ($y < 0 ? $wsizes->height + $y : $wsizes->height);
|
||||
// Create a transparent image the size of the new watermark.
|
||||
$tmpwatermark = $this->create_transparent_image($newwidth, $newheight);
|
||||
$this->debug("New size is $newwidth x $newheight and coords are $x , $y");
|
||||
// Call the resize function based on image format
|
||||
imagecopy(
|
||||
$tmpwatermark, $watermark, // Copy the new image into the tmp watermark
|
||||
0, 0,
|
||||
$x < 0 ? abs($x) : 0,
|
||||
$y < 0 ? abs($y) : 0,
|
||||
$newwidth, $newheight
|
||||
);
|
||||
// Set the variables for the image_merge
|
||||
$watermark = $tmpwatermark;
|
||||
$x = $x < 0 ? 0 : $x;
|
||||
$y = $y < 0 ? 0 : $y;
|
||||
}
|
||||
// Used as a workaround for lack of alpha support in imagecopymerge.
|
||||
$this->debug("Coords for watermark are $x , $y");
|
||||
$this->image_merge($this->image_data, $watermark, $x, $y, $this->config['watermark_alpha']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function _flip($mode)
|
||||
{
|
||||
$sizes = (array) $this->sizes();
|
||||
$source = array_merge($sizes, array('x' => 0, 'y' => 0));
|
||||
|
||||
switch ($mode)
|
||||
{
|
||||
case 'vertical':
|
||||
$source['y'] = $sizes['height'] - 1;
|
||||
$source['height'] = -$sizes['height'];
|
||||
break;
|
||||
|
||||
case 'horizontal':
|
||||
$source['x'] = $sizes['width'] - 1;
|
||||
$source['width'] = -$sizes['width'];
|
||||
break;
|
||||
|
||||
case 'both':
|
||||
$source['y'] = $sizes['height'] - 1;
|
||||
$source['x'] = $sizes['width'] - 1;
|
||||
$source['height'] = -$sizes['height'];
|
||||
$source['width'] = -$sizes['width'];
|
||||
break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
|
||||
$image = imagecreatetruecolor($sizes['width'], $sizes['height']);
|
||||
|
||||
imagecopyresampled(
|
||||
$image,
|
||||
$this->image_data,
|
||||
0,
|
||||
0,
|
||||
$source['x'],
|
||||
$source['y'],
|
||||
$sizes['width'],
|
||||
$sizes['height'],
|
||||
$source['width'],
|
||||
$source['height']
|
||||
);
|
||||
|
||||
$this->image_data = $image;
|
||||
}
|
||||
|
||||
protected function _border($size, $color = null)
|
||||
{
|
||||
extract(parent::_border($size, $color));
|
||||
$sizes = $this->sizes();
|
||||
$image = $this->create_transparent_image($sizes->width + ($size * 2), $sizes->height + ($size * 2));
|
||||
$color = $this->create_color($image, $color, 100);
|
||||
$this->image_merge($image, $this->image_data, $size, $size, 100);
|
||||
for ($s = 0; $s < $size; $s++)
|
||||
{
|
||||
imagerectangle($image, $s, $s, $sizes->width + ($size * 2) - $s - 1, $sizes->height + ($size * 2) - $s - 1, $color);
|
||||
}
|
||||
$this->image_data = $image;
|
||||
}
|
||||
|
||||
protected function _mask($maskimage)
|
||||
{
|
||||
extract(parent::_mask($maskimage));
|
||||
|
||||
// Get size and width of image
|
||||
$sizes = $this->sizes();
|
||||
$masksizes = $this->sizes($maskimage);
|
||||
|
||||
// Create new blank image
|
||||
$image = $this->create_transparent_image($sizes->width, $sizes->height);
|
||||
if (is_resource($maskimage))
|
||||
{
|
||||
$maskim = $maskimage;
|
||||
}
|
||||
else
|
||||
{
|
||||
$maskim = $this->load($maskimage, true);
|
||||
}
|
||||
|
||||
$masksizes->width > $sizes->width and $masksizes->width = $sizes->width;
|
||||
$masksizes->height > $sizes->width and $masksizes->height = $sizes->height;
|
||||
|
||||
// Loop through all the pixels
|
||||
for ($x = 0; $x < $masksizes->width; $x++)
|
||||
{
|
||||
for ($y = 0; $y < $masksizes->height; $y++)
|
||||
{
|
||||
$maskcolor = imagecolorat($maskim, $x, $y);
|
||||
$maskcolor = imagecolorsforindex($maskim, $maskcolor);
|
||||
$maskalpha = 127 - floor(($maskcolor['red'] + $maskcolor['green'] + $maskcolor['blue']) / 6);
|
||||
if ($maskalpha == 127)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($maskalpha == 0)
|
||||
{
|
||||
$ourcolor = array(
|
||||
'red' => 0,
|
||||
'green' => 0,
|
||||
'blue' => 0,
|
||||
'alpha' => 0,
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$ourcolor = imagecolorat($this->image_data, $x, $y);
|
||||
$ourcolor = imagecolorsforindex($this->image_data, $ourcolor);
|
||||
}
|
||||
|
||||
$ouralpha = 127 - $ourcolor['alpha'];
|
||||
if ($ouralpha == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$newalpha = floor($ouralpha - (($maskalpha / 127) * $ouralpha));
|
||||
$newcolor = imagecolorallocatealpha($image, $ourcolor['red'], $ourcolor['green'], $ourcolor['blue'], 127 - $newalpha);
|
||||
imagesetpixel($image, $x, $y, $newcolor);
|
||||
}
|
||||
}
|
||||
|
||||
$this->image_data = $image;
|
||||
}
|
||||
|
||||
protected function _rounded($radius, $sides, $antialias)
|
||||
{
|
||||
extract(parent::_rounded($radius, $sides, $antialias));
|
||||
|
||||
$tl and $this->round_corner($this->image_data, $radius, $antialias, true, true);
|
||||
$tr and $this->round_corner($this->image_data, $radius, $antialias, true, false);
|
||||
$bl and $this->round_corner($this->image_data, $radius, $antialias, false, true);
|
||||
$br and $this->round_corner($this->image_data, $radius, $antialias, false, false);
|
||||
}
|
||||
|
||||
protected function _grayscale()
|
||||
{
|
||||
$sizes = $this->sizes();
|
||||
|
||||
// Create the 256 color palette
|
||||
$bwpalette = array();
|
||||
for ($i = 0; $i < 256; $i++)
|
||||
{
|
||||
$bwpalette[$i] = imagecolorallocate($this->image_data, $i, $i, $i);
|
||||
}
|
||||
|
||||
for ($x = 0; $x < $sizes->width; $x++)
|
||||
{
|
||||
for ($y = 0; $y < $sizes->height; $y++)
|
||||
{
|
||||
$color = imagecolorat($this->image_data, $x, $y);
|
||||
$red = ($color >> 16) & 0xFF;
|
||||
$green = ($color >> 8) & 0xFF;
|
||||
$blue = $color & 0xFF;
|
||||
|
||||
// If its black or white, theres no use in setting the pixel
|
||||
if (($red == 0 && $green == 0 && $blue == 0) || ($red == 255 && $green == 255 && $blue == 255))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now set the color
|
||||
$shade = (($red*0.299)+($green*0.587)+($blue*0.114));
|
||||
imagesetpixel($this->image_data, $x, $y, $bwpalette[$shade]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function sizes($filename = null)
|
||||
{
|
||||
if (empty($filename) && !empty($this->image_fullpath))
|
||||
{
|
||||
$filename = $this->image_fullpath;
|
||||
}
|
||||
|
||||
if ($filename == $this->image_fullpath && is_resource($this->image_data))
|
||||
{
|
||||
$width = imagesx($this->image_data);
|
||||
$height = imagesy($this->image_data);
|
||||
}
|
||||
elseif (is_resource($filename))
|
||||
{
|
||||
$width = imagesx($filename);
|
||||
$height = imagesy($filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
list($width, $height) = getimagesize($filename);
|
||||
}
|
||||
return (object) array('width' => $width, 'height' => $height);
|
||||
}
|
||||
|
||||
public function save($filename = null, $permissions = null)
|
||||
{
|
||||
extract(parent::save($filename, $permissions));
|
||||
|
||||
$this->run_queue();
|
||||
$this->add_background();
|
||||
|
||||
$vars = array(&$this->image_data, $filename);
|
||||
$filetype = $this->image_extension;
|
||||
if ($filetype == 'jpg' || $filetype == 'jpeg')
|
||||
{
|
||||
$vars[] = $this->config['quality'];
|
||||
$filetype = 'jpeg';
|
||||
}
|
||||
elseif ($filetype == 'png')
|
||||
{
|
||||
$vars[] = floor(($this->config['quality'] / 100) * 9);
|
||||
}
|
||||
|
||||
call_fuel_func_array('image'.$filetype, $vars);
|
||||
if ($this->config['persistence'] === false)
|
||||
{
|
||||
$this->reload();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function output($filetype = null)
|
||||
{
|
||||
$this->gdresizefunc = ($filetype == 'gif') ? 'imagecopyresized' : $this->gdresizefunc = 'imagecopyresampled';
|
||||
|
||||
extract(parent::output($filetype));
|
||||
|
||||
$this->run_queue();
|
||||
$this->add_background();
|
||||
|
||||
$vars = array($this->image_data, null);
|
||||
if ($filetype == 'jpg' || $filetype == 'jpeg')
|
||||
{
|
||||
$vars[] = $this->config['quality'];
|
||||
$filetype = 'jpeg';
|
||||
}
|
||||
elseif ($filetype == 'png')
|
||||
{
|
||||
$vars[] = floor(($this->config['quality'] / 100) * 9);
|
||||
}
|
||||
|
||||
call_fuel_func_array('image'.$filetype, $vars);
|
||||
|
||||
if ($this->config['persistence'] === false)
|
||||
{
|
||||
$this->reload();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new color usable by GD.
|
||||
*
|
||||
* @param resource $image The image to create the color from
|
||||
* @param string $hex The hex code of the color
|
||||
* @param integer $alpha The alpha of the color, 0 (trans) to 100 (opaque)
|
||||
* @return integer The color
|
||||
*/
|
||||
protected function create_color(&$image, $hex, $alpha)
|
||||
{
|
||||
extract($this->create_hex_color($hex));
|
||||
|
||||
// Handling alpha is different among drivers
|
||||
if ($hex == null)
|
||||
{
|
||||
$alpha = 127;
|
||||
}
|
||||
else
|
||||
{
|
||||
$alpha = 127 - floor($alpha * 1.27);
|
||||
}
|
||||
|
||||
// Check if the transparency is allowed
|
||||
return imagecolorallocatealpha($image, $red, $green, $blue, $alpha);
|
||||
}
|
||||
|
||||
protected function add_background()
|
||||
{
|
||||
if ($this->config['bgcolor'] != null || ($this->new_extension == 'jpg' || $this->new_extension == 'jpeg'))
|
||||
{
|
||||
$bgcolor = $this->config['bgcolor'] == null ? '#000' : $this->config['bgcolor'];
|
||||
$this->debug("Adding background color $bgcolor");
|
||||
$sizes = $this->sizes();
|
||||
$bgimg = $this->create_transparent_image($sizes->width, $sizes->height);
|
||||
$color = $this->create_color($bgimg, $bgcolor, 100);
|
||||
imagefill($bgimg, 0, 0, $color);
|
||||
$this->image_merge($bgimg, $this->image_data, 0, 0, 100);
|
||||
$this->image_data = $bgimg;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new transparent image.
|
||||
*
|
||||
* @param integer $width The width of the image.
|
||||
* @param integer $height The height of the image.
|
||||
* @param resource $resource Optionally add an image to the new transparent image.
|
||||
* @return resource Returns the image in resource form.
|
||||
*/
|
||||
protected function create_transparent_image($width, $height, $resource = null)
|
||||
{
|
||||
$image = imagecreatetruecolor($width, $height);
|
||||
$bgcolor = $this->config['bgcolor'] == null ? '#000' : $this->config['bgcolor'];
|
||||
$color = $this->create_color($image, $bgcolor, 0);
|
||||
imagesavealpha($image, true);
|
||||
if ($this->image_extension == 'gif' || $this->image_extension == 'png')
|
||||
{
|
||||
// Get the current transparent color if possible...
|
||||
$transcolor = imagecolortransparent($image);
|
||||
if ($transcolor > 0)
|
||||
{
|
||||
$color = $transcolor;
|
||||
}
|
||||
imagecolortransparent($image, $color);
|
||||
}
|
||||
// Set the blending mode to false, add the bgcolor, then switch it back.
|
||||
imagealphablending($image, false);
|
||||
imagefilledrectangle($image, 0, 0, $width, $height, $color);
|
||||
imagealphablending($image, true);
|
||||
|
||||
if (is_resource($resource))
|
||||
{
|
||||
imagecopy($image, $resource, 0, 0, 0, 0, $width, $height);
|
||||
}
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a rounded corner on the image.
|
||||
*
|
||||
* @param resource $image
|
||||
* @param integer $radius
|
||||
* @param integer $antialias
|
||||
* @param boolean $top
|
||||
* @param boolean $left
|
||||
*/
|
||||
protected function round_corner(&$image, $radius, $antialias, $top, $left)
|
||||
{
|
||||
$this->debug("Rounding ".($top ? 'top' : 'bottom')." ".($left ? 'left' : 'right')." corner with a radius of ".$radius."px.");
|
||||
$sX = $left ? -$radius : 0;
|
||||
$sY = $top ? -$radius : 0;
|
||||
$eX = $left ? 0 : $radius;
|
||||
$eY = $top ? 0 : $radius;
|
||||
|
||||
// Get this images size
|
||||
$sizes = $this->sizes();
|
||||
$offsetX = ($left ? $radius : $sizes->width - $radius - 1);
|
||||
$offsetY = ($top ? $radius : $sizes->height - $radius - 1);
|
||||
|
||||
// Set the images alpha blend to false
|
||||
imagealphablending($image, false);
|
||||
|
||||
// Make this color ahead time
|
||||
$transparent = $this->create_color($image, null, 0);
|
||||
for ($x = $sX; $x <= $eX; $x++)
|
||||
{
|
||||
for ($y = $sY; $y <= $eY; $y++)
|
||||
{
|
||||
$dist = sqrt(($x * $x) + ($y * $y));
|
||||
if ($dist <= $radius + $antialias)
|
||||
{
|
||||
// Decide if anything needs to be changed
|
||||
// We subtract from antialias so the transparency makes sense.
|
||||
$fromCirc = $dist - $radius;
|
||||
if ($fromCirc > 0)
|
||||
{
|
||||
if ($fromCirc == 0)
|
||||
{
|
||||
imagesetpixel($image, $x + $offsetX, $y + $offsetY, $transparent);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get color information from this spot on the image
|
||||
$rgba = imagecolorat($image, $x + $offsetX, $y + $offsetY);
|
||||
$tmpColor = imagecolorallocatealpha(
|
||||
$image,
|
||||
($rgba >> 16) & 0xFF, // Red
|
||||
($rgba >> 8) & 0xFF, // Green
|
||||
$rgba & 0xFF, // Blue
|
||||
(127 - (($rgba >> 24) & 0xFF)) * ($fromCirc / $antialias) // Alpha
|
||||
);
|
||||
imagesetpixel($image, $x + $offsetX, $y + $offsetY, $tmpColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear this area out...
|
||||
imagesetpixel($image, $x + $offsetX, $y + $offsetY, $transparent);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reset alpha blending
|
||||
imagealphablending($image, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges to images together, using a fix for transparency
|
||||
*
|
||||
* @param resource $image The bottom image
|
||||
* @param resource $watermark The image to be placed on top
|
||||
* @param integer $x The position of the watermark on the X-axis
|
||||
* @param integer $y The position of the watermark on the Y-axis
|
||||
* @param integer $alpha The transparency of the watermark, 0 (trans) to 100 (opaque)
|
||||
*/
|
||||
protected function image_merge(&$image, $watermark, $x, $y, $alpha)
|
||||
{
|
||||
$wsizes = $this->sizes($watermark);
|
||||
$tmpimage = $this->create_transparent_image($wsizes->width, $wsizes->height);
|
||||
imagecopy($tmpimage, $image, 0, 0, $x, $y, $wsizes->width, $wsizes->height);
|
||||
imagecopy($tmpimage, $watermark, 0, 0, 0, 0, $wsizes->width, $wsizes->height);
|
||||
imagealphablending($image, false);
|
||||
imagecopymerge($image, $tmpimage, $x, $y, 0, 0, $wsizes->width, $wsizes->height, $alpha);
|
||||
imagealphablending($image, true);
|
||||
}
|
||||
}
|
@ -1,356 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Image_Imagemagick extends \Image_Driver
|
||||
{
|
||||
protected $image_temp = null;
|
||||
protected $accepted_extensions = array('png', 'gif', 'jpg', 'jpeg');
|
||||
protected $sizes_cache = null;
|
||||
protected $im_path = null;
|
||||
|
||||
public function load($filename, $return_data = false, $force_extension = false)
|
||||
{
|
||||
extract(parent::load($filename, $return_data, $force_extension));
|
||||
|
||||
$this->clear_sizes();
|
||||
if (empty($this->image_temp))
|
||||
{
|
||||
do
|
||||
{
|
||||
$this->image_temp = $this->config['temp_dir'].substr($this->config['temp_append'].md5(time() * microtime()), 0, 32).'.png';
|
||||
}
|
||||
while (is_file($this->image_temp));
|
||||
}
|
||||
elseif (is_file($this->image_temp))
|
||||
{
|
||||
$this->debug('Removing previous temporary image.');
|
||||
unlink($this->image_temp);
|
||||
}
|
||||
$this->debug('Temp file: '.$this->image_temp);
|
||||
if ( ! is_dir($this->config['temp_dir']))
|
||||
{
|
||||
throw new \RuntimeException("The temp directory that was given does not exist.");
|
||||
}
|
||||
elseif (!touch($this->config['temp_dir'] . $this->config['temp_append'] . '_touch'))
|
||||
{
|
||||
throw new \RuntimeException("Could not write in the temp directory.");
|
||||
}
|
||||
$this->exec('convert', '"'.$image_fullpath.'"[0] "'.$this->image_temp.'"');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function _crop($x1, $y1, $x2, $y2)
|
||||
{
|
||||
extract(parent::_crop($x1, $y1, $x2, $y2));
|
||||
$image = '"'.$this->image_temp.'"';
|
||||
$this->exec('convert', $image.' -crop '.($x2 - $x1).'x'.($y2 - $y1).'+'.$x1.'+'.$y1.' +repage '.$image);
|
||||
$this->clear_sizes();
|
||||
}
|
||||
|
||||
protected function _resize($width, $height = null, $keepar = true, $pad = true)
|
||||
{
|
||||
extract(parent::_resize($width, $height, $keepar, $pad));
|
||||
|
||||
$image = '"'.$this->image_temp.'"';
|
||||
$this->exec('convert', "-define png:size=".$cwidth."x".$cheight." ".$image." ".
|
||||
"-background none ".
|
||||
"-resize \"".($pad ? $width : $cwidth)."x".($pad ? $height : $cheight)."!\" ".
|
||||
"-gravity center ".
|
||||
"-extent ".$cwidth."x".$cheight." ".$image);
|
||||
$this->clear_sizes();
|
||||
}
|
||||
|
||||
protected function _rotate($degrees)
|
||||
{
|
||||
extract(parent::_rotate($degrees));
|
||||
|
||||
$image = '"'.$this->image_temp.'"';
|
||||
$this->exec('convert', $image." -background none -virtual-pixel background +distort ScaleRotateTranslate ".$degrees." +repage ".$image);
|
||||
|
||||
$this->clear_sizes();
|
||||
}
|
||||
|
||||
protected function _flip($direction)
|
||||
{
|
||||
switch ($direction)
|
||||
{
|
||||
case 'vertical':
|
||||
$arg = '-flip';
|
||||
break;
|
||||
|
||||
case 'horizontal':
|
||||
$arg = '-flop';
|
||||
break;
|
||||
|
||||
case 'both':
|
||||
$arg = '-flip -flop';
|
||||
break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
$image = '"'.$this->image_temp.'"';
|
||||
$this->exec('convert', $image.' '.$arg.' '.$image);
|
||||
}
|
||||
|
||||
protected function _watermark($filename, $position, $padding = 5)
|
||||
{
|
||||
$values = parent::_watermark($filename, $position, $padding);
|
||||
if ($values == false)
|
||||
{
|
||||
throw new \InvalidArgumentException("Watermark image not found or invalid filetype.");
|
||||
}
|
||||
|
||||
extract($values);
|
||||
$x >= 0 and $x = '+'.$x;
|
||||
$y >= 0 and $y = '+'.$y;
|
||||
|
||||
$image = '"'.$this->image_temp.'"';
|
||||
$this->exec(
|
||||
'composite',
|
||||
'-compose atop -geometry '.$x.$y.' '.
|
||||
'-dissolve '.$this->config['watermark_alpha'].'% '.
|
||||
'"'.$filename.'" "'.$this->image_temp.'" '.$image
|
||||
);
|
||||
}
|
||||
|
||||
protected function _border($size, $color = null)
|
||||
{
|
||||
extract(parent::_border($size, $color));
|
||||
|
||||
$image = '"'.$this->image_temp.'"';
|
||||
$color = $this->create_color($color, 100);
|
||||
$command = $image.' -compose copy -bordercolor '.$color.' -border '.$size.'x'.$size.' '.$image;
|
||||
$this->exec('convert', $command);
|
||||
|
||||
$this->clear_sizes();
|
||||
}
|
||||
|
||||
protected function _mask($maskimage)
|
||||
{
|
||||
extract(parent::_mask($maskimage));
|
||||
|
||||
$mimage = '"'.$maskimage.'"';
|
||||
$image = '"'.$this->image_temp.'"';
|
||||
$command = $image.' '.$mimage.' +matte -compose copy-opacity -composite '.$image;
|
||||
$this->exec('convert', $command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Credit to Leif Åstrand <leif@sitelogic.fi> for the base of the round corners.
|
||||
*
|
||||
* Note there is a defect with this, as non-transparent corners get opaque circles of color. Maybe mask it with auto-generated corners?
|
||||
*
|
||||
* @link http://www.imagemagick.org/Usage/thumbnails/#rounded
|
||||
*/
|
||||
protected function _rounded($radius, $sides, $antialias = 0)
|
||||
{
|
||||
extract(parent::_rounded($radius, $sides, null));
|
||||
|
||||
$image = '"'.$this->image_temp.'"';
|
||||
$r = $radius;
|
||||
$command = $image." \\( +clone -alpha extract ".
|
||||
( ! $tr ? '' : "-draw \"fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0\" ")."-flip ".
|
||||
( ! $br ? '' : "-draw \"fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0\" ")."-flop ".
|
||||
( ! $bl ? '' : "-draw \"fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0\" ")."-flip ".
|
||||
( ! $tl ? '' : "-draw \"fill black polygon 0,0 0,$r $r,0 fill white circle $r,$r $r,0\" ").
|
||||
'\\) -alpha off -compose CopyOpacity -composite '.$image;
|
||||
$this->exec('convert', $command);
|
||||
}
|
||||
|
||||
protected function _grayscale()
|
||||
{
|
||||
$image = '"'.$this->image_temp.'"';
|
||||
$this->exec('convert', $image." -colorspace Gray ".$image);
|
||||
}
|
||||
|
||||
public function sizes($filename = null, $usecache = true)
|
||||
{
|
||||
$is_loaded_file = $filename == null;
|
||||
if ( ! $is_loaded_file or $this->sizes_cache == null or !$usecache)
|
||||
{
|
||||
$reason = ($filename != null ? "filename" : ($this->sizes_cache == null ? 'cache' : 'option'));
|
||||
$this->debug("Generating size of image... (triggered by $reason)");
|
||||
|
||||
if ($is_loaded_file and ! empty($this->image_temp))
|
||||
{
|
||||
$filename = $this->image_temp;
|
||||
}
|
||||
|
||||
$output = $this->exec('identify', '-format "%w %h" "'.$filename.'"[0]');
|
||||
list($width, $height) = explode(" ", $output[0]);
|
||||
$return = (object) array(
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
);
|
||||
|
||||
if ($is_loaded_file)
|
||||
{
|
||||
$this->sizes_cache = $return;
|
||||
}
|
||||
$this->debug("Sizes ".( !$is_loaded_file ? "for <code>$filename</code> " : "")."are now $width and $height");
|
||||
}
|
||||
else
|
||||
{
|
||||
$return = $this->sizes_cache;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function save($filename = null, $permissions = null)
|
||||
{
|
||||
extract(parent::save($filename, $permissions));
|
||||
|
||||
$this->run_queue();
|
||||
$this->add_background();
|
||||
|
||||
$filetype = $this->image_extension;
|
||||
$old = '"'.$this->image_temp.'"';
|
||||
$new = '"'.$filename.'"';
|
||||
|
||||
if(($filetype == 'jpeg' or $filetype == 'jpg') and $this->config['quality'] != 100)
|
||||
{
|
||||
$quality = '"'.$this->config['quality'].'%"';
|
||||
$this->exec('convert', $old.' -quality '.$quality.' '.$new);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->exec('convert', $old.' '.$new);
|
||||
}
|
||||
|
||||
if ($this->config['persistence'] === false)
|
||||
{
|
||||
$this->reload();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function output($filetype = null)
|
||||
{
|
||||
extract(parent::output($filetype));
|
||||
|
||||
$this->run_queue();
|
||||
$this->add_background();
|
||||
|
||||
$image = '"'.$this->image_temp.'"';
|
||||
|
||||
if(($filetype == 'jpeg' or $filetype == 'jpg') and $this->config['quality'] != 100)
|
||||
{
|
||||
$quality = '"'.$this->config['quality'].'%"';
|
||||
$this->exec('convert', $image.' -quality '.$quality.' '.strtolower($filetype).':-', true);
|
||||
}
|
||||
elseif (substr($this->image_temp, -1 * strlen($filetype)) != $filetype)
|
||||
{
|
||||
if ( ! $this->config['debug'])
|
||||
{
|
||||
$this->exec('convert', $image.' '.strtolower($filetype).':-', true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! $this->config['debug'])
|
||||
{
|
||||
echo file_get_contents($this->image_temp);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->config['persistence'] === false)
|
||||
{
|
||||
$this->reload();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleared the currently loaded sizes, used to removed cached sizes.
|
||||
*/
|
||||
protected function clear_sizes()
|
||||
{
|
||||
$this->sizes_cache = null;
|
||||
}
|
||||
|
||||
protected function add_background()
|
||||
{
|
||||
if ($this->config['bgcolor'] != null)
|
||||
{
|
||||
$bgcolor = $this->config['bgcolor'] == null ? '#000' : $this->config['bgcolor'];
|
||||
$image = '"'.$this->image_temp.'"';
|
||||
$color = $this->create_color($bgcolor, 100);
|
||||
$sizes = $this->sizes();
|
||||
$command = '-size '.$sizes->width.'x'.$sizes->height.' '.'canvas:'.$color.' '.
|
||||
$image.' -composite '.$image;
|
||||
$this->exec('convert', $command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the specified imagemagick executable and returns the output.
|
||||
*
|
||||
* @param string $program The name of the executable.
|
||||
* @param string $params The parameters of the executable.
|
||||
* @param boolean $passthru Returns the output if false or pass it to browser.
|
||||
* @return mixed Either returns the output or returns nothing.
|
||||
*/
|
||||
protected function exec($program, $params, $passthru = false)
|
||||
{
|
||||
// Determine the path
|
||||
$this->im_path = realpath($this->config['imagemagick_dir'].$program);
|
||||
if ( ! $this->im_path)
|
||||
{
|
||||
$this->im_path = realpath($this->config['imagemagick_dir'].$program.'.exe');
|
||||
}
|
||||
if ( ! $this->im_path)
|
||||
{
|
||||
throw new \RuntimeException("imagemagick executables not found in ".$this->config['imagemagick_dir']);
|
||||
}
|
||||
|
||||
$command = $this->im_path." ".$params;
|
||||
$this->debug("Running command: <code>$command</code>");
|
||||
$code = 0;
|
||||
$output = null;
|
||||
|
||||
$passthru ? passthru($command) : exec($command, $output, $code);
|
||||
|
||||
if ($code != 0)
|
||||
{
|
||||
throw new \FuelException("imagemagick failed to edit the image. Returned with $code.<br /><br />Command:\n <code>$command</code>");
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new color usable by ImageMagick.
|
||||
*
|
||||
* @param string $hex The hex code of the color
|
||||
* @param integer $alpha The alpha of the color, 0 (trans) to 100 (opaque)
|
||||
* @return string rgba representation of the hex and alpha values.
|
||||
*/
|
||||
protected function create_color($hex, $alpha)
|
||||
{
|
||||
extract($this->create_hex_color($hex));
|
||||
return "\"rgba(".$red.", ".$green.", ".$blue.", ".round($alpha / 100, 2).")\"";
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (is_file($this->image_temp))
|
||||
{
|
||||
unlink($this->image_temp);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,265 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class Image_Imagick extends \Image_Driver
|
||||
{
|
||||
protected $accepted_extensions = array('png', 'gif', 'jpg', 'jpeg');
|
||||
protected $imagick = null;
|
||||
|
||||
public function load($filename, $return_data = false, $force_extension = false)
|
||||
{
|
||||
extract(parent::load($filename, $return_data, $force_extension));
|
||||
|
||||
if ($this->imagick == null)
|
||||
{
|
||||
$this->imagick = new \Imagick();
|
||||
}
|
||||
|
||||
$this->imagick->readImage($filename);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function _crop($x1, $y1, $x2, $y2)
|
||||
{
|
||||
extract(parent::_crop($x1, $y1, $x2, $y2));
|
||||
|
||||
$width = $x2 - $x1;
|
||||
$height = $y2 - $y1;
|
||||
|
||||
$this->debug("Cropping image ".$width."x".$height."+$x1+$y1 based on coords ($x1, $y1), ($x2, $y2)");
|
||||
|
||||
$this->imagick->cropImage($width, $height, $x1, $y1);
|
||||
$this->imagick->setImagePage(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
protected function _resize($width, $height = null, $keepar = true, $pad = true)
|
||||
{
|
||||
extract(parent::_resize($width, $height, $keepar, $pad));
|
||||
|
||||
$this->imagick->scaleImage($width, $height, $keepar);
|
||||
|
||||
if ($pad)
|
||||
{
|
||||
$tmpimage = new \Imagick();
|
||||
$tmpimage->newImage($cwidth, $cheight, $this->create_color('#000', 0), 'png');
|
||||
$tmpimage->compositeImage($this->imagick, \Imagick::COMPOSITE_DEFAULT, ($cwidth-$width) / 2, ($cheight-$height) / 2);
|
||||
$this->imagick = $tmpimage;
|
||||
}
|
||||
}
|
||||
|
||||
protected function _rotate($degrees)
|
||||
{
|
||||
extract(parent::_rotate($degrees));
|
||||
|
||||
$this->imagick->rotateImage($this->create_color('#000', 0), $degrees);
|
||||
}
|
||||
|
||||
protected function _watermark($filename, $position, $padding = 5)
|
||||
{
|
||||
extract(parent::_watermark($filename, $position, $padding));
|
||||
$wmimage = new \Imagick();
|
||||
$wmimage->readImage($filename);
|
||||
$wmimage->evaluateImage(\Imagick::EVALUATE_MULTIPLY, $this->config['watermark_alpha'] / 100, \Imagick::CHANNEL_ALPHA);
|
||||
$this->imagick->compositeImage($wmimage, \Imagick::COMPOSITE_DEFAULT, $x, $y);
|
||||
}
|
||||
|
||||
protected function _flip($direction)
|
||||
{
|
||||
switch ($direction)
|
||||
{
|
||||
case 'vertical':
|
||||
$this->imagick->flipImage();
|
||||
break;
|
||||
|
||||
case 'horizontal':
|
||||
$this->imagick->flopImage();
|
||||
break;
|
||||
|
||||
case 'both':
|
||||
$this->imagick->flipImage();
|
||||
$this->imagick->flopImage();
|
||||
break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function _border($size, $color = null)
|
||||
{
|
||||
extract(parent::_border($size, $color));
|
||||
|
||||
$this->imagick->borderImage($this->create_color($color, 100), $size, $size);
|
||||
}
|
||||
|
||||
protected function _mask($maskimage)
|
||||
{
|
||||
extract(parent::_mask($maskimage));
|
||||
$wmimage = new \Imagick();
|
||||
$wmimage->readImage($maskimage);
|
||||
$wmimage->setImageMatte(false);
|
||||
$this->imagick->compositeImage($wmimage, \Imagick::COMPOSITE_COPYOPACITY, 0, 0);
|
||||
}
|
||||
|
||||
protected function _rounded($radius, $sides, $antialias = 0)
|
||||
{
|
||||
extract(parent::_rounded($radius, $sides, null));
|
||||
|
||||
$sizes = $this->sizes();
|
||||
$sizes->width_half = $sizes->width / 2;
|
||||
$sizes->height_half = $sizes->height / 2;
|
||||
|
||||
$list = array();
|
||||
if (!$tl) {
|
||||
$list = array('x' => 0, 'y' => 0);
|
||||
}
|
||||
if (!$tr) {
|
||||
$list = array('x' => $sizes->width_half, 'y' => 0);
|
||||
}
|
||||
if (!$bl) {
|
||||
$list = array('x' => 0, 'y' => $sizes->height_half);
|
||||
}
|
||||
if (!$br) {
|
||||
$list = array('x' => $sizes->width_half, 'y' => $sizes->height_half);
|
||||
}
|
||||
|
||||
foreach($list as $index => $element) {
|
||||
$image = $this->imagick->clone();
|
||||
$image->cropImage($sizes->width_half, $sizes->height_half, $element['x'], $element['y']);
|
||||
$list[$index]['image'] = $image;
|
||||
}
|
||||
|
||||
$this->imagick->roundCorners($radius, $radius);
|
||||
|
||||
foreach($list as $element) {
|
||||
$this->imagick->compositeImage($element['image'], \Imagick::COMPOSITE_DEFAULT, $element['x'], $element['y']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function _grayscale()
|
||||
{
|
||||
$this->imagick->setImageType(\Imagick::IMGTYPE_GRAYSCALEMATTE);
|
||||
}
|
||||
|
||||
public function sizes($filename = null, $usecache = true)
|
||||
{
|
||||
if ($filename === null)
|
||||
{
|
||||
return (object) array(
|
||||
'width' => $this->imagick->getImageWidth(),
|
||||
'height' => $this->imagick->getImageHeight(),
|
||||
);
|
||||
}
|
||||
|
||||
$tmpimage = new \Imagick();
|
||||
$tmpimage->readImage($filename);
|
||||
return (object) array(
|
||||
'width' => $tmpimage->getImageWidth(),
|
||||
'height' => $tmpimage->getImageHeight(),
|
||||
);
|
||||
}
|
||||
|
||||
public function save($filename = null, $permissions = null)
|
||||
{
|
||||
extract(parent::save($filename, $permissions));
|
||||
|
||||
$this->run_queue();
|
||||
$this->add_background();
|
||||
|
||||
$filetype = $this->image_extension;
|
||||
|
||||
if ($filetype == 'jpg' or $filetype == 'jpeg')
|
||||
{
|
||||
$filetype = 'jpeg';
|
||||
}
|
||||
|
||||
if ($this->imagick->getImageFormat() != $filetype)
|
||||
{
|
||||
$this->imagick->setImageFormat($filetype);
|
||||
}
|
||||
|
||||
if($this->imagick->getImageFormat() == 'jpeg' and $this->config['quality'] != 100)
|
||||
{
|
||||
$this->imagick->setImageCompression(\Imagick::COMPRESSION_JPEG);
|
||||
$this->imagick->setImageCompressionQuality($this->config['quality']);
|
||||
$this->imagick->stripImage();
|
||||
}
|
||||
|
||||
file_put_contents($filename, $this->imagick->getImageBlob());
|
||||
|
||||
if ($this->config['persistence'] === false)
|
||||
{
|
||||
$this->reload();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function output($filetype = null)
|
||||
{
|
||||
extract(parent::output($filetype));
|
||||
|
||||
$this->run_queue();
|
||||
$this->add_background();
|
||||
|
||||
if ($filetype == 'jpg' or $filetype == 'jpeg')
|
||||
{
|
||||
$filetype = 'jpeg';
|
||||
}
|
||||
|
||||
if ($this->imagick->getImageFormat() != $filetype)
|
||||
{
|
||||
$this->imagick->setImageFormat($filetype);
|
||||
}
|
||||
|
||||
if($this->imagick->getImageFormat() == 'jpeg' and $this->config['quality'] != 100)
|
||||
{
|
||||
$this->imagick->setImageCompression(\Imagick::COMPRESSION_JPEG);
|
||||
$this->imagick->setImageCompressionQuality($this->config['quality']);
|
||||
$this->imagick->stripImage();
|
||||
}
|
||||
|
||||
if ( ! $this->config['debug'])
|
||||
{
|
||||
echo $this->imagick->getImageBlob();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function add_background()
|
||||
{
|
||||
if($this->config['bgcolor'] != null)
|
||||
{
|
||||
$tmpimage = new \Imagick();
|
||||
$sizes = $this->sizes();
|
||||
$tmpimage->newImage($sizes->width, $sizes->height, $this->create_color($this->config['bgcolor'], $this->config['bgcolor'] == null ? 0 : 100), 'png');
|
||||
$tmpimage->compositeImage($this->imagick, \Imagick::COMPOSITE_DEFAULT, 0, 0);
|
||||
$this->imagick = $tmpimage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new color usable by Imagick.
|
||||
*
|
||||
* @param string $hex The hex code of the color
|
||||
* @param integer $alpha The alpha of the color, 0 (trans) to 100 (opaque)
|
||||
* @return string rgba representation of the hex and alpha values.
|
||||
*/
|
||||
protected function create_color($hex, $alpha)
|
||||
{
|
||||
extract($this->create_hex_color($hex));
|
||||
return new \ImagickPixel('rgba('.$red.', '.$green.', '.$blue.', '.round($alpha / 100, 2).')');
|
||||
}
|
||||
}
|
@ -1,565 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Input class
|
||||
*
|
||||
* The input class allows you to access HTTP parameters, load server variables
|
||||
* and user agent details.
|
||||
*
|
||||
* @package Fuel
|
||||
* @category Core
|
||||
* @link http://docs.fuelphp.com/classes/input.html
|
||||
*/
|
||||
class Input
|
||||
{
|
||||
/**
|
||||
* @var $detected_uri The URI that was detected automatically
|
||||
*/
|
||||
protected static $detected_uri = null;
|
||||
|
||||
/**
|
||||
* @var $detected_ext The URI extension that was detected automatically
|
||||
*/
|
||||
protected static $detected_ext = null;
|
||||
|
||||
/**
|
||||
* @var array $input All of the input (GET, POST, PUT, DELETE, PATCH)
|
||||
*/
|
||||
protected static $input = null;
|
||||
|
||||
/**
|
||||
* @var array $put_patch_delete All of the put or delete vars
|
||||
*/
|
||||
protected static $put_patch_delete = null;
|
||||
|
||||
/**
|
||||
* @var $php_input Cache for the php://input stream
|
||||
*/
|
||||
protected static $php_input = null;
|
||||
|
||||
/**
|
||||
* @var $json parsed request body as json
|
||||
*/
|
||||
protected static $json = null;
|
||||
|
||||
/**
|
||||
* @var $xml parsed request body as xml
|
||||
*/
|
||||
protected static $xml = null;
|
||||
|
||||
/**
|
||||
* Get the request body interpreted as JSON.
|
||||
*
|
||||
* @param mixed $index
|
||||
* @param mixed $default
|
||||
* @return array parsed request body content.
|
||||
*/
|
||||
public static function json($index = null, $default = null)
|
||||
{
|
||||
static::$json === null and static::hydrate_raw_input('json');
|
||||
return (func_num_args() === 0) ? static::$json : \Arr::get(static::$json, $index, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request body interpreted as XML.
|
||||
*
|
||||
* @param mixed $index
|
||||
* @param mixed $default
|
||||
* @return array parsed request body content.
|
||||
*/
|
||||
public static function xml($index = null, $default = null)
|
||||
{
|
||||
static::$xml === null and static::hydrate_raw_input('xml');
|
||||
return (func_num_args() === 0) ? static::$xml : \Arr::get(static::$xml, $index, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydration from raw request (xml/json requests)
|
||||
*
|
||||
* @param string $type input type
|
||||
*/
|
||||
protected static function hydrate_raw_input($type)
|
||||
{
|
||||
static::$php_input === null and static::$php_input = file_get_contents('php://input');
|
||||
static::$$type = \Security::clean(\Format::forge(static::$php_input, $type)->to_array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects and returns the current URI based on a number of different server
|
||||
* variables.
|
||||
*
|
||||
* @throws \FuelException
|
||||
* @return string
|
||||
*/
|
||||
public static function uri()
|
||||
{
|
||||
if (static::$detected_uri !== null)
|
||||
{
|
||||
return static::$detected_uri;
|
||||
}
|
||||
|
||||
if (\Fuel::$is_cli)
|
||||
{
|
||||
if (($uri = \Cli::option('uri')) !== null)
|
||||
{
|
||||
static::$detected_uri = $uri;
|
||||
}
|
||||
else
|
||||
{
|
||||
static::$detected_uri = \Cli::option(1);
|
||||
}
|
||||
|
||||
return static::$detected_uri;
|
||||
}
|
||||
|
||||
// We want to use PATH_INFO if we can.
|
||||
if ( ! empty($_SERVER['PATH_INFO']))
|
||||
{
|
||||
$uri = $_SERVER['PATH_INFO'];
|
||||
}
|
||||
// Only use ORIG_PATH_INFO if it contains the path
|
||||
elseif ( ! empty($_SERVER['ORIG_PATH_INFO']) and ($path = str_replace($_SERVER['SCRIPT_NAME'], '', $_SERVER['ORIG_PATH_INFO'])) != '')
|
||||
{
|
||||
$uri = $path;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fall back to parsing the REQUEST URI
|
||||
if (isset($_SERVER['REQUEST_URI']))
|
||||
{
|
||||
$uri = strpos($_SERVER['SCRIPT_NAME'], $_SERVER['REQUEST_URI']) !== 0 ? $_SERVER['REQUEST_URI'] : '';
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \FuelException('Unable to detect the URI.');
|
||||
}
|
||||
|
||||
// Remove the base URL from the URI
|
||||
$base_url = parse_url(\Config::get('base_url'), PHP_URL_PATH);
|
||||
if ($uri != '' and strncmp($uri, $base_url, strlen($base_url)) === 0)
|
||||
{
|
||||
$uri = substr($uri, strlen($base_url) - 1);
|
||||
}
|
||||
|
||||
// If we are using an index file (not mod_rewrite) then remove it
|
||||
$index_file = \Config::get('index_file');
|
||||
if ($index_file and strncmp($uri, $index_file, strlen($index_file)) === 0)
|
||||
{
|
||||
$uri = substr($uri, strlen($index_file));
|
||||
}
|
||||
|
||||
// When index.php? is used and the config is set wrong, lets just
|
||||
// be nice and help them out.
|
||||
if ($index_file and strncmp($uri, '?/', 2) === 0)
|
||||
{
|
||||
$uri = substr($uri, 1);
|
||||
}
|
||||
|
||||
// decode the uri, and put any + back (does not mean a space in the url path)
|
||||
$uri = str_replace("\r", '+', urldecode(str_replace('+', "\r", $uri)));
|
||||
|
||||
// Lets split the URI up in case it contains a ?. This would
|
||||
// indicate the server requires 'index.php?' and that mod_rewrite
|
||||
// is not being used.
|
||||
preg_match('#(.*?)\?(.*)#i', $uri, $matches);
|
||||
|
||||
// If there are matches then lets set set everything correctly
|
||||
if ( ! empty($matches))
|
||||
{
|
||||
$uri = $matches[1];
|
||||
|
||||
// only reconstruct $_GET if we didn't have a query string
|
||||
if (empty($_SERVER['QUERY_STRING']))
|
||||
{
|
||||
$_SERVER['QUERY_STRING'] = $matches[2];
|
||||
parse_str($matches[2], $_GET);
|
||||
$_GET = \Security::clean($_GET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deal with any trailing dots
|
||||
$uri = rtrim($uri, '.');
|
||||
|
||||
// Do we have a URI and does it not end on a slash?
|
||||
if ($uri and substr($uri, -1) !== '/')
|
||||
{
|
||||
// Strip the defined url suffix from the uri if needed
|
||||
$ext = strrchr($uri, '.');
|
||||
$path = $ext === false ? $uri : substr($uri, 0, -strlen($ext));
|
||||
|
||||
// Did we detect something that looks like an extension?
|
||||
if ( ! empty($ext))
|
||||
{
|
||||
// if it has a slash in it, it's a URI segment with a dot in it
|
||||
if (strpos($ext, '/') === false)
|
||||
{
|
||||
static::$detected_ext = ltrim($ext, '.');
|
||||
|
||||
if (\Config::get('routing.strip_extension', true))
|
||||
{
|
||||
$uri = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do some final clean up of the uri
|
||||
static::$detected_uri = \Security::clean_uri($uri, true);
|
||||
|
||||
return static::$detected_uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects and returns the current URI extension
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function extension()
|
||||
{
|
||||
static::$detected_ext === null and static::uri();
|
||||
|
||||
return static::$detected_ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the public ip address of the user.
|
||||
*
|
||||
* @param string $default
|
||||
* @return array|string
|
||||
*/
|
||||
public static function ip($default = '0.0.0.0')
|
||||
{
|
||||
return static::server('REMOTE_ADDR', $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the real ip address of the user. Even if they are using a proxy.
|
||||
*
|
||||
* @param string $default the default to return on failure
|
||||
* @param bool $exclude_reserved exclude private and reserved IPs
|
||||
* @return string the real ip address of the user
|
||||
*/
|
||||
public static function real_ip($default = '0.0.0.0', $exclude_reserved = false)
|
||||
{
|
||||
static $server_keys = null;
|
||||
|
||||
if (empty($server_keys))
|
||||
{
|
||||
$server_keys = array('HTTP_CLIENT_IP', 'REMOTE_ADDR');
|
||||
if (\Config::get('security.allow_x_headers', false))
|
||||
{
|
||||
$server_keys = array_merge(array('HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_X_FORWARDED_FOR'), $server_keys);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($server_keys as $key)
|
||||
{
|
||||
if ( ! static::server($key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$ips = explode(',', static::server($key));
|
||||
array_walk($ips, function (&$ip) {
|
||||
$ip = trim($ip);
|
||||
});
|
||||
|
||||
$ips = array_filter($ips, function($ip) use($exclude_reserved) {
|
||||
return filter_var($ip, FILTER_VALIDATE_IP, $exclude_reserved ? FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE : null);
|
||||
});
|
||||
|
||||
if ($ips)
|
||||
{
|
||||
return reset($ips);
|
||||
}
|
||||
}
|
||||
|
||||
return \Fuel::value($default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return's the protocol that the request was made with
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function protocol()
|
||||
{
|
||||
if (static::server('HTTPS') == 'on' or
|
||||
static::server('HTTPS') == 1 or
|
||||
static::server('SERVER_PORT') == 443 or
|
||||
(\Config::get('security.allow_x_headers', false) and static::server('HTTP_X_FORWARDED_PROTO') == 'https') or
|
||||
(\Config::get('security.allow_x_headers', false) and static::server('HTTP_X_FORWARDED_PORT') == 443))
|
||||
{
|
||||
return 'https';
|
||||
}
|
||||
|
||||
return 'http';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return's whether this is an AJAX request or not
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_ajax()
|
||||
{
|
||||
return (static::server('HTTP_X_REQUESTED_WITH') !== null) and strtolower(static::server('HTTP_X_REQUESTED_WITH')) === 'xmlhttprequest';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return's the referrer
|
||||
*
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public static function referrer($default = '')
|
||||
{
|
||||
return static::server('HTTP_REFERER', $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return's the input method used (GET, POST, DELETE, etc.)
|
||||
*
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public static function method($default = 'GET')
|
||||
{
|
||||
// get the method from the current active request
|
||||
if ($request = \Request::active() and $method = $request->get_method())
|
||||
{
|
||||
return $method;
|
||||
}
|
||||
|
||||
// if called before a request is active, fall back to the global server setting
|
||||
if (\Config::get('security.allow_x_headers', false))
|
||||
{
|
||||
return static::server('HTTP_X_HTTP_METHOD_OVERRIDE', static::server('REQUEST_METHOD', $default));
|
||||
}
|
||||
else
|
||||
{
|
||||
return static::server('REQUEST_METHOD', $default);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return's the user agent
|
||||
*
|
||||
* @param $default
|
||||
* @return string
|
||||
*/
|
||||
public static function user_agent($default = '')
|
||||
{
|
||||
return static::server('HTTP_USER_AGENT', $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the GET, POST, PUT and DELETE variables.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function all()
|
||||
{
|
||||
static::$input === null and static::hydrate();
|
||||
return static::$input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the specified GET variable.
|
||||
*
|
||||
* @param string $index The index to get
|
||||
* @param string $default The default value
|
||||
* @return string|array
|
||||
*/
|
||||
public static function get($index = null, $default = null)
|
||||
{
|
||||
return (func_num_args() === 0) ? $_GET : \Arr::get($_GET, $index, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an item from the POST array
|
||||
*
|
||||
* @param string $index The index key
|
||||
* @param mixed $default The default value
|
||||
* @return string|array
|
||||
*/
|
||||
public static function post($index = null, $default = null)
|
||||
{
|
||||
return (func_num_args() === 0) ? $_POST : \Arr::get($_POST, $index, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an item from the php://input for put arguments
|
||||
*
|
||||
* @param string $index The index key
|
||||
* @param mixed $default The default value
|
||||
* @return string|array
|
||||
*/
|
||||
public static function put($index = null, $default = null)
|
||||
{
|
||||
static::$put_patch_delete === null and static::hydrate();
|
||||
return (func_num_args() === 0) ? static::$put_patch_delete : \Arr::get(static::$put_patch_delete, $index, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an item from the php://input for patch arguments
|
||||
*
|
||||
* @param string $index The index key
|
||||
* @param mixed $default The default value
|
||||
* @return string|array
|
||||
*/
|
||||
public static function patch($index = null, $default = null)
|
||||
{
|
||||
static::$put_patch_delete === null and static::hydrate();
|
||||
return (func_num_args() === 0) ? static::$put_patch_delete : \Arr::get(static::$put_patch_delete, $index, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an item from the php://input for delete arguments
|
||||
*
|
||||
* @param string $index The index key
|
||||
* @param mixed $default The default value
|
||||
* @return string|array
|
||||
*/
|
||||
public static function delete($index = null, $default = null)
|
||||
{
|
||||
static::$put_patch_delete === null and static::hydrate();
|
||||
return (is_null($index) and func_num_args() === 0) ? static::$put_patch_delete : \Arr::get(static::$put_patch_delete, $index, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an item from the FILE array
|
||||
*
|
||||
* @param string $index The index key
|
||||
* @param mixed $default The default value
|
||||
* @return string|array
|
||||
*/
|
||||
public static function file($index = null, $default = null)
|
||||
{
|
||||
return (func_num_args() === 0) ? $_FILES : \Arr::get($_FILES, $index, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an item from either the GET, POST, PUT, PATCH or DELETE array
|
||||
*
|
||||
* @param string $index The index key
|
||||
* @param mixed $default The default value
|
||||
* @return string|array
|
||||
*/
|
||||
public static function param($index = null, $default = null)
|
||||
{
|
||||
static::$input === null and static::hydrate();
|
||||
return \Arr::get(static::$input, $index, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an item from the COOKIE array
|
||||
*
|
||||
* @param string $index The index key
|
||||
* @param mixed $default The default value
|
||||
* @return string|array
|
||||
*/
|
||||
public static function cookie($index = null, $default = null)
|
||||
{
|
||||
return (func_num_args() === 0) ? $_COOKIE : \Arr::get($_COOKIE, $index, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an item from the SERVER array
|
||||
*
|
||||
* @param string $index The index key
|
||||
* @param mixed $default The default value
|
||||
* @return string|array
|
||||
*/
|
||||
public static function server($index = null, $default = null)
|
||||
{
|
||||
return (func_num_args() === 0) ? $_SERVER : \Arr::get($_SERVER, strtoupper($index), $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a item from the HTTP request headers
|
||||
*
|
||||
* @param mixed $index
|
||||
* @param mixed $default
|
||||
* @return array
|
||||
*/
|
||||
public static function headers($index = null, $default = null)
|
||||
{
|
||||
static $headers = null;
|
||||
|
||||
// do we need to fetch the headers?
|
||||
if ($headers === null)
|
||||
{
|
||||
// deal with fcgi or nginx installs
|
||||
if ( ! function_exists('getallheaders'))
|
||||
{
|
||||
$server = \Arr::filter_prefixed(static::server(), 'HTTP_', true);
|
||||
|
||||
foreach ($server as $key => $value)
|
||||
{
|
||||
$key = join('-', array_map('ucfirst', explode('_', strtolower($key))));
|
||||
|
||||
$headers[$key] = $value;
|
||||
}
|
||||
|
||||
$value = static::server('Content_Type', static::server('Content-Type')) and $headers['Content-Type'] = $value;
|
||||
$value = static::server('Content_Length', static::server('Content-Length')) and $headers['Content-Length'] = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$headers = getallheaders();
|
||||
}
|
||||
}
|
||||
|
||||
return empty($headers) ? $default : ((func_num_args() === 0) ? $headers : \Arr::get(array_change_key_case($headers), strtolower($index), $default));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates the input array
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function hydrate()
|
||||
{
|
||||
static::$input = array_merge($_GET, $_POST);
|
||||
|
||||
if (static::method() == 'PUT' or static::method() == 'PATCH' or static::method() == 'DELETE')
|
||||
{
|
||||
static::$php_input === null and static::$php_input = file_get_contents('php://input');
|
||||
if (strpos(static::headers('Content-Type'), 'www-form-urlencoded') > 0 and \Config::get('security.form-double-urlencoded', false))
|
||||
{
|
||||
static::$php_input = urldecode(static::$php_input);
|
||||
}
|
||||
parse_str(static::$php_input, static::$put_patch_delete);
|
||||
static::$input = array_merge(static::$input, static::$put_patch_delete);
|
||||
}
|
||||
else
|
||||
{
|
||||
static::$put_patch_delete = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return's the query string
|
||||
*
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public static function query_string($default = '')
|
||||
{
|
||||
return static::server('QUERY_STRING', $default);
|
||||
}
|
||||
}
|
@ -1,292 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Fuel framework.
|
||||
*
|
||||
* @package Fuel
|
||||
* @version 1.8
|
||||
* @author Fuel Development Team
|
||||
* @license MIT License
|
||||
* @copyright 2010 - 2016 Fuel Development Team
|
||||
* @link http://fuelphp.com
|
||||
*/
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
class LangException extends \FuelException { }
|
||||
|
||||
class Lang
|
||||
{
|
||||
/**
|
||||
* @var array $loaded_files array of loaded files
|
||||
*/
|
||||
public static $loaded_files = array();
|
||||
|
||||
/**
|
||||
* @var array language lines
|
||||
*/
|
||||
public static $lines = array();
|
||||
|
||||
/**
|
||||
* @var array language(s) to fall back on when loading a file from the current lang fails
|
||||
*/
|
||||
public static $fallback;
|
||||
|
||||
public static function _init()
|
||||
{
|
||||
static::$fallback = (array) \Config::get('language_fallback', 'en');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns currently active language.
|
||||
*
|
||||
* @return string currently active language
|
||||
*/
|
||||
public static function get_lang()
|
||||
{
|
||||
$language = \Config::get('language');
|
||||
empty($language) and $language = static::$fallback[0];
|
||||
return $language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a language file.
|
||||
*
|
||||
* @param mixed $file string file | language array | Lang_Interface instance
|
||||
* @param mixed $group null for no group, true for group is filename, false for not storing in the master lang
|
||||
* @param string|null $language name of the language to load, null for the configured language
|
||||
* @param bool $overwrite true for array_merge, false for \Arr::merge
|
||||
* @param bool $reload true to force a reload even if the file is already loaded
|
||||
* @return array the (loaded) language array
|
||||
* @throws \FuelException
|
||||
*/
|
||||
public static function load($file, $group = null, $language = null, $overwrite = false, $reload = false)
|
||||
{
|
||||
// get the active language and all fallback languages
|
||||
$language or $language = static::get_lang();
|
||||
$languages = static::$fallback;
|
||||
|
||||
// make sure we don't have the active language in the fallback array
|
||||
if (in_array($language, $languages))
|
||||
{
|
||||
unset($languages[array_search($language, $languages)]);
|
||||
}
|
||||
|
||||
// stick the active language to the front of the list
|
||||
array_unshift($languages, $language);
|
||||
|
||||
if ( ! $reload and
|
||||
! is_array($file) and
|
||||
! is_object($file) and
|
||||
array_key_exists($language.'/'.$file, static::$loaded_files))
|
||||
{
|
||||
$group === true and $group = $file;
|
||||
if ($group === null or $group === false or ! isset(static::$lines[$language][$group]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return static::$lines[$language][$group];
|
||||
}
|
||||
|
||||
$lang = array();
|
||||
if (is_array($file))
|
||||
{
|
||||
$lang = $file;
|
||||
}
|
||||
elseif (is_string($file))
|
||||
{
|
||||
$info = pathinfo($file);
|
||||
$type = 'php';
|
||||
if (isset($info['extension']))
|
||||
{
|
||||
$type = $info['extension'];
|
||||
// Keep extension when it's an absolute path, because the finder won't add it
|
||||
if ($file[0] !== '/' and $file[1] !== ':')
|
||||
{
|
||||
$file = substr($file, 0, -(strlen($type) + 1));
|
||||
}
|
||||
}
|
||||
$class = '\\Lang_'.ucfirst($type);
|
||||
|
||||
if (class_exists($class))
|
||||
{
|
||||
static::$loaded_files[$language.'/'.$file] = func_get_args();
|
||||
$file = new $class($file, $languages);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \FuelException(sprintf('Invalid lang type "%s".', $type));
|
||||
}
|
||||
}
|
||||
|
||||
if ($file instanceof Lang_Interface)
|
||||
{
|
||||
try
|
||||
{
|
||||
$lang = $file->load($overwrite);
|
||||
}
|
||||
catch (\LangException $e)
|
||||
{
|
||||
$lang = array();
|
||||
}
|
||||
$group = $group === true ? $file->group() : $group;
|
||||
}
|
||||
|
||||
isset(static::$lines[$language]) or static::$lines[$language] = array();
|
||||
if ($group === null)
|
||||
{
|
||||
static::$lines[$language] = $overwrite ? array_merge(static::$lines[$language], $lang) : \Arr::merge(static::$lines[$language], $lang);
|
||||
}
|
||||
else
|
||||
{
|
||||
$group = ($group === true) ? $file : $group;
|
||||
if ($overwrite)
|
||||
{
|
||||
\Arr::set(static::$lines[$language], $group, array_merge(\Arr::get(static::$lines[$language], $group, array()), $lang));
|
||||
}
|
||||
else
|
||||
{
|
||||
\Arr::set(static::$lines[$language], $group, \Arr::merge(\Arr::get(static::$lines[$language], $group, array()), $lang));
|
||||
}
|
||||
}
|
||||
|
||||
return $lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a language array to disk.
|
||||
*
|
||||
* @param string $file desired file name
|
||||
* @param string|array $lang master language array key or language array
|
||||
* @param string|null $language name of the language to load, null for the configured language
|
||||
* @return bool false when language is empty or invalid else \File::update result
|
||||
* @throws \LangException
|
||||
*/
|
||||
public static function save($file, $lang, $language = null)
|
||||
{
|
||||
($language === null) and $language = static::get_lang();
|
||||
|
||||
// prefix the file with the language
|
||||
if ( ! is_null($language))
|
||||
{
|
||||
$file = explode('::', $file);
|
||||
end($file);
|
||||
$file[key($file)] = $language.DS.end($file);
|
||||
$file = implode('::', $file);
|
||||
}
|
||||
|
||||
if ( ! is_array($lang))
|
||||
{
|
||||
if ( ! isset(static::$lines[$language][$lang]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$lang = static::$lines[$language][$lang];
|
||||
}
|
||||
|
||||
$type = pathinfo($file, PATHINFO_EXTENSION);
|
||||
if( ! $type)
|
||||
{
|
||||
$type = 'php';
|
||||
$file .= '.'.$type;
|
||||
}
|
||||
|
||||
$class = '\\Lang_'.ucfirst($type);
|
||||
|
||||
if( ! class_exists($class, true))
|
||||
{
|
||||
throw new \LangException('Cannot save a language file of type: '.$type);
|
||||
}
|
||||
|
||||
$driver = new $class;
|
||||
return $driver->save($file, $lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a (dot notated) language string
|
||||
*
|
||||
* @param string $line key for the line
|
||||
* @param array $params array of params to str_replace
|
||||
* @param mixed $default default value to return
|
||||
* @param string|null $language name of the language to get, null for the configured language
|
||||
* @return mixed either the line or default when not found
|
||||
*/
|
||||
public static function get($line, array $params = array(), $default = null, $language = null)
|
||||
{
|
||||
($language === null) and $language = static::get_lang();
|
||||
|
||||
return isset(static::$lines[$language]) ? \Str::tr(\Fuel::value(\Arr::get(static::$lines[$language], $line, $default)), $params) : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a (dot notated) language string
|
||||
*
|
||||
* @param string $line a (dot notated) language key
|
||||
* @param mixed $value the language string
|
||||
* @param string $group group
|
||||
* @param string|null $language name of the language to set, null for the configured language
|
||||
* @return void the \Arr::set result
|
||||
*/
|
||||
public static function set($line, $value, $group = null, $language = null)
|
||||
{
|
||||
$group === null or $line = $group.'.'.$line;
|
||||
|
||||
($language === null) and $language = static::get_lang();
|
||||
|
||||
isset(static::$lines[$language]) or static::$lines[$language] = array();
|
||||
|
||||
\Arr::set(static::$lines[$language], $line, \Fuel::value($value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a (dot notated) language string
|
||||
*
|
||||
* @param string $item a (dot notated) language key
|
||||
* @param string $group group
|
||||
* @param string|null $language name of the language to set, null for the configured language
|
||||
* @return array|bool the \Arr::delete result, success boolean or array of success booleans
|
||||
*/
|
||||
public static function delete($item, $group = null, $language = null)
|
||||
{
|
||||
$group === null or $item = $group.'.'.$item;
|
||||
|
||||
($language === null) and $language = static::get_lang();
|
||||
|
||||
return isset(static::$lines[$language]) ? \Arr::delete(static::$lines[$language], $item) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current language, and optionally reloads all language files loaded in another language
|
||||
*
|
||||
* @param string $language name of the language to activate
|
||||
* @param bool $reload true to force a reload of already loaded language files
|
||||
* @return bool success boolean, false if no language or the current was passed, true otherwise
|
||||
*/
|
||||
public static function set_lang($language, $reload = false)
|
||||
{
|
||||
// check if a language was passedd
|
||||
if ( ! empty($language) and $language != static::get_lang())
|
||||
{
|
||||
// set it
|
||||
\Config::set('language', $language);
|
||||
|
||||
// do we need to reload?
|
||||
if ($reload)
|
||||
{
|
||||
foreach (static::$loaded_files as $file => $args)
|
||||
{
|
||||
// reload with exactly the same arguments
|
||||
if (strpos($file, $language.'/') !== 0)
|
||||
{
|
||||
call_user_func_array('Lang::load', $args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// return success
|
||||
return true;
|
||||
}
|
||||
|
||||
// no language or the current language was passed
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* DB lang data parser
|
||||
*/
|
||||
class Lang_Db implements Lang_Interface
|
||||
{
|
||||
protected $identifier;
|
||||
|
||||
protected $ext = '.db';
|
||||
|
||||
protected $languages = array();
|
||||
|
||||
protected $vars = array();
|
||||
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* Sets up the file to be parsed and variables
|
||||
*
|
||||
* @param string $identifier Lang identifier name
|
||||
* @param array $languages Languages to scan for the lang file
|
||||
* @param array $vars Variables to parse in the data retrieved
|
||||
*/
|
||||
public function __construct($identifier = null, $languages = array(), $vars = array())
|
||||
{
|
||||
$this->identifier = $identifier;
|
||||
|
||||
// we need the highest priority language last in the list
|
||||
$this->languages = array_reverse($languages);
|
||||
|
||||
$this->vars = array(
|
||||
'APPPATH' => APPPATH,
|
||||
'COREPATH' => COREPATH,
|
||||
'PKGPATH' => PKGPATH,
|
||||
'DOCROOT' => DOCROOT,
|
||||
) + $vars;
|
||||
|
||||
$this->table = \Config::get('lang.table_name', 'lang');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the language file(s).
|
||||
*
|
||||
* @param bool $overwrite Whether to overwrite existing values
|
||||
* @return array the language array
|
||||
* @throws \Database_Exception
|
||||
*/
|
||||
public function load($overwrite = false)
|
||||
{
|
||||
$lang = array();
|
||||
|
||||
foreach ($this->languages as $language)
|
||||
{
|
||||
// try to retrieve the config from the database
|
||||
try
|
||||
{
|
||||
$result = \DB::select('lang')->from($this->table)->where('identifier', '=', $this->identifier)->where('language', '=', $language)->execute();
|
||||
}
|
||||
catch (Database_Exception $e)
|
||||
{
|
||||
// strip the actual query from the message
|
||||
$msg = $e->getMessage();
|
||||
$msg = substr($msg, 0, strlen($msg) - strlen(strrchr($msg, ':')));
|
||||
|
||||
// and rethrow it
|
||||
throw new \Database_Exception($msg);
|
||||
}
|
||||
|
||||
// did we succeed?
|
||||
if ($result->count())
|
||||
{
|
||||
if ( ! empty($result[0]['lang']))
|
||||
{
|
||||
$lang = $overwrite ?
|
||||
array_merge($lang, unserialize($this->parse_vars($result[0]['lang']))) :
|
||||
\Arr::merge($lang, unserialize($this->parse_vars($result[0]['lang'])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default group name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function group()
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string using all of the previously set variables. Allows you to
|
||||
* use something like %APPPATH% in non-PHP files.
|
||||
*
|
||||
* @param string $string String to parse
|
||||
* @return string
|
||||
*/
|
||||
protected function parse_vars($string)
|
||||
{
|
||||
foreach ($this->vars as $var => $val)
|
||||
{
|
||||
$string = str_replace("%$var%", $val, $string);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces FuelPHP's path constants to their string counterparts.
|
||||
*
|
||||
* @param array $array array to be prepped
|
||||
* @return array prepped array
|
||||
*/
|
||||
protected function prep_vars(&$array)
|
||||
{
|
||||
static $replacements = false;
|
||||
|
||||
if ($replacements === false)
|
||||
{
|
||||
foreach ($this->vars as $i => $v)
|
||||
{
|
||||
$replacements['#^('.preg_quote($v).'){1}(.*)?#'] = "%".$i."%$2";
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($array as $i => $value)
|
||||
{
|
||||
if (is_string($value))
|
||||
{
|
||||
$array[$i] = preg_replace(array_keys($replacements), array_values($replacements), $value);
|
||||
}
|
||||
elseif(is_array($value))
|
||||
{
|
||||
$this->prep_vars($array[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the output and saved it to the database.
|
||||
*
|
||||
* @param string $identifier filename
|
||||
* @param $contents $contents language array to save
|
||||
* @return bool DB result
|
||||
*/
|
||||
public function save($identifier, $contents)
|
||||
{
|
||||
// get the language and the identifier
|
||||
list($language, $identifier) = explode(DS, $identifier, 2);
|
||||
$identifier = basename($identifier, '.db');
|
||||
|
||||
// prep the contents
|
||||
$this->prep_vars($contents);
|
||||
$contents = serialize($contents);
|
||||
|
||||
// update the config in the database
|
||||
$result = \DB::update($this->table)->set(array('lang' => $contents, 'hash' => uniqid()))->where('identifier', '=', $identifier)->where('language', '=', $language)->execute();
|
||||
|
||||
// if there wasn't an update, do an insert
|
||||
if ($result === 0)
|
||||
{
|
||||
list($notused, $result) = \DB::insert($this->table)->set(array('identifier' => $identifier, 'language' => $language, 'lang' => $contents, 'hash' => uniqid()))->execute();
|
||||
}
|
||||
|
||||
return $result === 1;
|
||||
}
|
||||
}
|
@ -1,210 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* A base Lang File class for File based configs.
|
||||
*/
|
||||
abstract class Lang_File implements Lang_Interface
|
||||
{
|
||||
protected $file;
|
||||
|
||||
protected $languages = array();
|
||||
|
||||
protected $vars = array();
|
||||
|
||||
/**
|
||||
* Sets up the file to be parsed and variables
|
||||
*
|
||||
* @param string $file Lang file name
|
||||
* @param array $languages Languages to scan for the lang file
|
||||
* @param array $vars Variables to parse in the file
|
||||
*/
|
||||
public function __construct($file = null, $languages = array(), $vars = array())
|
||||
{
|
||||
$this->file = $file;
|
||||
|
||||
$this->languages = $languages;
|
||||
|
||||
$this->vars = array(
|
||||
'APPPATH' => APPPATH,
|
||||
'COREPATH' => COREPATH,
|
||||
'PKGPATH' => PKGPATH,
|
||||
'DOCROOT' => DOCROOT,
|
||||
) + $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the language file(s).
|
||||
*
|
||||
* @param bool $overwrite Whether to overwrite existing values
|
||||
* @return array the language array
|
||||
*/
|
||||
public function load($overwrite = false)
|
||||
{
|
||||
$paths = $this->find_file();
|
||||
|
||||
$lang = array();
|
||||
|
||||
foreach ($paths as $path)
|
||||
{
|
||||
$lang = $overwrite ?
|
||||
array_merge($lang, $this->load_file($path)) :
|
||||
\Arr::merge($lang, $this->load_file($path));
|
||||
}
|
||||
|
||||
return $lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default group name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function group()
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string using all of the previously set variables. Allows you to
|
||||
* use something like %APPPATH% in non-PHP files.
|
||||
*
|
||||
* @param string $string String to parse
|
||||
* @return string
|
||||
*/
|
||||
protected function parse_vars($string)
|
||||
{
|
||||
foreach ($this->vars as $var => $val)
|
||||
{
|
||||
$string = str_replace("%$var%", $val, $string);
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces FuelPHP's path constants to their string counterparts.
|
||||
*
|
||||
* @param array $array array to be prepped
|
||||
* @return array prepped array
|
||||
*/
|
||||
protected function prep_vars(&$array)
|
||||
{
|
||||
static $replacements = false;
|
||||
|
||||
if ($replacements === false)
|
||||
{
|
||||
foreach ($this->vars as $i => $v)
|
||||
{
|
||||
$replacements['#^('.preg_quote($v).'){1}(.*)?#'] = "%".$i."%$2";
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($array as $i => $value)
|
||||
{
|
||||
if (is_string($value))
|
||||
{
|
||||
$array[$i] = preg_replace(array_keys($replacements), array_values($replacements), $value);
|
||||
}
|
||||
elseif(is_array($value))
|
||||
{
|
||||
$this->prep_vars($array[$i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the given language files
|
||||
*
|
||||
* @return array
|
||||
* @throws \LangException
|
||||
*/
|
||||
protected function find_file()
|
||||
{
|
||||
$paths = array();
|
||||
foreach ($this->languages as $lang)
|
||||
{
|
||||
$paths = array_merge($paths, \Finder::search('lang'.DS.$lang, $this->file, $this->ext, true));
|
||||
}
|
||||
|
||||
if (empty($paths))
|
||||
{
|
||||
throw new \LangException(sprintf('File "%s" does not exist.', $this->file));
|
||||
}
|
||||
|
||||
return array_reverse($paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the output and saved it to disc.
|
||||
*
|
||||
* @param string $identifier filename
|
||||
* @param $contents $contents language array to save
|
||||
* @return bool \File::update result
|
||||
*/
|
||||
public function save($identifier, $contents)
|
||||
{
|
||||
// get the formatted output
|
||||
$output = $this->export_format($contents);
|
||||
|
||||
if ( ! $output)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! $path = \Finder::search('lang', $identifier))
|
||||
{
|
||||
if ($pos = strripos($identifier, '::'))
|
||||
{
|
||||
// get the namespace path
|
||||
if ($path = \Autoloader::namespace_path('\\'.ucfirst(substr($identifier, 0, $pos))))
|
||||
{
|
||||
// strip the namespace from the filename
|
||||
$identifier = substr($identifier, $pos+2);
|
||||
|
||||
// strip the classes directory as we need the module root
|
||||
$path = substr($path, 0, -8).'lang'.DS.$identifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
// invalid namespace requested
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// absolute path requested?
|
||||
if ($identifier[0] === '/' or (isset($identifier[1]) and $identifier[1] === ':'))
|
||||
{
|
||||
$path = $identifier;
|
||||
}
|
||||
|
||||
// make sure we have a fallback
|
||||
$path or $path = APPPATH.'lang'.DS.$identifier;
|
||||
|
||||
$path = pathinfo($path);
|
||||
if ( ! is_dir($path['dirname']))
|
||||
{
|
||||
mkdir($path['dirname'], 0777, true);
|
||||
}
|
||||
|
||||
return \File::update($path['dirname'], $path['basename'], $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Must be implemented by child class. Gets called for each file to load.
|
||||
*
|
||||
* @param string $file File to load
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function load_file($file);
|
||||
|
||||
/**
|
||||
* Must be impletmented by child class. Gets called when saving a language file.
|
||||
*
|
||||
* @param array $contents language array to save
|
||||
* @return string formatted output
|
||||
*/
|
||||
abstract protected function export_format($contents);
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* INI Lang file parser
|
||||
*/
|
||||
class Lang_Ini extends \Lang_File
|
||||
{
|
||||
protected $ext = '.ini';
|
||||
|
||||
/**
|
||||
* Loads in the given file and parses it.
|
||||
*
|
||||
* @param string $file File to load
|
||||
* @return array
|
||||
*/
|
||||
protected function load_file($file)
|
||||
{
|
||||
$contents = $this->parse_vars(file_get_contents($file));
|
||||
return parse_ini_string($contents, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatted language file contents.
|
||||
*
|
||||
* @param array $contents language array
|
||||
* @return string formatted language file contents
|
||||
* @throws \LangException
|
||||
*/
|
||||
protected function export_format($contents)
|
||||
{
|
||||
throw new \LangException('Saving lang to ini is not supported at this time');
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
interface Lang_Interface
|
||||
{
|
||||
public function load($overwrite = false);
|
||||
public function group();
|
||||
public function save($identifier, $contents);
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* JSON Lang file parser
|
||||
*/
|
||||
class Lang_Json extends \Lang_File
|
||||
{
|
||||
protected $ext = '.json';
|
||||
|
||||
/**
|
||||
* Loads in the given file and parses it.
|
||||
*
|
||||
* @param string $file File to load
|
||||
* @return array
|
||||
*/
|
||||
protected function load_file($file)
|
||||
{
|
||||
$contents = $this->parse_vars(file_get_contents($file));
|
||||
return json_decode($contents, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatted language file contents.
|
||||
*
|
||||
* @param array $contents config array
|
||||
* @return string formatted config file contents
|
||||
*/
|
||||
protected function export_format($contents)
|
||||
{
|
||||
$this->prep_vars($contents);
|
||||
return \Format::forge()->to_json($contents, true);
|
||||
}
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* PHP Lang file parser
|
||||
*/
|
||||
class Lang_Php extends \Lang_File
|
||||
{
|
||||
/**
|
||||
* @var bool whether or not opcache is in use
|
||||
*/
|
||||
protected static $uses_opcache = false;
|
||||
|
||||
/**
|
||||
* @var bool whether or not APC is in use
|
||||
*/
|
||||
protected static $uses_apc = false;
|
||||
|
||||
/**
|
||||
* @var bool whether or not we need to flush the opcode cache after a save
|
||||
*/
|
||||
protected static $flush_needed = false;
|
||||
|
||||
/**
|
||||
* check the status of any opcache mechanism in use
|
||||
*/
|
||||
public static function _init()
|
||||
{
|
||||
// do we have Opcache active?
|
||||
static::$uses_opcache = (PHP_VERSION_ID >= 50500 and function_exists('opcache_invalidate'));
|
||||
|
||||
// do we have APC active?
|
||||
static::$uses_apc = function_exists('apc_compile_file');
|
||||
|
||||
// determine if we have an opcode cache active
|
||||
static::$flush_needed = static::$uses_opcache or static::$uses_apc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string the extension used by this config file parser
|
||||
*/
|
||||
protected $ext = '.php';
|
||||
|
||||
/**
|
||||
* Formats the output and saved it to disc.
|
||||
*
|
||||
* @param string $identifier filename
|
||||
* @param $contents $contents language array to save
|
||||
* @return bool \File::update result
|
||||
*/
|
||||
public function save($identifier, $contents)
|
||||
{
|
||||
// store the current filename
|
||||
$file = $this->file;
|
||||
|
||||
// save it
|
||||
$return = parent::save($identifier, $contents);
|
||||
|
||||
// existing file? saved? and do we need to flush the opcode cache?
|
||||
if ($file == $this->file and $return and static::$flush_needed)
|
||||
{
|
||||
if ($this->file[0] !== '/' and ( ! isset($this->file[1]) or $this->file[1] !== ':'))
|
||||
{
|
||||
// locate the file
|
||||
if ($pos = strripos($identifier, '::'))
|
||||
{
|
||||
// get the namespace path
|
||||
if ($file = \Autoloader::namespace_path('\\'.ucfirst(substr($identifier, 0, $pos))))
|
||||
{
|
||||
// strip the namespace from the filename
|
||||
$identifier = substr($identifier, $pos+2);
|
||||
|
||||
// strip the classes directory as we need the module root
|
||||
$file = substr($file, 0, -8).'lang'.DS.$identifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
// invalid namespace requested
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$file = \Finder::search('lang', $identifier);
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we have a fallback
|
||||
$file or $file = APPPATH.'lang'.DS.$identifier;
|
||||
|
||||
// flush the opcode caches that are active
|
||||
static::$uses_opcache and opcache_invalidate($file, true);
|
||||
static::$uses_apc and apc_compile_file($file);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads in the given file and parses it.
|
||||
*
|
||||
* @param string $file File to load
|
||||
* @return array
|
||||
*/
|
||||
protected function load_file($file)
|
||||
{
|
||||
return \Fuel::load($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatted language file contents.
|
||||
*
|
||||
* @param array $contents config array
|
||||
* @return string formatted config file contents
|
||||
*/
|
||||
protected function export_format($contents)
|
||||
{
|
||||
$output = <<<CONF
|
||||
<?php
|
||||
|
||||
CONF;
|
||||
$output .= 'return '.str_replace(array(' ', 'array (', '\''.APPPATH, '\''.DOCROOT, '\''.COREPATH, '\''.PKGPATH), array("\t", 'array(', 'APPPATH.\'', 'DOCROOT.\'', 'COREPATH.\'', 'PKGPATH.\''), var_export($contents, true)).";\n";
|
||||
return $output;
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Fuel\Core;
|
||||
|
||||
/**
|
||||
* Yaml Lang file parser
|
||||
*/
|
||||
class Lang_Yml extends \Lang_File
|
||||
{
|
||||
protected $ext = '.yml';
|
||||
|
||||
/**
|
||||
* Loads in the given file and parses it.
|
||||
*
|
||||
* @param string $file File to load
|
||||
* @return array
|
||||
*/
|
||||
protected function load_file($file)
|
||||
{
|
||||
$contents = $this->parse_vars(file_get_contents($file));
|
||||
return \Format::forge($contents, 'yaml')->to_array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the formatted language file contents.
|
||||
*
|
||||
* @param array $contents config array
|
||||
* @return string formatted config file contents
|
||||
*/
|
||||
protected function export_format($contents)
|
||||
{
|
||||
if ( ! function_exists('spyc_load'))
|
||||
{
|
||||
import('spyc/spyc', 'vendor');
|
||||
}
|
||||
|
||||
$this->prep_vars($contents);
|
||||
return \Spyc::YAMLDump($contents);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue