From 12fe58d997d2066dc362fd393a18b4e5da190513 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Wed, 27 May 2009 15:11:53 -0700 Subject: 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. --- system/helpers/arr.php | 312 +++++++++++++++++++++++++ system/helpers/cookie.php | 84 +++++++ system/helpers/date.php | 405 ++++++++++++++++++++++++++++++++ system/helpers/download.php | 105 +++++++++ system/helpers/email.php | 181 +++++++++++++++ system/helpers/expires.php | 111 +++++++++ system/helpers/feed.php | 122 ++++++++++ system/helpers/file.php | 186 +++++++++++++++ system/helpers/form.php | 542 +++++++++++++++++++++++++++++++++++++++++++ system/helpers/format.php | 66 ++++++ system/helpers/html.php | 440 +++++++++++++++++++++++++++++++++++ system/helpers/inflector.php | 193 +++++++++++++++ system/helpers/num.php | 26 +++ system/helpers/remote.php | 66 ++++++ system/helpers/request.php | 239 +++++++++++++++++++ system/helpers/security.php | 47 ++++ system/helpers/text.php | 410 ++++++++++++++++++++++++++++++++ system/helpers/upload.php | 162 +++++++++++++ system/helpers/url.php | 252 ++++++++++++++++++++ system/helpers/valid.php | 338 +++++++++++++++++++++++++++ 20 files changed, 4287 insertions(+) create mode 100644 system/helpers/arr.php create mode 100644 system/helpers/cookie.php create mode 100644 system/helpers/date.php create mode 100644 system/helpers/download.php create mode 100644 system/helpers/email.php create mode 100644 system/helpers/expires.php create mode 100644 system/helpers/feed.php create mode 100644 system/helpers/file.php create mode 100644 system/helpers/form.php create mode 100644 system/helpers/format.php create mode 100644 system/helpers/html.php create mode 100644 system/helpers/inflector.php create mode 100644 system/helpers/num.php create mode 100644 system/helpers/remote.php create mode 100644 system/helpers/request.php create mode 100644 system/helpers/security.php create mode 100644 system/helpers/text.php create mode 100644 system/helpers/upload.php create mode 100644 system/helpers/url.php create mode 100644 system/helpers/valid.php (limited to 'system/helpers') 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 @@ + $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 @@ +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 @@ +> 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 @@ +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 @@ + 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 @@ +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 = ''; + $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 @@ +'."\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 ''."\n"; + } + + /** + * Generates a fieldset closing tag. + * + * @return string + */ + public static function close_fieldset() + { + return ''."\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 ''.$text.''."\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 ''; + } + + /** + * 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 ''.html::specialchars($value, $double_encode).''; + } + + /** + * 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 = ''."\n"; + foreach ((array) $options as $key => $val) + { + // Key should always be a string + $key = (string) $key; + + if (is_array($val)) + { + $input .= ''."\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 .= ''."\n"; + } + $input .= ''."\n"; + } + else + { + $sel = in_array($key, $selected) ? ' selected="selected"' : ''; + $input .= ''."\n"; + } + } + $input .= ''; + + 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 ''.$value.''; + } + + /** + * Closes an open form tag. + * + * @param string string to be attached after the closing tag + * @return string + */ + public static function close($extra = '') + { + return ''."\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 ''.$text.''; + } + + /** + * 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 @@ +=')) + { + $str = htmlspecialchars($str, ENT_QUOTES, 'UTF-8', FALSE); + } + else + { + $str = preg_replace('/&(?!(?:#\d++|[a-z]++);)/ui', '&', $str); + $str = str_replace(array('<', '>', '\'', '"'), array('<', '>', ''', '"'), $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 + '' + // Title empty? Use the parsed URL + .(($title === NULL) ? $site_url : $title).''; + } + + /** + * 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 + '' + // Title empty? Use the filename part of the URI + .(($title === NULL) ? end(explode('/', $file)) : $title) .''; + } + + /** + * 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 ''.$title.''; + } + + /** + * 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 ''; + } + + /** + * 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 = ''; + } + + 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 = ''; + } + + 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 ''; + } + + /** + * 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 @@ + 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 @@ + 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 @@ +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('#\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('<?', '?>'), $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 @@ + 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('#(? $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(?)(?: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(?|58;)(?!\.)[-+_a-z0-9.]++(? and
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('~$~im', "$0\n", $str); + } + + // Do the

magic! + $str = '

'.trim($str).'

'; + $str = preg_replace('~\n{2,}~', "

\n\n

", $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('~

(?=]*+>)~i', '', $str); + $str = preg_replace('~(]*+>)

~i', '$1', $str); + } + + // Convert single linebreaks to
+ $str = preg_replace('~(?\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).' '.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 @@ + '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 = '
    '; + foreach ($uri as $link) + { + $output .= '
  • '.html::anchor($link).'
  • '; + } + $output .= '
'; + + // The first URI will be used for the Location header + $uri = $uri[0]; + } + else + { + $output = '

'.html::anchor($uri).'

'; + } + + // 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('

'.$method.' - '.$codes[$method].'

'.$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 @@ += 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 -- cgit v1.2.3