summaryrefslogtreecommitdiff
path: root/system/helpers
diff options
context:
space:
mode:
authorBharat Mediratta <bharat@menalto.com>2009-05-27 15:11:53 -0700
committerBharat Mediratta <bharat@menalto.com>2009-05-27 15:11:53 -0700
commit12fe58d997d2066dc362fd393a18b4e5da190513 (patch)
tree3ad8e5afb77829e1541ec96d86785760d65c04ac /system/helpers
parent00f47d4ddddcd1902db817018dd79ac01bcc8e82 (diff)
Rename 'kohana' to 'system' to conform to the Kohana filesystem layout. I'm comfortable with us not clearly drawing the distinction about the fact that it's Kohana.
Diffstat (limited to 'system/helpers')
-rw-r--r--system/helpers/arr.php312
-rw-r--r--system/helpers/cookie.php84
-rw-r--r--system/helpers/date.php405
-rw-r--r--system/helpers/download.php105
-rw-r--r--system/helpers/email.php181
-rw-r--r--system/helpers/expires.php111
-rw-r--r--system/helpers/feed.php122
-rw-r--r--system/helpers/file.php186
-rw-r--r--system/helpers/form.php542
-rw-r--r--system/helpers/format.php66
-rw-r--r--system/helpers/html.php440
-rw-r--r--system/helpers/inflector.php193
-rw-r--r--system/helpers/num.php26
-rw-r--r--system/helpers/remote.php66
-rw-r--r--system/helpers/request.php239
-rw-r--r--system/helpers/security.php47
-rw-r--r--system/helpers/text.php410
-rw-r--r--system/helpers/upload.php162
-rw-r--r--system/helpers/url.php252
-rw-r--r--system/helpers/valid.php338
20 files changed, 4287 insertions, 0 deletions
diff --git a/system/helpers/arr.php b/system/helpers/arr.php
new file mode 100644
index 00000000..9570c4b5
--- /dev/null
+++ b/system/helpers/arr.php
@@ -0,0 +1,312 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Array helper class.
+ *
+ * $Id: arr.php 4346 2009-05-11 17:08:15Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class arr_Core {
+
+ /**
+ * Return a callback array from a string, eg: limit[10,20] would become
+ * array('limit', array('10', '20'))
+ *
+ * @param string callback string
+ * @return array
+ */
+ public static function callback_string($str)
+ {
+ // command[param,param]
+ if (preg_match('/([^\[]*+)\[(.+)\]/', (string) $str, $match))
+ {
+ // command
+ $command = $match[1];
+
+ // param,param
+ $params = preg_split('/(?<!\\\\),/', $match[2]);
+ $params = str_replace('\,', ',', $params);
+ }
+ else
+ {
+ // command
+ $command = $str;
+
+ // No params
+ $params = NULL;
+ }
+
+ return array($command, $params);
+ }
+
+ /**
+ * Rotates a 2D array clockwise.
+ * Example, turns a 2x3 array into a 3x2 array.
+ *
+ * @param array array to rotate
+ * @param boolean keep the keys in the final rotated array. the sub arrays of the source array need to have the same key values.
+ * if your subkeys might not match, you need to pass FALSE here!
+ * @return array
+ */
+ public static function rotate($source_array, $keep_keys = TRUE)
+ {
+ $new_array = array();
+ foreach ($source_array as $key => $value)
+ {
+ $value = ($keep_keys === TRUE) ? $value : array_values($value);
+ foreach ($value as $k => $v)
+ {
+ $new_array[$k][$key] = $v;
+ }
+ }
+
+ return $new_array;
+ }
+
+ /**
+ * Removes a key from an array and returns the value.
+ *
+ * @param string key to return
+ * @param array array to work on
+ * @return mixed value of the requested array key
+ */
+ public static function remove($key, & $array)
+ {
+ if ( ! array_key_exists($key, $array))
+ return NULL;
+
+ $val = $array[$key];
+ unset($array[$key]);
+
+ return $val;
+ }
+
+
+ /**
+ * Extract one or more keys from an array. Each key given after the first
+ * argument (the array) will be extracted. Keys that do not exist in the
+ * search array will be NULL in the extracted data.
+ *
+ * @param array array to search
+ * @param string key name
+ * @return array
+ */
+ public static function extract(array $search, $keys)
+ {
+ // Get the keys, removing the $search array
+ $keys = array_slice(func_get_args(), 1);
+
+ $found = array();
+ foreach ($keys as $key)
+ {
+ if (isset($search[$key]))
+ {
+ $found[$key] = $search[$key];
+ }
+ else
+ {
+ $found[$key] = NULL;
+ }
+ }
+
+ return $found;
+ }
+
+ /**
+ * Because PHP does not have this function.
+ *
+ * @param array array to unshift
+ * @param string key to unshift
+ * @param mixed value to unshift
+ * @return array
+ */
+ public static function unshift_assoc( array & $array, $key, $val)
+ {
+ $array = array_reverse($array, TRUE);
+ $array[$key] = $val;
+ $array = array_reverse($array, TRUE);
+
+ return $array;
+ }
+
+ /**
+ * Because PHP does not have this function, and array_walk_recursive creates
+ * references in arrays and is not truly recursive.
+ *
+ * @param mixed callback to apply to each member of the array
+ * @param array array to map to
+ * @return array
+ */
+ public static function map_recursive($callback, array $array)
+ {
+ foreach ($array as $key => $val)
+ {
+ // Map the callback to the key
+ $array[$key] = is_array($val) ? arr::map_recursive($callback, $val) : call_user_func($callback, $val);
+ }
+
+ return $array;
+ }
+
+ /**
+ * @param mixed $needle the value to search for
+ * @param array $haystack an array of values to search in
+ * @param boolean $sort sort the array now
+ * @return integer|FALSE the index of the match or FALSE when not found
+ */
+ public static function binary_search($needle, $haystack, $sort = FALSE)
+ {
+ if ($sort)
+ {
+ sort($haystack);
+ }
+
+ $high = count($haystack) - 1;
+ $low = 0;
+
+ while ($low <= $high)
+ {
+ $mid = ($low + $high) >> 1;
+
+ if ($haystack[$mid] < $needle)
+ {
+ $low = $mid + 1;
+ }
+ elseif ($haystack[$mid] > $needle)
+ {
+ $high = $mid - 1;
+ }
+ else
+ {
+ return $mid;
+ }
+ }
+
+ return FALSE;
+ }
+
+
+ /**
+ * Emulates array_merge_recursive, but appends numeric keys and replaces
+ * associative keys, instead of appending all keys.
+ *
+ * @param array any number of arrays
+ * @return array
+ */
+ public static function merge()
+ {
+ $total = func_num_args();
+
+ $result = array();
+ for ($i = 0; $i < $total; $i++)
+ {
+ foreach (func_get_arg($i) as $key => $val)
+ {
+ if (isset($result[$key]))
+ {
+ if (is_array($val))
+ {
+ // Arrays are merged recursively
+ $result[$key] = arr::merge($result[$key], $val);
+ }
+ elseif (is_int($key))
+ {
+ // Indexed arrays are appended
+ array_push($result, $val);
+ }
+ else
+ {
+ // Associative arrays are replaced
+ $result[$key] = $val;
+ }
+ }
+ else
+ {
+ // New values are added
+ $result[$key] = $val;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Overwrites an array with values from input array(s).
+ * Non-existing keys will not be appended!
+ *
+ * @param array key array
+ * @param array input array(s) that will overwrite key array values
+ * @return array
+ */
+ public static function overwrite($array1, $array2)
+ {
+ foreach (array_intersect_key($array2, $array1) as $key => $value)
+ {
+ $array1[$key] = $value;
+ }
+
+ if (func_num_args() > 2)
+ {
+ foreach (array_slice(func_get_args(), 2) as $array2)
+ {
+ foreach (array_intersect_key($array2, $array1) as $key => $value)
+ {
+ $array1[$key] = $value;
+ }
+ }
+ }
+
+ return $array1;
+ }
+
+ /**
+ * Fill an array with a range of numbers.
+ *
+ * @param integer stepping
+ * @param integer ending number
+ * @return array
+ */
+ public static function range($step = 10, $max = 100)
+ {
+ if ($step < 1)
+ return array();
+
+ $array = array();
+ for ($i = $step; $i <= $max; $i += $step)
+ {
+ $array[$i] = $i;
+ }
+
+ return $array;
+ }
+
+ /**
+ * Recursively convert an array to an object.
+ *
+ * @param array array to convert
+ * @return object
+ */
+ public static function to_object(array $array, $class = 'stdClass')
+ {
+ $object = new $class;
+
+ foreach ($array as $key => $value)
+ {
+ if (is_array($value))
+ {
+ // Convert the array to an object
+ $value = arr::to_object($value, $class);
+ }
+
+ // Add the value to the object
+ $object->{$key} = $value;
+ }
+
+ return $object;
+ }
+
+} // End arr
diff --git a/system/helpers/cookie.php b/system/helpers/cookie.php
new file mode 100644
index 00000000..901b6d86
--- /dev/null
+++ b/system/helpers/cookie.php
@@ -0,0 +1,84 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Cookie helper class.
+ *
+ * $Id: cookie.php 3769 2008-12-15 00:48:56Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class cookie_Core {
+
+ /**
+ * Sets a cookie with the given parameters.
+ *
+ * @param string cookie name or array of config options
+ * @param string cookie value
+ * @param integer number of seconds before the cookie expires
+ * @param string URL path to allow
+ * @param string URL domain to allow
+ * @param boolean HTTPS only
+ * @param boolean HTTP only (requires PHP 5.2 or higher)
+ * @return boolean
+ */
+ public static function set($name, $value = NULL, $expire = NULL, $path = NULL, $domain = NULL, $secure = NULL, $httponly = NULL)
+ {
+ if (headers_sent())
+ return FALSE;
+
+ // If the name param is an array, we import it
+ is_array($name) and extract($name, EXTR_OVERWRITE);
+
+ // Fetch default options
+ $config = Kohana::config('cookie');
+
+ foreach (array('value', 'expire', 'domain', 'path', 'secure', 'httponly') as $item)
+ {
+ if ($$item === NULL AND isset($config[$item]))
+ {
+ $$item = $config[$item];
+ }
+ }
+
+ // Expiration timestamp
+ $expire = ($expire == 0) ? 0 : time() + (int) $expire;
+
+ return setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
+ }
+
+ /**
+ * Fetch a cookie value, using the Input library.
+ *
+ * @param string cookie name
+ * @param mixed default value
+ * @param boolean use XSS cleaning on the value
+ * @return string
+ */
+ public static function get($name, $default = NULL, $xss_clean = FALSE)
+ {
+ return Input::instance()->cookie($name, $default, $xss_clean);
+ }
+
+ /**
+ * Nullify and unset a cookie.
+ *
+ * @param string cookie name
+ * @param string URL path
+ * @param string URL domain
+ * @return boolean
+ */
+ public static function delete($name, $path = NULL, $domain = NULL)
+ {
+ if ( ! isset($_COOKIE[$name]))
+ return FALSE;
+
+ // Delete the cookie from globals
+ unset($_COOKIE[$name]);
+
+ // Sets the cookie value to an empty string, and the expiration to 24 hours ago
+ return cookie::set($name, '', -86400, $path, $domain, FALSE, FALSE);
+ }
+
+} // End cookie \ No newline at end of file
diff --git a/system/helpers/date.php b/system/helpers/date.php
new file mode 100644
index 00000000..7d5a9ab6
--- /dev/null
+++ b/system/helpers/date.php
@@ -0,0 +1,405 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Date helper class.
+ *
+ * $Id: date.php 4316 2009-05-04 01:03:54Z kiall $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class date_Core {
+
+ /**
+ * Converts a UNIX timestamp to DOS format.
+ *
+ * @param integer UNIX timestamp
+ * @return integer
+ */
+ public static function unix2dos($timestamp = FALSE)
+ {
+ $timestamp = ($timestamp === FALSE) ? getdate() : getdate($timestamp);
+
+ if ($timestamp['year'] < 1980)
+ {
+ return (1 << 21 | 1 << 16);
+ }
+
+ $timestamp['year'] -= 1980;
+
+ // What voodoo is this? I have no idea... Geert can explain it though,
+ // and that's good enough for me.
+ return ($timestamp['year'] << 25 | $timestamp['mon'] << 21 |
+ $timestamp['mday'] << 16 | $timestamp['hours'] << 11 |
+ $timestamp['minutes'] << 5 | $timestamp['seconds'] >> 1);
+ }
+
+ /**
+ * Converts a DOS timestamp to UNIX format.
+ *
+ * @param integer DOS timestamp
+ * @return integer
+ */
+ public static function dos2unix($timestamp = FALSE)
+ {
+ $sec = 2 * ($timestamp & 0x1f);
+ $min = ($timestamp >> 5) & 0x3f;
+ $hrs = ($timestamp >> 11) & 0x1f;
+ $day = ($timestamp >> 16) & 0x1f;
+ $mon = ($timestamp >> 21) & 0x0f;
+ $year = ($timestamp >> 25) & 0x7f;
+
+ return mktime($hrs, $min, $sec, $mon, $day, $year + 1980);
+ }
+
+ /**
+ * Returns the offset (in seconds) between two time zones.
+ * @see http://php.net/timezones
+ *
+ * @param string timezone that to find the offset of
+ * @param string|boolean timezone used as the baseline
+ * @return integer
+ */
+ public static function offset($remote, $local = TRUE)
+ {
+ static $offsets;
+
+ // Default values
+ $remote = (string) $remote;
+ $local = ($local === TRUE) ? date_default_timezone_get() : (string) $local;
+
+ // Cache key name
+ $cache = $remote.$local;
+
+ if (empty($offsets[$cache]))
+ {
+ // Create timezone objects
+ $remote = new DateTimeZone($remote);
+ $local = new DateTimeZone($local);
+
+ // Create date objects from timezones
+ $time_there = new DateTime('now', $remote);
+ $time_here = new DateTime('now', $local);
+
+ // Find the offset
+ $offsets[$cache] = $remote->getOffset($time_there) - $local->getOffset($time_here);
+ }
+
+ return $offsets[$cache];
+ }
+
+ /**
+ * Number of seconds in a minute, incrementing by a step.
+ *
+ * @param integer amount to increment each step by, 1 to 30
+ * @param integer start value
+ * @param integer end value
+ * @return array A mirrored (foo => foo) array from 1-60.
+ */
+ public static function seconds($step = 1, $start = 0, $end = 60)
+ {
+ // Always integer
+ $step = (int) $step;
+
+ $seconds = array();
+
+ for ($i = $start; $i < $end; $i += $step)
+ {
+ $seconds[$i] = ($i < 10) ? '0'.$i : $i;
+ }
+
+ return $seconds;
+ }
+
+ /**
+ * Number of minutes in an hour, incrementing by a step.
+ *
+ * @param integer amount to increment each step by, 1 to 30
+ * @return array A mirrored (foo => foo) array from 1-60.
+ */
+ public static function minutes($step = 5)
+ {
+ // Because there are the same number of minutes as seconds in this set,
+ // we choose to re-use seconds(), rather than creating an entirely new
+ // function. Shhhh, it's cheating! ;) There are several more of these
+ // in the following methods.
+ return date::seconds($step);
+ }
+
+ /**
+ * Number of hours in a day.
+ *
+ * @param integer amount to increment each step by
+ * @param boolean use 24-hour time
+ * @param integer the hour to start at
+ * @return array A mirrored (foo => foo) array from start-12 or start-23.
+ */
+ public static function hours($step = 1, $long = FALSE, $start = NULL)
+ {
+ // Default values
+ $step = (int) $step;
+ $long = (bool) $long;
+ $hours = array();
+
+ // Set the default start if none was specified.
+ if ($start === NULL)
+ {
+ $start = ($long === FALSE) ? 1 : 0;
+ }
+
+ $hours = array();
+
+ // 24-hour time has 24 hours, instead of 12
+ $size = ($long === TRUE) ? 23 : 12;
+
+ for ($i = $start; $i <= $size; $i += $step)
+ {
+ $hours[$i] = $i;
+ }
+
+ return $hours;
+ }
+
+ /**
+ * Returns AM or PM, based on a given hour.
+ *
+ * @param integer number of the hour
+ * @return string
+ */
+ public static function ampm($hour)
+ {
+ // Always integer
+ $hour = (int) $hour;
+
+ return ($hour > 11) ? 'PM' : 'AM';
+ }
+
+ /**
+ * Adjusts a non-24-hour number into a 24-hour number.
+ *
+ * @param integer hour to adjust
+ * @param string AM or PM
+ * @return string
+ */
+ public static function adjust($hour, $ampm)
+ {
+ $hour = (int) $hour;
+ $ampm = strtolower($ampm);
+
+ switch ($ampm)
+ {
+ case 'am':
+ if ($hour == 12)
+ $hour = 0;
+ break;
+ case 'pm':
+ if ($hour < 12)
+ $hour += 12;
+ break;
+ }
+
+ return sprintf('%02s', $hour);
+ }
+
+ /**
+ * Number of days in month.
+ *
+ * @param integer number of month
+ * @param integer number of year to check month, defaults to the current year
+ * @return array A mirrored (foo => foo) array of the days.
+ */
+ public static function days($month, $year = FALSE)
+ {
+ static $months;
+
+ // Always integers
+ $month = (int) $month;
+ $year = (int) $year;
+
+ // Use the current year by default
+ $year = ($year == FALSE) ? date('Y') : $year;
+
+ // We use caching for months, because time functions are used
+ if (empty($months[$year][$month]))
+ {
+ $months[$year][$month] = array();
+
+ // Use date to find the number of days in the given month
+ $total = date('t', mktime(1, 0, 0, $month, 1, $year)) + 1;
+
+ for ($i = 1; $i < $total; $i++)
+ {
+ $months[$year][$month][$i] = $i;
+ }
+ }
+
+ return $months[$year][$month];
+ }
+
+ /**
+ * Number of months in a year
+ *
+ * @return array A mirrored (foo => foo) array from 1-12.
+ */
+ public static function months()
+ {
+ return date::hours();
+ }
+
+ /**
+ * Returns an array of years between a starting and ending year.
+ * Uses the current year +/- 5 as the max/min.
+ *
+ * @param integer starting year
+ * @param integer ending year
+ * @return array
+ */
+ public static function years($start = FALSE, $end = FALSE)
+ {
+ // Default values
+ $start = ($start === FALSE) ? date('Y') - 5 : (int) $start;
+ $end = ($end === FALSE) ? date('Y') + 5 : (int) $end;
+
+ $years = array();
+
+ // Add one, so that "less than" works
+ $end += 1;
+
+ for ($i = $start; $i < $end; $i++)
+ {
+ $years[$i] = $i;
+ }
+
+ return $years;
+ }
+
+ /**
+ * Returns time difference between two timestamps, in human readable format.
+ *
+ * @param integer timestamp
+ * @param integer timestamp, defaults to the current time
+ * @param string formatting string
+ * @return string|array
+ */
+ public static function timespan($time1, $time2 = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
+ {
+ // Array with the output formats
+ $output = preg_split('/[^a-z]+/', strtolower((string) $output));
+
+ // Invalid output
+ if (empty($output))
+ return FALSE;
+
+ // Make the output values into keys
+ extract(array_flip($output), EXTR_SKIP);
+
+ // Default values
+ $time1 = max(0, (int) $time1);
+ $time2 = empty($time2) ? time() : max(0, (int) $time2);
+
+ // Calculate timespan (seconds)
+ $timespan = abs($time1 - $time2);
+
+ // All values found using Google Calculator.
+ // Years and months do not match the formula exactly, due to leap years.
+
+ // Years ago, 60 * 60 * 24 * 365
+ isset($years) and $timespan -= 31556926 * ($years = (int) floor($timespan / 31556926));
+
+ // Months ago, 60 * 60 * 24 * 30
+ isset($months) and $timespan -= 2629744 * ($months = (int) floor($timespan / 2629743.83));
+
+ // Weeks ago, 60 * 60 * 24 * 7
+ isset($weeks) and $timespan -= 604800 * ($weeks = (int) floor($timespan / 604800));
+
+ // Days ago, 60 * 60 * 24
+ isset($days) and $timespan -= 86400 * ($days = (int) floor($timespan / 86400));
+
+ // Hours ago, 60 * 60
+ isset($hours) and $timespan -= 3600 * ($hours = (int) floor($timespan / 3600));
+
+ // Minutes ago, 60
+ isset($minutes) and $timespan -= 60 * ($minutes = (int) floor($timespan / 60));
+
+ // Seconds ago, 1
+ isset($seconds) and $seconds = $timespan;
+
+ // Remove the variables that cannot be accessed
+ unset($timespan, $time1, $time2);
+
+ // Deny access to these variables
+ $deny = array_flip(array('deny', 'key', 'difference', 'output'));
+
+ // Return the difference
+ $difference = array();
+ foreach ($output as $key)
+ {
+ if (isset($$key) AND ! isset($deny[$key]))
+ {
+ // Add requested key to the output
+ $difference[$key] = $$key;
+ }
+ }
+
+ // Invalid output formats string
+ if (empty($difference))
+ return FALSE;
+
+ // If only one output format was asked, don't put it in an array
+ if (count($difference) === 1)
+ return current($difference);
+
+ // Return array
+ return $difference;
+ }
+
+ /**
+ * Returns time difference between two timestamps, in the format:
+ * N year, N months, N weeks, N days, N hours, N minutes, and N seconds ago
+ *
+ * @param integer timestamp
+ * @param integer timestamp, defaults to the current time
+ * @param string formatting string
+ * @return string
+ */
+ public static function timespan_string($time1, $time2 = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
+ {
+ if ($difference = date::timespan($time1, $time2, $output) AND is_array($difference))
+ {
+ // Determine the key of the last item in the array
+ $last = end($difference);
+ $last = key($difference);
+
+ $span = array();
+ foreach ($difference as $name => $amount)
+ {
+ if ($amount === 0)
+ {
+ // Skip empty amounts
+ continue;
+ }
+
+ // Add the amount to the span
+ $span[] = ($name === $last ? ' and ' : ', ').$amount.' '.($amount === 1 ? inflector::singular($name) : $name);
+ }
+
+ // If the difference is less than 60 seconds, remove the preceding and.
+ if (count($span) === 1)
+ {
+ $span[0] = ltrim($span[0], 'and ');
+ }
+
+ // Replace difference by making the span into a string
+ $difference = trim(implode('', $span), ',');
+ }
+ elseif (is_int($difference))
+ {
+ // Single-value return
+ $difference = $difference.' '.($difference === 1 ? inflector::singular($output) : $output);
+ }
+
+ return $difference;
+ }
+
+} // End date \ No newline at end of file
diff --git a/system/helpers/download.php b/system/helpers/download.php
new file mode 100644
index 00000000..49fed42c
--- /dev/null
+++ b/system/helpers/download.php
@@ -0,0 +1,105 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Download helper class.
+ *
+ * $Id: download.php 3769 2008-12-15 00:48:56Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class download_Core {
+
+ /**
+ * Force a download of a file to the user's browser. This function is
+ * binary-safe and will work with any MIME type that Kohana is aware of.
+ *
+ * @param string a file path or file name
+ * @param mixed data to be sent if the filename does not exist
+ * @param string suggested filename to display in the download
+ * @return void
+ */
+ public static function force($filename = NULL, $data = NULL, $nicename = NULL)
+ {
+ if (empty($filename))
+ return FALSE;
+
+ if (is_file($filename))
+ {
+ // Get the real path
+ $filepath = str_replace('\\', '/', realpath($filename));
+
+ // Set filesize
+ $filesize = filesize($filepath);
+
+ // Get filename
+ $filename = substr(strrchr('/'.$filepath, '/'), 1);
+
+ // Get extension
+ $extension = strtolower(substr(strrchr($filepath, '.'), 1));
+ }
+ else
+ {
+ // Get filesize
+ $filesize = strlen($data);
+
+ // Make sure the filename does not have directory info
+ $filename = substr(strrchr('/'.$filename, '/'), 1);
+
+ // Get extension
+ $extension = strtolower(substr(strrchr($filename, '.'), 1));
+ }
+
+ // Get the mime type of the file
+ $mime = Kohana::config('mimes.'.$extension);
+
+ if (empty($mime))
+ {
+ // Set a default mime if none was found
+ $mime = array('application/octet-stream');
+ }
+
+ // Generate the server headers
+ header('Content-Type: '.$mime[0]);
+ header('Content-Disposition: attachment; filename="'.(empty($nicename) ? $filename : $nicename).'"');
+ header('Content-Transfer-Encoding: binary');
+ header('Content-Length: '.sprintf('%d', $filesize));
+
+ // More caching prevention
+ header('Expires: 0');
+
+ if (Kohana::user_agent('browser') === 'Internet Explorer')
+ {
+ // Send IE headers
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Pragma: public');
+ }
+ else
+ {
+ // Send normal headers
+ header('Pragma: no-cache');
+ }
+
+ // Clear the output buffer
+ Kohana::close_buffers(FALSE);
+
+ if (isset($filepath))
+ {
+ // Open the file
+ $handle = fopen($filepath, 'rb');
+
+ // Send the file data
+ fpassthru($handle);
+
+ // Close the file
+ fclose($handle);
+ }
+ else
+ {
+ // Send the file data
+ echo $data;
+ }
+ }
+
+} // End download \ No newline at end of file
diff --git a/system/helpers/email.php b/system/helpers/email.php
new file mode 100644
index 00000000..fb222d0c
--- /dev/null
+++ b/system/helpers/email.php
@@ -0,0 +1,181 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Email helper class.
+ *
+ * $Id: email.php 3769 2008-12-15 00:48:56Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class email_Core {
+
+ // SwiftMailer instance
+ protected static $mail;
+
+ /**
+ * Creates a SwiftMailer instance.
+ *
+ * @param string DSN connection string
+ * @return object Swift object
+ */
+ public static function connect($config = NULL)
+ {
+ if ( ! class_exists('Swift', FALSE))
+ {
+ // Load SwiftMailer
+ require Kohana::find_file('vendor', 'swift/Swift');
+
+ // Register the Swift ClassLoader as an autoload
+ spl_autoload_register(array('Swift_ClassLoader', 'load'));
+ }
+
+ // Load default configuration
+ ($config === NULL) and $config = Kohana::config('email');
+
+ switch ($config['driver'])
+ {
+ case 'smtp':
+ // Set port
+ $port = empty($config['options']['port']) ? NULL : (int) $config['options']['port'];
+
+ if (empty($config['options']['encryption']))
+ {
+ // No encryption
+ $encryption = Swift_Connection_SMTP::ENC_OFF;
+ }
+ else
+ {
+ // Set encryption
+ switch (strtolower($config['options']['encryption']))
+ {
+ case 'tls': $encryption = Swift_Connection_SMTP::ENC_TLS; break;
+ case 'ssl': $encryption = Swift_Connection_SMTP::ENC_SSL; break;
+ }
+ }
+
+ // Create a SMTP connection
+ $connection = new Swift_Connection_SMTP($config['options']['hostname'], $port, $encryption);
+
+ // Do authentication, if part of the DSN
+ empty($config['options']['username']) or $connection->setUsername($config['options']['username']);
+ empty($config['options']['password']) or $connection->setPassword($config['options']['password']);
+
+ if ( ! empty($config['options']['auth']))
+ {
+ // Get the class name and params
+ list ($class, $params) = arr::callback_string($config['options']['auth']);
+
+ if ($class === 'PopB4Smtp')
+ {
+ // Load the PopB4Smtp class manually, due to its odd filename
+ require Kohana::find_file('vendor', 'swift/Swift/Authenticator/$PopB4Smtp$');
+ }
+
+ // Prepare the class name for auto-loading
+ $class = 'Swift_Authenticator_'.$class;
+
+ // Attach the authenticator
+ $connection->attachAuthenticator(($params === NULL) ? new $class : new $class($params[0]));
+ }
+
+ // Set the timeout to 5 seconds
+ $connection->setTimeout(empty($config['options']['timeout']) ? 5 : (int) $config['options']['timeout']);
+ break;
+ case 'sendmail':
+ // Create a sendmail connection
+ $connection = new Swift_Connection_Sendmail
+ (
+ empty($config['options']) ? Swift_Connection_Sendmail::AUTO_DETECT : $config['options']
+ );
+
+ // Set the timeout to 5 seconds
+ $connection->setTimeout(5);
+ break;
+ default:
+ // Use the native connection
+ $connection = new Swift_Connection_NativeMail($config['options']);
+ break;
+ }
+
+ // Create the SwiftMailer instance
+ return email::$mail = new Swift($connection);
+ }
+
+ /**
+ * Send an email message.
+ *
+ * @param string|array recipient email (and name), or an array of To, Cc, Bcc names
+ * @param string|array sender email (and name)
+ * @param string message subject
+ * @param string message body
+ * @param boolean send email as HTML
+ * @return integer number of emails sent
+ */
+ public static function send($to, $from, $subject, $message, $html = FALSE)
+ {
+ // Connect to SwiftMailer
+ (email::$mail === NULL) and email::connect();
+
+ // Determine the message type
+ $html = ($html === TRUE) ? 'text/html' : 'text/plain';
+
+ // Create the message
+ $message = new Swift_Message($subject, $message, $html, '8bit', 'utf-8');
+
+ if (is_string($to))
+ {
+ // Single recipient
+ $recipients = new Swift_Address($to);
+ }
+ elseif (is_array($to))
+ {
+ if (isset($to[0]) AND isset($to[1]))
+ {
+ // Create To: address set
+ $to = array('to' => $to);
+ }
+
+ // Create a list of recipients
+ $recipients = new Swift_RecipientList;
+
+ foreach ($to as $method => $set)
+ {
+ if ( ! in_array($method, array('to', 'cc', 'bcc')))
+ {
+ // Use To: by default
+ $method = 'to';
+ }
+
+ // Create method name
+ $method = 'add'.ucfirst($method);
+
+ if (is_array($set))
+ {
+ // Add a recipient with name
+ $recipients->$method($set[0], $set[1]);
+ }
+ else
+ {
+ // Add a recipient without name
+ $recipients->$method($set);
+ }
+ }
+ }
+
+ if (is_string($from))
+ {
+ // From without a name
+ $from = new Swift_Address($from);
+ }
+ elseif (is_array($from))
+ {
+ // From with a name
+ $from = new Swift_Address($from[0], $from[1]);
+ }
+
+ return email::$mail->send($message, $recipients, $from);
+ }
+
+} // End email \ No newline at end of file
diff --git a/system/helpers/expires.php b/system/helpers/expires.php
new file mode 100644
index 00000000..c43cc0cc
--- /dev/null
+++ b/system/helpers/expires.php
@@ -0,0 +1,111 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Controls headers that effect client caching of pages
+ *
+ * $Id: expires.php 4272 2009-04-25 21:47:26Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class expires_Core {
+
+ /**
+ * Sets the amount of time before a page expires
+ *
+ * @param integer Seconds before the page expires
+ * @return boolean
+ */
+ public static function set($seconds = 60)
+ {
+ if (expires::check_headers())
+ {
+ $now = $expires = time();
+
+ // Set the expiration timestamp
+ $expires += $seconds;
+
+ // Send headers
+ header('Last-Modified: '.gmdate('D, d M Y H:i:s T', $now));
+ header('Expires: '.gmdate('D, d M Y H:i:s T', $expires));
+ header('Cache-Control: max-age='.$seconds);
+
+ return $expires;
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Checks to see if a page should be updated or send Not Modified status
+ *
+ * @param integer Seconds added to the modified time received to calculate what should be sent
+ * @return bool FALSE when the request needs to be updated
+ */
+ public static function check($seconds = 60)
+ {
+ if ( ! empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) AND expires::check_headers())
+ {
+ if (($strpos = strpos($_SERVER['HTTP_IF_MODIFIED_SINCE'], ';')) !== FALSE)
+ {
+ // IE6 and perhaps other IE versions send length too, compensate here
+ $mod_time = substr($_SERVER['HTTP_IF_MODIFIED_SINCE'], 0, $strpos);
+ }
+ else
+ {
+ $mod_time = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
+ }
+
+ $mod_time = strtotime($mod_time);
+ $mod_time_diff = $mod_time + $seconds - time();
+
+ if ($mod_time_diff > 0)
+ {
+ // Re-send headers
+ header('Last-Modified: '.gmdate('D, d M Y H:i:s T', $mod_time));
+ header('Expires: '.gmdate('D, d M Y H:i:s T', time() + $mod_time_diff));
+ header('Cache-Control: max-age='.$mod_time_diff);
+ header('Status: 304 Not Modified', TRUE, 304);
+
+ // Prevent any output
+ Event::add('system.display', array('expires', 'prevent_output'));
+
+ // Exit to prevent other output
+ exit;
+ }
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Check headers already created to not step on download or Img_lib's feet
+ *
+ * @return boolean
+ */
+ public static function check_headers()
+ {
+ foreach (headers_list() as $header)
+ {
+ if ((session_cache_limiter() == '' AND stripos($header, 'Last-Modified:') === 0)
+ OR stripos($header, 'Expires:') === 0)
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Prevent any output from being displayed. Executed during system.display.
+ *
+ * @return void
+ */
+ public static function prevent_output()
+ {
+ Kohana::$output = '';
+ }
+
+} // End expires \ No newline at end of file
diff --git a/system/helpers/feed.php b/system/helpers/feed.php
new file mode 100644
index 00000000..74bb2f6b
--- /dev/null
+++ b/system/helpers/feed.php
@@ -0,0 +1,122 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Feed helper class.
+ *
+ * $Id: feed.php 4152 2009-04-03 23:26:23Z ixmatus $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class feed_Core {
+
+ /**
+ * Parses a remote feed into an array.
+ *
+ * @param string remote feed URL
+ * @param integer item limit to fetch
+ * @return array
+ */
+ public static function parse($feed, $limit = 0)
+ {
+ // Check if SimpleXML is installed
+ if( ! function_exists('simplexml_load_file'))
+ throw new Kohana_User_Exception('Feed Error', 'SimpleXML must be installed!');
+
+ // Make limit an integer
+ $limit = (int) $limit;
+
+ // Disable error reporting while opening the feed
+ $ER = error_reporting(0);
+
+ // Allow loading by filename or raw XML string
+ $load = (is_file($feed) OR valid::url($feed)) ? 'simplexml_load_file' : 'simplexml_load_string';
+
+ // Load the feed
+ $feed = $load($feed, 'SimpleXMLElement', LIBXML_NOCDATA);
+
+ // Restore error reporting
+ error_reporting($ER);
+
+ // Feed could not be loaded
+ if ($feed === FALSE)
+ return array();
+
+ // Detect the feed type. RSS 1.0/2.0 and Atom 1.0 are supported.
+ $feed = isset($feed->channel) ? $feed->xpath('//item') : $feed->entry;
+
+ $i = 0;
+ $items = array();
+
+ foreach ($feed as $item)
+ {
+ if ($limit > 0 AND $i++ === $limit)
+ break;
+
+ $items[] = (array) $item;
+ }
+
+ return $items;
+ }
+
+ /**
+ * Creates a feed from the given parameters.
+ *
+ * @param array feed information
+ * @param array items to add to the feed
+ * @param string define which format to use
+ * @param string define which encoding to use
+ * @return string
+ */
+ public static function create($info, $items, $format = 'rss2', $encoding = 'UTF-8')
+ {
+ $info += array('title' => 'Generated Feed', 'link' => '', 'generator' => 'KohanaPHP');
+
+ $feed = '<?xml version="1.0" encoding="'.$encoding.'"?><rss version="2.0"><channel></channel></rss>';
+ $feed = simplexml_load_string($feed);
+
+ foreach ($info as $name => $value)
+ {
+ if (($name === 'pubDate' OR $name === 'lastBuildDate') AND (is_int($value) OR ctype_digit($value)))
+ {
+ // Convert timestamps to RFC 822 formatted dates
+ $value = date(DATE_RFC822, $value);
+ }
+ elseif (($name === 'link' OR $name === 'docs') AND strpos($value, '://') === FALSE)
+ {
+ // Convert URIs to URLs
+ $value = url::site($value, 'http');
+ }
+
+ // Add the info to the channel
+ $feed->channel->addChild($name, $value);
+ }
+
+ foreach ($items as $item)
+ {
+ // Add the item to the channel
+ $row = $feed->channel->addChild('item');
+
+ foreach ($item as $name => $value)
+ {
+ if ($name === 'pubDate' AND (is_int($value) OR ctype_digit($value)))
+ {
+ // Convert timestamps to RFC 822 formatted dates
+ $value = date(DATE_RFC822, $value);
+ }
+ elseif (($name === 'link' OR $name === 'guid') AND strpos($value, '://') === FALSE)
+ {
+ // Convert URIs to URLs
+ $value = url::site($value, 'http');
+ }
+
+ // Add the info to the row
+ $row->addChild($name, $value);
+ }
+ }
+
+ return $feed->asXML();
+ }
+
+} // End feed \ No newline at end of file
diff --git a/system/helpers/file.php b/system/helpers/file.php
new file mode 100644
index 00000000..b1b71740
--- /dev/null
+++ b/system/helpers/file.php
@@ -0,0 +1,186 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * File helper class.
+ *
+ * $Id: file.php 3769 2008-12-15 00:48:56Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class file_Core {
+
+ /**
+ * Attempt to get the mime type from a file. This method is horribly
+ * unreliable, due to PHP being horribly unreliable when it comes to
+ * determining the mime-type of a file.
+ *
+ * @param string filename
+ * @return string mime-type, if found
+ * @return boolean FALSE, if not found
+ */
+ public static function mime($filename)
+ {
+ // Make sure the file is readable
+ if ( ! (is_file($filename) AND is_readable($filename)))
+ return FALSE;
+
+ // Get the extension from the filename
+ $extension = strtolower(substr(strrchr($filename, '.'), 1));
+
+ if (preg_match('/^(?:jpe?g|png|[gt]if|bmp|swf)$/', $extension))
+ {
+ // Disable error reporting
+ $ER = error_reporting(0);
+
+ // Use getimagesize() to find the mime type on images
+ $mime = getimagesize($filename);
+
+ // Turn error reporting back on
+ error_reporting($ER);
+
+ // Return the mime type
+ if (isset($mime['mime']))
+ return $mime['mime'];
+ }
+
+ if (function_exists('finfo_open'))
+ {
+ // Use the fileinfo extension
+ $finfo = finfo_open(FILEINFO_MIME);
+ $mime = finfo_file($finfo, $filename);
+ finfo_close($finfo);
+
+ // Return the mime type
+ return $mime;
+ }
+
+ if (ini_get('mime_magic.magicfile') AND function_exists('mime_content_type'))
+ {
+ // Return the mime type using mime_content_type
+ return mime_content_type($filename);
+ }
+
+ if ( ! KOHANA_IS_WIN)
+ {
+ // Attempt to locate use the file command, checking the return value
+ if ($command = trim(exec('which file', $output, $return)) AND $return === 0)
+ {
+ return trim(exec($command.' -bi '.escapeshellarg($filename)));
+ }
+ }
+
+ if ( ! empty($extension) AND is_array($mime = Kohana::config('mimes.'.$extension)))
+ {
+ // Return the mime-type guess, based on the extension
+ return $mime[0];
+ }
+
+ // Unable to find the mime-type
+ return FALSE;
+ }
+
+ /**
+ * Split a file into pieces matching a specific size.
+ *
+ * @param string file to be split
+ * @param string directory to output to, defaults to the same directory as the file
+ * @param integer size, in MB, for each chunk to be
+ * @return integer The number of pieces that were created.
+ */
+ public static function split($filename, $output_dir = FALSE, $piece_size = 10)
+ {
+ // Find output dir
+ $output_dir = ($output_dir == FALSE) ? pathinfo(str_replace('\\', '/', realpath($filename)), PATHINFO_DIRNAME) : str_replace('\\', '/', realpath($output_dir));
+ $output_dir = rtrim($output_dir, '/').'/';
+
+ // Open files for writing
+ $input_file = fopen($filename, 'rb');
+
+ // Change the piece size to bytes
+ $piece_size = 1024 * 1024 * (int) $piece_size; // Size in bytes
+
+ // Set up reading variables
+ $read = 0; // Number of bytes read
+ $piece = 1; // Current piece
+ $chunk = 1024 * 8; // Chunk size to read
+
+ // Split the file
+ while ( ! feof($input_file))
+ {
+ // Open a new piece
+ $piece_name = $filename.'.'.str_pad($piece, 3, '0', STR_PAD_LEFT);
+ $piece_open = @fopen($piece_name, 'wb+') or die('Could not write piece '.$piece_name);
+
+ // Fill the current piece
+ while ($read < $piece_size AND $data = fread($input_file, $chunk))
+ {
+ fwrite($piece_open, $data) or die('Could not write to open piece '.$piece_name);
+ $read += $chunk;
+ }
+
+ // Close the current piece
+ fclose($piece_open);
+
+ // Prepare to open a new piece
+ $read = 0;
+ $piece++;
+
+ // Make sure that piece is valid
+ ($piece < 999) or die('Maximum of 999 pieces exceeded, try a larger piece size');
+ }
+
+ // Close input file
+ fclose($input_file);
+
+ // Returns the number of pieces that were created
+ return ($piece - 1);
+ }
+
+ /**
+ * Join a split file into a whole file.
+ *
+ * @param string split filename, without .000 extension
+ * @param string output filename, if different then an the filename
+ * @return integer The number of pieces that were joined.
+ */
+ public static function join($filename, $output = FALSE)
+ {
+ if ($output == FALSE)
+ $output = $filename;
+
+ // Set up reading variables
+ $piece = 1; // Current piece
+ $chunk = 1024 * 8; // Chunk size to read
+
+ // Open output file
+ $output_file = @fopen($output, 'wb+') or die('Could not open output file '.$output);
+
+ // Read each piece
+ while ($piece_open = @fopen(($piece_name = $filename.'.'.str_pad($piece, 3, '0', STR_PAD_LEFT)), 'rb'))
+ {
+ // Write the piece into the output file
+ while ( ! feof($piece_open))
+ {
+ fwrite($output_file, fread($piece_open, $chunk));
+ }
+
+ // Close the current piece
+ fclose($piece_open);
+
+ // Prepare for a new piece
+ $piece++;
+
+ // Make sure piece is valid
+ ($piece < 999) or die('Maximum of 999 pieces exceeded');
+ }
+
+ // Close the output file
+ fclose($output_file);
+
+ // Return the number of pieces joined
+ return ($piece - 1);
+ }
+
+} // End file \ No newline at end of file
diff --git a/system/helpers/form.php b/system/helpers/form.php
new file mode 100644
index 00000000..ce8767c5
--- /dev/null
+++ b/system/helpers/form.php
@@ -0,0 +1,542 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Form helper class.
+ *
+ * $Id: form.php 4291 2009-04-29 22:51:58Z kiall $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class form_Core {
+
+ /**
+ * Generates an opening HTML form tag.
+ *
+ * @param string form action attribute
+ * @param array extra attributes
+ * @param array hidden fields to be created immediately after the form tag
+ * @return string
+ */
+ public static function open($action = NULL, $attr = array(), $hidden = NULL)
+ {
+ // Make sure that the method is always set
+ empty($attr['method']) and $attr['method'] = 'post';
+
+ if ($attr['method'] !== 'post' AND $attr['method'] !== 'get')
+ {
+ // If the method is invalid, use post
+ $attr['method'] = 'post';
+ }
+
+ if ($action === NULL)
+ {
+ // Use the current URL as the default action
+ $action = url::site(Router::$complete_uri);
+ }
+ elseif (strpos($action, '://') === FALSE)
+ {
+ // Make the action URI into a URL
+ $action = url::site($action);
+ }
+
+ // Set action
+ $attr['action'] = $action;
+
+ // Form opening tag
+ $form = '<form'.form::attributes($attr).'>'."\n";
+
+ // Add hidden fields immediate after opening tag
+ empty($hidden) or $form .= form::hidden($hidden);
+
+ return $form;
+ }
+
+ /**
+ * Generates an opening HTML form tag that can be used for uploading files.
+ *
+ * @param string form action attribute
+ * @param array extra attributes
+ * @param array hidden fields to be created immediately after the form tag
+ * @return string
+ */
+ public static function open_multipart($action = NULL, $attr = array(), $hidden = array())
+ {
+ // Set multi-part form type
+ $attr['enctype'] = 'multipart/form-data';
+
+ return form::open($action, $attr, $hidden);
+ }
+
+ /**
+ * Generates a fieldset opening tag.
+ *
+ * @param array html attributes
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function open_fieldset($data = NULL, $extra = '')
+ {
+ return '<fieldset'.html::attributes((array) $data).' '.$extra.'>'."\n";
+ }
+
+ /**
+ * Generates a fieldset closing tag.
+ *
+ * @return string
+ */
+ public static function close_fieldset()
+ {
+ return '</fieldset>'."\n";
+ }
+
+ /**
+ * Generates a legend tag for use with a fieldset.
+ *
+ * @param string legend text
+ * @param array HTML attributes
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function legend($text = '', $data = NULL, $extra = '')
+ {
+ return '<legend'.form::attributes((array) $data).' '.$extra.'>'.$text.'</legend>'."\n";
+ }
+
+ /**
+ * Generates hidden form fields.
+ * You can pass a simple key/value string or an associative array with multiple values.
+ *
+ * @param string|array input name (string) or key/value pairs (array)
+ * @param string input value, if using an input name
+ * @return string
+ */
+ public static function hidden($data, $value = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array
+ (
+ $data => $value
+ );
+ }
+
+ $input = '';
+ foreach ($data as $name => $value)
+ {
+ $attr = array
+ (
+ 'type' => 'hidden',
+ 'name' => $name,
+ 'value' => $value
+ );
+
+ $input .= form::input($attr)."\n";
+ }
+
+ return $input;
+ }
+
+ /**
+ * Creates an HTML form input tag. Defaults to a text type.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function input($data, $value = '', $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ // Type and value are required attributes
+ $data += array
+ (
+ 'type' => 'text',
+ 'value' => $value
+ );
+
+ return '<input'.form::attributes($data).' '.$extra.' />';
+ }
+
+ /**
+ * Creates a HTML form password input tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function password($data, $value = '', $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ $data['type'] = 'password';
+
+ return form::input($data, $value, $extra);
+ }
+
+ /**
+ * Creates an HTML form upload input tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function upload($data, $value = '', $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ $data['type'] = 'file';
+
+ return form::input($data, $value, $extra);
+ }
+
+ /**
+ * Creates an HTML form textarea tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param string a string to be attached to the end of the attributes
+ * @param boolean encode existing entities
+ * @return string
+ */
+ public static function textarea($data, $value = '', $extra = '', $double_encode = TRUE)
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ // Use the value from $data if possible, or use $value
+ $value = isset($data['value']) ? $data['value'] : $value;
+
+ // Value is not part of the attributes
+ unset($data['value']);
+
+ return '<textarea'.form::attributes($data, 'textarea').' '.$extra.'>'.html::specialchars($value, $double_encode).'</textarea>';
+ }
+
+ /**
+ * Creates an HTML form select tag, or "dropdown menu".
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param array select options, when using a name
+ * @param string|array option key(s) that should be selected by default
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function dropdown($data, $options = NULL, $selected = NULL, $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+ else
+ {
+ if (isset($data['options']))
+ {
+ // Use data options
+ $options = $data['options'];
+ }
+
+ if (isset($data['selected']))
+ {
+ // Use data selected
+ $selected = $data['selected'];
+ }
+ }
+
+ if (is_array($selected))
+ {
+ // Multi-select box
+ $data['multiple'] = 'multiple';
+ }
+ else
+ {
+ // Single selection (but converted to an array)
+ $selected = array($selected);
+ }
+
+ $input = '<select'.form::attributes($data, 'select').' '.$extra.'>'."\n";
+ foreach ((array) $options as $key => $val)
+ {
+ // Key should always be a string
+ $key = (string) $key;
+
+ if (is_array($val))
+ {
+ $input .= '<optgroup label="'.$key.'">'."\n";
+ foreach ($val as $inner_key => $inner_val)
+ {
+ // Inner key should always be a string
+ $inner_key = (string) $inner_key;
+
+ $sel = in_array($inner_key, $selected) ? ' selected="selected"' : '';
+ $input .= '<option value="'.$inner_key.'"'.$sel.'>'.$inner_val.'</option>'."\n";
+ }
+ $input .= '</optgroup>'."\n";
+ }
+ else
+ {
+ $sel = in_array($key, $selected) ? ' selected="selected"' : '';
+ $input .= '<option value="'.$key.'"'.$sel.'>'.$val.'</option>'."\n";
+ }
+ }
+ $input .= '</select>';
+
+ return $input;
+ }
+
+ /**
+ * Creates an HTML form checkbox input tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param boolean make the checkbox checked by default
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function checkbox($data, $value = '', $checked = FALSE, $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ $data['type'] = 'checkbox';
+
+ if ($checked == TRUE OR (isset($data['checked']) AND $data['checked'] == TRUE))
+ {
+ $data['checked'] = 'checked';
+ }
+ else
+ {
+ unset($data['checked']);
+ }
+
+ return form::input($data, $value, $extra);
+ }
+
+ /**
+ * Creates an HTML form radio input tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param boolean make the radio selected by default
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function radio($data = '', $value = '', $checked = FALSE, $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ $data['type'] = 'radio';
+
+ if ($checked == TRUE OR (isset($data['checked']) AND $data['checked'] == TRUE))
+ {
+ $data['checked'] = 'checked';
+ }
+ else
+ {
+ unset($data['checked']);
+ }
+
+ return form::input($data, $value, $extra);
+ }
+
+ /**
+ * Creates an HTML form submit input tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function submit($data = '', $value = '', $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ if (empty($data['name']))
+ {
+ // Remove the name if it is empty
+ unset($data['name']);
+ }
+
+ $data['type'] = 'submit';
+
+ return form::input($data, $value, $extra);
+ }
+
+ /**
+ * Creates an HTML form button input tag.
+ *
+ * @param string|array input name or an array of HTML attributes
+ * @param string input value, when using a name
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function button($data = '', $value = '', $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ $data = array('name' => $data);
+ }
+
+ if (empty($data['name']))
+ {
+ // Remove the name if it is empty
+ unset($data['name']);
+ }
+
+ if (isset($data['value']) AND empty($value))
+ {
+ $value = arr::remove('value', $data);
+ }
+
+ return '<button'.form::attributes($data, 'button').' '.$extra.'>'.$value.'</button>';
+ }
+
+ /**
+ * Closes an open form tag.
+ *
+ * @param string string to be attached after the closing tag
+ * @return string
+ */
+ public static function close($extra = '')
+ {
+ return '</form>'."\n".$extra;
+ }
+
+ /**
+ * Creates an HTML form label tag.
+ *
+ * @param string|array label "for" name or an array of HTML attributes
+ * @param string label text or HTML
+ * @param string a string to be attached to the end of the attributes
+ * @return string
+ */
+ public static function label($data = '', $text = NULL, $extra = '')
+ {
+ if ( ! is_array($data))
+ {
+ if (is_string($data))
+ {
+ // Specify the input this label is for
+ $data = array('for' => $data);
+ }
+ else
+ {
+ // No input specified
+ $data = array();
+ }
+ }
+
+ if ($text === NULL AND isset($data['for']))
+ {
+ // Make the text the human-readable input name
+ $text = ucwords(inflector::humanize($data['for']));
+ }
+
+ return '<label'.form::attributes($data).' '.$extra.'>'.$text.'</label>';
+ }
+
+ /**
+ * Sorts a key/value array of HTML attributes, putting form attributes first,
+ * and returns an attribute string.
+ *
+ * @param array HTML attributes array
+ * @return string
+ */
+ public static function attributes($attr, $type = NULL)
+ {
+ if (empty($attr))
+ return '';
+
+ if (isset($attr['name']) AND empty($attr['id']) AND strpos($attr['name'], '[') === FALSE)
+ {
+ if ($type === NULL AND ! empty($attr['type']))
+ {
+ // Set the type by the attributes
+ $type = $attr['type'];
+ }
+
+ switch ($type)
+ {
+ case 'text':
+ case 'textarea':
+ case 'password':
+ case 'select':
+ case 'checkbox':
+ case 'file':
+ case 'image':
+ case 'button':
+ case 'submit':
+ // Only specific types of inputs use name to id matching
+ $attr['id'] = $attr['name'];
+ break;
+ }
+ }
+
+ $order = array
+ (
+ 'action',
+ 'method',
+ 'type',
+ 'id',
+ 'name',
+ 'value',
+ 'src',
+ 'size',
+ 'maxlength',
+ 'rows',
+ 'cols',
+ 'accept',
+ 'tabindex',
+ 'accesskey',
+ 'align',
+ 'alt',
+ 'title',
+ 'class',
+ 'style',
+ 'selected',
+ 'checked',
+ 'readonly',
+ 'disabled'
+ );
+
+ $sorted = array();
+ foreach ($order as $key)
+ {
+ if (isset($attr[$key]))
+ {
+ // Move the attribute to the sorted array
+ $sorted[$key] = $attr[$key];
+
+ // Remove the attribute from unsorted array
+ unset($attr[$key]);
+ }
+ }
+
+ // Combine the sorted and unsorted attributes and create an HTML string
+ return html::attributes(array_merge($sorted, $attr));
+ }
+
+} // End form \ No newline at end of file
diff --git a/system/helpers/format.php b/system/helpers/format.php
new file mode 100644
index 00000000..fb8a0294
--- /dev/null
+++ b/system/helpers/format.php
@@ -0,0 +1,66 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Format helper class.
+ *
+ * $Id: format.php 4070 2009-03-11 20:37:38Z Geert $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class format_Core {
+
+ /**
+ * Formats a phone number according to the specified format.
+ *
+ * @param string phone number
+ * @param string format string
+ * @return string
+ */
+ public static function phone($number, $format = '3-3-4')
+ {
+ // Get rid of all non-digit characters in number string
+ $number_clean = preg_replace('/\D+/', '', (string) $number);
+
+ // Array of digits we need for a valid format
+ $format_parts = preg_split('/[^1-9][^0-9]*/', $format, -1, PREG_SPLIT_NO_EMPTY);
+
+ // Number must match digit count of a valid format
+ if (strlen($number_clean) !== array_sum($format_parts))
+ return $number;
+
+ // Build regex
+ $regex = '(\d{'.implode('})(\d{', $format_parts).'})';
+
+ // Build replace string
+ for ($i = 1, $c = count($format_parts); $i <= $c; $i++)
+ {
+ $format = preg_replace('/(?<!\$)[1-9][0-9]*/', '\$'.$i, $format, 1);
+ }
+
+ // Hocus pocus!
+ return preg_replace('/^'.$regex.'$/', $format, $number_clean);
+ }
+
+ /**
+ * Formats a URL to contain a protocol at the beginning.
+ *
+ * @param string possibly incomplete URL
+ * @return string
+ */
+ public static function url($str = '')
+ {
+ // Clear protocol-only strings like "http://"
+ if ($str === '' OR substr($str, -3) === '://')
+ return '';
+
+ // If no protocol given, prepend "http://" by default
+ if (strpos($str, '://') === FALSE)
+ return 'http://'.$str;
+
+ // Return the original URL
+ return $str;
+ }
+
+} // End format \ No newline at end of file
diff --git a/system/helpers/html.php b/system/helpers/html.php
new file mode 100644
index 00000000..f40c86dc
--- /dev/null
+++ b/system/helpers/html.php
@@ -0,0 +1,440 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * HTML helper class.
+ *
+ * $Id: html.php 4141 2009-03-29 03:30:06Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class html_Core {
+
+ // Enable or disable automatic setting of target="_blank"
+ public static $windowed_urls = FALSE;
+
+ /**
+ * Convert special characters to HTML entities
+ *
+ * @param string string to convert
+ * @param boolean encode existing entities
+ * @return string
+ */
+ public static function specialchars($str, $double_encode = TRUE)
+ {
+ // Force the string to be a string
+ $str = (string) $str;
+
+ // Do encode existing HTML entities (default)
+ if ($double_encode === TRUE)
+ {
+ $str = htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
+ }
+ else
+ {
+ // Do not encode existing HTML entities
+ // From PHP 5.2.3 this functionality is built-in, otherwise use a regex
+ if (version_compare(PHP_VERSION, '5.2.3', '>='))
+ {
+ $str = htmlspecialchars($str, ENT_QUOTES, 'UTF-8', FALSE);
+ }
+ else
+ {
+ $str = preg_replace('/&(?!(?:#\d++|[a-z]++);)/ui', '&amp;', $str);
+ $str = str_replace(array('<', '>', '\'', '"'), array('&lt;', '&gt;', '&#39;', '&quot;'), $str);
+ }
+ }
+
+ return $str;
+ }
+
+ /**
+ * Perform a html::specialchars() with additional URL specific encoding.
+ *
+ * @param string string to convert
+ * @param boolean encode existing entities
+ * @return string
+ */
+ public static function specialurlencode($str, $double_encode = TRUE)
+ {
+ return str_replace(' ', '%20', html::specialchars($str, $double_encode));
+ }
+
+ /**
+ * Create HTML link anchors.
+ *
+ * @param string URL or URI string
+ * @param string link text
+ * @param array HTML anchor attributes
+ * @param string non-default protocol, eg: https
+ * @return string
+ */
+ public static function anchor($uri, $title = NULL, $attributes = NULL, $protocol = NULL)
+ {
+ if ($uri === '')
+ {
+ $site_url = url::base(FALSE);
+ }
+ elseif (strpos($uri, '://') === FALSE AND strpos($uri, '#') !== 0)
+ {
+ $site_url = url::site($uri, $protocol);
+ }
+ else
+ {
+ if (html::$windowed_urls === TRUE AND empty($attributes['target']))
+ {
+ $attributes['target'] = '_blank';
+ }
+
+ $site_url = $uri;
+ }
+
+ return
+ // Parsed URL
+ '<a href="'.html::specialurlencode($site_url, FALSE).'"'
+ // Attributes empty? Use an empty string
+ .(is_array($attributes) ? html::attributes($attributes) : '').'>'
+ // Title empty? Use the parsed URL
+ .(($title === NULL) ? $site_url : $title).'</a>';
+ }
+
+ /**
+ * Creates an HTML anchor to a file.
+ *
+ * @param string name of file to link to
+ * @param string link text
+ * @param array HTML anchor attributes
+ * @param string non-default protocol, eg: ftp
+ * @return string
+ */
+ public static function file_anchor($file, $title = NULL, $attributes = NULL, $protocol = NULL)
+ {
+ return
+ // Base URL + URI = full URL
+ '<a href="'.html::specialurlencode(url::base(FALSE, $protocol).$file, FALSE).'"'
+ // Attributes empty? Use an empty string
+ .(is_array($attributes) ? html::attributes($attributes) : '').'>'
+ // Title empty? Use the filename part of the URI
+ .(($title === NULL) ? end(explode('/', $file)) : $title) .'</a>';
+ }
+
+ /**
+ * Similar to anchor, but with the protocol parameter first.
+ *
+ * @param string link protocol
+ * @param string URI or URL to link to
+ * @param string link text
+ * @param array HTML anchor attributes
+ * @return string
+ */
+ public static function panchor($protocol, $uri, $title = NULL, $attributes = FALSE)
+ {
+ return html::anchor($uri, $title, $attributes, $protocol);
+ }
+
+ /**
+ * Create an array of anchors from an array of link/title pairs.
+ *
+ * @param array link/title pairs
+ * @return array
+ */
+ public static function anchor_array(array $array)
+ {
+ $anchors = array();
+ foreach ($array as $link => $title)
+ {
+ // Create list of anchors
+ $anchors[] = html::anchor($link, $title);
+ }
+ return $anchors;
+ }
+
+ /**
+ * Generates an obfuscated version of an email address.
+ *
+ * @param string email address
+ * @return string
+ */
+ public static function email($email)
+ {
+ $safe = '';
+ foreach (str_split($email) as $letter)
+ {
+ switch (($letter === '@') ? rand(1, 2) : rand(1, 3))
+ {
+ // HTML entity code
+ case 1: $safe .= '&#'.ord($letter).';'; break;
+ // Hex character code
+ case 2: $safe .= '&#x'.dechex(ord($letter)).';'; break;
+ // Raw (no) encoding
+ case 3: $safe .= $letter;
+ }
+ }
+
+ return $safe;
+ }
+
+ /**
+ * Creates an email anchor.
+ *
+ * @param string email address to send to
+ * @param string link text
+ * @param array HTML anchor attributes
+ * @return string
+ */
+ public static function mailto($email, $title = NULL, $attributes = NULL)
+ {
+ if (empty($email))
+ return $title;
+
+ // Remove the subject or other parameters that do not need to be encoded
+ if (strpos($email, '?') !== FALSE)
+ {
+ // Extract the parameters from the email address
+ list ($email, $params) = explode('?', $email, 2);
+
+ // Make the params into a query string, replacing spaces
+ $params = '?'.str_replace(' ', '%20', $params);
+ }
+ else
+ {
+ // No parameters
+ $params = '';
+ }
+
+ // Obfuscate email address
+ $safe = html::email($email);
+
+ // Title defaults to the encoded email address
+ empty($title) and $title = $safe;
+
+ // Parse attributes
+ empty($attributes) or $attributes = html::attributes($attributes);
+
+ // Encoded start of the href="" is a static encoded version of 'mailto:'
+ return '<a href="&#109;&#097;&#105;&#108;&#116;&#111;&#058;'.$safe.$params.'"'.$attributes.'>'.$title.'</a>';
+ }
+
+ /**
+ * Generate a "breadcrumb" list of anchors representing the URI.
+ *
+ * @param array segments to use as breadcrumbs, defaults to using Router::$segments
+ * @return string
+ */
+ public static function breadcrumb($segments = NULL)
+ {
+ empty($segments) and $segments = Router::$segments;
+
+ $array = array();
+ while ($segment = array_pop($segments))
+ {
+ $array[] = html::anchor
+ (
+ // Complete URI for the URL
+ implode('/', $segments).'/'.$segment,
+ // Title for the current segment
+ ucwords(inflector::humanize($segment))
+ );
+ }
+
+ // Retrun the array of all the segments
+ return array_reverse($array);
+ }
+
+ /**
+ * Creates a meta tag.
+ *
+ * @param string|array tag name, or an array of tags
+ * @param string tag "content" value
+ * @return string
+ */
+ public static function meta($tag, $value = NULL)
+ {
+ if (is_array($tag))
+ {
+ $tags = array();
+ foreach ($tag as $t => $v)
+ {
+ // Build each tag and add it to the array
+ $tags[] = html::meta($t, $v);
+ }
+
+ // Return all of the tags as a string
+ return implode("\n", $tags);
+ }
+
+ // Set the meta attribute value
+ $attr = in_array(strtolower($tag), Kohana::config('http.meta_equiv')) ? 'http-equiv' : 'name';
+
+ return '<meta '.$attr.'="'.$tag.'" content="'.$value.'" />';
+ }
+
+ /**
+ * Creates a stylesheet link.
+ *
+ * @param string|array filename, or array of filenames to match to array of medias
+ * @param string|array media type of stylesheet, or array to match filenames
+ * @param boolean include the index_page in the link
+ * @return string
+ */
+ public static function stylesheet($style, $media = FALSE, $index = FALSE)
+ {
+ return html::link($style, 'stylesheet', 'text/css', '.css', $media, $index);
+ }
+
+ /**
+ * Creates a link tag.
+ *
+ * @param string|array filename
+ * @param string|array relationship
+ * @param string|array mimetype
+ * @param string specifies suffix of the file
+ * @param string|array specifies on what device the document will be displayed
+ * @param boolean include the index_page in the link
+ * @return string
+ */
+ public static function link($href, $rel, $type, $suffix = FALSE, $media = FALSE, $index = FALSE)
+ {
+ $compiled = '';
+
+ if (is_array($href))
+ {
+ foreach ($href as $_href)
+ {
+ $_rel = is_array($rel) ? array_shift($rel) : $rel;
+ $_type = is_array($type) ? array_shift($type) : $type;
+ $_media = is_array($media) ? array_shift($media) : $media;
+
+ $compiled .= html::link($_href, $_rel, $_type, $suffix, $_media, $index);
+ }
+ }
+ else
+ {
+ if (strpos($href, '://') === FALSE)
+ {
+ // Make the URL absolute
+ $href = url::base($index).$href;
+ }
+
+ $length = strlen($suffix);
+
+ if ( $length > 0 AND substr_compare($href, $suffix, -$length, $length, FALSE) !== 0)
+ {
+ // Add the defined suffix
+ $href .= $suffix;
+ }
+
+ $attr = array
+ (
+ 'rel' => $rel,
+ 'type' => $type,
+ 'href' => $href,
+ );
+
+ if ( ! empty($media))
+ {
+ // Add the media type to the attributes
+ $attr['media'] = $media;
+ }
+
+ $compiled = '<link'.html::attributes($attr).' />';
+ }
+
+ return $compiled."\n";
+ }
+
+ /**
+ * Creates a script link.
+ *
+ * @param string|array filename
+ * @param boolean include the index_page in the link
+ * @return string
+ */
+ public static function script($script, $index = FALSE)
+ {
+ $compiled = '';
+
+ if (is_array($script))
+ {
+ foreach ($script as $name)
+ {
+ $compiled .= html::script($name, $index);
+ }
+ }
+ else
+ {
+ if (strpos($script, '://') === FALSE)
+ {
+ // Add the suffix only when it's not already present
+ $script = url::base((bool) $index).$script;
+ }
+
+ if (substr_compare($script, '.js', -3, 3, FALSE) !== 0)
+ {
+ // Add the javascript suffix
+ $script .= '.js';
+ }
+
+ $compiled = '<script type="text/javascript" src="'.$script.'"></script>';
+ }
+
+ return $compiled."\n";
+ }
+
+ /**
+ * Creates a image link.
+ *
+ * @param string image source, or an array of attributes
+ * @param string|array image alt attribute, or an array of attributes
+ * @param boolean include the index_page in the link
+ * @return string
+ */
+ public static function image($src = NULL, $alt = NULL, $index = FALSE)
+ {
+ // Create attribute list
+ $attributes = is_array($src) ? $src : array('src' => $src);
+
+ if (is_array($alt))
+ {
+ $attributes += $alt;
+ }
+ elseif ( ! empty($alt))
+ {
+ // Add alt to attributes
+ $attributes['alt'] = $alt;
+ }
+
+ if (strpos($attributes['src'], '://') === FALSE)
+ {
+ // Make the src attribute into an absolute URL
+ $attributes['src'] = url::base($index).$attributes['src'];
+ }
+
+ return '<img'.html::attributes($attributes).' />';
+ }
+
+ /**
+ * Compiles an array of HTML attributes into an attribute string.
+ *
+ * @param string|array array of attributes
+ * @return string
+ */
+ public static function attributes($attrs)
+ {
+ if (empty($attrs))
+ return '';
+
+ if (is_string($attrs))
+ return ' '.$attrs;
+
+ $compiled = '';
+ foreach ($attrs as $key => $val)
+ {
+ $compiled .= ' '.$key.'="'.html::specialchars($val).'"';
+ }
+
+ return $compiled;
+ }
+
+} // End html
diff --git a/system/helpers/inflector.php b/system/helpers/inflector.php
new file mode 100644
index 00000000..1e4fee23
--- /dev/null
+++ b/system/helpers/inflector.php
@@ -0,0 +1,193 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Inflector helper class.
+ *
+ * $Id: inflector.php 4072 2009-03-13 17:20:38Z jheathco $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class inflector_Core {
+
+ // Cached inflections
+ protected static $cache = array();
+
+ // Uncountable and irregular words
+ protected static $uncountable;
+ protected static $irregular;
+
+ /**
+ * Checks if a word is defined as uncountable.
+ *
+ * @param string word to check
+ * @return boolean
+ */
+ public static function uncountable($str)
+ {
+ if (inflector::$uncountable === NULL)
+ {
+ // Cache uncountables
+ inflector::$uncountable = Kohana::config('inflector.uncountable');
+
+ // Make uncountables mirroed
+ inflector::$uncountable = array_combine(inflector::$uncountable, inflector::$uncountable);
+ }
+
+ return isset(inflector::$uncountable[strtolower($str)]);
+ }
+
+ /**
+ * Makes a plural word singular.
+ *
+ * @param string word to singularize
+ * @param integer number of things
+ * @return string
+ */
+ public static function singular($str, $count = NULL)
+ {
+ // Remove garbage
+ $str = strtolower(trim($str));
+
+ if (is_string($count))
+ {
+ // Convert to integer when using a digit string
+ $count = (int) $count;
+ }
+
+ // Do nothing with a single count
+ if ($count === 0 OR $count > 1)
+ return $str;
+
+ // Cache key name
+ $key = 'singular_'.$str.$count;
+
+ if (isset(inflector::$cache[$key]))
+ return inflector::$cache[$key];
+
+ if (inflector::uncountable($str))
+ return inflector::$cache[$key] = $str;
+
+ if (empty(inflector::$irregular))
+ {
+ // Cache irregular words
+ inflector::$irregular = Kohana::config('inflector.irregular');
+ }
+
+ if ($irregular = array_search($str, inflector::$irregular))
+ {
+ $str = $irregular;
+ }
+ elseif (preg_match('/[sxz]es$/', $str) OR preg_match('/[^aeioudgkprt]hes$/', $str))
+ {
+ // Remove "es"
+ $str = substr($str, 0, -2);
+ }
+ elseif (preg_match('/[^aeiou]ies$/', $str))
+ {
+ $str = substr($str, 0, -3).'y';
+ }
+ elseif (substr($str, -1) === 's' AND substr($str, -2) !== 'ss')
+ {
+ $str = substr($str, 0, -1);
+ }
+
+ return inflector::$cache[$key] = $str;
+ }
+
+ /**
+ * Makes a singular word plural.
+ *
+ * @param string word to pluralize
+ * @return string
+ */
+ public static function plural($str, $count = NULL)
+ {
+ // Remove garbage
+ $str = strtolower(trim($str));
+
+ if (is_string($count))
+ {
+ // Convert to integer when using a digit string
+ $count = (int) $count;
+ }
+
+ // Do nothing with singular
+ if ($count === 1)
+ return $str;
+
+ // Cache key name
+ $key = 'plural_'.$str.$count;
+
+ if (isset(inflector::$cache[$key]))
+ return inflector::$cache[$key];
+
+ if (inflector::uncountable($str))
+ return inflector::$cache[$key] = $str;
+
+ if (empty(inflector::$irregular))
+ {
+ // Cache irregular words
+ inflector::$irregular = Kohana::config('inflector.irregular');
+ }
+
+ if (isset(inflector::$irregular[$str]))
+ {
+ $str = inflector::$irregular[$str];
+ }
+ elseif (preg_match('/[sxz]$/', $str) OR preg_match('/[^aeioudgkprt]h$/', $str))
+ {
+ $str .= 'es';
+ }
+ elseif (preg_match('/[^aeiou]y$/', $str))
+ {
+ // Change "y" to "ies"
+ $str = substr_replace($str, 'ies', -1);
+ }
+ else
+ {
+ $str .= 's';
+ }
+
+ // Set the cache and return
+ return inflector::$cache[$key] = $str;
+ }
+
+ /**
+ * Makes a phrase camel case.
+ *
+ * @param string phrase to camelize
+ * @return string
+ */
+ public static function camelize($str)
+ {
+ $str = 'x'.strtolower(trim($str));
+ $str = ucwords(preg_replace('/[\s_]+/', ' ', $str));
+
+ return substr(str_replace(' ', '', $str), 1);
+ }
+
+ /**
+ * Makes a phrase underscored instead of spaced.
+ *
+ * @param string phrase to underscore
+ * @return string
+ */
+ public static function underscore($str)
+ {
+ return preg_replace('/\s+/', '_', trim($str));
+ }
+
+ /**
+ * Makes an underscored or dashed phrase human-reable.
+ *
+ * @param string phrase to make human-reable
+ * @return string
+ */
+ public static function humanize($str)
+ {
+ return preg_replace('/[_-]+/', ' ', trim($str));
+ }
+
+} // End inflector \ No newline at end of file
diff --git a/system/helpers/num.php b/system/helpers/num.php
new file mode 100644
index 00000000..3eb5d5ac
--- /dev/null
+++ b/system/helpers/num.php
@@ -0,0 +1,26 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Number helper class.
+ *
+ * $Id: num.php 3769 2008-12-15 00:48:56Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class num_Core {
+
+ /**
+ * Round a number to the nearest nth
+ *
+ * @param integer number to round
+ * @param integer number to round to
+ * @return integer
+ */
+ public static function round($number, $nearest = 5)
+ {
+ return round($number / $nearest) * $nearest;
+ }
+
+} // End num \ No newline at end of file
diff --git a/system/helpers/remote.php b/system/helpers/remote.php
new file mode 100644
index 00000000..f9e0267f
--- /dev/null
+++ b/system/helpers/remote.php
@@ -0,0 +1,66 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Remote url/file helper.
+ *
+ * $Id: remote.php 3769 2008-12-15 00:48:56Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class remote_Core {
+
+ public static function status($url)
+ {
+ if ( ! valid::url($url, 'http'))
+ return FALSE;
+
+ // Get the hostname and path
+ $url = parse_url($url);
+
+ if (empty($url['path']))
+ {
+ // Request the root document
+ $url['path'] = '/';
+ }
+
+ // Open a remote connection
+ $remote = fsockopen($url['host'], 80, $errno, $errstr, 5);
+
+ if ( ! is_resource($remote))
+ return FALSE;
+
+ // Set CRLF
+ $CRLF = "\r\n";
+
+ // Send request
+ fwrite($remote, 'HEAD '.$url['path'].' HTTP/1.0'.$CRLF);
+ fwrite($remote, 'Host: '.$url['host'].$CRLF);
+ fwrite($remote, 'Connection: close'.$CRLF);
+ fwrite($remote, 'User-Agent: Kohana Framework (+http://kohanaphp.com/)'.$CRLF);
+
+ // Send one more CRLF to terminate the headers
+ fwrite($remote, $CRLF);
+
+ while ( ! feof($remote))
+ {
+ // Get the line
+ $line = trim(fgets($remote, 512));
+
+ if ($line !== '' AND preg_match('#^HTTP/1\.[01] (\d{3})#', $line, $matches))
+ {
+ // Response code found
+ $response = (int) $matches[1];
+
+ break;
+ }
+ }
+
+ // Close the connection
+ fclose($remote);
+
+ return isset($response) ? $response : FALSE;
+ }
+
+} // End remote \ No newline at end of file
diff --git a/system/helpers/request.php b/system/helpers/request.php
new file mode 100644
index 00000000..4203d0e5
--- /dev/null
+++ b/system/helpers/request.php
@@ -0,0 +1,239 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Request helper class.
+ *
+ * $Id: request.php 4355 2009-05-15 17:18:28Z kiall $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class request_Core {
+
+ // Possible HTTP methods
+ protected static $http_methods = array('get', 'head', 'options', 'post', 'put', 'delete');
+
+ // Content types from client's HTTP Accept request header (array)
+ protected static $accept_types;
+
+ /**
+ * Returns the HTTP referrer, or the default if the referrer is not set.
+ *
+ * @param mixed default to return
+ * @return string
+ */
+ public static function referrer($default = FALSE)
+ {
+ if ( ! empty($_SERVER['HTTP_REFERER']))
+ {
+ // Set referrer
+ $ref = $_SERVER['HTTP_REFERER'];
+
+ if (strpos($ref, url::base(FALSE)) === 0)
+ {
+ // Remove the base URL from the referrer
+ $ref = substr($ref, strlen(url::base(FALSE)));
+ }
+ }
+
+ return isset($ref) ? $ref : $default;
+ }
+
+ /**
+ * Returns the current request protocol, based on $_SERVER['https']. In CLI
+ * mode, NULL will be returned.
+ *
+ * @return string
+ */
+ public static function protocol()
+ {
+ if (PHP_SAPI === 'cli')
+ {
+ return NULL;
+ }
+ elseif ( ! empty($_SERVER['HTTPS']) AND $_SERVER['HTTPS'] === 'on')
+ {
+ return 'https';
+ }
+ else
+ {
+ return 'http';
+ }
+ }
+
+ /**
+ * Tests if the current request is an AJAX request by checking the X-Requested-With HTTP
+ * request header that most popular JS frameworks now set for AJAX calls.
+ *
+ * @return boolean
+ */
+ public static function is_ajax()
+ {
+ return (isset($_SERVER['HTTP_X_REQUESTED_WITH']) AND strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
+ }
+
+ /**
+ * Returns current request method.
+ *
+ * @throws Kohana_Exception in case of an unknown request method
+ * @return string
+ */
+ public static function method()
+ {
+ $method = strtolower($_SERVER['REQUEST_METHOD']);
+
+ if ( ! in_array($method, request::$http_methods))
+ throw new Kohana_Exception('request.unknown_method', $method);
+
+ return $method;
+ }
+
+ /**
+ * Returns boolean of whether client accepts content type.
+ *
+ * @param string content type
+ * @param boolean set to TRUE to disable wildcard checking
+ * @return boolean
+ */
+ public static function accepts($type = NULL, $explicit_check = FALSE)
+ {
+ request::parse_accept_header();
+
+ if ($type === NULL)
+ return request::$accept_types;
+
+ return (request::accepts_at_quality($type, $explicit_check) > 0);
+ }
+
+ /**
+ * Compare the q values for given array of content types and return the one with the highest value.
+ * If items are found to have the same q value, the first one encountered in the given array wins.
+ * If all items in the given array have a q value of 0, FALSE is returned.
+ *
+ * @param array content types
+ * @param boolean set to TRUE to disable wildcard checking
+ * @return mixed string mime type with highest q value, FALSE if none of the given types are accepted
+ */
+ public static function preferred_accept($types, $explicit_check = FALSE)
+ {
+ // Initialize
+ $mime_types = array();
+ $max_q = 0;
+ $preferred = FALSE;
+
+ // Load q values for all given content types
+ foreach (array_unique($types) as $type)
+ {
+ $mime_types[$type] = request::accepts_at_quality($type, $explicit_check);
+ }
+
+ // Look for the highest q value
+ foreach ($mime_types as $type => $q)
+ {
+ if ($q > $max_q)
+ {
+ $max_q = $q;
+ $preferred = $type;
+ }
+ }
+
+ return $preferred;
+ }
+
+ /**
+ * Returns quality factor at which the client accepts content type.
+ *
+ * @param string content type (e.g. "image/jpg", "jpg")
+ * @param boolean set to TRUE to disable wildcard checking
+ * @return integer|float
+ */
+ public static function accepts_at_quality($type = NULL, $explicit_check = FALSE)
+ {
+ request::parse_accept_header();
+
+ // Normalize type
+ $type = strtolower((string) $type);
+
+ // General content type (e.g. "jpg")
+ if (strpos($type, '/') === FALSE)
+ {
+ // Don't accept anything by default
+ $q = 0;
+
+ // Look up relevant mime types
+ foreach ((array) Kohana::config('mimes.'.$type) as $type)
+ {
+ $q2 = request::accepts_at_quality($type, $explicit_check);
+ $q = ($q2 > $q) ? $q2 : $q;
+ }
+
+ return $q;
+ }
+
+ // Content type with subtype given (e.g. "image/jpg")
+ $type = explode('/', $type, 2);
+
+ // Exact match
+ if (isset(request::$accept_types[$type[0]][$type[1]]))
+ return request::$accept_types[$type[0]][$type[1]];
+
+ // Wildcard match (if not checking explicitly)
+ if ($explicit_check === FALSE AND isset(request::$accept_types[$type[0]]['*']))
+ return request::$accept_types[$type[0]]['*'];
+
+ // Catch-all wildcard match (if not checking explicitly)
+ if ($explicit_check === FALSE AND isset(request::$accept_types['*']['*']))
+ return request::$accept_types['*']['*'];
+
+ // Content type not accepted
+ return 0;
+ }
+
+ /**
+ * Parses client's HTTP Accept request header, and builds array structure representing it.
+ *
+ * @return void
+ */
+ protected static function parse_accept_header()
+ {
+ // Run this function just once
+ if (request::$accept_types !== NULL)
+ return;
+
+ // Initialize accept_types array
+ request::$accept_types = array();
+
+ // No HTTP Accept header found
+ if (empty($_SERVER['HTTP_ACCEPT']))
+ {
+ // Accept everything
+ request::$accept_types['*']['*'] = 1;
+ return;
+ }
+
+ // Remove linebreaks and parse the HTTP Accept header
+ foreach (explode(',', str_replace(array("\r", "\n"), '', $_SERVER['HTTP_ACCEPT'])) as $accept_entry)
+ {
+ // Explode each entry in content type and possible quality factor
+ $accept_entry = explode(';', trim($accept_entry), 2);
+
+ // Explode each content type (e.g. "text/html")
+ $type = explode('/', $accept_entry[0], 2);
+
+ // Skip invalid content types
+ if ( ! isset($type[1]))
+ continue;
+
+ // Assume a default quality factor of 1 if no custom q value found
+ $q = (isset($accept_entry[1]) AND preg_match('~\bq\s*+=\s*+([.0-9]+)~', $accept_entry[1], $match)) ? (float) $match[1] : 1;
+
+ // Populate accept_types array
+ if ( ! isset(request::$accept_types[$type[0]][$type[1]]) OR $q > request::$accept_types[$type[0]][$type[1]])
+ {
+ request::$accept_types[$type[0]][$type[1]] = $q;
+ }
+ }
+ }
+
+} // End request \ No newline at end of file
diff --git a/system/helpers/security.php b/system/helpers/security.php
new file mode 100644
index 00000000..cd48d2e0
--- /dev/null
+++ b/system/helpers/security.php
@@ -0,0 +1,47 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Security helper class.
+ *
+ * $Id: security.php 3769 2008-12-15 00:48:56Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class security_Core {
+
+ /**
+ * Sanitize a string with the xss_clean method.
+ *
+ * @param string string to sanitize
+ * @return string
+ */
+ public static function xss_clean($str)
+ {
+ return Input::instance()->xss_clean($str);
+ }
+
+ /**
+ * Remove image tags from a string.
+ *
+ * @param string string to sanitize
+ * @return string
+ */
+ public static function strip_image_tags($str)
+ {
+ return preg_replace('#<img\s.*?(?:src\s*=\s*["\']?([^"\'<>\s]*)["\']?[^>]*)?>#is', '$1', $str);
+ }
+
+ /**
+ * Remove PHP tags from a string.
+ *
+ * @param string string to sanitize
+ * @return string
+ */
+ public static function encode_php_tags($str)
+ {
+ return str_replace(array('<?', '?>'), array('&lt;?', '?&gt;'), $str);
+ }
+
+} // End security \ No newline at end of file
diff --git a/system/helpers/text.php b/system/helpers/text.php
new file mode 100644
index 00000000..d0e573ec
--- /dev/null
+++ b/system/helpers/text.php
@@ -0,0 +1,410 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Text helper class.
+ *
+ * $Id: text.php 3769 2008-12-15 00:48:56Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class text_Core {
+
+ /**
+ * Limits a phrase to a given number of words.
+ *
+ * @param string phrase to limit words of
+ * @param integer number of words to limit to
+ * @param string end character or entity
+ * @return string
+ */
+ public static function limit_words($str, $limit = 100, $end_char = NULL)
+ {
+ $limit = (int) $limit;
+ $end_char = ($end_char === NULL) ? '&#8230;' : $end_char;
+
+ if (trim($str) === '')
+ return $str;
+
+ if ($limit <= 0)
+ return $end_char;
+
+ preg_match('/^\s*+(?:\S++\s*+){1,'.$limit.'}/u', $str, $matches);
+
+ // Only attach the end character if the matched string is shorter
+ // than the starting string.
+ return rtrim($matches[0]).(strlen($matches[0]) === strlen($str) ? '' : $end_char);
+ }
+
+ /**
+ * Limits a phrase to a given number of characters.
+ *
+ * @param string phrase to limit characters of
+ * @param integer number of characters to limit to
+ * @param string end character or entity
+ * @param boolean enable or disable the preservation of words while limiting
+ * @return string
+ */
+ public static function limit_chars($str, $limit = 100, $end_char = NULL, $preserve_words = FALSE)
+ {
+ $end_char = ($end_char === NULL) ? '&#8230;' : $end_char;
+
+ $limit = (int) $limit;
+
+ if (trim($str) === '' OR utf8::strlen($str) <= $limit)
+ return $str;
+
+ if ($limit <= 0)
+ return $end_char;
+
+ if ($preserve_words == FALSE)
+ {
+ return rtrim(utf8::substr($str, 0, $limit)).$end_char;
+ }
+
+ preg_match('/^.{'.($limit - 1).'}\S*/us', $str, $matches);
+
+ return rtrim($matches[0]).(strlen($matches[0]) == strlen($str) ? '' : $end_char);
+ }
+
+ /**
+ * Alternates between two or more strings.
+ *
+ * @param string strings to alternate between
+ * @return string
+ */
+ public static function alternate()
+ {
+ static $i;
+
+ if (func_num_args() === 0)
+ {
+ $i = 0;
+ return '';
+ }
+
+ $args = func_get_args();
+ return $args[($i++ % count($args))];
+ }
+
+ /**
+ * Generates a random string of a given type and length.
+ *
+ * @param string a type of pool, or a string of characters to use as the pool
+ * @param integer length of string to return
+ * @return string
+ *
+ * @tutorial alnum alpha-numeric characters
+ * @tutorial alpha alphabetical characters
+ * @tutorial hexdec hexadecimal characters, 0-9 plus a-f
+ * @tutorial numeric digit characters, 0-9
+ * @tutorial nozero digit characters, 1-9
+ * @tutorial distinct clearly distinct alpha-numeric characters
+ */
+ public static function random($type = 'alnum', $length = 8)
+ {
+ $utf8 = FALSE;
+
+ switch ($type)
+ {
+ case 'alnum':
+ $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ break;
+ case 'alpha':
+ $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ break;
+ case 'hexdec':
+ $pool = '0123456789abcdef';
+ break;
+ case 'numeric':
+ $pool = '0123456789';
+ break;
+ case 'nozero':
+ $pool = '123456789';
+ break;
+ case 'distinct':
+ $pool = '2345679ACDEFHJKLMNPRSTUVWXYZ';
+ break;
+ default:
+ $pool = (string) $type;
+ $utf8 = ! utf8::is_ascii($pool);
+ break;
+ }
+
+ // Split the pool into an array of characters
+ $pool = ($utf8 === TRUE) ? utf8::str_split($pool, 1) : str_split($pool, 1);
+
+ // Largest pool key
+ $max = count($pool) - 1;
+
+ $str = '';
+ for ($i = 0; $i < $length; $i++)
+ {
+ // Select a random character from the pool and add it to the string
+ $str .= $pool[mt_rand(0, $max)];
+ }
+
+ // Make sure alnum strings contain at least one letter and one digit
+ if ($type === 'alnum' AND $length > 1)
+ {
+ if (ctype_alpha($str))
+ {
+ // Add a random digit
+ $str[mt_rand(0, $length - 1)] = chr(mt_rand(48, 57));
+ }
+ elseif (ctype_digit($str))
+ {
+ // Add a random letter
+ $str[mt_rand(0, $length - 1)] = chr(mt_rand(65, 90));
+ }
+ }
+
+ return $str;
+ }
+
+ /**
+ * Reduces multiple slashes in a string to single slashes.
+ *
+ * @param string string to reduce slashes of
+ * @return string
+ */
+ public static function reduce_slashes($str)
+ {
+ return preg_replace('#(?<!:)//+#', '/', $str);
+ }
+
+ /**
+ * Replaces the given words with a string.
+ *
+ * @param string phrase to replace words in
+ * @param array words to replace
+ * @param string replacement string
+ * @param boolean replace words across word boundries (space, period, etc)
+ * @return string
+ */
+ public static function censor($str, $badwords, $replacement = '#', $replace_partial_words = FALSE)
+ {
+ foreach ((array) $badwords as $key => $badword)
+ {
+ $badwords[$key] = str_replace('\*', '\S*?', preg_quote((string) $badword));
+ }
+
+ $regex = '('.implode('|', $badwords).')';
+
+ if ($replace_partial_words == TRUE)
+ {
+ // Just using \b isn't sufficient when we need to replace a badword that already contains word boundaries itself
+ $regex = '(?<=\b|\s|^)'.$regex.'(?=\b|\s|$)';
+ }
+
+ $regex = '!'.$regex.'!ui';
+
+ if (utf8::strlen($replacement) == 1)
+ {
+ $regex .= 'e';
+ return preg_replace($regex, 'str_repeat($replacement, utf8::strlen(\'$1\'))', $str);
+ }
+
+ return preg_replace($regex, $replacement, $str);
+ }
+
+ /**
+ * Finds the text that is similar between a set of words.
+ *
+ * @param array words to find similar text of
+ * @return string
+ */
+ public static function similar(array $words)
+ {
+ // First word is the word to match against
+ $word = current($words);
+
+ for ($i = 0, $max = strlen($word); $i < $max; ++$i)
+ {
+ foreach ($words as $w)
+ {
+ // Once a difference is found, break out of the loops
+ if ( ! isset($w[$i]) OR $w[$i] !== $word[$i])
+ break 2;
+ }
+ }
+
+ // Return the similar text
+ return substr($word, 0, $i);
+ }
+
+ /**
+ * Converts text email addresses and anchors into links.
+ *
+ * @param string text to auto link
+ * @return string
+ */
+ public static function auto_link($text)
+ {
+ // Auto link emails first to prevent problems with "www.domain.com@example.com"
+ return text::auto_link_urls(text::auto_link_emails($text));
+ }
+
+ /**
+ * Converts text anchors into links.
+ *
+ * @param string text to auto link
+ * @return string
+ */
+ public static function auto_link_urls($text)
+ {
+ // Finds all http/https/ftp/ftps links that are not part of an existing html anchor
+ if (preg_match_all('~\b(?<!href="|">)(?:ht|f)tps?://\S+(?:/|\b)~i', $text, $matches))
+ {
+ foreach ($matches[0] as $match)
+ {
+ // Replace each link with an anchor
+ $text = str_replace($match, html::anchor($match), $text);
+ }
+ }
+
+ // Find all naked www.links.com (without http://)
+ if (preg_match_all('~\b(?<!://)www(?:\.[a-z0-9][-a-z0-9]*+)+\.[a-z]{2,6}\b~i', $text, $matches))
+ {
+ foreach ($matches[0] as $match)
+ {
+ // Replace each link with an anchor
+ $text = str_replace($match, html::anchor('http://'.$match, $match), $text);
+ }
+ }
+
+ return $text;
+ }
+
+ /**
+ * Converts text email addresses into links.
+ *
+ * @param string text to auto link
+ * @return string
+ */
+ public static function auto_link_emails($text)
+ {
+ // Finds all email addresses that are not part of an existing html mailto anchor
+ // Note: The "58;" negative lookbehind prevents matching of existing encoded html mailto anchors
+ // The html entity for a colon (:) is &#58; or &#058; or &#0058; etc.
+ if (preg_match_all('~\b(?<!href="mailto:|">|58;)(?!\.)[-+_a-z0-9.]++(?<!\.)@(?![-.])[-a-z0-9.]+(?<!\.)\.[a-z]{2,6}\b~i', $text, $matches))
+ {
+ foreach ($matches[0] as $match)
+ {
+ // Replace each email with an encoded mailto
+ $text = str_replace($match, html::mailto($match), $text);
+ }
+ }
+
+ return $text;
+ }
+
+ /**
+ * Automatically applies <p> and <br /> markup to text. Basically nl2br() on steroids.
+ *
+ * @param string subject
+ * @return string
+ */
+ public static function auto_p($str)
+ {
+ // Trim whitespace
+ if (($str = trim($str)) === '')
+ return '';
+
+ // Standardize newlines
+ $str = str_replace(array("\r\n", "\r"), "\n", $str);
+
+ // Trim whitespace on each line
+ $str = preg_replace('~^[ \t]+~m', '', $str);
+ $str = preg_replace('~[ \t]+$~m', '', $str);
+
+ // The following regexes only need to be executed if the string contains html
+ if ($html_found = (strpos($str, '<') !== FALSE))
+ {
+ // Elements that should not be surrounded by p tags
+ $no_p = '(?:p|div|h[1-6r]|ul|ol|li|blockquote|d[dlt]|pre|t[dhr]|t(?:able|body|foot|head)|c(?:aption|olgroup)|form|s(?:elect|tyle)|a(?:ddress|rea)|ma(?:p|th))';
+
+ // Put at least two linebreaks before and after $no_p elements
+ $str = preg_replace('~^<'.$no_p.'[^>]*+>~im', "\n$0", $str);
+ $str = preg_replace('~</'.$no_p.'\s*+>$~im', "$0\n", $str);
+ }
+
+ // Do the <p> magic!
+ $str = '<p>'.trim($str).'</p>';
+ $str = preg_replace('~\n{2,}~', "</p>\n\n<p>", $str);
+
+ // The following regexes only need to be executed if the string contains html
+ if ($html_found !== FALSE)
+ {
+ // Remove p tags around $no_p elements
+ $str = preg_replace('~<p>(?=</?'.$no_p.'[^>]*+>)~i', '', $str);
+ $str = preg_replace('~(</?'.$no_p.'[^>]*+>)</p>~i', '$1', $str);
+ }
+
+ // Convert single linebreaks to <br />
+ $str = preg_replace('~(?<!\n)\n(?!\n)~', "<br />\n", $str);
+
+ return $str;
+ }
+
+ /**
+ * Returns human readable sizes.
+ * @see Based on original functions written by:
+ * @see Aidan Lister: http://aidanlister.com/repos/v/function.size_readable.php
+ * @see Quentin Zervaas: http://www.phpriot.com/d/code/strings/filesize-format/
+ *
+ * @param integer size in bytes
+ * @param string a definitive unit
+ * @param string the return string format
+ * @param boolean whether to use SI prefixes or IEC
+ * @return string
+ */
+ public static function bytes($bytes, $force_unit = NULL, $format = NULL, $si = TRUE)
+ {
+ // Format string
+ $format = ($format === NULL) ? '%01.2f %s' : (string) $format;
+
+ // IEC prefixes (binary)
+ if ($si == FALSE OR strpos($force_unit, 'i') !== FALSE)
+ {
+ $units = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB');
+ $mod = 1024;
+ }
+ // SI prefixes (decimal)
+ else
+ {
+ $units = array('B', 'kB', 'MB', 'GB', 'TB', 'PB');
+ $mod = 1000;
+ }
+
+ // Determine unit to use
+ if (($power = array_search((string) $force_unit, $units)) === FALSE)
+ {
+ $power = ($bytes > 0) ? floor(log($bytes, $mod)) : 0;
+ }
+
+ return sprintf($format, $bytes / pow($mod, $power), $units[$power]);
+ }
+
+ /**
+ * Prevents widow words by inserting a non-breaking space between the last two words.
+ * @see http://www.shauninman.com/archive/2006/08/22/widont_wordpress_plugin
+ *
+ * @param string string to remove widows from
+ * @return string
+ */
+ public static function widont($str)
+ {
+ $str = rtrim($str);
+ $space = strrpos($str, ' ');
+
+ if ($space !== FALSE)
+ {
+ $str = substr($str, 0, $space).'&nbsp;'.substr($str, $space + 1);
+ }
+
+ return $str;
+ }
+
+} // End text \ No newline at end of file
diff --git a/system/helpers/upload.php b/system/helpers/upload.php
new file mode 100644
index 00000000..422e9e8d
--- /dev/null
+++ b/system/helpers/upload.php
@@ -0,0 +1,162 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Upload helper class for working with the global $_FILES
+ * array and Validation library.
+ *
+ * $Id: upload.php 3769 2008-12-15 00:48:56Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class upload_Core {
+
+ /**
+ * Save an uploaded file to a new location.
+ *
+ * @param mixed name of $_FILE input or array of upload data
+ * @param string new filename
+ * @param string new directory
+ * @param integer chmod mask
+ * @return string full path to new file
+ */
+ public static function save($file, $filename = NULL, $directory = NULL, $chmod = 0644)
+ {
+ // Load file data from FILES if not passed as array
+ $file = is_array($file) ? $file : $_FILES[$file];
+
+ if ($filename === NULL)
+ {
+ // Use the default filename, with a timestamp pre-pended
+ $filename = time().$file['name'];
+ }
+
+ if (Kohana::config('upload.remove_spaces') === TRUE)
+ {
+ // Remove spaces from the filename
+ $filename = preg_replace('/\s+/', '_', $filename);
+ }
+
+ if ($directory === NULL)
+ {
+ // Use the pre-configured upload directory
+ $directory = Kohana::config('upload.directory', TRUE);
+ }
+
+ // Make sure the directory ends with a slash
+ $directory = rtrim($directory, '/').'/';
+
+ if ( ! is_dir($directory) AND Kohana::config('upload.create_directories') === TRUE)
+ {
+ // Create the upload directory
+ mkdir($directory, 0777, TRUE);
+ }
+
+ if ( ! is_writable($directory))
+ throw new Kohana_Exception('upload.not_writable', $directory);
+
+ if (is_uploaded_file($file['tmp_name']) AND move_uploaded_file($file['tmp_name'], $filename = $directory.$filename))
+ {
+ if ($chmod !== FALSE)
+ {
+ // Set permissions on filename
+ chmod($filename, $chmod);
+ }
+
+ // Return new file path
+ return $filename;
+ }
+
+ return FALSE;
+ }
+
+ /* Validation Rules */
+
+ /**
+ * Tests if input data is valid file type, even if no upload is present.
+ *
+ * @param array $_FILES item
+ * @return bool
+ */
+ public static function valid($file)
+ {
+ return (is_array($file)
+ AND isset($file['error'])
+ AND isset($file['name'])
+ AND isset($file['type'])
+ AND isset($file['tmp_name'])
+ AND isset($file['size']));
+ }
+
+ /**
+ * Tests if input data has valid upload data.
+ *
+ * @param array $_FILES item
+ * @return bool
+ */
+ public static function required(array $file)
+ {
+ return (isset($file['tmp_name'])
+ AND isset($file['error'])
+ AND is_uploaded_file($file['tmp_name'])
+ AND (int) $file['error'] === UPLOAD_ERR_OK);
+ }
+
+ /**
+ * Validation rule to test if an uploaded file is allowed by extension.
+ *
+ * @param array $_FILES item
+ * @param array allowed file extensions
+ * @return bool
+ */
+ public static function type(array $file, array $allowed_types)
+ {
+ if ((int) $file['error'] !== UPLOAD_ERR_OK)
+ return TRUE;
+
+ // Get the default extension of the file
+ $extension = strtolower(substr(strrchr($file['name'], '.'), 1));
+
+ // Get the mime types for the extension
+ $mime_types = Kohana::config('mimes.'.$extension);
+
+ // Make sure there is an extension, that the extension is allowed, and that mime types exist
+ return ( ! empty($extension) AND in_array($extension, $allowed_types) AND is_array($mime_types));
+ }
+
+ /**
+ * Validation rule to test if an uploaded file is allowed by file size.
+ * File sizes are defined as: SB, where S is the size (1, 15, 300, etc) and
+ * B is the byte modifier: (B)ytes, (K)ilobytes, (M)egabytes, (G)igabytes.
+ * Eg: to limit the size to 1MB or less, you would use "1M".
+ *
+ * @param array $_FILES item
+ * @param array maximum file size
+ * @return bool
+ */
+ public static function size(array $file, array $size)
+ {
+ if ((int) $file['error'] !== UPLOAD_ERR_OK)
+ return TRUE;
+
+ // Only one size is allowed
+ $size = strtoupper($size[0]);
+
+ if ( ! preg_match('/[0-9]++[BKMG]/', $size))
+ return FALSE;
+
+ // Make the size into a power of 1024
+ switch (substr($size, -1))
+ {
+ case 'G': $size = intval($size) * pow(1024, 3); break;
+ case 'M': $size = intval($size) * pow(1024, 2); break;
+ case 'K': $size = intval($size) * pow(1024, 1); break;
+ default: $size = intval($size); break;
+ }
+
+ // Test that the file is under or equal to the max size
+ return ($file['size'] <= $size);
+ }
+
+} // End upload \ No newline at end of file
diff --git a/system/helpers/url.php b/system/helpers/url.php
new file mode 100644
index 00000000..f3d0ec8b
--- /dev/null
+++ b/system/helpers/url.php
@@ -0,0 +1,252 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * URL helper class.
+ *
+ * $Id: url.php 4029 2009-03-03 12:39:32Z Shadowhand $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class url_Core {
+
+ /**
+ * Fetches the current URI.
+ *
+ * @param boolean include the query string
+ * @return string
+ */
+ public static function current($qs = FALSE)
+ {
+ return ($qs === TRUE) ? Router::$complete_uri : Router::$current_uri;
+ }
+
+ /**
+ * Base URL, with or without the index page.
+ *
+ * If protocol (and core.site_protocol) and core.site_domain are both empty,
+ * then
+ *
+ * @param boolean include the index page
+ * @param boolean non-default protocol
+ * @return string
+ */
+ public static function base($index = FALSE, $protocol = FALSE)
+ {
+ if ($protocol == FALSE)
+ {
+ // Use the default configured protocol
+ $protocol = Kohana::config('core.site_protocol');
+ }
+
+ // Load the site domain
+ $site_domain = (string) Kohana::config('core.site_domain', TRUE);
+
+ if ($protocol == FALSE)
+ {
+ if ($site_domain === '' OR $site_domain[0] === '/')
+ {
+ // Use the configured site domain
+ $base_url = $site_domain;
+ }
+ else
+ {
+ // Guess the protocol to provide full http://domain/path URL
+ $base_url = ((empty($_SERVER['HTTPS']) OR $_SERVER['HTTPS'] === 'off') ? 'http' : 'https').'://'.$site_domain;
+ }
+ }
+ else
+ {
+ if ($site_domain === '' OR $site_domain[0] === '/')
+ {
+ // Guess the server name if the domain starts with slash
+ $base_url = $protocol.'://'.$_SERVER['HTTP_HOST'].$site_domain;
+ }
+ else
+ {
+ // Use the configured site domain
+ $base_url = $protocol.'://'.$site_domain;
+ }
+ }
+
+ if ($index === TRUE AND $index = Kohana::config('core.index_page'))
+ {
+ // Append the index page
+ $base_url = $base_url.$index;
+ }
+
+ // Force a slash on the end of the URL
+ return rtrim($base_url, '/').'/';
+ }
+
+ /**
+ * Fetches an absolute site URL based on a URI segment.
+ *
+ * @param string site URI to convert
+ * @param string non-default protocol
+ * @return string
+ */
+ public static function site($uri = '', $protocol = FALSE)
+ {
+ if ($path = trim(parse_url($uri, PHP_URL_PATH), '/'))
+ {
+ // Add path suffix
+ $path .= Kohana::config('core.url_suffix');
+ }
+
+ if ($query = parse_url($uri, PHP_URL_QUERY))
+ {
+ // ?query=string
+ $query = '?'.$query;
+ }
+
+ if ($fragment = parse_url($uri, PHP_URL_FRAGMENT))
+ {
+ // #fragment
+ $fragment = '#'.$fragment;
+ }
+
+ // Concat the URL
+ return url::base(TRUE, $protocol).$path.$query.$fragment;
+ }
+
+ /**
+ * Return the URL to a file. Absolute filenames and relative filenames
+ * are allowed.
+ *
+ * @param string filename
+ * @param boolean include the index page
+ * @return string
+ */
+ public static function file($file, $index = FALSE)
+ {
+ if (strpos($file, '://') === FALSE)
+ {
+ // Add the base URL to the filename
+ $file = url::base($index).$file;
+ }
+
+ return $file;
+ }
+
+ /**
+ * Merges an array of arguments with the current URI and query string to
+ * overload, instead of replace, the current query string.
+ *
+ * @param array associative array of arguments
+ * @return string
+ */
+ public static function merge(array $arguments)
+ {
+ if ($_GET === $arguments)
+ {
+ $query = Router::$query_string;
+ }
+ elseif ($query = http_build_query(array_merge($_GET, $arguments)))
+ {
+ $query = '?'.$query;
+ }
+
+ // Return the current URI with the arguments merged into the query string
+ return Router::$current_uri.$query;
+ }
+
+ /**
+ * Convert a phrase to a URL-safe title.
+ *
+ * @param string phrase to convert
+ * @param string word separator (- or _)
+ * @return string
+ */
+ public static function title($title, $separator = '-')
+ {
+ $separator = ($separator === '-') ? '-' : '_';
+
+ // Replace accented characters by their unaccented equivalents
+ $title = utf8::transliterate_to_ascii($title);
+
+ // Remove all characters that are not the separator, a-z, 0-9, or whitespace
+ $title = preg_replace('/[^'.$separator.'a-z0-9\s]+/', '', strtolower($title));
+
+ // Replace all separator characters and whitespace by a single separator
+ $title = preg_replace('/['.$separator.'\s]+/', $separator, $title);
+
+ // Trim separators from the beginning and end
+ return trim($title, $separator);
+ }
+
+ /**
+ * Sends a page redirect header and runs the system.redirect Event.
+ *
+ * @param mixed string site URI or URL to redirect to, or array of strings if method is 300
+ * @param string HTTP method of redirect
+ * @return void
+ */
+ public static function redirect($uri = '', $method = '302')
+ {
+ if (Event::has_run('system.send_headers'))
+ {
+ return FALSE;
+ }
+
+ $codes = array
+ (
+ 'refresh' => 'Refresh',
+ '300' => 'Multiple Choices',
+ '301' => 'Moved Permanently',
+ '302' => 'Found',
+ '303' => 'See Other',
+ '304' => 'Not Modified',
+ '305' => 'Use Proxy',
+ '307' => 'Temporary Redirect'
+ );
+
+ // Validate the method and default to 302
+ $method = isset($codes[$method]) ? (string) $method : '302';
+
+ if ($method === '300')
+ {
+ $uri = (array) $uri;
+
+ $output = '<ul>';
+ foreach ($uri as $link)
+ {
+ $output .= '<li>'.html::anchor($link).'</li>';
+ }
+ $output .= '</ul>';
+
+ // The first URI will be used for the Location header
+ $uri = $uri[0];
+ }
+ else
+ {
+ $output = '<p>'.html::anchor($uri).'</p>';
+ }
+
+ // Run the redirect event
+ Event::run('system.redirect', $uri);
+
+ if (strpos($uri, '://') === FALSE)
+ {
+ // HTTP headers expect absolute URLs
+ $uri = url::site($uri, request::protocol());
+ }
+
+ if ($method === 'refresh')
+ {
+ header('Refresh: 0; url='.$uri);
+ }
+ else
+ {
+ header('HTTP/1.1 '.$method.' '.$codes[$method]);
+ header('Location: '.$uri);
+ }
+
+ // We are about to exit, so run the send_headers event
+ Event::run('system.send_headers');
+
+ exit('<h1>'.$method.' - '.$codes[$method].'</h1>'.$output);
+ }
+
+} // End url \ No newline at end of file
diff --git a/system/helpers/valid.php b/system/helpers/valid.php
new file mode 100644
index 00000000..610076f3
--- /dev/null
+++ b/system/helpers/valid.php
@@ -0,0 +1,338 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Validation helper class.
+ *
+ * $Id: valid.php 4187 2009-04-08 04:01:23Z zombor $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2008 Kohana Team
+ * @license http://kohanaphp.com/license.html
+ */
+class valid_Core {
+
+ /**
+ * Validate email, commonly used characters only
+ *
+ * @param string email address
+ * @return boolean
+ */
+ public static function email($email)
+ {
+ return (bool) preg_match('/^[-_a-z0-9\'+*$^&%=~!?{}]++(?:\.[-_a-z0-9\'+*$^&%=~!?{}]+)*+@(?:(?![-.])[-a-z0-9.]+(?<![-.])\.[a-z]{2,6}|\d{1,3}(?:\.\d{1,3}){3})(?::\d++)?$/iD', (string) $email);
+ }
+
+ /**
+ * Validate the domain of an email address by checking if the domain has a
+ * valid MX record.
+ *
+ * @param string email address
+ * @return boolean
+ */
+ public static function email_domain($email)
+ {
+ // If we can't prove the domain is invalid, consider it valid
+ // Note: checkdnsrr() is not implemented on Windows platforms
+ if ( ! function_exists('checkdnsrr'))
+ return TRUE;
+
+ // Check if the email domain has a valid MX record
+ return (bool) checkdnsrr(preg_replace('/^[^@]+@/', '', $email), 'MX');
+ }
+
+ /**
+ * Validate email, RFC compliant version
+ * Note: This function is LESS strict than valid_email. Choose carefully.
+ *
+ * @see Originally by Cal Henderson, modified to fit Kohana syntax standards:
+ * @see http://www.iamcal.com/publish/articles/php/parsing_email/
+ * @see http://www.w3.org/Protocols/rfc822/
+ *
+ * @param string email address
+ * @return boolean
+ */
+ public static function email_rfc($email)
+ {
+ $qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
+ $dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
+ $atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
+ $pair = '\\x5c[\\x00-\\x7f]';
+
+ $domain_literal = "\\x5b($dtext|$pair)*\\x5d";
+ $quoted_string = "\\x22($qtext|$pair)*\\x22";
+ $sub_domain = "($atom|$domain_literal)";
+ $word = "($atom|$quoted_string)";
+ $domain = "$sub_domain(\\x2e$sub_domain)*";
+ $local_part = "$word(\\x2e$word)*";
+ $addr_spec = "$local_part\\x40$domain";
+
+ return (bool) preg_match('/^'.$addr_spec.'$/D', (string) $email);
+ }
+
+ /**
+ * Validate URL
+ *
+ * @param string URL
+ * @return boolean
+ */
+ public static function url($url)
+ {
+ return (bool) filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED);
+ }
+
+ /**
+ * Validate IP
+ *
+ * @param string IP address
+ * @param boolean allow IPv6 addresses
+ * @param boolean allow private IP networks
+ * @return boolean
+ */
+ public static function ip($ip, $ipv6 = FALSE, $allow_private = TRUE)
+ {
+ // By default do not allow private and reserved range IPs
+ $flags = FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE;
+ if ($allow_private === TRUE)
+ $flags = FILTER_FLAG_NO_RES_RANGE;
+
+ if ($ipv6 === TRUE)
+ return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flags);
+
+ return (bool) filter_var($ip, FILTER_VALIDATE_IP, $flags | FILTER_FLAG_IPV4);
+ }
+
+ /**
+ * Validates a credit card number using the Luhn (mod10) formula.
+ * @see http://en.wikipedia.org/wiki/Luhn_algorithm
+ *
+ * @param integer credit card number
+ * @param string|array card type, or an array of card types
+ * @return boolean
+ */
+ public static function credit_card($number, $type = NULL)
+ {
+ // Remove all non-digit characters from the number
+ if (($number = preg_replace('/\D+/', '', $number)) === '')
+ return FALSE;
+
+ if ($type == NULL)
+ {
+ // Use the default type
+ $type = 'default';
+ }
+ elseif (is_array($type))
+ {
+ foreach ($type as $t)
+ {
+ // Test each type for validity
+ if (valid::credit_card($number, $t))
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ $cards = Kohana::config('credit_cards');
+
+ // Check card type
+ $type = strtolower($type);
+
+ if ( ! isset($cards[$type]))
+ return FALSE;
+
+ // Check card number length
+ $length = strlen($number);
+
+ // Validate the card length by the card type
+ if ( ! in_array($length, preg_split('/\D+/', $cards[$type]['length'])))
+ return FALSE;
+
+ // Check card number prefix
+ if ( ! preg_match('/^'.$cards[$type]['prefix'].'/', $number))
+ return FALSE;
+
+ // No Luhn check required
+ if ($cards[$type]['luhn'] == FALSE)
+ return TRUE;
+
+ // Checksum of the card number
+ $checksum = 0;
+
+ for ($i = $length - 1; $i >= 0; $i -= 2)
+ {
+ // Add up every 2nd digit, starting from the right
+ $checksum += substr($number, $i, 1);
+ }
+
+ for ($i = $length - 2; $i >= 0; $i -= 2)
+ {
+ // Add up every 2nd digit doubled, starting from the right
+ $double = substr($number, $i, 1) * 2;
+
+ // Subtract 9 from the double where value is greater than 10
+ $checksum += ($double >= 10) ? $double - 9 : $double;
+ }
+
+ // If the checksum is a multiple of 10, the number is valid
+ return ($checksum % 10 === 0);
+ }
+
+ /**
+ * Checks if a phone number is valid.
+ *
+ * @param string phone number to check
+ * @return boolean
+ */
+ public static function phone($number, $lengths = NULL)
+ {
+ if ( ! is_array($lengths))
+ {
+ $lengths = array(7,10,11);
+ }
+
+ // Remove all non-digit characters from the number
+ $number = preg_replace('/\D+/', '', $number);
+
+ // Check if the number is within range
+ return in_array(strlen($number), $lengths);
+ }
+
+ /**
+ * Tests if a string is a valid date string.
+ *
+ * @param string date to check
+ * @return boolean
+ */
+ public static function date($str)
+ {
+ return (strtotime($str) !== FALSE);
+ }
+
+ /**
+ * Checks whether a string consists of alphabetical characters only.
+ *
+ * @param string input string
+ * @param boolean trigger UTF-8 compatibility
+ * @return boolean
+ */
+ public static function alpha($str, $utf8 = FALSE)
+ {
+ return ($utf8 === TRUE)
+ ? (bool) preg_match('/^\pL++$/uD', (string) $str)
+ : ctype_alpha((string) $str);
+ }
+
+ /**
+ * Checks whether a string consists of alphabetical characters and numbers only.
+ *
+ * @param string input string
+ * @param boolean trigger UTF-8 compatibility
+ * @return boolean
+ */
+ public static function alpha_numeric($str, $utf8 = FALSE)
+ {
+ return ($utf8 === TRUE)
+ ? (bool) preg_match('/^[\pL\pN]++$/uD', (string) $str)
+ : ctype_alnum((string) $str);
+ }
+
+ /**
+ * Checks whether a string consists of alphabetical characters, numbers, underscores and dashes only.
+ *
+ * @param string input string
+ * @param boolean trigger UTF-8 compatibility
+ * @return boolean
+ */
+ public static function alpha_dash($str, $utf8 = FALSE)
+ {
+ return ($utf8 === TRUE)
+ ? (bool) preg_match('/^[-\pL\pN_]++$/uD', (string) $str)
+ : (bool) preg_match('/^[-a-z0-9_]++$/iD', (string) $str);
+ }
+
+ /**
+ * Checks whether a string consists of digits only (no dots or dashes).
+ *
+ * @param string input string
+ * @param boolean trigger UTF-8 compatibility
+ * @return boolean
+ */
+ public static function digit($str, $utf8 = FALSE)
+ {
+ return ($utf8 === TRUE)
+ ? (bool) preg_match('/^\pN++$/uD', (string) $str)
+ : ctype_digit((string) $str);
+ }
+
+ /**
+ * Checks whether a string is a valid number (negative and decimal numbers allowed).
+ *
+ * @see Uses locale conversion to allow decimal point to be locale specific.
+ * @see http://www.php.net/manual/en/function.localeconv.php
+ *
+ * @param string input string
+ * @return boolean
+ */
+ public static function numeric($str)
+ {
+ // Use localeconv to set the decimal_point value: Usually a comma or period.
+ $locale = localeconv();
+ return (bool) preg_match('/^-?[0-9'.$locale['decimal_point'].']++$/D', (string) $str);
+ }
+
+ /**
+ * Checks whether a string is a valid text. Letters, numbers, whitespace,
+ * dashes, periods, and underscores are allowed.
+ *
+ * @param string text to check
+ * @return boolean
+ */
+ public static function standard_text($str)
+ {
+ // pL matches letters
+ // pN matches numbers
+ // pZ matches whitespace
+ // pPc matches underscores
+ // pPd matches dashes
+ // pPo matches normal puncuation
+ return (bool) preg_match('/^[\pL\pN\pZ\p{Pc}\p{Pd}\p{Po}]++$/uD', (string) $str);
+ }
+
+ /**
+ * Checks if a string is a proper decimal format. The format array can be
+ * used to specify a decimal length, or a number and decimal length, eg:
+ * array(2) would force the number to have 2 decimal places, array(4,2)
+ * would force the number to have 4 digits and 2 decimal places.
+ *
+ * @param string input string
+ * @param array decimal format: y or x,y
+ * @return boolean
+ */
+ public static function decimal($str, $format = NULL)
+ {
+ // Create the pattern
+ $pattern = '/^[0-9]%s\.[0-9]%s$/';
+
+ if ( ! empty($format))
+ {
+ if (count($format) > 1)
+ {
+ // Use the format for number and decimal length
+ $pattern = sprintf($pattern, '{'.$format[0].'}', '{'.$format[1].'}');
+ }
+ elseif (count($format) > 0)
+ {
+ // Use the format as decimal length
+ $pattern = sprintf($pattern, '+', '{'.$format[0].'}');
+ }
+ }
+ else
+ {
+ // No format
+ $pattern = sprintf($pattern, '+', '+');
+ }
+
+ return (bool) preg_match($pattern, (string) $str);
+ }
+
+} // End valid \ No newline at end of file